diff options
author | Michael Brown | 2016-07-10 20:25:26 +0200 |
---|---|---|
committer | Michael Brown | 2016-07-11 15:05:18 +0200 |
commit | e19c0a8fd2bc8c2331c5fabe17ea56e7c35d1900 (patch) | |
tree | 0aba5ee1c1274ff613d786c18b0d004cce7b9dc4 /src/arch | |
parent | [rng] Check for functioning RTC interrupt (diff) | |
download | ipxe-e19c0a8fd2bc8c2331c5fabe17ea56e7c35d1900.tar.gz ipxe-e19c0a8fd2bc8c2331c5fabe17ea56e7c35d1900.tar.xz ipxe-e19c0a8fd2bc8c2331c5fabe17ea56e7c35d1900.zip |
[acpi] Add support for ACPI power off
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch')
-rw-r--r-- | src/arch/x86/include/bios.h | 1 | ||||
-rw-r--r-- | src/arch/x86/include/bits/errfile.h | 1 | ||||
-rw-r--r-- | src/arch/x86/include/ipxe/acpipwr.h | 14 | ||||
-rw-r--r-- | src/arch/x86/include/ipxe/apm.h | 14 | ||||
-rw-r--r-- | src/arch/x86/interface/pcbios/acpipwr.c | 116 | ||||
-rw-r--r-- | src/arch/x86/interface/pcbios/apm.c | 6 | ||||
-rw-r--r-- | src/arch/x86/interface/pcbios/bios_reboot.c | 22 |
7 files changed, 170 insertions, 4 deletions
diff --git a/src/arch/x86/include/bios.h b/src/arch/x86/include/bios.h index 988bbc62..a5a5d887 100644 --- a/src/arch/x86/include/bios.h +++ b/src/arch/x86/include/bios.h @@ -4,6 +4,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_SEG 0x0040 +#define BDA_EBDA 0x000e #define BDA_EQUIPMENT_WORD 0x0010 #define BDA_FBMS 0x0013 #define BDA_REBOOT 0x0072 diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 79b6f882..f4816e62 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_int13con ( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 ) #define ERRFILE_gdbmach ( ERRFILE_ARCH | ERRFILE_CORE | 0x000e0000 ) #define ERRFILE_rtc_entropy ( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 ) +#define ERRFILE_acpipwr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00100000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/arch/x86/include/ipxe/acpipwr.h b/src/arch/x86/include/ipxe/acpipwr.h new file mode 100644 index 00000000..93da0942 --- /dev/null +++ b/src/arch/x86/include/ipxe/acpipwr.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_ACPIPWR_H +#define _IPXE_ACPIPWR_H + +/** @file + * + * ACPI power off + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern int acpi_poweroff ( void ); + +#endif /* _IPXE_ACPIPWR_H */ diff --git a/src/arch/x86/include/ipxe/apm.h b/src/arch/x86/include/ipxe/apm.h new file mode 100644 index 00000000..21d913ac --- /dev/null +++ b/src/arch/x86/include/ipxe/apm.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_APM_H +#define _IPXE_APM_H + +/** @file + * + * Advanced Power Management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern int apm_poweroff ( void ); + +#endif /* _IPXE_APM_H */ diff --git a/src/arch/x86/interface/pcbios/acpipwr.c b/src/arch/x86/interface/pcbios/acpipwr.c new file mode 100644 index 00000000..63b986b6 --- /dev/null +++ b/src/arch/x86/interface/pcbios/acpipwr.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <errno.h> +#include <byteswap.h> +#include <realmode.h> +#include <bios.h> +#include <ipxe/io.h> +#include <ipxe/acpi.h> +#include <ipxe/acpipwr.h> + +/** @file + * + * ACPI power off + * + */ + +/** Colour for debug messages */ +#define colour FADT_SIGNATURE + +/** _S5_ signature */ +#define S5_SIGNATURE ACPI_SIGNATURE ( '_', 'S', '5', '_' ) + +/** + * Power off the computer using ACPI + * + * @ret rc Return status code + */ +int acpi_poweroff ( void ) { + struct acpi_fadt fadtab; + uint16_t ebda; + userptr_t rsdt; + userptr_t fadt; + unsigned int pm1a_cnt_blk; + unsigned int pm1b_cnt_blk; + unsigned int pm1a_cnt; + unsigned int pm1b_cnt; + unsigned int slp_typa; + unsigned int slp_typb; + int s5; + int rc; + + /* Locate EBDA */ + get_real ( ebda, BDA_SEG, BDA_EBDA ); + + /* Locate RSDT */ + rsdt = acpi_find_rsdt ( real_to_user ( ebda, 0 ) ); + if ( ! rsdt ) { + DBGC ( colour, "ACPI could not find RSDT (EBDA %04x)\n", ebda ); + return -ENOENT; + } + + /* Locate FADT */ + fadt = acpi_find ( rsdt, FADT_SIGNATURE, 0 ); + if ( ! fadt ) { + DBGC ( colour, "ACPI could not find FADT\n" ); + return -ENOENT; + } + + /* Read FADT */ + copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); + pm1a_cnt_blk = le32_to_cpu ( fadtab.pm1a_cnt_blk ); + pm1b_cnt_blk = le32_to_cpu ( fadtab.pm1b_cnt_blk ); + pm1a_cnt = ( pm1a_cnt_blk + ACPI_PM1_CNT ); + pm1b_cnt = ( pm1b_cnt_blk + ACPI_PM1_CNT ); + + /* Extract \_S5 from DSDT or any SSDT */ + s5 = acpi_sx ( rsdt, S5_SIGNATURE ); + if ( s5 < 0 ) { + rc = s5; + DBGC ( colour, "ACPI could not extract \\_S5: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Power off system */ + if ( pm1a_cnt_blk ) { + slp_typa = ( ( s5 >> 0 ) & 0xff ); + DBGC ( colour, "ACPI PM1a sleep type %#x => %04x\n", + slp_typa, pm1a_cnt ); + outw ( ( ACPI_PM1_CNT_SLP_TYP ( slp_typa ) | + ACPI_PM1_CNT_SLP_EN ), pm1a_cnt ); + } + if ( pm1b_cnt_blk ) { + slp_typb = ( ( s5 >> 8 ) & 0xff ); + DBGC ( colour, "ACPI PM1b sleep type %#x => %04x\n", + slp_typb, pm1b_cnt ); + outw ( ( ACPI_PM1_CNT_SLP_TYP ( slp_typb ) | + ACPI_PM1_CNT_SLP_EN ), pm1b_cnt ); + } + + DBGC ( colour, "ACPI power off failed\n" ); + return -EPROTO; +} diff --git a/src/arch/x86/interface/pcbios/apm.c b/src/arch/x86/interface/pcbios/apm.c index 50b19cb8..680dbb16 100644 --- a/src/arch/x86/interface/pcbios/apm.c +++ b/src/arch/x86/interface/pcbios/apm.c @@ -32,14 +32,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <realmode.h> -#include <ipxe/reboot.h> +#include <ipxe/apm.h> /** * Power off the computer using APM * * @ret rc Return status code */ -static int apm_poweroff ( void ) { +int apm_poweroff ( void ) { uint16_t apm_version; uint16_t apm_signature; uint16_t apm_flags; @@ -108,5 +108,3 @@ static int apm_poweroff ( void ) { /* Should never happen */ return -ECANCELED; } - -PROVIDE_REBOOT ( pcbios, poweroff, apm_poweroff ); diff --git a/src/arch/x86/interface/pcbios/bios_reboot.c b/src/arch/x86/interface/pcbios/bios_reboot.c index ed18dde0..c6c5a5a9 100644 --- a/src/arch/x86/interface/pcbios/bios_reboot.c +++ b/src/arch/x86/interface/pcbios/bios_reboot.c @@ -32,6 +32,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/reboot.h> #include <realmode.h> #include <bios.h> +#include <ipxe/apm.h> +#include <ipxe/acpipwr.h> /** * Reboot system @@ -49,4 +51,24 @@ static void bios_reboot ( int warm ) { __asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) ); } +/** + * Power off system + * + * @ret rc Return status code + */ +static int bios_poweroff ( void ) { + int rc; + + /* Try APM */ + if ( ( rc = apm_poweroff() ) != 0 ) + DBG ( "APM power off failed: %s\n", strerror ( rc ) ); + + /* Try ACPI */ + if ( ( rc = acpi_poweroff() ) != 0 ) + DBG ( "ACPI power off failed: %s\n", strerror ( rc ) ); + + return rc; +} + PROVIDE_REBOOT ( pcbios, reboot, bios_reboot ); +PROVIDE_REBOOT ( pcbios, poweroff, bios_poweroff ); |