Patrick Rudolph has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/30077 )
Change subject: sb/intel/lynxpoint: Handle H81 only having 6 PCIe root ports
......................................................................
Patch Set 2: Code-Review+1
--
To view, visit https://review.coreboot.org/c/coreboot/+/30077
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: If3ce217e8a4f4ea4e111e4525b03dbbfc63f92b0
Gerrit-Change-Number: 30077
Gerrit-PatchSet: 2
Gerrit-Owner: Tristan Corrick <tristan(a)corrick.kiwi>
Gerrit-Reviewer: Patrick Georgi <pgeorgi(a)google.com>
Gerrit-Reviewer: Patrick Rudolph <siro(a)das-labor.org>
Gerrit-Reviewer: Tristan Corrick <tristan(a)corrick.kiwi>
Gerrit-Reviewer: build bot (Jenkins) <no-reply(a)coreboot.org>
Gerrit-Comment-Date: Sun, 09 Dec 2018 14:37:45 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/30119
Change subject: arch/x86/mmu: Port armv8 MMU to x86_64
......................................................................
arch/x86/mmu: Port armv8 MMU to x86_64
Add functions to setup page tables for long mode.
Required to support x86_64, as the MMU is always active.
Tested on qemu.
Change-Id: I6e8b46e65925823a84b8ccd647c7d6848aa20992
Signed-off-by: Patrick Rudolph <siro(a)das-labor.org>
---
M src/arch/x86/Kconfig
A src/arch/x86/include/arch/mmu.h
A src/arch/x86/mmu.c
M src/commonlib/include/commonlib/cbmem_id.h
4 files changed, 486 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/19/30119/1
diff --git a/src/arch/x86/Kconfig b/src/arch/x86/Kconfig
index 7c8371e..11389c0 100644
--- a/src/arch/x86/Kconfig
+++ b/src/arch/x86/Kconfig
@@ -317,6 +317,13 @@
help
The number of 4KiB pages that should be pre-allocated for page tables.
+config NUM_PAGE_TABLE_PAGES
+ int
+ default 128
+ depends on ARCH_X86
+ help
+ The number of 4KiB pages that should be available for ramstage.
+
# Provide the interrupt handlers to every stage. Not all
# stages may take advantage.
config IDT_IN_EVERY_STAGE
diff --git a/src/arch/x86/include/arch/mmu.h b/src/arch/x86/include/arch/mmu.h
new file mode 100644
index 0000000..4cb0354
--- /dev/null
+++ b/src/arch/x86/include/arch/mmu.h
@@ -0,0 +1,103 @@
+/*
+ * 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 *new_page_table);
+/* Install page tables */
+void mmu_enable(void *new_page_table);
+void mmu_config_range(uint64_t start, uint64_t size, uint64_t tag);
+
+#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..39802f0
--- /dev/null
+++ b/src/arch/x86/mmu.c
@@ -0,0 +1,375 @@
+/*
+ * 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;
+static CAR_GLOBAL uint64_t *page_tables;
+
+#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, "MMU: PTE PAT defaulting to WB: %lx\n", pat);
+ return 0 | 0 | 0;
+ }
+}
+
+static uint64_t *pagetable(void)
+{
+ return car_get_var(page_tables);
+}
+
+static uint64_t *epagetable(void)
+{
+ return (uint64_t *)((u8 *)car_get_var(page_tables) +
+ (CONFIG_NUM_PAGE_TABLE_PAGES * 4 * KiB));
+}
+
+/* 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 pagetable and set it up to match
+ * old parent entry.
+ */
+static uint64_t *setup_new_table(uint64_t desc, uint64_t xlat_size)
+{
+ uint64_t *nft = car_get_var(next_free_table);
+ while (nft[0] != UNUSED_DESC) {
+ car_set_var(next_free_table, car_get_var(next_free_table) +
+ GRANULE_SIZE/sizeof(*next_free_table));
+ nft = car_get_var(next_free_table);
+ if (epagetable() - car_get_var(next_free_table) <= 0)
+ die("Ran out of page table space!");
+ }
+
+ uint64_t frame_base = desc & XLAT_ADDR_MASK;
+ printk(BIOS_DEBUG, "MMU: Backing address range [0x%llx:0x%llx) with "
+ "new page table @%p\n", frame_base, frame_base +
+ (xlat_size << BITS_RESOLVED_PER_LVL), nft);
+
+ if (desc == INVALID_DESC) {
+ memset(nft, 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++) {
+ nft[i] = desc;
+ desc += xlat_size;
+ }
+ }
+
+ return nft;
+}
+
+/* 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 = pagetable();
+ 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 _pagetable must be in 32bit space */
+ uint64_t *pte = pagetable();
+
+ while (1) {
+ int index = ((uintptr_t)addr >> shift) &
+ ((1ULL << BITS_RESOLVED_PER_LVL) - 1);
+
+ 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_pagetable_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 pagetable).
+ */
+static void assert_correct_pagetable_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, "MMU: 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);
+
+}
+
+/**
+ * @brief Init MMU for long mode
+ *
+ * @param new_page_table The new area for page tables. Must not overlap with
+ * currently installed page tables.
+ *
+ * Can be called multiple times, as long as the new page table area does not
+ * overlap with page tables in use.
+ * If paging isn't active, there are no limitations.
+ */
+void mmu_init(void *new_page_table)
+{
+ assert(new_page_table);
+ assert(pagetable() != new_page_table);
+
+ car_set_var(page_tables, new_page_table);
+ car_set_var(next_free_table, new_page_table);
+
+ /* Initially mark all table slots unused (first PTE == UNUSED_DESC). */
+ uint64_t *table = pagetable();
+ for (; epagetable() - table > 0; table += GRANULE_SIZE/sizeof(*table))
+ table[0] = UNUSED_DESC;
+
+ printk(BIOS_DEBUG, "MMU: 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(root == pagetable());
+}
+
+/* Func : mmu_enable
+ * Desc : Install page tables, enable PAE and enable long mode.
+ */
+void mmu_enable(void *new_page_table)
+{
+ assert_correct_pagetable_mapping((uintptr_t)new_page_table);
+ assert_correct_pagetable_mapping((uintptr_t)new_page_table +
+ (CONFIG_NUM_PAGE_TABLE_PAGES * 4 * KiB) - 1);
+
+ /* Load the new page table address */
+ write_cr3((uintptr_t)new_page_table);
+
+ printk(BIOS_INFO, "MMU: Installed new page tables\n");
+
+ CRx_TYPE cr0;
+
+ cr0 = read_cr0();
+ if (!(cr0 & CR0_PG)) {
+ /* Enable paging */
+ cr0 |= CR0_PG;
+ write_cr0(cr0);
+ }
+}
diff --git a/src/commonlib/include/commonlib/cbmem_id.h b/src/commonlib/include/commonlib/cbmem_id.h
index 2e0eeb6..4a37663 100644
--- a/src/commonlib/include/commonlib/cbmem_id.h
+++ b/src/commonlib/include/commonlib/cbmem_id.h
@@ -46,6 +46,7 @@
#define CBMEM_ID_NONE 0x00000000
#define CBMEM_ID_PIRQ 0x49525154
#define CBMEM_ID_POWER_STATE 0x50535454
+#define CBMEM_ID_PAGE 0x50414745
#define CBMEM_ID_RAM_OOPS 0x05430095
#define CBMEM_ID_RAMSTAGE 0x9a357a9e
#define CBMEM_ID_RAMSTAGE_CACHE 0x9a3ca54e
--
To view, visit https://review.coreboot.org/c/coreboot/+/30119
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I6e8b46e65925823a84b8ccd647c7d6848aa20992
Gerrit-Change-Number: 30119
Gerrit-PatchSet: 1
Gerrit-Owner: Patrick Rudolph <siro(a)das-labor.org>
Gerrit-MessageType: newchange