riscv64: position-independent kernel
This commit is contained in:
@@ -124,8 +124,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
|
||||
// TODO stack is leaked
|
||||
let satp = InMemoryRegister::new(0);
|
||||
let kernel_table_phys =
|
||||
((&raw const mem::KERNEL_TABLES).addr() - KERNEL_VIRT_OFFSET) as u64;
|
||||
let kernel_table_phys = mem::fixed::table_physical_address().into_u64();
|
||||
satp.write(SATP::MODE::Sv39 + SATP::ASID.val(0) + SATP::PPN.val(kernel_table_phys >> 12));
|
||||
|
||||
Ok(Self {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
use kernel_arch_interface::sync::IrqSafeSpinlock;
|
||||
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
|
||||
|
||||
use crate::{
|
||||
mem::{
|
||||
auto_lower_address,
|
||||
table::{PageEntry, PageTable, L1},
|
||||
KERNEL_VIRT_OFFSET,
|
||||
},
|
||||
ArchitectureImpl,
|
||||
};
|
||||
|
||||
pub const IDENTITY_SIZE_L1: usize = 64;
|
||||
|
||||
pub(super) static mut KERNEL_L1: PageTable<L1> = const {
|
||||
let mut table = PageTable::zeroed();
|
||||
|
||||
let mut index = 0;
|
||||
while index < IDENTITY_SIZE_L1 {
|
||||
let entry = PageEntry::identity_block(PhysicalAddress::from_usize(index << L1::SHIFT));
|
||||
table.entries[index] = entry;
|
||||
table.entries[index + ((KERNEL_VIRT_OFFSET >> L1::SHIFT) & 0x1FF)] = entry;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
table
|
||||
};
|
||||
pub(super) static LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
|
||||
|
||||
pub fn table_physical_address() -> PhysicalAddress {
|
||||
PhysicalAddress::from_usize(auto_lower_address(&raw const KERNEL_L1))
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use libk_mm_interface::table::{EntryLevel, EntryLevelExt};
|
||||
|
||||
use crate::mem::table::L3;
|
||||
|
||||
pub fn tlb_flush_global_full() {
|
||||
tlb_flush_full();
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_global_va(va: usize) {
|
||||
tlb_flush_va(va);
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va(start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va(page);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va_asid(asid: usize, start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va_asid(page, asid);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_full() {
|
||||
unsafe { core::arch::asm!("sfence.vma") };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va(va: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, zero", in(reg) va) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_asid(asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma zero, {0}", in(reg) asid) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, {1}", in(reg) va, in(reg) asid) };
|
||||
}
|
||||
@@ -1,70 +1,26 @@
|
||||
use cfg_if::cfg_if;
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
split_spinlock,
|
||||
use kernel_arch_interface::mem::{
|
||||
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
|
||||
};
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
table::{page_index, EntryLevel, EntryLevelExt},
|
||||
};
|
||||
use memtables::riscv64::PageAttributes;
|
||||
use static_assertions::{const_assert, const_assert_eq};
|
||||
use table::{PageEntry, PageTable, L1, L2, L3};
|
||||
use tock_registers::interfaces::Writeable;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub use memtables::riscv64::FixedTables;
|
||||
use crate::{
|
||||
mem::table::{PageTable, L1, L3},
|
||||
registers::SATP,
|
||||
};
|
||||
|
||||
use crate::registers::SATP;
|
||||
pub use intrinsics::*;
|
||||
|
||||
pub mod fixed;
|
||||
pub mod intrinsics;
|
||||
pub mod process;
|
||||
pub mod table;
|
||||
|
||||
split_spinlock! {
|
||||
use crate::ArchitectureImpl;
|
||||
use crate::mem::FixedTables;
|
||||
use libk_mm_interface::KernelImageObject;
|
||||
|
||||
#[link_section = ".data.tables"]
|
||||
#[used]
|
||||
static KERNEL_TABLES: KernelImageObject<FixedTables> =
|
||||
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "riscv64_board_virt")] {
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
|
||||
} else if #[cfg(feature = "riscv64_board_jh7110")] {
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x40200000;
|
||||
} else if #[cfg(rust_analyzer)] {
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
|
||||
}
|
||||
}
|
||||
|
||||
pub const KERNEL_VIRT_OFFSET: usize = kernel_arch_interface::KERNEL_VIRT_OFFSET;
|
||||
pub const SIGN_EXTEND_MASK: usize = 0xFFFFFF80_00000000;
|
||||
|
||||
pub const KERNEL_START_L1I: usize = page_index::<L1>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
pub const KERNEL_L2I: usize = page_index::<L2>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
const_assert_eq!(KERNEL_L2I, 1);
|
||||
|
||||
// Runtime mappings
|
||||
// 1GiB of device memory space
|
||||
const DEVICE_MAPPING_L1I: usize = KERNEL_START_L1I + 1;
|
||||
const DEVICE_MAPPING_L3_COUNT: usize = 4;
|
||||
// 32GiB of RAM space
|
||||
const RAM_MAPPING_START_L1I: usize = KERNEL_START_L1I + 2;
|
||||
const RAM_MAPPING_L1_COUNT: usize = 32;
|
||||
const_assert!(RAM_MAPPING_START_L1I + RAM_MAPPING_L1_COUNT <= 512);
|
||||
const_assert!(DEVICE_MAPPING_L1I < 512);
|
||||
|
||||
const DEVICE_MAPPING_OFFSET: usize = (DEVICE_MAPPING_L1I << L1::SHIFT) | SIGN_EXTEND_MASK;
|
||||
const RAM_MAPPING_OFFSET: usize = (RAM_MAPPING_START_L1I << L1::SHIFT) | SIGN_EXTEND_MASK;
|
||||
|
||||
// Runtime tables
|
||||
static mut DEVICE_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
|
||||
static mut DEVICE_MAPPING_L3S: [PageTable<L3>; DEVICE_MAPPING_L3_COUNT] =
|
||||
[const { PageTable::zeroed() }; DEVICE_MAPPING_L3_COUNT];
|
||||
|
||||
/// Any VAs above this one are sign-extended
|
||||
pub const USER_BOUNDARY: usize = 0x40_00000000;
|
||||
@@ -75,17 +31,20 @@ pub struct KernelTableManagerImpl;
|
||||
impl KernelTableManager for KernelTableManagerImpl {
|
||||
fn virtualize(address: u64) -> usize {
|
||||
let address = address as usize;
|
||||
if address >= RAM_MAPPING_OFFSET {
|
||||
panic!("Invalid physical address: {address:#x}");
|
||||
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
|
||||
address + KERNEL_VIRT_OFFSET
|
||||
} else {
|
||||
panic!("Invalid physical address: {address:#x}")
|
||||
}
|
||||
address + RAM_MAPPING_OFFSET
|
||||
}
|
||||
|
||||
fn physicalize(address: usize) -> u64 {
|
||||
if address < RAM_MAPPING_OFFSET {
|
||||
panic!("Invalid \"physicalized\" virtual address {address:#x}");
|
||||
if address < KERNEL_VIRT_OFFSET
|
||||
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
|
||||
{
|
||||
panic!("Invalid virtualized address: {address:#x}");
|
||||
}
|
||||
(address - RAM_MAPPING_OFFSET) as u64
|
||||
(address - KERNEL_VIRT_OFFSET) as u64
|
||||
}
|
||||
|
||||
unsafe fn map_device_pages(
|
||||
@@ -93,146 +52,32 @@ impl KernelTableManager for KernelTableManagerImpl {
|
||||
count: usize,
|
||||
attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
|
||||
unsafe { map_device_memory(PhysicalAddress::from_u64(base), count, attrs) }
|
||||
let _ = attrs;
|
||||
let _lock = fixed::LOCK.lock();
|
||||
let base = PhysicalAddress::from_u64(base);
|
||||
let l3_aligned_base = base.page_align_down::<L3>();
|
||||
let l3_aligned_end = base.add(count).page_align_up::<L3>();
|
||||
let l3_offset = base - l3_aligned_base;
|
||||
let l3_page_count = (l3_aligned_end - l3_aligned_base).page_count::<L3>();
|
||||
let l3_aligned_virt = l3_aligned_base.add(KERNEL_VIRT_OFFSET).into_usize();
|
||||
|
||||
Ok(unsafe {
|
||||
RawDeviceMemoryMapping::from_raw_parts(
|
||||
l3_aligned_base.into_u64(),
|
||||
l3_aligned_virt + l3_offset,
|
||||
l3_aligned_virt,
|
||||
l3_page_count,
|
||||
L3::SIZE,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
|
||||
unsafe { unmap_device_memory(mapping) }
|
||||
let _ = mapping;
|
||||
}
|
||||
}
|
||||
|
||||
// Device mappings
|
||||
unsafe fn map_device_memory_l3(
|
||||
base: PhysicalAddress,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<usize, Error> {
|
||||
// TODO don't map pages if already mapped
|
||||
|
||||
'l0: for i in 0..DEVICE_MAPPING_L3_COUNT * 512 {
|
||||
for j in 0..count {
|
||||
let l2i = (i + j) / 512;
|
||||
let l3i = (i + j) % 512;
|
||||
|
||||
unsafe {
|
||||
if DEVICE_MAPPING_L3S[l2i][l3i].is_present() {
|
||||
continue 'l0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for j in 0..count {
|
||||
let l2i = (i + j) / 512;
|
||||
let l3i = (i + j) % 512;
|
||||
|
||||
unsafe {
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] =
|
||||
PageEntry::page(base.add(j * L3::SIZE), PageAttributes::W);
|
||||
}
|
||||
}
|
||||
|
||||
let start = DEVICE_MAPPING_OFFSET + i * L3::SIZE;
|
||||
tlb_flush_range_va(start, count * L3::SIZE);
|
||||
return Ok(start);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
unsafe fn map_device_memory_l2(
|
||||
base: PhysicalAddress,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<usize, Error> {
|
||||
'l0: for i in DEVICE_MAPPING_L3_COUNT..512 {
|
||||
for j in 0..count {
|
||||
unsafe {
|
||||
if DEVICE_MAPPING_L2[i + j].is_present() {
|
||||
continue 'l0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
for j in 0..count {
|
||||
DEVICE_MAPPING_L2[i + j] =
|
||||
PageEntry::<L2>::block(base.add(j * L2::SIZE), PageAttributes::W);
|
||||
}
|
||||
}
|
||||
|
||||
let start = DEVICE_MAPPING_OFFSET + i * L2::SIZE;
|
||||
tlb_flush_range_va(start, count * L2::SIZE);
|
||||
return Ok(start);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn map_device_memory(
|
||||
base: PhysicalAddress,
|
||||
size: usize,
|
||||
attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<KernelTableManagerImpl>, Error> {
|
||||
let l3_aligned = base.page_align_down::<L3>();
|
||||
let l3_offset = base.page_offset::<L3>();
|
||||
let page_count = (l3_offset + size).page_count::<L3>();
|
||||
|
||||
if page_count > 256 {
|
||||
// Large mapping, use L2 mapping instead
|
||||
let l2_aligned = base.page_align_down::<L2>();
|
||||
let l2_offset = base.page_offset::<L2>();
|
||||
let page_count = (l2_offset + size).page_count::<L2>();
|
||||
|
||||
unsafe {
|
||||
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
|
||||
let address = base_address + l2_offset;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
l2_aligned.into_u64(),
|
||||
address,
|
||||
base_address,
|
||||
page_count,
|
||||
L2::SIZE,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
// Just map the pages directly
|
||||
unsafe {
|
||||
let base_address = map_device_memory_l3(l3_aligned, page_count, attrs)?;
|
||||
let address = base_address + l3_offset;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
l3_aligned.into_u64(),
|
||||
address,
|
||||
base_address,
|
||||
page_count,
|
||||
L3::SIZE,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTableManagerImpl>) {
|
||||
match map.page_size {
|
||||
L3::SIZE => {
|
||||
for i in 0..map.page_count {
|
||||
let page = map.base_address + i * L3::SIZE;
|
||||
let l2i = page.page_index::<L2>();
|
||||
let l3i = page.page_index::<L3>();
|
||||
unsafe {
|
||||
assert!(DEVICE_MAPPING_L3S[l2i][l3i].is_present());
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
|
||||
}
|
||||
}
|
||||
tlb_flush_range_va(map.base_address, map.page_count * L3::SIZE);
|
||||
}
|
||||
L2::SIZE => todo!(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn auto_address<T>(x: *const T) -> usize {
|
||||
pub fn auto_lower_address<T>(x: *const T) -> usize {
|
||||
let x = x.addr();
|
||||
if x >= KERNEL_VIRT_OFFSET {
|
||||
x - KERNEL_VIRT_OFFSET
|
||||
@@ -247,113 +92,14 @@ pub fn auto_address<T>(x: *const T) -> usize {
|
||||
///
|
||||
/// 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;
|
||||
let l1_phys = auto_lower_address(&raw const fixed::KERNEL_L1) as u64;
|
||||
tlb_flush_full();
|
||||
SATP.write(SATP::PPN.val(l1_phys >> 12) + SATP::MODE::Sv39);
|
||||
}
|
||||
|
||||
/// Removes the lower half translation mappings.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Needs to be called once after secondary HARTs are initialized.
|
||||
pub unsafe fn unmap_lower_half() {
|
||||
let mut tables = KERNEL_TABLES.lock();
|
||||
let kernel_l1i_lower = page_index::<L1>(KERNEL_PHYS_BASE);
|
||||
tables.l1.data[kernel_l1i_lower] = 0;
|
||||
tlb_flush_range_va(0x0, L1::SIZE);
|
||||
}
|
||||
|
||||
/// Sets up run-time kernel translation tables.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure MMU is already enabled.
|
||||
pub unsafe fn setup_fixed_tables() {
|
||||
let mut tables = KERNEL_TABLES.lock();
|
||||
|
||||
let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
|
||||
|
||||
// Set up static runtime mappings
|
||||
for i in 0..DEVICE_MAPPING_L3_COUNT {
|
||||
unsafe {
|
||||
let device_mapping_l3_phys = PhysicalAddress::from_usize(
|
||||
(&raw const DEVICE_MAPPING_L3S[i]).addr() - KERNEL_VIRT_OFFSET,
|
||||
);
|
||||
DEVICE_MAPPING_L2[i] =
|
||||
PageEntry::table(device_mapping_l3_phys, PageAttributes::empty());
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(tables.l1.data[DEVICE_MAPPING_L1I], 0);
|
||||
tables.l1.data[DEVICE_MAPPING_L1I] =
|
||||
((device_mapping_l2_phys as u64) >> 2) | PageAttributes::V.bits();
|
||||
|
||||
for l1i in 0..RAM_MAPPING_L1_COUNT {
|
||||
let physical = (l1i as u64) << L1::SHIFT;
|
||||
tables.l1.data[l1i + RAM_MAPPING_START_L1I] = (physical >> 2)
|
||||
| (PageAttributes::R
|
||||
| PageAttributes::W
|
||||
| PageAttributes::A
|
||||
| PageAttributes::D
|
||||
| PageAttributes::V)
|
||||
.bits();
|
||||
}
|
||||
|
||||
tlb_flush_full();
|
||||
}
|
||||
|
||||
pub fn tlb_flush_global_full() {
|
||||
tlb_flush_full();
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_global_va(va: usize) {
|
||||
tlb_flush_va(va);
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va(start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va(page);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va_asid(asid: usize, start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va_asid(page, asid);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_full() {
|
||||
unsafe { core::arch::asm!("sfence.vma") };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va(va: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, zero", in(reg) va) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_asid(asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma zero, {0}", in(reg) asid) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, {1}", in(reg) va, in(reg) asid) };
|
||||
}
|
||||
|
||||
pub fn clone_kernel_tables(dst: &mut PageTable<L1>) {
|
||||
let tables = KERNEL_TABLES.lock();
|
||||
let _lock = fixed::LOCK.lock();
|
||||
for l1i in page_index::<L1>(USER_BOUNDARY)..512 {
|
||||
dst[l1i] = unsafe { PageEntry::from_raw(tables.l1.data[l1i]) };
|
||||
dst[l1i] = unsafe { fixed::KERNEL_L1[l1i] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ impl EntryLevel for L1 {
|
||||
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct PageTable<L: EntryLevel> {
|
||||
entries: [PageEntry<L>; 512],
|
||||
pub(crate) entries: [PageEntry<L>; 512],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@@ -204,6 +204,19 @@ impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
|
||||
}
|
||||
|
||||
impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
pub const fn identity_block(address: PhysicalAddress) -> Self {
|
||||
Self(
|
||||
(address.into_u64() >> 2)
|
||||
| PageAttributes::R.bits()
|
||||
| PageAttributes::W.bits()
|
||||
| PageAttributes::X.bits()
|
||||
| PageAttributes::V.bits()
|
||||
| PageAttributes::D.bits()
|
||||
| PageAttributes::A.bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
// TODO validate address alignment
|
||||
Self(
|
||||
|
||||
Reference in New Issue
Block a user