diff --git a/devicemodel/arch/x86/pm.c b/devicemodel/arch/x86/pm.c index 48d52c982..a444078e9 100644 --- a/devicemodel/arch/x86/pm.c +++ b/devicemodel/arch/x86/pm.c @@ -256,6 +256,11 @@ pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, error = vm_suspend(ctx, VM_SUSPEND_POWEROFF); assert(error == 0 || errno == EALREADY); } + + if ((pm1_control & PM1_SLP_TYP) >> 10 == 3) { + error = vm_suspend(ctx, VM_SUSPEND_SUSPEND); + assert(error == 0 || errno == EALREADY); + } } } return 0; diff --git a/devicemodel/core/main.c b/devicemodel/core/main.c index dfc639a7b..a9affc9e1 100644 --- a/devicemodel/core/main.c +++ b/devicemodel/core/main.c @@ -59,6 +59,7 @@ #include "sw_load.h" #include "monitor.h" #include "ioc.h" +#include "pm.h" #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ @@ -447,6 +448,22 @@ handle_vmexit(struct vmctx *ctx, struct vhm_request *vhm_req, int vcpu) default: exit(1); } + + /* If UOS is not in suspend or system reset mode, we don't + * need to notify request done. + * + * But there is little difference between reset and suspend: + * - We don't need to notify request done for reset because reset + * doesn't care it. + * - We don't need to notify request done for suspend because + * guest will offline all APs, write PM register to trigger + * PM. Then the PM register writting will trigger the latest + * vmexit and it doesn't care request done either. + */ + if ((VM_SUSPEND_SYSTEM_RESET == vm_get_suspend_mode()) || + (VM_SUSPEND_SUSPEND == vm_get_suspend_mode())) + return; + vm_notify_request_done(ctx, vcpu); } @@ -576,6 +593,39 @@ vm_system_reset(struct vmctx *ctx) vm_set_suspend_mode(VM_SUSPEND_NONE); } +static void +vm_suspend_resume(struct vmctx *ctx) +{ + int vcpu_id = 0; + + /* + * If we get warm reboot request, we don't want to exit the + * vcpu_loop/vm_loop/mevent_loop. So we do: + * 1. pause VM + * 2. notify request done to reset ioreq state in vhm + * 3. stop vm watchdog + * 4. wait for resume signal + * 5. reset vm watchdog + * 6. hypercall restart vm + */ + vm_pause(ctx); + for (vcpu_id = 0; vcpu_id < 4; vcpu_id++) { + struct vhm_request *vhm_req; + + vhm_req = &vhm_req_buf[vcpu_id]; + if (vhm_req->valid && + (vhm_req->processed == REQ_STATE_PROCESSING) && + (vhm_req->client == ctx->ioreq_client)) + vm_notify_request_done(ctx, vcpu_id); + } + + vm_stop_watchdog(ctx); + wait_for_resume(ctx); + + vm_reset_watchdog(ctx); + vm_reset(ctx); +} + static void vm_loop(struct vmctx *ctx) { @@ -606,6 +656,10 @@ vm_loop(struct vmctx *ctx) if (VM_SUSPEND_SYSTEM_RESET == vm_get_suspend_mode()) { vm_system_reset(ctx); } + + if (VM_SUSPEND_SUSPEND == vm_get_suspend_mode()) { + vm_suspend_resume(ctx); + } } quit_vm_loop = 0; printf("VM loop exit\n"); diff --git a/devicemodel/core/mevent.c b/devicemodel/core/mevent.c index 86860a6ce..69f7924e9 100644 --- a/devicemodel/core/mevent.c +++ b/devicemodel/core/mevent.c @@ -316,6 +316,8 @@ mevent_dispatch(void) assert(pipev != NULL); for (;;) { + int suspend_mode; + /* * Block awaiting events */ @@ -328,8 +330,11 @@ mevent_dispatch(void) */ mevent_handle(eventlist, ret); - if ((vm_get_suspend_mode() != VM_SUSPEND_NONE) && - (vm_get_suspend_mode() != VM_SUSPEND_SYSTEM_RESET)) + suspend_mode = vm_get_suspend_mode(); + + if ((suspend_mode != VM_SUSPEND_NONE) && + (suspend_mode != VM_SUSPEND_SYSTEM_RESET) && + (suspend_mode != VM_SUSPEND_SUSPEND)) break; } } diff --git a/devicemodel/hw/platform/acpi/acpi.c b/devicemodel/hw/platform/acpi/acpi.c index 27f7f587a..528f0330e 100644 --- a/devicemodel/hw/platform/acpi/acpi.c +++ b/devicemodel/hw/platform/acpi/acpi.c @@ -744,6 +744,11 @@ basl_fwrite_dsdt(FILE *fp, struct vmctx *ctx) dsdt_line("DefinitionBlock (\"dm_dsdt.aml\", \"DSDT\", 2," "\"DM \", \"DMDSDT \", 0x00000001)"); dsdt_line("{"); + dsdt_line(" Name (_S3, Package ()"); + dsdt_line(" {"); + dsdt_line(" 0x03,"); + dsdt_line(" Zero,"); + dsdt_line(" })"); dsdt_line(" Name (_S5, Package ()"); dsdt_line(" {"); dsdt_line(" 0x05,");