use ax_memory_addr::{PhysAddr, VirtAddr};
use riscv::{
asm,
register::{satp, sstatus, stvec},
};
#[cfg(feature = "tls")]
use crate::KernelTlsBase;
#[cfg(feature = "uspace")]
use crate::{InstalledAddressSpace, InstalledAddressSpaceMode};
pub fn address_space_tag_capacity(cpu_count: usize) -> u32 {
let original = satp::read();
unsafe { satp::set(original.mode(), u16::MAX as usize, original.ppn()) };
let mask = satp::read().asid();
unsafe { satp::set(original.mode(), original.asid(), original.ppn()) };
asm::sfence_vma_all();
let capacity = mask
.checked_add(1)
.and_then(|value| u32::try_from(value).ok())
.unwrap_or(1);
if capacity as usize > cpu_count.saturating_mul(2) {
capacity
} else {
1
}
}
#[cfg(feature = "uspace")]
fn flush_tlb_asid(asid: u16) {
unsafe {
core::arch::asm!(
"sfence.vma x0, {asid}",
asid = in(reg) usize::from(asid),
options(nostack),
)
}
}
#[cfg(feature = "uspace")]
pub unsafe fn install_user_address_space(address_space: InstalledAddressSpace) {
address_space.validate_architecture_support();
let tagged = matches!(address_space.mode(), InstalledAddressSpaceMode::Tagged);
let asid = if tagged {
usize::from(address_space.hardware_tag())
} else {
0
};
let mode = satp::read().mode();
unsafe { satp::set(mode, asid, address_space.root().as_usize() >> 12) };
if tagged {
flush_tlb_asid(address_space.hardware_tag());
} else {
asm::sfence_vma_all();
}
}
#[inline]
pub fn enable_irqs() {
unsafe { sstatus::set_sie() }
}
#[inline]
pub fn disable_irqs() {
unsafe { sstatus::clear_sie() }
}
#[inline]
pub fn irqs_enabled() -> bool {
sstatus::read().sie()
}
#[inline]
pub fn wait_for_irqs() {
riscv::asm::wfi()
}
#[inline]
pub fn wait_for_irqs_disabled() {
debug_assert!(!irqs_enabled());
riscv::asm::wfi();
enable_irqs();
}
#[inline]
pub fn halt() {
disable_irqs();
riscv::asm::wfi() }
#[inline]
pub fn read_user_page_table() -> PhysAddr {
pa!(satp::read().ppn() << 12)
}
#[inline]
pub fn read_kernel_page_table() -> PhysAddr {
read_user_page_table()
}
#[inline]
pub unsafe fn write_user_page_table(root_paddr: PhysAddr) {
let mode = satp::read().mode();
unsafe { satp::set(mode, 0, root_paddr.as_usize() >> 12) };
}
#[inline]
pub unsafe fn write_kernel_page_table(root_paddr: PhysAddr) {
unsafe { write_user_page_table(root_paddr) };
}
#[inline]
pub fn flush_icache_all() {
riscv::asm::fence_i();
}
#[inline]
pub fn flush_tlb(vaddr: Option<VirtAddr>) {
if let Some(vaddr) = vaddr {
unsafe {
core::arch::asm!(
"sfence.vma {addr}, x0",
addr = in(reg) vaddr.as_usize(),
options(nostack),
)
}
} else {
asm::sfence_vma_all();
}
}
#[inline]
pub fn update_mmu_cache(vaddr: VirtAddr) {
flush_tlb(Some(vaddr));
}
#[inline]
pub unsafe fn write_trap_vector_base(stvec: usize) {
let mut reg = stvec::read();
reg.set_address(stvec);
reg.set_trap_mode(stvec::TrapMode::Direct);
unsafe { stvec::write(reg) }
}
#[inline]
#[cfg(feature = "tls")]
pub fn read_thread_pointer() -> KernelTlsBase {
let tp;
unsafe { core::arch::asm!("mv {}, tp", out(reg) tp) };
KernelTlsBase::new(tp)
}
#[inline]
#[cfg(feature = "tls")]
pub unsafe fn write_thread_pointer(tls_base: KernelTlsBase) {
unsafe { core::arch::asm!("mv tp, {}", in(reg) tls_base.as_usize()) }
}
#[cfg(feature = "uspace")]
core::arch::global_asm!(
include_asm_macros!(),
include_str!("user_copy.S"),
include_str!("user_atomic.S"),
);
#[cfg(feature = "uspace")]
unsafe extern "C" {
pub fn user_copy(dst: *mut u8, src: *const u8, size: usize) -> usize;
}
#[cfg(feature = "uspace")]
#[inline]
pub unsafe fn user_access_ok_page(_vaddr: usize, _access: crate::UserAccessType) -> bool {
false
}