Add 'kernel/' from commit '7f1f6b73377367db17f98a740316b904c37ce3b1'
git-subtree-dir: kernel git-subtree-mainline:817f71f90fgit-subtree-split:7f1f6b7337
This commit is contained in:
@@ -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"
|
||||
@@ -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
|
||||
@@ -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)
|
||||
);
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user