riscv64: position-independent kernel

This commit is contained in:
2025-07-17 14:38:51 +03:00
parent 1f6f091c2c
commit 019146e9ff
19 changed files with 409 additions and 593 deletions
-58
View File
@@ -1,58 +0,0 @@
ENTRY(__rv64_entry);
KERNEL_PHYS_BASE = 0x40200000;
KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000;
SECTIONS {
. = KERNEL_PHYS_BASE;
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
.text.entry : {
*(.text.entry)
}
. = ALIGN(16);
. = . + KERNEL_VIRT_OFFSET;
.text : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.text.vectors));
*(.text*)
}
. = ALIGN(4K);
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
*(.rodata*)
*(.eh_frame*)
}
. = ALIGN(4K);
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.tables))
}
. = ALIGN(4K);
.data : AT(. - KERNEL_VIRT_OFFSET) {
*(.data*)
. = ALIGN(8);
/* PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET); */
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*))
PROVIDE(__init_array_end = .);
*(.got*)
}
. = ALIGN(4K);
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
.bss : AT(. - KERNEL_VIRT_OFFSET) {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
PROVIDE(__kernel_end = .);
};
-59
View File
@@ -1,59 +0,0 @@
ENTRY(__rv64_entry);
KERNEL_PHYS_BASE = 0x80200000;
KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000;
SECTIONS {
. = KERNEL_PHYS_BASE;
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
.text.entry : {
*(.text.entry)
}
. = ALIGN(16);
. = . + KERNEL_VIRT_OFFSET;
.text : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.text.vectors));
*(.text*)
}
. = ALIGN(4K);
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
*(.rodata*)
*(.eh_frame*)
}
. = ALIGN(4K);
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.tables))
}
. = ALIGN(4K);
.data : AT(. - KERNEL_VIRT_OFFSET) {
*(.data*)
. = ALIGN(8);
/* PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET); */
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*))
PROVIDE(__init_array_end = .);
*(.got*)
}
. = ALIGN(4K);
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
.bss : AT(. - KERNEL_VIRT_OFFSET) {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
PROVIDE(__kernel_end = .);
PROVIDE(__kernel_size = __kernel_end - __kernel_start);
};
+75
View File
@@ -0,0 +1,75 @@
ENTRY(__riscv64_entry);
SECTIONS {
. = 0x0;
PROVIDE(__kernel_start = .);
.text : {
*(.text.entry)
*(.text*)
}
. = ALIGN(4K);
.rodata : {
*(.rodata*)
*(.eh_frame*)
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*));
PROVIDE(__init_array_end = .);
}
. = ALIGN(4K);
.rela : {
PROVIDE(__rela_start = .);
*(.rela*)
PROVIDE(__rela_end = .);
}
.got : {
*(.got*)
}
.dynamic : {
*(.dynamic)
}
. = ALIGN(4K);
.data : {
*(.data*)
}
. = ALIGN(4K);
PROVIDE(__bss_start = .);
.bss : {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end = .);
PROVIDE(__kernel_end = .);
PROVIDE(__kernel_size = __kernel_end - __kernel_start);
.dynsym : {
*(.dynsym)
}
.gnu.hash : {
*(.gnu.hash)
}
.hash : {
*(.hash)
}
.dynstr : {
*(.dynstr)
}
}
+1
View File
@@ -14,6 +14,7 @@
"panic-strategy": "abort",
"dynamic-linking": true,
"relocation-model": "pic",
"position-independent-executables": true,
"code-model": "medium",
"eh-frame-header": false,
+1 -2
View File
@@ -124,8 +124,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
// TODO stack is leaked
let satp = InMemoryRegister::new(0);
let kernel_table_phys =
((&raw const mem::KERNEL_TABLES).addr() - KERNEL_VIRT_OFFSET) as u64;
let kernel_table_phys = mem::fixed::table_physical_address().into_u64();
satp.write(SATP::MODE::Sv39 + SATP::ASID.val(0) + SATP::PPN.val(kernel_table_phys >> 12));
Ok(Self {
+32
View File
@@ -0,0 +1,32 @@
use kernel_arch_interface::sync::IrqSafeSpinlock;
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
use crate::{
mem::{
auto_lower_address,
table::{PageEntry, PageTable, L1},
KERNEL_VIRT_OFFSET,
},
ArchitectureImpl,
};
pub const IDENTITY_SIZE_L1: usize = 64;
pub(super) static mut KERNEL_L1: PageTable<L1> = const {
let mut table = PageTable::zeroed();
let mut index = 0;
while index < IDENTITY_SIZE_L1 {
let entry = PageEntry::identity_block(PhysicalAddress::from_usize(index << L1::SHIFT));
table.entries[index] = entry;
table.entries[index + ((KERNEL_VIRT_OFFSET >> L1::SHIFT) & 0x1FF)] = entry;
index += 1;
}
table
};
pub(super) static LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
pub fn table_physical_address() -> PhysicalAddress {
PhysicalAddress::from_usize(auto_lower_address(&raw const KERNEL_L1))
}
+51
View File
@@ -0,0 +1,51 @@
use libk_mm_interface::table::{EntryLevel, EntryLevelExt};
use crate::mem::table::L3;
pub fn tlb_flush_global_full() {
tlb_flush_full();
// TODO send TLB shootdown IPI to other harts
}
pub fn tlb_flush_global_va(va: usize) {
tlb_flush_va(va);
// TODO send TLB shootdown IPI to other harts
}
pub fn tlb_flush_range_va(start: usize, size: usize) {
let end = (start + size).page_align_up::<L3>();
let start = start.page_align_down::<L3>();
for page in (start..end).step_by(L3::SIZE) {
tlb_flush_va(page);
}
}
pub fn tlb_flush_range_va_asid(asid: usize, start: usize, size: usize) {
let end = (start + size).page_align_up::<L3>();
let start = start.page_align_down::<L3>();
for page in (start..end).step_by(L3::SIZE) {
tlb_flush_va_asid(page, asid);
}
}
#[inline]
pub fn tlb_flush_full() {
unsafe { core::arch::asm!("sfence.vma") };
}
#[inline]
pub fn tlb_flush_va(va: usize) {
unsafe { core::arch::asm!("sfence.vma {0}, zero", in(reg) va) };
}
#[inline]
pub fn tlb_flush_asid(asid: usize) {
unsafe { core::arch::asm!("sfence.vma zero, {0}", in(reg) asid) };
}
#[inline]
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
unsafe { core::arch::asm!("sfence.vma {0}, {1}", in(reg) va, in(reg) asid) };
}
+41 -295
View File
@@ -1,70 +1,26 @@
use cfg_if::cfg_if;
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
split_spinlock,
use kernel_arch_interface::mem::{
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
};
use libk_mm_interface::{
address::PhysicalAddress,
table::{page_index, EntryLevel, EntryLevelExt},
};
use memtables::riscv64::PageAttributes;
use static_assertions::{const_assert, const_assert_eq};
use table::{PageEntry, PageTable, L1, L2, L3};
use tock_registers::interfaces::Writeable;
use yggdrasil_abi::error::Error;
pub use memtables::riscv64::FixedTables;
use crate::{
mem::table::{PageTable, L1, L3},
registers::SATP,
};
use crate::registers::SATP;
pub use intrinsics::*;
pub mod fixed;
pub mod intrinsics;
pub mod process;
pub mod table;
split_spinlock! {
use crate::ArchitectureImpl;
use crate::mem::FixedTables;
use libk_mm_interface::KernelImageObject;
#[link_section = ".data.tables"]
#[used]
static KERNEL_TABLES: KernelImageObject<FixedTables> =
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
}
cfg_if! {
if #[cfg(feature = "riscv64_board_virt")] {
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
} else if #[cfg(feature = "riscv64_board_jh7110")] {
pub const KERNEL_PHYS_BASE: usize = 0x40200000;
} else if #[cfg(rust_analyzer)] {
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
}
}
pub const KERNEL_VIRT_OFFSET: usize = kernel_arch_interface::KERNEL_VIRT_OFFSET;
pub const SIGN_EXTEND_MASK: usize = 0xFFFFFF80_00000000;
pub const KERNEL_START_L1I: usize = page_index::<L1>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
pub const KERNEL_L2I: usize = page_index::<L2>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
const_assert_eq!(KERNEL_L2I, 1);
// Runtime mappings
// 1GiB of device memory space
const DEVICE_MAPPING_L1I: usize = KERNEL_START_L1I + 1;
const DEVICE_MAPPING_L3_COUNT: usize = 4;
// 32GiB of RAM space
const RAM_MAPPING_START_L1I: usize = KERNEL_START_L1I + 2;
const RAM_MAPPING_L1_COUNT: usize = 32;
const_assert!(RAM_MAPPING_START_L1I + RAM_MAPPING_L1_COUNT <= 512);
const_assert!(DEVICE_MAPPING_L1I < 512);
const DEVICE_MAPPING_OFFSET: usize = (DEVICE_MAPPING_L1I << L1::SHIFT) | SIGN_EXTEND_MASK;
const RAM_MAPPING_OFFSET: usize = (RAM_MAPPING_START_L1I << L1::SHIFT) | SIGN_EXTEND_MASK;
// Runtime tables
static mut DEVICE_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
static mut DEVICE_MAPPING_L3S: [PageTable<L3>; DEVICE_MAPPING_L3_COUNT] =
[const { PageTable::zeroed() }; DEVICE_MAPPING_L3_COUNT];
/// Any VAs above this one are sign-extended
pub const USER_BOUNDARY: usize = 0x40_00000000;
@@ -75,17 +31,20 @@ pub struct KernelTableManagerImpl;
impl KernelTableManager for KernelTableManagerImpl {
fn virtualize(address: u64) -> usize {
let address = address as usize;
if address >= RAM_MAPPING_OFFSET {
panic!("Invalid physical address: {address:#x}");
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
address + KERNEL_VIRT_OFFSET
} else {
panic!("Invalid physical address: {address:#x}")
}
address + RAM_MAPPING_OFFSET
}
fn physicalize(address: usize) -> u64 {
if address < RAM_MAPPING_OFFSET {
panic!("Invalid \"physicalized\" virtual address {address:#x}");
if address < KERNEL_VIRT_OFFSET
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
{
panic!("Invalid virtualized address: {address:#x}");
}
(address - RAM_MAPPING_OFFSET) as u64
(address - KERNEL_VIRT_OFFSET) as u64
}
unsafe fn map_device_pages(
@@ -93,146 +52,32 @@ impl KernelTableManager for KernelTableManagerImpl {
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
unsafe { map_device_memory(PhysicalAddress::from_u64(base), count, attrs) }
let _ = attrs;
let _lock = fixed::LOCK.lock();
let base = PhysicalAddress::from_u64(base);
let l3_aligned_base = base.page_align_down::<L3>();
let l3_aligned_end = base.add(count).page_align_up::<L3>();
let l3_offset = base - l3_aligned_base;
let l3_page_count = (l3_aligned_end - l3_aligned_base).page_count::<L3>();
let l3_aligned_virt = l3_aligned_base.add(KERNEL_VIRT_OFFSET).into_usize();
Ok(unsafe {
RawDeviceMemoryMapping::from_raw_parts(
l3_aligned_base.into_u64(),
l3_aligned_virt + l3_offset,
l3_aligned_virt,
l3_page_count,
L3::SIZE,
)
})
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
unsafe { unmap_device_memory(mapping) }
let _ = mapping;
}
}
// Device mappings
unsafe fn map_device_memory_l3(
base: PhysicalAddress,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<usize, Error> {
// TODO don't map pages if already mapped
'l0: for i in 0..DEVICE_MAPPING_L3_COUNT * 512 {
for j in 0..count {
let l2i = (i + j) / 512;
let l3i = (i + j) % 512;
unsafe {
if DEVICE_MAPPING_L3S[l2i][l3i].is_present() {
continue 'l0;
}
}
}
for j in 0..count {
let l2i = (i + j) / 512;
let l3i = (i + j) % 512;
unsafe {
DEVICE_MAPPING_L3S[l2i][l3i] =
PageEntry::page(base.add(j * L3::SIZE), PageAttributes::W);
}
}
let start = DEVICE_MAPPING_OFFSET + i * L3::SIZE;
tlb_flush_range_va(start, count * L3::SIZE);
return Ok(start);
}
Err(Error::OutOfMemory)
}
#[allow(unused)]
unsafe fn map_device_memory_l2(
base: PhysicalAddress,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<usize, Error> {
'l0: for i in DEVICE_MAPPING_L3_COUNT..512 {
for j in 0..count {
unsafe {
if DEVICE_MAPPING_L2[i + j].is_present() {
continue 'l0;
}
}
}
unsafe {
for j in 0..count {
DEVICE_MAPPING_L2[i + j] =
PageEntry::<L2>::block(base.add(j * L2::SIZE), PageAttributes::W);
}
}
let start = DEVICE_MAPPING_OFFSET + i * L2::SIZE;
tlb_flush_range_va(start, count * L2::SIZE);
return Ok(start);
}
Err(Error::OutOfMemory)
}
pub(crate) unsafe fn map_device_memory(
base: PhysicalAddress,
size: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<KernelTableManagerImpl>, Error> {
let l3_aligned = base.page_align_down::<L3>();
let l3_offset = base.page_offset::<L3>();
let page_count = (l3_offset + size).page_count::<L3>();
if page_count > 256 {
// Large mapping, use L2 mapping instead
let l2_aligned = base.page_align_down::<L2>();
let l2_offset = base.page_offset::<L2>();
let page_count = (l2_offset + size).page_count::<L2>();
unsafe {
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
let address = base_address + l2_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l2_aligned.into_u64(),
address,
base_address,
page_count,
L2::SIZE,
))
}
} else {
// Just map the pages directly
unsafe {
let base_address = map_device_memory_l3(l3_aligned, page_count, attrs)?;
let address = base_address + l3_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l3_aligned.into_u64(),
address,
base_address,
page_count,
L3::SIZE,
))
}
}
}
pub(crate) unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTableManagerImpl>) {
match map.page_size {
L3::SIZE => {
for i in 0..map.page_count {
let page = map.base_address + i * L3::SIZE;
let l2i = page.page_index::<L2>();
let l3i = page.page_index::<L3>();
unsafe {
assert!(DEVICE_MAPPING_L3S[l2i][l3i].is_present());
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
}
}
tlb_flush_range_va(map.base_address, map.page_count * L3::SIZE);
}
L2::SIZE => todo!(),
_ => unimplemented!(),
}
}
pub fn auto_address<T>(x: *const T) -> usize {
pub fn auto_lower_address<T>(x: *const T) -> usize {
let x = x.addr();
if x >= KERNEL_VIRT_OFFSET {
x - KERNEL_VIRT_OFFSET
@@ -247,113 +92,14 @@ pub fn auto_address<T>(x: *const T) -> usize {
///
/// Only meant to be called once per each HART during their early init.
pub unsafe fn enable_mmu() {
let l1_phys = auto_address(&raw const KERNEL_TABLES) as u64;
let l1_phys = auto_lower_address(&raw const fixed::KERNEL_L1) as u64;
tlb_flush_full();
SATP.write(SATP::PPN.val(l1_phys >> 12) + SATP::MODE::Sv39);
}
/// Removes the lower half translation mappings.
///
/// # Safety
///
/// Needs to be called once after secondary HARTs are initialized.
pub unsafe fn unmap_lower_half() {
let mut tables = KERNEL_TABLES.lock();
let kernel_l1i_lower = page_index::<L1>(KERNEL_PHYS_BASE);
tables.l1.data[kernel_l1i_lower] = 0;
tlb_flush_range_va(0x0, L1::SIZE);
}
/// Sets up run-time kernel translation tables.
///
/// # Safety
///
/// The caller must ensure MMU is already enabled.
pub unsafe fn setup_fixed_tables() {
let mut tables = KERNEL_TABLES.lock();
let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
// Set up static runtime mappings
for i in 0..DEVICE_MAPPING_L3_COUNT {
unsafe {
let device_mapping_l3_phys = PhysicalAddress::from_usize(
(&raw const DEVICE_MAPPING_L3S[i]).addr() - KERNEL_VIRT_OFFSET,
);
DEVICE_MAPPING_L2[i] =
PageEntry::table(device_mapping_l3_phys, PageAttributes::empty());
}
}
assert_eq!(tables.l1.data[DEVICE_MAPPING_L1I], 0);
tables.l1.data[DEVICE_MAPPING_L1I] =
((device_mapping_l2_phys as u64) >> 2) | PageAttributes::V.bits();
for l1i in 0..RAM_MAPPING_L1_COUNT {
let physical = (l1i as u64) << L1::SHIFT;
tables.l1.data[l1i + RAM_MAPPING_START_L1I] = (physical >> 2)
| (PageAttributes::R
| PageAttributes::W
| PageAttributes::A
| PageAttributes::D
| PageAttributes::V)
.bits();
}
tlb_flush_full();
}
pub fn tlb_flush_global_full() {
tlb_flush_full();
// TODO send TLB shootdown IPI to other harts
}
pub fn tlb_flush_global_va(va: usize) {
tlb_flush_va(va);
// TODO send TLB shootdown IPI to other harts
}
pub fn tlb_flush_range_va(start: usize, size: usize) {
let end = (start + size).page_align_up::<L3>();
let start = start.page_align_down::<L3>();
for page in (start..end).step_by(L3::SIZE) {
tlb_flush_va(page);
}
}
pub fn tlb_flush_range_va_asid(asid: usize, start: usize, size: usize) {
let end = (start + size).page_align_up::<L3>();
let start = start.page_align_down::<L3>();
for page in (start..end).step_by(L3::SIZE) {
tlb_flush_va_asid(page, asid);
}
}
#[inline]
pub fn tlb_flush_full() {
unsafe { core::arch::asm!("sfence.vma") };
}
#[inline]
pub fn tlb_flush_va(va: usize) {
unsafe { core::arch::asm!("sfence.vma {0}, zero", in(reg) va) };
}
#[inline]
pub fn tlb_flush_asid(asid: usize) {
unsafe { core::arch::asm!("sfence.vma zero, {0}", in(reg) asid) };
}
#[inline]
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
unsafe { core::arch::asm!("sfence.vma {0}, {1}", in(reg) va, in(reg) asid) };
}
pub fn clone_kernel_tables(dst: &mut PageTable<L1>) {
let tables = KERNEL_TABLES.lock();
let _lock = fixed::LOCK.lock();
for l1i in page_index::<L1>(USER_BOUNDARY)..512 {
dst[l1i] = unsafe { PageEntry::from_raw(tables.l1.data[l1i]) };
dst[l1i] = unsafe { fixed::KERNEL_L1[l1i] };
}
}
+14 -1
View File
@@ -42,7 +42,7 @@ impl EntryLevel for L1 {
#[repr(C, align(0x1000))]
pub struct PageTable<L: EntryLevel> {
entries: [PageEntry<L>; 512],
pub(crate) entries: [PageEntry<L>; 512],
}
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -204,6 +204,19 @@ impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
}
impl<L: NonTerminalEntryLevel> PageEntry<L> {
pub const fn identity_block(address: PhysicalAddress) -> Self {
Self(
(address.into_u64() >> 2)
| PageAttributes::R.bits()
| PageAttributes::W.bits()
| PageAttributes::X.bits()
| PageAttributes::V.bits()
| PageAttributes::D.bits()
| PageAttributes::A.bits(),
PhantomData,
)
}
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
// TODO validate address alignment
Self(
+5 -5
View File
@@ -14,11 +14,11 @@ pub use aarch64::{AArch64 as PlatformImpl, L3, PLATFORM};
// pub mod x86_64;
// #[cfg(any(target_arch = "x86_64", rust_analyzer))]
// pub use x86_64::{PLATFORM, X86_64 as PlatformImpl};
//
// #[cfg(any(target_arch = "riscv64", rust_analyzer))]
// pub mod riscv64;
// #[cfg(any(target_arch = "riscv64", rust_analyzer))]
// pub use riscv64::{Riscv64 as PlatformImpl, PLATFORM};
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub mod riscv64;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub use riscv64::{Riscv64 as PlatformImpl, L3, PLATFORM};
//
// #[cfg(not(any(
// target_arch = "x86_64",
+49 -54
View File
@@ -1,82 +1,77 @@
// vi:ft=asm:
.macro LOAD_PCREL label, register, symbol
\label: auipc \register, %pcrel_hi(\symbol)
addi \register, \register, %pcrel_lo(\label)
.global __riscv64_entry
.global __riscv64_ap_entry
.macro LOAD_PCREL label, reg, sym
\label: auipc \reg, %pcrel_hi(\sym)
addi \reg, \reg, %pcrel_lo(\label)
.endm
.pushsection .text.entry
.option push
.option rvc
.global __rv64_entry
.global __boot_header
.global __rv64_secondary_entry
.type __rv64_entry, @function
.type __boot_header, @object
__boot_header:
__rv64_entry:
// Look <s>ma</s>u-boot, I'm Linux!
.ascii "MZ" // Magic 0
j __rv64_real_entry // Jump to real entry (if entered by non-Linux bootloader)
__riscv64_entry:
.ascii "MZ" // Magic 0
j __riscv64_real_entry // Jump to real entry (if entered by non-Linux bootloader)
.long 0
.quad 0x200000 // Offset from RAM start
.quad __kernel_size // Image size
.quad 0 // Kernel flags
.long 0x2 // Header version
.quad 0x200000 // Offset from RAM start
.quad __kernel_size // Image size
.quad 0 // Kernel flags
.long 0x2 // Header version
.long 0
.quad 0
.ascii "RISCV\x00\x00\x00" // Magic 1
.ascii "RSC\x05" // Magic 2
.ascii "RISCV\x00\x00\x00" // Magic 1
.ascii "RSC\x05" // Magic 2
.long 0
.size __rv64_entry, . - __rv64_entry
.size __boot_header, . - __boot_header
.option pop
.option push
.option norvc
.type __rv64_real_entry, @function
__rv64_real_entry:
// a0 - bootstrap HART ID
// a1 - device tree blob
// mhartid == a0
__riscv64_real_entry:
csrw satp, zero
mv tp, zero
// Zero the .bss
LOAD_PCREL .L00, t0, __bss_start_phys
LOAD_PCREL .L01, t1, __bss_end_phys
// a0 - bootstrap HART ID
// a1 - device tree blob
LOAD_PCREL .L00, s0, __riscv64_entry
mv s1, a0
mv s2, a1
//// Zero the .bss
LOAD_PCREL .L01, t0, __bss_start
LOAD_PCREL .L02, t1, __bss_end
1: bgeu t0, t1, 2f
sd zero, (t0)
addi t0, t0, 4
addi t0, t0, 8
j 1b
2:
// Setup boot stack and entry point
LOAD_PCREL .L02, sp, {boot_stack_bottom} + {boot_stack_size} - {kernel_virt_offset}
LOAD_PCREL .L03, t0, {entry_smode_lower} - {kernel_virt_offset}
//// Store boot parameters
LOAD_PCREL .L03, t0, {kernel_load_base}
LOAD_PCREL .L04, t1, {boot_hart_id}
LOAD_PCREL .L05, t2, {dtb_address}
sd s0, (t0)
sd s1, (t1)
sd s2, (t2)
jr t0
.size __rv64_real_entry, . - __rv64_real_entry
//// Setup a stack
LOAD_PCREL .L06, sp, {boot_stack_bottom} + {boot_stack_size}
.type __rv64_secondary_entry, @function
__rv64_secondary_entry:
//// Relocate the kernel
mv a0, s0
LOAD_PCREL .L07, a1, __rela_start
LOAD_PCREL .L08, a2, __rela_end
jal {relocate_kernel}
//// Enter S-mode kernel
j {bsp_smode_entry}
.popsection // .text.entry
.pushsection .text
__riscv64_ap_entry:
// a1 - context struct
csrw satp, zero
mv tp, zero
// Setup stack and jump to Rust entry code
// Setup stack and enter Rust code
ld sp, (a1)
mv a0, a1
mv a1, sp
LOAD_PCREL .L04, t0, {entry_smode_secondary_lower} - {kernel_virt_offset}
jr t0
.size __rv64_secondary_entry, . - __rv64_secondary_entry
.option pop
.popsection
j {ap_smode_entry}
.popsection // .text
+80 -54
View File
@@ -1,13 +1,13 @@
//! RISC-V boot code
use core::{
arch::global_asm,
sync::atomic::{compiler_fence, Ordering},
sync::atomic::{self, compiler_fence, Ordering},
};
use kernel_arch::Architecture;
use kernel_arch_riscv64::{
mem::{self, KERNEL_VIRT_OFFSET},
ArchitectureImpl, CPU_COUNT,
};
use elf::{abi, relocation::Elf64_Rela};
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_riscv64::{mem, CPU_COUNT};
use libk::{
debug,
fs::{devfs, sysfs},
@@ -18,12 +18,19 @@ use libk_mm::{
PageBox,
};
use crate::{kernel_main, kernel_secondary_main};
use super::{smp::SecondaryContext, PLATFORM};
use crate::{
arch::{riscv64::smp::SecondaryContext, PLATFORM},
kernel_main, kernel_secondary_main,
mem::KERNEL_VIRT_OFFSET,
};
const BOOT_STACK_SIZE: usize = 65536;
static mut KERNEL_LOAD_BASE: u64 = 0;
static mut DTB_ADDRESS: PhysicalAddress = PhysicalAddress::ZERO;
static mut BOOT_HART_ID: u64 = 0;
static mut BOOT_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
#[repr(C, align(0x10))]
struct BootStack<const N: usize>([u8; N]);
@@ -33,35 +40,59 @@ impl<const N: usize> BootStack<N> {
}
}
#[link_section = ".bss"]
static mut BOOT_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
unsafe extern "C" fn relocate_kernel(image_base: i64, rela_start: usize, rela_end: usize) {
let rela_count = (rela_end - rela_start) / size_of::<Elf64_Rela>();
let rela_ptr: *const Elf64_Rela = core::ptr::with_exposed_provenance(rela_start);
let rela_table = core::slice::from_raw_parts(rela_ptr, rela_count);
unsafe fn long_jump(pc: usize, sp: usize, a0: usize, a1: usize) -> ! {
core::arch::asm!(r#"
mv sp, {sp}
jr {pc}
"#,
in("a0") a0,
in("a1") a1,
pc = in(reg) pc,
sp = in(reg) sp,
options(noreturn)
);
for rela in rela_table {
let slot: *mut i64 =
core::ptr::with_exposed_provenance_mut(rela.r_offset as usize + image_base as usize);
let value = match rela.r_info as u32 {
abi::R_RISCV_RELATIVE => rela.r_addend + image_base,
_ => loop {},
};
slot.write_volatile(value);
}
}
unsafe extern "C" fn __rv64_bsp_entry_upper(bsp_hart_id: u64, dtb_physical: PhysicalAddress) -> ! {
/// Returns the ID of the bootstrap HART
pub fn boot_hart_id() -> u64 {
unsafe { BOOT_HART_ID }
}
unsafe fn long_jump(pc: usize, sp: usize, a0: usize) -> ! {
core::arch::asm!("mv sp, {sp}; jr {pc}", in("a0") a0, sp = in(reg) sp, pc = in(reg) pc, options(noreturn))
}
unsafe extern "C" fn bsp_entry_upper() -> ! {
// Relocate the kernel again
extern "C" {
static __rela_start: u8;
static __rela_end: u8;
}
atomic::fence(Ordering::SeqCst);
let rela_start = (&raw const __rela_start).addr();
let rela_end = (&raw const __rela_end).addr();
let image_base = (KERNEL_VIRT_OFFSET as u64 + KERNEL_LOAD_BASE) as i64;
relocate_kernel(image_base, rela_start, rela_end);
atomic::fence(Ordering::SeqCst);
debug::init_logger();
super::debug::register_sbi_debug();
log::info!("Starting riscv64 upper half");
if dtb_physical.is_zero() {
if DTB_ADDRESS.is_zero() {
log::error!("No device tree provided");
// No DTB provided
ArchitectureImpl::halt();
}
if let Err(error) = PLATFORM.init_memory_management(dtb_physical) {
if let Err(error) = PLATFORM.init_memory_management(DTB_ADDRESS) {
log::error!("Failed to initialize memory management: {error:?}");
ArchitectureImpl::halt();
}
@@ -71,7 +102,7 @@ unsafe extern "C" fn __rv64_bsp_entry_upper(bsp_hart_id: u64, dtb_physical: Phys
runtime::init_task_queue();
if let Err(error) = PLATFORM.init_platform(bsp_hart_id as _, 0, true) {
if let Err(error) = PLATFORM.init_platform(BOOT_HART_ID as u32, 0, true) {
log::error!("Failed to initialize the platform: {error:?}");
ArchitectureImpl::halt();
}
@@ -79,7 +110,14 @@ unsafe extern "C" fn __rv64_bsp_entry_upper(bsp_hart_id: u64, dtb_physical: Phys
kernel_main()
}
unsafe extern "C" fn __rv64_secondary_entry_upper(context: PhysicalAddress) -> ! {
unsafe extern "C" fn bsp_smode_entry() -> ! {
mem::enable_mmu();
let pc = bsp_entry_upper as usize + KERNEL_VIRT_OFFSET;
let sp = (&raw const BOOT_STACK).addr() + BOOT_STACK_SIZE + KERNEL_VIRT_OFFSET;
long_jump(pc, sp, 0)
}
unsafe extern "C" fn ap_smode_upper(context: PhysicalAddress) -> ! {
let hart_id = {
let context = PageBox::<SecondaryContext>::from_physical_raw(context);
context.hart_id
@@ -94,39 +132,27 @@ unsafe extern "C" fn __rv64_secondary_entry_upper(context: PhysicalAddress) -> !
kernel_secondary_main()
}
unsafe extern "C" fn __rv64_bsp_smode_entry_lower(a0: usize, a1: usize) -> ! {
ArchitectureImpl::set_interrupt_mask(true);
unsafe extern "C" fn ap_smode_entry(context: PhysicalAddress, sp: PhysicalAddress) -> ! {
compiler_fence(Ordering::SeqCst);
mem::enable_mmu();
let stack = (&raw const BOOT_STACK).addr() + KERNEL_VIRT_OFFSET;
let pc = __rv64_bsp_entry_upper as usize + KERNEL_VIRT_OFFSET;
let sp = stack + BOOT_STACK_SIZE;
long_jump(pc, sp, a0, a1)
}
unsafe extern "C" fn __rv64_secondary_smode_entry_lower(
context: PhysicalAddress,
sp: PhysicalAddress,
) -> ! {
compiler_fence(Ordering::SeqCst);
let pc = ap_smode_upper as usize + KERNEL_VIRT_OFFSET;
let sp = sp.virtualize();
ArchitectureImpl::set_interrupt_mask(true);
compiler_fence(Ordering::Release);
mem::enable_mmu();
compiler_fence(Ordering::Acquire);
let pc = __rv64_secondary_entry_upper as usize + KERNEL_VIRT_OFFSET;
long_jump(pc, sp, context.into_usize(), 0)
long_jump(pc, sp, context.into_usize())
}
global_asm!(
include_str!("entry.S"),
entry_smode_lower = sym __rv64_bsp_smode_entry_lower,
entry_smode_secondary_lower = sym __rv64_secondary_smode_entry_lower,
dtb_address = sym DTB_ADDRESS,
kernel_load_base = sym KERNEL_LOAD_BASE,
boot_hart_id = sym BOOT_HART_ID,
boot_stack_bottom = sym BOOT_STACK,
kernel_virt_offset = const KERNEL_VIRT_OFFSET,
boot_stack_size = const BOOT_STACK_SIZE,
bsp_smode_entry = sym bsp_smode_entry,
ap_smode_entry = sym ap_smode_entry,
relocate_kernel = sym relocate_kernel,
);
+4
View File
@@ -1,7 +1,10 @@
//! Debugging utilties specific to RISC-V
use abi::error::Error;
use kernel_arch_riscv64::sbi;
use libk::debug::{self, DebugSink, LogLevel};
/// SBI-based debug sink
pub struct SbiDebugConsole;
impl DebugSink for SbiDebugConsole {
@@ -17,6 +20,7 @@ impl DebugSink for SbiDebugConsole {
static SBI_DEBUG: SbiDebugConsole = SbiDebugConsole;
/// Sets up a SBI-based debug log sink
pub fn register_sbi_debug() {
debug::add_early_sink(&SBI_DEBUG, LogLevel::Debug);
}
+4
View File
@@ -1,3 +1,4 @@
//! RISC-V exception handling functions
use core::arch::global_asm;
use abi::{arch::SavedFrame, primitive_enum, process::Signal, SyscallFunction};
@@ -13,6 +14,7 @@ use crate::syscall;
use super::{smp, timer};
primitive_enum! {
#[allow(missing_docs)]
pub enum Cause: usize {
MisalignedInstruction = 0,
InstructionAccessFault = 1,
@@ -33,6 +35,7 @@ primitive_enum! {
#[derive(Debug)]
#[repr(C)]
#[allow(missing_docs)]
pub struct TrapFrame {
// General-purpose
pub ra: usize,
@@ -49,6 +52,7 @@ pub struct TrapFrame {
pub tp: usize,
}
/// Sets up kernel exception handling
pub fn init_smode_exceptions() {
extern "C" {
static __rv64_smode_trap_vectors: u8;
+29 -54
View File
@@ -1,79 +1,55 @@
#![allow(missing_docs)]
use core::sync::atomic::{self, AtomicU32, Ordering};
//! 64-bit RISC-V platform implementation
use core::sync::atomic::{self, Ordering};
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
interrupt::{IpiDeliveryTarget, IpiMessage},
ResetDevice,
};
use device_tree::{
driver::{unflatten_device_tree, InitSequence},
DeviceTree, DeviceTreeNodeExt,
};
use kernel_arch::Architecture;
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_riscv64::{
mem::{self, KERNEL_VIRT_OFFSET},
mem,
registers::{SIE, SSTATUS},
ArchitectureImpl, PerCpuData,
PerCpuData,
};
use libk::{arch::Cpu, config};
use libk_mm::{
address::PhysicalAddress,
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
pointer::PhysicalRef,
table::{EntryLevel, EntryLevelExt},
table::EntryLevelExt,
};
use libk_util::OneTimeInit;
use tock_registers::interfaces::ReadWriteable;
use ygg_driver_pci::PciBusManager;
use crate::{
arch::Platform,
device::MACHINE_NAME,
fs::{Initrd, INITRD_DATA},
util::call_init_array,
};
use super::Platform;
pub mod boot;
pub mod debug;
pub mod exception;
pub mod smp;
pub mod timer;
pub static BOOT_HART_ID: AtomicU32 = AtomicU32::new(u32::MAX);
/// Alias for terminal-level page tables
pub type L3 = mem::table::L3;
/// 64-bit RISC-V platform struct
pub struct Riscv64 {
dt: OneTimeInit<DeviceTree<'static>>,
initrd: OneTimeInit<PhysicalRef<'static, [u8]>>,
}
pub static PLATFORM: Riscv64 = Riscv64 {
dt: OneTimeInit::new(),
initrd: OneTimeInit::new(),
};
#[derive(Debug, Clone, Copy)]
pub struct L3;
impl EntryLevel for L3 {
const SIZE: usize = 4096;
const SHIFT: usize = 12;
}
impl Platform for Riscv64 {
const KERNEL_VIRT_OFFSET: usize = KERNEL_VIRT_OFFSET;
type L3 = L3;
unsafe fn reset(&self) -> ! {
ArchitectureImpl::halt();
}
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<bool, Error> {
smp::send_ipi(target, msg)?;
Ok(true)
}
unsafe fn start_application_processors(&self) {
// TODO asymmetric systems with different hart types are not yet supported.
// e.g., in JH7110 there're two different types of cores
@@ -82,36 +58,37 @@ impl Platform for Riscv64 {
log::error!("Couldn't start secondary harts: {error:?}");
}
}
fn register_reset_device(&self, reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
let _ = reset;
Ok(())
}
}
/// 64-bit RISC-V platform
pub static PLATFORM: Riscv64 = Riscv64 {
dt: OneTimeInit::new(),
initrd: OneTimeInit::new(),
};
impl Riscv64 {
unsafe fn init_memory_management(&'static self, dtb: PhysicalAddress) -> Result<(), Error> {
// Unmap the lower half
mem::setup_fixed_tables();
unsafe fn init_memory_management(
&'static self,
dtb_address: PhysicalAddress,
) -> Result<(), Error> {
let dtb = PhysicalRef::<u8>::map_slice(dtb_address, DeviceTree::MIN_HEADER_SIZE);
let dtb_size = DeviceTree::read_totalsize(&dtb[..]).map_err(|_| Error::InvalidArgument)?;
// Extract the size of the device tree
let dtb_size = {
let dtb_header = PhysicalRef::<u8>::map_slice(dtb, DeviceTree::MIN_HEADER_SIZE);
DeviceTree::read_totalsize(dtb_header.as_ref()).map_err(|_| Error::InvalidArgument)?
};
// // Unmap the lower half
// mem::setup_fixed_tables();
log::debug!("DTB: {:#x?}", dtb..dtb.add(dtb_size));
log::info!("DTB: {:#x?}", dtb_address..dtb_address.add(dtb_size));
reserve_region(
"dtb",
PhysicalMemoryRegion {
base: dtb,
base: dtb_address,
size: (dtb_size + 0xFFF) & !0xFFF,
},
);
let dtb_slice = PhysicalRef::<u8>::map_slice(dtb, dtb_size);
let dt = DeviceTree::from_raw(dtb_slice.as_ptr() as usize)?;
let dtb = PhysicalRef::<u8>::map_slice(dtb_address, dtb_size);
let dt = DeviceTree::from_raw(dtb.as_ptr().addr())?;
// Reserve memory regions specified in the DTB
log::info!("Reserved memory:");
@@ -139,7 +116,7 @@ impl Riscv64 {
}
// Initialize the physical memory
phys::init_from_iter(dt.memory_regions(), |_, _, _| Ok(()))?;
phys::init_from_iter(dt.memory_regions())?;
self.dt.init(dt);
@@ -184,8 +161,6 @@ impl Riscv64 {
exception::init_smode_exceptions();
if is_bsp {
BOOT_HART_ID.store(hart_id, Ordering::Release);
call_init_array();
atomic::compiler_fence(Ordering::SeqCst);
+15 -6
View File
@@ -1,9 +1,13 @@
//! RISC-V SMP implementation
use core::{mem::MaybeUninit, sync::atomic::Ordering};
use abi::error::Error;
use device_api::interrupt::{IpiDeliveryTarget, IpiMessage};
use device_tree::{DeviceTree, DeviceTreeNodeExt};
use kernel_arch_riscv64::{mem, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT};
use kernel_arch_riscv64::{
mem::auto_lower_address, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT,
};
use libk::arch::Cpu;
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
@@ -11,11 +15,13 @@ use libk_mm::{
};
use tock_registers::interfaces::ReadWriteable;
use crate::{arch::riscv64::BOOT_HART_ID, panic};
use crate::{arch::riscv64::boot::boot_hart_id, panic};
#[allow(missing_docs)]
pub const SECONDARY_STACK_SIZE: usize = 32768;
#[repr(C)]
#[allow(missing_docs)]
pub struct SecondaryContext {
// 0x00
pub stack_top: PhysicalAddress,
@@ -26,10 +32,10 @@ pub struct SecondaryContext {
fn start_secondary_hart(hart_id: u64) -> Result<(), Error> {
extern "C" {
fn __rv64_secondary_entry();
fn __riscv64_ap_entry();
}
let start_addr = __rv64_secondary_entry as usize;
let start_addr = auto_lower_address(__riscv64_ap_entry as *const fn());
let stack = PageBox::<u8>::new_uninit_slice(SECONDARY_STACK_SIZE)?;
let stack_top = unsafe { PageBox::as_physical_address(&stack).add(SECONDARY_STACK_SIZE) };
let context = PageBox::new(SecondaryContext {
@@ -56,10 +62,11 @@ fn start_secondary_hart(hart_id: u64) -> Result<(), Error> {
Ok(())
}
/// Main function to setup all secondary HARTs
pub fn start_secondary_harts(dt: &DeviceTree) -> Result<(), Error> {
log::info!("Setting up secondary harts");
let boot_hart_id = BOOT_HART_ID.load(Ordering::Acquire);
let boot_hart_id = boot_hart_id() as u32;
let cpus = dt.find_absolute("/cpus").ok_or(Error::DoesNotExist)?;
// Find the boot hart
@@ -104,11 +111,12 @@ pub fn start_secondary_harts(dt: &DeviceTree) -> Result<(), Error> {
}
// Can get rid of lower half now
unsafe { mem::unmap_lower_half() };
// unsafe { mem::unmap_lower_half() };
Ok(())
}
/// Sends an IPI to the target HART(s)
pub fn send_ipi(target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error> {
let local = Cpu::local();
@@ -130,6 +138,7 @@ pub fn send_ipi(target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error>
sbi::sbi_send_ipi(hart_mask, 0)
}
/// Handles received IPIs
pub fn handle_ipi() {
let local = Cpu::local();
if let Some(message) = local.get_ipi() {
+5
View File
@@ -1,3 +1,4 @@
//! RISC-V system timer driver
use core::sync::atomic::{AtomicU64, Ordering};
use abi::time::NANOSECONDS_IN_SECOND;
@@ -6,13 +7,16 @@ use kernel_arch_riscv64::{intrinsics, registers::SIE, sbi};
use libk::{arch::Cpu, task::runtime, time};
use tock_registers::interfaces::ReadWriteable;
/// Timer value at last tick
pub static LAST_TICK: AtomicU64 = AtomicU64::new(0);
/// Timer frequency
pub static FREQUENCY: AtomicU64 = AtomicU64::new(0);
// 1kHz
const TICK_RATE: u64 = 1000;
// TODO use stimecmp instead of sbi calls if sstc extension is available
/// Handles incoming timer interrupts
pub fn handle_interrupt() {
let frequency = FREQUENCY.load(Ordering::Acquire);
@@ -37,6 +41,7 @@ pub fn handle_interrupt() {
unsafe { Cpu::local().scheduler().yield_cpu() };
}
/// Sets up a timer for the calling HART
pub fn init_hart(is_bsp: bool) {
let frequency = FREQUENCY.load(Ordering::Acquire);
+3 -4
View File
@@ -1,5 +1,4 @@
//! RISC-V PLIC driver
use core::sync::atomic::Ordering;
use abi::{error::Error, primitive_enum};
use alloc::{sync::Arc, vec::Vec};
@@ -25,7 +24,7 @@ use tock_registers::{
registers::{ReadOnly, ReadWrite},
};
use crate::arch::riscv64::BOOT_HART_ID;
use crate::arch::riscv64::boot::boot_hart_id;
const MAX_IRQS: usize = 1024;
@@ -138,7 +137,7 @@ impl ExternalInterruptController for Plic {
fn enable_irq(&self, irq: Irq) -> Result<(), Error> {
// TODO balance IRQs between harts?
let irq = self.validate_irq(irq)?;
let bsp_hart_id = BOOT_HART_ID.load(Ordering::Acquire);
let bsp_hart_id = boot_hart_id() as u32;
let context = self
.hart_context(bsp_hart_id)
.ok_or(Error::InvalidArgument)
@@ -159,7 +158,7 @@ impl ExternalInterruptController for Plic {
_options: IrqOptions,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let bsp_hart_id = BOOT_HART_ID.load(Ordering::Acquire);
let bsp_hart_id = boot_hart_id() as u32;
let irq = self.validate_irq(irq)?;
let context = self
.hart_context(bsp_hart_id)
-1
View File
@@ -3,7 +3,6 @@
if_let_guard,
step_trait,
decl_macro,
naked_functions,
optimize_attribute,
const_trait_impl,
maybe_uninit_slice,