@@ -320,3 +320,126 @@ extern int test_many_calls()
320320 fork2.vmcall (" test_many_calls" );
321321 REQUIRE (fork2.return_value () == 42000 );
322322}
323+
324+ TEST_CASE (" Remote resume" , " [Remote]" )
325+ {
326+ const auto storage_binary = build_and_load (R"M(
327+ extern long write(int, const void*, unsigned long);
328+ #include <stdio.h>
329+ #include <string.h>
330+ __asm__(".global storage_wait_paused\n"
331+ ".type storage_wait_paused, @function\n"
332+ "storage_wait_paused:\n"
333+ ".cfi_startproc\n"
334+ " mov $0x10002, %eax\n"
335+ " out %eax, $0\n"
336+ " wrfsbase %rdi\n"
337+ " ret\n"
338+ ".cfi_endproc\n");
339+ extern size_t storage_wait_paused(void** ptr);
340+
341+ int main() {
342+ while (1) {
343+ void* p = NULL;
344+ size_t len = storage_wait_paused(&p);
345+ strcpy((char*)p, "Data from storage");
346+ }
347+ return 1234;
348+ }
349+ )M" , " -Wl,-Ttext-segment=0x40400000" );
350+
351+ // Extract storage remote symbols
352+ const std::string command = " objcopy -w --extract-symbol --strip-symbol=!remote* --strip-symbol=* " + storage_binary.first + " storage.syms" ;
353+ FILE* f = popen (command.c_str (), " r" );
354+ if (f == nullptr ) {
355+ throw std::runtime_error (" Unable to extract remote symbols" );
356+ }
357+ pclose (f);
358+
359+ const auto main_binary = build_and_load (R"M(
360+ #include <stdio.h>
361+ #include <string.h>
362+ __asm__(".global remote_resume\n"
363+ ".type remote_resume, @function\n"
364+ "remote_resume:\n"
365+ " mov $0x10001, %eax\n"
366+ " out %eax, $0\n"
367+ " ret\n");
368+ extern long remote_resume(void* data, size_t len);
369+ long test_remote() {
370+ for (int i = 0; i < 100; i++) {
371+ char buffer[8192];
372+ remote_resume(buffer, sizeof(buffer));
373+ if (strcmp(buffer, "Data from storage") == 0) {
374+ continue;
375+ }
376+ return 1;
377+ }
378+ return 2345;
379+ }
380+ int main() {
381+ return test_remote();
382+ }
383+ )M" ,
384+ " -Wl,--just-symbols=storage.syms" );
385+
386+ static bool is_waiting = false ;
387+ tinykvm::Machine::install_unhandled_syscall_handler (
388+ [] (tinykvm::vCPU& cpu, unsigned syscall_number) {
389+ switch (syscall_number) {
390+ case 0x10001 : { // remote_resume
391+ // Remember buffer address and length values
392+ const uint64_t src = cpu.registers ().rdi ;
393+ const uint64_t len = cpu.registers ().rsi ;
394+
395+ cpu.machine ().ipre_remote_resume_now (false ,
396+ [src, len] (tinykvm::Machine& m) {
397+ m.copy_to_guest (m.registers ().rdi , &src, sizeof (src));
398+ m.registers ().rax = len;
399+ });
400+ return ;
401+ }
402+ case 0x10002 : // wait_for_storage_task_paused
403+ cpu.stop ();
404+ is_waiting = true ;
405+ return ;
406+ }
407+ throw std::runtime_error (" Unhandled syscall in remote resume test: " + std::to_string (syscall_number));
408+ });
409+ tinykvm::Machine storage { storage_binary.second , {
410+ .max_mem = 16ULL << 20 , // MB
411+ .vmem_base_address = 1ULL << 30 , // 1GB
412+ } };
413+ storage.setup_linux ({" storage" }, env);
414+ storage.run (4 .0f );
415+ storage.registers ().rip += 2 ; // Skip OUT instruction
416+ REQUIRE (is_waiting);
417+
418+ tinykvm::Machine machine { main_binary.second , {
419+ .max_mem = MAX_MEMORY
420+ } };
421+ machine.setup_linux ({" main" }, env);
422+
423+ machine.remote_connect (storage);
424+ REQUIRE (machine.has_remote ());
425+
426+ machine.run (4 .0f );
427+ REQUIRE (machine.return_value () == 2345 );
428+ REQUIRE (!machine.is_remote_connected ());
429+ REQUIRE (machine.remote_connection_count () == 100 );
430+
431+ // Create a fork
432+ machine.prepare_copy_on_write ();
433+ tinykvm::Machine fork (machine, {
434+ .max_mem = MAX_MEMORY,
435+ .max_cow_mem = MAX_COWMEM,
436+ .split_hugepages = true
437+ });
438+ REQUIRE (fork.has_remote ());
439+
440+ // Test remote resume
441+ fork.vmcall (" test_remote" );
442+ REQUIRE (fork.return_value () == 2345 );
443+ REQUIRE (!fork.is_remote_connected ());
444+ REQUIRE (fork.remote_connection_count () == 100 );
445+ }
0 commit comments