aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2013-07-25 09:47:18 +0200
committerGerd Hoffmann <kraxel@redhat.com>2013-08-20 08:55:55 +0200
commit6375ffd667dce57bee62b2f424bf9bef30eb2432 (patch)
tree3e4f1e12d92aea5d3edb2f551668bdb93796f784
parent7d9cbe613694924921ed1a6f8947d711c5832eee (diff)
downloadseabios-6375ffd667dce57bee62b2f424bf9bef30eb2432.zip
seabios-6375ffd667dce57bee62b2f424bf9bef30eb2432.tar.gz
seabios-6375ffd667dce57bee62b2f424bf9bef30eb2432.tar.bz2
apm: fix shutdown
Qemu commit 9ee59f3 removed the bochs bios apm interface emulation at port 0x8900. That broke poweroff via APM. Fix it by powering off the machine using the acpi pm control register. Old code is left in, so seabios wil try both poweroff methods. Cleaning that eventually up is left for another patch, after checking it isn't needed. Qemu never implemented "Standby" and "Suspend", only "Shutdown", so it looks like there might be non-qemu use cases (bochs probably). Easiest way to test this is the syslinux poweroff module; modern linux distros usually have CONFIG_APM turned off. Reported-by: Sebastian Herbszt <herbszt@gmx.de> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> (cherry picked from commit 5b63109242042372c1d6c3d23602e980f1c9637b) Conflicts: src/acpi.c src/pciinit.c
-rw-r--r--src/acpi.c5
-rw-r--r--src/acpi.h1
-rw-r--r--src/apm.c5
-rw-r--r--src/pciinit.c3
4 files changed, 14 insertions, 0 deletions
diff --git a/src/acpi.c b/src/acpi.c
index 3699898..1994838 100644
--- a/src/acpi.c
+++ b/src/acpi.c
@@ -18,6 +18,8 @@
#include "acpi-dsdt.hex"
+u32 acpi_pm1a_cnt VARFSEG;
+
static void
build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev)
{
@@ -730,9 +732,12 @@ find_acpi_features(void)
if (!fadt)
return;
u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk);
+ u32 pm1a_cnt = le32_to_cpu(fadt->pm1a_cnt_blk);
dprintf(4, "pm_tmr_blk=%x\n", pm_tmr);
if (pm_tmr)
pmtimer_setup(pm_tmr, 3579);
+ if (pm1a_cnt)
+ acpi_pm1a_cnt = pm1a_cnt;
// Theoretically we should check the 'reset_reg_sup' flag, but Windows
// doesn't and thus nobody seems to *set* it. If the table is large enough
diff --git a/src/acpi.h b/src/acpi.h
index 5d1e104..f0d24d4 100644
--- a/src/acpi.h
+++ b/src/acpi.h
@@ -36,6 +36,7 @@ struct rsdp_descriptor { /* Root System Descriptor Pointer */
};
extern struct rsdp_descriptor *RsdpAddr;
+extern u32 acpi_pm1a_cnt;
/* Table structure from Linux kernel (the ACPI tables are under the
BSD license) */
diff --git a/src/apm.c b/src/apm.c
index b2eac6d..de6bd99 100644
--- a/src/apm.c
+++ b/src/apm.c
@@ -12,6 +12,7 @@
#include "config.h" // CONFIG_*
#include "biosvar.h" // GET_GLOBAL
#include "paravirt.h" // runningOnQEMU
+#include "acpi.h" // acpi_pm_ctl
static void
out_str(const char *str_cs)
@@ -108,7 +109,11 @@ handle_155306(struct bregs *regs)
void
apm_shutdown(void)
{
+ u16 pm1a_cnt = GET_GLOBAL(acpi_pm1a_cnt);
+
irq_disable();
+ if (pm1a_cnt)
+ outw(0x2000, pm1a_cnt);
out_str("Shutdown");
for (;;)
hlt();
diff --git a/src/pciinit.c b/src/pciinit.c
index 8370b96..0c6b0cb 100644
--- a/src/pciinit.c
+++ b/src/pciinit.c
@@ -15,6 +15,7 @@
#include "paravirt.h" // RamSize
#include "dev-q35.h" // Q35_HOST_BRIDGE_PCIEXBAR_ADDR
#include "list.h" // struct hlist_node
+#include "acpi.h" // acpi_pm1a_cnt
/* PM Timer ticks per second (HZ) */
#define PM_TIMER_FREQUENCY 3579545
@@ -194,6 +195,7 @@ void mch_isa_bridge_setup(struct pci_device *dev, void *arg)
/* acpi enable, SCI: IRQ9 000b = irq9*/
pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_ACPI_EN);
+ acpi_pm1a_cnt = PORT_ACPI_PM_BASE + 0x04;
pmtimer_setup(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000);
}
@@ -238,6 +240,7 @@ static void piix4_pm_setup(struct pci_device *pci, void *arg)
pci_config_writel(bdf, 0x90, PORT_SMB_BASE | 1);
pci_config_writeb(bdf, 0xd2, 0x09); /* enable SMBus io space */
+ acpi_pm1a_cnt = PORT_ACPI_PM_BASE + 0x04;
pmtimer_setup(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000);
}