From 26428d4bc68dfde77a223459409e0313841086ba Mon Sep 17 00:00:00 2001 From: Xiaoguang Sun Date: Thu, 23 May 2013 19:06:13 +0800 Subject: [PATCH] Add acpi poweroff Use acpi poweroff if it's possible. Change-Id: I103cc288523bf63fa536750b1d408ac88bbe35fb Signed-off-by: Ben Gras Signed-off-by: Tomas Hruby --- kernel/arch/i386/acpi.c | 116 ++++++++++++++++++++++++++++++++++ kernel/arch/i386/acpi.h | 66 +++++++++++++++++++ kernel/arch/i386/arch_reset.c | 7 ++ 3 files changed, 189 insertions(+) diff --git a/kernel/arch/i386/acpi.c b/kernel/arch/i386/acpi.c index 225467ba2..7770f336c 100644 --- a/kernel/arch/i386/acpi.c +++ b/kernel/arch/i386/acpi.c @@ -12,6 +12,20 @@ struct acpi_rsdp acpi_rsdp; static acpi_read_t read_func; #define MAX_RSDT 35 /* ACPI defines 35 signatures */ +#define SLP_EN_CODE (1 << 13) /* ACPI SLP_EN_CODE code */ +#define AMI_PACKAGE_OP_CODE (0x12) +#define AMI_NAME_OP_CODE (0x8) +#define AMI_BYTE_PREFIX_CODE (0xA) +#define AMI_PACKAGE_LENGTH_ENCODING_BITS_MASK (0xC0) +#define AMI_PACKAGE_LENGTH_ENCODING_BITS_SHIFT (6) +#define AMI_MIN_PACKAGE_LENGTH (1) +#define AMI_NUM_ELEMENTS_LENGTH (1) +#define AMI_SLP_TYPA_SHIFT (10) +#define AMI_SLP_TYPB_SHIFT (10) +#define AMI_S5_NAME_OP_OFFSET_1 (-1) +#define AMI_S5_NAME_OP_OFFSET_2 (-2) +#define AMI_S5_PACKAGE_OP_OFFSET (4) +#define AMI_S5_PACKET_LENGTH_OFFSET (5) static struct acpi_rsdt { struct acpi_sdt_header hdr; @@ -24,6 +38,10 @@ static struct { } sdt_trans[MAX_RSDT]; static int sdt_count; +static u16_t pm1a_cnt_blk = 0; +static u16_t pm1b_cnt_blk = 0; +static u16_t slp_typa = 0; +static u16_t slp_typb = 0; static int acpi_check_csum(struct acpi_sdt_header * tb, size_t size) { @@ -210,6 +228,86 @@ static int get_acpi_rsdp(void) return 0; } +static void acpi_init_poweroff(void) +{ + u8_t *ptr = NULL; + u8_t *start = NULL; + u8_t *end = NULL; + struct acpi_fadt_header *fadt_header = NULL; + struct acpi_rsdt * dsdt_header = NULL; + char *msg = NULL; + + /* Everything used here existed since ACPI spec 1.0 */ + /* So we can safely use them */ + fadt_header = (struct acpi_fadt_header *) + acpi_phys2vir(acpi_get_table_base("FACP")); + if (fadt_header == NULL) { + msg = "Could not load FACP"; + goto exit; + } + + dsdt_header = (struct acpi_rsdt *) + acpi_phys2vir((phys_bytes) fadt_header->dsdt); + if (dsdt_header == NULL) { + msg = "Could not load DSDT"; + goto exit; + } + + pm1a_cnt_blk = fadt_header->pm1a_cnt_blk; + pm1b_cnt_blk = fadt_header->pm1b_cnt_blk; + + ptr = start = (u8_t *) dsdt_header->data; + end = start + dsdt_header->hdr.length - 4; + + /* See http://forum.osdev.org/viewtopic.php?t=16990 */ + /* for layout of \_S5 */ + while (ptr < end && memcmp(ptr, "_S5_", 4) != 0) + ptr++; + + msg = "Could not read S5 data. Use default SLP_TYPa and SLP_TYPb"; + if (ptr >= end || ptr == start) + goto exit; + + /* validate AML structure */ + if (*(ptr + AMI_S5_PACKAGE_OP_OFFSET) != AMI_PACKAGE_OP_CODE) + goto exit; + + if ((ptr < start + (-AMI_S5_NAME_OP_OFFSET_2) || + (*(ptr + AMI_S5_NAME_OP_OFFSET_2) != AMI_NAME_OP_CODE || + *(ptr + AMI_S5_NAME_OP_OFFSET_2 + 1) != '\\')) && + *(ptr + AMI_S5_NAME_OP_OFFSET_1) != AMI_NAME_OP_CODE) + goto exit; + + ptr += AMI_S5_PACKET_LENGTH_OFFSET; + if (ptr >= end) + goto exit; + + /* package length */ + ptr += ((*ptr & AMI_PACKAGE_LENGTH_ENCODING_BITS_MASK) >> + AMI_PACKAGE_LENGTH_ENCODING_BITS_SHIFT) + + AMI_MIN_PACKAGE_LENGTH + AMI_NUM_ELEMENTS_LENGTH; + if (ptr >= end) + goto exit; + + if (*ptr == AMI_BYTE_PREFIX_CODE) + ptr++; /* skip byte prefix */ + + slp_typa = (*ptr) << AMI_SLP_TYPA_SHIFT; + + ptr++; /* move to SLP_TYPb */ + if (*ptr == AMI_BYTE_PREFIX_CODE) + ptr++; /* skip byte prefix */ + + slp_typb = (*ptr) << AMI_SLP_TYPB_SHIFT; + + msg = "poweroff initialized"; + +exit: + if (msg) { + printf("acpi: %s\n", msg); + } +} + void acpi_init(void) { int s, i; @@ -239,6 +337,8 @@ void acpi_init(void) sdt_trans[i].signature[ACPI_SDT_SIGNATURE_LEN] = '\0'; sdt_trans[i].length = hdr.length; } + + acpi_init_poweroff(); } struct acpi_madt_ioapic * acpi_get_ioapic_next(void) @@ -293,3 +393,19 @@ struct acpi_madt_lapic * acpi_get_lapic_next(void) return ret; } + +void __k_unpaged_acpi_poweroff(void) +{ + /* NO OP poweroff symbol*/ +} + +void acpi_poweroff(void) +{ + if (pm1a_cnt_blk == 0) { + return; + } + outw(pm1a_cnt_blk, slp_typa | SLP_EN_CODE); + if (pm1b_cnt_blk != 0) { + outw(pm1b_cnt_blk, slp_typb | SLP_EN_CODE); + } +} diff --git a/kernel/arch/i386/acpi.h b/kernel/arch/i386/acpi.h index ddbd50419..06e978c9a 100644 --- a/kernel/arch/i386/acpi.h +++ b/kernel/arch/i386/acpi.h @@ -30,6 +30,70 @@ struct acpi_sdt_header { u32_t creator_revision; }; +struct acpi_generic_address { + u8_t address_space_id; + u8_t register_bit_width; + u8_t register_bit_offset; + u8_t access_size; + u64_t address; +}; + +struct acpi_fadt_header +{ + struct acpi_sdt_header hdr; + u32_t facs; + u32_t dsdt; + u8_t model; + u8_t preferred_pm_profile; + u16_t sci_int; + u32_t smi_cmd; + u8_t acpi_enable; + u8_t acpi_disable; + u8_t s4bios_req; + u8_t pstate_cnt; + u32_t pm1a_evt_blk; + u32_t pm1b_evt_blk; + u32_t pm1a_cnt_blk; + u32_t pm1b_cnt_blk; + u32_t pm2_cnt_blk; + u32_t pm_tmr_blk; + u32_t gpe0_blk; + u32_t gpe1_blk; + u8_t pm1_evt_len; + u8_t pm1_cnt_len; + u8_t pm2_cnt_len; + u8_t pm_tmr_len; + u8_t gpe0_blk_len; + u8_t gpe1_blk_len; + u8_t gpe1_base; + u8_t cst_cnt; + u16_t p_lvl2_lat; + u16_t p_lvl3_lat; + u16_t flush_size; + u16_t flush_stride; + u8_t duty_offset; + u8_t duty_width; + u8_t day_alrm; + u8_t mon_alrm; + u8_t century; + u16_t iapc_boot_arch; + u8_t reserved1; + u32_t flags; + struct acpi_generic_address reset_reg; + u8_t reset_value; + u8_t reserved2[3]; + u64_t xfacs; + u64_t xdsdt; + struct acpi_generic_address xpm1a_evt_blk; + struct acpi_generic_address xpm1b_evt_blk; + struct acpi_generic_address xpm1a_cnt_blk; + struct acpi_generic_address xpm1b_cnt_blk; + struct acpi_generic_address xpm2_cnt_blk; + struct acpi_generic_address xpm_tmr_blk; + struct acpi_generic_address xgpe0_blk; + struct acpi_generic_address xgpe1_blk; +}; + struct acpi_madt_hdr { struct acpi_sdt_header hdr; u32_t local_apic_address; @@ -84,6 +148,8 @@ struct acpi_madt_nmi { void acpi_init(void); +void acpi_poweroff(void); + /* * Returns a pointer to the io acpi structure in the MADT table in ACPI. The * pointer is valid only until paging is turned off. No memory is allocated in diff --git a/kernel/arch/i386/arch_reset.c b/kernel/arch/i386/arch_reset.c index 2aad6f08f..89b43a4a1 100644 --- a/kernel/arch/i386/arch_reset.c +++ b/kernel/arch/i386/arch_reset.c @@ -22,6 +22,10 @@ #include "direct_utils.h" #include +#ifdef USE_ACPI +#include "acpi.h" +#endif + #define KBCMDP 4 /* kbd controller port (O) */ #define KBC_PULSE0 0xfe /* pulse output bit 0 */ #define IO_KBD 0x060 /* 8042 Keyboard */ @@ -85,6 +89,9 @@ poweroff(void) { const char *shutdown_str; +#ifdef USE_ACPI + acpi_poweroff(); +#endif /* Bochs/QEMU poweroff */ shutdown_str = "Shutdown"; while (*shutdown_str) outb(0x8900, *(shutdown_str++));