diff --git a/src/arch/x86/Kconfig b/src/arch/x86/Kconfig index f5bbd7827e..d7f144e179 100644 --- a/src/arch/x86/Kconfig +++ b/src/arch/x86/Kconfig @@ -286,3 +286,19 @@ config COLLECT_TIMESTAMPS_TSC depends on COLLECT_TIMESTAMPS help Use the TSC as the timestamp source. + +config PAGING_IN_CACHE_AS_RAM + bool + default n + depends on ARCH_X86 + help + Chipsets scan select this option to preallocate area in cache-as-ram + for storing paging data structures. PAE paging is currently the + only thing being supported. + +config NUM_CAR_PAGE_TABLE_PAGES + int + default 5 + depends on PAGING_IN_CACHE_AS_RAM + help + The number of 4KiB pages that should be pre-allocated for page tables. diff --git a/src/arch/x86/car.ld b/src/arch/x86/car.ld index bfc1b03bd3..2acd3f673f 100644 --- a/src/arch/x86/car.ld +++ b/src/arch/x86/car.ld @@ -19,6 +19,13 @@ . = CONFIG_DCACHE_RAM_BASE; .car.data . (NOLOAD) : { _car_region_start = . ; +#if IS_ENABLED(CONFIG_PAGING_IN_CACHE_AS_RAM) + /* Page table pre-allocation. CONFIG_DCACHE_RAM_BASE should be 4KiB + * aligned when using this option. */ + _pagetables = . ; + . += 4096 * CONFIG_NUM_CAR_PAGE_TABLE_PAGES; + _epagetables = . ; +#endif /* Vboot work buffer is completely volatile outside of verstage and * romstage. Appropriate code needs to handle the transition. */ #if IS_ENABLED(CONFIG_VBOOT_SEPARATE_VERSTAGE) @@ -37,6 +44,16 @@ * so that multiple stages (romstage and verstage) have a consistent * link address of these shared objects. */ PRERAM_CBMEM_CONSOLE(., (CONFIG_LATE_CBMEM_INIT ? 0 : CONFIG_PRERAM_CBMEM_CONSOLE_SIZE)) +#if IS_ENABLED(CONFIG_PAGING_IN_CACHE_AS_RAM) + . = ALIGN(32); + /* Page directory pointer table resides here. There are 4 8-byte entries + * totalling 32 bytes that need to be 32-byte aligned. The reason the + * pdpt are not colocated with the rest of the page tables is to reduce + * fragmentation of the CAR space that persists across stages. */ + _pdpt = .; + . += 32; + _epdpt = .; +#endif _car_relocatable_data_start = .; /* The timestamp implementation relies on this storage to be around * after migration. One of the fields indicates not to use it as the @@ -78,3 +95,6 @@ } _bogus = ASSERT((CONFIG_DCACHE_RAM_SIZE == 0) || (SIZEOF(.car.data) <= CONFIG_DCACHE_RAM_SIZE), "Cache as RAM area is too full"); +#if IS_ENABLED(CONFIG_PAGING_IN_CACHE_AS_RAM) +_bogus2 = ASSERT(_pagetables == ALIGN(_pagetables, 4096), "_pagetables aren't 4KiB aligned"); +#endif diff --git a/src/cpu/x86/pae/pgtbl.c b/src/cpu/x86/pae/pgtbl.c index cd03547ef8..c272b2da1c 100644 --- a/src/cpu/x86/pae/pgtbl.c +++ b/src/cpu/x86/pae/pgtbl.c @@ -13,6 +13,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -22,6 +23,7 @@ #include #include #include +#include void paging_enable_pae_cr3(uintptr_t cr3) { @@ -169,3 +171,48 @@ void paging_set_default_pat(void) PAT_ENCODE(UC_MINUS, 6) | PAT_ENCODE(WT, 7); paging_set_pat(pat); } + +static int read_from_cbfs(const char *name, void *buf, size_t size) +{ + struct cbfsf fh; + struct region_device rdev; + size_t rdev_sz; + + if (cbfs_boot_locate(&fh, name, NULL)) + return -1; + + cbfs_file_data(&rdev, &fh); + + rdev_sz = region_device_sz(&rdev); + + if (size < rdev_sz) { + printk(BIOS_ERR, "%s region too small to load: %zx < %zx\n", + name, size, rdev_sz); + return -1; + } + + if (rdev_readat(&rdev, buf, 0, rdev_sz) != rdev_sz) + return -1; + + return 0; +} + +int paging_enable_for_car(const char *pdpt_name, const char *pt_name) +{ + if (!ENV_CACHE_AS_RAM) + return -1; + + if (read_from_cbfs(pdpt_name, _pdpt, _pdpt_size)) { + printk(BIOS_ERR, "Couldn't load pdpt\n"); + return -1; + } + + if (read_from_cbfs(pt_name, _pagetables, _pagetables_size)) { + printk(BIOS_ERR, "Couldn't load page tables\n"); + return -1; + } + + paging_enable_pae_cr3((uintptr_t)_pdpt); + + return 0; +} diff --git a/src/include/cpu/x86/pae.h b/src/include/cpu/x86/pae.h index 96999bb0a8..a8b5e893fc 100644 --- a/src/include/cpu/x86/pae.h +++ b/src/include/cpu/x86/pae.h @@ -27,6 +27,11 @@ void paging_set_pat(uint64_t pat); /* Set coreboot default PAT value. */ void paging_set_default_pat(void); +/* Load page directory pointer table and page tables from cbfs identified by + * the provided the names then enable paging. Return 0 on success, < 0 on + * failure. */ +int paging_enable_for_car(const char *pdpt_name, const char *pt_name); + #define MAPPING_ERROR ((void *)0xffffffffUL) void *map_2M_page(unsigned long page); diff --git a/src/include/symbols.h b/src/include/symbols.h index ada7fa70df..5b92899cee 100644 --- a/src/include/symbols.h +++ b/src/include/symbols.h @@ -106,6 +106,10 @@ extern u8 _framebuffer[]; extern u8 _eframebuffer[]; #define _framebuffer_size (_eframebuffer - _framebuffer) +extern u8 _pdpt[]; +extern u8 _epdpt[]; +#define _pdpt_size (_epdpt - _pdpt) + /* Put this into a .c file accessing a linker script region to mark that region * as "optional". If it is defined in memlayout.ld (or anywhere else), the * values from that definition will be used. If not, start, end and size will