summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRudolf Marek <r.marek@assembler.cz>2009-04-13 18:07:02 +0000
committerRudolf Marek <r.marek@assembler.cz>2009-04-13 18:07:02 +0000
commit33cafe5bfb440d150e36872d091037fa0785863d (patch)
tree66380579c3a3f0f33da895634b9bad17a096cab3
parent497c8effceb9510ca89561a8fcc87cbc0acb8b08 (diff)
Following patch implements ACPI resume support for coreboot. The hardware main
hook will come in separate patch perhaps. Signed-off-by: Rudolf Marek <r.marek@assembler.cz> Acked-by: Peter Stuge <peter@stuge.se> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@4101 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
-rw-r--r--src/arch/i386/boot/Config.lb3
-rw-r--r--src/arch/i386/boot/acpi.c114
-rw-r--r--src/arch/i386/boot/wakeup.S89
-rw-r--r--src/arch/i386/include/arch/acpi.h9
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);