use core::arch::asm;
use aarch64_cpu::{asm::barrier, registers::*};
use ax_memory_addr::{PhysAddr, VirtAddr};
use super::asid::configured_tag_capacity;
#[cfg(kernel_tls)]
use crate::KernelTlsBase;
#[cfg(feature = "uspace")]
use crate::mmu::HardwareAddressSpace;
pub fn address_space_tag_capacity() -> u32 {
configured_tag_capacity(
ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::ASIDBits),
TCR_EL1.read(TCR_EL1::AS),
)
}
#[cfg(feature = "uspace")]
fn flush_tlb_asid(asid: u16) {
let operand = u64::from(asid) << 48;
unsafe {
asm!(
"dsb ishst; tlbi aside1is, {operand}; dsb ish; isb",
operand = in(reg) operand,
)
}
}
#[cfg(feature = "uspace")]
pub unsafe fn install_user_address_space(address_space: HardwareAddressSpace) {
if address_space.hardware_tag() != 0 {
let capacity = address_space_tag_capacity();
if u32::from(address_space.hardware_tag()) < capacity {
flush_tlb_asid(address_space.hardware_tag());
let value = address_space.root().as_usize() as u64
| (u64::from(address_space.hardware_tag()) << 48);
TTBR0_EL1.set(value);
barrier::isb(barrier::SY);
return;
}
}
TTBR0_EL1.set(address_space.root().as_usize() as u64);
flush_tlb(None);
}
#[inline]
pub fn enable_irqs() {
unsafe { asm!("msr daifclr, #2") };
}
#[inline]
pub fn disable_irqs() {
unsafe { asm!("msr daifset, #2") };
}
#[inline]
pub fn irqs_enabled() -> bool {
!DAIF.matches_all(DAIF::I::Masked)
}
#[inline]
pub fn wait_for_irqs() {
aarch64_cpu::asm::wfi();
}
#[inline]
pub fn wait_for_irqs_disabled() {
debug_assert!(!irqs_enabled());
barrier::dsb(barrier::SY);
aarch64_cpu::asm::wfi();
enable_irqs();
}
#[inline]
pub fn halt() {
disable_irqs();
aarch64_cpu::asm::wfi(); }
#[inline]
pub fn read_kernel_page_table() -> PhysAddr {
super::mmu::El1::read_kernel_page_table()
}
#[inline]
pub fn read_user_page_table() -> PhysAddr {
const TTBR_BADDR_MASK: u64 = (1 << 48) - 1;
let root = TTBR0_EL1.get() & TTBR_BADDR_MASK;
pa!(root as usize)
}
#[inline]
pub unsafe fn write_kernel_page_table(root_paddr: PhysAddr) {
unsafe { super::mmu::El1::write_kernel_page_table(root_paddr) };
}
#[inline]
pub unsafe fn write_user_page_table(root_paddr: PhysAddr) {
TTBR0_EL1.set(root_paddr.as_usize() as _);
}
#[inline]
pub fn synchronize_page_table_writes() {
unsafe { asm!("dsb ishst") };
}
#[inline]
pub fn flush_tlb(vaddr: Option<VirtAddr>) {
super::mmu::El1::flush_tlb(vaddr);
}
#[inline]
pub fn update_mmu_cache(_vaddr: VirtAddr) {}
#[inline]
pub fn flush_icache_all() {
unsafe { asm!("ic ialluis; dsb ish; isb") };
}
#[inline]
fn read_ctr_el0() -> u64 {
let value;
unsafe {
asm!("mrs {}, ctr_el0", out(reg) value);
}
value
}
#[inline]
pub fn dcache_line_size_from_ctr() -> usize {
let ctr = read_ctr_el0();
let dminline = ((ctr >> 16) & 0xf) as usize;
4usize << dminline
}
#[inline]
pub fn icache_line_size_from_ctr() -> usize {
let ctr = read_ctr_el0();
let iminline = (ctr & 0xf) as usize;
4usize << iminline
}
#[inline]
#[cfg(kernel_tls)]
pub fn read_thread_pointer() -> KernelTlsBase {
KernelTlsBase::new(TPIDR_EL0.get() as usize)
}
#[inline]
#[cfg(kernel_tls)]
pub unsafe fn write_thread_pointer(kernel_tls: KernelTlsBase) {
TPIDR_EL0.set(kernel_tls.as_usize() as _)
}
#[inline]
pub fn enable_fp() {
CPACR_EL1.write(CPACR_EL1::FPEN::TrapNothing);
barrier::isb(barrier::SY);
}
#[cfg(feature = "uspace")]
core::arch::global_asm!(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 {
let par: u64;
unsafe {
if access == crate::UserAccessType::Write {
asm!(
"at s1e0w, {vaddr}",
"isb",
"mrs {par}, par_el1",
vaddr = in(reg) vaddr,
par = out(reg) par,
options(nostack, preserves_flags),
);
} else {
asm!(
"at s1e0r, {vaddr}",
"isb",
"mrs {par}, par_el1",
vaddr = in(reg) vaddr,
par = out(reg) par,
options(nostack, preserves_flags),
);
}
}
par & 1 == 0
}