Revert "x86: pre-allocate address space"
This reverts commit 64f05d443a
.
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
parent
a2ec139bf7
commit
e980848ba7
19 changed files with 222 additions and 142 deletions
|
@ -591,7 +591,7 @@ config KERNEL_VM_OFFSET
|
||||||
|
|
||||||
config KERNEL_VM_SIZE
|
config KERNEL_VM_SIZE
|
||||||
hex "Size of kernel address space in bytes"
|
hex "Size of kernel address space in bytes"
|
||||||
default 0x800000
|
default 0xC0000000
|
||||||
help
|
help
|
||||||
Size of the kernel's address space. Constraining this helps control
|
Size of the kernel's address space. Constraining this helps control
|
||||||
how much total memory can be used for page tables.
|
how much total memory can be used for page tables.
|
||||||
|
|
|
@ -189,6 +189,29 @@ config X86_MMU
|
||||||
and creates a set of page tables at boot time that is runtime-
|
and creates a set of page tables at boot time that is runtime-
|
||||||
mutable.
|
mutable.
|
||||||
|
|
||||||
|
config X86_MMU_PAGE_POOL_PAGES
|
||||||
|
int "Number of pages to reserve for building page tables"
|
||||||
|
default 0
|
||||||
|
depends on X86_MMU
|
||||||
|
help
|
||||||
|
Define the number of pages in the pool used to allocate page table
|
||||||
|
data structures at runtime.
|
||||||
|
|
||||||
|
Pages might need to be drawn from the pool during memory mapping
|
||||||
|
operations, unless the address space has been completely pre-allocated.
|
||||||
|
|
||||||
|
Pages will need to drawn from the pool to initialize memory domains.
|
||||||
|
This does not include the default memory domain if KPTI=n.
|
||||||
|
|
||||||
|
The specific value used here depends on the size of physical RAM,
|
||||||
|
how much additional virtual memory will be mapped at runtime, and
|
||||||
|
how many memory domains need to be initialized.
|
||||||
|
|
||||||
|
The current suite of Zephyr test cases may initialize at most two
|
||||||
|
additional memory domains besides the default domain.
|
||||||
|
|
||||||
|
Unused pages in this pool cannot be used for other purposes.
|
||||||
|
|
||||||
config X86_COMMON_PAGE_TABLE
|
config X86_COMMON_PAGE_TABLE
|
||||||
bool "Use a single page table for all threads"
|
bool "Use a single page table for all threads"
|
||||||
default n
|
default n
|
||||||
|
@ -201,18 +224,6 @@ config X86_COMMON_PAGE_TABLE
|
||||||
page tables in place. This is much slower, but uses much less RAM
|
page tables in place. This is much slower, but uses much less RAM
|
||||||
for page tables.
|
for page tables.
|
||||||
|
|
||||||
config X86_MAX_ADDITIONAL_MEM_DOMAINS
|
|
||||||
int "Maximum number of memory domains"
|
|
||||||
default 3
|
|
||||||
depends on X86_MMU && USERSPACE && !X86_COMMON_PAGE_TABLE
|
|
||||||
help
|
|
||||||
The initial page tables at boot are pre-allocated, and used for the
|
|
||||||
default memory domain. Instantiation of additional memory domains
|
|
||||||
if common page tables are in use requires a pool of free pinned
|
|
||||||
memory pages for constructing page tables.
|
|
||||||
|
|
||||||
Zephyr test cases assume 3 additional domains can be instantiated.
|
|
||||||
|
|
||||||
config X86_NO_MELTDOWN
|
config X86_NO_MELTDOWN
|
||||||
bool
|
bool
|
||||||
help
|
help
|
||||||
|
|
|
@ -195,25 +195,24 @@ static const struct paging_level paging_levels[] = {
|
||||||
#define NUM_PD_ENTRIES 1024U
|
#define NUM_PD_ENTRIES 1024U
|
||||||
#define NUM_PT_ENTRIES 1024U
|
#define NUM_PT_ENTRIES 1024U
|
||||||
#endif /* !CONFIG_X86_64 && !CONFIG_X86_PAE */
|
#endif /* !CONFIG_X86_64 && !CONFIG_X86_PAE */
|
||||||
|
|
||||||
/* Memory range covered by an instance of various table types */
|
/* Memory range covered by an instance of various table types */
|
||||||
#define PT_AREA ((uintptr_t)(CONFIG_MMU_PAGE_SIZE * NUM_PT_ENTRIES))
|
#define PT_AREA ((uintptr_t)(CONFIG_MMU_PAGE_SIZE * NUM_PT_ENTRIES))
|
||||||
#define PD_AREA (PT_AREA * NUM_PD_ENTRIES)
|
#define PD_AREA (PT_AREA * NUM_PD_ENTRIES)
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
#define PDPT_AREA (PD_AREA * NUM_PDPT_ENTRIES)
|
#define PDPT_AREA (PD_AREA * NUM_PDPT_ENTRIES)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define VM_ADDR CONFIG_KERNEL_VM_BASE
|
#define VM_ADDR DT_REG_ADDR(DT_CHOSEN(zephyr_sram))
|
||||||
#define VM_SIZE CONFIG_KERNEL_VM_SIZE
|
#define VM_SIZE DT_REG_SIZE(DT_CHOSEN(zephyr_sram))
|
||||||
|
|
||||||
/* Define a range [PT_START, PT_END) which is the memory range
|
/* Define a range [PT_START, PT_END) which is the memory range
|
||||||
* covered by all the page tables needed for the address space
|
* covered by all the page tables needed for system RAM
|
||||||
*/
|
*/
|
||||||
#define PT_START ((uintptr_t)ROUND_DOWN(VM_ADDR, PT_AREA))
|
#define PT_START ((uintptr_t)ROUND_DOWN(VM_ADDR, PT_AREA))
|
||||||
#define PT_END ((uintptr_t)ROUND_UP(VM_ADDR + VM_SIZE, PT_AREA))
|
#define PT_END ((uintptr_t)ROUND_UP(VM_ADDR + VM_SIZE, PT_AREA))
|
||||||
|
|
||||||
/* Number of page tables needed to cover address space. Depends on the specific
|
/* Number of page tables needed to cover system RAM. Depends on the specific
|
||||||
* bounds, but roughly 1 page table per 2MB of RAM
|
* bounds of system RAM, but roughly 1 page table per 2MB of RAM
|
||||||
*/
|
*/
|
||||||
#define NUM_PT ((PT_END - PT_START) / PT_AREA)
|
#define NUM_PT ((PT_END - PT_START) / PT_AREA)
|
||||||
|
|
||||||
|
@ -223,8 +222,8 @@ static const struct paging_level paging_levels[] = {
|
||||||
*/
|
*/
|
||||||
#define PD_START ((uintptr_t)ROUND_DOWN(VM_ADDR, PD_AREA))
|
#define PD_START ((uintptr_t)ROUND_DOWN(VM_ADDR, PD_AREA))
|
||||||
#define PD_END ((uintptr_t)ROUND_UP(VM_ADDR + VM_SIZE, PD_AREA))
|
#define PD_END ((uintptr_t)ROUND_UP(VM_ADDR + VM_SIZE, PD_AREA))
|
||||||
/* Number of page directories needed to cover the address space. Depends on the
|
/* Number of page directories needed to cover system RAM. Depends on the
|
||||||
* specific bounds, but roughly 1 page directory per 1GB of RAM
|
* specific bounds of system RAM, but roughly 1 page directory per 1GB of RAM
|
||||||
*/
|
*/
|
||||||
#define NUM_PD ((PD_END - PD_START) / PD_AREA)
|
#define NUM_PD ((PD_END - PD_START) / PD_AREA)
|
||||||
#else
|
#else
|
||||||
|
@ -234,11 +233,13 @@ static const struct paging_level paging_levels[] = {
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
/* Same semantics as above, but for the page directory pointer tables needed
|
/* Same semantics as above, but for the page directory pointer tables needed
|
||||||
* to cover the address space. On 32-bit there is just one 4-entry PDPT.
|
* to cover system RAM. On 32-bit there is just one 4-entry PDPT.
|
||||||
*/
|
*/
|
||||||
#define PDPT_START ((uintptr_t)ROUND_DOWN(VM_ADDR, PDPT_AREA))
|
#define PDPT_START ((uintptr_t)ROUND_DOWN(VM_ADDR, PDPT_AREA))
|
||||||
#define PDPT_END ((uintptr_t)ROUND_UP(VM_ADDR + VM_SIZE, PDPT_AREA))
|
#define PDPT_END ((uintptr_t)ROUND_UP(VM_ADDR + VM_SIZE, PDPT_AREA))
|
||||||
/* Number of PDPTs needed to cover the address space. 1 PDPT per 512GB of VM */
|
/* Number of PDPTs needed to cover system RAM. Depends on the
|
||||||
|
* specific bounds of system RAM, but roughly 1 PDPT per 512GB of RAM
|
||||||
|
*/
|
||||||
#define NUM_PDPT ((PDPT_END - PDPT_START) / PDPT_AREA)
|
#define NUM_PDPT ((PDPT_END - PDPT_START) / PDPT_AREA)
|
||||||
|
|
||||||
/* All pages needed for page tables, using computed values plus one more for
|
/* All pages needed for page tables, using computed values plus one more for
|
||||||
|
@ -699,6 +700,47 @@ void z_x86_dump_mmu_flags(pentry_t *ptables, void *virt)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_EXCEPTION_DEBUG */
|
#endif /* CONFIG_EXCEPTION_DEBUG */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pool of free memory pages for creating new page tables, as needed.
|
||||||
|
*
|
||||||
|
* XXX: This is very crude, once obtained, pages may not be returned. Tuning
|
||||||
|
* the optimal value of CONFIG_X86_MMU_PAGE_POOL_PAGES is not intuitive,
|
||||||
|
* Better to have a kernel managed page pool of unused RAM that can be used for
|
||||||
|
* this, sbrk(), and other anonymous mappings. See #29526
|
||||||
|
*/
|
||||||
|
static uint8_t __noinit
|
||||||
|
page_pool[CONFIG_MMU_PAGE_SIZE * CONFIG_X86_MMU_PAGE_POOL_PAGES]
|
||||||
|
__aligned(CONFIG_MMU_PAGE_SIZE);
|
||||||
|
|
||||||
|
static uint8_t *page_pos = page_pool + sizeof(page_pool);
|
||||||
|
|
||||||
|
/* Return a zeroed and suitably aligned memory page for page table data
|
||||||
|
* from the global page pool
|
||||||
|
*/
|
||||||
|
static void *page_pool_get(void)
|
||||||
|
{
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
if (page_pos == page_pool) {
|
||||||
|
ret = NULL;
|
||||||
|
} else {
|
||||||
|
page_pos -= CONFIG_MMU_PAGE_SIZE;
|
||||||
|
ret = page_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != NULL) {
|
||||||
|
memset(ret, 0, CONFIG_MMU_PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debugging function to show how many pages are free in the pool */
|
||||||
|
static inline unsigned int pages_free(void)
|
||||||
|
{
|
||||||
|
return (page_pos - page_pool) / CONFIG_MMU_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset permissions on a PTE to original state when the mapping was made */
|
/* Reset permissions on a PTE to original state when the mapping was made */
|
||||||
static inline pentry_t reset_pte(pentry_t old_val)
|
static inline pentry_t reset_pte(pentry_t old_val)
|
||||||
{
|
{
|
||||||
|
@ -807,6 +849,12 @@ static inline bool atomic_pte_cas(pentry_t *target, pentry_t old_value,
|
||||||
*/
|
*/
|
||||||
#define OPTION_RESET BIT(2)
|
#define OPTION_RESET BIT(2)
|
||||||
|
|
||||||
|
/* Indicates that allocations from the page pool are allowed to instantiate
|
||||||
|
* new paging structures. Only necessary when establishing new mappings
|
||||||
|
* and the entire address space isn't pre-allocated.
|
||||||
|
*/
|
||||||
|
#define OPTION_ALLOC BIT(3)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Atomically update bits in a page table entry
|
* Atomically update bits in a page table entry
|
||||||
*
|
*
|
||||||
|
@ -875,6 +923,10 @@ static inline pentry_t pte_atomic_update(pentry_t *pte, pentry_t update_val,
|
||||||
* modified by another CPU, using atomic operations to update the requested
|
* modified by another CPU, using atomic operations to update the requested
|
||||||
* bits and return the previous PTE value.
|
* bits and return the previous PTE value.
|
||||||
*
|
*
|
||||||
|
* This function is NOT atomic with respect to allocating intermediate
|
||||||
|
* paging structures, and this must be called with x86_mmu_lock held if
|
||||||
|
* OPTION_ALLOC is used.
|
||||||
|
*
|
||||||
* Common mask values:
|
* Common mask values:
|
||||||
* MASK_ALL - Update all PTE bits. Exitsing state totally discarded.
|
* MASK_ALL - Update all PTE bits. Exitsing state totally discarded.
|
||||||
* MASK_PERM - Only update permission bits. All other bits and physical
|
* MASK_PERM - Only update permission bits. All other bits and physical
|
||||||
|
@ -886,13 +938,17 @@ static inline pentry_t pte_atomic_update(pentry_t *pte, pentry_t update_val,
|
||||||
* @param [out] old_val_ptr Filled in with previous PTE value. May be NULL.
|
* @param [out] old_val_ptr Filled in with previous PTE value. May be NULL.
|
||||||
* @param mask What bits to update in the PTE (ignored if OPTION_RESET)
|
* @param mask What bits to update in the PTE (ignored if OPTION_RESET)
|
||||||
* @param options Control options, described above
|
* @param options Control options, described above
|
||||||
|
* @retval 0 Success
|
||||||
|
* @retval -ENOMEM allocation required and no free pages (only if OPTION_ALLOC)
|
||||||
*/
|
*/
|
||||||
static void page_map_set(pentry_t *ptables, void *virt, pentry_t entry_val,
|
static int page_map_set(pentry_t *ptables, void *virt, pentry_t entry_val,
|
||||||
pentry_t *old_val_ptr, pentry_t mask, uint32_t options)
|
pentry_t *old_val_ptr, pentry_t mask, uint32_t options)
|
||||||
{
|
{
|
||||||
pentry_t *table = ptables;
|
pentry_t *table = ptables;
|
||||||
bool flush = (options & OPTION_FLUSH) != 0U;
|
bool flush = (options & OPTION_FLUSH) != 0U;
|
||||||
|
|
||||||
|
assert_virt_addr_aligned(virt);
|
||||||
|
|
||||||
for (int level = 0; level < NUM_LEVELS; level++) {
|
for (int level = 0; level < NUM_LEVELS; level++) {
|
||||||
int index;
|
int index;
|
||||||
pentry_t *entryp;
|
pentry_t *entryp;
|
||||||
|
@ -910,20 +966,42 @@ static void page_map_set(pentry_t *ptables, void *virt, pentry_t entry_val,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We fail an assertion here due to no support for
|
/* This is a non-leaf entry */
|
||||||
* splitting existing bigpage mappings.
|
if ((*entryp & MMU_P) == 0U) {
|
||||||
* If the PS bit is not supported at some level (like
|
/* Not present. Never done a mapping here yet, need
|
||||||
* in a PML4 entry) it is always reserved and must be 0
|
* some RAM for linked tables
|
||||||
*/
|
*/
|
||||||
__ASSERT((*entryp & MMU_PS) == 0U, "large page encountered");
|
void *new_table;
|
||||||
table = next_table(*entryp, level);
|
|
||||||
__ASSERT(table != NULL,
|
__ASSERT((options & OPTION_ALLOC) != 0,
|
||||||
"missing page table level %d when trying to map %p",
|
"missing page table and allocations disabled");
|
||||||
level + 1, virt);
|
|
||||||
|
new_table = page_pool_get();
|
||||||
|
|
||||||
|
if (new_table == NULL) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*entryp = ((pentry_t)z_x86_phys_addr(new_table) |
|
||||||
|
INT_FLAGS);
|
||||||
|
table = new_table;
|
||||||
|
} else {
|
||||||
|
/* We fail an assertion here due to no support for
|
||||||
|
* splitting existing bigpage mappings.
|
||||||
|
* If the PS bit is not supported at some level (like
|
||||||
|
* in a PML4 entry) it is always reserved and must be 0
|
||||||
|
*/
|
||||||
|
__ASSERT((*entryp & MMU_PS) == 0U,
|
||||||
|
"large page encountered");
|
||||||
|
table = next_table(*entryp, level);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flush) {
|
if (flush) {
|
||||||
tlb_flush_page(virt);
|
tlb_flush_page(virt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -932,6 +1010,8 @@ static void page_map_set(pentry_t *ptables, void *virt, pentry_t entry_val,
|
||||||
* See documentation for page_map_set() for additional notes about masks and
|
* See documentation for page_map_set() for additional notes about masks and
|
||||||
* supported options.
|
* supported options.
|
||||||
*
|
*
|
||||||
|
* Must call this with x86_mmu_lock held if OPTION_ALLOC is used.
|
||||||
|
*
|
||||||
* It is vital to remember that all virtual-to-physical mappings must be
|
* It is vital to remember that all virtual-to-physical mappings must be
|
||||||
* the same with respect to supervisor mode regardless of what thread is
|
* the same with respect to supervisor mode regardless of what thread is
|
||||||
* scheduled (and therefore, if multiple sets of page tables exist, which one
|
* scheduled (and therefore, if multiple sets of page tables exist, which one
|
||||||
|
@ -951,11 +1031,14 @@ static void page_map_set(pentry_t *ptables, void *virt, pentry_t entry_val,
|
||||||
* @param mask What bits to update in each PTE. Un-set bits will never be
|
* @param mask What bits to update in each PTE. Un-set bits will never be
|
||||||
* modified. Ignored if OPTION_RESET.
|
* modified. Ignored if OPTION_RESET.
|
||||||
* @param options Control options, described above
|
* @param options Control options, described above
|
||||||
|
* @retval 0 Success
|
||||||
|
* @retval -ENOMEM allocation required and no free pages (only if OPTION_ALLOC)
|
||||||
*/
|
*/
|
||||||
static void range_map_ptables(pentry_t *ptables, void *virt, uintptr_t phys,
|
static int range_map_ptables(pentry_t *ptables, void *virt, uintptr_t phys,
|
||||||
size_t size, pentry_t entry_flags, pentry_t mask,
|
size_t size, pentry_t entry_flags, pentry_t mask,
|
||||||
uint32_t options)
|
uint32_t options)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
bool reset = (options & OPTION_RESET) != 0U;
|
bool reset = (options & OPTION_RESET) != 0U;
|
||||||
|
|
||||||
assert_addr_aligned(phys);
|
assert_addr_aligned(phys);
|
||||||
|
@ -979,9 +1062,14 @@ static void range_map_ptables(pentry_t *ptables, void *virt, uintptr_t phys,
|
||||||
entry_val = (phys + offset) | entry_flags;
|
entry_val = (phys + offset) | entry_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
page_map_set(ptables, dest_virt, entry_val, NULL, mask,
|
ret = page_map_set(ptables, dest_virt, entry_val, NULL, mask,
|
||||||
options);
|
options);
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1005,10 +1093,14 @@ static void range_map_ptables(pentry_t *ptables, void *virt, uintptr_t phys,
|
||||||
* be preserved. Ignored if OPTION_RESET.
|
* be preserved. Ignored if OPTION_RESET.
|
||||||
* @param options Control options. Do not set OPTION_USER here. OPTION_FLUSH
|
* @param options Control options. Do not set OPTION_USER here. OPTION_FLUSH
|
||||||
* will trigger a TLB shootdown after all tables are updated.
|
* will trigger a TLB shootdown after all tables are updated.
|
||||||
|
* @retval 0 Success
|
||||||
|
* @retval -ENOMEM page table allocation required, but no free pages
|
||||||
*/
|
*/
|
||||||
static void range_map(void *virt, uintptr_t phys, size_t size,
|
static int range_map(void *virt, uintptr_t phys, size_t size,
|
||||||
pentry_t entry_flags, pentry_t mask, uint32_t options)
|
pentry_t entry_flags, pentry_t mask, uint32_t options)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
LOG_DBG("%s: %p -> %p (%zu) flags " PRI_ENTRY " mask "
|
LOG_DBG("%s: %p -> %p (%zu) flags " PRI_ENTRY " mask "
|
||||||
PRI_ENTRY " opt 0x%x", __func__, (void *)phys, virt, size,
|
PRI_ENTRY " opt 0x%x", __func__, (void *)phys, virt, size,
|
||||||
entry_flags, mask, options);
|
entry_flags, mask, options);
|
||||||
|
@ -1039,29 +1131,47 @@ static void range_map(void *virt, uintptr_t phys, size_t size,
|
||||||
struct arch_mem_domain *domain =
|
struct arch_mem_domain *domain =
|
||||||
CONTAINER_OF(node, struct arch_mem_domain, node);
|
CONTAINER_OF(node, struct arch_mem_domain, node);
|
||||||
|
|
||||||
range_map_ptables(domain->ptables, virt, phys, size,
|
ret = range_map_ptables(domain->ptables, virt, phys, size,
|
||||||
entry_flags, mask, options | OPTION_USER);
|
entry_flags, mask,
|
||||||
|
options | OPTION_USER);
|
||||||
|
if (ret != 0) {
|
||||||
|
/* NOTE: Currently we do not un-map a partially
|
||||||
|
* completed mapping.
|
||||||
|
*/
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_USERSPACE */
|
#endif /* CONFIG_USERSPACE */
|
||||||
range_map_ptables(z_x86_kernel_ptables, virt, phys, size, entry_flags,
|
ret = range_map_ptables(z_x86_kernel_ptables, virt, phys, size,
|
||||||
mask, options);
|
entry_flags, mask, options);
|
||||||
|
#if defined(CONFIG_USERSPACE) && !defined(CONFIG_X86_COMMON_PAGE_TABLE)
|
||||||
|
out_unlock:
|
||||||
|
#endif /* CONFIG_USERSPACE */
|
||||||
|
if (ret == 0 && (options & OPTION_ALLOC) != 0) {
|
||||||
|
LOG_DBG("page pool pages free: %u / %u", pages_free(),
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
if ((options & OPTION_FLUSH) != 0U) {
|
if ((options & OPTION_FLUSH) != 0U) {
|
||||||
tlb_shootdown();
|
tlb_shootdown();
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_SMP */
|
#endif /* CONFIG_SMP */
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void range_map_unlocked(void *virt, uintptr_t phys, size_t size,
|
static inline int range_map_unlocked(void *virt, uintptr_t phys, size_t size,
|
||||||
pentry_t entry_flags, pentry_t mask,
|
pentry_t entry_flags, pentry_t mask,
|
||||||
uint32_t options)
|
uint32_t options)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
k_spinlock_key_t key;
|
k_spinlock_key_t key;
|
||||||
|
|
||||||
key = k_spin_lock(&x86_mmu_lock);
|
key = k_spin_lock(&x86_mmu_lock);
|
||||||
range_map(virt, phys, size, entry_flags, mask, options);
|
ret = range_map(virt, phys, size, entry_flags, mask, options);
|
||||||
k_spin_unlock(&x86_mmu_lock, key);
|
k_spin_unlock(&x86_mmu_lock, key);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pentry_t flags_to_entry(uint32_t flags)
|
static pentry_t flags_to_entry(uint32_t flags)
|
||||||
|
@ -1105,10 +1215,8 @@ static pentry_t flags_to_entry(uint32_t flags)
|
||||||
/* map new region virt..virt+size to phys with provided arch-neutral flags */
|
/* map new region virt..virt+size to phys with provided arch-neutral flags */
|
||||||
int arch_mem_map(void *virt, uintptr_t phys, size_t size, uint32_t flags)
|
int arch_mem_map(void *virt, uintptr_t phys, size_t size, uint32_t flags)
|
||||||
{
|
{
|
||||||
range_map_unlocked(virt, phys, size, flags_to_entry(flags),
|
return range_map_unlocked(virt, phys, size, flags_to_entry(flags),
|
||||||
MASK_ALL, 0);
|
MASK_ALL, OPTION_ALLOC);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CONFIG_X86_STACK_PROTECTION
|
#if CONFIG_X86_STACK_PROTECTION
|
||||||
|
@ -1122,8 +1230,8 @@ void z_x86_set_stack_guard(k_thread_stack_t *stack)
|
||||||
* Guard page is always the first page of the stack object for both
|
* Guard page is always the first page of the stack object for both
|
||||||
* kernel and thread stacks.
|
* kernel and thread stacks.
|
||||||
*/
|
*/
|
||||||
range_map_unlocked(stack, 0, CONFIG_MMU_PAGE_SIZE,
|
(void)range_map_unlocked(stack, 0, CONFIG_MMU_PAGE_SIZE,
|
||||||
MMU_P | ENTRY_XD, MASK_PERM, OPTION_FLUSH);
|
MMU_P | ENTRY_XD, MASK_PERM, OPTION_FLUSH);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_X86_STACK_PROTECTION */
|
#endif /* CONFIG_X86_STACK_PROTECTION */
|
||||||
|
|
||||||
|
@ -1225,14 +1333,14 @@ int arch_buffer_validate(void *addr, size_t size, int write)
|
||||||
|
|
||||||
static inline void reset_region(uintptr_t start, size_t size)
|
static inline void reset_region(uintptr_t start, size_t size)
|
||||||
{
|
{
|
||||||
range_map_unlocked((void *)start, 0, size, 0, 0,
|
(void)range_map_unlocked((void *)start, 0, size, 0, 0,
|
||||||
OPTION_FLUSH | OPTION_RESET);
|
OPTION_FLUSH | OPTION_RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void apply_region(uintptr_t start, size_t size, pentry_t attr)
|
static inline void apply_region(uintptr_t start, size_t size, pentry_t attr)
|
||||||
{
|
{
|
||||||
range_map_unlocked((void *)start, 0, size, attr, MASK_PERM,
|
(void)range_map_unlocked((void *)start, 0, size, attr, MASK_PERM,
|
||||||
OPTION_FLUSH);
|
OPTION_FLUSH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cache of the current memory domain applied to the common page tables and
|
/* Cache of the current memory domain applied to the common page tables and
|
||||||
|
@ -1358,44 +1466,6 @@ void arch_mem_domain_destroy(struct k_mem_domain *domain)
|
||||||
#else
|
#else
|
||||||
/* Memory domains each have a set of page tables assigned to them */
|
/* Memory domains each have a set of page tables assigned to them */
|
||||||
|
|
||||||
/*
|
|
||||||
* Pool of free memory pages for copying page tables, as needed.
|
|
||||||
*/
|
|
||||||
#define PTABLE_COPY_SIZE (NUM_TABLE_PAGES * CONFIG_MMU_PAGE_SIZE)
|
|
||||||
|
|
||||||
static uint8_t __noinit
|
|
||||||
page_pool[PTABLE_COPY_SIZE * CONFIG_X86_MAX_ADDITIONAL_MEM_DOMAINS]
|
|
||||||
__aligned(CONFIG_MMU_PAGE_SIZE);
|
|
||||||
|
|
||||||
static uint8_t *page_pos = page_pool + sizeof(page_pool);
|
|
||||||
|
|
||||||
/* Return a zeroed and suitably aligned memory page for page table data
|
|
||||||
* from the global page pool
|
|
||||||
*/
|
|
||||||
static void *page_pool_get(void)
|
|
||||||
{
|
|
||||||
void *ret;
|
|
||||||
|
|
||||||
if (page_pos == page_pool) {
|
|
||||||
ret = NULL;
|
|
||||||
} else {
|
|
||||||
page_pos -= CONFIG_MMU_PAGE_SIZE;
|
|
||||||
ret = page_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret != NULL) {
|
|
||||||
memset(ret, 0, CONFIG_MMU_PAGE_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Debugging function to show how many pages are free in the pool */
|
|
||||||
static inline unsigned int pages_free(void)
|
|
||||||
{
|
|
||||||
return (page_pos - page_pool) / CONFIG_MMU_PAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duplicate an entire set of page tables
|
* Duplicate an entire set of page tables
|
||||||
*
|
*
|
||||||
|
@ -1566,6 +1636,9 @@ int arch_mem_domain_init(struct k_mem_domain *domain)
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
sys_slist_append(&x86_domain_list, &domain->arch.node);
|
sys_slist_append(&x86_domain_list, &domain->arch.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_DBG("page pool pages free: %u / %u\n", pages_free(),
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES);
|
||||||
k_spin_unlock(&x86_mmu_lock, key);
|
k_spin_unlock(&x86_mmu_lock, key);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -296,17 +296,7 @@ class PtableSet(object):
|
||||||
some kind of leaf page table class (Pt or PtXd)"""
|
some kind of leaf page table class (Pt or PtXd)"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def new_child_table(self, table, virt_addr, depth):
|
def map_page(self, virt_addr, phys_addr, flags):
|
||||||
new_table_addr = self.get_new_mmutable_addr()
|
|
||||||
new_table = self.levels[depth]()
|
|
||||||
debug("new %s at physical addr 0x%x"
|
|
||||||
% (self.levels[depth].__name__, new_table_addr))
|
|
||||||
self.tables[new_table_addr] = new_table
|
|
||||||
table.map(virt_addr, new_table_addr, INT_FLAGS)
|
|
||||||
|
|
||||||
return new_table
|
|
||||||
|
|
||||||
def map_page(self, virt_addr, phys_addr, flags, reserve):
|
|
||||||
"""Map a virtual address to a physical address in the page tables,
|
"""Map a virtual address to a physical address in the page tables,
|
||||||
with provided access flags"""
|
with provided access flags"""
|
||||||
table = self.toplevel
|
table = self.toplevel
|
||||||
|
@ -315,29 +305,18 @@ class PtableSet(object):
|
||||||
for depth in range(1, len(self.levels)):
|
for depth in range(1, len(self.levels)):
|
||||||
# Create child table if needed
|
# Create child table if needed
|
||||||
if not table.has_entry(virt_addr):
|
if not table.has_entry(virt_addr):
|
||||||
table = self.new_child_table(table, virt_addr, depth)
|
new_table_addr = self.get_new_mmutable_addr()
|
||||||
|
new_table = self.levels[depth]()
|
||||||
|
debug("new %s at physical addr 0x%x"
|
||||||
|
% (self.levels[depth].__name__, new_table_addr))
|
||||||
|
self.tables[new_table_addr] = new_table
|
||||||
|
table.map(virt_addr, new_table_addr, INT_FLAGS)
|
||||||
|
table = new_table
|
||||||
else:
|
else:
|
||||||
table = self.tables[table.lookup(virt_addr)]
|
table = self.tables[table.lookup(virt_addr)]
|
||||||
|
|
||||||
# Set up entry in leaf page table
|
# Set up entry in leaf page table
|
||||||
if not reserve:
|
table.map(virt_addr, phys_addr, flags)
|
||||||
table.map(virt_addr, phys_addr, flags)
|
|
||||||
|
|
||||||
def reserve(self, virt_base, size):
|
|
||||||
debug("Reserving paging structures 0x%x (%d)" %
|
|
||||||
(virt_base, size))
|
|
||||||
|
|
||||||
align_check(virt_base, size)
|
|
||||||
|
|
||||||
# How much memory is covered by leaf page table
|
|
||||||
scope = 1 << self.levels[-2].addr_shift
|
|
||||||
|
|
||||||
if virt_base % scope != 0:
|
|
||||||
error("misaligned virtual address space, 0x%x not a multiple of 0x%x" %
|
|
||||||
(virt_base, scope))
|
|
||||||
|
|
||||||
for addr in range(virt_base, virt_base + size, scope):
|
|
||||||
self.map_page(addr, 0, 0, True)
|
|
||||||
|
|
||||||
def map(self, phys_base, size, flags):
|
def map(self, phys_base, size, flags):
|
||||||
"""Identity map an address range in the page tables, with provided
|
"""Identity map an address range in the page tables, with provided
|
||||||
|
@ -352,7 +331,7 @@ class PtableSet(object):
|
||||||
# Never map the NULL page
|
# Never map the NULL page
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.map_page(addr, addr, flags, False)
|
self.map_page(addr, addr, flags)
|
||||||
|
|
||||||
def set_region_perms(self, name, flags):
|
def set_region_perms(self, name, flags):
|
||||||
"""Set access permissions for a named region that is already mapped
|
"""Set access permissions for a named region that is already mapped
|
||||||
|
@ -456,18 +435,12 @@ def main():
|
||||||
|
|
||||||
debug("building %s" % pclass.__name__)
|
debug("building %s" % pclass.__name__)
|
||||||
|
|
||||||
vm_base = syms["CONFIG_KERNEL_VM_BASE"]
|
# Identity-mapped Zephyr image in RAM
|
||||||
vm_size = syms["CONFIG_KERNEL_VM_SIZE"]
|
|
||||||
|
|
||||||
image_base = syms["z_mapped_start"]
|
image_base = syms["z_mapped_start"]
|
||||||
image_size = syms["z_mapped_size"]
|
image_size = syms["z_mapped_size"]
|
||||||
ptables_phys = syms["z_x86_pagetables_start"]
|
ptables_phys = syms["z_x86_pagetables_start"]
|
||||||
|
|
||||||
debug("Address space: 0x%x - 0x%x size %x" %
|
debug("Base addresses: physical 0x%x size %d" % (image_base, image_size))
|
||||||
(vm_base, vm_base + vm_size, vm_size))
|
|
||||||
|
|
||||||
debug("Zephyr image: 0x%x - 0x%x size %x" %
|
|
||||||
(image_base, image_base + image_size, image_size))
|
|
||||||
|
|
||||||
is_perm_regions = isdef("CONFIG_SRAM_REGION_PERMISSIONS")
|
is_perm_regions = isdef("CONFIG_SRAM_REGION_PERMISSIONS")
|
||||||
|
|
||||||
|
@ -479,9 +452,6 @@ def main():
|
||||||
map_flags = FLAG_P
|
map_flags = FLAG_P
|
||||||
|
|
||||||
pt = pclass(ptables_phys)
|
pt = pclass(ptables_phys)
|
||||||
# Instantiate all the paging structures for the address space
|
|
||||||
pt.reserve(vm_base, vm_size)
|
|
||||||
# Map the zephyr image
|
|
||||||
pt.map(image_base, image_size, map_flags | ENTRY_RW)
|
pt.map(image_base, image_size, map_flags | ENTRY_RW)
|
||||||
|
|
||||||
if isdef("CONFIG_X86_64"):
|
if isdef("CONFIG_X86_64"):
|
||||||
|
|
|
@ -13,3 +13,4 @@ CONFIG_UART_CONSOLE=y
|
||||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
||||||
CONFIG_BUILD_OUTPUT_BIN=y
|
CONFIG_BUILD_OUTPUT_BIN=y
|
||||||
CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n
|
CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=29
|
||||||
|
|
|
@ -13,3 +13,4 @@ CONFIG_UART_CONSOLE=y
|
||||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=1900000000
|
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=1900000000
|
||||||
CONFIG_BUILD_OUTPUT_BIN=y
|
CONFIG_BUILD_OUTPUT_BIN=y
|
||||||
CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n
|
CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=29
|
||||||
|
|
|
@ -12,6 +12,9 @@ config BUILD_OUTPUT_STRIPPED
|
||||||
config MP_NUM_CPUS
|
config MP_NUM_CPUS
|
||||||
default 2
|
default 2
|
||||||
|
|
||||||
|
config X86_MMU_PAGE_POOL_PAGES
|
||||||
|
default 3072 if X86_MMU
|
||||||
|
|
||||||
endif # BOARD_EHL_CRB
|
endif # BOARD_EHL_CRB
|
||||||
|
|
||||||
if BOARD_EHL_CRB_SBL
|
if BOARD_EHL_CRB_SBL
|
||||||
|
@ -25,6 +28,9 @@ config BUILD_OUTPUT_STRIPPED
|
||||||
config MP_NUM_CPUS
|
config MP_NUM_CPUS
|
||||||
default 2
|
default 2
|
||||||
|
|
||||||
|
config X86_MMU_PAGE_POOL_PAGES
|
||||||
|
default 3072 if X86_MMU
|
||||||
|
|
||||||
config SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN
|
config SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN
|
||||||
depends on SHELL_BACKEND_SERIAL
|
depends on SHELL_BACKEND_SERIAL
|
||||||
default n
|
default n
|
||||||
|
|
|
@ -10,3 +10,4 @@ CONFIG_UART_NS16550=y
|
||||||
CONFIG_UART_CONSOLE=y
|
CONFIG_UART_CONSOLE=y
|
||||||
CONFIG_X2APIC=y
|
CONFIG_X2APIC=y
|
||||||
CONFIG_SMP=y
|
CONFIG_SMP=y
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=3092
|
||||||
|
|
|
@ -10,3 +10,4 @@ CONFIG_UART_NS16550=y
|
||||||
CONFIG_UART_CONSOLE=y
|
CONFIG_UART_CONSOLE=y
|
||||||
CONFIG_X2APIC=y
|
CONFIG_X2APIC=y
|
||||||
CONFIG_SMP=y
|
CONFIG_SMP=y
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=3092
|
||||||
|
|
|
@ -8,4 +8,7 @@ config BOARD
|
||||||
config BUILD_OUTPUT_STRIPPED
|
config BUILD_OUTPUT_STRIPPED
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
config X86_MMU_PAGE_POOL_PAGES
|
||||||
|
default 3086 if X86_MMU
|
||||||
|
|
||||||
endif # BOARD_MINNOWBOARD
|
endif # BOARD_MINNOWBOARD
|
||||||
|
|
|
@ -17,3 +17,4 @@ CONFIG_MP_NUM_CPUS=2
|
||||||
CONFIG_X86_MMU=y
|
CONFIG_X86_MMU=y
|
||||||
CONFIG_X86_VERY_EARLY_CONSOLE=y
|
CONFIG_X86_VERY_EARLY_CONSOLE=y
|
||||||
CONFIG_QEMU_ICOUNT=n
|
CONFIG_QEMU_ICOUNT=n
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=23
|
||||||
|
|
|
@ -18,3 +18,4 @@ CONFIG_X86_MMU=y
|
||||||
CONFIG_X86_VERY_EARLY_CONSOLE=y
|
CONFIG_X86_VERY_EARLY_CONSOLE=y
|
||||||
CONFIG_QEMU_ICOUNT=n
|
CONFIG_QEMU_ICOUNT=n
|
||||||
CONFIG_X86_KPTI=n
|
CONFIG_X86_KPTI=n
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=16
|
||||||
|
|
|
@ -12,6 +12,7 @@ CONFIG_UART_CONSOLE=y
|
||||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
CONFIG_TEST_RANDOM_GENERATOR=y
|
||||||
CONFIG_X86_MMU=y
|
CONFIG_X86_MMU=y
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=17
|
||||||
CONFIG_DEBUG_INFO=y
|
CONFIG_DEBUG_INFO=y
|
||||||
CONFIG_SCHED_SCALABLE=y
|
CONFIG_SCHED_SCALABLE=y
|
||||||
CONFIG_WAITQ_SCALABLE=y
|
CONFIG_WAITQ_SCALABLE=y
|
||||||
|
|
|
@ -12,6 +12,7 @@ CONFIG_UART_CONSOLE=y
|
||||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
CONFIG_TEST_RANDOM_GENERATOR=y
|
||||||
CONFIG_X86_MMU=y
|
CONFIG_X86_MMU=y
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=17
|
||||||
CONFIG_DEBUG_INFO=y
|
CONFIG_DEBUG_INFO=y
|
||||||
CONFIG_SCHED_SCALABLE=y
|
CONFIG_SCHED_SCALABLE=y
|
||||||
CONFIG_WAITQ_SCALABLE=y
|
CONFIG_WAITQ_SCALABLE=y
|
||||||
|
|
|
@ -12,6 +12,7 @@ CONFIG_UART_CONSOLE=y
|
||||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
CONFIG_TEST_RANDOM_GENERATOR=y
|
||||||
CONFIG_X86_MMU=y
|
CONFIG_X86_MMU=y
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=12
|
||||||
CONFIG_DEBUG_INFO=y
|
CONFIG_DEBUG_INFO=y
|
||||||
CONFIG_SCHED_SCALABLE=y
|
CONFIG_SCHED_SCALABLE=y
|
||||||
CONFIG_WAITQ_SCALABLE=y
|
CONFIG_WAITQ_SCALABLE=y
|
||||||
|
|
|
@ -12,6 +12,7 @@ CONFIG_UART_CONSOLE=y
|
||||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
||||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
CONFIG_TEST_RANDOM_GENERATOR=y
|
||||||
CONFIG_X86_MMU=y
|
CONFIG_X86_MMU=y
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=10
|
||||||
CONFIG_DEBUG_INFO=y
|
CONFIG_DEBUG_INFO=y
|
||||||
CONFIG_SCHED_SCALABLE=y
|
CONFIG_SCHED_SCALABLE=y
|
||||||
CONFIG_WAITQ_SCALABLE=y
|
CONFIG_WAITQ_SCALABLE=y
|
||||||
|
|
|
@ -5,5 +5,5 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define DT_DRAM_BASE 0x100000
|
#define DT_DRAM_BASE 0x100000
|
||||||
#define DT_DRAM_SIZE DT_SIZE_K(256)
|
#define DT_DRAM_SIZE DT_SIZE_K(128)
|
||||||
#include "qemu_x86.dts"
|
#include "qemu_x86.dts"
|
||||||
|
|
|
@ -23,5 +23,6 @@ CONFIG_X86_COMMON_PAGE_TABLE=y
|
||||||
CONFIG_X86_KPTI=n
|
CONFIG_X86_KPTI=n
|
||||||
CONFIG_KERNEL_VM_SIZE=0x400000
|
CONFIG_KERNEL_VM_SIZE=0x400000
|
||||||
CONFIG_KERNEL_VM_BASE=0x0
|
CONFIG_KERNEL_VM_BASE=0x0
|
||||||
|
CONFIG_X86_MMU_PAGE_POOL_PAGES=0
|
||||||
CONFIG_KERNEL_VM_OFFSET=0x100000
|
CONFIG_KERNEL_VM_OFFSET=0x100000
|
||||||
CONFIG_X86_KERNEL_OFFSET=0
|
CONFIG_X86_KERNEL_OFFSET=0
|
|
@ -11,6 +11,9 @@ config BUILD_OUTPUT_STRIPPED
|
||||||
config MP_NUM_CPUS
|
config MP_NUM_CPUS
|
||||||
default 2
|
default 2
|
||||||
|
|
||||||
|
config X86_MMU_PAGE_POOL_PAGES
|
||||||
|
default 3092 if X86_MMU
|
||||||
|
|
||||||
endif # BOARD_UP_SQUARED
|
endif # BOARD_UP_SQUARED
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,4 +25,7 @@ config BOARD
|
||||||
config BUILD_OUTPUT_STRIPPED
|
config BUILD_OUTPUT_STRIPPED
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
config X86_MMU_PAGE_POOL_PAGES
|
||||||
|
default 3086 if X86_MMU
|
||||||
|
|
||||||
endif # BOARD_UP_SQUARED_32
|
endif # BOARD_UP_SQUARED_32
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue