mod multislot_portal;
pub use multislot_portal::MultislotPortal;
use crate::{build_sstatus, LocalContext};
#[cfg(target_arch = "riscv64")]
use spin::Lazy;
#[repr(C)]
pub struct PortalCache {
a0: usize, a1: usize, satp: usize, sstatus: usize, sepc: usize, stvec: usize, sscratch: usize, }
impl PortalCache {
#[inline]
pub fn init(&mut self, satp: usize, pc: usize, a0: usize, supervisor: bool, interrupt: bool) {
self.satp = satp;
self.sepc = pc;
self.a0 = a0;
self.sstatus = build_sstatus(supervisor, interrupt);
}
#[inline]
pub fn address(&mut self) -> usize {
self as *mut _ as _
}
}
pub trait ForeignPortal {
unsafe fn transit_entry(&self) -> usize;
unsafe fn transit_cache(&mut self, key: impl SlotKey) -> &mut PortalCache;
}
pub trait MonoForeignPortal {
fn total_size(&self) -> usize;
fn transit_address(&self) -> usize;
fn text_offset(&self) -> usize;
fn cache_offset(&self, key: usize) -> usize;
}
impl<T: MonoForeignPortal> ForeignPortal for T {
#[inline]
unsafe fn transit_entry(&self) -> usize {
self.transit_address() + self.text_offset()
}
#[inline]
unsafe fn transit_cache(&mut self, key: impl SlotKey) -> &mut PortalCache {
&mut *((self.transit_address() + self.cache_offset(key.index())) as *mut _)
}
}
pub struct ForeignContext {
pub context: LocalContext,
pub satp: usize,
}
impl ForeignContext {
pub unsafe fn execute(&mut self, portal: &mut impl ForeignPortal, key: impl SlotKey) -> usize {
use core::mem::replace;
let supervisor = replace(&mut self.context.supervisor, true);
let interrupt = replace(&mut self.context.interrupt, false);
let entry = portal.transit_entry();
let cache = portal.transit_cache(key);
cache.init(
self.satp,
self.context.sepc,
self.context.a(0),
supervisor,
interrupt,
);
*self.context.pc_mut() = entry;
*self.context.a_mut(0) = cache.address();
let sstatus = self.context.execute();
self.context.supervisor = supervisor;
self.context.interrupt = interrupt;
*self.context.a_mut(0) = cache.a0;
sstatus
}
}
pub trait SlotKey {
fn index(self) -> usize;
}
impl SlotKey for () {
#[inline]
fn index(self) -> usize {
0
}
}
impl SlotKey for usize {
#[inline]
fn index(self) -> usize {
self
}
}
pub struct TpReg;
impl SlotKey for TpReg {
#[inline]
fn index(self) -> usize {
#[cfg(target_arch = "riscv64")]
{
let ans: usize;
unsafe { core::arch::asm!("mv {}, tp", out(reg) ans) };
ans
}
#[cfg(not(target_arch = "riscv64"))]
unimplemented!("TpReg::index() is only supported on riscv64")
}
}
#[cfg(target_arch = "riscv64")]
struct PortalText(&'static [u16]);
#[cfg(target_arch = "riscv64")]
static PORTAL_TEXT: Lazy<PortalText> = Lazy::new(PortalText::new);
#[cfg(target_arch = "riscv64")]
impl PortalText {
pub fn new() -> Self {
for len in 32.. {
let slice = unsafe { core::slice::from_raw_parts(foreign_execute as *const _, len) };
if slice.ends_with(&[0x8502, 0]) {
return Self(slice);
}
}
unreachable!()
}
#[inline]
pub fn aligned_size(&self) -> usize {
const USIZE_MASK: usize = core::mem::size_of::<usize>() - 1;
(self.0.len() * core::mem::size_of::<u16>() + USIZE_MASK) & !USIZE_MASK
}
#[inline]
pub unsafe fn copy_to(&self, address: usize) {
(address as *mut u16).copy_from_nonoverlapping(self.0.as_ptr(), self.0.len());
}
}
#[cfg(target_arch = "riscv64")]
#[unsafe(naked)]
unsafe extern "C" fn foreign_execute(ctx: *mut PortalCache) {
core::arch::naked_asm!(
" .option push
.option nopic
",
" sd a1, 1*8(a0)",
" ld a1, 2*8(a0)
csrrw a1, satp, a1
sfence.vma
sd a1, 2*8(a0)
",
" ld a1, 3*8(a0)
csrw sstatus, a1
",
" ld a1, 4*8(a0)
csrw sepc, a1
",
" la a1, 1f
csrrw a1, stvec, a1
sd a1, 5*8(a0)
",
" csrrw a1, sscratch, a0
sd a1, 6*8(a0)
",
" ld a1, 1*8(a0)
ld a0, (a0)
",
" sret",
" .align 2",
"1: csrrw a0, sscratch, a0",
" sd a1, 1*8(a0)",
" ld a1, 6*8(a0)
csrrw a1, sscratch, a1
sd a1, (a0)
",
" ld a1, 2*8(a0)
csrrw a1, satp, a1
sfence.vma
sd a1, 2*8(a0)
",
" ld a1, 1*8(a0)",
" ld a0, 5*8(a0)
csrw stvec, a0
",
" jr a0",
" .half 0",
" .option pop",
)
}