rv64: boot into usermode
This commit is contained in:
parent
20fa34c945
commit
8ba37c9762
@ -83,6 +83,7 @@ pub struct UserContextInfo {
|
||||
pub stack_pointer: usize,
|
||||
pub thread_pointer: usize,
|
||||
pub address_space: u64,
|
||||
pub asid: u64,
|
||||
pub single_step: bool,
|
||||
}
|
||||
|
||||
|
@ -97,14 +97,32 @@ __rv64_task_enter_kernel:
|
||||
|
||||
csrw sstatus, t0
|
||||
csrw sepc, ra
|
||||
csrw sscratch, zero
|
||||
|
||||
sret
|
||||
.size __rv64_task_enter_kernel, . - __rv64_task_enter_kernel
|
||||
|
||||
.type __rv64_task_enter_user, @function
|
||||
__rv64_task_enter_user:
|
||||
// TODO
|
||||
j .
|
||||
csrw sscratch, tp
|
||||
|
||||
ld a0, 0 * 8(sp) // argument
|
||||
ld ra, 1 * 8(sp) // entry
|
||||
ld tp, 2 * 8(sp) // thread pointer
|
||||
ld sp, 3 * 8(sp) // user stack
|
||||
|
||||
// Set SPIE to enable interrupts
|
||||
// Set SPP = 0 to indicate a return to U-mode
|
||||
li t1, (1 << 8)
|
||||
not t1, t1
|
||||
|
||||
csrr t0, sstatus
|
||||
ori t0, t0, (1 << 5)
|
||||
and t0, t0, t1
|
||||
csrw sstatus, t0
|
||||
csrw sepc, ra
|
||||
|
||||
sret
|
||||
.size __rv64_task_enter_user, . - __rv64_task_enter_user
|
||||
|
||||
.option pop
|
||||
|
@ -3,16 +3,29 @@ use core::{arch::global_asm, cell::UnsafeCell, marker::PhantomData};
|
||||
use kernel_arch_interface::{
|
||||
mem::{KernelTableManager, PhysicalMemoryAllocator},
|
||||
task::{StackBuilder, TaskContext, UserContextInfo},
|
||||
Architecture,
|
||||
};
|
||||
use libk_mm_interface::address::PhysicalAddress;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
registers::InMemoryRegister,
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
mem::{self, KERNEL_VIRT_OFFSET},
|
||||
registers::SATP,
|
||||
ArchitectureImpl, PerCpuData,
|
||||
};
|
||||
|
||||
pub const CONTEXT_SIZE: usize = 14 * size_of::<usize>();
|
||||
|
||||
#[repr(C, align(0x10))]
|
||||
struct TaskContextInner {
|
||||
// 0x00
|
||||
sp: usize,
|
||||
|
||||
satp: InMemoryRegister<u64, SATP::Register>,
|
||||
}
|
||||
|
||||
pub struct TaskContextImpl<
|
||||
@ -22,6 +35,7 @@ pub struct TaskContextImpl<
|
||||
inner: UnsafeCell<TaskContextInner>,
|
||||
// fp_context: UnsafeCell<FpContext>,
|
||||
stack_base_phys: PhysicalAddress,
|
||||
stack_top: usize,
|
||||
stack_size: usize,
|
||||
|
||||
_pd: PhantomData<(K, PA)>,
|
||||
@ -32,6 +46,15 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
{
|
||||
unsafe fn load_state(&self) {
|
||||
// TODO load new SATP value
|
||||
let inner = unsafe { &*self.inner.get() };
|
||||
let cpu = unsafe { &mut *ArchitectureImpl::local_cpu().cast::<PerCpuData>() };
|
||||
|
||||
// Copy new SATP
|
||||
let satp = inner.satp.get();
|
||||
if satp != SATP.get() {
|
||||
SATP.set(satp);
|
||||
}
|
||||
cpu.smode_sp = self.stack_top;
|
||||
}
|
||||
|
||||
unsafe fn store_state(&self) {}
|
||||
@ -40,12 +63,46 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
|
||||
TaskContext<K, PA> for TaskContextImpl<K, PA>
|
||||
{
|
||||
const USER_STACK_EXTRA_ALIGN: usize = 0;
|
||||
const USER_STACK_EXTRA_ALIGN: usize = 8;
|
||||
const SIGNAL_STACK_EXTRA_ALIGN: usize = 0;
|
||||
|
||||
fn user(context: UserContextInfo) -> Result<Self, Error> {
|
||||
let _ = context;
|
||||
todo!()
|
||||
const USER_TASK_PAGES: usize = 16;
|
||||
let stack_base_phys = PA::allocate_contiguous_pages(USER_TASK_PAGES)?;
|
||||
let stack_base = stack_base_phys.raw_virtualize::<K>();
|
||||
|
||||
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
|
||||
|
||||
log::info!(
|
||||
"Set up user task: pc={:#x}, sp={:#x}, tp={:#x}",
|
||||
context.entry,
|
||||
context.stack_pointer,
|
||||
context.thread_pointer
|
||||
);
|
||||
stack.push(context.stack_pointer);
|
||||
stack.push(context.thread_pointer);
|
||||
stack.push(context.entry);
|
||||
stack.push(context.argument);
|
||||
|
||||
setup_common_context(&mut stack, __rv64_task_enter_user as _);
|
||||
|
||||
let sp = stack.build();
|
||||
let satp = InMemoryRegister::new(0);
|
||||
satp.write(
|
||||
SATP::MODE::Sv39
|
||||
+ SATP::ASID.val(context.asid)
|
||||
+ SATP::PPN.val(context.address_space >> 12),
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
inner: UnsafeCell::new(TaskContextInner { sp, satp }),
|
||||
// fp_context: UnsafeCell::new(FpContext::new()),
|
||||
stack_base_phys,
|
||||
stack_top: stack_base + USER_TASK_PAGES * 0x1000,
|
||||
stack_size: USER_TASK_PAGES * 0x1000,
|
||||
|
||||
_pd: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
|
||||
@ -65,11 +122,16 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
|
||||
// TODO stack is leaked
|
||||
log::info!("stack = {:#x}", stack_base);
|
||||
let satp = InMemoryRegister::new(0);
|
||||
let kernel_table_phys =
|
||||
((&raw const mem::KERNEL_TABLES).addr() - KERNEL_VIRT_OFFSET) as u64;
|
||||
satp.write(SATP::MODE::Sv39 + SATP::ASID.val(0) + SATP::PPN.val(kernel_table_phys >> 12));
|
||||
|
||||
Ok(Self {
|
||||
inner: UnsafeCell::new(TaskContextInner { sp }),
|
||||
inner: UnsafeCell::new(TaskContextInner { sp, satp }),
|
||||
// fp_context: UnsafeCell::new(FpContext::new()),
|
||||
stack_base_phys,
|
||||
stack_top: 0,
|
||||
stack_size: KERNEL_TASK_PAGES * 0x1000,
|
||||
|
||||
_pd: PhantomData,
|
||||
|
@ -10,9 +10,9 @@ use kernel_arch_interface::{
|
||||
task::Scheduler,
|
||||
Architecture,
|
||||
};
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable};
|
||||
|
||||
use registers::{SSCRATCH, SSTATUS};
|
||||
use registers::SSTATUS;
|
||||
|
||||
pub mod mem;
|
||||
pub use mem::{process::ProcessAddressSpaceImpl, KernelTableManagerImpl};
|
||||
@ -28,7 +28,8 @@ pub struct ArchitectureImpl;
|
||||
pub struct PerCpuData {
|
||||
// Used in assembly
|
||||
pub tmp_t0: usize, // 0x00
|
||||
pub smode_sp: usize, // 0x08
|
||||
pub umode_sp: usize, // 0x08
|
||||
pub smode_sp: usize, // 0x10
|
||||
|
||||
// Used elsewhere
|
||||
pub bootstrap: bool,
|
||||
@ -69,7 +70,6 @@ impl Architecture for ArchitectureImpl {
|
||||
}
|
||||
|
||||
unsafe fn set_local_cpu(cpu: *mut ()) {
|
||||
SSCRATCH.set(cpu.addr() as u64);
|
||||
unsafe { core::arch::asm!("mv tp, {0}", in(reg) cpu) };
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ impl Architecture for ArchitectureImpl {
|
||||
|
||||
fn ipi_queue(cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
|
||||
let _ = cpu_id;
|
||||
loop {}
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -125,25 +125,25 @@ impl Architecture for ArchitectureImpl {
|
||||
}
|
||||
|
||||
fn cpu_index<S: Scheduler + 'static>() -> u32 {
|
||||
loop {}
|
||||
CpuImpl::<Self, S>::local().id()
|
||||
}
|
||||
|
||||
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
|
||||
let _ = cpu;
|
||||
loop {}
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn cpu_available_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
|
||||
let _ = cpu;
|
||||
loop {}
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
|
||||
loop {}
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
|
||||
loop {}
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn idle_task() -> extern "C" fn(usize) -> ! {
|
||||
|
@ -229,13 +229,23 @@ pub fn auto_address<T>(x: *const T) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables the memory translation.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only meant to be called once per each HART during their early init.
|
||||
pub unsafe fn enable_mmu() {
|
||||
let l1_phys = auto_address(&raw const KERNEL_TABLES) as u64;
|
||||
|
||||
SATP.write(SATP::PPN.val(l1_phys >> 12) + SATP::MODE::Sv39);
|
||||
}
|
||||
|
||||
// Also unmaps the lower half
|
||||
/// Sets up run-time kernel translation tables and removed the lower-half mapping.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure MMU is already enabled and that lower-half addresses will no
|
||||
/// longer be referred to.
|
||||
pub unsafe fn setup_fixed_tables() {
|
||||
let kernel_l1i_lower = page_index::<L1>(KERNEL_PHYS_BASE);
|
||||
let mut tables = KERNEL_TABLES.lock();
|
||||
@ -275,3 +285,16 @@ pub fn tlb_flush_va(va: usize) {
|
||||
core::arch::asm!("sfence.vma zero, {0}", in(reg) va);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
|
||||
unsafe {
|
||||
core::arch::asm!("sfence.vma {0}, {1}", in(reg) asid, in(reg) va);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone_kernel_tables(dst: &mut PageTable<L1>) {
|
||||
let tables = KERNEL_TABLES.lock();
|
||||
for l1i in page_index::<L1>(USER_BOUNDARY)..512 {
|
||||
dst[l1i] = unsafe { PageEntry::from_raw(tables.l1.data[l1i]) };
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,56 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
sync::atomic::{AtomicU16, Ordering},
|
||||
};
|
||||
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
pointer::PhysicalRefMut,
|
||||
process::ProcessAddressSpaceManager,
|
||||
table::{MapAttributes, TableAllocator},
|
||||
table::{EntryLevel, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator},
|
||||
};
|
||||
use memtables::riscv64::PageAttributes;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::mem::{clone_kernel_tables, table::PageEntry};
|
||||
|
||||
use super::{
|
||||
table::{PageTable, L1, L2, L3},
|
||||
tlb_flush_va_asid, KernelTableManagerImpl, USER_BOUNDARY,
|
||||
};
|
||||
|
||||
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
|
||||
l1: PhysicalRefMut<'static, PageTable<L1>, KernelTableManagerImpl>,
|
||||
asid: u16,
|
||||
_pd: PhantomData<TA>,
|
||||
}
|
||||
|
||||
impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceImpl<TA> {
|
||||
const LOWER_LIMIT_PFN: usize = 0;
|
||||
const UPPER_LIMIT_PFN: usize = 0;
|
||||
const LOWER_LIMIT_PFN: usize = 8;
|
||||
const UPPER_LIMIT_PFN: usize = (16 << 30) / L3::SIZE;
|
||||
|
||||
fn new() -> Result<Self, Error> {
|
||||
todo!()
|
||||
static LAST_ASID: AtomicU16 = AtomicU16::new(1);
|
||||
|
||||
let mut l1 = unsafe {
|
||||
PhysicalRefMut::<'static, PageTable<L1>, KernelTableManagerImpl>::map(
|
||||
TA::allocate_page_table()?,
|
||||
)
|
||||
};
|
||||
|
||||
for i in 0..512 {
|
||||
l1[i] = PageEntry::INVALID;
|
||||
}
|
||||
// Copy the kernel mappings
|
||||
clone_kernel_tables(&mut l1);
|
||||
|
||||
let asid = LAST_ASID.fetch_add(1, Ordering::AcqRel);
|
||||
|
||||
Ok(Self {
|
||||
l1,
|
||||
asid,
|
||||
_pd: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn map_page(
|
||||
@ -25,27 +59,138 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
|
||||
physical: PhysicalAddress,
|
||||
flags: MapAttributes,
|
||||
) -> Result<(), Error> {
|
||||
let _ = address;
|
||||
let _ = physical;
|
||||
let _ = flags;
|
||||
todo!()
|
||||
self.write_l3_entry(
|
||||
address,
|
||||
PageEntry::page(physical, to_page_attributes(flags)),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
|
||||
let _ = address;
|
||||
todo!()
|
||||
self.pop_l3_entry(address)
|
||||
}
|
||||
|
||||
fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
|
||||
let _ = address;
|
||||
todo!()
|
||||
self.read_l3_entry(address).ok_or(Error::DoesNotExist)
|
||||
}
|
||||
|
||||
fn as_address_with_asid(&self) -> u64 {
|
||||
todo!()
|
||||
fn as_address_with_asid(&self) -> (u64, u64) {
|
||||
let physical = unsafe { self.l1.as_physical_address() }.into_u64();
|
||||
(physical, self.asid as u64)
|
||||
}
|
||||
|
||||
unsafe fn clear(&mut self) {
|
||||
todo!()
|
||||
unsafe fn clear(&mut self) {}
|
||||
}
|
||||
|
||||
impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
|
||||
// Write a single 4KiB entry
|
||||
fn write_l3_entry(
|
||||
&mut self,
|
||||
virt: usize,
|
||||
entry: PageEntry<L3>,
|
||||
overwrite: bool,
|
||||
) -> Result<(), Error> {
|
||||
if virt >= USER_BOUNDARY {
|
||||
log::warn!("Tried to map a userspace page to a non-userspace virtual region");
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let l1i = virt.page_index::<L1>();
|
||||
let l2i = virt.page_index::<L2>();
|
||||
let l3i = virt.page_index::<L3>();
|
||||
|
||||
let mut l2 = self.l1.get_mut_or_alloc::<TA>(l1i)?;
|
||||
let mut l3 = l2.get_mut_or_alloc::<TA>(l2i)?;
|
||||
|
||||
if l3[l3i].is_present() && !overwrite {
|
||||
todo!();
|
||||
}
|
||||
|
||||
l3[l3i] = entry;
|
||||
tlb_flush_va_asid(virt, self.asid as usize);
|
||||
// dc_cvac((&raw const l3[l3i]).addr());
|
||||
// tlb_flush_vaae1(virt);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
|
||||
let l1i = virt.page_index::<L1>();
|
||||
let l2i = virt.page_index::<L2>();
|
||||
let l3i = virt.page_index::<L3>();
|
||||
|
||||
// TODO somehow drop tables if they're known to be empty?
|
||||
let mut l2 = self.l1.get_mut(l1i).ok_or(Error::DoesNotExist)?;
|
||||
let mut l3 = l2.get_mut(l2i).ok_or(Error::DoesNotExist)?;
|
||||
|
||||
let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?;
|
||||
|
||||
l3[l3i] = PageEntry::INVALID;
|
||||
tlb_flush_va_asid(virt, self.asid as usize);
|
||||
// ic_iallu();
|
||||
// dc_cvac((&raw const l3[l3i]).addr());
|
||||
// tlb_flush_vaae1(virt);
|
||||
|
||||
Ok(page)
|
||||
}
|
||||
|
||||
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
|
||||
if virt >= USER_BOUNDARY {
|
||||
log::warn!("Tried read an userspace page to a non-userspace virtual region");
|
||||
return None;
|
||||
}
|
||||
|
||||
let l1i = virt.page_index::<L1>();
|
||||
let l2i = virt.page_index::<L2>();
|
||||
let l3i = virt.page_index::<L3>();
|
||||
|
||||
let l2 = self.l1.get(l1i)?;
|
||||
let l3 = l2.get(l2i)?;
|
||||
|
||||
let page = l3[l3i].as_page()?;
|
||||
|
||||
Some((
|
||||
page.add(virt & 0xFFF),
|
||||
to_map_attributes(l3[l3i].attributes()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<TA: TableAllocator> Drop for ProcessAddressSpaceImpl<TA> {
|
||||
fn drop(&mut self) {
|
||||
// // SAFETY: with safe usage of the ProcessAddressSpaceImpl, clearing and dropping
|
||||
// // is safe, no one refers to the memory
|
||||
// unsafe {
|
||||
// self.clear();
|
||||
// let l1_phys = self.l1.as_physical_address();
|
||||
// TA::free_page_table(l1_phys);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
fn to_page_attributes(src: MapAttributes) -> PageAttributes {
|
||||
let mut result = PageAttributes::R | PageAttributes::X;
|
||||
if src.contains(MapAttributes::USER_WRITE) {
|
||||
result |= PageAttributes::W;
|
||||
}
|
||||
if src.intersects(MapAttributes::USER_READ | MapAttributes::USER_WRITE) {
|
||||
result |= PageAttributes::U;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn to_map_attributes(src: PageAttributes) -> MapAttributes {
|
||||
let mut result = MapAttributes::NON_GLOBAL;
|
||||
|
||||
if src.contains(PageAttributes::U) {
|
||||
result |= MapAttributes::USER_READ;
|
||||
|
||||
if src.contains(PageAttributes::W) {
|
||||
result |= MapAttributes::USER_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use core::{
|
||||
};
|
||||
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
pointer::{PhysicalRef, PhysicalRefMut},
|
||||
table::{EntryLevel, NextPageTable, NonTerminalEntryLevel, TableAllocator},
|
||||
};
|
||||
@ -42,7 +42,7 @@ pub struct PageTable<L: EntryLevel> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct PageEntry<L: EntryLevel>(u64, PhantomData<L>);
|
||||
pub struct PageEntry<L: EntryLevel>(pub u64, PhantomData<L>);
|
||||
|
||||
impl NonTerminalEntryLevel for L1 {
|
||||
type NextLevel = L2;
|
||||
@ -57,11 +57,33 @@ impl<L: EntryLevel> PageTable<L> {
|
||||
entries: [PageEntry::INVALID; 512],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_zeroed<'a, TA: TableAllocator>(
|
||||
) -> Result<PhysicalRefMut<'a, PageTable<L>, KernelTableManagerImpl>, Error> {
|
||||
let physical = TA::allocate_page_table()?;
|
||||
let mut table =
|
||||
unsafe { PhysicalRefMut::<'a, Self, KernelTableManagerImpl>::map(physical) };
|
||||
|
||||
for i in 0..512 {
|
||||
table[i] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
Ok(table)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> PageEntry<L> {
|
||||
pub const INVALID: Self = Self(0, PhantomData);
|
||||
|
||||
/// Constructs a [PageEntry] from its raw representation.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure `value` is actually a "valid" PTE.
|
||||
pub const unsafe fn from_raw(value: u64) -> Self {
|
||||
Self(value, PhantomData)
|
||||
}
|
||||
|
||||
pub const fn is_present(&self) -> bool {
|
||||
self.0 & PageAttributes::V.bits() != 0
|
||||
}
|
||||
@ -76,19 +98,31 @@ impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
|
||||
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(&self, index: usize) -> Option<Self::TableRef> {
|
||||
let table = self[index].as_table()?;
|
||||
Some(unsafe { PhysicalRef::map(table) })
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, _index: usize) -> Option<Self::TableRefMut> {
|
||||
loop {}
|
||||
fn get_mut(&mut self, index: usize) -> Option<Self::TableRefMut> {
|
||||
let table = self[index].as_table()?;
|
||||
Some(unsafe { PhysicalRefMut::map(table) })
|
||||
}
|
||||
|
||||
fn get_mut_or_alloc<TA: TableAllocator>(
|
||||
&mut self,
|
||||
_index: usize,
|
||||
index: usize,
|
||||
) -> Result<Self::TableRefMut, Error> {
|
||||
loop {}
|
||||
if let Some(table) = self[index].as_table() {
|
||||
Ok(unsafe { PhysicalRefMut::map(table) })
|
||||
} else {
|
||||
let table = PageTable::new_zeroed::<TA>()?;
|
||||
self[index] = PageEntry::<L>::table(
|
||||
unsafe { table.as_physical_address() },
|
||||
PageAttributes::empty(),
|
||||
);
|
||||
// dc_cvac((&raw const self[index]).addr());
|
||||
Ok(table)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,6 +142,15 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_table(&self) -> Option<PhysicalAddress> {
|
||||
(self.0
|
||||
& (PageAttributes::R | PageAttributes::W | PageAttributes::X | PageAttributes::V)
|
||||
.bits()
|
||||
== PageAttributes::V.bits())
|
||||
.then_some((self.0 << 2) & !0xFFF)
|
||||
.map(PhysicalAddress::from_u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl PageEntry<L3> {
|
||||
@ -119,7 +162,9 @@ impl PageEntry<L3> {
|
||||
}
|
||||
|
||||
pub fn as_page(&self) -> Option<PhysicalAddress> {
|
||||
loop {}
|
||||
(self.0 & PageAttributes::V.bits() != 0)
|
||||
.then_some((self.0 << 2) & !0xFFF)
|
||||
.map(PhysicalAddress::from_u64)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,6 +448,8 @@ pub mod sstatus {
|
||||
register_bitfields!(
|
||||
u64,
|
||||
pub SSTATUS [
|
||||
SUM OFFSET(18) NUMBITS(1) [],
|
||||
SPP OFFSET(8) NUMBITS(1) [],
|
||||
SIE OFFSET(1) NUMBITS(1) [],
|
||||
]
|
||||
);
|
||||
|
@ -1,3 +1,5 @@
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[inline(always)]
|
||||
unsafe fn sbi_do_call(
|
||||
extension: u64,
|
||||
function: u64,
|
||||
|
@ -39,8 +39,8 @@ pub trait ProcessAddressSpaceManager<TA: TableAllocator>: Sized {
|
||||
/// if one is mapped
|
||||
fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error>;
|
||||
|
||||
/// Returns the implementation specific physical address of this space, with ASID applied
|
||||
fn as_address_with_asid(&self) -> u64;
|
||||
/// Returns the physical address of the translation table along with its ASID
|
||||
fn as_address_with_asid(&self) -> (u64, u64);
|
||||
|
||||
/// Clears the address space by dropping and non-global tables.
|
||||
///
|
||||
|
@ -335,7 +335,8 @@ impl<TA: TableAllocator> ProcessAddressSpace<TA> {
|
||||
ProcessAddressSpaceImpl::<TA>::LOWER_LIMIT_PFN,
|
||||
ProcessAddressSpaceImpl::<TA>::UPPER_LIMIT_PFN,
|
||||
);
|
||||
log::debug!("New AddressSpace {:#x}", table.as_address_with_asid());
|
||||
let (physical, asid) = table.as_address_with_asid();
|
||||
log::debug!("New AddressSpace {:#x}, asid {:#x}", physical, asid);
|
||||
Ok(Self {
|
||||
inner: IrqSafeSpinlock::new(Inner { table, allocator }),
|
||||
})
|
||||
@ -451,8 +452,8 @@ impl<TA: TableAllocator> ProcessAddressSpace<TA> {
|
||||
lock.unmap_range(address, size / L3_PAGE_SIZE)
|
||||
}
|
||||
|
||||
/// Returns the physical address of this table, with ASID applied
|
||||
pub fn as_address_with_asid(&self) -> u64 {
|
||||
/// Returns the physical address of the translation table along with its ASID
|
||||
pub fn as_address_with_asid(&self) -> (u64, u64) {
|
||||
self.inner.lock().table.as_address_with_asid()
|
||||
}
|
||||
|
||||
@ -465,7 +466,8 @@ impl<TA: TableAllocator> ProcessAddressSpace<TA> {
|
||||
|
||||
impl<TA: TableAllocator> Drop for ProcessAddressSpace<TA> {
|
||||
fn drop(&mut self) {
|
||||
log::debug!("Drop AddressSpace {:#x}", self.as_address_with_asid());
|
||||
let (physical, asid) = self.as_address_with_asid();
|
||||
log::debug!("Drop AddressSpace {:#x}, asid {:#x}", physical, asid);
|
||||
self.clear().ok();
|
||||
}
|
||||
}
|
||||
|
@ -488,7 +488,7 @@ fn write_rela(rela: &Rela, space: &ProcessAddressSpace, b: usize) -> Result<(),
|
||||
let rel_field = rela.r_offset as usize + b;
|
||||
|
||||
let (value, width) = match rela.r_type {
|
||||
elf::abi::R_X86_64_RELATIVE | elf::abi::R_AARCH64_RELATIVE => {
|
||||
elf::abi::R_X86_64_RELATIVE | elf::abi::R_AARCH64_RELATIVE | elf::abi::R_RISCV_RELATIVE => {
|
||||
// B + A
|
||||
// Width: qword
|
||||
(b as i64 + a, 8)
|
||||
|
@ -217,13 +217,21 @@ where
|
||||
|
||||
// let tls_address = elf::clone_tls(space, image)?;
|
||||
|
||||
log::debug!("argument = {:#x}", argument);
|
||||
log::debug!(
|
||||
"argument = {:#x}, user_sp = {:#x}, stack: {:#x}..{:#x}",
|
||||
argument,
|
||||
user_sp,
|
||||
virt_stack_base,
|
||||
virt_stack_base + USER_STACK_PAGES * 0x1000 - TaskContextImpl::USER_STACK_EXTRA_ALIGN
|
||||
);
|
||||
let (address_space, asid) = space.as_address_with_asid();
|
||||
TaskContext::user(UserContextInfo {
|
||||
entry: image.entry,
|
||||
argument,
|
||||
stack_pointer: ptr.addr(),
|
||||
thread_pointer: 0,
|
||||
address_space: space.as_address_with_asid(),
|
||||
address_space,
|
||||
asid,
|
||||
single_step: options.single_step,
|
||||
})
|
||||
}
|
||||
|
@ -194,10 +194,13 @@ impl Process {
|
||||
let sp = TaskContextImpl::align_stack_for_entry(options.stack_top) as *mut usize;
|
||||
let sp = unsafe { Thread::setup_stack_header(&space, sp, options.argument)? };
|
||||
|
||||
let (address_space, asid) = space.as_address_with_asid();
|
||||
|
||||
let info = UserContextInfo {
|
||||
entry: options.entry as _,
|
||||
argument: options.argument,
|
||||
address_space: space.as_address_with_asid(),
|
||||
address_space,
|
||||
asid,
|
||||
stack_pointer: sp.addr(),
|
||||
single_step: false,
|
||||
thread_pointer: 0,
|
||||
|
@ -14,7 +14,7 @@ __rv64_entry:
|
||||
// a0 - bootstrap HART ID
|
||||
// a1 - device tree blob
|
||||
// mhartid == a0
|
||||
// satp == 0
|
||||
csrw satp, zero
|
||||
|
||||
// Zero the .bss
|
||||
LOAD_PCREL .L00, t0, __bss_start_phys
|
||||
|
@ -1,24 +1,56 @@
|
||||
use core::arch::global_asm;
|
||||
|
||||
use kernel_arch::{task::Scheduler, Architecture};
|
||||
use libk::arch::Cpu;
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable};
|
||||
use abi::{arch::SavedFrame, primitive_enum, process::Signal, SyscallFunction};
|
||||
use kernel_arch::{
|
||||
task::{Scheduler, TaskFrame},
|
||||
Architecture,
|
||||
};
|
||||
use libk::{arch::Cpu, task::thread::Thread};
|
||||
use tock_registers::interfaces::ReadWriteable;
|
||||
|
||||
use kernel_arch_riscv64::{
|
||||
intrinsics,
|
||||
registers::{SCAUSE, SEPC, STVAL, STVEC},
|
||||
registers::{SSTATUS, STVEC},
|
||||
sbi, ArchitectureImpl,
|
||||
};
|
||||
|
||||
use crate::syscall;
|
||||
|
||||
primitive_enum! {
|
||||
pub enum Cause: u64 {
|
||||
MisalignedInstruction = 0,
|
||||
InstructionAccessFault = 1,
|
||||
IllegalInstruction = 2,
|
||||
Breakpoint = 3,
|
||||
LoadAddressMisaligned = 4,
|
||||
LoadAccessFault = 5,
|
||||
StoreAddressMisaligned = 6,
|
||||
StoreAccessFault = 7,
|
||||
EcallUmode = 8,
|
||||
EcallSmode = 9,
|
||||
EcallMmode = 11,
|
||||
InstructionPageFault = 12,
|
||||
LoadPageFault = 13,
|
||||
StorePageFault = 15,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct TrapFrame {
|
||||
// General-purpose
|
||||
pub ra: u64,
|
||||
pub gp: u64,
|
||||
pub t0_2: [u64; 3],
|
||||
pub a0_7: [u64; 8],
|
||||
pub t3_6: [u64; 4],
|
||||
pub s0: u64,
|
||||
pub tn: [u64; 7],
|
||||
pub sn: [u64; 12],
|
||||
pub an: [usize; 8],
|
||||
// Special
|
||||
pub sp: u64,
|
||||
pub sstatus: u64,
|
||||
pub sepc: u64,
|
||||
pub stval: u64,
|
||||
pub scause: u64,
|
||||
pub tp: u64,
|
||||
}
|
||||
|
||||
pub fn init_smode_exceptions() {
|
||||
@ -31,42 +63,180 @@ pub fn init_smode_exceptions() {
|
||||
STVEC.modify(STVEC::MODE::Vectored);
|
||||
}
|
||||
|
||||
unsafe fn smode_exception_handler(frame: *mut TrapFrame) {
|
||||
let _ = frame;
|
||||
let cause = SCAUSE.read(SCAUSE::CODE);
|
||||
let tval = STVAL.get();
|
||||
let epc = SEPC.get();
|
||||
unsafe fn umode_exception_handler(frame: &mut TrapFrame) {
|
||||
let thread = Thread::current();
|
||||
|
||||
log::error!("S-mode exception cause={cause}, tval={tval:#x}, epc={epc:#x}");
|
||||
let cause = Cause::try_from(frame.scause).ok();
|
||||
|
||||
let dump = match cause {
|
||||
Some(Cause::LoadPageFault)
|
||||
| Some(Cause::StorePageFault)
|
||||
| Some(Cause::LoadAccessFault)
|
||||
| Some(Cause::StoreAccessFault)
|
||||
| Some(Cause::InstructionPageFault)
|
||||
| Some(Cause::InstructionAccessFault) => {
|
||||
let translation = if let Some(space) = thread.try_get_process().map(|p| p.space()) {
|
||||
space.translate(frame.stval as usize).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
|
||||
if let Some(physical) = translation {
|
||||
log::warn!(" * tval translates to {physical:#x}");
|
||||
} else {
|
||||
log::warn!(" * tval does not translate");
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
Some(Cause::EcallUmode) => {
|
||||
// TODO more granular control over how U-mode pages are accessed from S-mode
|
||||
SSTATUS.modify(SSTATUS::SUM::SET);
|
||||
|
||||
let func = frame.an[0];
|
||||
if func == usize::from(SyscallFunction::ExitSignal) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
let args = &frame.an[1..];
|
||||
let result = syscall::raw_syscall_handler(func, args) as _;
|
||||
frame.an[0] = result;
|
||||
frame.sepc += 4;
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
if dump {
|
||||
log::warn!(
|
||||
"U-mode exception cause={:?} ({}), epc={:#x}, sp={:#x}, tval={:#x}",
|
||||
cause,
|
||||
frame.scause,
|
||||
frame.sepc,
|
||||
frame.sp,
|
||||
frame.stval
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn smode_exception_handler(frame: &mut TrapFrame) {
|
||||
let cause = Cause::try_from(frame.scause).expect("Invalid exception cause");
|
||||
log::error!(
|
||||
"S-mode exception cause={:?} ({}), tval={:#x}, epc={:#x}, sp={:#x}",
|
||||
cause,
|
||||
frame.scause,
|
||||
frame.stval,
|
||||
frame.sepc,
|
||||
frame.sp
|
||||
);
|
||||
match cause {
|
||||
Cause::LoadPageFault
|
||||
| Cause::StorePageFault
|
||||
| Cause::LoadAccessFault
|
||||
| Cause::StoreAccessFault
|
||||
| Cause::InstructionPageFault
|
||||
| Cause::InstructionAccessFault => {
|
||||
let translation = if let Some(space) = Thread::get_current()
|
||||
.and_then(|t| t.try_get_process())
|
||||
.map(|p| p.space())
|
||||
{
|
||||
space.translate(frame.stval as usize).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(physical) = translation {
|
||||
log::warn!(" * tval translates to {physical:#x}");
|
||||
} else {
|
||||
log::warn!(" * tval does not translate");
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
ArchitectureImpl::halt();
|
||||
}
|
||||
|
||||
unsafe extern "C" fn smode_interrupt_handler(frame: *mut TrapFrame) {
|
||||
let _ = frame;
|
||||
let cause = SCAUSE.read(SCAUSE::CODE);
|
||||
|
||||
match cause {
|
||||
let frame = &*frame;
|
||||
match frame.scause & !(1 << 63) {
|
||||
// S-mode timer interrupt
|
||||
5 => {
|
||||
sbi::sbi_set_timer(intrinsics::rdtime() + 1_000_000);
|
||||
sbi::sbi_set_timer(intrinsics::rdtime() + 100_000);
|
||||
|
||||
// TODO runtime tick, time accounting
|
||||
Cpu::local().scheduler().yield_cpu();
|
||||
}
|
||||
_ => {
|
||||
log::warn!("Unknown/unhandled S-mode interrupt {cause}");
|
||||
ArchitectureImpl::halt();
|
||||
}
|
||||
n => todo!("Unhandled interrupt #{n}"),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn smode_general_trap_handler(frame: *mut TrapFrame) {
|
||||
let interrupt = SCAUSE.matches_all(SCAUSE::INTERRUPT::SET);
|
||||
let frame = &mut *frame;
|
||||
|
||||
if interrupt {
|
||||
smode_interrupt_handler(frame);
|
||||
} else {
|
||||
smode_exception_handler(frame);
|
||||
let interrupt = frame.scause & (1 << 63) != 0;
|
||||
let smode = frame.sstatus & (1 << 8) != 0;
|
||||
|
||||
match (interrupt, smode) {
|
||||
(true, _) => smode_interrupt_handler(frame),
|
||||
(false, true) => smode_exception_handler(frame),
|
||||
(false, false) => umode_exception_handler(frame),
|
||||
}
|
||||
|
||||
if !smode && let Some(thread) = Thread::get_current() {
|
||||
thread.handle_pending_signals(frame);
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskFrame for TrapFrame {
|
||||
fn store(&self) -> SavedFrame {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn restore(&mut self, saved: &SavedFrame) {
|
||||
let _ = saved;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn user_sp(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn user_ip(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn argument(&self) -> u64 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_user_sp(&mut self, value: usize) {
|
||||
let _ = value;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_user_ip(&mut self, value: usize) {
|
||||
let _ = value;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_argument(&mut self, value: u64) {
|
||||
let _ = value;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_single_step(&mut self, step: bool) {
|
||||
let _ = step;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_return_value(&mut self, value: u64) {
|
||||
let _ = value;
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,12 @@ use device_api::{
|
||||
ResetDevice,
|
||||
};
|
||||
use device_tree::{driver::unflatten_device_tree, DeviceTree, DeviceTreeNodeExt};
|
||||
use kernel_arch::Architecture;
|
||||
use kernel_arch_riscv64::{
|
||||
intrinsics,
|
||||
mem::{self, KERNEL_VIRT_OFFSET},
|
||||
registers::SIE,
|
||||
sbi, PerCpuData,
|
||||
sbi, ArchitectureImpl, PerCpuData,
|
||||
};
|
||||
use libk::{arch::Cpu, config};
|
||||
use libk_mm::{
|
||||
@ -59,7 +60,7 @@ impl Platform for Riscv64 {
|
||||
type L3 = L3;
|
||||
|
||||
unsafe fn reset(&self) -> ! {
|
||||
loop {}
|
||||
ArchitectureImpl::halt();
|
||||
}
|
||||
|
||||
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<bool, Error> {
|
||||
@ -76,7 +77,7 @@ impl Platform for Riscv64 {
|
||||
|
||||
fn register_reset_device(&self, reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
|
||||
let _ = reset;
|
||||
loop {}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +120,7 @@ impl Riscv64 {
|
||||
let aligned_end = end.page_align_up::<L3>();
|
||||
|
||||
let size = aligned_end - aligned_start;
|
||||
log::info!("Reserve initrd @ {:#x}..{:#x}", aligned_start, aligned_end);
|
||||
reserve_region(
|
||||
"initrd",
|
||||
PhysicalMemoryRegion {
|
||||
@ -159,6 +161,7 @@ impl Riscv64 {
|
||||
|
||||
let per_cpu = PerCpuData {
|
||||
tmp_t0: 0,
|
||||
umode_sp: 0,
|
||||
smode_sp: 0,
|
||||
|
||||
bootstrap: is_bsp,
|
||||
@ -220,7 +223,7 @@ impl Riscv64 {
|
||||
|
||||
// Setup the timer
|
||||
SIE.modify(SIE::STIE::SET);
|
||||
sbi::sbi_set_timer(intrinsics::rdtime() + 1_000_000);
|
||||
sbi::sbi_set_timer(intrinsics::rdtime() + 100_000);
|
||||
|
||||
// Test call into M-mode
|
||||
// core::arch::asm!("ecall", in("a0") MModeFunction::WriteTimerComparator as u64, in("a1") 0x4321);
|
||||
|
@ -2,112 +2,158 @@
|
||||
|
||||
.section .text
|
||||
|
||||
.set SMODE_TRAP_STATE_SIZE, (8 * 18)
|
||||
// ra+gp, 7 tN, 12 sN, 8 aN
|
||||
.set GP_REGS_SIZE, (2 + 7 + 12 + 8) * 8
|
||||
// U-mode sp, sstatus, sepc, stval, scause, sscratch
|
||||
.set CTL_REGS_SIZE, 6 * 8
|
||||
.set TRAP_CONTEXT_SIZE, (GP_REGS_SIZE) + (CTL_REGS_SIZE)
|
||||
|
||||
.macro SAVE_TRAP_CONTEXT
|
||||
addi sp, sp, -SMODE_TRAP_STATE_SIZE
|
||||
sd ra, 8 * 0(sp)
|
||||
sd gp, 8 * 1(sp)
|
||||
sd t0, 8 * 2(sp)
|
||||
sd t1, 8 * 3(sp)
|
||||
sd t2, 8 * 4(sp)
|
||||
sd a0, 8 * 5(sp)
|
||||
sd a1, 8 * 6(sp)
|
||||
sd a2, 8 * 7(sp)
|
||||
sd a3, 8 * 8(sp)
|
||||
sd a4, 8 * 9(sp)
|
||||
sd a5, 8 * 10(sp)
|
||||
sd a6, 8 * 11(sp)
|
||||
sd a7, 8 * 12(sp)
|
||||
sd t3, 8 * 13(sp)
|
||||
sd t4, 8 * 14(sp)
|
||||
sd t5, 8 * 15(sp)
|
||||
sd t6, 8 * 16(sp)
|
||||
sd s0, 8 * 17(sp)
|
||||
.set REG_UMODE_SP, (GP_REGS_SIZE + 0 * 8)
|
||||
.set REG_SSTATUS, (GP_REGS_SIZE + 1 * 8)
|
||||
.set REG_SEPC, (GP_REGS_SIZE + 2 * 8)
|
||||
.set REG_STVAL, (GP_REGS_SIZE + 3 * 8)
|
||||
.set REG_SCAUSE, (GP_REGS_SIZE + 4 * 8)
|
||||
.set REG_SSCRATCH, (GP_REGS_SIZE + 5 * 8)
|
||||
|
||||
.macro SAVE_GP_REGS
|
||||
// Save all general-purpose registers, except:
|
||||
// * sp (saved elsewhere)
|
||||
// * tp (saved elsewhere)
|
||||
sd ra, 0 * 8(sp)
|
||||
sd gp, 1 * 8(sp)
|
||||
|
||||
sd t0, 2 * 8(sp)
|
||||
sd t1, 3 * 8(sp)
|
||||
sd t2, 4 * 8(sp)
|
||||
sd t3, 5 * 8(sp)
|
||||
sd t4, 6 * 8(sp)
|
||||
sd t5, 7 * 8(sp)
|
||||
sd t6, 8 * 8(sp)
|
||||
|
||||
sd s0, 9 * 8(sp)
|
||||
sd s1, 10 * 8(sp)
|
||||
sd s2, 11 * 8(sp)
|
||||
sd s3, 12 * 8(sp)
|
||||
sd s4, 13 * 8(sp)
|
||||
sd s5, 14 * 8(sp)
|
||||
sd s6, 15 * 8(sp)
|
||||
sd s7, 16 * 8(sp)
|
||||
sd s8, 17 * 8(sp)
|
||||
sd s9, 18 * 8(sp)
|
||||
sd s10, 19 * 8(sp)
|
||||
sd s11, 20 * 8(sp)
|
||||
|
||||
sd a0, 21 * 8(sp)
|
||||
sd a1, 22 * 8(sp)
|
||||
sd a2, 23 * 8(sp)
|
||||
sd a3, 24 * 8(sp)
|
||||
sd a4, 25 * 8(sp)
|
||||
sd a5, 26 * 8(sp)
|
||||
sd a6, 27 * 8(sp)
|
||||
sd a7, 28 * 8(sp)
|
||||
.endm
|
||||
|
||||
.macro LOAD_TRAP_CONTEXT
|
||||
ld ra, 8 * 0(sp)
|
||||
ld gp, 8 * 1(sp)
|
||||
ld t0, 8 * 2(sp)
|
||||
ld t1, 8 * 3(sp)
|
||||
ld t2, 8 * 4(sp)
|
||||
ld a0, 8 * 5(sp)
|
||||
ld a1, 8 * 6(sp)
|
||||
ld a2, 8 * 7(sp)
|
||||
ld a3, 8 * 8(sp)
|
||||
ld a4, 8 * 9(sp)
|
||||
ld a5, 8 * 10(sp)
|
||||
ld a6, 8 * 11(sp)
|
||||
ld a7, 8 * 12(sp)
|
||||
ld t3, 8 * 13(sp)
|
||||
ld t4, 8 * 14(sp)
|
||||
ld t5, 8 * 15(sp)
|
||||
ld t6, 8 * 16(sp)
|
||||
ld s0, 8 * 17(sp)
|
||||
addi sp, sp, SMODE_TRAP_STATE_SIZE
|
||||
.endm
|
||||
.macro LOAD_GP_REGS
|
||||
ld ra, 0 * 8(sp)
|
||||
ld gp, 1 * 8(sp)
|
||||
|
||||
// * Switch stack to kernel if needed
|
||||
// * Store pre-trap register state on the stack
|
||||
// * Make a0 point to the frame
|
||||
// * Make s0 = original_tp
|
||||
.macro SMODE_TRAP_ENTER
|
||||
// Stack may be either U-mode or S-mode stack depending on sstatus.SPP
|
||||
// Original tp -> sscratch
|
||||
// Per-CPU struct -> tp
|
||||
csrrw tp, sscratch, tp
|
||||
ld t0, 2 * 8(sp)
|
||||
ld t1, 3 * 8(sp)
|
||||
ld t2, 4 * 8(sp)
|
||||
ld t3, 5 * 8(sp)
|
||||
ld t4, 6 * 8(sp)
|
||||
ld t5, 7 * 8(sp)
|
||||
ld t6, 8 * 8(sp)
|
||||
|
||||
// Store t0 in per-CPU scratch space
|
||||
sd t0, 0(tp)
|
||||
ld s0, 9 * 8(sp)
|
||||
ld s1, 10 * 8(sp)
|
||||
ld s2, 11 * 8(sp)
|
||||
ld s3, 12 * 8(sp)
|
||||
ld s4, 13 * 8(sp)
|
||||
ld s5, 14 * 8(sp)
|
||||
ld s6, 15 * 8(sp)
|
||||
ld s7, 16 * 8(sp)
|
||||
ld s8, 17 * 8(sp)
|
||||
ld s9, 18 * 8(sp)
|
||||
ld s10, 19 * 8(sp)
|
||||
ld s11, 20 * 8(sp)
|
||||
|
||||
// Determine where the interrupt came from (SPP is bit 8)
|
||||
csrr t0, sstatus
|
||||
andi t0, t0, (1 << 8)
|
||||
bnez t0, 1f
|
||||
|
||||
// Trap came from U-mode
|
||||
// TODO
|
||||
j .
|
||||
1:
|
||||
// Trap came from S-mode
|
||||
2:
|
||||
// Either stack was adjusted or the trap came from S-mode
|
||||
|
||||
// Load t0 back
|
||||
ld t0, 0(tp)
|
||||
SAVE_TRAP_CONTEXT
|
||||
|
||||
mv a0, sp
|
||||
csrr s0, sscratch
|
||||
.endm
|
||||
|
||||
// * Set sscratch to pre-trap tp
|
||||
// * Restore the pre-trap register state
|
||||
// * Return
|
||||
.macro SMODE_TRAP_LEAVE_TO_SMODE
|
||||
csrw sscratch, s0
|
||||
|
||||
// Restore the state
|
||||
LOAD_TRAP_CONTEXT
|
||||
|
||||
// Swap the tp<->scratch back
|
||||
csrrw tp, sscratch, tp
|
||||
sret
|
||||
ld a0, 21 * 8(sp)
|
||||
ld a1, 22 * 8(sp)
|
||||
ld a2, 23 * 8(sp)
|
||||
ld a3, 24 * 8(sp)
|
||||
ld a4, 25 * 8(sp)
|
||||
ld a5, 26 * 8(sp)
|
||||
ld a6, 27 * 8(sp)
|
||||
ld a7, 28 * 8(sp)
|
||||
.endm
|
||||
|
||||
.macro SMODE_TRAP n, handler
|
||||
.type __rv64_smode_trap_\n, @function
|
||||
__rv64_smode_trap_\n:
|
||||
SMODE_TRAP_ENTER
|
||||
// If coming from userspace, sscratch = kernel-mode tp
|
||||
// If coming from kernelspace, sscratch = 0
|
||||
csrrw tp, sscratch, tp
|
||||
bnez tp, 1f
|
||||
|
||||
// TODO when coming through a non-zero vector, trap is always asyncrhonous, so
|
||||
// the interrupt handler can be called directly instead of a more generic
|
||||
// trap handler to avoid an extra indirection
|
||||
// Coming from S-mode
|
||||
// tp = 0, sscratch contains kernel tp
|
||||
csrr tp, sscratch
|
||||
sd sp, 16(tp) // Set proper S-mode sp
|
||||
1:
|
||||
sd sp, 8(tp) // Store U-mode sp
|
||||
ld sp, 16(tp) // Load S-mode sp
|
||||
|
||||
// Store pre-trap context
|
||||
addi sp, sp, -(TRAP_CONTEXT_SIZE)
|
||||
|
||||
SAVE_GP_REGS
|
||||
// Save special registers
|
||||
ld t0, 8(tp)
|
||||
csrr t1, sstatus
|
||||
csrr t2, sepc
|
||||
csrr t3, stval
|
||||
csrr t4, scause
|
||||
csrr t5, sscratch
|
||||
|
||||
sd t0, REG_UMODE_SP (sp)
|
||||
sd t1, REG_SSTATUS (sp)
|
||||
sd t2, REG_SEPC (sp)
|
||||
sd t3, REG_STVAL (sp)
|
||||
sd t4, REG_SCAUSE (sp)
|
||||
sd t5, REG_SSCRATCH (sp)
|
||||
|
||||
// Reset sscratch to zero to make sure a S-mode -> S-mode nested exception
|
||||
// happens properly
|
||||
csrw sscratch, zero
|
||||
|
||||
mv a0, sp
|
||||
call \handler
|
||||
|
||||
// TODO U-mode trap return
|
||||
SMODE_TRAP_LEAVE_TO_SMODE
|
||||
// Return from exception
|
||||
ld t0, REG_SSTATUS (sp)
|
||||
andi t0, t0, (1 << 8)
|
||||
bnez t0, 2f
|
||||
|
||||
// Return to U-mode
|
||||
// Restore SSCRATCH to a proper value
|
||||
csrw sscratch, tp
|
||||
2:
|
||||
// Return to S-mode
|
||||
|
||||
// Restore special registers
|
||||
ld t0, REG_SSTATUS (sp)
|
||||
ld t1, REG_SEPC (sp)
|
||||
csrw sstatus, t0
|
||||
csrw sepc, t1
|
||||
|
||||
// Restore general-purpose registers
|
||||
LOAD_GP_REGS
|
||||
|
||||
ld tp, REG_SSCRATCH (sp)
|
||||
ld sp, REG_UMODE_SP (sp)
|
||||
|
||||
sret
|
||||
.size __rv64_smode_trap_\n, . - __rv64_smode_trap_\n
|
||||
.endm
|
||||
|
||||
|
@ -58,7 +58,7 @@ pub fn kinit() -> Result<(), Error> {
|
||||
|
||||
// TODO move this to userspace so it doesn't block the init process, maybe lazy-load on first
|
||||
// attempt to load a module?
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
#[cfg(all(not(target_arch = "aarch64"), not(target_arch = "riscv64")))]
|
||||
{
|
||||
use libk::module::load_kernel_symbol_table;
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
clippy::match_ref_pats,
|
||||
clippy::match_single_binding,
|
||||
clippy::missing_transmute_annotations,
|
||||
clippy::modulo_one,
|
||||
async_fn_in_trait
|
||||
)]
|
||||
#![deny(missing_docs)]
|
||||
|
@ -53,6 +53,9 @@ pub(crate) fn map_memory(
|
||||
|
||||
space.allocate(None, len, backing, attrs)
|
||||
})
|
||||
.inspect_err(|error| {
|
||||
log::warn!("map_memory({len}) failed: {error:?}");
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn unmap_memory(address: usize, len: usize) -> Result<(), Error> {
|
||||
|
@ -151,7 +151,7 @@ impl<F: Read + Seek> Riscv64Builder<F> {
|
||||
|
||||
let entry = shift_pfn(paddr) | (PageAttributes::V | flags).bits();
|
||||
|
||||
let l2i = (vaddr >> L2_SHIFT) as usize & 0x1FF - start_l2i;
|
||||
let l2i = ((vaddr >> L2_SHIFT) as usize & 0x1FF) - start_l2i;
|
||||
let l3i = (vaddr >> L3_SHIFT) as usize & 0x1FF;
|
||||
|
||||
let l3 = &mut tables.kernel_l3s[l2i];
|
||||
|
@ -17,7 +17,11 @@ pub struct QemuRiscv64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Image {
|
||||
OpenSBI { kernel: PathBuf, bios: PathBuf },
|
||||
OpenSBI {
|
||||
bios: PathBuf,
|
||||
kernel: PathBuf,
|
||||
initrd: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl IntoArgs for Machine {
|
||||
@ -41,9 +45,15 @@ impl IntoArgs for Cpu {
|
||||
impl IntoArgs for Image {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
match self {
|
||||
Self::OpenSBI { kernel, bios } => {
|
||||
Self::OpenSBI {
|
||||
bios,
|
||||
kernel,
|
||||
initrd,
|
||||
} => {
|
||||
command.arg("-kernel");
|
||||
command.arg(kernel);
|
||||
command.arg("-initrd");
|
||||
command.arg(initrd);
|
||||
command.arg("-bios");
|
||||
command.arg(bios);
|
||||
}
|
||||
|
@ -63,6 +63,13 @@ pub struct Dtv {
|
||||
specific: Vec<*mut c_void>,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub struct TlsInfo {
|
||||
base: usize,
|
||||
tp: usize,
|
||||
module0_offset: Option<usize>,
|
||||
}
|
||||
|
||||
struct TcbHeader {
|
||||
#[allow(unused)]
|
||||
self_pointer: usize,
|
||||
@ -170,8 +177,10 @@ impl Dtv {
|
||||
/// Will panic if key == 0.
|
||||
/// Will panic if key is longer than the DTV itself.
|
||||
pub fn set_specific(&mut self, key: usize, value: *mut c_void, grow: bool) {
|
||||
self.try_set_specific(key, value, grow)
|
||||
.expect("Dtv::set_specific(): invalid key")
|
||||
if self.try_set_specific(key, value, grow).is_err() {
|
||||
crate::debug_trace!("Dtv::set_specific(): invalid key {key}");
|
||||
panic!("Dtv::set_specific(): invalid key {key})")
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a DTV entry for a thread-specific key.
|
||||
@ -203,7 +212,13 @@ impl Dtv {
|
||||
/// Will panic if key == 0.
|
||||
/// Will panic if key is larger than the DTV itself.
|
||||
pub fn get(&self, key: usize) -> *mut c_void {
|
||||
Self::get_key(&self.entries, key).expect("Out-of-bounds DTV key")
|
||||
match Self::get_key(&self.entries, key) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
crate::debug_trace!("Dtv::get(): out-of-bounds DTV key: {key}");
|
||||
panic!("Dtv::get(): out-of-bounds DTV key: {key}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a DTV entry, growing the DTV allocation if necessary
|
||||
@ -216,7 +231,8 @@ impl Dtv {
|
||||
self.entries.resize(key, null_mut());
|
||||
}
|
||||
if !Self::set_key(&mut self.entries, key, value) {
|
||||
panic!("Dtv::set(): invalid key");
|
||||
crate::debug_trace!("Dtv::set(): invalid key {key}");
|
||||
panic!("Dtv::set(): invalid key {key}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,9 +247,9 @@ pub fn init_tls_from_auxv<'a, I: Iterator<Item = &'a AuxValue>>(
|
||||
};
|
||||
|
||||
if force || !tls_image.already_initialized {
|
||||
let (base, tp) = clone_tls(&tls_image)?;
|
||||
unsafe { set_thread_pointer(tp) }?;
|
||||
setup_dtv(&tls_image, base)?;
|
||||
let tls = clone_tls(&tls_image)?;
|
||||
unsafe { set_thread_pointer(tls.tp) }?;
|
||||
setup_dtv(&tls_image, &tls)?;
|
||||
}
|
||||
|
||||
Ok(Some(tls_image))
|
||||
@ -248,9 +264,9 @@ pub fn init_tls(image: Option<&TlsImage>, force: bool) -> Result<(), Error> {
|
||||
};
|
||||
|
||||
if force || !image.already_initialized {
|
||||
let (base, tp) = clone_tls(image)?;
|
||||
unsafe { set_thread_pointer(tp) }?;
|
||||
setup_dtv(image, base)?;
|
||||
let tls = clone_tls(image)?;
|
||||
unsafe { set_thread_pointer(tls.tp) }?;
|
||||
setup_dtv(image, &tls)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -265,16 +281,31 @@ fn get_tcb_mut() -> &'static mut TcbHeader {
|
||||
unsafe { &mut *get_tcb_raw() }
|
||||
}
|
||||
|
||||
fn setup_dtv(image: &TlsImage, tls_base: usize) -> Result<(), Error> {
|
||||
fn setup_dtv(image: &TlsImage, tls_info: &TlsInfo) -> Result<(), Error> {
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
const DTV_OFFSET: usize = 0x800;
|
||||
#[cfg(any(not(target_arch = "riscv64"), rust_analyzer))]
|
||||
const DTV_OFFSET: usize = 0;
|
||||
|
||||
let dtv = get_dtv();
|
||||
// Executable itself
|
||||
// NOTE if module 1 is specified again by the dynamic loader, it will be overriden with
|
||||
// what dynamic loader says
|
||||
if let Some(module0_offset) = tls_info.module0_offset {
|
||||
dtv.set(
|
||||
1,
|
||||
core::ptr::without_provenance_mut(tls_info.base + module0_offset + DTV_OFFSET),
|
||||
);
|
||||
}
|
||||
|
||||
if image.module_offsets.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let dtv = get_dtv();
|
||||
for &(module_id, module_offset) in image.module_offsets.iter() {
|
||||
assert!(module_offset < image.full_size);
|
||||
dtv.set(
|
||||
module_id,
|
||||
core::ptr::with_exposed_provenance_mut(tls_base + module_offset),
|
||||
core::ptr::with_exposed_provenance_mut(tls_info.base + module_offset + DTV_OFFSET),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
@ -292,14 +323,12 @@ pub fn get_dtv() -> &'static mut Dtv {
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(target_arch = "x86", target_arch = "x86_64"),
|
||||
any(target_arch = "x86", target_arch = "x86_64", target_arch = "riscv64"),
|
||||
any(feature = "__tls_get_addr", rust_analyzer)
|
||||
))]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn __tls_get_addr(index: *mut usize) -> *mut c_void {
|
||||
let module_id = index.read();
|
||||
let offset = index.add(1).read();
|
||||
assert!(module_id > 0);
|
||||
|
||||
get_dtv().get(module_id).add(offset)
|
||||
}
|
||||
|
@ -3,10 +3,17 @@
|
||||
use abi::error::Error;
|
||||
|
||||
pub fn get_thread_pointer() -> usize {
|
||||
todo!()
|
||||
let output: usize;
|
||||
unsafe { core::arch::asm!("mv {0}, tp", out(reg) output) };
|
||||
output
|
||||
}
|
||||
|
||||
/// Writes `value` into `tp` register.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Usual pointer rules apply.
|
||||
pub unsafe fn set_thread_pointer(value: usize) -> Result<(), Error> {
|
||||
let _ = value;
|
||||
todo!()
|
||||
core::arch::asm!("mv tp, {0}", in(reg) value);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
use abi::{
|
||||
error::Error,
|
||||
mem::{MappingFlags, MappingSource},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
use super::TlsImage;
|
||||
use super::{TlsImage, TlsInfo};
|
||||
|
||||
// Variant I TLS layout:
|
||||
//
|
||||
@ -16,14 +17,16 @@ use super::TlsImage;
|
||||
|
||||
/// Creates a new TLS image in the process memory, copying data from the TLS master copy (if any).
|
||||
/// Returns the resulting thread pointer.
|
||||
pub fn clone_tls(image: &TlsImage) -> Result<(usize, usize), Error> {
|
||||
pub fn clone_tls(image: &TlsImage) -> Result<TlsInfo, Error> {
|
||||
const TCB_SIZE: usize = size_of::<usize>() * 2;
|
||||
|
||||
if !image.align.is_power_of_two() {
|
||||
panic!("TLS layout not aligned to a power of two: {}", image.align)
|
||||
crate::debug_trace!("TLS layout not aligned to a power of two: {}", image.align);
|
||||
unsafe { crate::sys::exit_process(ExitCode::Exited(1)) };
|
||||
}
|
||||
if image.align > 0x1000 {
|
||||
panic!("TODO: TLS alignment larger than a page size is not supported");
|
||||
crate::debug_trace!("TODO: TLS alignment larger than a page size is not supported");
|
||||
unsafe { crate::sys::exit_process(ExitCode::Exited(1)) };
|
||||
}
|
||||
|
||||
// TCB size, padded to align. Also the start of the first module
|
||||
@ -72,7 +75,11 @@ pub fn clone_tls(image: &TlsImage) -> Result<(usize, usize), Error> {
|
||||
|
||||
crate::debug_trace!("TLS: base={:#x}, tp={:#x}", base, tp);
|
||||
|
||||
Ok((base, tp))
|
||||
Ok(TlsInfo {
|
||||
base,
|
||||
tp,
|
||||
module0_offset: Some(tcb_aligned_size),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn get_tcb_raw(tp: usize) -> *mut u8 {
|
||||
|
@ -3,7 +3,7 @@ use abi::{
|
||||
mem::{MappingFlags, MappingSource},
|
||||
};
|
||||
|
||||
use super::TlsImage;
|
||||
use super::{TlsImage, TlsInfo};
|
||||
|
||||
// Variant II TLS layout:
|
||||
//
|
||||
@ -14,7 +14,7 @@ use super::TlsImage;
|
||||
|
||||
/// Creates a new TLS image in the process memory, copying data from the TLS master copy (if any).
|
||||
/// Returns the resulting thread pointer.
|
||||
pub fn clone_tls(image: &TlsImage) -> Result<(usize, usize), Error> {
|
||||
pub fn clone_tls(image: &TlsImage) -> Result<TlsInfo, Error> {
|
||||
// Basically, the layout is:
|
||||
// * align(image.full_size) below the TP
|
||||
// * tcb_size starting with the TP
|
||||
@ -73,7 +73,12 @@ pub fn clone_tls(image: &TlsImage) -> Result<(usize, usize), Error> {
|
||||
|
||||
crate::debug_trace!("TLS: base={:#x}, tp={:#x}", base, tp);
|
||||
|
||||
Ok((base, tp))
|
||||
Ok(TlsInfo {
|
||||
base,
|
||||
tp,
|
||||
module0_offset: None,
|
||||
})
|
||||
// Ok((base, tp))
|
||||
}
|
||||
|
||||
// In Variant II, the TP points directly at the TCB start
|
||||
|
@ -2,52 +2,50 @@
|
||||
#[macro_export]
|
||||
macro_rules! syscall {
|
||||
($num:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
todo!()
|
||||
let mut a0 = usize::from($num);
|
||||
core::arch::asm!("ecall", inlateout("a0") a0);
|
||||
a0
|
||||
}};
|
||||
($num:expr, $a0:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
todo!()
|
||||
($num:expr, $a1:expr $(,)?) => {{
|
||||
let mut a0 = usize::from($num);
|
||||
core::arch::asm!("ecall", inlateout("a0") a0, in("a1") $a1);
|
||||
a0
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
todo!()
|
||||
($num:expr, $a1:expr, $a2:expr $(,)?) => {{
|
||||
let mut a0 = usize::from($num);
|
||||
core::arch::asm!("ecall", inlateout("a0") a0, in("a1") $a1, in("a2") $a2);
|
||||
a0
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
let _ = $a2;
|
||||
todo!()
|
||||
($num:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => {{
|
||||
let mut a0 = usize::from($num);
|
||||
core::arch::asm!("ecall", inlateout("a0") a0, in("a1") $a1, in("a2") $a2, in("a3") $a3);
|
||||
a0
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
let _ = $a2;
|
||||
let _ = $a3;
|
||||
todo!()
|
||||
($num:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => {{
|
||||
let mut a0 = usize::from($num);
|
||||
core::arch::asm!(
|
||||
"ecall",
|
||||
inlateout("a0") a0,
|
||||
in("a1") $a1, in("a2") $a2, in("a3") $a3, in("a4") $a4
|
||||
);
|
||||
a0
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
let _ = $a2;
|
||||
let _ = $a3;
|
||||
let _ = $a4;
|
||||
todo!()
|
||||
($num:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr $(,)?) => {{
|
||||
let mut a0 = usize::from($num);
|
||||
core::arch::asm!(
|
||||
"ecall",
|
||||
inlateout("a0") a0,
|
||||
in("a1") $a1, in("a2") $a2, in("a3") $a3, in("a4") $a4, in("a5") $a5
|
||||
);
|
||||
a0
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
let _ = $a2;
|
||||
let _ = $a3;
|
||||
let _ = $a4;
|
||||
let _ = $a5;
|
||||
todo!()
|
||||
($num:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr $(,)?) => {{
|
||||
let mut a0 = usize::from($num);
|
||||
core::arch::asm!(
|
||||
"ecall",
|
||||
inlateout("a0") a0,
|
||||
in("a1") $a1, in("a2") $a2, in("a3") $a3, in("a4") $a4, in("a5") $a5, in("a6") $a6
|
||||
);
|
||||
a0
|
||||
}};
|
||||
}
|
||||
|
4
userspace/arch/riscv64/inittab
Normal file
4
userspace/arch/riscv64/inittab
Normal file
@ -0,0 +1,4 @@
|
||||
init:1:wait:/sbin/rc default
|
||||
logd:1:once:/sbin/logd
|
||||
|
||||
user:1:once:/sbin/login /dev/ttyS0
|
@ -71,17 +71,13 @@ fn run(binary: &str, args: &[String]) -> Result<!, Error> {
|
||||
});
|
||||
|
||||
for module in layout.segments.iter() {
|
||||
if module.object_id == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
auxv.push(AuxValue {
|
||||
tag: auxv::TLS_MODULE_ID,
|
||||
val: module.object_id as _,
|
||||
val: module.object_id as u64 + 1,
|
||||
});
|
||||
auxv.push(AuxValue {
|
||||
tag: auxv::TLS_MODULE_OFFSET,
|
||||
val: module.offset as _,
|
||||
val: module.offset as u64,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -124,6 +120,12 @@ unsafe fn enter(entry: extern "C" fn(usize), argument: usize) -> ! {
|
||||
options(att_syntax, noreturn)
|
||||
);
|
||||
}
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
{
|
||||
let _ = entry;
|
||||
let _ = argument;
|
||||
todo!()
|
||||
}
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", rust_analyzer))]
|
||||
{
|
||||
entry(argument);
|
||||
|
@ -8,6 +8,8 @@ mod aarch64;
|
||||
mod x86_64;
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
mod i686;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
mod riscv64;
|
||||
|
||||
pub enum RelaValue {
|
||||
DQWord(i64, i64),
|
||||
|
33
userspace/dyn-loader/src/relocation/riscv64.rs
Normal file
33
userspace/dyn-loader/src/relocation/riscv64.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use elf::relocation::{Rel, Rela};
|
||||
|
||||
use crate::{error::Error, object::ResolvedSymbol, state::State};
|
||||
|
||||
use super::{RelValue, RelaValue, Relocation};
|
||||
|
||||
impl Relocation for Rel {
|
||||
type Value = RelValue;
|
||||
|
||||
fn resolve(
|
||||
&self,
|
||||
_state: &State,
|
||||
_name: &str,
|
||||
_symbol: &ResolvedSymbol,
|
||||
_load_base: usize,
|
||||
) -> Result<Option<Self::Value>, Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Relocation for Rela {
|
||||
type Value = RelaValue;
|
||||
|
||||
fn resolve(
|
||||
&self,
|
||||
_state: &State,
|
||||
_name: &str,
|
||||
_symbol: &ResolvedSymbol,
|
||||
_load_base: usize,
|
||||
) -> Result<Option<Self::Value>, Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ cfg_if! {
|
||||
} else if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
|
||||
mod variant2;
|
||||
pub use variant2::TlsLayoutImpl;
|
||||
} else if #[cfg(target_arch = "aarch64")] {
|
||||
} else if #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] {
|
||||
mod variant1;
|
||||
pub use variant1::TlsLayoutImpl;
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ pub struct KernelProcessed(pub KernelBuilt);
|
||||
pub struct InitrdGenerated(pub PathBuf);
|
||||
pub struct ImageBuilt(pub PathBuf);
|
||||
pub enum AllBuilt {
|
||||
Riscv64(KernelProcessed),
|
||||
Riscv64(KernelProcessed, InitrdGenerated),
|
||||
X86_64(ImageBuilt),
|
||||
AArch64(KernelProcessed, InitrdGenerated),
|
||||
I686(ImageBuilt),
|
||||
@ -99,16 +99,13 @@ pub fn build_all(env: &BuildEnv) -> Result<AllBuilt, Error> {
|
||||
// for module in modules {
|
||||
// install_extra.push((module.clone(), module.file_name().unwrap().into()));
|
||||
// }
|
||||
if env.arch == Arch::riscv64 {
|
||||
return Ok(AllBuilt::Riscv64(kernel));
|
||||
}
|
||||
|
||||
// Userspace stuff
|
||||
let initrd = userspace::build_initrd(env, install_extra, check)?;
|
||||
|
||||
// Build target-specific image
|
||||
let image = match env.arch {
|
||||
Arch::riscv64 => AllBuilt::Riscv64(kernel),
|
||||
Arch::riscv64 => AllBuilt::Riscv64(kernel, initrd),
|
||||
Arch::aarch64 => AllBuilt::AArch64(kernel, initrd),
|
||||
Arch::x86_64 => AllBuilt::X86_64(x86_64::build_image(env, kernel, initrd)?),
|
||||
Arch::i686 => AllBuilt::I686(i686::build_image(env, kernel, initrd)?),
|
||||
|
@ -22,6 +22,12 @@ pub struct AArch64TargetConfig {
|
||||
pub components: BuildComponents,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Riscv64TargetConfig {
|
||||
pub components: BuildComponents,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct X86_64TargetConfig {
|
||||
@ -38,6 +44,7 @@ pub struct I686TargetConfig {
|
||||
#[serde(default)]
|
||||
pub struct TargetConfig {
|
||||
pub aarch64: AArch64TargetConfig,
|
||||
pub riscv64: Riscv64TargetConfig,
|
||||
pub x86_64: X86_64TargetConfig,
|
||||
pub i686: I686TargetConfig,
|
||||
}
|
||||
@ -193,7 +200,7 @@ impl BuildEnv {
|
||||
impl XTaskConfig {
|
||||
pub fn components(&self, env: &BuildEnv) -> &BuildComponents {
|
||||
match env.arch {
|
||||
Arch::riscv64 => todo!(),
|
||||
Arch::riscv64 => &self.target.riscv64.components,
|
||||
Arch::aarch64 => &self.target.aarch64.components,
|
||||
Arch::x86_64 => &self.target.x86_64.components,
|
||||
Arch::i686 => &self.target.i686.components,
|
||||
|
@ -260,6 +260,7 @@ fn run_riscv64(
|
||||
qemu_bin: Option<PathBuf>,
|
||||
devices: Vec<QemuDevice>,
|
||||
kernel: PathBuf,
|
||||
initrd: PathBuf,
|
||||
) -> Result<Command, Error> {
|
||||
let _ = config;
|
||||
let _ = devices;
|
||||
@ -272,7 +273,11 @@ fn run_riscv64(
|
||||
qemu.with_serial(QemuSerialTarget::MonStdio)
|
||||
.with_machine(riscv64::Machine::Virt)
|
||||
.with_cpu(riscv64::Cpu::Rv64)
|
||||
.with_boot_image(riscv64::Image::OpenSBI { kernel, bios });
|
||||
.with_boot_image(riscv64::Image::OpenSBI {
|
||||
kernel,
|
||||
initrd,
|
||||
bios,
|
||||
});
|
||||
|
||||
Ok(qemu.into_command())
|
||||
}
|
||||
@ -349,8 +354,8 @@ pub fn run(
|
||||
add_devices_from_config(&mut devices, disk.as_ref(), &config)?;
|
||||
|
||||
let mut command = match built {
|
||||
AllBuilt::Riscv64(KernelProcessed(KernelBuilt(kernel))) => {
|
||||
run_riscv64(&config, &env, qemu, devices, kernel)?
|
||||
AllBuilt::Riscv64(KernelProcessed(KernelBuilt(kernel)), InitrdGenerated(initrd)) => {
|
||||
run_riscv64(&config, &env, qemu, devices, kernel, initrd)?
|
||||
}
|
||||
AllBuilt::AArch64(KernelProcessed(KernelBuilt(kernel)), InitrdGenerated(initrd)) => {
|
||||
make_kernel_bin(kernel, &kernel_bin)?;
|
||||
|
Loading…
x
Reference in New Issue
Block a user