arch/x86: add support for cache-as-ram paging
Processors, such as glk, need to have paging enabled while in cache-as-ram mode because the front end is agressive about fetching lines into the L1I cache. If the line is dirty and in the L1D then it writes it back to "memory". However, in this case there is no backing store so the cache-as-ram data that was written back transforms to all 0xff's when read back in causing corruption. In order to mitigate the failure add x86 architecture support for enabling paging while in cache-as-ram mode. A Kconfig variable, NUM_CAR_PAGE_TABLE_PAGES, determines the number of pages to carve out for page tables within the cache-as-ram region. Additionally, the page directory pointer table is also carved out of cache-as-ram. Both areas are allocated from the persist-across-stages region of cache-as-ram so all stages utilizing cache-as-ram don't corrupt the page tables. The two paging-related areas are loaded by calling paging_enable_for_car() with the names of cbfs files to load the initial paging structures from. BUG=b:72728953 Change-Id: I7ea6e3e7be94a0ef9fd3205ce848e539bfbdcb6e Signed-off-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: https://review.coreboot.org/25717 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Furquan Shaikh <furquan@google.com> Reviewed-by: Justin TerAvest <teravest@chromium.org>
This commit is contained in:
parent
cfb1680a88
commit
0f35af8f42
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <cbfs.h>
|
||||
#include <compiler.h>
|
||||
#include <console/console.h>
|
||||
#include <cpu/cpu.h>
|
||||
|
@ -22,6 +23,7 @@
|
|||
#include <cpu/x86/pae.h>
|
||||
#include <rules.h>
|
||||
#include <string.h>
|
||||
#include <symbols.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue