Add 'kernel/' from commit '7f1f6b73377367db17f98a740316b904c37ce3b1'

git-subtree-dir: kernel
git-subtree-mainline: 817f71f90f
git-subtree-split: 7f1f6b7337
This commit is contained in:
2024-03-12 15:52:48 +02:00
291 changed files with 42349 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "kernel-arch-x86_64"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
kernel-arch-interface = { path = "../interface" }
libk-mm-interface = { path = "../../libk/libk-mm/interface" }
memtables = { path = "../../lib/memtables" }
device-api = { path = "../../lib/device-api", features = ["derive"] }
bitflags = "2.3.3"
static_assertions = "1.1.0"
tock-registers = "0.8.1"
+186
View File
@@ -0,0 +1,186 @@
// vi: set ft=asm :
.set MSR_IA32_FS_BASE, 0xC0000100
.macro SAVE_TASK_STATE
sub ${context_size}, %rsp
mov %rbx, 0(%rsp)
mov %r12, 8(%rsp)
mov %r13, 16(%rsp)
mov %r14, 24(%rsp)
mov %r15, 32(%rsp)
// Store FS_BASE
mov $MSR_IA32_FS_BASE, %ecx
rdmsr
mov %edx, %ecx
shl $32, %rcx
or %rax, %rcx
mov %rcx, 40(%rsp)
// TODO save %fs
mov %rbp, 48(%rsp)
mov %cr3, %rbx
mov %rbx, 56(%rsp)
.endm
.macro LOAD_TASK_STATE
mov 56(%rsp), %rbx
mov %rbx, %cr3
mov 0(%rsp), %rbx
mov 8(%rsp), %r12
mov 16(%rsp), %r13
mov 24(%rsp), %r14
mov 32(%rsp), %r15
// Load FS_BASE
// edx:eax = fs_base
mov 40(%rsp), %rdx
mov %edx, %eax
shr $32, %rdx
mov $MSR_IA32_FS_BASE, %ecx
wrmsr
// mov 40(%rsp), %fs
mov 48(%rsp), %rbp
add ${context_size}, %rsp
.endm
.global __x86_64_task_enter_user
.global __x86_64_task_enter_kernel
.global __x86_64_task_enter_from_fork
.global __x86_64_enter_task
.global __x86_64_switch_task
.global __x86_64_switch_and_drop
.section .text
__x86_64_task_enter_from_fork:
xorq %rax, %rax
xorq %rcx, %rcx
xorq %r11, %r11
popq %rdi
popq %rsi
popq %rdx
popq %r10
popq %r8
popq %r9
swapgs
iretq
__x86_64_task_enter_user:
// User stack pointer
popq %rcx
// Argument
popq %rdi
// Entry address
popq %rax
// SS:RSP
pushq $0x1B
pushq %rcx
// RFLAGS
pushq $0x200
// CS:RIP
pushq $0x23
pushq %rax
swapgs
iretq
__x86_64_task_enter_kernel:
// Argument
popq %rdi
// Entry address
popq %rax
// Alignment word + fake return address to terminate "call chain"
pushq $0
// Enable IRQ in RFLAGS
pushfq
popq %rdx
or $(1 << 9), %rdx
mov %rsp, %rcx
// SS:RSP
pushq $0x10
pushq %rcx
// RFLAGS
pushq %rdx
// CS:RIP
pushq $0x08
pushq %rax
iretq
// %rsi - from struct ptr, %rdi - to struct ptr
__x86_64_switch_task:
SAVE_TASK_STATE
mov %rsp, 0(%rsi)
// TSS.RSP0
mov 8(%rdi), %rax
// Kernel stack
mov 0(%rdi), %rdi
mov %rdi, %rsp
// Load TSS.RSP0
mov %gs:(8), %rdi
mov %rax, 4(%rdi)
LOAD_TASK_STATE
ret
__x86_64_switch_and_drop:
// TSS.RSP0
mov 8(%rdi), %rax
// Kernel stack
mov 0(%rdi), %rdi
mov %rdi, %rsp
// Load TSS.RSP0
mov %gs:(8), %rdi
mov %rax, 4(%rdi)
mov %rsi, %rdi
call __arch_drop_thread
LOAD_TASK_STATE
ret
// %rdi - to struct ptr
__x86_64_enter_task:
// TSS.RSP0
mov 8(%rdi), %rax
// Kernel stack
mov 0(%rdi), %rdi
mov %rdi, %rsp
// Load TSS.RSP0
mov %gs:(8), %rdi
mov %rax, 4(%rdi)
LOAD_TASK_STATE
ret
+525
View File
@@ -0,0 +1,525 @@
use core::{arch::global_asm, cell::UnsafeCell, marker::PhantomData};
use kernel_arch_interface::{
mem::{KernelTableManager, PhysicalMemoryAllocator},
task::{ForkFrame, StackBuilder, TaskContext, TaskFrame},
};
use libk_mm_interface::address::{AsPhysicalAddress, IntoRaw, PhysicalAddress};
use yggdrasil_abi::{arch::SavedFrame, error::Error};
use crate::{mem::KERNEL_TABLES, registers::FpuContext};
/// Frame saved onto the stack when taking an IRQ
#[derive(Debug)]
#[repr(C)]
pub struct IrqFrame {
pub rax: u64,
pub rcx: u64,
pub rdx: u64,
pub rbx: u64,
pub rsi: u64,
pub rdi: u64,
pub rbp: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
pub rip: u64,
pub cs: u64,
pub rflags: u64,
pub rsp: u64,
pub ss: u64,
}
/// Set of registers saved when taking an exception/interrupt
#[derive(Debug)]
#[repr(C)]
pub struct ExceptionFrame {
pub rax: u64,
pub rcx: u64,
pub rdx: u64,
pub rbx: u64,
pub rsi: u64,
pub rdi: u64,
pub rbp: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
pub exc_number: u64,
pub exc_code: u64,
pub rip: u64,
pub cs: u64,
pub rflags: u64,
pub rsp: u64,
pub ss: u64,
}
/// Set of registers saved when taking a syscall instruction
#[derive(Debug)]
#[repr(C)]
pub struct SyscallFrame {
pub rax: u64,
pub args: [u64; 6],
pub rcx: u64,
pub r11: u64,
pub user_ip: u64,
pub user_sp: u64,
pub user_flags: u64,
pub rbx: u64,
pub rbp: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
}
#[repr(C, align(0x10))]
struct Inner {
// 0x00
sp: usize,
// 0x08
tss_rsp0: usize,
}
/// x86-64 implementation of a task context
#[allow(dead_code)]
pub struct TaskContextImpl<
K: KernelTableManager,
PA: PhysicalMemoryAllocator<Address = PhysicalAddress>,
> {
inner: UnsafeCell<Inner>,
fpu_context: UnsafeCell<FpuContext>,
stack_base_phys: PhysicalAddress,
stack_size: usize,
_alloc: PhantomData<PA>,
_table_manager: PhantomData<K>,
}
// 8 registers + return address (which is not included)
const COMMON_CONTEXT_SIZE: usize = 8 * 8;
impl TaskFrame for IrqFrame {
fn store(&self) -> SavedFrame {
SavedFrame {
rax: self.rax,
rcx: self.rcx,
rdx: self.rdx,
rbx: self.rbx,
rsi: self.rsi,
rdi: self.rdi,
rbp: self.rbp,
r8: self.r8,
r9: self.r9,
r10: self.r10,
r11: self.r11,
r12: self.r12,
r13: self.r13,
r14: self.r14,
r15: self.r15,
user_ip: self.rip,
user_sp: self.rsp,
rflags: self.rflags,
}
}
fn restore(&mut self, _saved: &SavedFrame) {
todo!()
}
fn argument(&self) -> u64 {
self.rdi as _
}
fn user_ip(&self) -> usize {
self.rip as _
}
fn user_sp(&self) -> usize {
self.rsp as _
}
fn set_argument(&mut self, value: u64) {
self.rdi = value;
}
fn set_return_value(&mut self, value: u64) {
self.rax = value;
}
fn set_user_ip(&mut self, value: usize) {
self.rip = value as _;
}
fn set_user_sp(&mut self, value: usize) {
self.rsp = value as _;
}
}
impl TaskFrame for ExceptionFrame {
fn store(&self) -> SavedFrame {
SavedFrame {
rax: self.rax,
rcx: self.rcx,
rdx: self.rdx,
rbx: self.rbx,
rsi: self.rsi,
rdi: self.rdi,
rbp: self.rbp,
r8: self.r8,
r9: self.r9,
r10: self.r10,
r11: self.r11,
r12: self.r12,
r13: self.r13,
r14: self.r14,
r15: self.r15,
user_ip: self.rip,
user_sp: self.rsp,
rflags: self.rflags,
}
}
fn restore(&mut self, _saved: &SavedFrame) {
todo!()
}
fn argument(&self) -> u64 {
0
}
fn user_sp(&self) -> usize {
self.rsp as _
}
fn user_ip(&self) -> usize {
self.rip as _
}
fn set_user_sp(&mut self, value: usize) {
self.rsp = value as _;
}
fn set_user_ip(&mut self, value: usize) {
self.rip = value as _;
}
fn set_return_value(&mut self, _value: u64) {
// Not in syscall, do not overwrite
}
fn set_argument(&mut self, value: u64) {
self.rdi = value;
}
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>> ForkFrame<K, PA>
for SyscallFrame
{
type Context = TaskContextImpl<K, PA>;
unsafe fn fork(&self, address_space: u64) -> Result<TaskContextImpl<K, PA>, Error> {
TaskContextImpl::from_syscall_frame(self, address_space)
}
fn set_return_value(&mut self, value: u64) {
self.rax = value;
}
}
impl TaskFrame for SyscallFrame {
fn store(&self) -> SavedFrame {
SavedFrame {
rax: self.rax,
rcx: self.rcx,
rdx: self.args[2],
rbx: self.rbx,
rsi: self.args[1],
rdi: self.args[0],
rbp: self.rbp,
r8: self.args[4],
r9: self.args[5],
r10: self.args[3],
r11: self.r11,
r12: self.r12,
r13: self.r13,
r14: self.r14,
r15: self.r15,
user_ip: self.user_ip,
user_sp: self.user_sp,
rflags: self.user_flags,
}
}
fn restore(&mut self, saved: &SavedFrame) {
self.rax = saved.rax;
self.args[0] = saved.rdi;
self.args[1] = saved.rsi;
self.args[2] = saved.rdx;
self.args[3] = saved.r10;
self.args[4] = saved.r8;
self.args[5] = saved.r9;
self.rcx = saved.rcx;
self.r11 = saved.r11;
self.user_ip = saved.user_ip;
self.user_sp = saved.user_sp;
self.user_flags = saved.rflags;
self.rbx = saved.rbx;
self.rbp = saved.rbp;
self.r12 = saved.r12;
self.r13 = saved.r13;
self.r14 = saved.r14;
self.r15 = saved.r15;
}
fn argument(&self) -> u64 {
self.args[0]
}
fn user_sp(&self) -> usize {
self.user_sp as _
}
fn user_ip(&self) -> usize {
self.user_ip as _
}
fn set_user_sp(&mut self, value: usize) {
self.user_sp = value as _;
}
fn set_user_ip(&mut self, value: usize) {
self.user_ip = value as _;
}
fn set_return_value(&mut self, value: u64) {
self.rax = value;
}
fn set_argument(&mut self, value: u64) {
self.args[0] = value;
}
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContextImpl<K, PA>
{
/// Constructs a new task context from a "forked" syscall frame
pub(super) unsafe fn from_syscall_frame(frame: &SyscallFrame, cr3: u64) -> Result<Self, Error> {
const USER_TASK_PAGES: usize = 8;
let stack_base_phys = PA::allocate_contiguous_pages(USER_TASK_PAGES)?;
let stack_base = stack_base_phys.raw_virtualize::<K>();
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
// iretq frame
stack.push(0x1B);
stack.push(frame.user_sp as _);
stack.push(0x200);
stack.push(0x23);
stack.push(frame.user_ip as _);
stack.push(frame.args[5] as _); // r9
stack.push(frame.args[4] as _); // r8
stack.push(frame.args[3] as _); // r10
stack.push(frame.args[2] as _); // rdx
stack.push(frame.args[1] as _); // rsi
stack.push(frame.args[0] as _); // rdi
// callee-saved registers
stack.push(__x86_64_task_enter_from_fork as _);
stack.push(cr3 as _);
stack.push(frame.rbp as _);
stack.push(0x12345678); // XXX TODO: fs_base from SyscallFrame
stack.push(frame.r15 as _);
stack.push(frame.r14 as _);
stack.push(frame.r13 as _);
stack.push(frame.r12 as _);
stack.push(frame.rbx as _);
let sp = stack.build();
let rsp0 = stack_base + USER_TASK_PAGES * 0x1000;
Ok(Self {
inner: UnsafeCell::new(Inner { sp, tss_rsp0: rsp0 }),
fpu_context: UnsafeCell::new(FpuContext::new()),
stack_base_phys,
stack_size: USER_TASK_PAGES * 0x1000,
_alloc: PhantomData,
_table_manager: PhantomData,
})
}
}
unsafe impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>> Sync
for TaskContextImpl<K, PA>
{
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContext<K, PA> for TaskContextImpl<K, PA>
{
const SIGNAL_STACK_EXTRA_ALIGN: usize = 8;
const USER_STACK_EXTRA_ALIGN: usize = 8;
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
const KERNEL_TASK_PAGES: usize = 32;
let stack_base_phys = PA::allocate_contiguous_pages(KERNEL_TASK_PAGES)?;
let stack_base = stack_base_phys.raw_virtualize::<K>();
let mut stack = StackBuilder::new(stack_base, KERNEL_TASK_PAGES * 0x1000);
// Entry and argument
stack.push(entry as _);
stack.push(arg);
// XXX
setup_common_context(
&mut stack,
__x86_64_task_enter_kernel as _,
unsafe { KERNEL_TABLES.as_physical_address().into_raw() },
0,
);
let sp = stack.build();
// TODO stack is leaked
Ok(Self {
inner: UnsafeCell::new(Inner { sp, tss_rsp0: 0 }),
fpu_context: UnsafeCell::new(FpuContext::new()),
stack_base_phys,
stack_size: KERNEL_TASK_PAGES * 0x1000,
_alloc: PhantomData,
_table_manager: PhantomData,
})
}
fn user(
entry: usize,
arg: usize,
cr3: u64,
user_stack_sp: usize,
fs_base: usize,
) -> Result<Self, Error> {
const USER_TASK_PAGES: usize = 8;
let stack_base_phys = PA::allocate_contiguous_pages(USER_TASK_PAGES)?;
let stack_base = stack_base_phys.raw_virtualize::<K>();
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
stack.push(entry as _);
stack.push(arg);
stack.push(user_stack_sp);
setup_common_context(&mut stack, __x86_64_task_enter_user as _, cr3, fs_base);
let sp = stack.build();
let rsp0 = stack_base + USER_TASK_PAGES * 0x1000;
Ok(Self {
inner: UnsafeCell::new(Inner { sp, tss_rsp0: rsp0 }),
fpu_context: UnsafeCell::new(FpuContext::new()),
stack_base_phys,
stack_size: USER_TASK_PAGES * 0x1000,
_alloc: PhantomData,
_table_manager: PhantomData,
})
}
unsafe fn enter(&self) -> ! {
FpuContext::restore(self.fpu_context.get());
__x86_64_enter_task(self.inner.get())
}
unsafe fn switch(&self, from: &Self) {
let dst = self.inner.get();
let src = from.inner.get();
if dst != src {
// Save the old context
FpuContext::save(from.fpu_context.get());
// Load next context
FpuContext::restore(self.fpu_context.get());
__x86_64_switch_task(dst, src);
}
}
unsafe fn switch_and_drop(&self, thread: *const ()) {
let dst = self.inner.get();
FpuContext::restore(self.fpu_context.get());
__x86_64_switch_and_drop(dst, thread)
}
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>> Drop
for TaskContextImpl<K, PA>
{
fn drop(&mut self) {
assert_eq!(self.stack_size % 0x1000, 0);
for offset in (0..self.stack_size).step_by(0x1000) {
unsafe {
PA::free_page(self.stack_base_phys.add(offset));
}
}
}
}
fn setup_common_context(builder: &mut StackBuilder, entry: usize, cr3: u64, fs_base: usize) {
builder.push(entry);
builder.push(cr3 as _);
builder.push(0); // %rbp
builder.push(fs_base); // %fs_base
builder.push(0); // %r15
builder.push(0); // %r14
builder.push(0); // %r13
builder.push(0); // %r12
builder.push(0); // %rbx
}
extern "C" {
fn __x86_64_task_enter_kernel();
fn __x86_64_task_enter_user();
fn __x86_64_task_enter_from_fork();
fn __x86_64_enter_task(to: *mut Inner) -> !;
fn __x86_64_switch_task(to: *mut Inner, from: *mut Inner);
fn __x86_64_switch_and_drop(to: *mut Inner, from: *const ());
}
global_asm!(
include_str!("context.S"),
context_size = const COMMON_CONTEXT_SIZE,
options(att_syntax)
);
+172
View File
@@ -0,0 +1,172 @@
#![no_std]
#![feature(
effects,
strict_provenance,
asm_const,
naked_functions,
trait_upcasting
)]
extern crate alloc;
use core::{
ops::DerefMut,
sync::atomic::{AtomicUsize, Ordering},
};
use alloc::vec::Vec;
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
cpu::{CpuImpl, IpiQueue},
task::Scheduler,
util::OneTimeInit,
Architecture,
};
use libk_mm_interface::address::PhysicalAddress;
use registers::MSR_IA32_KERNEL_GS_BASE;
use tock_registers::interfaces::Writeable;
pub mod context;
pub mod mem;
pub mod registers;
pub use context::TaskContextImpl;
pub use mem::{process::ProcessAddressSpaceImpl, KernelTableManagerImpl};
pub struct ArchitectureImpl;
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
pub trait LocalApicInterface: LocalInterruptController + MessageInterruptController {
/// Performs an application processor startup sequence.
///
/// # Safety
///
/// Unsafe: only meant to be called by the BSP during SMP init.
unsafe fn wakeup_cpu(&self, apic_id: u32, bootstrap_code: PhysicalAddress);
/// Signals local APIC that we've handled the IRQ
fn clear_interrupt(&self);
}
#[repr(C, align(0x10))]
pub struct PerCpuData {
// 0x00
pub this: *mut Self,
// 0x08, used in assembly
pub tss_address: usize,
// 0x10, used in assembly
pub tmp_address: usize,
pub local_apic: &'static dyn LocalApicInterface,
}
impl PerCpuData {
pub fn local_apic(&self) -> &'static dyn LocalApicInterface {
self.local_apic
}
}
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
#[naked]
extern "C" fn idle_task(_: usize) -> ! {
unsafe {
core::arch::asm!(
r#"
1:
nop
jmp 1b
"#,
options(noreturn, att_syntax)
);
}
}
impl ArchitectureImpl {
fn local_cpu_data() -> Option<&'static mut PerCpuData> {
unsafe { (Self::local_cpu() as *mut PerCpuData).as_mut() }
}
}
impl Architecture for ArchitectureImpl {
type PerCpuData = PerCpuData;
unsafe fn set_local_cpu(cpu: *mut ()) {
MSR_IA32_KERNEL_GS_BASE.set(cpu as u64);
core::arch::asm!("wbinvd; swapgs");
}
fn local_cpu() -> *mut () {
let mut addr: u64;
unsafe {
core::arch::asm!("movq %gs:(0), {0}", out(reg) addr, options(att_syntax));
}
addr as _
}
unsafe fn init_ipi_queues(queues: Vec<IpiQueue<Self>>) {
IPI_QUEUES.init(queues);
}
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
use alloc::boxed::Box;
let cpu = Box::leak(Box::new(CpuImpl::<Self, S>::new(
id.expect("x86_64 required manual CPU ID set"),
data,
)));
cpu.this = cpu.deref_mut();
cpu.set_local();
}
fn idle_task() -> extern "C" fn(usize) -> ! {
idle_task
}
fn cpu_count() -> usize {
CPU_COUNT.load(Ordering::Acquire)
}
fn cpu_index<S: Scheduler + 'static>() -> u32 {
CpuImpl::<Self, S>::local().id()
}
fn interrupt_mask() -> bool {
let mut flags: u64;
unsafe {
core::arch::asm!("pushfq; pop {0}", out(reg) flags, options(att_syntax));
}
// If IF is zero, interrupts are disabled (masked)
flags & (1 << 9) == 0
}
unsafe fn set_interrupt_mask(mask: bool) -> bool {
let old = Self::interrupt_mask();
if mask {
core::arch::asm!("cli");
} else {
core::arch::asm!("sti");
}
old
}
#[inline]
fn wait_for_interrupt() {
unsafe {
core::arch::asm!("hlt");
}
}
fn local_interrupt_controller() -> &'static dyn LocalInterruptController {
let local = Self::local_cpu_data().unwrap();
local.local_apic
}
fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
let local = Self::local_cpu_data().unwrap();
local.local_apic
}
}
+405
View File
@@ -0,0 +1,405 @@
use core::{
alloc::Layout,
ops::{Deref, DerefMut},
ptr::addr_of,
sync::atomic::{AtomicUsize, Ordering},
};
use kernel_arch_interface::mem::{
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
};
use libk_mm_interface::{
address::{FromRaw, PhysicalAddress},
table::{EntryLevel, EntryLevelExt},
KernelImageObject,
};
use memtables::x86_64::FixedTables;
use static_assertions::{const_assert_eq, const_assert_ne};
use yggdrasil_abi::error::Error;
use crate::{registers::CR3, KERNEL_VIRT_OFFSET};
use self::table::{PageAttributes, PageEntry, PageTable, L0, L1, L2, L3};
pub mod process;
pub mod table;
#[derive(Debug)]
pub struct KernelTableManagerImpl;
const CANONICAL_ADDRESS_MASK: usize = 0xFFFF000000000000;
const KERNEL_PHYS_BASE: usize = 0x200000;
// Mapped at compile time
const KERNEL_MAPPING_BASE: usize = KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE;
const KERNEL_L0_INDEX: usize = KERNEL_MAPPING_BASE.page_index::<L0>();
const KERNEL_L1_INDEX: usize = KERNEL_MAPPING_BASE.page_index::<L1>();
const KERNEL_START_L2_INDEX: usize = KERNEL_MAPPING_BASE.page_index::<L2>();
// Must not be zero, should be at 4MiB
const_assert_ne!(KERNEL_START_L2_INDEX, 0);
// From static mapping
const_assert_eq!(KERNEL_L0_INDEX, 511);
const_assert_eq!(KERNEL_L1_INDEX, 0);
// Mapped at boot
const EARLY_MAPPING_L2I: usize = KERNEL_START_L2_INDEX - 1;
const HEAP_MAPPING_L1I: usize = KERNEL_L1_INDEX + 1;
const DEVICE_MAPPING_L1I: usize = KERNEL_L1_INDEX + 2;
const RAM_MAPPING_L0I: usize = KERNEL_L0_INDEX - 1;
const DEVICE_MAPPING_L3_COUNT: usize = 4;
#[link_section = ".data.tables"]
pub static mut KERNEL_TABLES: KernelImageObject<FixedTables> =
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
// 2MiB for early mappings
const EARLY_MAPPING_OFFSET: usize = CANONICAL_ADDRESS_MASK
| (KERNEL_L0_INDEX * L0::SIZE)
| (KERNEL_L1_INDEX * L1::SIZE)
| (EARLY_MAPPING_L2I * L2::SIZE);
static mut EARLY_MAPPING_L3: PageTable<L3> = PageTable::zeroed();
// 1GiB for heap mapping
pub const HEAP_MAPPING_OFFSET: usize =
CANONICAL_ADDRESS_MASK | (KERNEL_L0_INDEX * L0::SIZE) | (HEAP_MAPPING_L1I * L1::SIZE);
pub(super) static mut HEAP_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
// 1GiB for device MMIO mapping
const DEVICE_MAPPING_OFFSET: usize =
CANONICAL_ADDRESS_MASK | (KERNEL_L0_INDEX * L0::SIZE) | (DEVICE_MAPPING_L1I * L1::SIZE);
static mut DEVICE_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
static mut DEVICE_MAPPING_L3S: [PageTable<L3>; DEVICE_MAPPING_L3_COUNT] =
[PageTable::zeroed(); DEVICE_MAPPING_L3_COUNT];
// 512GiB for whole RAM mapping
pub const RAM_MAPPING_OFFSET: usize = CANONICAL_ADDRESS_MASK | (RAM_MAPPING_L0I * L0::SIZE);
pub static MEMORY_LIMIT: AtomicUsize = AtomicUsize::new(0);
pub static mut RAM_MAPPING_L1: PageTable<L1> = PageTable::zeroed();
impl KernelTableManager for KernelTableManagerImpl {
fn virtualize(address: u64) -> usize {
let address = address as usize;
if address < MEMORY_LIMIT.load(Ordering::Acquire) {
address + RAM_MAPPING_OFFSET
} else {
panic!("Invalid physical address: {:#x}", address);
}
}
fn physicalize(address: usize) -> u64 {
if address < RAM_MAPPING_OFFSET
|| address - RAM_MAPPING_OFFSET >= MEMORY_LIMIT.load(Ordering::Acquire)
{
panic!("Not a virtualized physical address: {:#x}", address);
}
(address - RAM_MAPPING_OFFSET) as _
}
unsafe fn map_device_pages(
base: u64,
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
map_device_memory(PhysicalAddress::from_raw(base), count, attrs)
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
unmap_device_memory(mapping)
}
}
// Early mappings
unsafe fn map_early_pages(physical: PhysicalAddress, count: usize) -> Result<usize, Error> {
for l3i in 0..512 {
let mut taken = false;
for i in 0..count {
if EARLY_MAPPING_L3[i + l3i].is_present() {
taken = true;
break;
}
}
if taken {
continue;
}
for i in 0..count {
// TODO NX, NC
EARLY_MAPPING_L3[i + l3i] =
PageEntry::page(physical.add(i * L3::SIZE), PageAttributes::WRITABLE);
}
return Ok(EARLY_MAPPING_OFFSET + l3i * L3::SIZE);
}
Err(Error::OutOfMemory)
}
unsafe fn unmap_early_page(address: usize) {
if !(EARLY_MAPPING_OFFSET..EARLY_MAPPING_OFFSET + L2::SIZE).contains(&address) {
panic!("Tried to unmap invalid early mapping: {:#x}", address);
}
let l3i = (address - EARLY_MAPPING_OFFSET).page_index::<L3>();
assert!(EARLY_MAPPING_L3[l3i].is_present());
EARLY_MAPPING_L3[l3i] = PageEntry::INVALID;
}
// 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;
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;
// TODO NX, NC
DEVICE_MAPPING_L3S[l2i][l3i] =
PageEntry::page(base.add(j * L3::SIZE), PageAttributes::WRITABLE);
}
return Ok(DEVICE_MAPPING_OFFSET + i * L3::SIZE);
}
Err(Error::OutOfMemory)
}
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 {
if DEVICE_MAPPING_L2[i + j].is_present() {
continue 'l0;
}
}
for j in 0..count {
DEVICE_MAPPING_L2[i + j] =
PageEntry::<L2>::block(base.add(j * L2::SIZE), PageAttributes::WRITABLE);
}
// debugln!(
// "map l2s: base={:#x}, count={} -> {:#x}",
// base,
// count,
// DEVICE_MAPPING_OFFSET + i * L2::SIZE
// );
return Ok(DEVICE_MAPPING_OFFSET + i * L2::SIZE);
}
Err(Error::OutOfMemory)
}
unsafe fn map_device_memory(
base: PhysicalAddress,
size: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<KernelTableManagerImpl>, Error> {
// debugln!("Map {}B @ {:#x}", size, base);
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>();
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
let address = base_address + l2_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
address,
base_address,
page_count,
L2::SIZE,
))
} else {
// Just map the pages directly
let base_address = map_device_memory_l3(l3_aligned, page_count, attrs)?;
let address = base_address + l3_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
address,
base_address,
page_count,
L3::SIZE,
))
}
}
unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTableManagerImpl>) {
// debugln!(
// "Unmap {}B @ {:#x}",
// map.page_count * map.page_size,
// map.base_address
// );
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>();
assert!(DEVICE_MAPPING_L3S[l2i][l3i].is_present());
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
flush_tlb_entry(page);
}
}
L2::SIZE => todo!(),
_ => unimplemented!(),
}
}
pub unsafe fn map_heap_block(index: usize, page: PhysicalAddress) {
if !page.is_page_aligned_for::<L2>() {
panic!("Attempted to map a misaligned 2MiB page");
}
assert!(index < 512);
if HEAP_MAPPING_L2[index].is_present() {
panic!("Page is already mappged: {:#x}", page);
}
// TODO NX
HEAP_MAPPING_L2[index] = PageEntry::<L2>::block(page, PageAttributes::WRITABLE);
}
/// Memory mapping which may be used for performing early kernel initialization
pub struct EarlyMapping<'a, T: ?Sized> {
value: &'a mut T,
page_count: usize,
}
impl<'a, T: Sized> EarlyMapping<'a, T> {
pub unsafe fn map(physical: PhysicalAddress) -> Result<EarlyMapping<'a, T>, Error> {
let layout = Layout::new::<T>();
let aligned = physical.page_align_down::<L3>();
let offset = physical.page_offset::<L3>();
let page_count = (offset + layout.size() + L3::SIZE - 1) / L3::SIZE;
let virt = map_early_pages(aligned, page_count)?;
let value = &mut *((virt + offset) as *mut T);
Ok(EarlyMapping { value, page_count })
}
pub unsafe fn map_slice(
physical: PhysicalAddress,
len: usize,
) -> Result<EarlyMapping<'a, [T]>, Error> {
let layout = Layout::array::<T>(len).unwrap();
let aligned = physical.page_align_down::<L3>();
let offset = physical.page_offset::<L3>();
let page_count = (offset + layout.size() + L3::SIZE - 1) / L3::SIZE;
let virt = map_early_pages(aligned, page_count)?;
let value = core::slice::from_raw_parts_mut((virt + offset) as *mut T, len);
Ok(EarlyMapping { value, page_count })
}
}
impl<'a, T: ?Sized> Deref for EarlyMapping<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<'a, T: ?Sized> DerefMut for EarlyMapping<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.value
}
}
impl<'a, T: ?Sized> Drop for EarlyMapping<'a, T> {
fn drop(&mut self) {
let address = (self.value as *mut T).addr() & !(L3::SIZE - 1);
for i in 0..self.page_count {
let page = address + i * L3::SIZE;
unsafe {
unmap_early_page(page);
}
}
}
}
pub fn clone_kernel_tables(dst: &mut PageTable<L0>) {
unsafe {
dst[KERNEL_L0_INDEX] = PageEntry::from_raw(KERNEL_TABLES.l0.data[KERNEL_L0_INDEX]);
dst[RAM_MAPPING_L0I] = PageEntry::from_raw(KERNEL_TABLES.l0.data[RAM_MAPPING_L0I]);
}
}
/// Sets up the following memory map:
/// ...: KERNEL_TABLES.l0:
/// * 0xFFFFFF0000000000 .. 0xFFFFFFFF8000000000 : RAM_MAPPING_L1
/// * 0xFFFFFF8000000000 .. ... : KERNEL_TABLES.kernel_l1:
/// * 0xFFFFFF8000000000 .. 0xFFFFFF8040000000 : KERNEL_TABLES.kernel_l2
/// * 0xFFFFFF8000000000 .. 0xFFFFFF8000200000 : ---
/// * 0xFFFFFF8000200000 .. 0xFFFFFF8000400000 : EARLY_MAPPING_L3
/// * 0xFFFFFF8000400000 .. ... : KERNEL_TABLES.kernel_l3s
/// * 0xFFFFFF8040000000 .. 0xFFFFFF8080000000 : HEAP_MAPPING_L2
/// * 0xFFFFFF8080000000 .. 0xFFFFFF8100000000 : DEVICE_MAPPING_L2
/// * 0xFFFFFF8080000000 .. 0xFFFFFF8080800000 : DEVICE_MAPPING_L3S
/// * 0xFFFFFF8080800000 .. 0xFFFFFF8100000000 : ...
pub unsafe fn init_fixed_tables() {
// TODO this could be built in compile-time too?
let early_mapping_l3_phys = addr_of!(EARLY_MAPPING_L3) as usize - KERNEL_VIRT_OFFSET;
let device_mapping_l2_phys = addr_of!(DEVICE_MAPPING_L2) as usize - KERNEL_VIRT_OFFSET;
let heap_mapping_l2_phys = addr_of!(HEAP_MAPPING_L2) as usize - KERNEL_VIRT_OFFSET;
let ram_mapping_l1_phys = addr_of!(RAM_MAPPING_L1) as usize - KERNEL_VIRT_OFFSET;
for i in 0..DEVICE_MAPPING_L3_COUNT {
let device_mapping_l3_phys = PhysicalAddress::from_raw(
&DEVICE_MAPPING_L3S[i] as *const _ as usize - KERNEL_VIRT_OFFSET,
);
DEVICE_MAPPING_L2[i] = PageEntry::table(device_mapping_l3_phys, PageAttributes::WRITABLE);
}
assert_eq!(KERNEL_TABLES.kernel_l2.data[EARLY_MAPPING_L2I], 0);
KERNEL_TABLES.kernel_l2.data[EARLY_MAPPING_L2I] = (early_mapping_l3_phys as u64)
| (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
assert_eq!(KERNEL_TABLES.kernel_l1.data[HEAP_MAPPING_L1I], 0);
KERNEL_TABLES.kernel_l1.data[HEAP_MAPPING_L1I] =
(heap_mapping_l2_phys as u64) | (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
assert_eq!(KERNEL_TABLES.kernel_l1.data[DEVICE_MAPPING_L1I], 0);
KERNEL_TABLES.kernel_l1.data[DEVICE_MAPPING_L1I] = (device_mapping_l2_phys as u64)
| (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
assert_eq!(KERNEL_TABLES.l0.data[RAM_MAPPING_L0I], 0);
KERNEL_TABLES.l0.data[RAM_MAPPING_L0I] =
(ram_mapping_l1_phys as u64) | (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
// TODO ENABLE EFER.NXE
let cr3 = &KERNEL_TABLES.l0 as *const _ as usize - KERNEL_VIRT_OFFSET;
CR3.set_address(cr3);
}
#[inline]
pub unsafe fn flush_tlb_entry(address: usize) {
core::arch::asm!("invlpg ({0})", in(reg) address, options(att_syntax));
}
+161
View File
@@ -0,0 +1,161 @@
//! x86-64-specific process address space management functions
use core::marker::PhantomData;
use libk_mm_interface::{
address::{AsPhysicalAddress, IntoRaw, PhysicalAddress},
pointer::PhysicalRefMut,
process::ProcessAddressSpaceManager,
table::{
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
},
};
use yggdrasil_abi::error::Error;
use crate::KernelTableManagerImpl;
use super::{
clone_kernel_tables, flush_tlb_entry,
table::{PageEntry, PageTable, L0, L1, L2, L3},
};
/// Represents a process or kernel address space. Because x86-64 does not have cool stuff like
/// TTBR0 and TTBR1, all address spaces are initially cloned from the kernel space.
#[repr(C)]
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
l0: PhysicalRefMut<'static, PageTable<L0>, KernelTableManagerImpl>,
_alloc: PhantomData<TA>,
}
impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceImpl<TA> {
// Start with 8GiB
const LOWER_LIMIT_PFN: usize = (8 << 30) / L3::SIZE;
// 16GiB VM limit
const UPPER_LIMIT_PFN: usize = (16 << 30) / L3::SIZE;
fn new() -> Result<Self, Error> {
let mut l0 = unsafe {
PhysicalRefMut::<'static, PageTable<L0>, KernelTableManagerImpl>::map(
TA::allocate_page_table()?,
)
};
for i in 0..512 {
l0[i] = PageEntry::INVALID;
}
clone_kernel_tables(&mut l0);
Ok(Self {
l0,
_alloc: PhantomData,
})
}
#[inline]
unsafe fn map_page(
&mut self,
address: usize,
physical: PhysicalAddress,
flags: MapAttributes,
) -> Result<(), Error> {
self.write_l3_entry(address, PageEntry::page(physical, flags.into()), false)
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
self.pop_l3_entry(address)
}
#[inline]
fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
self.read_l3_entry(address)
.ok_or(Error::InvalidMemoryOperation)
}
fn as_address_with_asid(&self) -> u64 {
// TODO x86-64 PCID/ASID?
unsafe { self.l0.as_physical_address().into_raw() }
}
unsafe fn clear(&mut self) {
self.l0
.drop_range::<TA>(0..((Self::UPPER_LIMIT_PFN * L3::SIZE).page_index::<L1>()));
}
}
impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
// Write a single 4KiB entry
fn write_l3_entry(
&mut self,
virt: usize,
entry: PageEntry<L3>,
overwrite: bool,
) -> Result<(), Error> {
let l0i = virt.page_index::<L0>();
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
let mut l1 = self.l0.get_mut_or_alloc::<TA>(l0i)?;
let mut l2 = l1.get_mut_or_alloc::<TA>(l1i)?;
let mut l3 = l2.get_mut_or_alloc::<TA>(l2i)?;
if l3[l3i].is_present() && !overwrite {
todo!();
}
l3[l3i] = entry;
unsafe {
flush_tlb_entry(virt);
}
Ok(())
}
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
let l0i = virt.page_index::<L0>();
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
// TODO somehow drop tables if they're known to be empty?
let mut l1 = self.l0.get_mut(l0i).ok_or(Error::DoesNotExist)?;
let mut l2 = l1.get_mut(l1i).ok_or(Error::DoesNotExist)?;
let mut l3 = l2.get_mut(l2i).ok_or(Error::DoesNotExist)?;
let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?;
l3[l3i] = PageEntry::INVALID;
unsafe {
flush_tlb_entry(virt);
}
Ok(page)
}
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
let l0i = virt.page_index::<L0>();
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
let l1 = self.l0.get(l0i)?;
let l2 = l1.get(l1i)?;
let l3 = l2.get(l2i)?;
let page = l3[l3i].as_page()?;
Some((page, l3[l3i].attributes().into()))
}
}
impl<TA: TableAllocator> Drop for ProcessAddressSpaceImpl<TA> {
fn drop(&mut self) {
// SAFETY: with safe usage of the ProcessAddressSpaceImpl, clearing and dropping
// is safe, no one refers to the memory
unsafe {
self.clear();
let l0_phys = self.l0.as_physical_address();
TA::free_page_table(l0_phys);
}
}
}
+335
View File
@@ -0,0 +1,335 @@
//! x86-64-specific memory translation table management interfaces and functions
use core::{
marker::PhantomData,
ops::{Index, IndexMut, Range},
};
use bitflags::bitflags;
use libk_mm_interface::{
address::{AsPhysicalAddress, FromRaw, PhysicalAddress},
pointer::{PhysicalRef, PhysicalRefMut},
table::{
EntryLevel, EntryLevelDrop, MapAttributes, NextPageTable, NonTerminalEntryLevel,
TableAllocator,
},
};
use yggdrasil_abi::error::Error;
use crate::KernelTableManagerImpl;
bitflags! {
/// Describes how each page table entry is mapped
pub struct PageAttributes: u64 {
/// When set, the mapping is considered valid and pointing somewhere
const PRESENT = 1 << 0;
/// For tables, allows writes to further translation levels, for pages/blocks, allows
/// writes to the region covered by the entry
const WRITABLE = 1 << 1;
/// When set for L2 entries, the mapping specifies a 2MiB page instead of a page table
/// reference
const BLOCK = 1 << 7;
/// For tables, allows user access to further translation levels, for pages/blocks, allows
/// user access to the region covered by the entry
const USER = 1 << 2;
}
}
/// Represents a single virtual address space mapping depending on its translation level
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct PageEntry<L: EntryLevel>(u64, PhantomData<L>);
/// Table describing a single level of address translation
#[derive(Clone, Copy)]
#[repr(C, align(0x1000))]
pub struct PageTable<L: EntryLevel> {
data: [PageEntry<L>; 512],
}
/// Translation level 0 (PML4): Entry is 512GiB table
#[derive(Clone, Copy, Debug)]
pub struct L0;
/// Translation level 1 (PDPT): Entry is 1GiB table
#[derive(Clone, Copy, Debug)]
pub struct L1;
/// Translation level 2 (Page directory): Entry is 2MiB block/table
#[derive(Clone, Copy, Debug)]
pub struct L2;
/// Translation level 3 (Page table): Entry is 4KiB page
#[derive(Clone, Copy, Debug)]
pub struct L3;
impl NonTerminalEntryLevel for L0 {
type NextLevel = L1;
}
impl NonTerminalEntryLevel for L1 {
type NextLevel = L2;
}
impl NonTerminalEntryLevel for L2 {
type NextLevel = L3;
}
impl EntryLevel for L0 {
const SHIFT: usize = 39;
}
impl EntryLevel for L1 {
const SHIFT: usize = 30;
}
impl EntryLevel for L2 {
const SHIFT: usize = 21;
}
impl EntryLevel for L3 {
const SHIFT: usize = 12;
}
impl PageEntry<L3> {
/// Constructs a mapping which points to a 4KiB page
pub fn page(phys: PhysicalAddress, attrs: PageAttributes) -> Self {
Self(
u64::from(phys) | (attrs | PageAttributes::PRESENT | PageAttributes::USER).bits(),
PhantomData,
)
}
/// Returns the physical address of the page this entry refers to, returning None if it does
/// not
pub fn as_page(self) -> Option<PhysicalAddress> {
if self.0 & PageAttributes::PRESENT.bits() != 0 {
Some(PhysicalAddress::from_raw(self.0 & !0xFFF))
} else {
None
}
}
}
impl PageEntry<L2> {
/// Constructs a mapping which points to a 2MiB block
pub fn block(phys: PhysicalAddress, attrs: PageAttributes) -> Self {
Self(
u64::from(phys) | (attrs | PageAttributes::PRESENT | PageAttributes::BLOCK).bits(),
PhantomData,
)
}
}
impl PageEntry<L1> {
/// Constructs a mapping which points to a 1GiB block
pub fn block(phys: PhysicalAddress, attrs: PageAttributes) -> Self {
Self(
u64::from(phys) | (attrs | PageAttributes::PRESENT | PageAttributes::BLOCK).bits(),
PhantomData,
)
}
}
impl<L: NonTerminalEntryLevel> PageEntry<L> {
/// Constructs a mapping which points to a next-level table
pub fn table(phys: PhysicalAddress, attrs: PageAttributes) -> Self {
Self(
u64::from(phys)
| (attrs
| PageAttributes::PRESENT
| PageAttributes::WRITABLE
| PageAttributes::USER)
.bits(),
PhantomData,
)
}
/// Returns the physical address of the table this entry refers to, returning None if it
/// does not
pub fn as_table(self) -> Option<PhysicalAddress> {
if self.0 & PageAttributes::PRESENT.bits() != 0
&& self.0 & PageAttributes::BLOCK.bits() == 0
{
Some(PhysicalAddress::from_raw(self.0 & !0xFFF))
} else {
None
}
}
/// Returns `true` if the mapping represents a "page"/"block" and not a table
pub fn is_block(self) -> bool {
self.0 & PageAttributes::BLOCK.bits() != 0
}
}
impl<L: EntryLevel> PageEntry<L> {
/// An entry that is not mapped
pub const INVALID: Self = Self(0, PhantomData);
/// Reinterprets raw [u64] as a [PageEntry].
///
/// # Safety
///
/// Unsafe: the caller must ensure the value is a valid page translation entry.
pub const unsafe fn from_raw(raw: u64) -> Self {
Self(raw, PhantomData)
}
/// Returns the translation attributes of the entry
pub fn attributes(&self) -> PageAttributes {
PageAttributes::from_bits_retain(self.0)
}
/// Returns `true` if the entry contains a valid mapping to either a table or to a page/block
pub fn is_present(&self) -> bool {
self.0 & PageAttributes::PRESENT.bits() != 0
}
}
impl<L: EntryLevel> PageTable<L> {
/// Constructs a page table filled with invalid (non-present) entries
pub const fn zeroed() -> Self {
Self {
data: [PageEntry::INVALID; 512],
}
}
/// Reinterprets given [PageEntry] slice as a reference to [PageTable].
///
/// # Safety
///
/// Unsafe: the caller must ensure the provided reference is properly aligned and contains sane
/// data.
pub unsafe fn from_raw_slice_mut(data: &mut [PageEntry<L>; 512]) -> &mut Self {
core::mem::transmute(data)
}
/// Allocates a new page table, filling it with non-preset entries
pub fn new_zeroed<'a, TA: TableAllocator>(
) -> Result<PhysicalRefMut<'a, Self, KernelTableManagerImpl>, Error> {
let physical = TA::allocate_page_table()?;
let mut table =
unsafe { PhysicalRefMut::<'a, Self, KernelTableManagerImpl>::map(physical) };
for i in 0..512 {
table[i] = PageEntry::INVALID;
}
Ok(table)
}
// /// Returns the physical address of this table
// pub fn physical_address(&self) -> usize {
// unsafe { (self.data.as_ptr() as usize).physicalize() }
// }
}
impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
type NextLevel = PageTable<L::NextLevel>;
type TableRef = PhysicalRef<'static, Self::NextLevel, KernelTableManagerImpl>;
type TableRefMut = PhysicalRefMut<'static, Self::NextLevel, KernelTableManagerImpl>;
fn get(&self, index: usize) -> Option<Self::TableRef> {
self[index]
.as_table()
.map(|addr| unsafe { PhysicalRef::map(addr) })
}
fn get_mut(&mut self, index: usize) -> Option<Self::TableRefMut> {
self[index]
.as_table()
.map(|addr| unsafe { PhysicalRefMut::map(addr) })
}
fn get_mut_or_alloc<TA: TableAllocator>(
&mut self,
index: usize,
) -> Result<Self::TableRefMut, Error> {
let entry = self[index];
if let Some(table) = entry.as_table() {
Ok(unsafe { PhysicalRefMut::map(table) })
} else {
let table = PageTable::new_zeroed::<TA>()?;
self[index] = PageEntry::<L>::table(
unsafe { table.as_physical_address() },
PageAttributes::WRITABLE | PageAttributes::USER,
);
Ok(table)
}
}
}
impl EntryLevelDrop for PageTable<L3> {
const FULL_RANGE: Range<usize> = 0..512;
// Do nothing
unsafe fn drop_range<TA: TableAllocator>(&mut self, _range: Range<usize>) {}
}
impl<L: NonTerminalEntryLevel + 'static> EntryLevelDrop for PageTable<L>
where
PageTable<L::NextLevel>: EntryLevelDrop,
{
const FULL_RANGE: Range<usize> = 0..512;
unsafe fn drop_range<TA: TableAllocator>(&mut self, range: Range<usize>) {
for index in range {
let entry = self[index];
if let Some(table) = entry.as_table() {
let mut table_ref: PhysicalRefMut<PageTable<L::NextLevel>, KernelTableManagerImpl> =
PhysicalRefMut::map(table);
table_ref.drop_all::<TA>();
// Drop the table
drop(table_ref);
TA::free_page_table(table);
} else if entry.is_present() {
// Memory must've been cleared beforehand, so no non-table entries must be present
panic!(
"Expected a table containing only tables, got table[{}] = {:#x?}",
index, entry.0
);
}
self[index] = PageEntry::INVALID;
}
}
}
impl<L: EntryLevel> Index<usize> for PageTable<L> {
type Output = PageEntry<L>;
fn index(&self, index: usize) -> &Self::Output {
&self.data[index]
}
}
impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.data[index]
}
}
impl From<MapAttributes> for PageAttributes {
fn from(value: MapAttributes) -> Self {
let mut res = PageAttributes::WRITABLE;
if value.intersects(MapAttributes::USER_READ | MapAttributes::USER_WRITE) {
res |= PageAttributes::USER;
}
res
}
}
impl From<PageAttributes> for MapAttributes {
fn from(value: PageAttributes) -> Self {
let mut res = MapAttributes::empty();
if value.contains(PageAttributes::USER) {
res |= MapAttributes::USER_READ;
if value.contains(PageAttributes::WRITABLE) {
res |= MapAttributes::USER_WRITE;
}
}
// TODO ???
res |= MapAttributes::NON_GLOBAL;
res
}
}
+426
View File
@@ -0,0 +1,426 @@
//! Helper types for interfacing with x86-64 registers
#![allow(unused)]
macro_rules! impl_read {
($t:ident, $register:ty, $body:expr) => {
impl tock_registers::interfaces::Readable for $t {
type T = u64;
type R = $register;
#[inline]
fn get(&self) -> u64 {
$body
}
}
};
}
macro_rules! impl_write {
($t:ident, $register:ty, $value:ident, $body:expr) => {
impl tock_registers::interfaces::Writeable for $t {
type T = u64;
type R = $register;
#[inline]
fn set(&self, $value: u64) {
$body
}
}
};
}
macro_rules! msr_impl_read {
($t:ident, $addr:expr, $register:ty) => {
impl_read!($t, $register, {
let (high, low): (u32, u32);
unsafe {
core::arch::asm!(
"rdmsr",
in("ecx") $addr,
out("eax") low,
out("edx") high,
options(att_syntax)
);
}
((high as u64) << 32) | (low as u64)
});
};
($t:ident, $addr:expr) => { msr_impl_read!($t, $addr, ()); };
}
macro_rules! msr_impl_write {
($t:ident, $addr:expr, $register:ty) => {
impl_write!($t, $register, value, {
let low = value as u32;
let high = (value >> 32) as u32;
unsafe {
core::arch::asm!(
"wrmsr",
in("ecx") $addr,
in("eax") low,
in("edx") high,
options(att_syntax)
);
}
});
};
($t:ident, $addr:expr) => { msr_impl_write!($t, $addr, ()); };
}
macro_rules! cr_impl_read {
($t:ident, $cr:ident, $register:ty) => {
impl_read!($t, $register, {
let value: u64;
unsafe {
core::arch::asm!(
concat!("mov %", stringify!($cr), ", {}"),
out(reg) value,
options(att_syntax)
);
}
value
});
};
}
macro_rules! cr_impl_write {
($t:ident, $cr:ident, $register:ty) => {
impl_write!($t, $register, value, {
unsafe {
core::arch::asm!(
concat!("mov {}, %", stringify!($cr)),
in(reg) value,
options(att_syntax)
);
}
});
};
}
mod msr_ia32_kernel_gs_base {
const ADDR: u32 = 0xC0000102;
pub struct Reg;
msr_impl_read!(Reg, ADDR);
msr_impl_write!(Reg, ADDR);
/// IA32_KERNEL_GS_BASE model-specific register. Provides the base address for %gs-relative
/// loads/stores.
pub const MSR_IA32_KERNEL_GS_BASE: Reg = Reg;
}
mod msr_ia32_apic_base {
use tock_registers::{interfaces::Readable, register_bitfields};
register_bitfields! {
u64,
#[allow(missing_docs)]
#[doc = "IA32_APIC_BASE model-specific register"]
pub MSR_IA32_APIC_BASE [
#[doc = "Contains a virtual page number of the Local APIC base address for this processor"]
AddressPage OFFSET(12) NUMBITS(40) [],
#[doc = "If set, the APIC is enabled"]
ApicEnable OFFSET(11) NUMBITS(1) [],
#[doc = "If set, x2APIC mode is enabled"]
ExtendedEnable OFFSET(10) NUMBITS(1) [],
#[doc = "If set, this CPU is a bootstrap processor"]
BootstrapCpuCore OFFSET(8) NUMBITS(1) [],
]
}
const ADDR: u32 = 0x0000001B;
pub struct Reg;
msr_impl_read!(Reg, ADDR, MSR_IA32_APIC_BASE::Register);
msr_impl_write!(Reg, ADDR, MSR_IA32_APIC_BASE::Register);
impl Reg {
#[inline]
pub fn read_base(&self) -> u64 {
self.read(MSR_IA32_APIC_BASE::AddressPage) << 12
}
}
/// IA32_APIC_BASE model-specific register
pub const MSR_IA32_APIC_BASE: Reg = Reg;
}
mod msr_ia32_sfmask {
use tock_registers::register_bitfields;
register_bitfields! {
u64,
#[allow(missing_docs)]
pub MSR_IA32_SFMASK [
IF OFFSET(9) NUMBITS(1) [
Masked = 1,
Unmasked = 0
]
]
}
const ADDR: u32 = 0xC0000084;
pub struct Reg;
msr_impl_read!(Reg, ADDR, MSR_IA32_SFMASK::Register);
msr_impl_write!(Reg, ADDR, MSR_IA32_SFMASK::Register);
/// IA32_SFMASK model-specific register
pub const MSR_IA32_SFMASK: Reg = Reg;
}
mod msr_ia32_star {
use tock_registers::register_bitfields;
register_bitfields! {
u64,
#[allow(missing_docs)]
pub MSR_IA32_STAR [
SYSCALL_CS_SS OFFSET(32) NUMBITS(16) [],
SYSRET_CS_SS OFFSET(48) NUMBITS(16) [],
]
}
const ADDR: u32 = 0xC0000081;
pub struct Reg;
msr_impl_read!(Reg, ADDR, MSR_IA32_STAR::Register);
msr_impl_write!(Reg, ADDR, MSR_IA32_STAR::Register);
/// IA32_STAR model-specific register
pub const MSR_IA32_STAR: Reg = Reg;
}
mod msr_ia32_lstar {
const ADDR: u32 = 0xC0000082;
pub struct Reg;
msr_impl_read!(Reg, ADDR);
msr_impl_write!(Reg, ADDR);
/// IA32_LSTAR model-specific register
pub const MSR_IA32_LSTAR: Reg = Reg;
}
mod msr_ia32_efer {
use tock_registers::register_bitfields;
register_bitfields! {
u64,
#[allow(missing_docs)]
pub MSR_IA32_EFER [
// If set, support for SYSCALL/SYSRET instructions is enabled
SCE OFFSET(0) NUMBITS(1) [
Enable = 1,
Disable = 0
]
]
}
const ADDR: u32 = 0xC0000080;
pub struct Reg;
msr_impl_read!(Reg, ADDR, MSR_IA32_EFER::Register);
msr_impl_write!(Reg, ADDR, MSR_IA32_EFER::Register);
/// IA32_EFER Extended Feature Enable model-specific Register
pub const MSR_IA32_EFER: Reg = Reg;
}
mod cr0 {
use tock_registers::register_bitfields;
register_bitfields! {
u64,
#[allow(missing_docs)]
pub CR0 [
PG OFFSET(31) NUMBITS(1) [],
CD OFFSET(30) NUMBITS(1) [],
NW OFFSET(29) NUMBITS(1) [],
AM OFFSET(18) NUMBITS(1) [],
WP OFFSET(16) NUMBITS(1) [],
NE OFFSET(5) NUMBITS(1) [],
ET OFFSET(4) NUMBITS(1) [],
TS OFFSET(3) NUMBITS(1) [],
EM OFFSET(2) NUMBITS(1) [],
MP OFFSET(1) NUMBITS(1) [],
PE OFFSET(0) NUMBITS(1) [],
]
}
pub struct Reg;
cr_impl_read!(Reg, cr0, CR0::Register);
cr_impl_write!(Reg, cr0, CR0::Register);
/// x86-64 control register 0
pub const CR0: Reg = Reg;
}
mod cr3 {
use tock_registers::{interfaces::ReadWriteable, register_bitfields};
register_bitfields! {
u64,
#[allow(missing_docs)]
pub CR3 [
ADDR OFFSET(12) NUMBITS(40) [],
]
}
pub struct Reg;
cr_impl_read!(Reg, cr3, CR3::Register);
cr_impl_write!(Reg, cr3, CR3::Register);
impl Reg {
pub fn set_address(&self, address: usize) {
assert_eq!(address & 0xFFF, 0);
self.modify(CR3::ADDR.val((address as u64) >> 12))
}
}
/// x86-64 control register 3
pub const CR3: Reg = Reg;
}
mod cr4 {
use tock_registers::register_bitfields;
register_bitfields! {
u64,
#[allow(missing_docs)]
pub CR4 [
/// If set, XSAVE and extended processor states are enabled
OSXSAVE OFFSET(18) NUMBITS(1) [],
/// Indicates OS support for FXSAVE and FXRSTOR instructions
OSFXSR OFFSET(9) NUMBITS(1) [],
/// Performance-Monitoring Counter enable
PCE OFFSET(8) NUMBITS(1) [],
/// If set, "page global" attribute is enabled
PGE OFFSET(7) NUMBITS(1) [],
/// Machine Check enable
MCE OFFSET(6) NUMBITS(1) [],
/// Physical Address Extension (enabled if 64-bit mode)
PAE OFFSET(5) NUMBITS(1) [],
/// Page Size Extension (should be enabled by yboot)
PSE OFFSET(4) NUMBITS(1) [],
/// Debugging extensions
DE OFFSET(3) NUMBITS(1) [],
TSD OFFSET(2) NUMBITS(1) [],
PVI OFFSET(1) NUMBITS(1) [],
VME OFFSET(0) NUMBITS(1) [],
]
}
pub struct Reg;
cr_impl_read!(Reg, cr4, CR4::Register);
cr_impl_write!(Reg, cr4, CR4::Register);
/// x86-64 control register 4
pub const CR4: Reg = Reg;
}
mod xcr0 {
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields,
};
register_bitfields! {
u64,
#[allow(missing_docs)]
pub XCR0 [
/// If set, x87 FPU/MMX is enabled
X87 OFFSET(0) NUMBITS(1) [],
/// If set, XSAVE support for MXCSR and XMM registers is enabled
SSE OFFSET(1) NUMBITS(1) [],
/// If set, AVX is enabled and XSAVE supports YMM upper halves
AVX OFFSET(2) NUMBITS(1) [],
]
}
pub struct Reg;
impl Readable for Reg {
type T = u64;
type R = XCR0::Register;
fn get(&self) -> Self::T {
let eax: u32;
let edx: u32;
unsafe {
core::arch::asm!(
"xgetbv",
in("ecx") 0,
out("eax") eax,
out("edx") edx,
options(att_syntax)
);
}
((edx as u64) << 32) | (eax as u64)
}
}
impl Writeable for Reg {
type T = u64;
type R = XCR0::Register;
fn set(&self, value: Self::T) {
let eax = value as u32;
let edx = (value >> 32) as u32;
unsafe {
core::arch::asm!(
"xsetbv",
in("ecx") 0,
in("eax") eax,
in("edx") edx,
options(att_syntax)
);
}
}
}
/// Extended control register for SSE/AVX/FPU configuration
pub const XCR0: Reg = Reg;
}
use core::ptr::NonNull;
pub use cr0::CR0;
pub use cr3::CR3;
pub use cr4::CR4;
pub use msr_ia32_apic_base::MSR_IA32_APIC_BASE;
pub use msr_ia32_efer::MSR_IA32_EFER;
pub use msr_ia32_kernel_gs_base::MSR_IA32_KERNEL_GS_BASE;
pub use msr_ia32_lstar::MSR_IA32_LSTAR;
pub use msr_ia32_sfmask::MSR_IA32_SFMASK;
pub use msr_ia32_star::MSR_IA32_STAR;
pub use xcr0::XCR0;
#[repr(C, align(0x10))]
pub struct FpuContext {
data: [u8; 512],
}
impl FpuContext {
pub fn new() -> Self {
let mut value = Self { data: [0; 512] };
unsafe {
let ptr = value.data.as_mut_ptr();
core::arch::asm!("fninit; fxsave64 ({})", in(reg) ptr, options(att_syntax));
}
value
}
pub unsafe fn save(dst: *mut FpuContext) {
core::arch::asm!("fxsave64 ({})", in(reg) dst, options(att_syntax));
}
pub unsafe fn restore(src: *mut FpuContext) {
core::arch::asm!("fxrstor64 ({})", in(reg) src, options(att_syntax));
}
}