rv64: platform init, task switching
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#[inline]
|
||||
pub fn rdtime() -> u64 {
|
||||
let mut output: u64;
|
||||
unsafe { core::arch::asm!("rdtime {0}", out(reg) output) };
|
||||
output
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) };
|
||||
}
|
||||
Reference in New Issue
Block a user