rv64: platform init, task switching

This commit is contained in:
2025-01-19 15:16:26 +02:00
parent f46f3ddc31
commit 20fa34c945
42 changed files with 2244 additions and 568 deletions
+1
View File
@@ -13,6 +13,7 @@ device-api = { workspace = true, features = ["derive"] }
tock-registers.workspace = true
bitflags.workspace = true
static_assertions.workspace = true
log.workspace = true
[lints]
workspace = true
+110
View File
@@ -0,0 +1,110 @@
// vi:ft=asm:
.section .text
.macro SAVE_TASK_STATE
addi sp, sp, -{context_size}
sd ra, 0 * 8(sp)
sd gp, 1 * 8(sp)
sd s11, 2 * 8(sp)
sd s10, 3 * 8(sp)
sd s9, 4 * 8(sp)
sd s8, 5 * 8(sp)
sd s7, 6 * 8(sp)
sd s6, 7 * 8(sp)
sd s5, 8 * 8(sp)
sd s4, 9 * 8(sp)
sd s3, 10 * 8(sp)
sd s2, 11 * 8(sp)
sd s1, 12 * 8(sp)
sd s0, 13 * 8(sp)
.endm
.macro LOAD_TASK_STATE
ld ra, 0 * 8(sp)
ld gp, 1 * 8(sp)
ld s11, 2 * 8(sp)
ld s10, 3 * 8(sp)
ld s9, 4 * 8(sp)
ld s8, 5 * 8(sp)
ld s7, 6 * 8(sp)
ld s6, 7 * 8(sp)
ld s5, 8 * 8(sp)
ld s4, 9 * 8(sp)
ld s3, 10 * 8(sp)
ld s2, 11 * 8(sp)
ld s1, 12 * 8(sp)
ld s0, 13 * 8(sp)
addi sp, sp, {context_size}
.endm
.option push
.option norvc
.global __rv64_task_enter_kernel
.global __rv64_task_enter_user
.global __rv64_switch_task
.global __rv64_switch_task_and_drop
.global __rv64_enter_task
// Context switching
.type __rv64_enter_task, @function
__rv64_enter_task:
// a0 - task ctx
ld sp, (a0)
LOAD_TASK_STATE
ret
.size __rv64_enter_task, . - __rv64_enter_task
.type __rv64_switch_task, @function
__rv64_switch_task:
// a0 - destination task ctx
// a1 - source task ctx
SAVE_TASK_STATE
sd sp, (a1)
ld sp, (a0)
LOAD_TASK_STATE
ret
.size __rv64_switch_task, . - __rv64_switch_task
.type __rv64_switch_task_and_drop, @function
__rv64_switch_task_and_drop:
// a0 - destination task ctx
// a1 - thread struct to drop
ld sp, (a0)
mv a0, a1
call __arch_drop_thread
LOAD_TASK_STATE
ret
.size __rv64_switch_task_and_drop, . - __rv64_switch_task_and_drop
// Entry functions
.type __rv64_task_enter_kernel, @function
__rv64_task_enter_kernel:
ld a0, (sp) // argument
ld ra, 8(sp) // entry
addi sp, sp, 16
// Set SPIE to enable interrupts
// Set SPP = 1 to indicate a return to S-mode
csrr t0, sstatus
ori t0, t0, (1 << 5)
ori t0, t0, (1 << 8)
csrw sstatus, t0
csrw sepc, ra
sret
.size __rv64_task_enter_kernel, . - __rv64_task_enter_kernel
.type __rv64_task_enter_user, @function
__rv64_task_enter_user:
// TODO
j .
.size __rv64_task_enter_user, . - __rv64_task_enter_user
.option pop
+117 -11
View File
@@ -1,16 +1,42 @@
use core::marker::PhantomData;
use core::{arch::global_asm, cell::UnsafeCell, marker::PhantomData};
use kernel_arch_interface::{
mem::{KernelTableManager, PhysicalMemoryAllocator},
task::{TaskContext, UserContextInfo},
task::{StackBuilder, TaskContext, UserContextInfo},
};
use libk_mm_interface::address::PhysicalAddress;
use yggdrasil_abi::error::Error;
pub struct TaskContextImpl<K, PA> {
pub const CONTEXT_SIZE: usize = 14 * size_of::<usize>();
#[repr(C, align(0x10))]
struct TaskContextInner {
// 0x00
sp: usize,
}
pub struct TaskContextImpl<
K: KernelTableManager,
PA: PhysicalMemoryAllocator<Address = PhysicalAddress>,
> {
inner: UnsafeCell<TaskContextInner>,
// fp_context: UnsafeCell<FpContext>,
stack_base_phys: PhysicalAddress,
stack_size: usize,
_pd: PhantomData<(K, PA)>,
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContextImpl<K, PA>
{
unsafe fn load_state(&self) {
// TODO load new SATP value
}
unsafe fn store_state(&self) {}
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContext<K, PA> for TaskContextImpl<K, PA>
{
@@ -23,9 +49,31 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
}
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
let _ = entry;
let _ = arg;
todo!()
const KERNEL_TASK_PAGES: usize = 8;
let stack_base_phys = PA::allocate_contiguous_pages(KERNEL_TASK_PAGES)?;
let stack_base = stack_base_phys.raw_virtualize::<K>();
let mut stack = StackBuilder::new(stack_base, KERNEL_TASK_PAGES * 0x1000);
// Entry and argument
stack.push(entry as _);
stack.push(arg);
setup_common_context(&mut stack, __rv64_task_enter_kernel as _);
let sp = stack.build();
// TODO stack is leaked
log::info!("stack = {:#x}", stack_base);
Ok(Self {
inner: UnsafeCell::new(TaskContextInner { sp }),
// fp_context: UnsafeCell::new(FpContext::new()),
stack_base_phys,
stack_size: KERNEL_TASK_PAGES * 0x1000,
_pd: PhantomData,
})
}
fn set_thread_pointer(&self, tp: usize) {
@@ -39,16 +87,74 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
}
unsafe fn enter(&self) -> ! {
todo!()
unsafe {
self.load_state();
__rv64_enter_task(self.inner.get())
}
}
unsafe fn switch(&self, from: &Self) {
let _ = from;
todo!()
if core::ptr::addr_eq(self, from) {
return;
}
unsafe {
from.store_state();
self.load_state();
__rv64_switch_task(self.inner.get(), from.inner.get())
}
}
unsafe fn switch_and_drop(&self, thread: *const ()) {
let _ = thread;
todo!()
unsafe {
self.load_state();
__rv64_switch_task_and_drop(self.inner.get(), thread)
}
}
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>> Drop
for TaskContextImpl<K, PA>
{
fn drop(&mut self) {
assert_eq!(self.stack_size % 0x1000, 0);
for offset in (0..self.stack_size).step_by(0x1000) {
unsafe {
PA::free_page(self.stack_base_phys.add(offset));
}
}
}
}
fn setup_common_context(builder: &mut StackBuilder, entry: usize) {
builder.push(0); // x8/s0/fp
builder.push(0); // x9/s1
builder.push(0); // x18/s2
builder.push(0); // x19/s3
builder.push(0); // x20/s4
builder.push(0); // x21/s5
builder.push(0); // x22/s6
builder.push(0); // x23/s7
builder.push(0); // x24/s8
builder.push(0); // x25/s9
builder.push(0); // x26/s10
builder.push(0); // x27/s11
builder.push(0); // x4/gp
builder.push(entry); // x1/ra return address
}
unsafe extern "C" {
fn __rv64_enter_task(to: *mut TaskContextInner) -> !;
fn __rv64_switch_task(to: *mut TaskContextInner, from: *mut TaskContextInner);
fn __rv64_switch_task_and_drop(to: *mut TaskContextInner, thread: *const ()) -> !;
fn __rv64_task_enter_kernel();
fn __rv64_task_enter_user();
// fn __rv64_fp_store_context(to: *mut c_void);
// fn __rv64_fp_restore_context(from: *const c_void);
}
global_asm!(
include_str!("context.S"),
context_size = const CONTEXT_SIZE,
);
+6
View File
@@ -0,0 +1,6 @@
#[inline]
pub fn rdtime() -> u64 {
let mut output: u64;
unsafe { core::arch::asm!("rdtime {0}", out(reg) output) };
output
}
+64 -21
View File
@@ -1,55 +1,95 @@
#![feature(decl_macro)]
#![feature(decl_macro, naked_functions)]
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
use alloc::{boxed::Box, vec::Vec};
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
cpu::{CpuImpl, IpiQueue},
cpu::{CpuData, CpuImpl, IpiQueue},
task::Scheduler,
Architecture,
};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use registers::{SSCRATCH, SSTATUS};
pub mod mem;
pub use mem::{KernelTableManagerImpl, ProcessAddressSpaceImpl};
pub use mem::{process::ProcessAddressSpaceImpl, KernelTableManagerImpl};
pub mod context;
pub use context::TaskContextImpl;
use registers::MSTATUS;
use tock_registers::interfaces::{ReadWriteable, Readable};
pub mod intrinsics;
pub mod registers;
pub mod sbi;
pub struct ArchitectureImpl;
#[repr(C)]
pub struct PerCpuData {
// Used in assembly
pub tmp_t0: usize, // 0x00
pub smode_sp: usize, // 0x08
// Used elsewhere
pub bootstrap: bool,
pub queue_index: usize,
}
impl CpuData for PerCpuData {
fn is_bootstrap(&self, id: u32) -> bool {
let _ = id;
self.bootstrap
}
fn queue_index(&self, id: u32) -> usize {
let _ = id;
self.queue_index
}
}
#[naked]
extern "C" fn idle_task(_: usize) -> ! {
unsafe {
core::arch::naked_asm!("1: nop; j 1b");
}
}
impl Architecture for ArchitectureImpl {
type PerCpuData = ();
type PerCpuData = PerCpuData;
type CpuFeatures = ();
type BreakpointType = u32;
const BREAKPOINT_VALUE: Self::BreakpointType = 0;
fn halt() -> ! {
loop {}
loop {
unsafe { Self::set_interrupt_mask(true) };
Self::wait_for_interrupt();
}
}
unsafe fn set_local_cpu(cpu: *mut ()) {
let _ = cpu;
loop {}
SSCRATCH.set(cpu.addr() as u64);
unsafe { core::arch::asm!("mv tp, {0}", in(reg) cpu) };
}
#[inline]
fn local_cpu() -> *mut () {
loop {}
let value: u64;
unsafe { core::arch::asm!("mv {0}, tp", out(reg) value) };
value as _
}
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
let _ = id;
let _ = data;
loop {}
let id = id.expect("riscv64 requires an explicit HART ID in its per-processor struct");
let cpu = Box::leak(Box::new(CpuImpl::<Self, S>::new(id, data)));
unsafe { cpu.set_local() };
}
unsafe fn init_ipi_queues(queues: Vec<IpiQueue<Self>>) {
// TODO
let _ = queues;
loop {}
// loop {}
}
fn ipi_queue(cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
@@ -61,24 +101,27 @@ impl Architecture for ArchitectureImpl {
unsafe fn set_interrupt_mask(mask: bool) -> bool {
let old = Self::interrupt_mask();
if mask {
MSTATUS.modify(MSTATUS::MIE::CLEAR);
SSTATUS.modify(SSTATUS::SIE::CLEAR);
} else {
MSTATUS.modify(MSTATUS::MIE::SET);
SSTATUS.modify(SSTATUS::SIE::SET);
}
old
}
#[inline]
fn interrupt_mask() -> bool {
MSTATUS.matches_all(MSTATUS::MIE::SET)
SSTATUS.matches_all(SSTATUS::SIE::CLEAR)
}
fn wait_for_interrupt() {
loop {}
unsafe {
core::arch::asm!("wfi");
}
}
fn cpu_count() -> usize {
loop {}
// TODO
1
}
fn cpu_index<S: Scheduler + 'static>() -> u32 {
@@ -104,6 +147,6 @@ impl Architecture for ArchitectureImpl {
}
fn idle_task() -> extern "C" fn(usize) -> ! {
loop {}
idle_task
}
}
+214 -57
View File
@@ -1,20 +1,22 @@
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},
table::{page_index, EntryLevel, EntryLevelExt},
};
use static_assertions::const_assert_eq;
use table::{L1, L2};
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::registers::SATP;
pub mod process;
pub mod table;
split_spinlock! {
@@ -28,34 +30,53 @@ split_spinlock! {
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
}
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFFF0_00000000;
pub const KERNEL_VIRT_OFFSET: usize = kernel_arch_interface::KERNEL_VIRT_OFFSET;
pub const KERNEL_PHYS_BASE: usize = 0x80000000;
pub const SIGN_EXTEND_MASK: usize = 0xFFFFFFC0_00000000;
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_START_L1I, 450);
const_assert_eq!(KERNEL_L2I, 0);
// 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;
#[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 virtualize(address: u64) -> usize {
let address = address as usize;
if address >= RAM_MAPPING_OFFSET {
panic!("Invalid physical address: {address:#x}");
}
address + RAM_MAPPING_OFFSET
}
fn physicalize(virt: usize) -> u64 {
let _ = virt;
loop {}
fn physicalize(address: usize) -> u64 {
if address < RAM_MAPPING_OFFSET {
panic!("Invalid \"physicalized\" virtual address {address:#x}");
}
(address - RAM_MAPPING_OFFSET) as u64
}
unsafe fn map_device_pages(
@@ -63,58 +84,194 @@ impl KernelTableManager for KernelTableManagerImpl {
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
let _ = base;
let _ = count;
let _ = attrs;
loop {}
unsafe { map_device_memory(PhysicalAddress::from_u64(base), count, attrs) }
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
let _ = mapping;
loop {}
}
unsafe fn unmap_physical_address(virt: usize) {
let _ = virt;
loop {}
unsafe { unmap_device_memory(mapping) }
}
}
impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceImpl<TA> {
const LOWER_LIMIT_PFN: usize = 0;
const UPPER_LIMIT_PFN: usize = 0;
// 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
fn new() -> Result<Self, Error> {
todo!()
'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);
}
tlb_flush_va(DEVICE_MAPPING_OFFSET + l2i * L2::SIZE + l3i * L3::SIZE);
}
return Ok(DEVICE_MAPPING_OFFSET + i * L3::SIZE);
}
unsafe fn map_page(
&mut self,
address: usize,
physical: PhysicalAddress,
flags: MapAttributes,
) -> Result<(), Error> {
let _ = address;
let _ = physical;
let _ = flags;
todo!()
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);
// tlb_flush_vaae1(DEVICE_MAPPING_OFFSET + (i + j) * L2::SIZE);
}
}
return Ok(DEVICE_MAPPING_OFFSET + i * L2::SIZE);
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
let _ = address;
todo!()
}
Err(Error::OutOfMemory)
}
fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
let _ = address;
todo!()
}
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>();
fn as_address_with_asid(&self) -> u64 {
todo!()
}
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 fn clear(&mut self) {
todo!()
unsafe {
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
let address = base_address + l2_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
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(
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_vaae1(page);
}
}
L2::SIZE => todo!(),
_ => unimplemented!(),
}
}
pub fn auto_address<T>(x: *const T) -> usize {
let x = x.addr();
if x >= KERNEL_VIRT_OFFSET {
x - KERNEL_VIRT_OFFSET
} else {
x
}
}
pub unsafe fn enable_mmu() {
let l1_phys = auto_address(&raw const KERNEL_TABLES) as u64;
SATP.write(SATP::PPN.val(l1_phys >> 12) + SATP::MODE::Sv39);
}
// Also unmaps the lower half
pub unsafe fn setup_fixed_tables() {
let kernel_l1i_lower = page_index::<L1>(KERNEL_PHYS_BASE);
let mut tables = KERNEL_TABLES.lock();
let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
// Unmap the lower half
tables.l1.data[kernel_l1i_lower] = 0;
// 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();
// tlb_flush_vaae1(DEVICE_MAPPING_OFFSET);
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::V).bits();
}
// tlb_flush_all()
}
pub fn tlb_flush_va(va: usize) {
unsafe {
core::arch::asm!("sfence.vma zero, {0}", in(reg) va);
}
}
+51
View File
@@ -0,0 +1,51 @@
use core::marker::PhantomData;
use libk_mm_interface::{
address::PhysicalAddress,
process::ProcessAddressSpaceManager,
table::{MapAttributes, TableAllocator},
};
use yggdrasil_abi::error::Error;
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
_pd: PhantomData<TA>,
}
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!()
}
}
+1 -32
View File
@@ -3,7 +3,6 @@ use core::{
ops::{Index, IndexMut},
};
use bitflags::bitflags;
use libk_mm_interface::{
address::PhysicalAddress,
pointer::{PhysicalRef, PhysicalRefMut},
@@ -13,37 +12,7 @@ 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
}
pub use memtables::riscv64::PageAttributes;
/// L3 - entry is 4KiB
#[derive(Debug, Clone, Copy)]
+409 -184
View File
@@ -27,184 +27,312 @@ macro impl_csr_write($struct:ident, $repr:ty, $reg:ident, $register:ty) {
}
}
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 mtvec {
use tock_registers::{interfaces::ReadWriteable, register_bitfields};
use super::{impl_csr_read, impl_csr_write};
pub struct Reg;
register_bitfields!(
u64,
pub MTVEC [
MODE OFFSET(0) NUMBITS(2) [
Direct = 0,
Vectored = 1
],
BASE OFFSET(2) NUMBITS(62) [],
]
);
impl_csr_read!(Reg, u64, mtvec, MTVEC::Register);
impl_csr_write!(Reg, u64, mtvec, MTVEC::Register);
impl Reg {
pub fn set_base(&self, base: usize) {
debug_assert_eq!(base & 0xF, 0);
let mask = match base & 63 != 0 {
false => 0,
true => 0x3 << 62,
};
self.modify(MTVEC::BASE.val(((base as u64) >> 2) | mask));
}
}
pub const MTVEC: Reg = Reg;
}
pub mod medeleg {
use super::{impl_csr_read, impl_csr_write};
pub struct Reg;
impl_csr_read!(Reg, u64, medeleg, ());
impl_csr_write!(Reg, u64, medeleg, ());
pub const MEDELEG: Reg = Reg;
}
pub mod mideleg {
use super::{impl_csr_read, impl_csr_write};
pub struct Reg;
impl_csr_read!(Reg, u64, mideleg, ());
impl_csr_write!(Reg, u64, mideleg, ());
pub const MIDELEG: Reg = Reg;
}
// 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 mtvec {
// use tock_registers::{interfaces::ReadWriteable, register_bitfields};
//
// use super::{impl_csr_read, impl_csr_write};
//
// pub struct Reg;
//
// register_bitfields!(
// u64,
// pub MTVEC [
// MODE OFFSET(0) NUMBITS(2) [
// Direct = 0,
// Vectored = 1
// ],
// BASE OFFSET(2) NUMBITS(62) [],
// ]
// );
//
// impl_csr_read!(Reg, u64, mtvec, MTVEC::Register);
// impl_csr_write!(Reg, u64, mtvec, MTVEC::Register);
//
// impl Reg {
// pub fn set_base(&self, base: usize) {
// debug_assert_eq!(base & 0xF, 0);
// let mask = match base & 63 != 0 {
// false => 0,
// true => 0x3 << 62,
// };
// self.modify(MTVEC::BASE.val(((base as u64) >> 2) | mask));
// }
// }
//
// pub const MTVEC: Reg = Reg;
// }
//
// pub mod medeleg {
// use tock_registers::register_bitfields;
//
// use super::{impl_csr_read, impl_csr_write};
//
// pub struct Reg;
//
// register_bitfields!(
// u64,
// pub MEDELEG [
// ECALL_SMODE OFFSET(9) NUMBITS(1) [],
// ]
// );
//
// impl_csr_read!(Reg, u64, medeleg, MEDELEG::Register);
// impl_csr_write!(Reg, u64, medeleg, MEDELEG::Register);
//
// pub const MEDELEG: Reg = Reg;
// }
//
// pub mod mideleg {
// use super::{impl_csr_read, impl_csr_write, MIE};
//
// pub struct Reg;
//
// impl_csr_read!(Reg, u64, mideleg, MIE::Register);
// impl_csr_write!(Reg, u64, mideleg, MIE::Register);
//
// pub const MIDELEG: Reg = Reg;
// }
//
// pub mod mcause {
// use tock_registers::register_bitfields;
//
// use super::{impl_csr_read, impl_csr_write};
//
// register_bitfields!(
// u64,
// pub MCAUSE [
// CODE OFFSET(0) NUMBITS(63) [],
// INTERRUPT OFFSET(63) NUMBITS(1) [],
// ]
// );
//
// pub struct Reg;
//
// impl_csr_read!(Reg, u64, mcause, MCAUSE::Register);
// impl_csr_write!(Reg, u64, mcause, MCAUSE::Register);
//
// pub const MCAUSE: Reg = Reg;
// }
//
// pub mod mie {
// use tock_registers::register_bitfields;
//
// use super::{impl_csr_read, impl_csr_write};
//
// register_bitfields!(
// u64,
// pub MIE [
// /// ???
// SSIE OFFSET(1) NUMBITS(1) [],
// /// ???
// MSIE OFFSET(3) NUMBITS(1) [],
// /// S-mode timer enable
// STIE OFFSET(5) NUMBITS(1) [],
// /// M-mode timer enable
// MTIE OFFSET(7) NUMBITS(1) [],
// /// S-mode external interrupt enable
// SEIE OFFSET(9) NUMBITS(1) [],
// /// M-mode external interrupt enable
// MEIE OFFSET(11) NUMBITS(1) [],
// ]
// );
//
// pub struct Reg;
//
// impl_csr_read!(Reg, u64, mie, MIE::Register);
// impl_csr_write!(Reg, u64, mie, MIE::Register);
//
// pub const MIE: Reg = Reg;
// }
//
// pub mod mip {
// use tock_registers::register_bitfields;
//
// use super::{impl_csr_read, impl_csr_write};
//
// register_bitfields!(
// u64,
// pub MIP [
// /// ???
// SSIP OFFSET(1) NUMBITS(1) [],
// /// ???
// MSIP OFFSET(3) NUMBITS(1) [],
// /// S-mode timer pending
// STIP OFFSET(5) NUMBITS(1) [],
// /// M-mode timer pending
// MTIP OFFSET(7) NUMBITS(1) [],
// /// S-mode external interrupt pending
// SEIP OFFSET(9) NUMBITS(1) [],
// /// M-mode external interrupt pending
// MEIP OFFSET(11) NUMBITS(1) [],
// ]
// );
//
// pub struct Reg;
//
// impl_csr_read!(Reg, u64, mip, MIP::Register);
// impl_csr_write!(Reg, u64, mip, MIP::Register);
//
// pub const MIP: Reg = Reg;
// }
//
// pub mod mcounteren {
// use tock_registers::register_bitfields;
//
// use super::{impl_csr_read, impl_csr_write};
//
// register_bitfields!(
// u64,
// pub MCOUNTEREN [
// /// Enable reading cycle counter from S-mode
// CY OFFSET(1) NUMBITS(1) [],
// /// Enable reading time counter from S-mode
// TM OFFSET(2) NUMBITS(1) [],
// /// Enable reading instret counter from S-mode
// IR OFFSET(3) NUMBITS(1) [],
// ]
// );
//
// pub struct Reg;
//
// impl_csr_read!(Reg, u64, mcounteren, MCOUNTEREN::Register);
// impl_csr_write!(Reg, u64, mcounteren, MCOUNTEREN::Register);
//
// pub const MCOUNTEREN: Reg = Reg;
// }
//
// pub mod mscratch {
// use super::{impl_csr_read, impl_csr_write};
//
// pub struct Reg;
//
// impl_csr_read!(Reg, u64, mscratch, ());
// impl_csr_write!(Reg, u64, mscratch, ());
//
// pub const MSCRATCH: Reg = Reg;
// }
pub mod satp {
use tock_registers::register_bitfields;
@@ -290,6 +418,17 @@ pub mod scause {
pub const SCAUSE: Reg = Reg;
}
pub mod stval {
use super::{impl_csr_read, impl_csr_write};
pub struct Reg;
impl_csr_read!(Reg, u64, stval, ());
impl_csr_write!(Reg, u64, stval, ());
pub const STVAL: Reg = Reg;
}
pub mod sepc {
use super::{impl_csr_read, impl_csr_write};
@@ -301,13 +440,99 @@ pub mod sepc {
pub const SEPC: Reg = Reg;
}
pub use medeleg::MEDELEG;
pub use mepc::MEPC;
pub use mideleg::MIDELEG;
pub use misa::MISA;
pub use mstatus::MSTATUS;
pub use mtvec::MTVEC;
pub mod sstatus {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub SSTATUS [
SIE OFFSET(1) NUMBITS(1) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, sstatus, SSTATUS::Register);
impl_csr_write!(Reg, u64, sstatus, SSTATUS::Register);
pub const SSTATUS: Reg = Reg;
}
pub mod sscratch {
use super::{impl_csr_read, impl_csr_write};
pub struct Reg;
impl_csr_read!(Reg, u64, sscratch, ());
impl_csr_write!(Reg, u64, sscratch, ());
pub const SSCRATCH: Reg = Reg;
}
pub mod sip {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub SIP [
SSIP OFFSET(1) NUMBITS(1) [],
STIP OFFSET(5) NUMBITS(1) [],
SEIP OFFSET(9) NUMBITS(1) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, sip, SIP::Register);
impl_csr_write!(Reg, u64, sip, SIP::Register);
pub const SIP: Reg = Reg;
}
pub mod sie {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub SIE [
SSIE OFFSET(1) NUMBITS(1) [],
STIE OFFSET(5) NUMBITS(1) [],
SEIE OFFSET(9) NUMBITS(1) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, sie, SIE::Register);
impl_csr_write!(Reg, u64, sie, SIE::Register);
pub const SIE: Reg = Reg;
}
// pub use mcause::MCAUSE;
// pub use mcounteren::MCOUNTEREN;
// pub use medeleg::MEDELEG;
// pub use mepc::MEPC;
// pub use mideleg::MIDELEG;
// pub use mie::MIE;
// pub use mip::MIP;
// pub use misa::MISA;
// pub use mscratch::MSCRATCH;
// pub use mstatus::MSTATUS;
// pub use mtvec::MTVEC;
pub use satp::SATP;
pub use scause::SCAUSE;
pub use sepc::SEPC;
pub use sie::SIE;
pub use sip::SIP;
pub use sscratch::SSCRATCH;
pub use sstatus::SSTATUS;
pub use stval::STVAL;
pub use stvec::STVEC;
+35
View File
@@ -0,0 +1,35 @@
unsafe fn sbi_do_call(
extension: u64,
function: u64,
mut a0: u64,
mut a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
) {
unsafe {
core::arch::asm!(
"ecall",
inlateout("a0") a0,
inlateout("a1") a1,
in("a2") a2,
in("a3") a3,
in("a4") a4,
in("a5") a5,
in("a6") function,
in("a7") extension,
);
}
// TODO return `struct sbiret`
let _ = a0;
let _ = a1;
}
pub fn sbi_debug_console_write_byte(byte: u8) {
unsafe { sbi_do_call(0x4442434E, 0x02, byte as u64, 0, 0, 0, 0, 0) };
}
pub fn sbi_set_timer(next_event: u64) {
unsafe { sbi_do_call(0x54494D45, 0x00, next_event, 0, 0, 0, 0, 0) };
}