use core::arch::naked_asm;
use loongArch64::register::{MemoryAccessType, crmd, pgdh, pgdl, pwch::*, pwcl::*, stlbps};
use num_align::NumAlign;
use page_table_generic::{MapConfig, MemAttributes, PteConfig, TableMeta, VirtAddr};
pub use super::pte::Entry;
use crate::{
arch::addrspace::to_phys,
console::print_mapping,
consts::PAGE_SIZE,
mem::{__kimage_va, __va, MB, PageTableInfo},
smp::PerCpuMeta,
};
#[cfg(page_size_4k)]
const PS: usize = 0x0c;
#[cfg(page_size_16k)]
const PS: u64 = 0x0e;
pub const PAGE_SHIFT: usize = PAGE_SIZE.trailing_zeros() as usize;
pub const PTE_INDEX_BITS: usize = PAGE_SHIFT - 3;
#[inline(always)]
pub fn local_flush_tlb_all() {
unsafe {
core::arch::asm!("dbar 0; tlbflush", options(nomem, nostack));
}
}
#[inline(always)]
pub fn local_flush_tlb_page(vaddr: usize) {
unsafe {
core::arch::asm!(
"invtlb 0x5, $zero, {}",
in(reg) vaddr,
options(nomem, nostack)
);
}
}
pub fn setup() {
stlbps::set_ps(PS);
set_dir3_base(12 + 9 + 9 + 9);
set_dir3_width(9);
set_dir2_base(12 + 9 + 9);
set_dir2_width(9);
set_dir1_base(12 + 9);
set_dir1_width(9);
set_ptbase(12);
set_ptwidth(9);
set_pte_width(8);
local_flush_tlb_all();
}
#[derive(Clone, Copy)]
pub struct Generic;
#[cfg(page_size_4k)]
impl TableMeta for Generic {
type P = Entry;
const PAGE_SIZE: usize = 0x1000;
const LEVEL_BITS: &[usize] = &[
PTE_INDEX_BITS, PTE_INDEX_BITS, PTE_INDEX_BITS, PTE_INDEX_BITS, ];
const MAX_BLOCK_LEVEL: usize = 1;
fn flush(vaddr: Option<VirtAddr>) {
match vaddr {
Some(va) => local_flush_tlb_page(va.raw()),
None => local_flush_tlb_all(),
}
}
}
pub fn relocate_kernel_to_vm_code() -> ! {
let k_start = crate::mem::kimage_range().start;
let mut table = crate::mem::mmu::new_boot_table();
let pte = PteConfig {
valid: true,
read: true,
writable: true,
executable: true,
mem_attr: MemAttributes::Normal,
..Default::default()
};
println!("Page table entry flags: {:?}", pte);
let v_start = __kimage_va(k_start);
let v_end = v_start as usize + crate::mem::kimage_range().len();
let size = v_end.align_up(2 * MB) - v_start as usize;
print_mapping("KImage", v_start as _, k_start, size);
println!(
"Mapping: vaddr={:#x}, paddr={:#x}, size={:#x}",
v_start as usize, k_start, size
);
table
.map(&MapConfig {
vaddr: v_start.into(),
paddr: k_start.into(),
size,
pte,
allow_huge: true,
flush: false,
})
.unwrap();
let tb_addr = table.root_paddr();
crate::mem::mmu::set_boot_table(table);
println!("Boot page table at physical address: {:#x}", tb_addr.raw());
let mmu_entry_phys = to_phys(super::entry::mmu_entry as *const () as usize);
println!("MMU Entry point at physical address: {:#x}", mmu_entry_phys);
let v_entry = __kimage_va(mmu_entry_phys) as usize;
println!("MMU Entry virtual address: {:#x}", v_entry);
let tb = PageTableInfo {
asid: 0,
addr: tb_addr.into(),
};
let v_sp = __va(to_phys(sym_running_addr!(__cpu0_stack_top))) as usize;
let v_entry = __kimage_va(mmu_entry_phys) as usize;
println!("Setting up page table...");
pgdh::set_base(tb.addr as _);
pgdl::set_base(tb.addr as _);
unsafe {
core::arch::asm!("dbar 0", options(nomem, nostack));
}
println!("Enabling MMU...");
setup();
println!("MMU enabled, jumping to {v_entry:#x}, sp={v_sp:#x}");
crate::arch::relocate::reset();
unsafe {
core::arch::asm!("ibar 0", options(nomem, nostack));
core::arch::asm!("dbar 0", options(nomem, nostack));
}
relocate_kernel(v_entry, v_sp);
unreachable!()
}
pub fn enable_mmu_secondary(cpu_meta_paddr: usize) -> ! {
let meta = unsafe {
let meta_va = super::addrspace::to_cache(cpu_meta_paddr);
&*(meta_va as *const PerCpuMeta)
};
pgdh::set_base(meta.boot_table_paddr);
pgdl::set_base(meta.boot_table_paddr);
setup();
super::trap::init_entries_for_secondary();
let mut crmd_bits = crmd::read().raw();
crmd_bits &= !(1 << 3);
crmd_bits |= 1 << 4;
crmd_bits &= !(0b11 << 5);
crmd_bits |= (MemoryAccessType::CoherentCached as usize) << 5;
crmd_bits &= !(0b11 << 7);
crmd_bits |= (MemoryAccessType::CoherentCached as usize) << 7;
unsafe {
core::arch::asm!("csrwr {}, {}", in(reg) crmd_bits, const 0x0);
}
crate::mem::mmu::set_mmu_enabled();
jump_to_secondary_entry(cpu_meta_paddr, meta.stack_top_virt, meta.entry_virt)
}
#[unsafe(naked)]
extern "C" fn jump_to_secondary_entry(_arg: usize, _sp: usize, _entry: usize) -> ! {
naked_asm!(
"
ibar 0
dbar 0
move $sp, $a1
jr $a2
"
)
}
#[unsafe(naked)]
extern "C" fn relocate_kernel(entry: usize, sp: usize) {
naked_asm!(
"
move $sp, $a1
jr $a0
",
)
}