Hello Patrick Rudolph,
I'd like you to do a code review. Please visit
https://review.coreboot.org/29667
to review the following change.
Change subject: [WIP]x86|mb/emulation/qemu-q35: 64bit ramstage support ......................................................................
[WIP]x86|mb/emulation/qemu-q35: 64bit ramstage support
* Add mmu.c ported from arm64 * Generate 4 level page tables for long mode * Enable long mode * Activate long mode in c_start.S * Some fixes for 64bit * Add support for 64bit rmodule
Non working: * Loading gdt and setting %ds
Signed-off-by: Patrick Rudolph siro@das-labor.org Change-Id: If2f02a95b2f91ab51043d4e81054354f4a6eb5d5 --- M src/arch/x86/Kconfig M src/arch/x86/Makefile.inc M src/arch/x86/boot.c M src/arch/x86/c_start.S M src/arch/x86/exception.c A src/arch/x86/include/arch/mmu.h A src/arch/x86/mmu.c M src/commonlib/include/commonlib/cbmem_id.h M src/cpu/qemu-x86/Kconfig M src/cpu/x86/Makefile.inc M src/lib/timestamp.c M src/mainboard/emulation/qemu-q35/Kconfig M src/mainboard/emulation/qemu-q35/Makefile.inc M src/mainboard/emulation/qemu-q35/romstage.c M src/southbridge/intel/common/smi.c M src/southbridge/intel/i82801ix/hdaudio.c M src/southbridge/intel/i82801ix/lpc.c M src/southbridge/intel/i82801ix/sata.c M util/cbfstool/elf.h M util/cbfstool/elfheaders.c M util/cbfstool/rmodule.c M util/xcompile/xcompile 22 files changed, 663 insertions(+), 14 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/67/29667/1
diff --git a/src/arch/x86/Kconfig b/src/arch/x86/Kconfig index 7c8371e..ed07a05 100644 --- a/src/arch/x86/Kconfig +++ b/src/arch/x86/Kconfig @@ -330,3 +330,7 @@ config HAVE_CF9_RESET_PREPARE bool depends on HAVE_CF9_RESET + +config TTB_SIZE_KB + hex "Size of TTB" + default 0x1000 diff --git a/src/arch/x86/Makefile.inc b/src/arch/x86/Makefile.inc index fcc57c5..c5cf625 100644 --- a/src/arch/x86/Makefile.inc +++ b/src/arch/x86/Makefile.inc @@ -398,7 +398,7 @@
$(objgenerated)/ramstage.o: $$(ramstage-objs) $(COMPILER_RT_ramstage) $$(ramstage-libs) @printf " CC $(subst $(obj)/,,$(@))\n" -ifeq ($(CONFIG_ARCH_ROMSTAGE_X86_32),y) +ifeq ($(CONFIG_ARCH_RAMSTAGE_X86_32),y) $(LD_ramstage) -m elf_i386 -r -o $@ $(COMPILER_RT_FLAGS_ramstage) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) $(ramstage-libs) --no-whole-archive $(COMPILER_RT_ramstage) --end-group else $(LD_ramstage) -m elf_x86_64 -r -o $@ $(COMPILER_RT_FLAGS_ramstage) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) $(ramstage-libs) --no-whole-archive $(COMPILER_RT_ramstage) --end-group diff --git a/src/arch/x86/boot.c b/src/arch/x86/boot.c index 2967cf6..fa98704 100644 --- a/src/arch/x86/boot.c +++ b/src/arch/x86/boot.c @@ -29,16 +29,17 @@
return 0; } + #include <arch/mmu.h>
void arch_prog_run(struct prog *prog) { + printk(BIOS_DEBUG, "Jumping to %p\n", (void*)prog_entry(prog)); __asm__ volatile ( #ifdef __x86_64__ "jmp *%%rdi\n" #else "jmp *%%edi\n" #endif - :: "D"(prog_entry(prog)) ); } diff --git a/src/arch/x86/c_start.S b/src/arch/x86/c_start.S index 6426ef3..11b0145 100644 --- a/src/arch/x86/c_start.S +++ b/src/arch/x86/c_start.S @@ -12,6 +12,7 @@ */
#include <cpu/x86/post_code.h> +#include <arch/rom_segs.h>
/* Place the stack in the bss section. It's not necessary to define it in the * the linker script. */ @@ -35,13 +36,57 @@ #else .code32 #endif + .globl _start _start: + cli - lgdt %cs:gdtaddr +#ifdef __x86_64__ + /* Test for page tables installed */ + mov %cr3, %eax + cmp $0, %eax + je longmode_setup_err + + /* Test for long mode active (EFER.LMA = 1 EFER.LME = 1) */ + movl $0xC0000080, %ecx // EFER MSR number. + rdmsr + andl $0x500, %eax + cmp $0x500, %eax + je longmode_active + + /* Test for long mode disabled (EFER.LME = 0) */ + cmp $0, %eax + je longmode_setup_err + + /* + * Previous stage prepared long mode: + * + Long mode is enabled (EFER.LME = 1) + * + paging is disalbed + * + page tables are installed + */ + + /* Enable paging */ + mov %cr0, %eax + orl $(1 <<31), %eax + mov %eax, %cr0 + + jmp longmode_active + +longmode_setup_err: + post_code(POST_DEAD_CODE) /* post ee */ + hlt + jmp longmode_setup_err + +longmode_active: +#endif + + lgdt %cs:gdtaddr + #ifndef __x86_64__ + /* Enable protected mode */ ljmp $0x10, $1f #endif + /* Use flat data segment */ 1: movl $0x18, %eax movl %eax, %ds movl %eax, %es @@ -134,7 +179,7 @@ #endif
.data - + .balign 8 /* 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 diff --git a/src/arch/x86/exception.c b/src/arch/x86/exception.c index 0f42fdf..9dda706 100644 --- a/src/arch/x86/exception.c +++ b/src/arch/x86/exception.c @@ -516,7 +516,7 @@ "edi: %08x esi: %08x ebp: %08x esp: %08x\n", logical_processor, (unsigned int)lapicid(), info->vector, info->cs, info->eip, - info->error_code, info->eflags, read_cr2(), + info->error_code, info->eflags, (unsigned int)read_cr2(), info->eax, info->ebx, info->ecx, info->edx, info->edi, info->esi, info->ebp, info->esp); u8 *code = (u8 *)((uintptr_t)info->eip - (MDUMP_SIZE >> 1)); diff --git a/src/arch/x86/include/arch/mmu.h b/src/arch/x86/include/arch/mmu.h new file mode 100644 index 0000000..c3bd3f7 --- /dev/null +++ b/src/arch/x86/include/arch/mmu.h @@ -0,0 +1,105 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Google Inc. + * + * 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. + */ + +#ifndef __ARCH_X86_MMU_H__ +#define __ARCH_X86_MMU_H__ + +#include <types.h> + +/* Memory attributes for mmap regions + * These attributes act as tag values for memrange regions + */ + +/* Descriptor attributes */ + +#define DESC_MASK 0xeaf +#define INVALID_DESC 0 +#define TABLE_DESC 0x623 +#define PAGE_DESC 0x4a3 +#define BLOCK_DESC 0x2a3 + +#define AVAIL_SHIFT 9 +#define AVAIL_MASK (7 << AVAIL_SHIFT) +#define AVAIL_INVAL 0 +#define AVAIL_TABLE 3 +#define AVAIL_PAGE 2 +#define AVAIL_BLOCK 1 + + +/* Block descriptor */ +#define BLOCK_NS (1 << 5) + +#define BLOCK_AP_RW (0 << 7) +#define BLOCK_AP_RO (1 << 7) + +#define BLOCK_ACCESS (1 << 10) + +#define BLOCK_XN (1ULL << 54) + +#define BLOCK_SH_SHIFT (8) +#define BLOCK_SH_NON_SHAREABLE (0 << BLOCK_SH_SHIFT) +#define BLOCK_SH_UNPREDICTABLE (1 << BLOCK_SH_SHIFT) +#define BLOCK_SH_OUTER_SHAREABLE (2 << BLOCK_SH_SHIFT) +#define BLOCK_SH_INNER_SHAREABLE (3 << BLOCK_SH_SHIFT) + +/* Sentinel descriptor to mark first PTE of an unused table. It must be a value + * that cannot occur naturally as part of a page table. (Bits [1:0] = 0b00 makes + * this an unmapped page, but some page attribute bits are still set.) */ +#define UNUSED_DESC 0x6EbAAD0BBADbA000ULL + +/* XLAT Table Init Attributes */ + +#define VA_START 0x0 +#define BITS_PER_VA 48 +/* Granule size of 4KB is being used */ +#define GRANULE_SIZE_SHIFT 12 +#define GRANULE_SIZE (1 << GRANULE_SIZE_SHIFT) +#define XLAT_ADDR_MASK ((1ULL << BITS_PER_VA) - GRANULE_SIZE) +#define GRANULE_SIZE_MASK ((1ULL << GRANULE_SIZE_SHIFT) - 1) + +#define BITS_RESOLVED_PER_LVL (GRANULE_SIZE_SHIFT - 3) +#define L0_ADDR_SHIFT (GRANULE_SIZE_SHIFT + BITS_RESOLVED_PER_LVL * 3) +#define L1_ADDR_SHIFT (GRANULE_SIZE_SHIFT + BITS_RESOLVED_PER_LVL * 2) +#define L2_ADDR_SHIFT (GRANULE_SIZE_SHIFT + BITS_RESOLVED_PER_LVL * 1) +#define L3_ADDR_SHIFT (GRANULE_SIZE_SHIFT + BITS_RESOLVED_PER_LVL * 0) + +#define L0_ADDR_MASK (((1ULL << BITS_RESOLVED_PER_LVL) - 1) << L0_ADDR_SHIFT) +#define L1_ADDR_MASK (((1ULL << BITS_RESOLVED_PER_LVL) - 1) << L1_ADDR_SHIFT) +#define L2_ADDR_MASK (((1ULL << BITS_RESOLVED_PER_LVL) - 1) << L2_ADDR_SHIFT) +#define L3_ADDR_MASK (((1ULL << BITS_RESOLVED_PER_LVL) - 1) << L3_ADDR_SHIFT) + +/* These macros give the size of the region addressed by each entry of a xlat + table at any given level */ +#define L3_XLAT_SIZE (1ULL << L3_ADDR_SHIFT) +#define L2_XLAT_SIZE (1ULL << L2_ADDR_SHIFT) +#define L1_XLAT_SIZE (1ULL << L1_ADDR_SHIFT) +#define L0_XLAT_SIZE (1ULL << L0_ADDR_SHIFT) + +#define PAT_UC 0 +#define PAT_WC 1 +#define PAT_WT 4 +#define PAT_WP 5 +#define PAT_WB 6 +#define PAT_UC_MINUS 7 + +/* Initialize MMU registers and page table memory region. */ +void mmu_init(void); +/* Install page tables and enable long mode */ +void mmu_install_pagetables(void); +void mmu_config_range(uint64_t start, uint64_t size, uint64_t tag); +/* Disable the MMU (which also disables dcache but not icache). */ +void mmu_disable(void); + +#endif /* __ARCH_X86_MMU_H__ */ diff --git a/src/arch/x86/mmu.c b/src/arch/x86/mmu.c new file mode 100644 index 0000000..a0e3e78 --- /dev/null +++ b/src/arch/x86/mmu.c @@ -0,0 +1,403 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <symbols.h> + +#include <console/console.h> +#include <arch/mmu.h> +#include <arch/cache.h> +#include <cpu/x86/msr.h> +#include <cbmem.h> + +/* This just caches the next free table slot (okay to do since they fill up from + * bottom to top and can never be freed up again). It will reset to its initial + * value on stage transition, so we still need to check it for UNUSED_DESC. */ +static CAR_GLOBAL uint64_t *next_free_table; + +#define PTE_PRES (1ULL << 0) +#define PTE_RW (1ULL << 1) +#define PTE_US (1ULL << 2) +#define PTE_PWT (1ULL << 3) +#define PTE_PCD (1ULL << 4) +#define PTE_A (1ULL << 5) +#define PTE_D (1ULL << 6) +#define PTE_PAT (1ULL << 7) +#define PTE_G (1ULL << 8) +#define PTE_XD (1ULL << 63) +#define PDE_PS (1ULL << 7) + +#define BLOCK_INDEX_MASK (PTE_PWT | PTE_PCD | PTE_PRES | PTE_RW | PTE_A | PTE_D) +#define BLOCK_INDEX_SHIFT 0 +#define BLOCK_INDEX_MEM_NORMAL (PTE_PRES | PTE_RW | PTE_A | PTE_D) + +static void print_tag(int level, uint64_t pat) +{ + switch (pat) { + case PAT_UC: + printk(level, "UC\n"); + break; + case PAT_WC: + printk(level, "WC\n"); + break; + case PAT_WT: + printk(level, "WT\n"); + break; + case PAT_WP: + printk(level, "WP\n"); + break; + case PAT_WB: + printk(level, "WB\n"); + break; + case PAT_UC_MINUS: + printk(level, "UC-\n"); + break; + default: + break; + } +} + +static uint64_t pte_pat_flags(unsigned long pat) +{ + switch (pat) { + case PAT_UC: + return 0 | PTE_PCD | PTE_PWT; + case PAT_WC: + return 0 | 0 | PTE_PWT; + case PAT_WT: + return PTE_PAT | PTE_PCD | PTE_PWT; + case PAT_WP: + return PTE_PAT | 0 | PTE_PWT; + case PAT_WB: + return 0 | 0 | 0; + case PAT_UC_MINUS: + return 0 | PTE_PCD | 0; + default: + printk(BIOS_ERR, "PTE PAT defaulting to WB: %lx\n", pat); + return 0 | 0 | 0; + } +} + +static u8 *ttb(void) +{ + return cbmem_find(CBMEM_ID_TTB); +} + +static u8 *ettb(void) +{ + return (u8 *)cbmem_find(CBMEM_ID_TTB) + (CONFIG_TTB_SIZE_KB * 1024); +} + +/* Func : get_block_attr + * Desc : Get block descriptor attributes based on the value of tag in memrange + * region + */ +static uint64_t get_block_attr(unsigned long tag, bool ps) +{ + uint64_t flags = PTE_PRES | PTE_RW | PTE_US | PTE_A | PTE_D; + flags |= pte_pat_flags(tag); + if (ps) { + if (flags & PTE_PAT) { + /* If PS=1 PAT is at position 12 instead of 7 */ + flags &= ~PTE_PAT; + flags |= (PTE_PAT << 5); + } + flags |= PDE_PS; + } + return flags; +} + +/* Func : setup_new_table + * Desc : Get next free table from TTB and set it up to match old parent entry. + */ +static uint64_t *setup_new_table(uint64_t desc, uint64_t xlat_size) +{ + while (next_free_table[0] != UNUSED_DESC) { + next_free_table += GRANULE_SIZE/sizeof(*next_free_table); + if (ettb() - (u8 *)next_free_table <= 0) + die("Ran out of page table space!"); + } + + uint64_t frame_base = desc & XLAT_ADDR_MASK; + printk(BIOS_DEBUG, "Backing address range [0x%llx:0x%llx) with new page" + " table @%p\n", frame_base, frame_base + + (xlat_size << BITS_RESOLVED_PER_LVL), next_free_table); + + if (desc == INVALID_DESC) { + memset(next_free_table, 0, GRANULE_SIZE); + } else { + /* Can reuse old parent entry, but may need to adjust type. */ + if (xlat_size == L3_XLAT_SIZE) + desc |= PAGE_DESC; + + int i = 0; + for (; i < GRANULE_SIZE/sizeof(*next_free_table); i++) { + next_free_table[i] = desc; + desc += xlat_size; + } + } + + return next_free_table; +} + +/* Func: get_next_level_table + * Desc: Check if the table entry is a valid descriptor. If not, initialize new + * table, update the entry and return the table addr. If valid, return the addr + */ +static uint64_t *get_next_level_table(uint64_t *ptr, size_t xlat_size) +{ + uint64_t desc = *ptr; + + if ((desc & DESC_MASK) != TABLE_DESC) { + uint64_t *new_table = setup_new_table(desc, xlat_size); + desc = ((uintptr_t)new_table) | TABLE_DESC; + *ptr = desc; + } + return (uint64_t *)(uintptr_t)(desc & XLAT_ADDR_MASK); +} + +/* + Func : init_xlat_table + * Desc : Given a base address and size, it identifies the indices within + * different level XLAT tables which map the given base addr. Similar to table + * walk, except that all invalid entries during the walk are updated + * accordingly. On success, it returns the size of the block/page addressed by + * the final table. + */ +static uint64_t init_xlat_table(uint64_t base_addr, + uint64_t size, + uint64_t tag) +{ + uint64_t l0_index = (base_addr & L0_ADDR_MASK) >> L0_ADDR_SHIFT; + uint64_t l1_index = (base_addr & L1_ADDR_MASK) >> L1_ADDR_SHIFT; + uint64_t l2_index = (base_addr & L2_ADDR_MASK) >> L2_ADDR_SHIFT; + uint64_t l3_index = (base_addr & L3_ADDR_MASK) >> L3_ADDR_SHIFT; + uint64_t *table = (uint64_t *)ttb(); + uint64_t desc; + + /* check if CPU supports 1GB huge tables */ + const bool hugepage_1gb = !!(cpuid_edx(0x80000001) & (1 << 26)); + + /* L0 entry stores a table descriptor (doesn't support blocks) */ + table = get_next_level_table(&table[l0_index], L1_XLAT_SIZE); + + /* L1 table lookup */ + if ((size >= L1_XLAT_SIZE) && + IS_ALIGNED(base_addr, (1UL << L1_ADDR_SHIFT)) && + hugepage_1gb) { + /* If block address is aligned and size is greater than + * or equal to size addressed by each L1 entry, we can + * directly store a block desc */ + desc = base_addr | BLOCK_DESC | get_block_attr(tag, 1); + table[l1_index] = desc; + /* L2 lookup is not required */ + return L1_XLAT_SIZE; + } + + /* L1 entry stores a table descriptor */ + table = get_next_level_table(&table[l1_index], L2_XLAT_SIZE); + + /* L2 table lookup */ + if ((size >= L2_XLAT_SIZE) && + IS_ALIGNED(base_addr, (1UL << L2_ADDR_SHIFT))) { + /* If block address is aligned and size is greater than + * or equal to size addressed by each L2 entry, we can + * directly store a block desc */ + desc = base_addr | BLOCK_DESC | get_block_attr(tag, 1); + table[l2_index] = desc; + /* L3 lookup is not required */ + return L2_XLAT_SIZE; + } + + /* L2 entry stores a table descriptor */ + table = get_next_level_table(&table[l2_index], L3_XLAT_SIZE); + + /* L3 table lookup */ + desc = base_addr | PAGE_DESC | get_block_attr(tag, 0); + table[l3_index] = desc; + return L3_XLAT_SIZE; +} + +/* Func : sanity_check + * Desc : Check address/size alignment of a table or page. + */ +static void sanity_check(uint64_t addr, uint64_t size) +{ + assert(!(addr & GRANULE_SIZE_MASK) && + !(size & GRANULE_SIZE_MASK) && + (addr + size < (1ULL << BITS_PER_VA)) && + size >= GRANULE_SIZE); +} + +/* Func : get_pte + * Desc : Returns the page table entry governing a specific address. */ +static uint64_t get_pte(uint64_t addr) +{ + int shift = L0_ADDR_SHIFT; + /* Using pointer here is OK, as _ttb must be in 32bit space */ + uint64_t *pte = (uint64_t *)ttb(); + printk(BIOS_DEBUG, "%llx ", addr); + + while (1) { + int index = ((uintptr_t)addr >> shift) & + ((1ULL << BITS_RESOLVED_PER_LVL) - 1); + + switch ((pte[index] & AVAIL_MASK) >> AVAIL_SHIFT) { + case AVAIL_INVAL: + printk(BIOS_DEBUG, "INVAL ");break; + case AVAIL_TABLE: + printk(BIOS_DEBUG, "TABLE @ %llx\n", pte[index] & XLAT_ADDR_MASK);break; + case AVAIL_PAGE: + printk(BIOS_DEBUG, "PAGE %llx, %llx %llx\n", pte[index] & ((XLAT_ADDR_MASK<<1)), + (pte[index] & ((XLAT_ADDR_MASK<<1))) + (1ULL << shift) -1,pte[index] & 0xfff);break; + + case AVAIL_BLOCK: + printk(BIOS_DEBUG, "BLOCK %llx, %llx %llx\n", pte[index] & ((XLAT_ADDR_MASK<<1)), + (pte[index] & ((XLAT_ADDR_MASK<<1))) + (1ULL << shift) -1,pte[index] & 0xfff);break; + } + //printk(BIOS_DEBUG, "pte[%d] =%llx\n", index, pte[index]); + if ((pte[index] & DESC_MASK) != TABLE_DESC || + shift <= GRANULE_SIZE_SHIFT) + return pte[index]; + + pte = (uint64_t *)(uintptr_t)(pte[index] & XLAT_ADDR_MASK); + shift -= BITS_RESOLVED_PER_LVL; + } +} + +/* Func : assert_correct_ttb_mapping + * Desc : Asserts that mapping for addr matches the access type used by the + * page table walk (i.e. addr is correctly mapped to be part of the TTB). */ +static void assert_correct_ttb_mapping(uint64_t addr) +{ + return; + + uint64_t pte = get_pte(addr); + assert(((pte >> BLOCK_INDEX_SHIFT) & BLOCK_INDEX_MASK) + == BLOCK_INDEX_MEM_NORMAL); +} + +/* Func : mmu_config_range + * Desc : This function repeatedly calls init_xlat_table with the base + * address. Based on size returned from init_xlat_table, base_addr is updated + * and subsequent calls are made for initializing the xlat table until the whole + * region is initialized. + */ +void mmu_config_range(uint64_t start, uint64_t size, uint64_t tag) +{ + uint64_t base_addr = start; + uint64_t temp_size = size; + + printk(BIOS_INFO, "Mapping address range [0x%llx:0x%llx) as ", + start, start + size); + print_tag(BIOS_INFO, tag); + + sanity_check(base_addr, temp_size); + + while (temp_size) + temp_size -= init_xlat_table(base_addr + (size - temp_size), + temp_size, tag); + +} + +/* Func : mmu_init + * Desc : Initialize MMU registers and page table memory region. This must be + * called exactly ONCE PER BOOT before trying to configure any mappings. + */ +void mmu_init(void) +{ + next_free_table = cbmem_find(CBMEM_ID_TTB); + if (!next_free_table) + next_free_table = cbmem_add(CBMEM_ID_TTB, CONFIG_TTB_SIZE_KB * 1024); + if (!next_free_table) + die("Could not allocate memory for TTB\n"); + + /* Initially mark all table slots unused (first PTE == UNUSED_DESC). */ + uint64_t *table = (uint64_t *)ttb(); + for (; ettb() - (u8 *)table > 0; table += GRANULE_SIZE/sizeof(*table)) + table[0] = UNUSED_DESC; + + printk(BIOS_DEBUG, "Initialize the root table (L0)\n"); + /* Initialize the root table (L0) to be completely unmapped. */ + uint64_t *root = setup_new_table(INVALID_DESC, L0_XLAT_SIZE); + assert((u8 *)root == ttb()); +} + +/* Func : mmu_install_pagetables + * Desc : Install page tables, enable PAE and enable long mode. + * It does not enable paging and thus does not activate long mode! + */ +void mmu_install_pagetables(void) +{ + assert_correct_ttb_mapping((uintptr_t)ttb()); + assert_correct_ttb_mapping((uintptr_t)ettb() - 1); + + unsigned long long i; + for (i = 0x407a0000; i < 0x408a0000; i += (1024 * 1024)) { + uint64_t pte = get_pte(i); + if (pte == 5) + break; + } + /* Load the page table address */ + write_cr3((uintptr_t)cbmem_find(CBMEM_ID_TTB)); + + printk(BIOS_INFO, "MMU: installed page tables\n"); + + CRx_TYPE cr0; + CRx_TYPE cr4; + + /* Disable Paging */ + cr0 = read_cr0(); + cr0 &= ~CR0_PG; + write_cr0(cr0); + + /* Enable PAE */ + cr4 = read_cr4(); + cr4 |= CR4_PAE; + write_cr4(cr4); + + printk(BIOS_INFO, "MMU: enabled PAE\n"); + + #define MSR_EFER 0xc0000080 /* extended feature register */ + + /* Set the LM-bit */ + msr_t msr = rdmsr(MSR_EFER); + msr.lo |= (1 << 8); + wrmsr(MSR_EFER, msr); + + printk(BIOS_INFO, "MMU: enabled long mode\n"); + + /* + * Paging isn't enabled here! + * c_start.S will enable it to enter long mode. + */ +} diff --git a/src/commonlib/include/commonlib/cbmem_id.h b/src/commonlib/include/commonlib/cbmem_id.h index 042ea6e..366cb3e 100644 --- a/src/commonlib/include/commonlib/cbmem_id.h +++ b/src/commonlib/include/commonlib/cbmem_id.h @@ -62,6 +62,7 @@ #define CBMEM_ID_STAGEx_CACHE 0x57a9e100 #define CBMEM_ID_STAGEx_RAW 0x57a9e200 #define CBMEM_ID_STORAGE_DATA 0x53746f72 +#define CBMEM_ID_TTB 0x54544200 #define CBMEM_ID_TCPA_LOG 0x54435041 #define CBMEM_ID_TCPA_TCG_LOG 0x54445041 #define CBMEM_ID_TIMESTAMP 0x54494d45 diff --git a/src/cpu/qemu-x86/Kconfig b/src/cpu/qemu-x86/Kconfig index 70cce9b..a90acf6 100644 --- a/src/cpu/qemu-x86/Kconfig +++ b/src/cpu/qemu-x86/Kconfig @@ -18,7 +18,8 @@ select ARCH_BOOTBLOCK_X86_32 select ARCH_VERSTAGE_X86_32 select ARCH_ROMSTAGE_X86_32 - select ARCH_RAMSTAGE_X86_32 + #select ARCH_RAMSTAGE_X86_32 + select ARCH_RAMSTAGE_X86_64 select SMP select UDELAY_TSC select C_ENVIRONMENT_BOOTBLOCK diff --git a/src/cpu/x86/Makefile.inc b/src/cpu/x86/Makefile.inc index 3e8a664..1c9b83e 100644 --- a/src/cpu/x86/Makefile.inc +++ b/src/cpu/x86/Makefile.inc @@ -4,7 +4,7 @@ endif endif
-subdirs-y += pae +#subdirs-y += pae subdirs-$(CONFIG_PARALLEL_MP) += name ramstage-$(CONFIG_PARALLEL_MP) += mp_init.c ramstage-$(CONFIG_MIRROR_PAYLOAD_TO_RAM_BEFORE_LOADING) += mirror_payload.c diff --git a/src/lib/timestamp.c b/src/lib/timestamp.c index 105b696..e2c7450 100644 --- a/src/lib/timestamp.c +++ b/src/lib/timestamp.c @@ -178,7 +178,7 @@ tse->entry_stamp = ts_time - ts_table->base_time;
if (IS_ENABLED(CONFIG_TIMESTAMPS_ON_CONSOLE)) - printk(BIOS_SPEW, "Timestamp - %s: %" PRIu64 "\n", + printk(BIOS_SPEW, "Timestamp - %s: %llu\n", timestamp_name(id), ts_time);
if (ts_table->num_entries == ts_table->max_entries) diff --git a/src/mainboard/emulation/qemu-q35/Kconfig b/src/mainboard/emulation/qemu-q35/Kconfig index 3be034a..f14867b 100644 --- a/src/mainboard/emulation/qemu-q35/Kconfig +++ b/src/mainboard/emulation/qemu-q35/Kconfig @@ -12,6 +12,7 @@ select MAINBOARD_HAS_NATIVE_VGA_INIT select MAINBOARD_FORCE_NATIVE_VGA_INIT select BOOTBLOCK_CONSOLE + select IDT_IN_EVERY_STAGE
config MAINBOARD_DIR string diff --git a/src/mainboard/emulation/qemu-q35/Makefile.inc b/src/mainboard/emulation/qemu-q35/Makefile.inc index 1503220..65b8e70 100644 --- a/src/mainboard/emulation/qemu-q35/Makefile.inc +++ b/src/mainboard/emulation/qemu-q35/Makefile.inc @@ -2,4 +2,6 @@ ramstage-y += ../qemu-i440fx/memory.c ramstage-y += ../qemu-i440fx/fw_cfg.c romstage-y += ../qemu-i440fx/memory.c +romstage-y += ../../../arch/x86/mmu.c + bootblock-y += bootblock.c diff --git a/src/mainboard/emulation/qemu-q35/romstage.c b/src/mainboard/emulation/qemu-q35/romstage.c index 2b8d935..592d33d 100644 --- a/src/mainboard/emulation/qemu-q35/romstage.c +++ b/src/mainboard/emulation/qemu-q35/romstage.c @@ -21,15 +21,27 @@ #include <timestamp.h> #include <southbridge/intel/i82801ix/i82801ix.h> #include <program_loading.h> +#include <arch/mmu.h> +#include <arch/exception.h> + +#include "../qemu-i440fx/memory.h"
asmlinkage void car_stage_entry(void) { i82801ix_early_init(); console_init(); + exception_init();
- cbmem_recovery(0); + cbmem_initialize_empty();
timestamp_add_now(TS_START_ROMSTAGE);
+ mmu_init(); + mmu_config_range(0, 0x100000000ULL, PAT_WT); + mmu_config_range(0, (uint64_t)qemu_get_memory_size() * 1024ULL, PAT_WB); + if (qemu_get_high_memory_size() > 0) + mmu_config_range(0x100000000ULL, (uint64_t)qemu_get_high_memory_size() * 1024ULL, PAT_WB); + mmu_install_pagetables(); + run_ramstage(); } diff --git a/src/southbridge/intel/common/smi.c b/src/southbridge/intel/common/smi.c index 3c25556..b543b8b 100644 --- a/src/southbridge/intel/common/smi.c +++ b/src/southbridge/intel/common/smi.c @@ -150,7 +150,7 @@ "outb %%al, %%dx\n\t" : /* ignore result */ : "a" (APM_CNT_GNVS_UPDATE), - "b" ((u32)gnvs), + "b" ((u32)(uintptr_t)gnvs), "d" (APM_CNT) ); } diff --git a/src/southbridge/intel/i82801ix/hdaudio.c b/src/southbridge/intel/i82801ix/hdaudio.c index 607604b..b4cee46 100644 --- a/src/southbridge/intel/i82801ix/hdaudio.c +++ b/src/southbridge/intel/i82801ix/hdaudio.c @@ -278,7 +278,7 @@ // NOTE this will break as soon as the Azalia get's a bar above // 4G. Is there anything we can do about it? base = res2mmio(res, 0, 0); - printk(BIOS_DEBUG, "Azalia: base = %08x\n", (u32)base); + printk(BIOS_DEBUG, "Azalia: base = %p\n", base); codec_mask = codec_detect(base);
if (codec_mask) { diff --git a/src/southbridge/intel/i82801ix/lpc.c b/src/southbridge/intel/i82801ix/lpc.c index a69b879..5d7a226c 100644 --- a/src/southbridge/intel/i82801ix/lpc.c +++ b/src/southbridge/intel/i82801ix/lpc.c @@ -553,7 +553,7 @@
/* Add it to SSDT. */ acpigen_write_scope("\"); - acpigen_write_name_dword("NVSA", (u32) gnvs); + acpigen_write_name_dword("NVSA", (u32) (uintptr_t)gnvs); acpigen_pop_len(); } } diff --git a/src/southbridge/intel/i82801ix/sata.c b/src/southbridge/intel/i82801ix/sata.c index dcdeeb4..71030948 100644 --- a/src/southbridge/intel/i82801ix/sata.c +++ b/src/southbridge/intel/i82801ix/sata.c @@ -32,7 +32,7 @@ u32 reg32;
/* Initialize AHCI memory-mapped space */ - u8 *abar = (u8 *)pci_read_config32(dev, PCI_BASE_ADDRESS_5); + u8 *abar = (u8 *)(uintptr_t)pci_read_config32(dev, PCI_BASE_ADDRESS_5); printk(BIOS_DEBUG, "ABAR: %p\n", abar);
/* Set AHCI access mode. diff --git a/util/cbfstool/elf.h b/util/cbfstool/elf.h index a0bb35d..43fd7f3 100644 --- a/util/cbfstool/elf.h +++ b/util/cbfstool/elf.h @@ -1148,6 +1148,43 @@ /* Keep this the last entry. */ #define R_386_NUM 38
+/* AMD64 specific definitions. */ +#define R_AMD64_NONE 0 /* relocation types */ +#define R_AMD64_64 1 +#define R_AMD64_PC32 2 +#define R_AMD64_GOT32 3 +#define R_AMD64_PLT32 4 +#define R_AMD64_COPY 5 +#define R_AMD64_GLOB_DAT 6 +#define R_AMD64_JUMP_SLOT 7 +#define R_AMD64_RELATIVE 8 +#define R_AMD64_GOTPCREL 9 +#define R_AMD64_32 10 +#define R_AMD64_32S 11 +#define R_AMD64_16 12 +#define R_AMD64_PC16 13 +#define R_AMD64_8 14 +#define R_AMD64_PC8 15 +#define R_AMD64_DTPMOD64 16 +#define R_AMD64_DTPOFF64 17 +#define R_AMD64_TPOFF64 18 +#define R_AMD64_TLSGD 19 +#define R_AMD64_TLSLD 20 +#define R_AMD64_DTPOFF32 21 +#define R_AMD64_GOTTPOFF 22 +#define R_AMD64_TPOFF32 23 +#define R_AMD64_PC64 24 +#define R_AMD64_GOTOFF64 25 +#define R_AMD64_GOTPC32 26 +#define R_AMD64_GOT64 27 /* reserved for future expansion */ +#define R_AMD64_GOTPCREL64 28 /* reserved for future expansion */ +#define R_AMD64_GOTPC64 29 /* reserved for future expansion */ +#define R_AMD64_GOTPLT64 30 /* reserved for future expansion */ +#define R_AMD64_PLTOFF64 31 /* reserved for future expansion */ +#define R_AMD64_SIZE32 32 +#define R_AMD64_SIZE64 33 +#define R_AMD64_NUM 34 + /* SUN SPARC specific definitions. */
/* Legal values for ST_TYPE subfield of st_info (symbol type). */ diff --git a/util/cbfstool/elfheaders.c b/util/cbfstool/elfheaders.c index 9d02c30..8da54d0 100644 --- a/util/cbfstool/elfheaders.c +++ b/util/cbfstool/elfheaders.c @@ -1072,6 +1072,9 @@ case EM_386: type = R_386_32; break; + case EM_X86_64: + type = R_AMD64_64; + break; case EM_ARM: type = R_ARM_ABS32; break; diff --git a/util/cbfstool/rmodule.c b/util/cbfstool/rmodule.c index 07957cb..b7969c9 100644 --- a/util/cbfstool/rmodule.c +++ b/util/cbfstool/rmodule.c @@ -43,6 +43,29 @@ return (type == R_386_32); }
+static int valid_reloc_amd64(Elf64_Rela *rel) +{ + int type; + + type = ELF64_R_TYPE(rel->r_info); + + /* Only these 2 relocations are expected to be found. */ + return (type == R_AMD64_64 || + type == R_AMD64_PC64 || + type == R_AMD64_32S || + type == R_AMD64_32 || + type == R_AMD64_PC32); +} + +static int should_emit_amd64(Elf64_Rela *rel) +{ + int type; + + type = ELF64_R_TYPE(rel->r_info); + + return (type == R_AMD64_64); +} + static int valid_reloc_arm(Elf64_Rela *rel) { int type; @@ -100,6 +123,11 @@ .should_emit = should_emit_386, }, { + .arch = EM_X86_64, + .valid_type = valid_reloc_amd64, + .should_emit = should_emit_amd64, + }, + { .arch = EM_ARM, .valid_type = valid_reloc_arm, .should_emit = should_emit_arm, diff --git a/util/xcompile/xcompile b/util/xcompile/xcompile index 6d82a4d..60d8a39 100755 --- a/util/xcompile/xcompile +++ b/util/xcompile/xcompile @@ -236,7 +236,13 @@ # The Quark processor doesn't support the instructions # introduced with the Pentium 6 architecture, so allow it # to use i586 instead. -if [ "${TARCH}" = "x86_64" ] || [ "${TARCH}" = "x86_32" ]; then +if [ "${TARCH}" = "x86_64" ]; then +cat <<EOF + GCC_CFLAGS_${TARCH} += -march=core2 +EOF +fi + +if [ "${TARCH}" = "x86_32" ]; then cat <<EOF
ifneq ($(CONFIG_USE_MARCH_586)$(CONFIG_LP_USE_MARCH_586),)