Hi,
This patch series (and the seabios patch series following in a minute) improve s3 support in qemu.
The patches allocates a bunch of acpi gpe bits for s3 wakeup of piix chipset devices (ps/2 keyboard, ps/2 mouse, serial port, uhci controller) and does the windup needed so the guest can enable+disable wakeup per device using apci.
With this patch series applied /proc/acpi/wakeup inside the guest looks like this ...
Device S-state Status Sysfs node UHCI S3 *enabled pci:0000:00:01.2 KBD S3 *enabled pnp:00:02 MOU S3 *disabled pnp:00:03 COM1 S3 *disabled pnp:00:05
... and you can use "echo $device > /proc/acpi/wakeup" to toggle enabled/disabled.
comments? Gerd
Gerd Hoffmann (4): wakeup: add acpi gpe wakeup reasons wakeup: make ps/2 configurable wakeup: make serial configurable wakeup: uhci support
hw/acpi.c | 26 ++++++++++++++++++++++++++ hw/ps2.c | 4 ++-- hw/serial.c | 10 +++++++++- hw/usb/hcd-uhci.c | 35 +++++++++++++++++++++++++++++++++++ sysemu.h | 4 ++++ 5 files changed, 76 insertions(+), 3 deletions(-)
Allocate four acpi gpe bits (0x08 -> 0x0b) for s3 wakeup configuration and notification.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- hw/acpi.c | 26 ++++++++++++++++++++++++++ sysemu.h | 4 ++++ 2 files changed, 30 insertions(+), 0 deletions(-)
diff --git a/hw/acpi.c b/hw/acpi.c index effc7ec..208c4af 100644 --- a/hw/acpi.c +++ b/hw/acpi.c @@ -262,6 +262,22 @@ static void acpi_notify_wakeup(Notifier *notifier, void *data) ar->pm1.evt.sts |= (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS); break; + case QEMU_WAKEUP_REASON_GPE_8: + ar->pm1.evt.sts |= ACPI_BITMASK_WAKE_STATUS; + ar->gpe.sts[1] |= (1 << 0); + break; + case QEMU_WAKEUP_REASON_GPE_9: + ar->pm1.evt.sts |= ACPI_BITMASK_WAKE_STATUS; + ar->gpe.sts[1] |= (1 << 1); + break; + case QEMU_WAKEUP_REASON_GPE_a: + ar->pm1.evt.sts |= ACPI_BITMASK_WAKE_STATUS; + ar->gpe.sts[1] |= (1 << 2); + break; + case QEMU_WAKEUP_REASON_GPE_b: + ar->pm1.evt.sts |= ACPI_BITMASK_WAKE_STATUS; + ar->gpe.sts[1] |= (1 << 3); + break; case QEMU_WAKEUP_REASON_OTHER: default: /* ACPI_BITMASK_WAKE_STATUS should be set on resume. @@ -426,6 +442,10 @@ void acpi_gpe_reset(ACPIREGS *ar) { memset(ar->gpe.sts, 0, ar->gpe.len / 2); memset(ar->gpe.en, 0, ar->gpe.len / 2); + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_GPE_8, 0); + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_GPE_9, 0); + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_GPE_a, 0); + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_GPE_b, 0); }
static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr) @@ -455,6 +475,12 @@ void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val) } else if (addr < ar->gpe.len) { /* GPE_EN */ *cur = val; + if (addr == ar->gpe.len / 2 + 1) { + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_GPE_8, val & (1 << 0)); + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_GPE_9, val & (1 << 1)); + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_GPE_a, val & (1 << 2)); + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_GPE_b, val & (1 << 3)); + } } else { abort(); } diff --git a/sysemu.h b/sysemu.h index 6540c79..8cd3443 100644 --- a/sysemu.h +++ b/sysemu.h @@ -42,6 +42,10 @@ typedef enum WakeupReason { QEMU_WAKEUP_REASON_OTHER = 0, QEMU_WAKEUP_REASON_RTC, QEMU_WAKEUP_REASON_PMTIMER, + QEMU_WAKEUP_REASON_GPE_8, + QEMU_WAKEUP_REASON_GPE_9, + QEMU_WAKEUP_REASON_GPE_a, + QEMU_WAKEUP_REASON_GPE_b, } WakeupReason;
void qemu_system_reset_request(void);
ps/2 gets gpe bits 0x08 (keyboard) and 0x09 (mouse).
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- hw/ps2.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/ps2.c b/hw/ps2.c index f93cd24..a3cd124 100644 --- a/hw/ps2.c +++ b/hw/ps2.c @@ -155,7 +155,7 @@ static void ps2_put_keycode(void *opaque, int keycode) { PS2KbdState *s = opaque;
- qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_GPE_8); /* XXX: add support for scancode set 1 */ if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) { if (keycode & 0x80) { @@ -371,7 +371,7 @@ static void ps2_mouse_event(void *opaque, s->mouse_buttons = buttons_state;
if (buttons_state) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_GPE_9); }
if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
serial port #1 gets gpe 0x0a. Now that the wakeup is guest-configurable via acpi we also enable it unconditionally.
Other serial ports are unchanged: they continue to use the "other" exit reason and are disabled unless explicitly enabled via wakeup property.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- hw/serial.c | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-)
diff --git a/hw/serial.c b/hw/serial.c index a421d1e..06f7e28 100644 --- a/hw/serial.c +++ b/hw/serial.c @@ -140,6 +140,7 @@ struct SerialState { int baudbase; int tsr_retry; uint32_t wakeup; + WakeupReason reason;
uint64_t last_xmit_ts; /* Time when the last byte was successfully sent out of the tsr */ SerialFIFO recv_fifo; @@ -641,7 +642,7 @@ static void serial_receive1(void *opaque, const uint8_t *buf, int size) SerialState *s = opaque;
if (s->wakeup) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + qemu_system_wakeup_request(s->reason); } if(s->fcr & UART_FCR_FE) { int i; @@ -789,6 +790,13 @@ static int serial_isa_initfn(ISADevice *dev) isa->isairq = isa_serial_irq[isa->index]; index++;
+ if (isa->iobase == 0x3f8) { + s->reason = QEMU_WAKEUP_REASON_GPE_a; + s->wakeup = 1; + } else { + s->reason = QEMU_WAKEUP_REASON_OTHER; + } + s->baudbase = 115200; isa_init_irq(dev, &s->irq, isa->isairq); serial_init_core(s);
Implement the (intel-specific) pci configuration register 0xc4, which is a bitmask saying which ports are allowed to wakeup the system.
Also assign gpe bit 0x0b (used only in case uhci handles device 01.2).
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- hw/usb/hcd-uhci.c | 35 +++++++++++++++++++++++++++++++++++ 1 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 8f652d2..b2da21d 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -32,6 +32,7 @@ #include "iov.h" #include "dma.h" #include "trace.h" +#include "sysemu.h"
//#define DEBUG //#define DEBUG_DUMP_DATA @@ -129,6 +130,7 @@ struct UHCIState { uint32_t fl_base_addr; /* frame list base address */ uint8_t sof_timing; uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ + uint8_t system_wakeup; int64_t expire_time; QEMUTimer *frame_timer; QEMUBH *bh; @@ -665,6 +667,22 @@ static void uhci_wakeup(USBPort *port1) } }
+static void uhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) +{ + USBPort *port1 = ep->dev->port; + UHCIState *s = port1->opaque; + + if (!(s->system_wakeup & (1 << port1->index))) { + return; + } + if (s->dev.devfn == PCI_DEVFN(1, 2)) { + /* piix3/4 chipset uhci controller */ + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_GPE_b); + } else { + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + } +} + static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr) { USBDevice *dev; @@ -1181,6 +1199,7 @@ static USBPortOps uhci_port_ops = { };
static USBBusOps uhci_bus_ops = { + .wakeup_endpoint = uhci_wakeup_endpoint, };
static int usb_uhci_common_initfn(PCIDevice *dev) @@ -1242,6 +1261,17 @@ static int usb_uhci_common_initfn(PCIDevice *dev) return 0; }
+static void usb_uhci_intel_write_config(PCIDevice *dev, uint32_t addr, + uint32_t val, int len) +{ + UHCIState *s = DO_UPCAST(UHCIState, dev, dev); + + pci_default_write_config(dev, addr, val, len); + if (addr == 0xc4) { + s->system_wakeup = val; + } +} + static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) { UHCIState *s = DO_UPCAST(UHCIState, dev, dev); @@ -1279,6 +1309,7 @@ static void piix3_uhci_class_init(ObjectClass *klass, void *data)
k->init = usb_uhci_common_initfn; k->exit = usb_uhci_exit; + k->config_write = usb_uhci_intel_write_config; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82371SB_2; k->revision = 0x01; @@ -1301,6 +1332,7 @@ static void piix4_uhci_class_init(ObjectClass *klass, void *data)
k->init = usb_uhci_common_initfn; k->exit = usb_uhci_exit; + k->config_write = usb_uhci_intel_write_config; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82371AB_2; k->revision = 0x01; @@ -1344,6 +1376,7 @@ static void ich9_uhci1_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->init = usb_uhci_common_initfn; + k->config_write = usb_uhci_intel_write_config; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1; k->revision = 0x03; @@ -1365,6 +1398,7 @@ static void ich9_uhci2_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->init = usb_uhci_common_initfn; + k->config_write = usb_uhci_intel_write_config; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2; k->revision = 0x03; @@ -1386,6 +1420,7 @@ static void ich9_uhci3_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->init = usb_uhci_common_initfn; + k->config_write = usb_uhci_intel_write_config; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3; k->revision = 0x03;