riscv: Map devices with large pages on 32 & 64-bit

For 64-bit, this adds a 2nd-level page table for mapping devices using
2MiB frames instead of 1GiB frames.

The boot mapping and hardware header generator have also been fixed to
correctly report the number of large frames needed for devices rather
than only reporting the first. The frame size is also specified
correctly (rather than assuming mapping with 4KiB frames).

This likely fixes an issue whereby only the first 4KiB frame of a device
was reserved but the remaining region of that kernel device could be
mapped at user level.

Signed-off-by: Curtis Millar <curtis.millar@data61.csiro.au>
This commit is contained in:
Curtis Millar
2020-11-19 17:16:41 +11:00
committed by Curtis Millar
parent 8e57add854
commit ab3d8c44cb
8 changed files with 38 additions and 5 deletions

View File

@@ -31,5 +31,6 @@ extern pte_t kernel_root_pageTable[BIT(PT_INDEX_BITS)] VISIBLE;
* page entry to avoid PMP exception. */
#if __riscv_xlen != 32
extern pte_t kernel_image_level2_pt[BIT(PT_INDEX_BITS)];
extern pte_t kernel_image_level2_dev_pt[BIT(PT_INDEX_BITS)];
#endif

View File

@@ -16,7 +16,7 @@
* https://static.dev.sifive.com/U54-MC-RVCoreIP.pdf
*/
#define PLIC_PPTR_BASE (PLIC_PPTR + 0x0C000000)
#define PLIC_PPTR_BASE PLIC_PPTR
#define PLIC_HART_ID (CONFIG_FIRST_HART_ID)

View File

@@ -85,9 +85,22 @@ static pte_t pte_next(word_t phys_addr, bool_t is_leaf)
BOOT_CODE void map_kernel_frame(paddr_t paddr, pptr_t vaddr, vm_rights_t vm_rights)
{
#if __riscv_xlen == 32
paddr = ROUND_DOWN(paddr, RISCV_GET_LVL_PGSIZE_BITS(0));
assert((paddr % RISCV_GET_LVL_PGSIZE(0)) == 0);
kernel_root_pageTable[RISCV_GET_PT_INDEX(vaddr, 0)] = pte_next(paddr, true);
#else
if (vaddr >= KDEV_BASE) {
/* Map devices in 2nd-level page table */
paddr = ROUND_DOWN(paddr, RISCV_GET_LVL_PGSIZE_BITS(1));
assert((paddr % RISCV_GET_LVL_PGSIZE(1)) == 0);
kernel_image_level2_dev_pt[RISCV_GET_PT_INDEX(vaddr, 1)] = pte_next(paddr, true);
} else {
paddr = ROUND_DOWN(paddr, RISCV_GET_LVL_PGSIZE_BITS(0));
assert((paddr % RISCV_GET_LVL_PGSIZE(0)) == 0);
kernel_root_pageTable[RISCV_GET_PT_INDEX(vaddr, 0)] = pte_next(paddr, true);
}
#endif
}
BOOT_CODE VISIBLE void map_kernel_window(void)
@@ -133,6 +146,10 @@ BOOT_CODE VISIBLE void map_kernel_window(void)
pptr += RISCV_GET_LVL_PGSIZE(1);
paddr += RISCV_GET_LVL_PGSIZE(1);
}
/* Map kernel device page table */
kernel_root_pageTable[RISCV_GET_PT_INDEX(KDEV_BASE, 0)] =
pte_next(kpptr_to_paddr(kernel_image_level2_dev_pt), false);
#endif
/* There should be 1GiB free where we put device mapping */

View File

@@ -55,12 +55,12 @@ BOOT_CODE void map_kernel_devices(void)
}
for (int i = 0; i < (sizeof(kernel_devices) / sizeof(kernel_frame_t)); i++) {
map_kernel_frame(kernel_devices[i].paddr, KDEV_BASE,
map_kernel_frame(kernel_devices[i].paddr, kernel_devices[i].pptr,
VMKernelOnly);
if (!kernel_devices[i].userAvailable) {
p_region_t reg = {
.start = kernel_devices[i].paddr,
.end = kernel_devices[i].paddr + (1 << PAGE_BITS),
.end = kernel_devices[i].paddr + (1 << seL4_LargePageBits),
};
reserve_region(reg);
}

View File

@@ -22,6 +22,7 @@ pte_t kernel_root_pageTable[BIT(PT_INDEX_BITS)] ALIGN_BSS(BIT(seL4_PageTableBits
#if __riscv_xlen != 32
pte_t kernel_image_level2_pt[BIT(PT_INDEX_BITS)] ALIGN_BSS(BIT(seL4_PageTableBits));
pte_t kernel_image_level2_dev_pt[BIT(PT_INDEX_BITS)] ALIGN_BSS(BIT(seL4_PageTableBits));
#endif
SMP_STATE_DEFINE(core_map_t, coreMap);

View File

@@ -245,6 +245,7 @@ devices:
regions:
- index: 0
kernel: PLIC_PPTR
kernel_size: 0x04000000
# elfloader rules
- compatible:

View File

@@ -25,6 +25,10 @@ class Config:
''' Get page size in bits for this arch '''
return 12 # 4096-byte pages
def get_device_page_bits(self) -> int:
''' Get page size in bits for mapping devices for this arch '''
return self.get_page_bits()
class ARMConfig(Config):
''' Config class for ARM '''
@@ -46,6 +50,15 @@ class RISCVConfig(Config):
of physical memory. Mark it as unavailable. '''
return self.MEGA_PAGE_SIZE
def get_device_page_bits(self) -> int:
''' Get page size in bits for mapping devices for this arch '''
if self.addrspace_max > (1 << 32):
# rv39 and rv48 use 2MiB device pages
return 21
else:
# rv32 uses 4MiB device pages
return 22
def get_arch_config(arch: str, addrspace_max: int) -> Config:
''' Return an appropriate Config object for the given architecture '''

View File

@@ -168,14 +168,14 @@ class DeviceRule:
kernel_name = rule['kernel']
user = rule.get('user', False)
macro = rule.get('macro', None)
max_size = 1 << self.config.get_page_bits()
max_size = 1 << self.config.get_device_page_bits()
if 'kernel_size' in rule:
max_size = rule['kernel_size']
elif max_size < reg.size:
logging.warning(
"Only mapping {}/{} bytes from node {}, region {}. Set kernel_size in YAML to silence.".format(max_size, reg.size, node.path, i))
ret.append(KernelRegionGroup(reg, kernel_name,
self.config.get_page_bits(), max_size, macro, user))
self.config.get_device_page_bits(), max_size, macro, user))
return ret