diff options
Diffstat (limited to 'src/arch/x86/interface/pcbios')
-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 |
3 files changed, 140 insertions, 4 deletions
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 ); |