rv64: boot into upper half

This commit is contained in:
2025-01-17 02:25:49 +02:00
parent 07458f33e4
commit 86eb2d3252
36 changed files with 1262 additions and 20 deletions
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "kernel-arch-riscv64"
version = "0.1.0"
edition = "2024"
[dependencies]
yggdrasil-abi.workspace = true
kernel-arch-interface.workspace = true
libk-mm-interface.workspace = true
memtables.workspace = true
device-api = { workspace = true, features = ["derive"] }
tock-registers.workspace = true
bitflags.workspace = true
static_assertions.workspace = true
[lints]
workspace = true
+54
View File
@@ -0,0 +1,54 @@
use core::marker::PhantomData;
use kernel_arch_interface::{
mem::{KernelTableManager, PhysicalMemoryAllocator},
task::{TaskContext, UserContextInfo},
};
use libk_mm_interface::address::PhysicalAddress;
use yggdrasil_abi::error::Error;
pub struct TaskContextImpl<K, PA> {
_pd: PhantomData<(K, PA)>,
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContext<K, PA> for TaskContextImpl<K, PA>
{
const USER_STACK_EXTRA_ALIGN: usize = 0;
const SIGNAL_STACK_EXTRA_ALIGN: usize = 0;
fn user(context: UserContextInfo) -> Result<Self, Error> {
let _ = context;
todo!()
}
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
let _ = entry;
let _ = arg;
todo!()
}
fn set_thread_pointer(&self, tp: usize) {
let _ = tp;
todo!()
}
fn align_stack_for_entry(sp: usize) -> usize {
let _ = sp;
todo!()
}
unsafe fn enter(&self) -> ! {
todo!()
}
unsafe fn switch(&self, from: &Self) {
let _ = from;
todo!()
}
unsafe fn switch_and_drop(&self, thread: *const ()) {
let _ = thread;
todo!()
}
}
+109
View File
@@ -0,0 +1,109 @@
#![feature(decl_macro)]
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
cpu::{CpuImpl, IpiQueue},
task::Scheduler,
Architecture,
};
pub mod mem;
pub use mem::{KernelTableManagerImpl, ProcessAddressSpaceImpl};
pub mod context;
pub use context::TaskContextImpl;
use registers::MSTATUS;
use tock_registers::interfaces::{ReadWriteable, Readable};
pub mod registers;
pub struct ArchitectureImpl;
impl Architecture for ArchitectureImpl {
type PerCpuData = ();
type CpuFeatures = ();
type BreakpointType = u32;
const BREAKPOINT_VALUE: Self::BreakpointType = 0;
fn halt() -> ! {
loop {}
}
unsafe fn set_local_cpu(cpu: *mut ()) {
let _ = cpu;
loop {}
}
fn local_cpu() -> *mut () {
loop {}
}
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
let _ = id;
let _ = data;
loop {}
}
unsafe fn init_ipi_queues(queues: Vec<IpiQueue<Self>>) {
let _ = queues;
loop {}
}
fn ipi_queue(cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
let _ = cpu_id;
loop {}
}
#[inline]
unsafe fn set_interrupt_mask(mask: bool) -> bool {
let old = Self::interrupt_mask();
if mask {
MSTATUS.modify(MSTATUS::MIE::CLEAR);
} else {
MSTATUS.modify(MSTATUS::MIE::SET);
}
old
}
#[inline]
fn interrupt_mask() -> bool {
MSTATUS.matches_all(MSTATUS::MIE::SET)
}
fn wait_for_interrupt() {
loop {}
}
fn cpu_count() -> usize {
loop {}
}
fn cpu_index<S: Scheduler + 'static>() -> u32 {
loop {}
}
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
let _ = cpu;
loop {}
}
fn cpu_available_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
let _ = cpu;
loop {}
}
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
loop {}
}
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
loop {}
}
fn idle_task() -> extern "C" fn(usize) -> ! {
loop {}
}
}
+120
View File
@@ -0,0 +1,120 @@
use core::marker::PhantomData;
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
split_spinlock,
};
use libk_mm_interface::{
address::PhysicalAddress,
process::ProcessAddressSpaceManager,
table::{page_index, MapAttributes, TableAllocator},
};
use static_assertions::const_assert_eq;
use table::{L1, L2};
use yggdrasil_abi::error::Error;
pub use memtables::riscv64::FixedTables;
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()) };
}
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFFF0_00000000;
pub const KERNEL_PHYS_BASE: usize = 0x80000000;
pub const SIGN_EXTEND_MASK: usize = 0xFFFFFFC0_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_START_L1I, 450);
const_assert_eq!(KERNEL_L2I, 0);
/// Any VAs above this one are sign-extended
pub const USER_BOUNDARY: usize = 0x40_00000000;
#[derive(Debug)]
pub struct KernelTableManagerImpl;
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
_pd: PhantomData<TA>,
}
impl KernelTableManager for KernelTableManagerImpl {
fn virtualize(phys: u64) -> usize {
let _ = phys;
loop {}
}
fn physicalize(virt: usize) -> u64 {
let _ = virt;
loop {}
}
unsafe fn map_device_pages(
base: u64,
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
let _ = base;
let _ = count;
let _ = attrs;
loop {}
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
let _ = mapping;
loop {}
}
unsafe fn unmap_physical_address(virt: usize) {
let _ = virt;
loop {}
}
}
impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceImpl<TA> {
const LOWER_LIMIT_PFN: usize = 0;
const UPPER_LIMIT_PFN: usize = 0;
fn new() -> Result<Self, Error> {
todo!()
}
unsafe fn map_page(
&mut self,
address: usize,
physical: PhysicalAddress,
flags: MapAttributes,
) -> Result<(), Error> {
let _ = address;
let _ = physical;
let _ = flags;
todo!()
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
let _ = address;
todo!()
}
fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
let _ = address;
todo!()
}
fn as_address_with_asid(&self) -> u64 {
todo!()
}
unsafe fn clear(&mut self) {
todo!()
}
}
+169
View File
@@ -0,0 +1,169 @@
use core::{
marker::PhantomData,
ops::{Index, IndexMut},
};
use bitflags::bitflags;
use libk_mm_interface::{
address::PhysicalAddress,
pointer::{PhysicalRef, PhysicalRefMut},
table::{EntryLevel, NextPageTable, NonTerminalEntryLevel, TableAllocator},
};
use yggdrasil_abi::error::Error;
use super::KernelTableManagerImpl;
bitflags! {
pub struct PageAttributes: u64 {
const N = 1 << 63;
/// Dirty bit
const D = 1 << 7;
/// Access bit
const A = 1 << 6;
/// Global mapping bit, implies all lower levels are also global
const G = 1 << 5;
/// U-mode access permission
const U = 1 << 4;
/// Execute permission
const X = 1 << 3;
/// Write permission
const W = 1 << 2;
/// Read-permission
const R = 1 << 1;
/// Valid bit
const V = 1 << 0;
}
// X W R Meaning
// 0 0 0 Pointer to next level of page table
// 0 0 1 Read-only page
// 0 1 0 ---
// 0 1 1 Read-write page
// 1 0 0 Execute only
// 1 0 1 Read-execute page
// 1 1 0 ---
// 1 1 1 Read-write-execute page
}
/// L3 - entry is 4KiB
#[derive(Debug, Clone, Copy)]
pub struct L3;
/// L2 - entry is 2MiB
#[derive(Debug, Clone, Copy)]
pub struct L2;
/// L1 - entry is 1GiB
#[derive(Debug, Clone, Copy)]
pub struct L1;
impl EntryLevel for L3 {
const SHIFT: usize = 12;
}
impl EntryLevel for L2 {
const SHIFT: usize = 21;
}
impl EntryLevel for L1 {
const SHIFT: usize = 30;
}
#[repr(C, align(0x1000))]
pub struct PageTable<L: EntryLevel> {
entries: [PageEntry<L>; 512],
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PageEntry<L: EntryLevel>(u64, PhantomData<L>);
impl NonTerminalEntryLevel for L1 {
type NextLevel = L2;
}
impl NonTerminalEntryLevel for L2 {
type NextLevel = L3;
}
impl<L: EntryLevel> PageTable<L> {
pub const fn zeroed() -> Self {
Self {
entries: [PageEntry::INVALID; 512],
}
}
}
impl<L: EntryLevel> PageEntry<L> {
pub const INVALID: Self = Self(0, PhantomData);
pub const fn is_present(&self) -> bool {
self.0 & PageAttributes::V.bits() != 0
}
pub fn attributes(self) -> PageAttributes {
PageAttributes::from_bits_retain(self.0)
}
}
impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
type NextLevel = PageTable<L::NextLevel>;
type TableRef = PhysicalRef<'static, PageTable<L::NextLevel>, KernelTableManagerImpl>;
type TableRefMut = PhysicalRefMut<'static, PageTable<L::NextLevel>, KernelTableManagerImpl>;
fn get(&self, _index: usize) -> Option<Self::TableRef> {
loop {}
}
fn get_mut(&mut self, _index: usize) -> Option<Self::TableRefMut> {
loop {}
}
fn get_mut_or_alloc<TA: TableAllocator>(
&mut self,
_index: usize,
) -> Result<Self::TableRefMut, Error> {
loop {}
}
}
impl<L: NonTerminalEntryLevel> PageEntry<L> {
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
// TODO validate address alignment
Self(
(address.into_u64() >> 2) | (PageAttributes::R | PageAttributes::V | attrs).bits(),
PhantomData,
)
}
pub fn table(address: PhysicalAddress, mut attrs: PageAttributes) -> Self {
attrs.remove(PageAttributes::R | PageAttributes::W | PageAttributes::X);
Self(
(address.into_u64() >> 2) | (PageAttributes::V | attrs).bits(),
PhantomData,
)
}
}
impl PageEntry<L3> {
pub fn page(address: PhysicalAddress, attrs: PageAttributes) -> Self {
Self(
(address.into_u64() >> 2) | (PageAttributes::R | PageAttributes::V | attrs).bits(),
PhantomData,
)
}
pub fn as_page(&self) -> Option<PhysicalAddress> {
loop {}
}
}
impl<L: EntryLevel> Index<usize> for PageTable<L> {
type Output = PageEntry<L>;
fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}
impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.entries[index]
}
}
+183
View File
@@ -0,0 +1,183 @@
macro impl_csr_read($struct:ident, $repr:ty, $reg:ident, $register:ty) {
impl tock_registers::interfaces::Readable for $struct {
type T = $repr;
type R = $register;
#[inline]
fn get(&self) -> $repr {
let mut value: $repr;
unsafe {
core::arch::asm!(concat!("csrr {0}, ", stringify!($reg)), out(reg) value);
}
value
}
}
}
macro impl_csr_write($struct:ident, $repr:ty, $reg:ident, $register:ty) {
impl tock_registers::interfaces::Writeable for $struct {
type T = $repr;
type R = $register;
#[inline]
fn set(&self, value: $repr) {
unsafe {
core::arch::asm!(concat!("csrw ", stringify!($reg), ", {0}"), in(reg) value);
}
}
}
}
pub mod misa {
use tock_registers::{interfaces::Readable, register_bitfields};
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub MISA [
A OFFSET(0) NUMBITS(1) [],
C OFFSET(2) NUMBITS(1) [],
D OFFSET(3) NUMBITS(1) [],
E OFFSET(4) NUMBITS(1) [],
F OFFSET(5) NUMBITS(1) [],
H OFFSET(6) NUMBITS(1) [],
I OFFSET(7) NUMBITS(1) [],
M OFFSET(12) NUMBITS(1) [],
Q OFFSET(16) NUMBITS(1) [],
S OFFSET(17) NUMBITS(1) [],
U OFFSET(18) NUMBITS(1) [],
X OFFSET(23) NUMBITS(1) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, misa, MISA::Register);
impl_csr_write!(Reg, u64, misa, MISA::Register);
impl Reg {
pub fn is_valid(&self) -> bool {
self.get() != 0
}
}
pub const MISA: Reg = Reg;
}
pub mod mstatus {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub MSTATUS [
/// Interrupt enable for S-mode
SIE OFFSET(1) NUMBITS(1) [],
/// Interrupt enable for M-mode
MIE OFFSET(3) NUMBITS(1) [],
/// Stored SIE state on S-mode trap delegation
SPIE OFFSET(5) NUMBITS(1) [],
/// U-mode big endian
UBE OFFSET(6) NUMBITS(1) [],
/// TODO: something written here on trap to M-mode
MPIE OFFSET(7) NUMBITS(1) [],
/// TODO: something for nested traps
SPP OFFSET(8) NUMBITS(1) [],
/// Vector register dirty status
VS OFFSET(9) NUMBITS(2) [],
/// Original mode before being trapped into M-mode
MPP OFFSET(11) NUMBITS(2) [
U = 0,
S = 1,
M = 3
],
/// Float register dirty status
FS OFFSET(13) NUMBITS(2) [],
/// U-mode extension dirty status
XS OFFSET(15) NUMBITS(2) [],
/// Effective privilege mode at which loads and stores execute.
///
/// When MPRV = 0, loads and stores behave as normal
/// MPRV = 1, loads/stores are translated and protected
MPRV OFFSET(17) NUMBITS(1) [],
/// Permit supervisor user memory access
///
/// When SUM = 0, S-mode access to pages accessible by U-mode will fault
SUM OFFSET(18) NUMBITS(1) [],
MXR OFFSET(19) NUMBITS(1) [],
/// Trap virtual memory
///
/// When TVM = 1, attempts to read/write satp CSR, execute sfence.vma or sinval.vma
/// in S-mode will raise an illegal instruction exception
TVM OFFSET(20) NUMBITS(1) [],
/// Timeout wait
///
/// When TW = 1, wfi executed in lower privilege level which does not complete
/// within some implementation-specific timeout, raises an illegal
/// instruction exception
TW OFFSET(21) NUMBITS(1) [],
TSR OFFSET(22) NUMBITS(1) [],
/// U-mode XLEN value
UXL OFFSET(32) NUMBITS(2) [],
/// S-mode XLEN value
SXL OFFSET(34) NUMBITS(2) [],
/// S-mode big endian
SBE OFFSET(36) NUMBITS(1) [],
/// M-mode big endian
MBE OFFSET(37) NUMBITS(1) [],
SD OFFSET(63) NUMBITS(1) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, mstatus, MSTATUS::Register);
impl_csr_write!(Reg, u64, mstatus, MSTATUS::Register);
pub const MSTATUS: Reg = Reg;
}
pub mod mepc {
use super::{impl_csr_read, impl_csr_write};
pub struct Reg;
impl_csr_read!(Reg, u64, mepc, ());
impl_csr_write!(Reg, u64, mepc, ());
pub const MEPC: Reg = Reg;
}
pub mod satp {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub SATP [
PPN OFFSET(0) NUMBITS(44) [],
ASID OFFSET(44) NUMBITS(16) [],
MODE OFFSET(60) NUMBITS(4) [
Bare = 0,
Sv39 = 8,
Sv48 = 9,
Sv57 = 10,
Sv64 = 11,
],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, satp, SATP::Register);
impl_csr_write!(Reg, u64, satp, SATP::Register);
pub const SATP: Reg = Reg;
}
pub use mepc::MEPC;
pub use misa::MISA;
pub use mstatus::MSTATUS;
pub use satp::SATP;