diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/arch/i386/boot/Config.lb | 3 | ||||
-rw-r--r-- | src/arch/i386/boot/acpi.c | 114 | ||||
-rw-r--r-- | src/arch/i386/boot/wakeup.S | 89 | ||||
-rw-r--r-- | src/arch/i386/include/arch/acpi.h | 9 |
4 files changed, 212 insertions, 3 deletions
diff --git a/src/arch/i386/boot/Config.lb b/src/arch/i386/boot/Config.lb index 6526a2f3f6..dc72ea49bf 100644 --- a/src/arch/i386/boot/Config.lb +++ b/src/arch/i386/boot/Config.lb @@ -14,4 +14,7 @@ end if HAVE_ACPI_TABLES object acpi.o object acpigen.o +if HAVE_ACPI_RESUME +object wakeup.S +end end diff --git a/src/arch/i386/boot/acpi.c b/src/arch/i386/boot/acpi.c index 1612c804e4..c7046e1b31 100644 --- a/src/arch/i386/boot/acpi.c +++ b/src/arch/i386/boot/acpi.c @@ -27,6 +27,11 @@ #include <arch/acpigen.h> #include <device/pci.h> +#if HAVE_ACPI_RESUME == 1 +/* this is to be filled by SB code - startup value what was found */ +u8 acpi_slp_type; +#endif + u8 acpi_checksum(u8 *table, u32 length) { u8 ret=0; @@ -323,12 +328,12 @@ void acpi_create_hpet(acpi_hpet_t *hpet) header->checksum = acpi_checksum((void *)hpet, sizeof(acpi_hpet_t)); } - void acpi_create_facs(acpi_facs_t *facs) { + memset( (void *)facs,0, sizeof(acpi_facs_t)); - memcpy(facs->signature,"FACS",4); + memcpy(facs->signature, FACS_NAME, 4); facs->length = sizeof(acpi_facs_t); facs->hardware_signature = 0; facs->firmware_waking_vector = 0; @@ -365,11 +370,114 @@ void acpi_write_rsdp(acpi_rsdp_t *rsdp, acpi_rsdt_t *rsdt) { memcpy(rsdp->signature, RSDP_SIG, 8); memcpy(rsdp->oem_id, OEM_ID, 6); - rsdp->length = sizeof(acpi_rsdp_t); rsdp->rsdt_address = (u32)rsdt; + rsdp->revision = 2; rsdp->checksum = acpi_checksum((void *)rsdp, 20); rsdp->ext_checksum = acpi_checksum((void *)rsdp, sizeof(acpi_rsdp_t)); } +#if HAVE_ACPI_RESUME == 1 + +int acpi_get_sleep_type(void) +{ + return acpi_slp_type; +} + +int acpi_is_wakeup(void) +{ + return (acpi_slp_type == 3); +} + +static acpi_rsdp_t *valid_rsdp(acpi_rsdp_t *rsdp) +{ + if (strncmp((char *)rsdp, RSDP_SIG, sizeof(RSDP_SIG) - 1) != 0) + return NULL; + + printk_debug("Looking on %p for valid checksum\n", rsdp); + + if (acpi_checksum((void *)rsdp, 20) != 0) + return NULL; + printk_debug("Checksum 1 passed\n"); + + if ((rsdp->revision > 1) && (acpi_checksum((void *)rsdp, + rsdp->length) != 0)) + return NULL; + + printk_debug("Checksum 2 passed all OK\n"); + + return rsdp; +} + +static acpi_rsdp_t *rsdp; + +void *acpi_get_wakeup_rsdp(void) +{ + return rsdp; +} +void *acpi_find_wakeup_vector(void) +{ + char *p, *end; + + acpi_rsdt_t *rsdt; + acpi_facs_t *facs; + acpi_fadt_t *fadt; + void *wake_vec; + int i; + + rsdp = NULL; + + if (!acpi_is_wakeup()) + return NULL; + + printk_debug("Trying to find the wakeup vector ...\n"); + + /* find RSDP */ + for (p = (char *) 0xe0000; p < (char *) 0xfffff; p+=16) { + if ((rsdp = valid_rsdp((acpi_rsdp_t *) p))) + break; + } + + if (rsdp == NULL) + return NULL; + + printk_debug("RSDP found at %p\n", rsdp); + rsdt = (acpi_rsdt_t *) rsdp->rsdt_address; + + end = (char *) rsdt + rsdt->header.length; + printk_debug("RSDT found at %p ends at %p\n", rsdt, end); + + for (i = 0; ((char *) &rsdt->entry[i]) < end; i++) { + fadt = (acpi_fadt_t *) rsdt->entry[i]; + if (strncmp((char *)fadt, FADT_NAME, sizeof(FADT_NAME) - 1) == 0) + break; + fadt = NULL; + } + + if (fadt == NULL) + return NULL; + + printk_debug("FADT found at %p\n", fadt); + facs = fadt->firmware_ctrl; + + if (facs == NULL) + return NULL; + + printk_debug("FACS found at %p\n", facs); + wake_vec = (void *) facs->firmware_waking_vector; + printk_debug("OS waking vector is %p\n", wake_vec); + return wake_vec; +} + +extern char *lowmem_backup; +extern char *lowmem_backup_ptr; +extern int lowmem_backup_size; + +void acpi_jump_to_wakeup(void *vector) +{ + /* just restore the SMP trampoline and continue with wakeup on assembly level */ + memcpy(lowmem_backup_ptr, lowmem_backup, lowmem_backup_size); + acpi_jmp_to_realm_wakeup((u32) vector); +} +#endif diff --git a/src/arch/i386/boot/wakeup.S b/src/arch/i386/boot/wakeup.S new file mode 100644 index 0000000000..5985c8e1c0 --- /dev/null +++ b/src/arch/i386/boot/wakeup.S @@ -0,0 +1,89 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Rudolf Marek <r.marek@assembler.cz> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ + .text + .code32 + .globl acpi_jmp_to_realm_wakeup + /* This function does the PM -> RM switch, but + it can run >1MB even in real mode */ + +acpi_jmp_to_realm_wakeup: + mov 0x4(%esp), %eax + /* last 4 bits of linear addr are taken as offset */ + andw $0x0f, %ax + movw %ax, (jmp_off) + mov 0x4(%esp), %eax + /* the rest is taken as segment */ + shr $4, %eax + movw %ax, (jmp_seg) + lgdt gdtaddr_wakeup + movl $0x008,%eax + mov %eax,%ds + movl %eax,%es + movl %eax,%ss + movl %eax,%fs + movl %eax,%gs + ljmp $0x0010,$reload_cs + .code16gcc +reload_cs: + /* switch off PE */ + movl %cr0, %eax + andb $0xfe,%al + movl %eax, %cr0 + movw $0x0, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + /* far jump to OS waking vector. The linear addr is changed to SEG and OFFSET + check ACPI specs or above code for details */ + .byte 0xea +jmp_off: + .word 0 +jmp_seg: + .word 0 + +.code32 +gdt_wakeup_limit = gdt_wakeup_end - gdt_wakeup - 1 /* compute the table limit */ + gdtaddr_wakeup: + .word gdt_wakeup_limit /* the table limit */ + .long gdt_wakeup /* we know the offset */ + + .data + + /* This is the gdt for GCC part of coreboot. + * It is different from the gdt in ROMCC/ASM part of coreboot + * which is defined in entry32.inc */ +gdt_wakeup: + /* selgdt 0, unused */ + .word 0x0000, 0x0000 /* dummy */ + .byte 0x00, 0x00, 0x00, 0x00 + + /* selgdt 8, flat data segment 16bit */ + .word 0x0000, 0x0000 /* dummy */ + .byte 0x00, 0x93, 0x8f, 0x00 /* G=1 and 0x0f, So we get 4Gbytes for limit */ + + /* selgdt 0x10, flat code segment 16bit */ + .word 0xffff, 0x0000 + .byte 0x00, 0x9b, 0x8f, 0x00 /* G=1 and 0x0f, So we get 4Gbytes for limit */ + +gdt_wakeup_end: + + .previous +.code32 diff --git a/src/arch/i386/include/arch/acpi.h b/src/arch/i386/include/arch/acpi.h index 1fccd62a7b..99bec4b30c 100644 --- a/src/arch/i386/include/arch/acpi.h +++ b/src/arch/i386/include/arch/acpi.h @@ -16,6 +16,9 @@ #if HAVE_ACPI_TABLES==1 #include <stdint.h> + +/* 0 = S0, 1 = S1 ...*/ +extern u8 acpi_slp_type; #define RSDP_SIG "RSD PTR " /* RSDT Pointer signature */ #define RSDP_NAME "RSDP" @@ -27,6 +30,8 @@ #define SRAT_NAME "SRAT" #define SLIT_NAME "SLIT" #define SSDT_NAME "SSDT" +#define FACS_NAME "FACS" +#define FADT_NAME "FACP" #define RSDT_TABLE "RSDT " #define HPET_TABLE "AMD64 " @@ -328,6 +333,10 @@ void acpi_create_facs(acpi_facs_t *facs); void acpi_write_rsdt(acpi_rsdt_t *rsdt); void acpi_write_rsdp(acpi_rsdp_t *rsdp, acpi_rsdt_t *rsdt); +void *acpi_find_wakeup_vector(void); +void *acpi_get_wakeup_rsdp(void); +extern void acpi_jmp_to_realm_wakeup(u32 linear_addr); +void acpi_jump_to_wakeup(void *wakeup_addr); unsigned long acpi_add_ssdt_pstates(acpi_rsdt_t *rsdt, unsigned long current); |