riscv64: position-independent kernel
This commit is contained in:
@@ -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 = .);
|
||||
};
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
"panic-strategy": "abort",
|
||||
"dynamic-linking": true,
|
||||
"relocation-model": "pic",
|
||||
"position-independent-executables": true,
|
||||
"code-model": "medium",
|
||||
"eh-frame-header": false,
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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) };
|
||||
}
|
||||
@@ -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] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
if_let_guard,
|
||||
step_trait,
|
||||
decl_macro,
|
||||
naked_functions,
|
||||
optimize_attribute,
|
||||
const_trait_impl,
|
||||
maybe_uninit_slice,
|
||||
|
||||
Reference in New Issue
Block a user