diff options
Diffstat (limited to 'src/arch/x86/boot')
-rw-r--r-- | src/arch/x86/boot/Makefile.inc | 13 | ||||
-rw-r--r-- | src/arch/x86/boot/acpi.c | 604 | ||||
-rw-r--r-- | src/arch/x86/boot/acpigen.c | 517 | ||||
-rw-r--r-- | src/arch/x86/boot/boot.c | 186 | ||||
-rw-r--r-- | src/arch/x86/boot/coreboot_table.c | 600 | ||||
-rw-r--r-- | src/arch/x86/boot/gdt.c | 60 | ||||
-rw-r--r-- | src/arch/x86/boot/mpspec.c | 383 | ||||
-rw-r--r-- | src/arch/x86/boot/multiboot.c | 77 | ||||
-rw-r--r-- | src/arch/x86/boot/pirq_routing.c | 169 | ||||
-rw-r--r-- | src/arch/x86/boot/tables.c | 231 | ||||
-rw-r--r-- | src/arch/x86/boot/wakeup.S | 105 |
11 files changed, 2945 insertions, 0 deletions
diff --git a/src/arch/x86/boot/Makefile.inc b/src/arch/x86/boot/Makefile.inc new file mode 100644 index 0000000000..d4a377f74b --- /dev/null +++ b/src/arch/x86/boot/Makefile.inc @@ -0,0 +1,13 @@ +ramstage-y += boot.c +ramstage-y += coreboot_table.c +ramstage-$(CONFIG_MULTIBOOT) += multiboot.c +ramstage-y += gdt.c +ramstage-y += tables.c +ramstage-$(CONFIG_GENERATE_MP_TABLE) += mpspec.c +ramstage-$(CONFIG_GENERATE_PIRQ_TABLE) += pirq_routing.c +ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c +ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpigen.c +ramstage-$(CONFIG_HAVE_ACPI_RESUME) += wakeup.S + +$(obj)/arch/x86/boot/coreboot_table.ramstage.o : $(OPTION_TABLE_H) + diff --git a/src/arch/x86/boot/acpi.c b/src/arch/x86/boot/acpi.c new file mode 100644 index 0000000000..957ec4559a --- /dev/null +++ b/src/arch/x86/boot/acpi.c @@ -0,0 +1,604 @@ +/* + * This file is part of the coreboot project. + * + * coreboot ACPI Table support + * written by Stefan Reinauer <stepan@openbios.org> + * + * Copyright (C) 2004 SUSE LINUX AG + * Copyright (C) 2005-2009 coresystems GmbH + * + * ACPI FADT, FACS, and DSDT table support added by + * Nick Barker <nick.barker9@btinternet.com>, and those portions + * Copyright (C) 2004 Nick Barker + * + * Copyright (C) 2005 ADVANCED MICRO DEVICES, INC. All Rights Reserved. + * 2005.9 yhlu add SRAT table generation + */ + +/* + * Each system port implementing ACPI has to provide two functions: + * + * write_acpi_tables() + * acpi_dump_apics() + * + * See Kontron 986LCD-M port for a good example of an ACPI implementation + * in coreboot. + */ + +#include <console/console.h> +#include <string.h> +#include <arch/acpi.h> +#include <arch/acpigen.h> +#include <device/pci.h> +#include <cbmem.h> + +u8 acpi_checksum(u8 *table, u32 length) +{ + u8 ret = 0; + while (length--) { + ret += *table; + table++; + } + return -ret; +} + +/** + * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length + * and checksum. + */ +void acpi_add_table(acpi_rsdp_t *rsdp, void *table) +{ + int i, entries_num; + acpi_rsdt_t *rsdt; + acpi_xsdt_t *xsdt = NULL; + + /* The RSDT is mandatory... */ + rsdt = (acpi_rsdt_t *)rsdp->rsdt_address; + + /* ...while the XSDT is not. */ + if (rsdp->xsdt_address) + xsdt = (acpi_xsdt_t *)((u32)rsdp->xsdt_address); + + /* This should always be MAX_ACPI_TABLES. */ + entries_num = ARRAY_SIZE(rsdt->entry); + + for (i = 0; i < entries_num; i++) { + if (rsdt->entry[i] == 0) + break; + } + + if (i >= entries_num) { + printk(BIOS_ERR, "ACPI: Error: Could not add ACPI table, " + "too many tables.\n"); + return; + } + + /* Add table to the RSDT. */ + rsdt->entry[i] = (u32)table; + + /* Fix RSDT length or the kernel will assume invalid entries. */ + rsdt->header.length = sizeof(acpi_header_t) + (sizeof(u32) * (i + 1)); + + /* Re-calculate checksum. */ + rsdt->header.checksum = 0; /* Hope this won't get optimized away */ + rsdt->header.checksum = acpi_checksum((u8 *)rsdt, rsdt->header.length); + + /* + * And now the same thing for the XSDT. We use the same index as for + * now we want the XSDT and RSDT to always be in sync in coreboot. + */ + if (xsdt) { + /* Add table to the XSDT. */ + xsdt->entry[i] = (u64)(u32)table; + + /* Fix XSDT length. */ + xsdt->header.length = sizeof(acpi_header_t) + + (sizeof(u64) * (i + 1)); + + /* Re-calculate checksum. */ + xsdt->header.checksum = 0; + xsdt->header.checksum = acpi_checksum((u8 *)xsdt, + xsdt->header.length); + } + + printk(BIOS_DEBUG, "ACPI: added table %d/%d, length now %d\n", + i + 1, entries_num, rsdt->header.length); +} + +int acpi_create_mcfg_mmconfig(acpi_mcfg_mmconfig_t *mmconfig, u32 base, + u16 seg_nr, u8 start, u8 end) +{ + mmconfig->base_address = base; + mmconfig->base_reserved = 0; + mmconfig->pci_segment_group_number = seg_nr; + mmconfig->start_bus_number = start; + mmconfig->end_bus_number = end; + + return sizeof(acpi_mcfg_mmconfig_t); +} + +int acpi_create_madt_lapic(acpi_madt_lapic_t *lapic, u8 cpu, u8 apic) +{ + lapic->type = 0; /* Local APIC structure */ + lapic->length = sizeof(acpi_madt_lapic_t); + lapic->flags = (1 << 0); /* Processor/LAPIC enabled */ + lapic->processor_id = cpu; + lapic->apic_id = apic; + + return lapic->length; +} + +unsigned long acpi_create_madt_lapics(unsigned long current) +{ + device_t cpu; + int cpu_index = 0; + + for (cpu = all_devices; cpu; cpu = cpu->next) { + if ((cpu->path.type != DEVICE_PATH_APIC) || + (cpu->bus->dev->path.type != DEVICE_PATH_APIC_CLUSTER)) { + continue; + } + if (!cpu->enabled) + continue; + current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current, + cpu_index, cpu->path.apic.apic_id); + cpu_index++; + } + + return current; +} + +int acpi_create_madt_ioapic(acpi_madt_ioapic_t *ioapic, u8 id, u32 addr, + u32 gsi_base) +{ + ioapic->type = 1; /* I/O APIC structure */ + ioapic->length = sizeof(acpi_madt_ioapic_t); + ioapic->reserved = 0x00; + ioapic->gsi_base = gsi_base; + ioapic->ioapic_id = id; + ioapic->ioapic_addr = addr; + + return ioapic->length; +} + +int acpi_create_madt_irqoverride(acpi_madt_irqoverride_t *irqoverride, + u8 bus, u8 source, u32 gsirq, u16 flags) +{ + irqoverride->type = 2; /* Interrupt source override */ + irqoverride->length = sizeof(acpi_madt_irqoverride_t); + irqoverride->bus = bus; + irqoverride->source = source; + irqoverride->gsirq = gsirq; + irqoverride->flags = flags; + + return irqoverride->length; +} + +int acpi_create_madt_lapic_nmi(acpi_madt_lapic_nmi_t *lapic_nmi, u8 cpu, + u16 flags, u8 lint) +{ + lapic_nmi->type = 4; /* Local APIC NMI structure */ + lapic_nmi->length = sizeof(acpi_madt_lapic_nmi_t); + lapic_nmi->flags = flags; + lapic_nmi->processor_id = cpu; + lapic_nmi->lint = lint; + + return lapic_nmi->length; +} + +void acpi_create_madt(acpi_madt_t *madt) +{ +#define LOCAL_APIC_ADDR 0xfee00000ULL + + acpi_header_t *header = &(madt->header); + unsigned long current = (unsigned long)madt + sizeof(acpi_madt_t); + + memset((void *)madt, 0, sizeof(acpi_madt_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "APIC", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_madt_t); + header->revision = 1; /* ACPI 1.0/2.0: 1, ACPI 3.0: 2, ACPI 4.0: 3 */ + + madt->lapic_addr = LOCAL_APIC_ADDR; + madt->flags = 0x1; /* PCAT_COMPAT */ + + current = acpi_fill_madt(current); + + /* (Re)calculate length and checksum. */ + header->length = current - (unsigned long)madt; + + header->checksum = acpi_checksum((void *)madt, header->length); +} + +/* MCFG is defined in the PCI Firmware Specification 3.0. */ +void acpi_create_mcfg(acpi_mcfg_t *mcfg) +{ + acpi_header_t *header = &(mcfg->header); + unsigned long current = (unsigned long)mcfg + sizeof(acpi_mcfg_t); + + memset((void *)mcfg, 0, sizeof(acpi_mcfg_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "MCFG", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_mcfg_t); + header->revision = 1; + + current = acpi_fill_mcfg(current); + + /* (Re)calculate length and checksum. */ + header->length = current - (unsigned long)mcfg; + header->checksum = acpi_checksum((void *)mcfg, header->length); +} + +/* + * This can be overriden by platform ACPI setup code, if it calls + * acpi_create_ssdt_generator(). + */ +unsigned long __attribute__((weak)) acpi_fill_ssdt_generator( + unsigned long current, const char *oem_table_id) +{ + return current; +} + +void acpi_create_ssdt_generator(acpi_header_t *ssdt, const char *oem_table_id) +{ + unsigned long current = (unsigned long)ssdt + sizeof(acpi_header_t); + + memset((void *)ssdt, 0, sizeof(acpi_header_t)); + + memcpy(&ssdt->signature, "SSDT", 4); + ssdt->revision = 2; /* ACPI 1.0/2.0: ?, ACPI 3.0/4.0: 2 */ + memcpy(&ssdt->oem_id, OEM_ID, 6); + memcpy(&ssdt->oem_table_id, oem_table_id, 8); + ssdt->oem_revision = 42; + memcpy(&ssdt->asl_compiler_id, ASLC, 4); + ssdt->asl_compiler_revision = 42; + ssdt->length = sizeof(acpi_header_t); + + acpigen_set_current((char *) current); + current = acpi_fill_ssdt_generator(current, oem_table_id); + + /* (Re)calculate length and checksum. */ + ssdt->length = current - (unsigned long)ssdt; + ssdt->checksum = acpi_checksum((void *)ssdt, ssdt->length); +} + +int acpi_create_srat_lapic(acpi_srat_lapic_t *lapic, u8 node, u8 apic) +{ + memset((void *)lapic, 0, sizeof(acpi_srat_lapic_t)); + + lapic->type = 0; /* Processor local APIC/SAPIC affinity structure */ + lapic->length = sizeof(acpi_srat_lapic_t); + lapic->flags = (1 << 0); /* Enabled (the use of this structure). */ + lapic->proximity_domain_7_0 = node; + /* TODO: proximity_domain_31_8, local SAPIC EID, clock domain. */ + lapic->apic_id = apic; + + return lapic->length; +} + +int acpi_create_srat_mem(acpi_srat_mem_t *mem, u8 node, u32 basek, u32 sizek, + u32 flags) +{ + mem->type = 1; /* Memory affinity structure */ + mem->length = sizeof(acpi_srat_mem_t); + mem->base_address_low = (basek << 10); + mem->base_address_high = (basek >> (32 - 10)); + mem->length_low = (sizek << 10); + mem->length_high = (sizek >> (32 - 10)); + mem->proximity_domain = node; + mem->flags = flags; + + return mem->length; +} + +/* http://www.microsoft.com/whdc/system/sysinternals/sratdwn.mspx */ +void acpi_create_srat(acpi_srat_t *srat) +{ + acpi_header_t *header = &(srat->header); + unsigned long current = (unsigned long)srat + sizeof(acpi_srat_t); + + memset((void *)srat, 0, sizeof(acpi_srat_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "SRAT", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_srat_t); + header->revision = 1; /* ACPI 1.0: N/A, 2.0: 1, 3.0: 2, 4.0: 3 */ + + srat->resv = 1; /* Spec: Reserved to 1 for backwards compatibility. */ + + current = acpi_fill_srat(current); + + /* (Re)calculate length and checksum. */ + header->length = current - (unsigned long)srat; + header->checksum = acpi_checksum((void *)srat, header->length); +} + +/* http://h21007.www2.hp.com/portal/download/files/unprot/Itanium/slit.pdf */ +void acpi_create_slit(acpi_slit_t *slit) +{ + acpi_header_t *header = &(slit->header); + unsigned long current = (unsigned long)slit + sizeof(acpi_slit_t); + + memset((void *)slit, 0, sizeof(acpi_slit_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "SLIT", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_slit_t); + header->revision = 1; /* ACPI 1.0: N/A, ACPI 2.0/3.0/4.0: 1 */ + + current = acpi_fill_slit(current); + + /* (Re)calculate length and checksum. */ + header->length = current - (unsigned long)slit; + header->checksum = acpi_checksum((void *)slit, header->length); +} + +/* http://www.intel.com/hardwaredesign/hpetspec_1.pdf */ +void acpi_create_hpet(acpi_hpet_t *hpet) +{ +#define HPET_ADDR 0xfed00000ULL + acpi_header_t *header = &(hpet->header); + acpi_addr_t *addr = &(hpet->addr); + + memset((void *)hpet, 0, sizeof(acpi_hpet_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "HPET", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_hpet_t); + header->revision = 1; /* Currently 1. Table added in ACPI 2.0. */ + + /* Fill out HPET address. */ + addr->space_id = 0; /* Memory */ + addr->bit_width = 64; + addr->bit_offset = 0; + addr->addrl = HPET_ADDR & 0xffffffff; + addr->addrh = HPET_ADDR >> 32; + + hpet->id = 0x102282a0; /* AMD! FIXME */ + hpet->number = 0; + hpet->min_tick = 4096; + + 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); + facs->length = sizeof(acpi_facs_t); + facs->hardware_signature = 0; + facs->firmware_waking_vector = 0; + facs->global_lock = 0; + facs->flags = 0; + facs->x_firmware_waking_vector_l = 0; + facs->x_firmware_waking_vector_h = 0; + facs->version = 1; /* ACPI 1.0: 0, ACPI 2.0/3.0: 1, ACPI 4.0: 2 */ +} + +void acpi_write_rsdt(acpi_rsdt_t *rsdt) +{ + acpi_header_t *header = &(rsdt->header); + + /* Fill out header fields. */ + memcpy(header->signature, "RSDT", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_rsdt_t); + header->revision = 1; /* ACPI 1.0/2.0/3.0/4.0: 1 */ + + /* Entries are filled in later, we come with an empty set. */ + + /* Fix checksum. */ + header->checksum = acpi_checksum((void *)rsdt, sizeof(acpi_rsdt_t)); +} + +void acpi_write_xsdt(acpi_xsdt_t *xsdt) +{ + acpi_header_t *header = &(xsdt->header); + + /* Fill out header fields. */ + memcpy(header->signature, "XSDT", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_xsdt_t); + header->revision = 1; /* ACPI 1.0: N/A, 2.0/3.0/4.0: 1 */ + + /* Entries are filled in later, we come with an empty set. */ + + /* Fix checksum. */ + header->checksum = acpi_checksum((void *)xsdt, sizeof(acpi_xsdt_t)); +} + +void acpi_write_rsdp(acpi_rsdp_t *rsdp, acpi_rsdt_t *rsdt, acpi_xsdt_t *xsdt) +{ + memset(rsdp, 0, sizeof(acpi_rsdp_t)); + + memcpy(rsdp->signature, RSDP_SIG, 8); + memcpy(rsdp->oem_id, OEM_ID, 6); + + rsdp->length = sizeof(acpi_rsdp_t); + rsdp->rsdt_address = (u32)rsdt; + + /* + * Revision: ACPI 1.0: 0, ACPI 2.0/3.0/4.0: 2. + * + * Some OSes expect an XSDT to be present for RSD PTR revisions >= 2. + * If we don't have an ACPI XSDT, force ACPI 1.0 (and thus RSD PTR + * revision 0). + */ + if (xsdt == NULL) { + rsdp->revision = 0; + } else { + rsdp->xsdt_address = (u64)(u32)xsdt; + rsdp->revision = 2; + } + + /* Calculate checksums. */ + rsdp->checksum = acpi_checksum((void *)rsdp, 20); + rsdp->ext_checksum = acpi_checksum((void *)rsdp, sizeof(acpi_rsdp_t)); +} + +#if CONFIG_HAVE_ACPI_RESUME == 1 +void suspend_resume(void) +{ + void *wake_vec; + + /* If we happen to be resuming find wakeup vector and jump to OS. */ + wake_vec = acpi_find_wakeup_vector(); + if (wake_vec) + acpi_jump_to_wakeup(wake_vec); +} + +/* This is to be filled by SB code - startup value what was found. */ +u8 acpi_slp_type = 0; + +static 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(BIOS_DEBUG, "Looking on %p for valid checksum\n", rsdp); + + if (acpi_checksum((void *)rsdp, 20) != 0) + return NULL; + printk(BIOS_DEBUG, "Checksum 1 passed\n"); + + if ((rsdp->revision > 1) && (acpi_checksum((void *)rsdp, + rsdp->length) != 0)) + return NULL; + printk(BIOS_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(BIOS_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(BIOS_DEBUG, "RSDP found at %p\n", rsdp); + rsdt = (acpi_rsdt_t *) rsdp->rsdt_address; + + end = (char *)rsdt + rsdt->header.length; + printk(BIOS_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, "FACP", 4) == 0) + break; + fadt = NULL; + } + + if (fadt == NULL) + return NULL; + + printk(BIOS_DEBUG, "FADT found at %p\n", fadt); + facs = (acpi_facs_t *)fadt->firmware_ctrl; + + if (facs == NULL) { + printk(BIOS_DEBUG, "No FACS found, wake up from S3 not " + "possible.\n"); + return NULL; + } + + printk(BIOS_DEBUG, "FACS found at %p\n", facs); + wake_vec = (void *)facs->firmware_waking_vector; + printk(BIOS_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; + +#define WAKEUP_BASE 0x600 + +void (*acpi_do_wakeup)(u32 vector, u32 backup_source, u32 backup_target, + u32 backup_size) __attribute__((regparm(0))) = (void *)WAKEUP_BASE; + +extern unsigned char __wakeup, __wakeup_size; + +void acpi_jump_to_wakeup(void *vector) +{ + u32 acpi_backup_memory = (u32)cbmem_find(CBMEM_ID_RESUME); + + if (!acpi_backup_memory) { + printk(BIOS_WARNING, "ACPI: Backup memory missing. " + "No S3 resume.\n"); + return; + } + + // FIXME: This should go into the ACPI backup memory, too. No pork saussages. + /* + * Just restore the SMP trampoline and continue with wakeup on + * assembly level. + */ + memcpy(lowmem_backup_ptr, lowmem_backup, lowmem_backup_size); + + /* Copy wakeup trampoline in place. */ + memcpy((void *)WAKEUP_BASE, &__wakeup, (size_t)&__wakeup_size); + + acpi_do_wakeup((u32)vector, acpi_backup_memory, CONFIG_RAMBASE, + HIGH_MEMORY_SAVE); +} +#endif diff --git a/src/arch/x86/boot/acpigen.c b/src/arch/x86/boot/acpigen.c new file mode 100644 index 0000000000..e8cd724e23 --- /dev/null +++ b/src/arch/x86/boot/acpigen.c @@ -0,0 +1,517 @@ +/* + * 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 as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 + */ + +/* how many nesting we support */ +#define ACPIGEN_LENSTACK_SIZE 10 + +/* if you need to change this, change the acpigen_write_f and + acpigen_patch_len */ + +#define ACPIGEN_MAXLEN 0xfff + +#include <string.h> +#include <arch/acpigen.h> +#include <console/console.h> +#include <device/device.h> + +static char *gencurrent; + +char *len_stack[ACPIGEN_LENSTACK_SIZE]; +int ltop = 0; + +static int acpigen_write_len_f(void) +{ + ASSERT(ltop < (ACPIGEN_LENSTACK_SIZE - 1)) + len_stack[ltop++] = gencurrent; + acpigen_emit_byte(0); + acpigen_emit_byte(0); + return 2; +} + +void acpigen_patch_len(int len) +{ + ASSERT(len <= ACPIGEN_MAXLEN) + ASSERT(ltop > 0) + char *p = len_stack[--ltop]; + /* generate store length for 0xfff max */ + p[0] = (0x40 | (len & 0xf)); + p[1] = (len >> 4 & 0xff); + +} + +void acpigen_set_current(char *curr) +{ + gencurrent = curr; +} + +char *acpigen_get_current(void) +{ + return gencurrent; +} + +int acpigen_emit_byte(unsigned char b) +{ + (*gencurrent++) = b; + return 1; +} + +int acpigen_write_package(int nr_el) +{ + int len; + /* package op */ + acpigen_emit_byte(0x12); + len = acpigen_write_len_f(); + acpigen_emit_byte(nr_el); + return len + 2; +} + +int acpigen_write_byte(unsigned int data) +{ + /* byte op */ + acpigen_emit_byte(0xa); + acpigen_emit_byte(data & 0xff); + return 2; +} + +int acpigen_write_dword(unsigned int data) +{ + /* dword op */ + acpigen_emit_byte(0xc); + acpigen_emit_byte(data & 0xff); + acpigen_emit_byte((data >> 8) & 0xff); + acpigen_emit_byte((data >> 16) & 0xff); + acpigen_emit_byte((data >> 24) & 0xff); + return 5; +} + +int acpigen_write_qword(uint64_t data) +{ + /* qword op */ + acpigen_emit_byte(0xe); + acpigen_emit_byte(data & 0xff); + acpigen_emit_byte((data >> 8) & 0xff); + acpigen_emit_byte((data >> 16) & 0xff); + acpigen_emit_byte((data >> 24) & 0xff); + acpigen_emit_byte((data >> 32) & 0xff); + acpigen_emit_byte((data >> 40) & 0xff); + acpigen_emit_byte((data >> 48) & 0xff); + acpigen_emit_byte((data >> 56) & 0xff); + return 9; +} + +int acpigen_write_name_byte(const char *name, uint8_t val) +{ + int len; + len = acpigen_write_name(name); + len += acpigen_write_byte(val); + return len; +} + +int acpigen_write_name_dword(const char *name, uint32_t val) +{ + int len; + len = acpigen_write_name(name); + len += acpigen_write_dword(val); + return len; +} + +int acpigen_write_name_qword(const char *name, uint64_t val) +{ + int len; + len = acpigen_write_name(name); + len += acpigen_write_qword(val); + return len; +} + +int acpigen_emit_stream(const char *data, int size) +{ + int i; + for (i = 0; i < size; i++) { + acpigen_emit_byte(data[i]); + } + return size; +} + +/* The NameString are bit tricky, each element can be 4 chars, if + less its padded with underscore. Check 18.2.2 and 18.4 + and 5.3 of ACPI specs 3.0 for details +*/ + +static int acpigen_emit_simple_namestring(const char *name) { + int i, len = 0; + char ud[] = "____"; + for (i = 0; i < 4; i++) { + if ((name[i] == '\0') || (name[i] == '.')) { + len += acpigen_emit_stream(ud, 4 - i); + break; + } else { + len += acpigen_emit_byte(name[i]); + } + } + return len; +} + +static int acpigen_emit_double_namestring(const char *name, int dotpos) { + int len = 0; + /* mark dual name prefix */ + len += acpigen_emit_byte(0x2e); + len += acpigen_emit_simple_namestring(name); + len += acpigen_emit_simple_namestring(&name[dotpos + 1]); + return len; +} + +static int acpigen_emit_multi_namestring(const char *name) { + int len = 0, count = 0; + unsigned char *pathlen; + /* mark multi name prefix */ + len += acpigen_emit_byte(0x2f); + len += acpigen_emit_byte(0x0); + pathlen = ((unsigned char *) acpigen_get_current()) - 1; + + while (name[0] != '\0') { + len += acpigen_emit_simple_namestring(name); + /* find end or next entity */ + while ((name[0] != '.') && (name[0] != '\0')) + name++; + /* forward to next */ + if (name[0] == '.') + name++; + count++; + } + + pathlen[0] = count; + return len; +} + + +int acpigen_emit_namestring(const char *namepath) { + int dotcount = 0, i; + int dotpos = 0; + int len = 0; + + /* we can start with a \ */ + if (namepath[0] == '\\') { + len += acpigen_emit_byte('\\'); + namepath++; + } + + /* and there can be any number of ^ */ + while (namepath[0] == '^') { + len += acpigen_emit_byte('^'); + namepath++; + } + + ASSERT(namepath[0] != '\0'); + + i = 0; + while (namepath[i] != '\0') { + if (namepath[i] == '.') { + dotcount++; + dotpos = i; + } + i++; + } + + if (dotcount == 0) { + len += acpigen_emit_simple_namestring(namepath); + } else if (dotcount == 1) { + len += acpigen_emit_double_namestring(namepath, dotpos); + } else { + len += acpigen_emit_multi_namestring(namepath); + } + return len; +} + +int acpigen_write_name(const char *name) +{ + int len; + /* name op */ + len = acpigen_emit_byte(0x8); + return len + acpigen_emit_namestring(name); +} + +int acpigen_write_scope(const char *name) +{ + int len; + /* scope op */ + len = acpigen_emit_byte(0x10); + len += acpigen_write_len_f(); + return len + acpigen_emit_namestring(name); +} + +int acpigen_write_processor(u8 cpuindex, u32 pblock_addr, u8 pblock_len) +{ +/* + Processor (\_PR.CPUcpuindex, cpuindex, pblock_addr, pblock_len) + { +*/ + char pscope[16]; + int len; + /* processor op */ + acpigen_emit_byte(0x5b); + acpigen_emit_byte(0x83); + len = acpigen_write_len_f(); + + sprintf(pscope, "\\_PR.CPU%x", (unsigned int) cpuindex); + len += acpigen_emit_namestring(pscope); + acpigen_emit_byte(cpuindex); + acpigen_emit_byte(pblock_addr & 0xff); + acpigen_emit_byte((pblock_addr >> 8) & 0xff); + acpigen_emit_byte((pblock_addr >> 16) & 0xff); + acpigen_emit_byte((pblock_addr >> 24) & 0xff); + acpigen_emit_byte(pblock_len); + return 6 + 2 + len; +} + +int acpigen_write_empty_PCT(void) +{ +/* + Name (_PCT, Package (0x02) + { + ResourceTemplate () + { + Register (FFixedHW, + 0x00, // Bit Width + 0x00, // Bit Offset + 0x0000000000000000, // Address + ,) + }, + + ResourceTemplate () + { + Register (FFixedHW, + 0x00, // Bit Width + 0x00, // Bit Offset + 0x0000000000000000, // Address + ,) + } + }) +*/ + static char stream[] = { + 0x08, 0x5F, 0x50, 0x43, 0x54, 0x12, 0x2C, /* 00000030 "0._PCT.," */ + 0x02, 0x11, 0x14, 0x0A, 0x11, 0x82, 0x0C, 0x00, /* 00000038 "........" */ + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000040 "........" */ + 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x11, 0x14, /* 00000048 "....y..." */ + 0x0A, 0x11, 0x82, 0x0C, 0x00, 0x7F, 0x00, 0x00, /* 00000050 "........" */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000058 "........" */ + 0x00, 0x79, 0x00 + }; + return acpigen_emit_stream(stream, ARRAY_SIZE(stream)); +} + +/* generates a func with max supported P states */ +int acpigen_write_PPC(u8 nr) +{ +/* + Method (_PPC, 0, NotSerialized) + { + Return (nr) + } +*/ + int len; + /* method op */ + acpigen_emit_byte(0x14); + len = acpigen_write_len_f(); + len += acpigen_emit_namestring("_PPC"); + /* no fnarg */ + acpigen_emit_byte(0x00); + /* return */ + acpigen_emit_byte(0xa4); + /* arg */ + len += acpigen_write_byte(nr); + /* add all single bytes */ + len += 3; + acpigen_patch_len(len - 1); + return len; +} + +int acpigen_write_PSS_package(u32 coreFreq, u32 power, u32 transLat, + u32 busmLat, u32 control, u32 status) +{ + int len; + len = acpigen_write_package(6); + len += acpigen_write_dword(coreFreq); + len += acpigen_write_dword(power); + len += acpigen_write_dword(transLat); + len += acpigen_write_dword(busmLat); + len += acpigen_write_dword(control); + len += acpigen_write_dword(status); + //pkglen without the len opcode + acpigen_patch_len(len - 1); + return len; +} + +int acpigen_write_PSD_package(u32 domain, u32 numprocs, PSD_coord coordtype) +{ + int len, lenh, lenp; + lenh = acpigen_write_name("_PSD"); + lenp = acpigen_write_package(1); + len = acpigen_write_package(5); + len += acpigen_write_byte(5); // 5 values + len += acpigen_write_byte(0); // revision 0 + len += acpigen_write_dword(domain); + len += acpigen_write_dword(coordtype); + len += acpigen_write_dword(numprocs); + acpigen_patch_len(len - 1); + len += lenp; + acpigen_patch_len(len - 1); + return len + lenh; +} + +int acpigen_write_mem32fixed(int readwrite, u32 base, u32 size) +{ + /* + * acpi 4.0 section 6.4.3.4: 32-Bit Fixed Memory Range Descriptor + * Byte 0: + * Bit7 : 1 => big item + * Bit6-0: 0000110 (0x6) => 32-bit fixed memory + */ + acpigen_emit_byte(0x86); + /* Byte 1+2: length (0x0009) */ + acpigen_emit_byte(0x09); + acpigen_emit_byte(0x00); + /* bit1-7 are ignored */ + acpigen_emit_byte(readwrite ? 0x01 : 0x00); + acpigen_emit_byte(base & 0xff); + acpigen_emit_byte((base >> 8) & 0xff); + acpigen_emit_byte((base >> 16) & 0xff); + acpigen_emit_byte((base >> 24) & 0xff); + acpigen_emit_byte(size & 0xff); + acpigen_emit_byte((size >> 8) & 0xff); + acpigen_emit_byte((size >> 16) & 0xff); + acpigen_emit_byte((size >> 24) & 0xff); + return 12; +} + +int acpigen_write_io16(u16 min, u16 max, u8 align, u8 len, u8 decode16) +{ + /* + * acpi 4.0 section 6.4.2.6: I/O Port Descriptor + * Byte 0: + * Bit7 : 0 => small item + * Bit6-3: 1000 (0x8) => I/O port descriptor + * Bit2-0: 111 (0x7) => 7 Bytes long + */ + acpigen_emit_byte(0x47); + /* does the device decode all 16 or just 10 bits? */ + /* bit1-7 are ignored */ + acpigen_emit_byte(decode16 ? 0x01 : 0x00); + /* minimum base address the device may be configured for */ + acpigen_emit_byte(min & 0xff); + acpigen_emit_byte((min >> 8) & 0xff); + /* maximum base address the device may be configured for */ + acpigen_emit_byte(max & 0xff); + acpigen_emit_byte((max >> 8) & 0xff); + /* alignment for min base */ + acpigen_emit_byte(align & 0xff); + acpigen_emit_byte(len & 0xff); + return 8; +} + +int acpigen_write_resourcetemplate_header(void) +{ + int len; + /* + * A ResourceTemplate() is a Buffer() with a + * (Byte|Word|DWord) containing the length, followed by one or more + * resource items, terminated by the end tag + * (small item 0xf, len 1) + */ + len = acpigen_emit_byte(0x11); /* Buffer opcode */ + len += acpigen_write_len_f(); + len += acpigen_emit_byte(0x0b); /* Word opcode */ + len_stack[ltop++] = acpigen_get_current(); + len += acpigen_emit_byte(0x00); + len += acpigen_emit_byte(0x00); + return len; +} + +int acpigen_write_resourcetemplate_footer(int len) +{ + char *p = len_stack[--ltop]; + /* + * end tag (acpi 4.0 Section 6.4.2.8) + * 0x79 <checksum> + * 0x00 is treated as a good checksum according to the spec + * and is what iasl generates. + */ + len += acpigen_emit_byte(0x79); + len += acpigen_emit_byte(0x00); + /* patch len word */ + p[0] = (len-6) & 0xff; + p[1] = ((len-6) >> 8) & 0xff; + /* patch len field */ + acpigen_patch_len(len-1); + return 2; +} + +static void acpigen_add_mainboard_rsvd_mem32(void *gp, struct device *dev, + struct resource *res) +{ + acpigen_write_mem32fixed(0, res->base, res->size); +} + +static void acpigen_add_mainboard_rsvd_io(void *gp, struct device *dev, + struct resource *res) +{ + resource_t base = res->base; + resource_t size = res->size; + while (size > 0) { + resource_t sz = size > 255 ? 255 : size; + acpigen_write_io16(base, base, 0, sz, 1); + size -= sz; + base += sz; + } +} + +int acpigen_write_mainboard_resource_template(void) +{ + int len; + char *start; + char *end; + len = acpigen_write_resourcetemplate_header(); + start = acpigen_get_current(); + + /* Add reserved memory ranges */ + search_global_resources( + IORESOURCE_MEM | IORESOURCE_RESERVE, + IORESOURCE_MEM | IORESOURCE_RESERVE, + acpigen_add_mainboard_rsvd_mem32, 0); + + /* Add reserved io ranges */ + search_global_resources( + IORESOURCE_IO | IORESOURCE_RESERVE, + IORESOURCE_IO | IORESOURCE_RESERVE, + acpigen_add_mainboard_rsvd_io, 0); + + end = acpigen_get_current(); + len += end-start; + len += acpigen_write_resourcetemplate_footer(len); + return len; +} + +int acpigen_write_mainboard_resources(const char *scope, const char *name) +{ + int len; + len = acpigen_write_scope(scope); + len += acpigen_write_name(name); + len += acpigen_write_mainboard_resource_template(); + acpigen_patch_len(len - 1); + return len; +} diff --git a/src/arch/x86/boot/boot.c b/src/arch/x86/boot/boot.c new file mode 100644 index 0000000000..d9cb02e776 --- /dev/null +++ b/src/arch/x86/boot/boot.c @@ -0,0 +1,186 @@ +#include <console/console.h> +#include <ip_checksum.h> +#include <boot/elf.h> +#include <boot/elf_boot.h> +#include <string.h> +#include <cpu/x86/multiboot.h> + + +#ifndef CMD_LINE +#define CMD_LINE "" +#endif + + + +#define UPSZ(X) ((sizeof(X) + 3) &~3) + +static struct { + Elf_Bhdr hdr; + Elf_Nhdr ft_hdr; + unsigned char ft_desc[UPSZ(FIRMWARE_TYPE)]; + Elf_Nhdr bl_hdr; + unsigned char bl_desc[UPSZ(BOOTLOADER)]; + Elf_Nhdr blv_hdr; + unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)]; + Elf_Nhdr cmd_hdr; + unsigned char cmd_desc[UPSZ(CMD_LINE)]; +} elf_boot_notes = { + .hdr = { + .b_signature = 0x0E1FB007, + .b_size = sizeof(elf_boot_notes), + .b_checksum = 0, + .b_records = 4, + }, + .ft_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(FIRMWARE_TYPE), + .n_type = EBN_FIRMWARE_TYPE, + }, + .ft_desc = FIRMWARE_TYPE, + .bl_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER), + .n_type = EBN_BOOTLOADER_NAME, + }, + .bl_desc = BOOTLOADER, + .blv_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER_VERSION), + .n_type = EBN_BOOTLOADER_VERSION, + }, + .blv_desc = BOOTLOADER_VERSION, + .cmd_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(CMD_LINE), + .n_type = EBN_COMMAND_LINE, + }, + .cmd_desc = CMD_LINE, +}; + + +int elf_check_arch(Elf_ehdr *ehdr) +{ + return ( + ((ehdr->e_machine == EM_386) || (ehdr->e_machine == EM_486)) && + (ehdr->e_ident[EI_CLASS] == ELFCLASS32) && + (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + ); + +} + +void jmp_to_elf_entry(void *entry, unsigned long buffer, unsigned long size) +{ + extern unsigned char _ram_seg, _eram_seg; + unsigned long lb_start, lb_size; + unsigned long adjust, adjusted_boot_notes; + + elf_boot_notes.hdr.b_checksum = + compute_ip_checksum(&elf_boot_notes, sizeof(elf_boot_notes)); + + lb_start = (unsigned long)&_ram_seg; + lb_size = (unsigned long)(&_eram_seg - &_ram_seg); + adjust = buffer + size - lb_start; + + adjusted_boot_notes = (unsigned long)&elf_boot_notes; + adjusted_boot_notes += adjust; + + printk(BIOS_SPEW, "entry = 0x%08lx\n", (unsigned long)entry); + printk(BIOS_SPEW, "lb_start = 0x%08lx\n", lb_start); + printk(BIOS_SPEW, "lb_size = 0x%08lx\n", lb_size); + printk(BIOS_SPEW, "adjust = 0x%08lx\n", adjust); + printk(BIOS_SPEW, "buffer = 0x%08lx\n", buffer); + printk(BIOS_SPEW, " elf_boot_notes = 0x%08lx\n", (unsigned long)&elf_boot_notes); + printk(BIOS_SPEW, "adjusted_boot_notes = 0x%08lx\n", adjusted_boot_notes); + + /* Jump to kernel */ + __asm__ __volatile__( + " cld \n\t" + /* Save the callee save registers... */ + " pushl %%esi\n\t" + " pushl %%edi\n\t" + " pushl %%ebx\n\t" + /* Save the parameters I was passed */ + " pushl $0\n\t" /* 20 adjust */ + " pushl %0\n\t" /* 16 lb_start */ + " pushl %1\n\t" /* 12 buffer */ + " pushl %2\n\t" /* 8 lb_size */ + " pushl %3\n\t" /* 4 entry */ + " pushl %4\n\t" /* 0 elf_boot_notes */ + /* Compute the adjustment */ + " xorl %%eax, %%eax\n\t" + " subl 16(%%esp), %%eax\n\t" + " addl 12(%%esp), %%eax\n\t" + " addl 8(%%esp), %%eax\n\t" + " movl %%eax, 20(%%esp)\n\t" + /* Place a copy of coreboot in its new location */ + /* Move ``longs'' the coreboot size is 4 byte aligned */ + " movl 12(%%esp), %%edi\n\t" + " addl 8(%%esp), %%edi\n\t" + " movl 16(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\n" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Adjust the stack pointer to point into the new coreboot image */ + " addl 20(%%esp), %%esp\n\t" + /* Adjust the instruction pointer to point into the new coreboot image */ + " movl $1f, %%eax\n\t" + " addl 20(%%esp), %%eax\n\t" + " jmp *%%eax\n\t" + "1: \n\t" + + /* Copy the coreboot bounce buffer over coreboot */ + /* Move ``longs'' the coreboot size is 4 byte aligned */ + " movl 16(%%esp), %%edi\n\t" + " movl 12(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\t" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Now jump to the loaded image */ + " movl %5, %%eax\n\t" + " movl 0(%%esp), %%ebx\n\t" + " call *4(%%esp)\n\t" + + /* The loaded image returned? */ + " cli \n\t" + " cld \n\t" + + /* Copy the saved copy of coreboot where coreboot runs */ + /* Move ``longs'' the coreboot size is 4 byte aligned */ + " movl 16(%%esp), %%edi\n\t" + " movl 12(%%esp), %%esi\n\t" + " addl 8(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\t" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Adjust the stack pointer to point into the old coreboot image */ + " subl 20(%%esp), %%esp\n\t" + + /* Adjust the instruction pointer to point into the old coreboot image */ + " movl $1f, %%eax\n\t" + " subl 20(%%esp), %%eax\n\t" + " jmp *%%eax\n\t" + "1: \n\t" + + /* Drop the parameters I was passed */ + " addl $24, %%esp\n\t" + + /* Restore the callee save registers */ + " popl %%ebx\n\t" + " popl %%edi\n\t" + " popl %%esi\n\t" + + :: + "ri" (lb_start), "ri" (buffer), "ri" (lb_size), + "ri" (entry), +#if CONFIG_MULTIBOOT + "ri"(mbi), "ri" (MB_MAGIC2) +#else + "ri"(adjusted_boot_notes), "ri" (0x0E1FB007) +#endif + ); +} + + diff --git a/src/arch/x86/boot/coreboot_table.c b/src/arch/x86/boot/coreboot_table.c new file mode 100644 index 0000000000..484340c96a --- /dev/null +++ b/src/arch/x86/boot/coreboot_table.c @@ -0,0 +1,600 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2003-2004 Eric Biederman + * Copyright (C) 2005-2010 coresystems GmbH + * + * 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; version 2 of + * the License. + * + * 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 + */ + +#include <console/console.h> +#include <ip_checksum.h> +#include <boot/tables.h> +#include <boot/coreboot_tables.h> +#include <arch/coreboot_tables.h> +#include <string.h> +#include <version.h> +#include <device/device.h> +#include <stdlib.h> +#if (CONFIG_USE_OPTION_TABLE == 1) +#include <option_table.h> +#endif + +static struct lb_header *lb_table_init(unsigned long addr) +{ + struct lb_header *header; + + /* 16 byte align the address */ + addr += 15; + addr &= ~15; + + header = (void *)addr; + header->signature[0] = 'L'; + header->signature[1] = 'B'; + header->signature[2] = 'I'; + header->signature[3] = 'O'; + header->header_bytes = sizeof(*header); + header->header_checksum = 0; + header->table_bytes = 0; + header->table_checksum = 0; + header->table_entries = 0; + return header; +} + +static struct lb_record *lb_first_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = (void *)(((char *)header) + sizeof(*header)); + return rec; +} + +static struct lb_record *lb_last_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = (void *)(((char *)header) + sizeof(*header) + header->table_bytes); + return rec; +} + +#if 0 +static struct lb_record *lb_next_record(struct lb_record *rec) +{ + rec = (void *)(((char *)rec) + rec->size); + return rec; +} +#endif + +static struct lb_record *lb_new_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = lb_last_record(header); + if (header->table_entries) { + header->table_bytes += rec->size; + } + rec = lb_last_record(header); + header->table_entries++; + rec->tag = LB_TAG_UNUSED; + rec->size = sizeof(*rec); + return rec; +} + + +static struct lb_memory *lb_memory(struct lb_header *header) +{ + struct lb_record *rec; + struct lb_memory *mem; + rec = lb_new_record(header); + mem = (struct lb_memory *)rec; + mem->tag = LB_TAG_MEMORY; + mem->size = sizeof(*mem); + return mem; +} + +static struct lb_serial *lb_serial(struct lb_header *header) +{ +#if CONFIG_CONSOLE_SERIAL8250 + struct lb_record *rec; + struct lb_serial *serial; + rec = lb_new_record(header); + serial = (struct lb_serial *)rec; + serial->tag = LB_TAG_SERIAL; + serial->size = sizeof(*serial); + serial->ioport = CONFIG_TTYS0_BASE; + serial->baud = CONFIG_TTYS0_BAUD; + return serial; +#else + return header; +#endif +} + +static void add_console(struct lb_header *header, u16 consoletype) +{ + struct lb_console *console; + + console = (struct lb_console *)lb_new_record(header); + console->tag = LB_TAG_CONSOLE; + console->size = sizeof(*console); + console->type = consoletype; +} + +static void lb_console(struct lb_header *header) +{ +#if CONFIG_CONSOLE_SERIAL8250 + add_console(header, LB_TAG_CONSOLE_SERIAL8250); +#endif +#if CONFIG_CONSOLE_LOGBUF + add_console(header, LB_TAG_CONSOLE_LOGBUF); +#endif +#if CONFIG_USBDEBUG + add_console(header, LB_TAG_CONSOLE_EHCI); +#endif +} + +static void lb_framebuffer(struct lb_header *header) +{ +#if defined(CONFIG_BOOTSPLASH) && CONFIG_BOOTSPLASH && CONFIG_COREBOOT_KEEP_FRAMEBUFFER + void fill_lb_framebuffer(struct lb_framebuffer *framebuffer); + + struct lb_framebuffer *framebuffer; + framebuffer = (struct lb_framebuffer *)lb_new_record(header); + framebuffer->tag = LB_TAG_FRAMEBUFFER; + framebuffer->size = sizeof(*framebuffer); + fill_lb_framebuffer(framebuffer); +#endif +} + +static struct lb_mainboard *lb_mainboard(struct lb_header *header) +{ + struct lb_record *rec; + struct lb_mainboard *mainboard; + rec = lb_new_record(header); + mainboard = (struct lb_mainboard *)rec; + mainboard->tag = LB_TAG_MAINBOARD; + + mainboard->size = (sizeof(*mainboard) + + strlen(mainboard_vendor) + 1 + + strlen(mainboard_part_number) + 1 + + 3) & ~3; + + mainboard->vendor_idx = 0; + mainboard->part_number_idx = strlen(mainboard_vendor) + 1; + + memcpy(mainboard->strings + mainboard->vendor_idx, + mainboard_vendor, strlen(mainboard_vendor) + 1); + memcpy(mainboard->strings + mainboard->part_number_idx, + mainboard_part_number, strlen(mainboard_part_number) + 1); + + return mainboard; +} + +#if (CONFIG_USE_OPTION_TABLE == 1) +static struct cmos_checksum *lb_cmos_checksum(struct lb_header *header) +{ + struct lb_record *rec; + struct cmos_checksum *cmos_checksum; + rec = lb_new_record(header); + cmos_checksum = (struct cmos_checksum *)rec; + cmos_checksum->tag = LB_TAG_OPTION_CHECKSUM; + + cmos_checksum->size = (sizeof(*cmos_checksum)); + + cmos_checksum->range_start = LB_CKS_RANGE_START * 8; + cmos_checksum->range_end = ( LB_CKS_RANGE_END * 8 ) + 7; + cmos_checksum->location = LB_CKS_LOC * 8; + cmos_checksum->type = CHECKSUM_PCBIOS; + + return cmos_checksum; +} +#endif + +static void lb_strings(struct lb_header *header) +{ + static const struct { + uint32_t tag; + const char *string; + } strings[] = { + { LB_TAG_VERSION, coreboot_version, }, + { LB_TAG_EXTRA_VERSION, coreboot_extra_version, }, + { LB_TAG_BUILD, coreboot_build, }, + { LB_TAG_COMPILE_TIME, coreboot_compile_time, }, + { LB_TAG_COMPILE_BY, coreboot_compile_by, }, + { LB_TAG_COMPILE_HOST, coreboot_compile_host, }, + { LB_TAG_COMPILE_DOMAIN, coreboot_compile_domain, }, + { LB_TAG_COMPILER, coreboot_compiler, }, + { LB_TAG_LINKER, coreboot_linker, }, + { LB_TAG_ASSEMBLER, coreboot_assembler, }, + }; + unsigned int i; + for(i = 0; i < ARRAY_SIZE(strings); i++) { + struct lb_string *rec; + size_t len; + rec = (struct lb_string *)lb_new_record(header); + len = strlen(strings[i].string); + rec->tag = strings[i].tag; + rec->size = (sizeof(*rec) + len + 1 + 3) & ~3; + memcpy(rec->string, strings[i].string, len+1); + } + +} + +#if CONFIG_WRITE_HIGH_TABLES == 1 +static struct lb_forward *lb_forward(struct lb_header *header, struct lb_header *next_header) +{ + struct lb_record *rec; + struct lb_forward *forward; + rec = lb_new_record(header); + forward = (struct lb_forward *)rec; + forward->tag = LB_TAG_FORWARD; + forward->size = sizeof(*forward); + forward->forward = (uint64_t)(unsigned long)next_header; + return forward; +} +#endif + +void lb_memory_range(struct lb_memory *mem, + uint32_t type, uint64_t start, uint64_t size) +{ + int entries; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + mem->map[entries].start = pack_lb64(start); + mem->map[entries].size = pack_lb64(size); + mem->map[entries].type = type; + mem->size += sizeof(mem->map[0]); +} + +static void lb_reserve_table_memory(struct lb_header *head) +{ + struct lb_record *last_rec; + struct lb_memory *mem; + uint64_t start; + uint64_t end; + int i, entries; + + last_rec = lb_last_record(head); + mem = get_lb_mem(); + start = (unsigned long)head; + end = (unsigned long)last_rec; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + /* Resize the right two memory areas so this table is in + * a reserved area of memory. Everything has been carefully + * setup so that is all we need to do. + */ + for(i = 0; i < entries; i++ ) { + uint64_t map_start = unpack_lb64(mem->map[i].start); + uint64_t map_end = map_start + unpack_lb64(mem->map[i].size); + /* Does this area need to be expanded? */ + if (map_end == start) { + mem->map[i].size = pack_lb64(end - map_start); + } + /* Does this area need to be contracted? */ + else if (map_start == start) { + mem->map[i].start = pack_lb64(end); + mem->map[i].size = pack_lb64(map_end - end); + } + } +} + +static unsigned long lb_table_fini(struct lb_header *head, int fixup) +{ + struct lb_record *rec, *first_rec; + rec = lb_last_record(head); + if (head->table_entries) { + head->table_bytes += rec->size; + } + + if (fixup) + lb_reserve_table_memory(head); + + first_rec = lb_first_record(head); + head->table_checksum = compute_ip_checksum(first_rec, head->table_bytes); + head->header_checksum = 0; + head->header_checksum = compute_ip_checksum(head, sizeof(*head)); + printk(BIOS_DEBUG, "Wrote coreboot table at: %p - %p checksum %x\n", + head, rec, head->table_checksum); + return (unsigned long)rec; +} + +static void lb_cleanup_memory_ranges(struct lb_memory *mem) +{ + int entries; + int i, j; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + + /* Sort the lb memory ranges */ + for(i = 0; i < entries; i++) { + uint64_t entry_start = unpack_lb64(mem->map[i].start); + for(j = i; j < entries; j++) { + uint64_t temp_start = unpack_lb64(mem->map[j].start); + if (temp_start < entry_start) { + struct lb_memory_range tmp; + tmp = mem->map[i]; + mem->map[i] = mem->map[j]; + mem->map[j] = tmp; + } + } + } + + /* Merge adjacent entries */ + for(i = 0; (i + 1) < entries; i++) { + uint64_t start, end, nstart, nend; + if (mem->map[i].type != mem->map[i + 1].type) { + continue; + } + start = unpack_lb64(mem->map[i].start); + end = start + unpack_lb64(mem->map[i].size); + nstart = unpack_lb64(mem->map[i + 1].start); + nend = nstart + unpack_lb64(mem->map[i + 1].size); + if ((start <= nstart) && (end > nstart)) { + if (start > nstart) { + start = nstart; + } + if (end < nend) { + end = nend; + } + /* Record the new region size */ + mem->map[i].start = pack_lb64(start); + mem->map[i].size = pack_lb64(end - start); + + /* Delete the entry I have merged with */ + memmove(&mem->map[i + 1], &mem->map[i + 2], + ((entries - i - 2) * sizeof(mem->map[0]))); + mem->size -= sizeof(mem->map[0]); + entries -= 1; + /* See if I can merge with the next entry as well */ + i -= 1; + } + } +} + +static void lb_remove_memory_range(struct lb_memory *mem, + uint64_t start, uint64_t size) +{ + uint64_t end; + int entries; + int i; + + end = start + size; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + + /* Remove a reserved area from the memory map */ + for(i = 0; i < entries; i++) { + uint64_t map_start = unpack_lb64(mem->map[i].start); + uint64_t map_end = map_start + unpack_lb64(mem->map[i].size); + if ((start <= map_start) && (end >= map_end)) { + /* Remove the completely covered range */ + memmove(&mem->map[i], &mem->map[i + 1], + ((entries - i - 1) * sizeof(mem->map[0]))); + mem->size -= sizeof(mem->map[0]); + entries -= 1; + /* Since the index will disappear revisit what will appear here */ + i -= 1; + } + else if ((start > map_start) && (end < map_end)) { + /* Split the memory range */ + memmove(&mem->map[i + 1], &mem->map[i], + ((entries - i) * sizeof(mem->map[0]))); + mem->size += sizeof(mem->map[0]); + entries += 1; + /* Update the first map entry */ + mem->map[i].size = pack_lb64(start - map_start); + /* Update the second map entry */ + mem->map[i + 1].start = pack_lb64(end); + mem->map[i + 1].size = pack_lb64(map_end - end); + /* Don't bother with this map entry again */ + i += 1; + } + else if ((start <= map_start) && (end > map_start)) { + /* Shrink the start of the memory range */ + mem->map[i].start = pack_lb64(end); + mem->map[i].size = pack_lb64(map_end - end); + } + else if ((start < map_end) && (start > map_start)) { + /* Shrink the end of the memory range */ + mem->map[i].size = pack_lb64(start - map_start); + } + } +} + +/* This function is used in mainboard specific code, too */ +void lb_add_memory_range(struct lb_memory *mem, + uint32_t type, uint64_t start, uint64_t size) +{ + lb_remove_memory_range(mem, start, size); + lb_memory_range(mem, type, start, size); + lb_cleanup_memory_ranges(mem); +} + +static void lb_dump_memory_ranges(struct lb_memory *mem) +{ + int entries; + int i; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + + printk(BIOS_DEBUG, "coreboot memory table:\n"); + for(i = 0; i < entries; i++) { + uint64_t entry_start = unpack_lb64(mem->map[i].start); + uint64_t entry_size = unpack_lb64(mem->map[i].size); + const char *entry_type; + + switch (mem->map[i].type) { + case LB_MEM_RAM: entry_type="RAM"; break; + case LB_MEM_RESERVED: entry_type="RESERVED"; break; + case LB_MEM_ACPI: entry_type="ACPI"; break; + case LB_MEM_NVS: entry_type="NVS"; break; + case LB_MEM_UNUSABLE: entry_type="UNUSABLE"; break; + case LB_MEM_VENDOR_RSVD: entry_type="VENDOR RESERVED"; break; + case LB_MEM_TABLE: entry_type="CONFIGURATION TABLES"; break; + default: entry_type="UNKNOWN!"; break; + } + + printk(BIOS_DEBUG, "%2d. %016llx-%016llx: %s\n", + i, entry_start, entry_start+entry_size-1, entry_type); + + } +} + + +/* Routines to extract part so the coreboot table or + * information from the coreboot table after we have written it. + * Currently get_lb_mem relies on a global we can change the + * implementaiton. + */ +static struct lb_memory *mem_ranges = 0; +struct lb_memory *get_lb_mem(void) +{ + return mem_ranges; +} + +static void build_lb_mem_range(void *gp, struct device *dev, struct resource *res) +{ + struct lb_memory *mem = gp; + lb_memory_range(mem, LB_MEM_RAM, res->base, res->size); +} + +static struct lb_memory *build_lb_mem(struct lb_header *head) +{ + struct lb_memory *mem; + + /* Record where the lb memory ranges will live */ + mem = lb_memory(head); + mem_ranges = mem; + + /* Build the raw table of memory */ + search_global_resources( + IORESOURCE_MEM | IORESOURCE_CACHEABLE, IORESOURCE_MEM | IORESOURCE_CACHEABLE, + build_lb_mem_range, mem); + lb_cleanup_memory_ranges(mem); + return mem; +} + +static void lb_add_rsvd_range(void *gp, struct device *dev, struct resource *res) +{ + struct lb_memory *mem = gp; + lb_add_memory_range(mem, LB_MEM_RESERVED, res->base, res->size); +} + +static void add_lb_reserved(struct lb_memory *mem) +{ + /* Add reserved ranges */ + search_global_resources( + IORESOURCE_MEM | IORESOURCE_RESERVE, IORESOURCE_MEM | IORESOURCE_RESERVE, + lb_add_rsvd_range, mem); +} + +#if CONFIG_WRITE_HIGH_TABLES == 1 +extern uint64_t high_tables_base, high_tables_size; +#endif + +unsigned long write_coreboot_table( + unsigned long low_table_start, unsigned long low_table_end, + unsigned long rom_table_start, unsigned long rom_table_end) +{ + struct lb_header *head; + struct lb_memory *mem; + +#if CONFIG_WRITE_HIGH_TABLES == 1 + printk(BIOS_DEBUG, "Writing high table forward entry at 0x%08lx\n", + low_table_end); + head = lb_table_init(low_table_end); + lb_forward(head, (struct lb_header*)rom_table_end); + + low_table_end = (unsigned long) lb_table_fini(head, 0); + printk(BIOS_DEBUG, "New low_table_end: 0x%08lx\n", low_table_end); + printk(BIOS_DEBUG, "Now going to write high coreboot table at 0x%08lx\n", + rom_table_end); + + head = lb_table_init(rom_table_end); + rom_table_end = (unsigned long)head; + printk(BIOS_DEBUG, "rom_table_end = 0x%08lx\n", rom_table_end); +#else + if(low_table_end > (0x1000 - sizeof(struct lb_header))) { /* after 4K */ + /* We need to put lbtable on to [0xf0000,0x100000) */ + head = lb_table_init(rom_table_end); + rom_table_end = (unsigned long)head; + } else { + head = lb_table_init(low_table_end); + low_table_end = (unsigned long)head; + } +#endif + + printk(BIOS_DEBUG, "Adjust low_table_end from 0x%08lx to ", low_table_end); + low_table_end += 0xfff; // 4K aligned + low_table_end &= ~0xfff; + printk(BIOS_DEBUG, "0x%08lx \n", low_table_end); + + /* The Linux kernel assumes this region is reserved */ + printk(BIOS_DEBUG, "Adjust rom_table_end from 0x%08lx to ", rom_table_end); + rom_table_end += 0xffff; // 64K align + rom_table_end &= ~0xffff; + printk(BIOS_DEBUG, "0x%08lx \n", rom_table_end); + +#if (CONFIG_USE_OPTION_TABLE == 1) + { + struct lb_record *rec_dest = lb_new_record(head); + /* Copy the option config table, it's already a lb_record... */ + memcpy(rec_dest, &option_table, option_table.size); + /* Create cmos checksum entry in coreboot table */ + lb_cmos_checksum(head); + } +#endif + /* Record where RAM is located */ + mem = build_lb_mem(head); + + /* Record the mptable and the the lb_table (This will be adjusted later) */ + lb_add_memory_range(mem, LB_MEM_TABLE, + low_table_start, low_table_end - low_table_start); + + /* Record the pirq table, acpi tables, and maybe the mptable */ + lb_add_memory_range(mem, LB_MEM_TABLE, + rom_table_start, rom_table_end-rom_table_start); + +#if CONFIG_WRITE_HIGH_TABLES == 1 + printk(BIOS_DEBUG, "Adding high table area\n"); + // should this be LB_MEM_ACPI? + lb_add_memory_range(mem, LB_MEM_TABLE, + high_tables_base, high_tables_size); +#endif + + /* Add reserved regions */ + add_lb_reserved(mem); + +#if (CONFIG_HAVE_MAINBOARD_RESOURCES == 1) + add_mainboard_resources(mem); +#endif + + lb_dump_memory_ranges(mem); + + /* Note: + * I assume that there is always memory at immediately after + * the low_table_end. This means that after I setup the coreboot table. + * I can trivially fixup the reserved memory ranges to hold the correct + * size of the coreboot table. + */ + + /* Record our motherboard */ + lb_mainboard(head); + /* Record the serial port, if present */ + lb_serial(head); + /* Record our console setup */ + lb_console(head); + /* Record our various random string information */ + lb_strings(head); + /* Record our framebuffer */ + lb_framebuffer(head); + + /* Remember where my valid memory ranges are */ + return lb_table_fini(head, 1); + +} diff --git a/src/arch/x86/boot/gdt.c b/src/arch/x86/boot/gdt.c new file mode 100644 index 0000000000..b425ade59d --- /dev/null +++ b/src/arch/x86/boot/gdt.c @@ -0,0 +1,60 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * 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; version 2 of the License. + * + * 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 + */ + +#include <types.h> +#include <string.h> +#include <cbmem.h> +#include <lib.h> +#include <console/console.h> + +// Global Descriptor Table, defined in c_start.S +extern char gdt; +extern char gdt_end; + +/* i386 lgdt argument */ +struct gdtarg { + u16 limit; + u32 base; +} __attribute__((packed)); + +// Copy GDT to new location and reload it +void move_gdt(void) +{ + void *newgdt; + u16 num_gdt_bytes = &gdt_end - &gdt; + struct gdtarg gdtarg; + + newgdt = cbmem_find(CBMEM_ID_GDT); + if (!newgdt) { + newgdt = cbmem_add(CBMEM_ID_GDT, ALIGN(num_gdt_bytes, 512)); + if (!newgdt) { + printk(BIOS_ERR, "Error: Could not relocate GDT.\n"); + return; + } + printk(BIOS_DEBUG, "Moving GDT to %p...", newgdt); + memcpy((void*)newgdt, &gdt, num_gdt_bytes); + } + + gdtarg.base = (u32)newgdt; + gdtarg.limit = num_gdt_bytes - 1; + + __asm__ __volatile__ ("lgdt %0\n\t" : : "m" (gdtarg)); + printk(BIOS_DEBUG, "ok\n"); +} + diff --git a/src/arch/x86/boot/mpspec.c b/src/arch/x86/boot/mpspec.c new file mode 100644 index 0000000000..70bc5401fd --- /dev/null +++ b/src/arch/x86/boot/mpspec.c @@ -0,0 +1,383 @@ +#include <console/console.h> +#include <device/path.h> +#include <device/pci_ids.h> +#include <cpu/cpu.h> +#include <arch/smp/mpspec.h> +#include <string.h> +#include <arch/cpu.h> +#include <cpu/x86/lapic.h> + +/* Initialize the specified "mc" struct with initial values. */ +void mptable_init(struct mp_config_table *mc, const char *productid, + u32 lapic_addr) +{ + /* Error out if 'product_id' length doesn't match exactly. */ + if (strlen(productid) != 12) + die("ERROR: 'productid' must be 12 bytes long!"); + + memset(mc, 0, sizeof(*mc)); + memcpy(mc->mpc_signature, MPC_SIGNATURE, 4); + mc->mpc_length = sizeof(*mc); /* Initially just the header size. */ + mc->mpc_spec = 0x04; /* MultiProcessor specification 1.4 */ + mc->mpc_checksum = 0; /* Not yet computed. */ + memcpy(mc->mpc_oem, "COREBOOT", 8); + memcpy(mc->mpc_productid, productid, 12); + mc->mpc_oemptr = 0; + mc->mpc_oemsize = 0; + mc->mpc_entry_count = 0; /* No entries yet... */ + mc->mpc_lapic = lapic_addr; + mc->mpe_length = 0; + mc->mpe_checksum = 0; + mc->reserved = 0; +} + +unsigned char smp_compute_checksum(void *v, int len) +{ + unsigned char *bytes; + unsigned char checksum; + int i; + bytes = v; + checksum = 0; + for(i = 0; i < len; i++) { + checksum -= bytes[i]; + } + return checksum; +} + +void *smp_write_floating_table(unsigned long addr) +{ + /* 16 byte align the table address */ + addr = (addr + 0xf) & (~0xf); + return smp_write_floating_table_physaddr(addr, addr + SMP_FLOATING_TABLE_LEN); +} + +void *smp_write_floating_table_physaddr(unsigned long addr, unsigned long mpf_physptr) +{ + struct intel_mp_floating *mf; + void *v; + + v = (void *)addr; + mf = v; + mf->mpf_signature[0] = '_'; + mf->mpf_signature[1] = 'M'; + mf->mpf_signature[2] = 'P'; + mf->mpf_signature[3] = '_'; + mf->mpf_physptr = mpf_physptr; + mf->mpf_length = 1; + mf->mpf_specification = 4; + mf->mpf_checksum = 0; + mf->mpf_feature1 = 0; + mf->mpf_feature2 = 0; + mf->mpf_feature3 = 0; + mf->mpf_feature4 = 0; + mf->mpf_feature5 = 0; + mf->mpf_checksum = smp_compute_checksum(mf, mf->mpf_length*16); + return v; +} + +void *smp_next_mpc_entry(struct mp_config_table *mc) +{ + void *v; + v = (void *)(((char *)mc) + mc->mpc_length); + return v; +} +static void smp_add_mpc_entry(struct mp_config_table *mc, unsigned length) +{ + mc->mpc_length += length; + mc->mpc_entry_count++; +} + +void *smp_next_mpe_entry(struct mp_config_table *mc) +{ + void *v; + v = (void *)(((char *)mc) + mc->mpc_length + mc->mpe_length); + return v; +} +static void smp_add_mpe_entry(struct mp_config_table *mc, mpe_t mpe) +{ + mc->mpe_length += mpe->mpe_length; +} + +void smp_write_processor(struct mp_config_table *mc, + unsigned char apicid, unsigned char apicver, + unsigned char cpuflag, unsigned int cpufeature, + unsigned int featureflag) +{ + struct mpc_config_processor *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_PROCESSOR; + mpc->mpc_apicid = apicid; + mpc->mpc_apicver = apicver; + mpc->mpc_cpuflag = cpuflag; + mpc->mpc_cpufeature = cpufeature; + mpc->mpc_featureflag = featureflag; + smp_add_mpc_entry(mc, sizeof(*mpc)); +} + +/* If we assume a symmetric processor configuration we can + * get all of the information we need to write the processor + * entry from the bootstrap processor. + * Plus I don't think linux really even cares. + * Having the proper apicid's in the table so the non-bootstrap + * processors can be woken up should be enough. + */ +void smp_write_processors(struct mp_config_table *mc) +{ + int boot_apic_id; + unsigned apic_version; + unsigned cpu_features; + unsigned cpu_feature_flags; + struct cpuid_result result; + device_t cpu; + + boot_apic_id = lapicid(); + apic_version = lapic_read(LAPIC_LVR) & 0xff; + result = cpuid(1); + cpu_features = result.eax; + cpu_feature_flags = result.edx; + for(cpu = all_devices; cpu; cpu = cpu->next) { + unsigned long cpu_flag; + if ((cpu->path.type != DEVICE_PATH_APIC) || + (cpu->bus->dev->path.type != DEVICE_PATH_APIC_CLUSTER)) + { + continue; + } + if (!cpu->enabled) { + continue; + } + cpu_flag = MPC_CPU_ENABLED; + if (boot_apic_id == cpu->path.apic.apic_id) { + cpu_flag = MPC_CPU_ENABLED | MPC_CPU_BOOTPROCESSOR; + } + smp_write_processor(mc, + cpu->path.apic.apic_id, apic_version, + cpu_flag, cpu_features, cpu_feature_flags + ); + } +} + +static void smp_write_bus(struct mp_config_table *mc, + unsigned char id, const char *bustype) +{ + struct mpc_config_bus *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_BUS; + mpc->mpc_busid = id; + memcpy(mpc->mpc_bustype, bustype, sizeof(mpc->mpc_bustype)); + smp_add_mpc_entry(mc, sizeof(*mpc)); +} + +void smp_write_ioapic(struct mp_config_table *mc, + unsigned char id, unsigned char ver, + unsigned long apicaddr) +{ + struct mpc_config_ioapic *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_IOAPIC; + mpc->mpc_apicid = id; + mpc->mpc_apicver = ver; + mpc->mpc_flags = MPC_APIC_USABLE; + mpc->mpc_apicaddr = apicaddr; + smp_add_mpc_entry(mc, sizeof(*mpc)); +} + +void smp_write_intsrc(struct mp_config_table *mc, + unsigned char irqtype, unsigned short irqflag, + unsigned char srcbus, unsigned char srcbusirq, + unsigned char dstapic, unsigned char dstirq) +{ + struct mpc_config_intsrc *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_INTSRC; + mpc->mpc_irqtype = irqtype; + mpc->mpc_irqflag = irqflag; + mpc->mpc_srcbus = srcbus; + mpc->mpc_srcbusirq = srcbusirq; + mpc->mpc_dstapic = dstapic; + mpc->mpc_dstirq = dstirq; + smp_add_mpc_entry(mc, sizeof(*mpc)); +#ifdef DEBUG_MPTABLE + printk(BIOS_DEBUG, "add intsrc srcbus 0x%x srcbusirq 0x%x, dstapic 0x%x, dstirq 0x%x\n", + srcbus, srcbusirq, dstapic, dstirq); + hexdump(__func__, mpc, sizeof(*mpc)); +#endif +} + +void smp_write_intsrc_pci_bridge(struct mp_config_table *mc, + unsigned char irqtype, unsigned short irqflag, + struct device *dev, + unsigned char dstapic, unsigned char *dstirq) +{ + struct device *child; + + int i; + int srcbus; + int slot; + + struct bus *link; + unsigned char dstirq_x[4]; + + for (link = dev->link_list; link; link = link->next) { + + child = link->children; + srcbus = link->secondary; + + while (child) { + if (child->path.type != DEVICE_PATH_PCI) + goto next; + + slot = (child->path.pci.devfn >> 3); + /* round pins */ + for (i = 0; i < 4; i++) + dstirq_x[i] = dstirq[(i + slot) % 4]; + + if ((child->class >> 16) != PCI_BASE_CLASS_BRIDGE) { + /* pci device */ + printk(BIOS_DEBUG, "route irq: %s\n", dev_path(child)); + for (i = 0; i < 4; i++) + smp_write_intsrc(mc, irqtype, irqflag, srcbus, (slot<<2)|i, dstapic, dstirq_x[i]); + goto next; + } + + switch (child->class>>8) { + case PCI_CLASS_BRIDGE_PCI: + case PCI_CLASS_BRIDGE_PCMCIA: + case PCI_CLASS_BRIDGE_CARDBUS: + printk(BIOS_DEBUG, "route irq bridge: %s\n", dev_path(child)); + smp_write_intsrc_pci_bridge(mc, irqtype, irqflag, child, dstapic, dstirq_x); + } + + next: + child = child->sibling; + } + + } +} + +void smp_write_lintsrc(struct mp_config_table *mc, + unsigned char irqtype, unsigned short irqflag, + unsigned char srcbusid, unsigned char srcbusirq, + unsigned char destapic, unsigned char destapiclint) +{ + struct mpc_config_lintsrc *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_LINTSRC; + mpc->mpc_irqtype = irqtype; + mpc->mpc_irqflag = irqflag; + mpc->mpc_srcbusid = srcbusid; + mpc->mpc_srcbusirq = srcbusirq; + mpc->mpc_destapic = destapic; + mpc->mpc_destapiclint = destapiclint; + smp_add_mpc_entry(mc, sizeof(*mpc)); +} + +void smp_write_address_space(struct mp_config_table *mc, + unsigned char busid, unsigned char address_type, + unsigned int address_base_low, unsigned int address_base_high, + unsigned int address_length_low, unsigned int address_length_high) +{ + struct mp_exten_system_address_space *mpe; + mpe = smp_next_mpe_entry(mc); + memset(mpe, '\0', sizeof(*mpe)); + mpe->mpe_type = MPE_SYSTEM_ADDRESS_SPACE; + mpe->mpe_length = sizeof(*mpe); + mpe->mpe_busid = busid; + mpe->mpe_address_type = address_type; + mpe->mpe_address_base_low = address_base_low; + mpe->mpe_address_base_high = address_base_high; + mpe->mpe_address_length_low = address_length_low; + mpe->mpe_address_length_high = address_length_high; + smp_add_mpe_entry(mc, (mpe_t)mpe); +} + + +void smp_write_bus_hierarchy(struct mp_config_table *mc, + unsigned char busid, unsigned char bus_info, + unsigned char parent_busid) +{ + struct mp_exten_bus_hierarchy *mpe; + mpe = smp_next_mpe_entry(mc); + memset(mpe, '\0', sizeof(*mpe)); + mpe->mpe_type = MPE_BUS_HIERARCHY; + mpe->mpe_length = sizeof(*mpe); + mpe->mpe_busid = busid; + mpe->mpe_bus_info = bus_info; + mpe->mpe_parent_busid = parent_busid; + smp_add_mpe_entry(mc, (mpe_t)mpe); +} + +void smp_write_compatibility_address_space(struct mp_config_table *mc, + unsigned char busid, unsigned char address_modifier, + unsigned int range_list) +{ + struct mp_exten_compatibility_address_space *mpe; + mpe = smp_next_mpe_entry(mc); + memset(mpe, '\0', sizeof(*mpe)); + mpe->mpe_type = MPE_COMPATIBILITY_ADDRESS_SPACE; + mpe->mpe_length = sizeof(*mpe); + mpe->mpe_busid = busid; + mpe->mpe_address_modifier = address_modifier; + mpe->mpe_range_list = range_list; + smp_add_mpe_entry(mc, (mpe_t)mpe); +} + +void mptable_add_isa_interrupts(struct mp_config_table *mc, unsigned long bus_isa, unsigned long apicid, int external_int2) +{ +/*I/O Ints: Type Trigger Polarity Bus ID IRQ APIC ID PIN# */ + smp_write_intsrc(mc, external_int2?mp_INT:mp_ExtINT, + MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x0, apicid, 0x0); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x1, apicid, 0x1); + smp_write_intsrc(mc, external_int2?mp_ExtINT:mp_INT, + MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x0, apicid, 0x2); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x3, apicid, 0x3); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x4, apicid, 0x4); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x6, apicid, 0x6); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x7, apicid, 0x7); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x8, apicid, 0x8); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x9, apicid, 0x9); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xa, apicid, 0xa); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xb, apicid, 0xb); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xc, apicid, 0xc); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xd, apicid, 0xd); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xe, apicid, 0xe); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xf, apicid, 0xf); +} + +void mptable_write_buses(struct mp_config_table *mc, int *max_pci_bus, int *isa_bus) { + int dummy, i, highest; + char buses[256]; + struct device *dev; + + if (!max_pci_bus) max_pci_bus = &dummy; + if (!isa_bus) isa_bus = &dummy; + + *max_pci_bus = 0; + highest = 0; + memset(buses, 0, sizeof(buses)); + + for (dev = all_devices; dev; dev = dev->next) { + struct bus *bus; + for (bus = dev->link_list; bus; bus = bus->next) { + if (bus->secondary > 255) { + printk(BIOS_ERR, "A bus claims to have a bus ID > 255?!? Aborting"); + return; + } + buses[bus->secondary] = 1; + if (highest < bus->secondary) highest = bus->secondary; + } + } + for (i=0; i <= highest; i++) { + if (buses[i]) { + smp_write_bus(mc, i, "PCI "); + *max_pci_bus = i; + } + } + *isa_bus = *max_pci_bus + 1; + smp_write_bus(mc, *isa_bus, "ISA "); +} + diff --git a/src/arch/x86/boot/multiboot.c b/src/arch/x86/boot/multiboot.c new file mode 100644 index 0000000000..4059f2736b --- /dev/null +++ b/src/arch/x86/boot/multiboot.c @@ -0,0 +1,77 @@ +/* + * support for Multiboot payloads + * + * Copyright (C) 2008 Robert Millan + * + * 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 + * (at your option) 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, see <http://www.gnu.org/licenses/>. + * + */ + +#include <cpu/x86/multiboot.h> +#include <string.h> +#include <device/resource.h> +#include <console/console.h> +#include <boot/coreboot_tables.h> +#include <arch/coreboot_tables.h> + +struct multiboot_info *mbi = NULL; + +unsigned long write_multiboot_info(unsigned long rom_table_end) +{ + static struct multiboot_mmap_entry *mb_mem; + struct lb_memory* coreboot_table; + int entries; + int i; + + mbi = (struct multiboot_info *)rom_table_end; + + memset(mbi, 0, sizeof(*mbi)); + rom_table_end += sizeof(*mbi); + + mbi->mmap_addr = (u32) rom_table_end; + mb_mem = (struct multiboot_mmap_entry *)rom_table_end; + + /* copy regions from coreboot tables */ + coreboot_table = get_lb_mem(); + entries = (coreboot_table->size - sizeof(*coreboot_table))/sizeof(coreboot_table->map[0]); + + if (coreboot_table == NULL || entries < 1) { + printk(BIOS_INFO, "%s: Cannot find coreboot table.\n", __func__); + return (unsigned long) mb_mem; + } + + for (i = 0; i < entries; i++) { + uint64_t entry_start = unpack_lb64(coreboot_table->map[i].start); + uint64_t entry_size = unpack_lb64(coreboot_table->map[i].size); + mb_mem->addr = entry_start; + mb_mem->len = entry_size; + switch (coreboot_table->map[i].type) { + case LB_MEM_RAM: + mb_mem->type = MULTIBOOT_MEMORY_AVAILABLE; + break; + default: // anything other than usable RAM + mb_mem->type = MULTIBOOT_MEMORY_RESERVED; + break; + } + mb_mem->size = sizeof(*mb_mem) - sizeof(mb_mem->size); + mb_mem++; + } + + mbi->mmap_length = ((u32) mb_mem) - mbi->mmap_addr; + mbi->flags |= MB_INFO_MEM_MAP; + + printk(BIOS_INFO, "Multiboot Information structure has been written.\n"); + + return (unsigned long)mb_mem; +} diff --git a/src/arch/x86/boot/pirq_routing.c b/src/arch/x86/boot/pirq_routing.c new file mode 100644 index 0000000000..bb8a7b605a --- /dev/null +++ b/src/arch/x86/boot/pirq_routing.c @@ -0,0 +1,169 @@ +#include <console/console.h> +#include <arch/pirq_routing.h> +#include <string.h> +#include <device/pci.h> + +#if CONFIG_DEBUG_PIRQ +static void check_pirq_routing_table(struct irq_routing_table *rt) +{ + uint8_t *addr = (uint8_t *)rt; + uint8_t sum=0; + int i; + + printk(BIOS_INFO, "Checking Interrupt Routing Table consistency...\n"); + + if (sizeof(struct irq_routing_table) != rt->size) { + printk(BIOS_WARNING, "Inconsistent Interrupt Routing Table size (0x%x/0x%x).\n", + (unsigned int) sizeof(struct irq_routing_table), + rt->size + ); + rt->size=sizeof(struct irq_routing_table); + } + + for (i = 0; i < rt->size; i++) + sum += addr[i]; + + printk(BIOS_DEBUG, "%s(): Interrupt Routing Table located at %p.\n", + __func__, addr); + + + sum = rt->checksum - sum; + + if (sum != rt->checksum) { + printk(BIOS_WARNING, "Interrupt Routing Table checksum is: 0x%02x but should be: 0x%02x.\n", + rt->checksum, sum); + rt->checksum = sum; + } + + if (rt->signature != PIRQ_SIGNATURE || rt->version != PIRQ_VERSION || + rt->size % 16 ) { + printk(BIOS_WARNING, "Interrupt Routing Table not valid.\n"); + return; + } + + sum = 0; + for (i=0; i<rt->size; i++) + sum += addr[i]; + + /* We're manually fixing the checksum above. This warning can probably + * never happen because if the target location is read-only this + * function would have bailed out earlier. + */ + if (sum) { + printk(BIOS_WARNING, "Checksum error in Interrupt Routing Table " + "could not be fixed.\n"); + } + + printk(BIOS_INFO, "done.\n"); +} + +static int verify_copy_pirq_routing_table(unsigned long addr) +{ + int i; + uint8_t *rt_orig, *rt_curr; + + rt_curr = (uint8_t*)addr; + rt_orig = (uint8_t*)&intel_irq_routing_table; + printk(BIOS_INFO, "Verifying copy of Interrupt Routing Table at 0x%08lx... ", addr); + for (i = 0; i < intel_irq_routing_table.size; i++) { + if (*(rt_curr + i) != *(rt_orig + i)) { + printk(BIOS_INFO, "failed\n"); + return -1; + } + } + printk(BIOS_INFO, "done\n"); + + check_pirq_routing_table((struct irq_routing_table *)addr); + + return 0; +} +#endif + +unsigned long copy_pirq_routing_table(unsigned long addr) +{ + /* Align the table to be 16 byte aligned. */ + addr += 15; + addr &= ~15; + + /* This table must be betweeen 0xf0000 & 0x100000 */ + printk(BIOS_INFO, "Copying Interrupt Routing Table to 0x%08lx... ", addr); + memcpy((void *)addr, &intel_irq_routing_table, intel_irq_routing_table.size); + printk(BIOS_INFO, "done.\n"); +#if CONFIG_DEBUG_PIRQ + verify_copy_pirq_routing_table(addr); +#endif + pirq_routing_irqs(addr); + return addr + intel_irq_routing_table.size; +} + +#if CONFIG_PIRQ_ROUTE +void pirq_routing_irqs(unsigned long addr) +{ + int i, j, k, num_entries; + unsigned char irq_slot[4]; + unsigned char pirq[4] = {0, 0, 0, 0}; + struct irq_routing_table *pirq_tbl; + + pirq_tbl = (struct irq_routing_table *)(addr); + num_entries = (pirq_tbl->size - 32) / 16; + + /* Set PCI IRQs. */ + for (i = 0; i < num_entries; i++) { + + printk(BIOS_DEBUG, "PIRQ Entry %d Dev/Fn: %X Slot: %d\n", i, + pirq_tbl->slots[i].devfn >> 3, pirq_tbl->slots[i].slot); + + for (j = 0; j < 4; j++) { + + int link = pirq_tbl->slots[i].irq[j].link; + int bitmap = pirq_tbl->slots[i].irq[j].bitmap; + int irq = 0; + + printk(BIOS_DEBUG, "INT: %c link: %x bitmap: %x ", + 'A' + j, link, bitmap); + + if (!bitmap|| !link || link > 4) { + + printk(BIOS_DEBUG, "not routed\n"); + irq_slot[j] = irq; + continue; + } + + /* yet not routed */ + if (!pirq[link - 1]) { + + for (k = 2; k <= 15; k++) { + + if (!((bitmap >> k) & 1)) + continue; + + irq = k; + + /* yet not routed */ + if (pirq[0] != irq && pirq[1] != irq && pirq[2] != irq && pirq[3] != irq) + break; + } + + if (irq) + pirq[link - 1] = irq; + } + else + irq = pirq[link - 1]; + + printk(BIOS_DEBUG, "IRQ: %d\n", irq); + irq_slot[j] = irq; + } + + /* Bus, device, slots IRQs for {A,B,C,D}. */ + pci_assign_irqs(pirq_tbl->slots[i].bus, + pirq_tbl->slots[i].devfn >> 3, irq_slot); + } + + printk(BIOS_DEBUG, "PIRQ1: %d\n", pirq[0]); + printk(BIOS_DEBUG, "PIRQ2: %d\n", pirq[1]); + printk(BIOS_DEBUG, "PIRQ3: %d\n", pirq[2]); + printk(BIOS_DEBUG, "PIRQ4: %d\n", pirq[3]); + + pirq_assign_irqs(pirq); +} +#endif diff --git a/src/arch/x86/boot/tables.c b/src/arch/x86/boot/tables.c new file mode 100644 index 0000000000..d816e76750 --- /dev/null +++ b/src/arch/x86/boot/tables.c @@ -0,0 +1,231 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2003 Eric Biederman + * Copyright (C) 2005 Steve Magnani + * Copyright (C) 2008-2009 coresystems GmbH + * + * 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; version 2 of the License. + * + * 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 + */ + +#include <console/console.h> +#include <cpu/cpu.h> +#include <boot/tables.h> +#include <boot/coreboot_tables.h> +#include <arch/coreboot_tables.h> +#include <arch/pirq_routing.h> +#include <arch/smp/mpspec.h> +#include <arch/acpi.h> +#include <string.h> +#include <cpu/x86/multiboot.h> +#include <cbmem.h> +#include <lib.h> + +uint64_t high_tables_base = 0; +uint64_t high_tables_size; + +void cbmem_arch_init(void) +{ + /* defined in gdt.c */ + move_gdt(); +} + +struct lb_memory *write_tables(void) +{ + unsigned long low_table_start, low_table_end; + unsigned long rom_table_start, rom_table_end; + + /* Even if high tables are configured, some tables are copied both to + * the low and the high area, so payloads and OSes don't need to know + * about the high tables. + */ + unsigned long high_table_pointer; + + if (!high_tables_base) { + printk(BIOS_ERR, "ERROR: High Tables Base is not set.\n"); + // Are there any boards without? + // Stepan thinks we should die() here! + } + + printk(BIOS_DEBUG, "High Tables Base is %llx.\n", high_tables_base); + + rom_table_start = 0xf0000; + rom_table_end = 0xf0000; + + /* Start low addr at 0x500, so we don't run into conflicts with the BDA + * in case our data structures grow beyound 0x400. Only multiboot, GDT + * and the coreboot table use low_tables. + */ + low_table_start = 0; + low_table_end = 0x500; + +#if CONFIG_GENERATE_PIRQ_TABLE == 1 +#define MAX_PIRQ_TABLE_SIZE (4 * 1024) + post_code(0x9a); + + /* This table must be between 0x0f0000 and 0x100000 */ + rom_table_end = write_pirq_routing_table(rom_table_end); + rom_table_end = ALIGN(rom_table_end, 1024); + + /* And add a high table version for those payloads that + * want to live in the F segment + */ + high_table_pointer = (unsigned long)cbmem_add(CBMEM_ID_PIRQ, MAX_PIRQ_TABLE_SIZE); + if (high_table_pointer) { + unsigned long new_high_table_pointer; + new_high_table_pointer = write_pirq_routing_table(high_table_pointer); + // FIXME make pirq table code intelligent enough to know how + // much space it's going to need. + if (new_high_table_pointer > (high_table_pointer + MAX_PIRQ_TABLE_SIZE)) { + printk(BIOS_ERR, "ERROR: Increase PIRQ size.\n"); + } + printk(BIOS_DEBUG, "PIRQ table: %ld bytes.\n", + new_high_table_pointer - high_table_pointer); + } + +#endif + +#if CONFIG_GENERATE_MP_TABLE == 1 +#define MAX_MP_TABLE_SIZE (4 * 1024) + post_code(0x9b); + + /* The smp table must be in 0-1K, 639K-640K, or 960K-1M */ + rom_table_end = write_smp_table(rom_table_end); + rom_table_end = ALIGN(rom_table_end, 1024); + + high_table_pointer = (unsigned long)cbmem_add(CBMEM_ID_MPTABLE, MAX_MP_TABLE_SIZE); + if (high_table_pointer) { + unsigned long new_high_table_pointer; + new_high_table_pointer = write_smp_table(high_table_pointer); + // FIXME make mp table code intelligent enough to know how + // much space it's going to need. + if (new_high_table_pointer > (high_table_pointer + MAX_MP_TABLE_SIZE)) { + printk(BIOS_ERR, "ERROR: Increase MP table size.\n"); + } + + printk(BIOS_DEBUG, "MP table: %ld bytes.\n", + new_high_table_pointer - high_table_pointer); + } +#endif /* CONFIG_GENERATE_MP_TABLE */ + +#if CONFIG_GENERATE_ACPI_TABLES == 1 +#define MAX_ACPI_SIZE (47 * 1024) + post_code(0x9c); + + /* Write ACPI tables to F segment and high tables area */ + + /* Ok, this is a bit hacky still, because some day we want to have this + * completely dynamic. But right now we are setting fixed sizes. + * It's probably still better than the old high_table_base code because + * now at least we know when we have an overflow in the area. + * + * We want to use 1MB - 64K for Resume backup. We use 512B for TOC and + * 512 byte for GDT, 4K for PIRQ and 4K for MP table and 8KB for the + * coreboot table. This leaves us with 47KB for all of ACPI. Let's see + * how far we get. + */ + high_table_pointer = (unsigned long)cbmem_add(CBMEM_ID_ACPI, MAX_ACPI_SIZE); + if (high_table_pointer) { + unsigned long acpi_start = high_table_pointer; + unsigned long new_high_table_pointer; + + rom_table_end = ALIGN(rom_table_end, 16); + new_high_table_pointer = write_acpi_tables(high_table_pointer); + if (new_high_table_pointer > ( high_table_pointer + MAX_ACPI_SIZE)) { + printk(BIOS_ERR, "ERROR: Increase ACPI size\n"); + } + printk(BIOS_DEBUG, "ACPI tables: %ld bytes.\n", + new_high_table_pointer - high_table_pointer); + + /* Now we need to create a low table copy of the RSDP. */ + + /* First we look for the high table RSDP */ + while (acpi_start < new_high_table_pointer) { + if (memcmp(((acpi_rsdp_t *)acpi_start)->signature, RSDP_SIG, 8) == 0) { + break; + } + acpi_start++; + } + + /* Now, if we found the RSDP, we take the RSDT and XSDT pointer + * from it in order to write the low RSDP + */ + if (acpi_start < new_high_table_pointer) { + acpi_rsdp_t *low_rsdp = (acpi_rsdp_t *)rom_table_end, + *high_rsdp = (acpi_rsdp_t *)acpi_start; + + acpi_write_rsdp(low_rsdp, + (acpi_rsdt_t *)(high_rsdp->rsdt_address), + (acpi_xsdt_t *)((unsigned long)high_rsdp->xsdt_address)); + } else { + printk(BIOS_ERR, "ERROR: Didn't find RSDP in high table.\n"); + } + rom_table_end = ALIGN(rom_table_end + sizeof(acpi_rsdp_t), 16); + } else { + rom_table_end = write_acpi_tables(rom_table_end); + rom_table_end = ALIGN(rom_table_end, 1024); + } + +#endif + + +#define MAX_COREBOOT_TABLE_SIZE (8 * 1024) + post_code(0x9d); + + high_table_pointer = (unsigned long)cbmem_add(CBMEM_ID_CBTABLE, MAX_COREBOOT_TABLE_SIZE); + + if (high_table_pointer) { + unsigned long new_high_table_pointer; + + /* Also put a forwarder entry into 0-4K */ + new_high_table_pointer = write_coreboot_table(low_table_start, low_table_end, + high_tables_base, high_table_pointer); + + if (new_high_table_pointer > (high_table_pointer + + MAX_COREBOOT_TABLE_SIZE)) + printk(BIOS_ERR, "%s: coreboot table didn't fit (%lx)\n", + __func__, new_high_table_pointer - + high_table_pointer); + + printk(BIOS_DEBUG, "coreboot table: %ld bytes.\n", + new_high_table_pointer - high_table_pointer); + } else { + /* The coreboot table must be in 0-4K or 960K-1M */ + rom_table_end = write_coreboot_table( + low_table_start, low_table_end, + rom_table_start, rom_table_end); + } + + post_code(0x9e); + +#if CONFIG_HAVE_ACPI_RESUME + /* Let's prepare the ACPI S3 Resume area now already, so we can rely on + * it begin there during reboot time. We don't need the pointer, nor + * the result right now. If it fails, ACPI resume will be disabled. + */ + cbmem_add(CBMEM_ID_RESUME, HIGH_MEMORY_SAVE); +#endif + +#if CONFIG_MULTIBOOT + post_code(0x9d); + + /* The Multiboot information structure */ + write_multiboot_info(rom_table_end); +#endif + + // Remove before sending upstream + cbmem_list(); + + return get_lb_mem(); +} diff --git a/src/arch/x86/boot/wakeup.S b/src/arch/x86/boot/wakeup.S new file mode 100644 index 0000000000..a1df4d5597 --- /dev/null +++ b/src/arch/x86/boot/wakeup.S @@ -0,0 +1,105 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Rudolf Marek <r.marek@assembler.cz> + * Copyright (C) 2009 coresystems GmbH + * + * 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; version 2 of the License. + * + * 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 + */ + +#define WAKEUP_BASE 0x600 +#define RELOCATED(x) (x - __wakeup + WAKEUP_BASE) + +/* CR0 bits */ +#define PE (1 << 0) + + .code32 + .globl __wakeup +__wakeup: + /* First prepare the jmp to the resume vector */ + mov 0x4(%esp), %eax /* vector */ + /* last 4 bits of linear addr are taken as offset */ + andw $0x0f, %ax + movw %ax, (__wakeup_offset) + mov 0x4(%esp), %eax + /* the rest is taken as segment */ + shr $4, %eax + movw %ax, (__wakeup_segment) + + /* Then overwrite coreboot with our backed up memory */ + movl 8(%esp), %esi + movl 12(%esp), %edi + movl 16(%esp), %ecx + shrl $4, %ecx +1: + movl 0(%esi),%eax + movl 4(%esi),%edx + movl 8(%esi),%ebx + movl 12(%esi),%ebp + addl $16,%esi + subl $1,%ecx + movl %eax,0(%edi) + movl %edx,4(%edi) + movl %ebx,8(%edi) + movl %ebp,12(%edi) + leal 16(%edi),%edi + jne 1b + + /* Activate the right segment descriptor real mode. */ + ljmp $0x28, $RELOCATED(1f) +1: +.code16 + /* 16 bit code from here on... */ + + /* Load the segment registers w/ properly configured + * segment descriptors. They will retain these + * configurations (limits, writability, etc.) once + * protected mode is turned off. + */ + mov $0x30, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + /* Turn off protection */ + movl %cr0, %eax + andl $~PE, %eax + movl %eax, %cr0 + + /* Now really going into real mode */ + ljmp $0, $RELOCATED(1f) +1: + movw $0x0, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + /* This is a FAR JMP to the OS waking vector. The C code changed + * the address to be correct. + */ + .byte 0xea + +__wakeup_offset = RELOCATED(.) + .word 0x0000 + +__wakeup_segment = RELOCATED(.) + .word 0x0000 + + .globl __wakeup_size +__wakeup_size = ( . - __wakeup) + |