*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
+ *
+ * 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 "apm.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;
-
- APMState apm;
+#include "acpi.h"
+#include "monitor.h"
- QEMUTimer *tmr_timer;
- int64_t tmr_overflow_time;
-
- PMSMBus smb;
-
- 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);
- }
- break;
- case 0x02:
- s->pmen = 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 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;
-}
-
-static void apm_ctrl_changed(uint32_t val, void *arg)
-{
- PIIX4PMState *s = arg;
+ for (f = strtok(buf, ":"); f; f = strtok(NULL, ":")) {
+ int fd = open(f, O_RDONLY);
- /* 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;
+ }
}
+
+ close(fd);
}
-}
-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_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState),
- 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 */
-
- /* APM */
- apm_init(&s->apm, apm_ctrl_changed, 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;
}