asterinas/ostd/src/arch/riscv/boot/boot.S

134 lines
3.6 KiB
ArmAsm

/* SPDX-License-Identifier: MPL-2.0 */
// The boot routine executed by the bootstrap processor (BSP) on RISC-V.
SATP_MODE_SV39 = 8 << 60
SATP_MODE_SV48 = 9 << 60
SATP_PPN_SHIFT = 0
PTE_V = 0x01
PTE_R = 0x02
PTE_W = 0x04
PTE_X = 0x08
PTE_PPN_SHIFT = 10
PTE_SIZE = 8
PAGE_SHIFT = 12
KERNEL_VMA_OFFSET = 0xffffffff00000000
.section ".boot", "awx", @progbits
.global _start
_start:
# Arguments passed from SBI:
# a0 = hart id
# a1 = device tree paddr
# We do not touch them here. They are passed to the Rust entrypoint.
# Set up the Sv48 page table.
# sv48_boot_l4pt[511] = (PPN(sv48_boot_l3pt) << PTE_PPN_SHIFT) | PTE_V
lla t1, sv48_boot_l4pt
li t0, 511 * PTE_SIZE
add t1, t1, t0
lla t0, sv48_boot_l3pt
srli t0, t0, PAGE_SHIFT - PTE_PPN_SHIFT
ori t0, t0, PTE_V
sd t0, 0(t1)
# Try loading the Sv48 page table.
lla t0, sv48_boot_l4pt
li t1, SATP_MODE_SV48
srli t0, t0, PAGE_SHIFT - SATP_PPN_SHIFT
or t0, t0, t1
csrw satp, t0
# Check if the write to satp succeeds. If not, try Sv39.
# Reference: <https://riscv.github.io/riscv-isa-manual/snapshot/privileged/#satp>.
csrr t1, satp
beq t0, t1, flush_tlb_bsp
# Try loading the Sv39 page table.
lla t0, sv39_boot_l3pt
li t1, SATP_MODE_SV39
srli t0, t0, PAGE_SHIFT - SATP_PPN_SHIFT
or t0, t0, t1
csrw satp, t0
# Check again if the write to satp succeeds.
csrr t1, satp
beq t0, t1, flush_tlb_bsp
# If the CPU doesn't support either Sv48 or Sv39 this is actually reachable.
unreachable_pa_bsp:
j unreachable_pa_bsp
flush_tlb_bsp:
sfence.vma
# Update SP/PC to use the virtual address.
li t1, KERNEL_VMA_OFFSET
lla sp, boot_stack_top
or sp, sp, t1
lla t0, _start_virt - KERNEL_VMA_OFFSET
or t0, t0, t1
jr t0
PTE_VRWX = PTE_V | PTE_R | PTE_W | PTE_X
.balign 4096
sv48_boot_l4pt:
.quad (0x0 << PTE_PPN_SHIFT) | PTE_VRWX # identity 0~512 GiB
.zero 255 * PTE_SIZE
.quad (0x0 << PTE_PPN_SHIFT) | PTE_VRWX # linear 0~512 GiB
.zero 254 * PTE_SIZE
.quad 0 # TBA (-> boot_l3pt)
sv48_boot_l3pt: # 0xffff_ffff_0000_0000 -> 0x0000_0000_0000_0000
.zero 508 * PTE_SIZE
.quad (0x00000 << PTE_PPN_SHIFT) | PTE_VRWX # code 0~1 GiB
.quad (0x40000 << PTE_PPN_SHIFT) | PTE_VRWX # code 1~2 GiB
.quad (0x80000 << PTE_PPN_SHIFT) | PTE_VRWX # code 2~3 GiB
.quad 0
.balign 4096
sv39_boot_l3pt:
.set i, 0
.rept 128 # identity 0~128 GiB
.quad ((i * 0x40000) << PTE_PPN_SHIFT) | PTE_VRWX
.set i, i + 1
.endr
.zero 128 * PTE_SIZE
.set i, 0
.rept 128 # linear 0~128 GiB
.quad ((i * 0x40000) << PTE_PPN_SHIFT) | PTE_VRWX
.set i, i + 1
.endr
.zero 124 * PTE_SIZE
.quad (0x00000 << PTE_PPN_SHIFT) | PTE_VRWX # code 0~1 GiB
.quad (0x40000 << PTE_PPN_SHIFT) | PTE_VRWX # code 1~2 GiB
.quad (0x80000 << PTE_PPN_SHIFT) | PTE_VRWX # code 2~3 GiB
.quad 0
.section ".boot.stack", "aw", @nobits
boot_stack_bottom:
.balign 4096
.skip 0x40000 # 256 KiB
boot_stack_top:
# From here, we no longer use physical address.
.text
_start_virt:
# Initialize GP to the CPU-local storage's base address.
.extern __cpu_local_start
lla gp, __cpu_local_start
# Jump into Rust code.
lla t0, riscv_boot
jr t0
unreachable_va_bsp:
j unreachable_va_bsp