X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/fc0bdd995ca36a34cc576ee706df239dd5ff79a9..104981d52b63dc3d68f39d4442881c667f44bbb9:/hw/acpi.c diff --git a/hw/acpi.c b/hw/acpi.c index ad8f4c79dd..f7950be267 100644 --- a/hw/acpi.c +++ b/hw/acpi.c @@ -14,771 +14,465 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. */ +#include "sysemu.h" #include "hw.h" #include "pc.h" -#include "pm_smbus.h" -#include "pci.h" -#include "qemu-timer.h" -#include "sysemu.h" -#include "i2c.h" -#include "smbus.h" - -//#define DEBUG - -/* i82731AB (PIIX4) compatible power management function */ -#define PM_FREQ 3579545 - -#define ACPI_DBG_IO_ADDR 0xb044 - -typedef struct PIIX4PMState { - PCIDevice dev; - uint16_t pmsts; - uint16_t pmen; - uint16_t pmcntrl; - uint8_t apmc; - uint8_t apms; - QEMUTimer *tmr_timer; - int64_t tmr_overflow_time; - - PMSMBus smb; +#include "acpi.h" +#include "monitor.h" - qemu_irq irq; - qemu_irq cmos_s3; - qemu_irq smi_irq; - int kvm_enabled; -} PIIX4PMState; - -#define RSM_STS (1 << 15) -#define PWRBTN_STS (1 << 8) -#define RTC_EN (1 << 10) -#define PWRBTN_EN (1 << 8) -#define GBL_EN (1 << 5) -#define TMROF_EN (1 << 0) - -#define SCI_EN (1 << 0) - -#define SUS_EN (1 << 13) +struct acpi_table_header { + uint16_t _length; /* our length, not actual part of the hdr */ + /* XXX why we have 2 length fields here? */ + char sig[4]; /* ACPI signature (4 ASCII characters) */ + uint32_t length; /* Length of table, in bytes, including header */ + uint8_t revision; /* ACPI Specification minor version # */ + uint8_t checksum; /* To make sum of entire table == 0 */ + char oem_id[6]; /* OEM identification */ + char oem_table_id[8]; /* OEM table identification */ + uint32_t oem_revision; /* OEM revision number */ + char asl_compiler_id[4]; /* ASL compiler vendor ID */ + uint32_t asl_compiler_revision; /* ASL compiler revision number */ +} QEMU_PACKED; -#define ACPI_ENABLE 0xf1 -#define ACPI_DISABLE 0xf0 +#define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header) +#define ACPI_TABLE_PFX_SIZE sizeof(uint16_t) /* size of the extra prefix */ -static PIIX4PMState *pm_state; +static const char dfl_hdr[ACPI_TABLE_HDR_SIZE] = + "\0\0" /* fake _length (2) */ + "QEMU\0\0\0\0\1\0" /* sig (4), len(4), revno (1), csum (1) */ + "QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */ + "QEMU\1\0\0\0" /* ASL compiler ID (4), version (4) */ + ; -static uint32_t get_pmtmr(PIIX4PMState *s) -{ - uint32_t d; - d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, get_ticks_per_sec()); - return d & 0xffffff; -} +char *acpi_tables; +size_t acpi_tables_len; -static int get_pmsts(PIIX4PMState *s) +static int acpi_checksum(const uint8_t *data, int len) { - int64_t d; - - d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, get_ticks_per_sec()); - if (d >= s->tmr_overflow_time) - s->pmsts |= TMROF_EN; - return s->pmsts; + int sum, i; + sum = 0; + for (i = 0; i < len; i++) { + sum += data[i]; + } + return (-sum) & 0xff; } -static void pm_update_sci(PIIX4PMState *s) +/* like strncpy() but zero-fills the tail of destination */ +static void strzcpy(char *dst, const char *src, size_t size) { - int sci_level, pmsts; - int64_t expire_time; - - pmsts = get_pmsts(s); - sci_level = (((pmsts & s->pmen) & - (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0); - qemu_set_irq(s->irq, sci_level); - /* schedule a timer interruption if needed */ - if ((s->pmen & TMROF_EN) && !(pmsts & TMROF_EN)) { - expire_time = muldiv64(s->tmr_overflow_time, get_ticks_per_sec(), PM_FREQ); - qemu_mod_timer(s->tmr_timer, expire_time); + size_t len = strlen(src); + if (len >= size) { + len = size; } else { - qemu_del_timer(s->tmr_timer); + memset(dst + len, 0, size - len); } + memcpy(dst, src, len); } -static void pm_tmr_timer(void *opaque) -{ - PIIX4PMState *s = opaque; - pm_update_sci(s); -} - -static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) +/* XXX fixme: this function uses obsolete argument parsing interface */ +int acpi_table_add(const char *t) { - PIIX4PMState *s = opaque; - addr &= 0x3f; - switch(addr) { - case 0x00: - { - int64_t d; - int pmsts; - pmsts = get_pmsts(s); - if (pmsts & val & TMROF_EN) { - /* if TMRSTS is reset, then compute the new overflow time */ - d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, - get_ticks_per_sec()); - s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL; - } - s->pmsts &= ~val; - pm_update_sci(s); - } + char buf[1024], *p, *f; + unsigned long val; + size_t len, start, allen; + bool has_header; + int changed; + int r; + struct acpi_table_header hdr; + + r = 0; + r |= get_param_value(buf, sizeof(buf), "data", t) ? 1 : 0; + r |= get_param_value(buf, sizeof(buf), "file", t) ? 2 : 0; + switch (r) { + case 0: + buf[0] = '\0'; + /* fallthrough for default behavior */ + case 1: + has_header = false; break; - case 0x02: - s->pmen = val; - pm_update_sci(s); - break; - case 0x04: - { - int sus_typ; - s->pmcntrl = val & ~(SUS_EN); - if (val & SUS_EN) { - /* change suspend type */ - sus_typ = (val >> 10) & 7; - switch(sus_typ) { - case 0: /* soft power off */ - qemu_system_shutdown_request(); - break; - case 1: - /* RSM_STS should be set on resume. Pretend that resume - was caused by power button */ - s->pmsts |= (RSM_STS | PWRBTN_STS); - qemu_system_reset_request(); - if (s->cmos_s3) { - qemu_irq_raise(s->cmos_s3); - } - default: - break; - } - } - } + case 2: + has_header = true; break; default: - break; + fprintf(stderr, "acpitable: both data and file are specified\n"); + return -1; } -#ifdef DEBUG - printf("PM writew port=0x%04x val=0x%04x\n", addr, val); -#endif -} -static uint32_t pm_ioport_readw(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - uint32_t val; - - addr &= 0x3f; - switch(addr) { - case 0x00: - val = get_pmsts(s); - break; - case 0x02: - val = s->pmen; - break; - case 0x04: - val = s->pmcntrl; - break; - default: - val = 0; - break; + if (!acpi_tables) { + allen = sizeof(uint16_t); + acpi_tables = g_malloc0(allen); + } else { + allen = acpi_tables_len; } -#ifdef DEBUG - printf("PM readw port=0x%04x val=0x%04x\n", addr, val); -#endif - return val; -} -static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val) -{ - // PIIX4PMState *s = opaque; -#ifdef DEBUG - addr &= 0x3f; - printf("PM writel port=0x%04x val=0x%08x\n", addr, val); -#endif -} + start = allen; + acpi_tables = g_realloc(acpi_tables, start + ACPI_TABLE_HDR_SIZE); + allen += has_header ? ACPI_TABLE_PFX_SIZE : ACPI_TABLE_HDR_SIZE; -static uint32_t pm_ioport_readl(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - uint32_t val; + /* now read in the data files, reallocating buffer as needed */ - addr &= 0x3f; - switch(addr) { - case 0x08: - val = get_pmtmr(s); - break; - default: - val = 0; - break; - } -#ifdef DEBUG - printf("PM readl port=0x%04x val=0x%08x\n", addr, val); -#endif - return val; -} + for (f = strtok(buf, ":"); f; f = strtok(NULL, ":")) { + int fd = open(f, O_RDONLY); -static void pm_smi_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - PIIX4PMState *s = opaque; - addr &= 1; -#ifdef DEBUG - printf("pm_smi_writeb addr=0x%x val=0x%02x\n", addr, val); -#endif - if (addr == 0) { - s->apmc = val; - - /* ACPI specs 3.0, 4.7.2.5 */ - if (val == ACPI_ENABLE) { - s->pmcntrl |= SCI_EN; - } else if (val == ACPI_DISABLE) { - s->pmcntrl &= ~SCI_EN; + if (fd < 0) { + fprintf(stderr, "can't open file %s: %s\n", f, strerror(errno)); + return -1; } - if (s->dev.config[0x5b] & (1 << 1)) { - if (s->smi_irq) { - qemu_irq_raise(s->smi_irq); + for (;;) { + char data[8192]; + r = read(fd, data, sizeof(data)); + if (r == 0) { + break; + } else if (r > 0) { + acpi_tables = g_realloc(acpi_tables, allen + r); + memcpy(acpi_tables + allen, data, r); + allen += r; + } else if (errno != EINTR) { + fprintf(stderr, "can't read file %s: %s\n", + f, strerror(errno)); + close(fd); + return -1; } } - } else { - s->apms = val; - } -} -static uint32_t pm_smi_readb(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - uint32_t val; - - addr &= 1; - if (addr == 0) { - val = s->apmc; - } else { - val = s->apms; + close(fd); } -#ifdef DEBUG - printf("pm_smi_readb addr=0x%x val=0x%02x\n", addr, val); -#endif - return val; -} -static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val) -{ -#if defined(DEBUG) - printf("ACPI: DBG: 0x%08x\n", val); -#endif -} + /* now fill in the header fields */ -static void pm_io_space_update(PIIX4PMState *s) -{ - uint32_t pm_io_base; - - if (s->dev.config[0x80] & 1) { - pm_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x40)); - pm_io_base &= 0xffc0; - - /* XXX: need to improve memory and ioport allocation */ -#if defined(DEBUG) - printf("PM: mapping to 0x%x\n", pm_io_base); -#endif - register_ioport_write(pm_io_base, 64, 2, pm_ioport_writew, s); - register_ioport_read(pm_io_base, 64, 2, pm_ioport_readw, s); - register_ioport_write(pm_io_base, 64, 4, pm_ioport_writel, s); - register_ioport_read(pm_io_base, 64, 4, pm_ioport_readl, s); - } -} + f = acpi_tables + start; /* start of the table */ + changed = 0; -static void pm_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(d, address, val, len); - if (range_covers_byte(address, len, 0x80)) - pm_io_space_update((PIIX4PMState *)d); -} + /* copy the header to temp place to align the fields */ + memcpy(&hdr, has_header ? f : dfl_hdr, ACPI_TABLE_HDR_SIZE); -static int vmstate_acpi_post_load(void *opaque, int version_id) -{ - PIIX4PMState *s = opaque; + /* length of the table minus our prefix */ + len = allen - start - ACPI_TABLE_PFX_SIZE; - pm_io_space_update(s); - return 0; -} + hdr._length = cpu_to_le16(len); -static const VMStateDescription vmstate_acpi = { - .name = "piix4_pm", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .post_load = vmstate_acpi_post_load, - .fields = (VMStateField []) { - VMSTATE_PCI_DEVICE(dev, PIIX4PMState), - VMSTATE_UINT16(pmsts, PIIX4PMState), - VMSTATE_UINT16(pmen, PIIX4PMState), - VMSTATE_UINT16(pmcntrl, PIIX4PMState), - VMSTATE_UINT8(apmc, PIIX4PMState), - VMSTATE_UINT8(apms, PIIX4PMState), - VMSTATE_TIMER(tmr_timer, PIIX4PMState), - VMSTATE_INT64(tmr_overflow_time, PIIX4PMState), - VMSTATE_END_OF_LIST() + if (get_param_value(buf, sizeof(buf), "sig", t)) { + strzcpy(hdr.sig, buf, sizeof(hdr.sig)); + ++changed; } -}; -static void piix4_reset(void *opaque) -{ - PIIX4PMState *s = opaque; - uint8_t *pci_conf = s->dev.config; + /* length of the table including header, in bytes */ + if (has_header) { + /* check if actual length is correct */ + val = le32_to_cpu(hdr.length); + if (val != len) { + fprintf(stderr, + "warning: acpitable has wrong length," + " header says %lu, actual size %zu bytes\n", + val, len); + ++changed; + } + } + /* we may avoid putting length here if has_header is true */ + hdr.length = cpu_to_le32(len); - pci_conf[0x58] = 0; - pci_conf[0x59] = 0; - pci_conf[0x5a] = 0; - pci_conf[0x5b] = 0; + if (get_param_value(buf, sizeof(buf), "rev", t)) { + val = strtoul(buf, &p, 0); + if (val > 255 || *p) { + fprintf(stderr, "acpitable: \"rev=%s\" is invalid\n", buf); + return -1; + } + hdr.revision = (uint8_t)val; + ++changed; + } - if (s->kvm_enabled) { - /* Mark SMM as already inited (until KVM supports SMM). */ - pci_conf[0x5B] = 0x02; + if (get_param_value(buf, sizeof(buf), "oem_id", t)) { + strzcpy(hdr.oem_id, buf, sizeof(hdr.oem_id)); + ++changed; } -} -static void piix4_powerdown(void *opaque, int irq, int power_failing) -{ - PIIX4PMState *s = opaque; + if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) { + strzcpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id)); + ++changed; + } - if (!s) { - qemu_system_shutdown_request(); - } else if (s->pmen & PWRBTN_EN) { - s->pmsts |= PWRBTN_EN; - pm_update_sci(s); + if (get_param_value(buf, sizeof(buf), "oem_rev", t)) { + val = strtol(buf, &p, 0); + if (*p) { + fprintf(stderr, "acpitable: \"oem_rev=%s\" is invalid\n", buf); + return -1; + } + hdr.oem_revision = cpu_to_le32(val); + ++changed; } -} -i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, - qemu_irq sci_irq, qemu_irq cmos_s3, qemu_irq smi_irq, - int kvm_enabled) -{ - PIIX4PMState *s; - uint8_t *pci_conf; - - s = (PIIX4PMState *)pci_register_device(bus, - "PM", sizeof(PIIX4PMState), - devfn, NULL, pm_write_config); - pm_state = s; - pci_conf = s->dev.config; - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_3); - pci_conf[0x06] = 0x80; - pci_conf[0x07] = 0x02; - pci_conf[0x08] = 0x03; // revision number - pci_conf[0x09] = 0x00; - pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER); - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type - pci_conf[0x3d] = 0x01; // interrupt pin 1 - - pci_conf[0x40] = 0x01; /* PM io base read only bit */ - - register_ioport_write(0xb2, 2, 1, pm_smi_writeb, s); - register_ioport_read(0xb2, 2, 1, pm_smi_readb, s); - - register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, s); - - s->kvm_enabled = kvm_enabled; - if (s->kvm_enabled) { - /* Mark SMM as already inited to prevent SMM from running. KVM does not - * support SMM mode. */ - pci_conf[0x5B] = 0x02; + if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) { + strzcpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id)); + ++changed; } - /* XXX: which specification is used ? The i82731AB has different - mappings */ - pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10; - pci_conf[0x63] = 0x60; - pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) | - (serial_hds[1] != NULL ? 0x90 : 0); + if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) { + val = strtol(buf, &p, 0); + if (*p) { + fprintf(stderr, "acpitable: \"%s=%s\" is invalid\n", + "asl_compiler_rev", buf); + return -1; + } + hdr.asl_compiler_revision = cpu_to_le32(val); + ++changed; + } - pci_conf[0x90] = smb_io_base | 1; - pci_conf[0x91] = smb_io_base >> 8; - pci_conf[0xd2] = 0x09; - register_ioport_write(smb_io_base, 64, 1, smb_ioport_writeb, &s->smb); - register_ioport_read(smb_io_base, 64, 1, smb_ioport_readb, &s->smb); + if (!has_header && !changed) { + fprintf(stderr, "warning: acpitable: no table headers are specified\n"); + } - s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s); - qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1); + /* now calculate checksum of the table, complete with the header */ + /* we may as well leave checksum intact if has_header is true */ + /* alternatively there may be a way to set cksum to a given value */ + hdr.checksum = 0; /* for checksum calculation */ - vmstate_register(0, &vmstate_acpi, s); + /* put header back */ + memcpy(f, &hdr, sizeof(hdr)); - pm_smbus_init(NULL, &s->smb); - s->irq = sci_irq; - s->cmos_s3 = cmos_s3; - s->smi_irq = smi_irq; - qemu_register_reset(piix4_reset, s); + if (changed || !has_header || 1) { + ((struct acpi_table_header *)f)->checksum = + acpi_checksum((uint8_t *)f + ACPI_TABLE_PFX_SIZE, len); + } - return s->smb.smbus; -} + /* increase number of tables */ + (*(uint16_t *)acpi_tables) = + cpu_to_le32(le32_to_cpu(*(uint16_t *)acpi_tables) + 1); -#define GPE_BASE 0xafe0 -#define PCI_BASE 0xae00 -#define PCI_EJ_BASE 0xae08 + acpi_tables_len = allen; + return 0; -struct gpe_regs { - uint16_t sts; /* status */ - uint16_t en; /* enabled */ -}; +} -struct pci_status { - uint32_t up; - uint32_t down; -}; +static void acpi_notify_wakeup(Notifier *notifier, void *data) +{ + ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup); + WakeupReason *reason = data; -static struct gpe_regs gpe; -static struct pci_status pci0_status; + switch (*reason) { + case QEMU_WAKEUP_REASON_RTC: + ar->pm1.evt.sts |= + (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS); + break; + case QEMU_WAKEUP_REASON_PMTIMER: + ar->pm1.evt.sts |= + (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS); + break; + case QEMU_WAKEUP_REASON_OTHER: + default: + /* ACPI_BITMASK_WAKE_STATUS should be set on resume. + Pretend that resume was caused by power button */ + ar->pm1.evt.sts |= + (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS); + break; + } +} -static uint32_t gpe_read_val(uint16_t val, uint32_t addr) +/* ACPI PM1a EVT */ +uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar) { - if (addr & 1) - return (val >> 8) & 0xff; - return val & 0xff; + int64_t d = acpi_pm_tmr_get_clock(); + if (d >= ar->tmr.overflow_time) { + ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS; + } + return ar->pm1.evt.sts; } -static uint32_t gpe_readb(void *opaque, uint32_t addr) +void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val) { - uint32_t val = 0; - struct gpe_regs *g = opaque; - switch (addr) { - case GPE_BASE: - case GPE_BASE + 1: - val = gpe_read_val(g->sts, addr); - break; - case GPE_BASE + 2: - case GPE_BASE + 3: - val = gpe_read_val(g->en, addr); - break; - default: - break; + uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar); + if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) { + /* if TMRSTS is reset, then compute the new overflow time */ + acpi_pm_tmr_calc_overflow_time(ar); } - -#if defined(DEBUG) - printf("gpe read %x == %x\n", addr, val); -#endif - return val; + ar->pm1.evt.sts &= ~val; } -static void gpe_write_val(uint16_t *cur, int addr, uint32_t val) +void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val) { - if (addr & 1) - *cur = (*cur & 0xff) | (val << 8); - else - *cur = (*cur & 0xff00) | (val & 0xff); + ar->pm1.evt.en = val; + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, + val & ACPI_BITMASK_RT_CLOCK_ENABLE); + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, + val & ACPI_BITMASK_TIMER_ENABLE); } -static void gpe_reset_val(uint16_t *cur, int addr, uint32_t val) +void acpi_pm1_evt_power_down(ACPIREGS *ar) { - uint16_t x1, x0 = val & 0xff; - int shift = (addr & 1) ? 8 : 0; - - x1 = (*cur >> shift) & 0xff; - - x1 = x1 & ~x0; - - *cur = (*cur & (0xff << (8 - shift))) | (x1 << shift); + if (ar->pm1.evt.en & ACPI_BITMASK_POWER_BUTTON_ENABLE) { + ar->pm1.evt.sts |= ACPI_BITMASK_POWER_BUTTON_STATUS; + ar->tmr.update_sci(ar); + } } -static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val) +void acpi_pm1_evt_reset(ACPIREGS *ar) { - struct gpe_regs *g = opaque; - switch (addr) { - case GPE_BASE: - case GPE_BASE + 1: - gpe_reset_val(&g->sts, addr, val); - break; - case GPE_BASE + 2: - case GPE_BASE + 3: - gpe_write_val(&g->en, addr, val); - break; - default: - break; - } - -#if defined(DEBUG) - printf("gpe write %x <== %d\n", addr, val); -#endif + ar->pm1.evt.sts = 0; + ar->pm1.evt.en = 0; + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, 0); + qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, 0); } -static uint32_t pcihotplug_read(void *opaque, uint32_t addr) +/* ACPI PM_TMR */ +void acpi_pm_tmr_update(ACPIREGS *ar, bool enable) { - uint32_t val = 0; - struct pci_status *g = opaque; - switch (addr) { - case PCI_BASE: - val = g->up; - break; - case PCI_BASE + 4: - val = g->down; - break; - default: - break; - } + int64_t expire_time; -#if defined(DEBUG) - printf("pcihotplug read %x == %x\n", addr, val); -#endif - return val; + /* schedule a timer interruption if needed */ + if (enable) { + expire_time = muldiv64(ar->tmr.overflow_time, get_ticks_per_sec(), + PM_TIMER_FREQUENCY); + qemu_mod_timer(ar->tmr.timer, expire_time); + } else { + qemu_del_timer(ar->tmr.timer); + } } -static void pcihotplug_write(void *opaque, uint32_t addr, uint32_t val) +void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar) { - struct pci_status *g = opaque; - switch (addr) { - case PCI_BASE: - g->up = val; - break; - case PCI_BASE + 4: - g->down = val; - break; - } - -#if defined(DEBUG) - printf("pcihotplug write %x <== %d\n", addr, val); -#endif + int64_t d = acpi_pm_tmr_get_clock(); + ar->tmr.overflow_time = (d + 0x800000LL) & ~0x7fffffLL; } -static uint32_t pciej_read(void *opaque, uint32_t addr) +uint32_t acpi_pm_tmr_get(ACPIREGS *ar) { -#if defined(DEBUG) - printf("pciej read %x\n", addr); -#endif - return 0; + uint32_t d = acpi_pm_tmr_get_clock(); + return d & 0xffffff; } -static void pciej_write(void *opaque, uint32_t addr, uint32_t val) +static void acpi_pm_tmr_timer(void *opaque) { - BusState *bus = opaque; - DeviceState *qdev, *next; - PCIDevice *dev; - int slot = ffs(val) - 1; - - QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) { - dev = DO_UPCAST(PCIDevice, qdev, qdev); - if (PCI_SLOT(dev->devfn) == slot) { - qdev_free(qdev); - } - } - - -#if defined(DEBUG) - printf("pciej write %x <== %d\n", addr, val); -#endif + ACPIREGS *ar = opaque; + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER); + ar->tmr.update_sci(ar); } -static int piix4_device_hotplug(PCIDevice *dev, int state); - -void piix4_acpi_system_hot_add_init(PCIBus *bus) +void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci) { - register_ioport_write(GPE_BASE, 4, 1, gpe_writeb, &gpe); - register_ioport_read(GPE_BASE, 4, 1, gpe_readb, &gpe); - - register_ioport_write(PCI_BASE, 8, 4, pcihotplug_write, &pci0_status); - register_ioport_read(PCI_BASE, 8, 4, pcihotplug_read, &pci0_status); - - register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, bus); - register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, bus); - - pci_bus_hotplug(bus, piix4_device_hotplug); + ar->tmr.update_sci = update_sci; + ar->tmr.timer = qemu_new_timer_ns(vm_clock, acpi_pm_tmr_timer, ar); } -static void enable_device(struct pci_status *p, struct gpe_regs *g, int slot) +void acpi_pm_tmr_reset(ACPIREGS *ar) { - g->sts |= 2; - p->up |= (1 << slot); + ar->tmr.overflow_time = 0; + qemu_del_timer(ar->tmr.timer); } -static void disable_device(struct pci_status *p, struct gpe_regs *g, int slot) +/* ACPI PM1aCNT */ +void acpi_pm1_cnt_init(ACPIREGS *ar) { - g->sts |= 2; - p->down |= (1 << slot); + ar->wakeup.notify = acpi_notify_wakeup; + qemu_register_wakeup_notifier(&ar->wakeup); } -static int piix4_device_hotplug(PCIDevice *dev, int state) +void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val, char s4) { - int slot = PCI_SLOT(dev->devfn); - - pci0_status.up = 0; - pci0_status.down = 0; - if (state) - enable_device(&pci0_status, &gpe, slot); - else - disable_device(&pci0_status, &gpe, slot); - if (gpe.en & 2) { - qemu_set_irq(pm_state->irq, 1); - qemu_set_irq(pm_state->irq, 0); + ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE); + + if (val & ACPI_BITMASK_SLEEP_ENABLE) { + /* change suspend type */ + uint16_t sus_typ = (val >> 10) & 7; + switch(sus_typ) { + case 0: /* soft power off */ + qemu_system_shutdown_request(); + break; + case 1: + qemu_system_suspend_request(); + break; + default: + if (sus_typ == s4) { /* S4 request */ + monitor_protocol_event(QEVENT_SUSPEND_DISK, NULL); + qemu_system_shutdown_request(); + } + break; + } } - return 0; } -struct acpi_table_header +void acpi_pm1_cnt_update(ACPIREGS *ar, + bool sci_enable, bool sci_disable) { - char signature [4]; /* ACPI signature (4 ASCII characters) */ - uint32_t length; /* Length of table, in bytes, including header */ - uint8_t revision; /* ACPI Specification minor version # */ - uint8_t checksum; /* To make sum of entire table == 0 */ - char oem_id [6]; /* OEM identification */ - char oem_table_id [8]; /* OEM table identification */ - uint32_t oem_revision; /* OEM revision number */ - char asl_compiler_id [4]; /* ASL compiler vendor ID */ - uint32_t asl_compiler_revision; /* ASL compiler revision number */ -} __attribute__((packed)); + /* ACPI specs 3.0, 4.7.2.5 */ + if (sci_enable) { + ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE; + } else if (sci_disable) { + ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE; + } +} -char *acpi_tables; -size_t acpi_tables_len; +void acpi_pm1_cnt_reset(ACPIREGS *ar) +{ + ar->pm1.cnt.cnt = 0; +} -static int acpi_checksum(const uint8_t *data, int len) +/* ACPI GPE */ +void acpi_gpe_init(ACPIREGS *ar, uint8_t len) { - int sum, i; - sum = 0; - for(i = 0; i < len; i++) - sum += data[i]; - return (-sum) & 0xff; + ar->gpe.len = len; + ar->gpe.sts = g_malloc0(len / 2); + ar->gpe.en = g_malloc0(len / 2); } -int acpi_table_add(const char *t) +void acpi_gpe_blk(ACPIREGS *ar, uint32_t blk) { - static const char *dfl_id = "QEMUQEMU"; - char buf[1024], *p, *f; - struct acpi_table_header acpi_hdr; - unsigned long val; - size_t off; + ar->gpe.blk = blk; +} - memset(&acpi_hdr, 0, sizeof(acpi_hdr)); - - if (get_param_value(buf, sizeof(buf), "sig", t)) { - strncpy(acpi_hdr.signature, buf, 4); - } else { - strncpy(acpi_hdr.signature, dfl_id, 4); - } - if (get_param_value(buf, sizeof(buf), "rev", t)) { - val = strtoul(buf, &p, 10); - if (val > 255 || *p != '\0') - goto out; - } else { - val = 1; - } - acpi_hdr.revision = (int8_t)val; +void acpi_gpe_reset(ACPIREGS *ar) +{ + memset(ar->gpe.sts, 0, ar->gpe.len / 2); + memset(ar->gpe.en, 0, ar->gpe.len / 2); +} - if (get_param_value(buf, sizeof(buf), "oem_id", t)) { - strncpy(acpi_hdr.oem_id, buf, 6); - } else { - strncpy(acpi_hdr.oem_id, dfl_id, 6); - } +static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr) +{ + uint8_t *cur = NULL; - if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) { - strncpy(acpi_hdr.oem_table_id, buf, 8); + if (addr < ar->gpe.len / 2) { + cur = ar->gpe.sts + addr; + } else if (addr < ar->gpe.len) { + cur = ar->gpe.en + addr - ar->gpe.len / 2; } else { - strncpy(acpi_hdr.oem_table_id, dfl_id, 8); + abort(); } - if (get_param_value(buf, sizeof(buf), "oem_rev", t)) { - val = strtol(buf, &p, 10); - if(*p != '\0') - goto out; - } else { - val = 1; - } - acpi_hdr.oem_revision = cpu_to_le32(val); + return cur; +} - if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) { - strncpy(acpi_hdr.asl_compiler_id, buf, 4); - } else { - strncpy(acpi_hdr.asl_compiler_id, dfl_id, 4); - } +void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val) +{ + uint8_t *cur; - if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) { - val = strtol(buf, &p, 10); - if(*p != '\0') - goto out; + addr -= ar->gpe.blk; + cur = acpi_gpe_ioport_get_ptr(ar, addr); + if (addr < ar->gpe.len / 2) { + /* GPE_STS */ + *cur = (*cur) & ~val; + } else if (addr < ar->gpe.len) { + /* GPE_EN */ + *cur = val; } else { - val = 1; - } - acpi_hdr.asl_compiler_revision = cpu_to_le32(val); - - if (!get_param_value(buf, sizeof(buf), "data", t)) { - buf[0] = '\0'; - } - - acpi_hdr.length = sizeof(acpi_hdr); - - f = buf; - while (buf[0]) { - struct stat s; - char *n = strchr(f, ':'); - if (n) - *n = '\0'; - if(stat(f, &s) < 0) { - fprintf(stderr, "Can't stat file '%s': %s\n", f, strerror(errno)); - goto out; - } - acpi_hdr.length += s.st_size; - if (!n) - break; - *n = ':'; - f = n + 1; - } - - if (!acpi_tables) { - acpi_tables_len = sizeof(uint16_t); - acpi_tables = qemu_mallocz(acpi_tables_len); + abort(); } - p = acpi_tables + acpi_tables_len; - acpi_tables_len += sizeof(uint16_t) + acpi_hdr.length; - acpi_tables = qemu_realloc(acpi_tables, acpi_tables_len); - - acpi_hdr.length = cpu_to_le32(acpi_hdr.length); - *(uint16_t*)p = acpi_hdr.length; - p += sizeof(uint16_t); - memcpy(p, &acpi_hdr, sizeof(acpi_hdr)); - off = sizeof(acpi_hdr); - - f = buf; - while (buf[0]) { - struct stat s; - int fd; - char *n = strchr(f, ':'); - if (n) - *n = '\0'; - fd = open(f, O_RDONLY); - - if(fd < 0) - goto out; - if(fstat(fd, &s) < 0) { - close(fd); - goto out; - } +} - do { - int r; - r = read(fd, p + off, s.st_size); - if (r > 0) { - off += r; - s.st_size -= r; - } else if ((r < 0 && errno != EINTR) || r == 0) { - close(fd); - goto out; - } - } while(s.st_size); +uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr) +{ + uint8_t *cur; + uint32_t val; - close(fd); - if (!n) - break; - f = n + 1; + addr -= ar->gpe.blk; + cur = acpi_gpe_ioport_get_ptr(ar, addr); + val = 0; + if (cur != NULL) { + val = *cur; } - ((struct acpi_table_header*)p)->checksum = acpi_checksum((uint8_t*)p, off); - /* increase number of tables */ - (*(uint16_t*)acpi_tables) = - cpu_to_le32(le32_to_cpu(*(uint16_t*)acpi_tables) + 1); - return 0; -out: - if (acpi_tables) { - qemu_free(acpi_tables); - acpi_tables = NULL; - } - return -1; + return val; }