[arch][riscv] Initial implementation of MMU for RISC-V
Implements both SV39 and SV48. No 32bit support yet. Currently implements basic setup of paging in start.S by mapping a large chunk of memory into both an identity map and to the bottom of the kernel address space. Run the kernel out of this physical mapping. Added basic arch mmu support for querying existing paging structures and mapping 4K pages. No unmap support as of yet. System boots with mmu on when running supervisor test on qemu. Untested on real hardware as of yet.
This commit is contained in:
@@ -67,12 +67,17 @@ void arch_init(void) {
|
||||
riscv_init_percpu();
|
||||
|
||||
// print some arch info
|
||||
#if RISCV_M_MODE
|
||||
dprintf(INFO, "RISCV: Machine mode\n");
|
||||
dprintf(INFO, "RISCV: mvendorid %#lx marchid %#lx mimpid %#lx mhartid %#x\n",
|
||||
riscv_get_mvendorid(), riscv_get_marchid(),
|
||||
riscv_get_mimpid(), riscv_current_hart());
|
||||
#if RISCV_M_MODE
|
||||
dprintf(INFO, "RISCV: misa %#lx\n", riscv_csr_read(RISCV_CSR_MISA));
|
||||
#else
|
||||
dprintf(INFO, "RISCV: Supervisor mode\n");
|
||||
#if RISCV_MMU
|
||||
dprintf(INFO, "RISCV: MMU enabled sv%u\n", RISCV_MMU);
|
||||
#endif
|
||||
dprintf(INFO, "RISCV: SBI impl id %#lx version %#lx\n", sbi_call(SBI_GET_SBI_IMPL_ID).value, sbi_call(SBI_GET_SBI_IMPL_VERSION).value);
|
||||
|
||||
// probe some SBI extensions
|
||||
|
||||
29
arch/riscv/include/arch/aspace.h
Normal file
29
arch/riscv/include/arch/aspace.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <lk/compiler.h>
|
||||
#include <arch/riscv/mmu.h>
|
||||
|
||||
__BEGIN_CDECLS
|
||||
|
||||
struct arch_aspace {
|
||||
/* pointer to the translation table */
|
||||
paddr_t pt_phys;
|
||||
volatile riscv_pte_t *pt_virt;
|
||||
|
||||
uint flags;
|
||||
|
||||
/* range of address space */
|
||||
vaddr_t base;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
__END_CDECLS
|
||||
|
||||
|
||||
86
arch/riscv/include/arch/riscv/mmu.h
Normal file
86
arch/riscv/include/arch/riscv/mmu.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if RISCV_MMU
|
||||
|
||||
#include <stdint.h>
|
||||
#include <arch/defines.h>
|
||||
|
||||
// RISC-V specific mmu #defines and structures here
|
||||
#define KB (1024UL)
|
||||
#define MB (1024UL*1024UL)
|
||||
#define GB (1024UL*1024UL*1024UL)
|
||||
|
||||
typedef uintptr_t riscv_pte_t;
|
||||
|
||||
// some constants based on our particular implementation
|
||||
#if RISCV_MMU == 48
|
||||
#define RISCV_MMU_PT_LEVELS 4
|
||||
#define RISCV_MMU_PT_SHIFT 9
|
||||
#define RISCV_MMU_PT_ENTRIES 512 // 1 << PT_SHIFT
|
||||
#define RISCV_MMU_CANONICAL_MASK ((1UL << 48) - 1)
|
||||
#define RISCV_MMU_PPN_BITS 56
|
||||
#elif RISCV_MMU == 39
|
||||
#define RISCV_MMU_PT_LEVELS 3
|
||||
#define RISCV_MMU_PT_SHIFT 9
|
||||
#define RISCV_MMU_PT_ENTRIES 512 // 1 << PT_SHIFT
|
||||
#define RISCV_MMU_CANONICAL_MASK ((1UL << 39) - 1)
|
||||
#define RISCV_MMU_PPN_BITS 56
|
||||
#elif RISCV_MMU == 32
|
||||
#define RISCV_MMU_PT_LEVELS 2
|
||||
#define RISCV_MMU_PT_SHIFT 10
|
||||
#define RISCV_MMU_PT_ENTRIES 1024 // 1 << PT_SHIFT
|
||||
#define RISCV_MMU_CANONICAL_MASK UINT32_MASK
|
||||
#define RISCV_MMU_PPN_BITS 32
|
||||
#else
|
||||
#error implement
|
||||
#endif
|
||||
|
||||
// page table bits
|
||||
#define RISCV_PTE_V (1 << 0) // valid
|
||||
#define RISCV_PTE_R (1 << 1) // read
|
||||
#define RISCV_PTE_W (1 << 2) // write
|
||||
#define RISCV_PTE_X (1 << 3) // execute
|
||||
#define RISCV_PTE_PERM_MASK (0x7 << 1)
|
||||
#define RISCV_PTE_U (1 << 4) // user
|
||||
#define RISCV_PTE_G (1 << 5) // global
|
||||
#define RISCV_PTE_A (1 << 6) // accessed
|
||||
#define RISCV_PTE_D (1 << 7) // dirty
|
||||
#define RISCV_PTE_RSW_MASK (3 << 8) // reserved for software
|
||||
#define RISCV_PTE_PPN_SHIFT (10)
|
||||
#define RISCV_PTE_PPN_MASK (((1UL << (RISCV_MMU_PPN_BITS - PAGE_SIZE_SHIFT)) - 1) << RISCV_PTE_PPN_SHIFT)
|
||||
|
||||
// riscv PPN is stored shifed over 2 from the natural alignment
|
||||
#define RISCV_PTE_PPN(pte) (((pte) & RISCV_PTE_PPN_MASK) << (PAGE_SIZE_SHIFT - RISCV_PTE_PPN_SHIFT))
|
||||
#define RISCV_PTE_PPN_TO_PTE(paddr) (((paddr) >> PAGE_SIZE_SHIFT) << RISCV_PTE_PPN_SHIFT)
|
||||
|
||||
// SATP register, contains the current mmu mode, address space id, and
|
||||
// pointer to root page table
|
||||
#define RISCV_SATP_MODE_NONE (0)
|
||||
#define RISCV_SATP_MODE_SV32 (1)
|
||||
#define RISCV_SATP_MODE_SV39 (8)
|
||||
#define RISCV_SATP_MODE_SV48 (9)
|
||||
#define RISCV_SATP_MODE_SV57 (10)
|
||||
#define RISCV_SATP_MODE_SV64 (11)
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
#define RISCV_SATP_MODE_SHIFT (31)
|
||||
#define RISCV_SATP_ASID_SHIFT (22)
|
||||
#define RISCV_SATP_ASID_SIZE (9)
|
||||
#define RISCV_SATP_ASID_MASK ((1 << RISCV_SATP_ASID_SIZE) - 1)
|
||||
|
||||
#elif __riscv_xlen == 64
|
||||
#define RISCV_SATP_MODE_SHIFT (60)
|
||||
#define RISCV_SATP_ASID_SHIFT (44)
|
||||
#define RISCV_SATP_ASID_SIZE (16)
|
||||
#define RISCV_SATP_ASID_MASK ((1UL << RISCV_SATP_ASID_SIZE) - 1)
|
||||
#endif
|
||||
|
||||
#endif // RISCV_MMU
|
||||
|
||||
341
arch/riscv/mmu.c
Normal file
341
arch/riscv/mmu.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#if RISCV_MMU
|
||||
|
||||
#include "arch/riscv/mmu.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <lk/debug.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/trace.h>
|
||||
#include <arch/ops.h>
|
||||
#include <arch/mmu.h>
|
||||
#include <arch/riscv.h>
|
||||
#include <arch/riscv/csr.h>
|
||||
#include <kernel/vm.h>
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
#include <kernel/vm.h>
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
#error "32 bit mmu not supported yet"
|
||||
#endif
|
||||
|
||||
riscv_pte_t kernel_pgtable[512] __ALIGNED(PAGE_SIZE);
|
||||
paddr_t kernel_pgtable_phys; // filled in by start.S
|
||||
|
||||
// initial memory mappings. VM uses to construct mappings after the fact
|
||||
struct mmu_initial_mapping mmu_initial_mappings[] = {
|
||||
// all of memory, mapped in start.S
|
||||
{
|
||||
.phys = 0,
|
||||
.virt = KERNEL_ASPACE_BASE,
|
||||
#if RISCV_MMU == 48
|
||||
.size = 512UL * GB,
|
||||
#elif RISCV_MMU == 39
|
||||
.size = 64UL * GB,
|
||||
#else
|
||||
#error implement
|
||||
#endif
|
||||
.flags = 0,
|
||||
.name = "memory"
|
||||
},
|
||||
|
||||
// null entry to terminate the list
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static inline void riscv_set_satp(uint asid, paddr_t pt) {
|
||||
ulong satp;
|
||||
|
||||
#if RISCV_MMU == 48
|
||||
satp = RISCV_SATP_MODE_SV48;
|
||||
#elif RISCV_MMU == 39
|
||||
satp = RISCV_SATP_MODE_SV39;
|
||||
#endif
|
||||
|
||||
// make sure the asid is in range
|
||||
DEBUG_ASSERT((asid & RISCV_SATP_ASID_MASK) == 0);
|
||||
satp |= (ulong)asid << RISCV_SATP_ASID_SHIFT;
|
||||
|
||||
// make sure the page table is aligned
|
||||
DEBUG_ASSERT(IS_PAGE_ALIGNED(pt));
|
||||
satp |= pt;
|
||||
|
||||
riscv_csr_write(RISCV_CSR_SATP, satp);
|
||||
|
||||
// TODO: TLB flush here or use asid properly
|
||||
// sfence.vma zero, zero
|
||||
}
|
||||
|
||||
// given a va address and the level, compute the index in the current PT
|
||||
static inline uint vaddr_to_index(vaddr_t va, uint level) {
|
||||
// levels count down from PT_LEVELS - 1
|
||||
DEBUG_ASSERT(level < RISCV_MMU_PT_LEVELS);
|
||||
|
||||
// canonicalize the address
|
||||
va &= RISCV_MMU_CANONICAL_MASK;
|
||||
|
||||
uint index = ((va >> PAGE_SIZE_SHIFT) >> (level * RISCV_MMU_PT_SHIFT)) & (RISCV_MMU_PT_ENTRIES - 1);
|
||||
LTRACEF_LEVEL(3, "canonical va %#lx, level %u = index %#x\n", va, level, index);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static uintptr_t page_size_per_level(uint level) {
|
||||
// levels count down from PT_LEVELS - 1
|
||||
DEBUG_ASSERT(level < RISCV_MMU_PT_LEVELS);
|
||||
|
||||
return 1UL << (PAGE_SIZE_SHIFT + level * RISCV_MMU_PT_SHIFT);
|
||||
}
|
||||
|
||||
static uintptr_t page_mask_per_level(uint level) {
|
||||
return page_size_per_level(level) - 1;
|
||||
}
|
||||
|
||||
static volatile riscv_pte_t *alloc_ptable(paddr_t *pa) {
|
||||
// grab a page from the pmm
|
||||
vm_page_t *p = pmm_alloc_page();
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get the physical and virtual mappings of the page
|
||||
*pa = vm_page_to_paddr(p);
|
||||
riscv_pte_t *pte = paddr_to_kvaddr(*pa);
|
||||
|
||||
// zero it out
|
||||
memset(pte, 0, PAGE_SIZE);
|
||||
|
||||
smp_wmb();
|
||||
|
||||
LTRACEF_LEVEL(3, "returning pa %#lx, va %p\n", *pa, pte);
|
||||
return pte;
|
||||
}
|
||||
|
||||
static riscv_pte_t mmu_flags_to_pte(uint flags) {
|
||||
riscv_pte_t pte = 0;
|
||||
|
||||
pte |= (flags & ARCH_MMU_FLAG_PERM_USER) ? RISCV_PTE_U : 0;
|
||||
pte |= (flags & ARCH_MMU_FLAG_PERM_RO) ? RISCV_PTE_R : (RISCV_PTE_R | RISCV_PTE_W);
|
||||
pte |= (flags & ARCH_MMU_FLAG_PERM_NO_EXECUTE) ? 0 : RISCV_PTE_X;
|
||||
|
||||
return pte;
|
||||
}
|
||||
|
||||
static uint pte_flags_to_mmu_flags(riscv_pte_t pte) {
|
||||
uint f = 0;
|
||||
if ((pte & (RISCV_PTE_R | RISCV_PTE_W)) == RISCV_PTE_R) {
|
||||
f |= ARCH_MMU_FLAG_PERM_RO;
|
||||
}
|
||||
f |= (pte & RISCV_PTE_X) ? 0 : ARCH_MMU_FLAG_PERM_NO_EXECUTE;
|
||||
f |= (pte & RISCV_PTE_U) ? ARCH_MMU_FLAG_PERM_USER : 0;
|
||||
return f;
|
||||
}
|
||||
|
||||
// public api
|
||||
|
||||
// initialize per address space
|
||||
status_t arch_mmu_init_aspace(arch_aspace_t *aspace, vaddr_t base, size_t size, uint flags) {
|
||||
LTRACEF("aspace %p, base %#lx, size %#zx, flags %#x\n", aspace, base, size, flags);
|
||||
|
||||
DEBUG_ASSERT(aspace);
|
||||
|
||||
// validate that the base + size is sane and doesn't wrap
|
||||
DEBUG_ASSERT(size > PAGE_SIZE);
|
||||
DEBUG_ASSERT(base + size - 1 > base);
|
||||
|
||||
aspace->flags = flags;
|
||||
if (flags & ARCH_ASPACE_FLAG_KERNEL) {
|
||||
// at the moment we can only deal with address spaces as globally defined
|
||||
DEBUG_ASSERT(base == KERNEL_ASPACE_BASE);
|
||||
DEBUG_ASSERT(size == KERNEL_ASPACE_SIZE);
|
||||
|
||||
aspace->base = base;
|
||||
aspace->size = size;
|
||||
aspace->pt_virt = kernel_pgtable;
|
||||
aspace->pt_phys = kernel_pgtable_phys;
|
||||
} else {
|
||||
PANIC_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
LTRACEF("pt phys %#lx, pt virt %p\n", aspace->pt_phys, aspace->pt_virt);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
status_t arch_mmu_destroy_aspace(arch_aspace_t *aspace) {
|
||||
LTRACEF("aspace %p\n", aspace);
|
||||
|
||||
PANIC_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
// routines to map/unmap/query mappings per address space
|
||||
int arch_mmu_map(arch_aspace_t *aspace, vaddr_t vaddr, paddr_t paddr, uint count, const uint flags) {
|
||||
LTRACEF("vaddr %#lx paddr %#lx count %u flags %#x\n", vaddr, paddr, count, flags);
|
||||
|
||||
DEBUG_ASSERT(aspace);
|
||||
|
||||
restart:
|
||||
if (count == 0)
|
||||
return NO_ERROR;
|
||||
|
||||
// bootstrap the top level walk
|
||||
uint level = RISCV_MMU_PT_LEVELS - 1;
|
||||
uint index = vaddr_to_index(vaddr, level);
|
||||
volatile riscv_pte_t *ptep = aspace->pt_virt + index;
|
||||
|
||||
for (;;) {
|
||||
LTRACEF_LEVEL(2, "level %u, index %u, pte %p (%#lx) va %#lx pa %#lx\n",
|
||||
level, index, ptep, *ptep, vaddr, paddr);
|
||||
|
||||
// look at our page table entry
|
||||
riscv_pte_t pte = *ptep;
|
||||
if (level > 0 && !(pte & RISCV_PTE_V)) {
|
||||
// invalid entry, will have to add a page table
|
||||
paddr_t ptp;
|
||||
volatile riscv_pte_t *ptv = alloc_ptable(&ptp);
|
||||
if (!ptv) {
|
||||
return ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
LTRACEF_LEVEL(2, "new ptable table %p, pa %#lx\n", ptv, ptp);
|
||||
|
||||
// link it in. RMW == 0 is a page table link
|
||||
pte = RISCV_PTE_PPN_TO_PTE(ptp) | RISCV_PTE_V;
|
||||
*ptep = pte;
|
||||
|
||||
// go one level deeper
|
||||
level--;
|
||||
index = vaddr_to_index(vaddr, level);
|
||||
ptep = ptv + index;
|
||||
} else if ((pte & RISCV_PTE_V) && !(pte & RISCV_PTE_PERM_MASK)) {
|
||||
// next level page table pointer (RWX = 0)
|
||||
paddr_t ptp = RISCV_PTE_PPN(pte);
|
||||
volatile riscv_pte_t *ptv = paddr_to_kvaddr(ptp);
|
||||
|
||||
LTRACEF_LEVEL(2, "next level page table at %p, pa %#lx\n", ptv, ptp);
|
||||
|
||||
// go one level deeper
|
||||
level--;
|
||||
index = vaddr_to_index(vaddr, level);
|
||||
ptep = ptv + index;
|
||||
} else if (pte & RISCV_PTE_V) {
|
||||
// terminal entry already exists
|
||||
if (level > 0) {
|
||||
PANIC_UNIMPLEMENTED_MSG("terminal large page entry");
|
||||
} else {
|
||||
PANIC_UNIMPLEMENTED_MSG("terminal page entry");
|
||||
}
|
||||
} else {
|
||||
DEBUG_ASSERT(level == 0 && !(pte & RISCV_PTE_V));
|
||||
|
||||
// hit a open terminal page table entry, lets add ours
|
||||
pte = RISCV_PTE_PPN_TO_PTE(paddr);
|
||||
pte |= mmu_flags_to_pte(flags);
|
||||
pte |= RISCV_PTE_A | RISCV_PTE_D | RISCV_PTE_V;
|
||||
pte |= (aspace->flags & ARCH_ASPACE_FLAG_KERNEL) ? RISCV_PTE_G : 0;
|
||||
|
||||
LTRACEF_LEVEL(2, "added new terminal entry: pte %#lx\n", pte);
|
||||
|
||||
*ptep = pte;
|
||||
|
||||
// simple algorithm: restart walk from top, one page at a time
|
||||
// TODO: more efficiently deal with runs and large pages
|
||||
count--;
|
||||
paddr += PAGE_SIZE;
|
||||
vaddr += PAGE_SIZE;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
// make sure we didn't decrement level one too many
|
||||
DEBUG_ASSERT(level < RISCV_MMU_PT_LEVELS);
|
||||
}
|
||||
// unreachable
|
||||
}
|
||||
|
||||
int arch_mmu_unmap(arch_aspace_t *aspace, vaddr_t vaddr, uint count) {
|
||||
LTRACEF("vaddr %#lx count %u\n", vaddr, count);
|
||||
|
||||
PANIC_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
status_t arch_mmu_query(arch_aspace_t *aspace, const vaddr_t vaddr, paddr_t *paddr, uint *flags) {
|
||||
LTRACEF("aspace %p, vaddr %#lx\n", aspace, vaddr);
|
||||
|
||||
DEBUG_ASSERT(aspace);
|
||||
|
||||
// trim the vaddr to the aspace
|
||||
if (vaddr < aspace->base || vaddr > aspace->base + aspace->size - 1) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
uint level = RISCV_MMU_PT_LEVELS - 1;
|
||||
uint index = vaddr_to_index(vaddr, level);
|
||||
volatile riscv_pte_t *ptep = aspace->pt_virt + index;
|
||||
|
||||
// walk down through the levels, looking for a terminal entry that matches our address
|
||||
for (;;) {
|
||||
LTRACEF_LEVEL(2, "level %u, index %u, pte %p (%#lx)\n", level, index, ptep, *ptep);
|
||||
|
||||
// look at our page table entry
|
||||
riscv_pte_t pte = *ptep;
|
||||
if ((pte & RISCV_PTE_V) == 0) {
|
||||
// invalid entry, terminate search
|
||||
return ERR_NOT_FOUND;
|
||||
} else if ((pte & RISCV_PTE_PERM_MASK) == 0) {
|
||||
// next level page table pointer (RWX = 0)
|
||||
paddr_t ptp = RISCV_PTE_PPN(pte);
|
||||
volatile riscv_pte_t *ptv = paddr_to_kvaddr(ptp);
|
||||
|
||||
LTRACEF_LEVEL(2, "next level page table at %p, pa %#lx\n", ptv, ptp);
|
||||
|
||||
// go one level deeper
|
||||
level--;
|
||||
index = vaddr_to_index(vaddr, level);
|
||||
ptep = ptv + index;
|
||||
} else {
|
||||
// terminal entry
|
||||
LTRACEF_LEVEL(3, "terminal entry\n");
|
||||
|
||||
if (paddr) {
|
||||
// extract the ppn
|
||||
paddr_t pa = RISCV_PTE_PPN(pte);
|
||||
uintptr_t page_mask = page_mask_per_level(level);
|
||||
|
||||
// add the va offset into the physical address
|
||||
*paddr = pa | (vaddr & page_mask);
|
||||
LTRACEF_LEVEL(3, "raw pa %#lx, page_mask %#lx, final pa %#lx\n", pa, page_mask, *paddr);
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
// compute the flags
|
||||
*flags = pte_flags_to_mmu_flags(pte);
|
||||
LTRACEF_LEVEL(3, "computed flags %#x\n", *flags);
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// make sure we didn't decrement level one too many
|
||||
DEBUG_ASSERT(level < RISCV_MMU_PT_LEVELS);
|
||||
}
|
||||
// unreachable
|
||||
}
|
||||
|
||||
|
||||
// load a new user address space context.
|
||||
// aspace argument NULL should load kernel-only context
|
||||
void arch_mmu_context_switch(arch_aspace_t *aspace) {
|
||||
LTRACEF("aspace %p\n", aspace);
|
||||
|
||||
PANIC_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -7,6 +7,7 @@ MODULE_SRCS += $(LOCAL_DIR)/arch.c
|
||||
MODULE_SRCS += $(LOCAL_DIR)/asm.S
|
||||
MODULE_SRCS += $(LOCAL_DIR)/exceptions.c
|
||||
MODULE_SRCS += $(LOCAL_DIR)/thread.c
|
||||
MODULE_SRCS += $(LOCAL_DIR)/mmu.c
|
||||
MODULE_SRCS += $(LOCAL_DIR)/mp.c
|
||||
MODULE_SRCS += $(LOCAL_DIR)/time.c
|
||||
|
||||
@@ -16,6 +17,7 @@ MODULE_CFLAGS += -Wno-override-init
|
||||
SMP_MAX_CPUS ?= 1
|
||||
RISCV_MAX_HARTS ?= $(SMP_MAX_CPUS)
|
||||
RISCV_BOOT_HART ?= 0
|
||||
RISCV_MMU ?= none
|
||||
|
||||
GLOBAL_DEFINES += SMP_MAX_CPUS=$(SMP_MAX_CPUS)
|
||||
GLOBAL_DEFINES += RISCV_MAX_HARTS=$(RISCV_MAX_HARTS)
|
||||
@@ -33,19 +35,81 @@ RISCV_MODE ?= machine
|
||||
ifeq ($(strip $(RISCV_MODE)),machine)
|
||||
$(info RISCV: Machine Mode)
|
||||
GLOBAL_DEFINES += RISCV_M_MODE=1
|
||||
else
|
||||
ifeq ($(strip $(RISCV_MODE)),supervisor)
|
||||
ifneq ($(RISCV_MMU),none)
|
||||
$(error RISCV mmu not supported in machine mode)
|
||||
endif
|
||||
else ifeq ($(strip $(RISCV_MODE)),supervisor)
|
||||
$(info RISCV: Supervisor Mode)
|
||||
GLOBAL_DEFINES += RISCV_S_MODE=1
|
||||
else
|
||||
$(error Unknown RISC-V mode: "$(strip $(RISCV_MODE))" (valid values are "machine", "supervisor"))
|
||||
endif
|
||||
|
||||
ifeq ($(RISCV_MMU),sv48)
|
||||
ifeq ($(SUBARCH),32)
|
||||
$(error RISCV: sv48 mmu not supported for 32 bit riscv)
|
||||
endif
|
||||
$(info RISCV: MMU sv48)
|
||||
GLOBAL_DEFINES += RISCV_MMU=48
|
||||
WITH_KERNEL_VM ?= 1
|
||||
|
||||
# 48 bits split between two 47 bit halves
|
||||
KERNEL_ASPACE_BASE := 0xffff800000000000
|
||||
KERNEL_ASPACE_SIZE := 0x0000800000000000
|
||||
USER_ASPACE_BASE := 0x0000000001000000
|
||||
USER_ASPACE_SIZE := 0x00007ffffe000000
|
||||
|
||||
else ifeq ($(RISCV_MMU),sv39)
|
||||
ifeq ($(SUBARCH),32)
|
||||
$(error RISCV: sv39 mmu not supported for 32 bit riscv)
|
||||
endif
|
||||
$(info RISCV: MMU sv39)
|
||||
GLOBAL_DEFINES += RISCV_MMU=39
|
||||
WITH_KERNEL_VM ?= 1
|
||||
|
||||
# 39 bits split between two 38 bit halves
|
||||
KERNEL_ASPACE_BASE := 0xffffffc000000000
|
||||
KERNEL_ASPACE_SIZE := 0x0000004000000000
|
||||
USER_ASPACE_BASE := 0x0000000001000000
|
||||
USER_ASPACE_SIZE := 0x0000003ffe000000
|
||||
|
||||
else ifeq ($(RISCV_MMU),sv32)
|
||||
$(info RISCV: MMU sv32)
|
||||
GLOBAL_DEFINES += RISCV_MMU=32
|
||||
WITH_KERNEL_VM ?= 1
|
||||
|
||||
# 32 bits split between two 31 bit halves
|
||||
KERNEL_ASPACE_BASE := 0x80000000
|
||||
KERNEL_ASPACE_SIZE := 0x80000000
|
||||
USER_ASPACE_BASE := 0x01000000
|
||||
USER_ASPACE_SIZE := 0x7e000000
|
||||
|
||||
else ifeq ($(RISCV_MMU),none)
|
||||
else
|
||||
$(error Unknown RISCV_MMU: "$(strip $(RISCV_MMU))" (valid values are "none", "sv32", "sv39", "sv48"))
|
||||
endif
|
||||
|
||||
WITH_LINKER_GC ?= 0
|
||||
ifeq ($(WITH_KERNEL_VM),1)
|
||||
|
||||
GLOBAL_DEFINES += \
|
||||
KERNEL_ASPACE_BASE=$(KERNEL_ASPACE_BASE) \
|
||||
KERNEL_ASPACE_SIZE=$(KERNEL_ASPACE_SIZE) \
|
||||
USER_ASPACE_BASE=$(USER_ASPACE_BASE) \
|
||||
USER_ASPACE_SIZE=$(USER_ASPACE_SIZE)
|
||||
|
||||
KERNEL_BASE ?= $(KERNEL_ASPACE_BASE)+$(MEMBASE)
|
||||
KERNEL_LOAD_OFFSET ?= 0
|
||||
|
||||
GLOBAL_DEFINES += KERNEL_BASE=$(KERNEL_BASE)
|
||||
GLOBAL_DEFINES += KERNEL_LOAD_OFFSET=$(KERNEL_LOAD_OFFSET)
|
||||
|
||||
else # no kernel vm
|
||||
|
||||
KERNEL_BASE ?= $(MEMBASE)
|
||||
KERNEL_LOAD_OFFSET ?= 0
|
||||
|
||||
endif
|
||||
|
||||
ROMBASE ?= 0
|
||||
|
||||
GLOBAL_DEFINES += ROMBASE=$(ROMBASE)
|
||||
@@ -67,6 +131,7 @@ ifndef TOOLCHAIN_PREFIX
|
||||
TOOLCHAIN_PREFIX := riscv64-elf-
|
||||
endif
|
||||
ARCH_COMPILEFLAGS := -march=rv64imac -mabi=lp64 -mcmodel=medany
|
||||
GLOBAL_DEFINES += IS_64BIT=1
|
||||
|
||||
else
|
||||
$(error SUBARCH not set or set to something unknown)
|
||||
@@ -81,6 +146,8 @@ else
|
||||
ARCH_OPTFLAGS ?= -O2
|
||||
endif
|
||||
|
||||
WITH_LINKER_GC ?= 0
|
||||
|
||||
LIBGCC := $(shell $(TOOLCHAIN_PREFIX)gcc $(GLOBAL_COMPILEFLAGS) $(ARCH_COMPILEFLAGS) $(GLOBAL_CFLAGS) -print-libgcc-file-name)
|
||||
$(info LIBGCC = $(LIBGCC))
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ FUNCTION(_start)
|
||||
.option push
|
||||
.option norelax
|
||||
// set the global pointer
|
||||
la gp, __global_pointer$
|
||||
lla gp, __global_pointer$
|
||||
.option pop
|
||||
|
||||
#if RISCV_M_MODE
|
||||
@@ -25,10 +25,10 @@ FUNCTION(_start)
|
||||
|
||||
// if the hart is too high, trap it
|
||||
li t0, RISCV_MAX_HARTS
|
||||
ble t0, a0, .Lhart_trap
|
||||
ble t0, a0, hart_trap
|
||||
|
||||
// set the default stack per cpu
|
||||
la sp, default_stack_top
|
||||
lla sp, default_stack_top
|
||||
// default stack locations for each hart:
|
||||
// LOW ------------ HIGH
|
||||
// [hart2][hart1][hart0]
|
||||
@@ -38,13 +38,13 @@ FUNCTION(_start)
|
||||
|
||||
// if our hart isnt RISCV_BOOT_HART, trap the cpu
|
||||
li t2, RISCV_BOOT_HART
|
||||
bne t2, a0, .Lsecondary_trap
|
||||
bne t2, a0, secondary_trap
|
||||
|
||||
#if ARCH_RISCV_TWOSEGMENT
|
||||
// copy preinitialized data from flash to memory
|
||||
la t0, __data_start_rom
|
||||
la t1, __data_start
|
||||
la t2, __data_end
|
||||
lla t0, __data_start_rom
|
||||
lla t1, __data_start
|
||||
lla t2, __data_end
|
||||
beq t0, t1, 1f
|
||||
|
||||
0:
|
||||
@@ -57,17 +57,21 @@ FUNCTION(_start)
|
||||
|
||||
// zero bss
|
||||
1:
|
||||
la t0, __bss_start
|
||||
la t1, __bss_end
|
||||
lla t0, __bss_start
|
||||
lla t1, __bss_end
|
||||
0:
|
||||
sw zero, (t0)
|
||||
add t0, t0, 4
|
||||
bne t0, t1, 0b
|
||||
|
||||
#if RISCV_MMU
|
||||
jal _mmu_init
|
||||
#endif
|
||||
|
||||
#if WITH_SMP
|
||||
// Release any other harts into riscv_secondary_entry
|
||||
fence w, w
|
||||
la t1, _boot_status
|
||||
lla t1, _boot_status
|
||||
li t0, 1
|
||||
sb t0, (t1)
|
||||
#endif
|
||||
@@ -89,12 +93,17 @@ FUNCTION(_start)
|
||||
// should never return here
|
||||
j .
|
||||
|
||||
.Lsecondary_trap:
|
||||
LOCAL_FUNCTION(secondary_trap)
|
||||
#if WITH_SMP
|
||||
// wait for _boot_status to be nonzero, then go into riscv_secondary_entry
|
||||
la t5, _boot_status
|
||||
lla t5, _boot_status
|
||||
lb t0, (t5)
|
||||
beqz t0, .Lsecondary_trap
|
||||
beqz t0, secondary_trap
|
||||
|
||||
#if RISCV_MMU
|
||||
// TODO: enable the mmu on this core
|
||||
jal .Lenable_mmu
|
||||
#endif
|
||||
|
||||
// set the per cpu structure before getting into the secondary boot path
|
||||
jal riscv_configure_percpu_early
|
||||
@@ -106,11 +115,102 @@ FUNCTION(_start)
|
||||
j .
|
||||
#endif
|
||||
|
||||
.Lhart_trap:
|
||||
LOCAL_FUNCTION(hart_trap)
|
||||
// cpus with too high of a hart id go here and spin forever
|
||||
wfi
|
||||
j .
|
||||
|
||||
#if RISCV_MMU
|
||||
// initialize the kernel page tables
|
||||
// for all MMU versions, identity map some amount of memory near 0 and
|
||||
// the same amount at the bottom of the kernel's address space
|
||||
LOCAL_FUNCTION(_mmu_init)
|
||||
lla t0, kernel_pgtable
|
||||
|
||||
// store the physical address of the pgtable for future use
|
||||
lla t1, kernel_pgtable_phys
|
||||
sd t0, (t1)
|
||||
|
||||
// compute kernel pgtable pointer (index 256)
|
||||
addi t1, t0, (8 * 128)
|
||||
addi t1, t1, (8 * 128)
|
||||
|
||||
// page table entry: address 0, A, D, G, XWR, V
|
||||
li t2, (0 | (1<<7) | (1<<6) | (1<<5) | (1<<3) | (1<<2) | (1<<1) | (1<<0))
|
||||
|
||||
// num interations and increment count
|
||||
#if RISCV_MMU == 48
|
||||
// RV48: map the first 512GB of the physical address space at the
|
||||
// bottom of the kernel address space using a single terapage
|
||||
li t3, 1
|
||||
li t4, (512 * 1024 * 1024 * 1024) >> 2
|
||||
#elif RISCV_MMU == 39
|
||||
// RV39: map the first 64GB of the physical address space at the
|
||||
// bottom of the kernel address space using 64 1GB gigapages
|
||||
li t3, 64
|
||||
li t4, (1 * 1024 * 1024 * 1024) >> 2
|
||||
#else
|
||||
#error implement
|
||||
#endif
|
||||
|
||||
// loop, writing t3 entries out and incrementing by t4 address
|
||||
0:
|
||||
sd t2, (t1)
|
||||
sd t2, (t0)
|
||||
add t2, t2, t4
|
||||
addi t0, t0, 8
|
||||
addi t1, t1, 8
|
||||
addi t3, t3, -1
|
||||
bnez t3, 0b
|
||||
|
||||
// ensure it's written out
|
||||
fence w,w
|
||||
|
||||
.Lenable_mmu:
|
||||
// set the satp register and enable the mmu
|
||||
// ASID 0, kernel_pgtable address
|
||||
lla t0, kernel_pgtable
|
||||
srli t1, t0, 12
|
||||
#if RISCV_MMU == 48
|
||||
li t2, (9 << 60) // mode 9, SV48
|
||||
#elif RISCV_MMU == 39
|
||||
li t2, (8 << 60) // mode 8, SV39
|
||||
#else
|
||||
#error implement
|
||||
#endif
|
||||
or t1, t1, t2
|
||||
csrw satp, t1
|
||||
lla s0, kernel_pgtable
|
||||
|
||||
// global tlb fence
|
||||
sfence.vma zero, zero
|
||||
|
||||
// mmu is initialized and we're running out of an identity physical map
|
||||
|
||||
// save the physical address of .Lhigh
|
||||
lla t1, .Lhigh
|
||||
|
||||
// bounce to the high address
|
||||
lla t0, .Lhigh_addr
|
||||
ld t0, (t0)
|
||||
jr t0
|
||||
|
||||
// the full virtual address of the .Lhigh label
|
||||
.Lhigh_addr:
|
||||
.quad .Lhigh
|
||||
.Lhigh:
|
||||
|
||||
// we're now running at the high virtual address
|
||||
// compute the delta between the old physical and newer high addresses
|
||||
sub t0, t0, t1
|
||||
|
||||
// fix up the gp, stack pointer, and return address
|
||||
add gp, gp, t0
|
||||
add sp, sp, t0
|
||||
add ra, ra, t0
|
||||
ret
|
||||
#endif // RISCV_MMU
|
||||
|
||||
.bss
|
||||
.align 4
|
||||
LOCAL_DATA(default_stack)
|
||||
|
||||
@@ -30,3 +30,24 @@
|
||||
#define PLIC_HART_IDX(hart) ((2 * (hart)) + 1)
|
||||
#endif
|
||||
|
||||
#define MEMORY_BASE_PHYS (0x80000000)
|
||||
#if __riscv_xlen == 64
|
||||
// up to 64 GB of ram, which seems to be a soft cap
|
||||
#define MEMORY_APERTURE_SIZE (64ULL * 1024 * 1024 * 1024)
|
||||
#else
|
||||
// cap after 1GB
|
||||
#define MEMORY_APERTURE_SIZE (1UL * 1024 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
// map all of 0-1GB into kernel space in one shot
|
||||
#define PERIPHERAL_BASE_PHYS (0)
|
||||
#define PERIPHERAL_BASE_SIZE (0x40000000UL) // 1GB
|
||||
|
||||
// XXX clean up
|
||||
#if __riscv_xlen == 64
|
||||
#define PERIPHERAL_BASE_VIRT (0xffffffffc0000000ULL) // -1GB
|
||||
#else
|
||||
#define PERIPHERAL_BASE_VIRT (0xc0000000UL) // -1GB
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <lk/reg.h>
|
||||
#include <lk/trace.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/novm.h>
|
||||
#include <platform.h>
|
||||
#include <platform/interrupts.h>
|
||||
#include <platform/debug.h>
|
||||
@@ -21,6 +20,11 @@
|
||||
#if WITH_LIB_MINIP
|
||||
#include <lib/minip.h>
|
||||
#endif
|
||||
#if WITH_KERNEL_VM
|
||||
#include <kernel/vm.h>
|
||||
#else
|
||||
#include <kernel/novm.h>
|
||||
#endif
|
||||
|
||||
#include "platform_p.h"
|
||||
|
||||
@@ -28,16 +32,33 @@
|
||||
|
||||
extern ulong lk_boot_args[4];
|
||||
|
||||
#if WITH_KERNEL_VM
|
||||
#define DEFAULT_MEMORY_SIZE (MEMSIZE) /* try to fetch from the emulator via the fdt */
|
||||
|
||||
static pmm_arena_t arena = {
|
||||
.name = "ram",
|
||||
.base = MEMORY_BASE_PHYS,
|
||||
.size = DEFAULT_MEMORY_SIZE,
|
||||
.flags = PMM_ARENA_FLAG_KMAP,
|
||||
};
|
||||
#endif
|
||||
|
||||
// callbacks to the fdt_walk routine
|
||||
static void memcallback(uint64_t base, uint64_t len, void *cookie) {
|
||||
bool *found_mem = (bool *)cookie;
|
||||
|
||||
LTRACEF("base %#llx len %#llx cookie %p\n", base, len, cookie);
|
||||
|
||||
/* add another novm arena */
|
||||
/* add another vm arena */
|
||||
if (!*found_mem) {
|
||||
printf("FDT: found memory arena, base %#llx size %#llx\n", base, len);
|
||||
#if WITH_KERNEL_VM
|
||||
arena.base = base;
|
||||
arena.size = len;
|
||||
pmm_add_arena(&arena);
|
||||
#else
|
||||
novm_add_arena("fdt", base, len);
|
||||
#endif
|
||||
*found_mem = true; // stop searching after the first one
|
||||
}
|
||||
}
|
||||
@@ -75,7 +96,11 @@ void platform_early_init(void) {
|
||||
}
|
||||
|
||||
if (!found_mem) {
|
||||
#if WITH_KERNEL_VM
|
||||
pmm_add_arena(&arena);
|
||||
#else
|
||||
novm_add_arena("default", MEMBASE, MEMSIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cpu_count > 0) {
|
||||
@@ -83,6 +108,13 @@ void platform_early_init(void) {
|
||||
riscv_set_secondary_count(cpu_count - 1);
|
||||
}
|
||||
|
||||
#if WITH_KERNEL_VM
|
||||
/* reserve the first 128K of ram which is marked protected by the PMP in firmware */
|
||||
struct list_node list = LIST_INITIAL_VALUE(list);
|
||||
pmm_alloc_range(MEMBASE, 0x20000 / PAGE_SIZE, &list);
|
||||
#endif
|
||||
|
||||
|
||||
LTRACEF("done scanning FDT\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,14 @@ RISCV_MODE ?= machine
|
||||
WITH_SMP ?= 1
|
||||
SMP_MAX_CPUS ?= 8
|
||||
|
||||
ifeq ($(RISCV_MODE),supervisor)
|
||||
ifeq ($(SUBARCH),32)
|
||||
RISCV_MMU ?= sv32
|
||||
else
|
||||
RISCV_MMU ?= sv48
|
||||
endif
|
||||
endif
|
||||
|
||||
MODULE_DEPS += lib/cbuf
|
||||
MODULE_DEPS += lib/fdt
|
||||
MODULE_DEPS += lib/fdtwalk
|
||||
@@ -19,7 +27,6 @@ MODULE_SRCS += $(LOCAL_DIR)/platform.c
|
||||
MODULE_SRCS += $(LOCAL_DIR)/plic.c
|
||||
MODULE_SRCS += $(LOCAL_DIR)/uart.c
|
||||
|
||||
#ROMBASE ?= 0x20400000 # if running from rom, start here
|
||||
MEMBASE ?= 0x80000000
|
||||
MEMSIZE ?= 0x01000000 # default to 16MB
|
||||
ifeq ($(RISCV_MODE),supervisor)
|
||||
|
||||
@@ -54,6 +54,7 @@ static inline void hexdump8(const void *ptr, size_t len) {
|
||||
void panic(const char *fmt, ...) __PRINTFLIKE(1, 2) __NO_RETURN;
|
||||
|
||||
#define PANIC_UNIMPLEMENTED panic("%s unimplemented\n", __PRETTY_FUNCTION__)
|
||||
#define PANIC_UNIMPLEMENTED_MSG(x...) panic("%s unimplemented: %s\n", __PRETTY_FUNCTION__, x)
|
||||
|
||||
/* spin the cpu for a period of (short) time */
|
||||
void spin(uint32_t usecs);
|
||||
|
||||
Reference in New Issue
Block a user