Files
lk/arch/riscv/start.S
Travis Geiselbrecht 7c9906a5ff [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.
2020-05-10 17:09:48 -07:00

224 lines
5.3 KiB
ArmAsm

/*
* Copyright (c) 2015 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
*/
#include <lk/asm.h>
#include <arch/defines.h>
#include "config.h"
.section ".text.boot"
FUNCTION(_start)
.option push
.option norelax
// set the global pointer
lla gp, __global_pointer$
.option pop
#if RISCV_M_MODE
// copy the hart id into a0 which we'll use later
// supervisor mode should already have hart id in a0
csrr a0, mhartid
#endif
// if the hart is too high, trap it
li t0, RISCV_MAX_HARTS
ble t0, a0, hart_trap
// set the default stack per cpu
lla sp, default_stack_top
// default stack locations for each hart:
// LOW ------------ HIGH
// [hart2][hart1][hart0]
li t1, ARCH_DEFAULT_STACK_SIZE
mul t1, t1, a0
sub sp, sp, t1
// if our hart isnt RISCV_BOOT_HART, trap the cpu
li t2, RISCV_BOOT_HART
bne t2, a0, secondary_trap
#if ARCH_RISCV_TWOSEGMENT
// copy preinitialized data from flash to memory
lla t0, __data_start_rom
lla t1, __data_start
lla t2, __data_end
beq t0, t1, 1f
0:
lw t3, (t0)
sw t3, (t1)
add t0, t0, 4
add t1, t1, 4
bne t1, t2, 0b
#endif
// zero bss
1:
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
lla t1, _boot_status
li t0, 1
sb t0, (t1)
#endif
// call into early C code to set up the percpu structure
mv s0, a0
mv s1, a1
mv s2, a2
mv s3, a3
jal riscv_configure_percpu_early
mv a0, s0
mv a1, s1
mv a2, s2
mv a3, s3
// call main
jal lk_main
// should never return here
j .
LOCAL_FUNCTION(secondary_trap)
#if WITH_SMP
// wait for _boot_status to be nonzero, then go into riscv_secondary_entry
lla t5, _boot_status
lb t0, (t5)
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
// bootstrap the secondary cpus
jal riscv_secondary_entry
#else
wfi
j .
#endif
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)
.skip ARCH_DEFAULT_STACK_SIZE * RISCV_MAX_HARTS;
LOCAL_DATA(default_stack_top)
// put boot status in .data so it doesn't get paved over during BSS initialization
.data
LOCAL_DATA(_boot_status)
.byte 0