feat: ring3
This commit is contained in:
@@ -127,14 +127,6 @@ impl LocalApic {
|
||||
return Err(LocalApicError::NotBootSystemProcessor);
|
||||
}
|
||||
|
||||
println!("BSP: {}", bsp);
|
||||
println!("x2Apic: {}", x2apix);
|
||||
println!("enabled: {}", enabled);
|
||||
println!(
|
||||
"xApic physical address: {:x}",
|
||||
xapic_physical_addr.as_usize()
|
||||
);
|
||||
|
||||
let access = match x2apix {
|
||||
true => LocalApicAccess::X2Apic,
|
||||
false => {
|
||||
|
||||
+24
-4
@@ -11,6 +11,7 @@ pub enum CpuFeaturesError {
|
||||
pub(crate) struct CpuFeatures {
|
||||
pub nx_supported: bool,
|
||||
pub nx_enabled: bool,
|
||||
pub global_pages: bool,
|
||||
pub physical_address_bits: u8,
|
||||
pub virtual_address_bits: u8,
|
||||
pub five_level_paging_active: bool,
|
||||
@@ -23,22 +24,23 @@ pub fn detect_features_and_enable() -> Result<CpuFeatures, CpuFeaturesError> {
|
||||
let mut features = CpuFeatures {
|
||||
nx_supported: false,
|
||||
nx_enabled: false,
|
||||
global_pages: false,
|
||||
physical_address_bits: 0,
|
||||
virtual_address_bits: 0,
|
||||
five_level_paging_active: false,
|
||||
};
|
||||
|
||||
let cpuid_result = core::arch::x86_64::__cpuid_count(0x80000000, 0);
|
||||
let cpuid_result = core::arch::x86_64::__cpuid(0x80000000);
|
||||
|
||||
if cpuid_result.eax < 0x80000008 {
|
||||
return Err(CpuFeaturesError::CpuidFeaturesNotSupported);
|
||||
}
|
||||
|
||||
let cpuid_result = core::arch::x86_64::__cpuid_count(0x80000001, 0);
|
||||
let cpuid_result = core::arch::x86_64::__cpuid(0x80000001);
|
||||
|
||||
features.nx_supported = cpuid_result.edx & (1 << 20) != 0;
|
||||
|
||||
let cpuid_result = core::arch::x86_64::__cpuid_count(0x80000008, 0);
|
||||
let cpuid_result = core::arch::x86_64::__cpuid(0x80000008);
|
||||
|
||||
features.physical_address_bits = (cpuid_result.eax & 0xFF) as u8;
|
||||
if !(12..=52).contains(&features.physical_address_bits) {
|
||||
@@ -57,8 +59,16 @@ pub fn detect_features_and_enable() -> Result<CpuFeatures, CpuFeaturesError> {
|
||||
return Err(CpuFeaturesError::InvalidVirtualAddressWidth);
|
||||
}
|
||||
|
||||
let cpuid_result = core::arch::x86_64::__cpuid(0x1);
|
||||
|
||||
features.global_pages = cpuid_result.edx & (1 << 13) != 0;
|
||||
|
||||
if features.global_pages {
|
||||
let cr4 = read_cr4();
|
||||
write_cr4(cr4 | 1 << 7);
|
||||
}
|
||||
|
||||
if features.nx_supported {
|
||||
let cpuid_result = core::arch::x86_64::__cpuid_count(0x1, 0);
|
||||
let msr_supported = cpuid_result.edx & (1 << 5) != 0;
|
||||
|
||||
if !msr_supported {
|
||||
@@ -78,6 +88,16 @@ pub fn detect_features_and_enable() -> Result<CpuFeatures, CpuFeaturesError> {
|
||||
Ok(features)
|
||||
}
|
||||
|
||||
fn write_cr4(value: usize) {
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov cr4, {}",
|
||||
in(reg) value,
|
||||
options(nostack, preserves_flags)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_cr4() -> usize {
|
||||
let value: usize;
|
||||
|
||||
|
||||
+35
-6
@@ -1,13 +1,15 @@
|
||||
use core::arch::asm;
|
||||
|
||||
use crate::memory::VirtualAddr;
|
||||
|
||||
#[repr(C, align(8))]
|
||||
struct Gdt {
|
||||
entries: [u64; 5],
|
||||
entries: [u64; 7],
|
||||
}
|
||||
|
||||
impl Gdt {
|
||||
pub const fn new() -> Self {
|
||||
Self { entries: [0; 5] }
|
||||
Self { entries: [0; 7] }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +49,9 @@ const DOUBLE_FAULT_STACK_SIZE: usize = 16 * 1024;
|
||||
|
||||
pub(super) const KERNEL_CODE_SELECTOR: u16 = 1 * 8;
|
||||
pub(super) const KERNEL_DATA_SELECTOR: u16 = 2 * 8;
|
||||
pub(super) const TSS_SELECTOR: u16 = 3 * 8;
|
||||
pub(super) const USER_CODE_SELECTOR: u16 = (3 * 8) | 3;
|
||||
pub(super) const USER_DATA_SELECTOR: u16 = (4 * 8) | 3;
|
||||
pub(super) const TSS_SELECTOR: u16 = 5 * 8;
|
||||
|
||||
#[repr(align(16))]
|
||||
#[allow(dead_code)] // field 0 is read, rust just cant tell
|
||||
@@ -74,6 +78,8 @@ pub fn init() {
|
||||
0,
|
||||
kernel_code_descriptor(),
|
||||
kernel_data_descriptor(),
|
||||
user_code_descriptor(),
|
||||
user_data_descriptor(),
|
||||
tss_low,
|
||||
tss_high,
|
||||
],
|
||||
@@ -84,6 +90,12 @@ pub fn init() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_kernel_stack(stack_top: VirtualAddr) {
|
||||
unsafe {
|
||||
TSS.privilege_stacks[0] = stack_top.as_usize() as u64;
|
||||
}
|
||||
}
|
||||
|
||||
fn gdt_reload() {
|
||||
unsafe {
|
||||
asm!(
|
||||
@@ -119,17 +131,34 @@ fn gdt_reload() {
|
||||
}
|
||||
|
||||
const PRESENT: u64 = 1 << 47;
|
||||
const USER_DESCRIPTOR: u64 = 1 << 44;
|
||||
const CODE_DATA_DESCRIPTOR: u64 = 1 << 44;
|
||||
const USER_PRIVILEGE: u64 = 3 << 45;
|
||||
const EXECUTABLE: u64 = 1 << 43;
|
||||
const READ_WRITE: u64 = 1 << 41;
|
||||
const GRANULARITY: u64 = 1 << 55;
|
||||
const SIZE: u64 = 1 << 54;
|
||||
const LONG_MODE: u64 = 1 << 53;
|
||||
|
||||
fn kernel_code_descriptor() -> u64 {
|
||||
PRESENT | USER_DESCRIPTOR | EXECUTABLE | READ_WRITE | LONG_MODE
|
||||
PRESENT | CODE_DATA_DESCRIPTOR | EXECUTABLE | READ_WRITE | LONG_MODE | GRANULARITY
|
||||
}
|
||||
|
||||
fn kernel_data_descriptor() -> u64 {
|
||||
PRESENT | USER_DESCRIPTOR | READ_WRITE
|
||||
PRESENT | CODE_DATA_DESCRIPTOR | READ_WRITE | SIZE | GRANULARITY
|
||||
}
|
||||
|
||||
fn user_code_descriptor() -> u64 {
|
||||
PRESENT
|
||||
| CODE_DATA_DESCRIPTOR
|
||||
| USER_PRIVILEGE
|
||||
| EXECUTABLE
|
||||
| READ_WRITE
|
||||
| LONG_MODE
|
||||
| GRANULARITY
|
||||
}
|
||||
|
||||
fn user_data_descriptor() -> u64 {
|
||||
PRESENT | CODE_DATA_DESCRIPTOR | USER_PRIVILEGE | READ_WRITE | SIZE | GRANULARITY
|
||||
}
|
||||
|
||||
fn tss_descriptor(tss: *const TaskStateSegment) -> [u64; 2] {
|
||||
|
||||
@@ -46,11 +46,20 @@ fatal_with_error_code!(stack_segment_fault_handler, "STACK-SEGMENT FAULT");
|
||||
fatal_with_error_code!(general_protection_handler, "GENERAL PROTECTION FAULT");
|
||||
fatal_with_error_code!(alignment_check_handler, "ALIGNMENT CHECK");
|
||||
|
||||
extern "x86-interrupt" fn user_test_exit_handler(frame: InterruptStackFrame) {
|
||||
if frame.code_segment & 0b11 != 3 {
|
||||
panic!("user_test_exit_handler called from kernel");
|
||||
}
|
||||
|
||||
println!("User test exit");
|
||||
hcf();
|
||||
}
|
||||
|
||||
pub(super) fn install(idt: &mut idt::Idt) {
|
||||
idt.set_handler(0, divide_error_handler, 0);
|
||||
idt.set_handler(1, debug_handler, 0);
|
||||
idt.set_handler(2, non_maskable_interrupt_handler, 0);
|
||||
idt.set_handler(3, breakpoint_handler, 0);
|
||||
idt.set_user_handler(3, breakpoint_handler, 0);
|
||||
idt.set_handler(6, invalid_opcode_handler, 0);
|
||||
idt.set_handler(7, device_not_available_handler, 0);
|
||||
idt.set_error_code_handler(8, double_fault_handler, 1);
|
||||
@@ -63,6 +72,8 @@ pub(super) fn install(idt: &mut idt::Idt) {
|
||||
idt.set_error_code_handler(17, alignment_check_handler, 0);
|
||||
idt.set_handler(18, machine_check_handler, 0);
|
||||
idt.set_handler(19, simd_floating_point_handler, 0);
|
||||
|
||||
idt.set_user_handler(0x80, user_test_exit_handler, 0);
|
||||
}
|
||||
|
||||
fn read_cr2() -> u64 {
|
||||
|
||||
@@ -20,7 +20,7 @@ impl IdtEntry {
|
||||
const fn missing() -> Self {
|
||||
return Self {
|
||||
offset_low: 0,
|
||||
code_selector: 0x08,
|
||||
code_selector: 0,
|
||||
ist: 0,
|
||||
attributes: 0,
|
||||
offset_middle: 0,
|
||||
@@ -49,6 +49,8 @@ pub(super) struct InterruptStackFrame {
|
||||
const INTERRUPT_GATE: u8 = 0b1110;
|
||||
const PRESENT: u8 = 1 << 7;
|
||||
const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE;
|
||||
const USER_DPL: u8 = 3 << 5;
|
||||
const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE;
|
||||
|
||||
pub(super) type Handler = extern "x86-interrupt" fn(InterruptStackFrame);
|
||||
pub(super) type ErrorCodeHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64);
|
||||
@@ -65,7 +67,7 @@ impl Idt {
|
||||
}
|
||||
|
||||
pub(super) fn set_handler(&mut self, vector: u8, handler: Handler, ist: u8) {
|
||||
self.set_handler_address(vector, handler as usize, ist);
|
||||
self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE);
|
||||
}
|
||||
|
||||
pub(super) fn set_error_code_handler(
|
||||
@@ -74,17 +76,21 @@ impl Idt {
|
||||
handler: ErrorCodeHandler,
|
||||
ist: u8,
|
||||
) {
|
||||
self.set_handler_address(vector, handler as usize, ist);
|
||||
self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE);
|
||||
}
|
||||
|
||||
fn set_handler_address(&mut self, vector: u8, address: usize, ist: u8) {
|
||||
pub(super) fn set_user_handler(&mut self, vector: u8, handler: Handler, ist: u8) {
|
||||
self.set_handler_address(vector, handler as usize, ist, USER_INTERRUPT_GATE);
|
||||
}
|
||||
|
||||
fn set_handler_address(&mut self, vector: u8, address: usize, ist: u8, attributes: u8) {
|
||||
self.entries[vector as usize] = IdtEntry {
|
||||
offset_low: address as u16,
|
||||
offset_middle: (address >> 16) as u16,
|
||||
offset_high: (address >> 32) as u32,
|
||||
code_selector: KERNEL_CODE_SELECTOR,
|
||||
ist: ist & 0b111,
|
||||
attributes: KERNEL_INTERRUPT_GATE,
|
||||
attributes,
|
||||
reserved: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ const MASKED: u32 = 1 << 16;
|
||||
pub const IOAPIC_VIRTUAL_ADDRESS: VirtualAddr = VirtualAddr::new(0xFFFF_FFFD_1000_0000);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(unused)]
|
||||
pub enum IoApicError {
|
||||
IdMismatch { expected: u8, actual: u8 },
|
||||
GsiOutsideRange,
|
||||
@@ -73,13 +74,6 @@ impl IoApic {
|
||||
actual: id,
|
||||
});
|
||||
}
|
||||
println!("IOAPIC ID: {:#X}", id);
|
||||
println!("IOAPIC version: {:#X}", version & 0xFF);
|
||||
|
||||
println!(
|
||||
"IOAPIC redirection entry count: {:#X}",
|
||||
io_apic.redirection_entry_count
|
||||
);
|
||||
|
||||
Ok(io_apic)
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ pub fn init() -> ArchState {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(unused)]
|
||||
pub enum InterruptInitError {
|
||||
InvalidLocalApicId,
|
||||
InvalidLocalApicAddress,
|
||||
@@ -162,6 +163,8 @@ pub fn init_interrupt_controller(
|
||||
/// - The stack is currently mapped, writable, and 16-byte aligned
|
||||
pub unsafe fn enter_kernel(stack_top: VirtualAddr, handoff: *mut KernelHandoff) -> ! {
|
||||
unsafe {
|
||||
gdt::set_kernel_stack(stack_top);
|
||||
|
||||
asm!(
|
||||
"mov rsp, {stack_top}",
|
||||
"xor rbp, rbp",
|
||||
@@ -175,6 +178,38 @@ pub unsafe fn enter_kernel(stack_top: VirtualAddr, handoff: *mut KernelHandoff)
|
||||
};
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// - `user_instruction_pointer` and `user_stack_pointer` must be valid user mappings.
|
||||
/// - The active address space must contain the kernel and supplied user mappings.
|
||||
pub unsafe fn enter_user(
|
||||
user_instruction_pointer: VirtualAddr,
|
||||
user_stack_pointer: VirtualAddr,
|
||||
) -> ! {
|
||||
println!("Entering user mode");
|
||||
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov ds, {user_data_selector:x}",
|
||||
"mov es, {user_data_selector:x}",
|
||||
"mov fs, {user_data_selector:x}",
|
||||
"mov gs, {user_data_selector:x}", // ss is handled by iretq
|
||||
|
||||
"push {user_data_selector}",
|
||||
"push {user_stack_pointer}",
|
||||
"pushfq",
|
||||
"push {user_code_selector}",
|
||||
"push {user_instruction_pointer}",
|
||||
"iretq",
|
||||
user_data_selector = in(reg) gdt::USER_DATA_SELECTOR as usize,
|
||||
user_code_selector = in(reg) gdt::USER_CODE_SELECTOR as usize,
|
||||
user_instruction_pointer = in(reg) user_instruction_pointer.as_usize(),
|
||||
user_stack_pointer = in(reg) user_stack_pointer.as_usize(),
|
||||
options(noreturn)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn halt() {
|
||||
unsafe {
|
||||
asm!("hlt");
|
||||
|
||||
@@ -14,6 +14,7 @@ pub const PAGE_TABLE_ENTRIES: usize = 512;
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PagingConfig {
|
||||
physical_address_bits: u8,
|
||||
global_pages: bool,
|
||||
nx_enabled: bool,
|
||||
mode: PagingMode,
|
||||
}
|
||||
@@ -23,6 +24,7 @@ impl PagingConfig {
|
||||
Self {
|
||||
physical_address_bits: features.physical_address_bits,
|
||||
nx_enabled: features.nx_enabled,
|
||||
global_pages: features.global_pages,
|
||||
mode: if features.five_level_paging_active {
|
||||
PagingMode::FiveLevel
|
||||
} else {
|
||||
@@ -119,6 +121,7 @@ impl PageTableEntry {
|
||||
const WRITABLE: u64 = 1 << 1;
|
||||
const USER_ACCESSIBLE: u64 = 1 << 2;
|
||||
const HUGE_PAGE: u64 = 1 << 7;
|
||||
const GLOBAL: u64 = 1 << 8;
|
||||
const NX: u64 = 1 << 63;
|
||||
|
||||
const WRITE_THROUGH: u64 = 1 << 3;
|
||||
@@ -130,6 +133,7 @@ impl PageTableEntry {
|
||||
permissions: PagePermissions,
|
||||
cache_policy: CachePolicy,
|
||||
config: PagingConfig,
|
||||
global: bool,
|
||||
) -> Result<Self, PageTableEntryError> {
|
||||
if physical_address.as_usize() >= config.physical_address_limit() {
|
||||
return Err(PageTableEntryError::PhysicalAddressTooLarge);
|
||||
@@ -151,6 +155,10 @@ impl PageTableEntry {
|
||||
value |= Self::NX;
|
||||
}
|
||||
|
||||
if global && config.global_pages {
|
||||
value |= Self::GLOBAL;
|
||||
}
|
||||
|
||||
// TODO: these bit positions very by page size
|
||||
// set PAT
|
||||
match cache_policy {
|
||||
@@ -200,6 +208,10 @@ impl PageTableEntry {
|
||||
self.0 & Self::HUGE_PAGE != 0
|
||||
}
|
||||
|
||||
fn is_global(&self) -> bool {
|
||||
self.0 & Self::GLOBAL != 0
|
||||
}
|
||||
|
||||
fn table_frame(&self, config: PagingConfig) -> Option<FrameAddr> {
|
||||
if !self.is_present() || self.is_huge() {
|
||||
return None;
|
||||
@@ -254,8 +266,8 @@ pub(crate) enum PageTableCreateError {
|
||||
|
||||
pub struct PageTable {
|
||||
pub direct_map: DirectMap,
|
||||
frame: OwnedFrame,
|
||||
config: PagingConfig,
|
||||
frame: OwnedFrame,
|
||||
}
|
||||
|
||||
impl PageTable {
|
||||
@@ -279,6 +291,10 @@ impl PageTable {
|
||||
})
|
||||
}
|
||||
|
||||
pub const fn config(&self) -> PagingConfig {
|
||||
self.config
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
let cr3 = unsafe { read_cr3(self.config) };
|
||||
|
||||
@@ -395,6 +411,7 @@ impl PageTable {
|
||||
permissions: PagePermissions,
|
||||
allocator: &mut FrameAllocator,
|
||||
cache_policy: CachePolicy,
|
||||
global: bool,
|
||||
) -> Result<(), MapError> {
|
||||
let address = mapped_addr.as_usize();
|
||||
|
||||
@@ -411,6 +428,7 @@ impl PageTable {
|
||||
permissions,
|
||||
cache_policy,
|
||||
self.config,
|
||||
global,
|
||||
)
|
||||
.map_err(|error| match error {
|
||||
PageTableEntryError::PhysicalAddressTooLarge => MapError::PhysicalAddressTooLarge,
|
||||
@@ -644,6 +662,17 @@ impl PageTable {
|
||||
Ok(frame)
|
||||
}
|
||||
|
||||
pub fn copy_kernel_mappings_to(&self, destination: &mut PageTable) {
|
||||
let src = self
|
||||
.table(self.frame.frame_address())
|
||||
.expect("source page table outside direct map");
|
||||
let dest = destination
|
||||
.table_mut(destination.frame.frame_address())
|
||||
.expect("destination page table outside direct map");
|
||||
|
||||
dest[256..512].copy_from_slice(&src[256..512]);
|
||||
}
|
||||
|
||||
fn flush_tlb_if_active(&self, page: VirtualAddr) {
|
||||
debug_assert!(self.is_canonical(page.as_usize()));
|
||||
|
||||
@@ -702,6 +731,30 @@ impl PageTable {
|
||||
|
||||
unsafe { allocator.dealloc(self.frame) };
|
||||
}
|
||||
|
||||
pub unsafe fn destroy_user(mut self, allocator: &mut FrameAllocator) {
|
||||
assert!(!self.is_active(), "attempted to destroy active page table");
|
||||
|
||||
let root_frame = self.frame.frame_address();
|
||||
|
||||
for idx in 0..256 {
|
||||
let entry = self.table(root_frame).expect("table outside direct map")[idx];
|
||||
if !entry.is_present() || entry.is_huge() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let child = entry
|
||||
.table_frame(self.config)
|
||||
.expect("invalid page table entry");
|
||||
self.destroy_children(child, 1, allocator);
|
||||
|
||||
self.table_mut(root_frame)
|
||||
.expect("table outside direct map")[idx] = PageTableEntry::null();
|
||||
unsafe { allocator.dealloc(OwnedFrame::from_raw(child)) };
|
||||
}
|
||||
|
||||
unsafe { allocator.dealloc(self.frame) };
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_huge_page(
|
||||
|
||||
+44
-84
@@ -11,86 +11,11 @@ mod platform;
|
||||
|
||||
use crate::{
|
||||
debug::serial,
|
||||
memory::{AddressSpace, MemoryRegionKind, VirtualAddr},
|
||||
memory::{
|
||||
AddressSpace, KernelStack, MemoryRegionKind, PagePermissions, UserStack, VirtualAddr,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(unused)]
|
||||
enum KernelStackCreateError {
|
||||
AddressOverflow,
|
||||
OutOfFrames,
|
||||
GuardPageMapped,
|
||||
Map(memory::MapError),
|
||||
}
|
||||
|
||||
struct KernelStack {
|
||||
start: memory::VirtualAddr,
|
||||
pages: usize,
|
||||
}
|
||||
|
||||
const BOOTSTRAP_STACK_TOP: usize = 0xFFFF_FFFE_0000_0000;
|
||||
const KERNEL_STACK_SIZE: usize = 64 * 1024;
|
||||
const KERNEL_STACK_GUARD_SIZE: usize = memory::FRAME_SIZE;
|
||||
|
||||
const BOOTSTRAP_STACK_START: usize = BOOTSTRAP_STACK_TOP - KERNEL_STACK_SIZE;
|
||||
const BOOTSTRAP_STACK_GUARD: usize = BOOTSTRAP_STACK_START - KERNEL_STACK_GUARD_SIZE;
|
||||
|
||||
impl KernelStack {
|
||||
fn allocate(
|
||||
address_space: &mut AddressSpace,
|
||||
allocator: &mut memory::FrameAllocator,
|
||||
) -> Result<Self, KernelStackCreateError> {
|
||||
let kernel_stack_start = VirtualAddr::new(BOOTSTRAP_STACK_START);
|
||||
|
||||
let guard_page = VirtualAddr::new(BOOTSTRAP_STACK_GUARD);
|
||||
|
||||
if address_space.to_physical(guard_page).is_some() {
|
||||
// guard page should be *unmapped* so we get a page fault if we try to access it
|
||||
return Err(KernelStackCreateError::GuardPageMapped);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
// on error we will just leak the frames
|
||||
// because like what are going to do if we recover them? Die happily without leaking frames? idgaf
|
||||
while i < KERNEL_STACK_SIZE {
|
||||
let frame = allocator
|
||||
.alloc()
|
||||
.ok_or(KernelStackCreateError::OutOfFrames)?;
|
||||
address_space
|
||||
.map(
|
||||
frame.frame_address().start_address(),
|
||||
VirtualAddr::new(
|
||||
kernel_stack_start
|
||||
.as_usize()
|
||||
.checked_add(i)
|
||||
.ok_or(KernelStackCreateError::AddressOverflow)?,
|
||||
),
|
||||
memory::PagePermissions::new(true, false, false),
|
||||
allocator,
|
||||
memory::CachePolicy::WriteBack,
|
||||
)
|
||||
.map_err(|err| KernelStackCreateError::Map(err))?;
|
||||
i += memory::FRAME_SIZE;
|
||||
}
|
||||
|
||||
debug_assert!(address_space.to_physical(guard_page).is_none());
|
||||
|
||||
Ok(Self {
|
||||
start: kernel_stack_start,
|
||||
pages: i / memory::FRAME_SIZE,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn top(&self) -> Result<VirtualAddr, KernelStackCreateError> {
|
||||
Ok(VirtualAddr::new(
|
||||
self.start
|
||||
.as_usize()
|
||||
.checked_add(self.pages * memory::FRAME_SIZE)
|
||||
.ok_or(KernelStackCreateError::AddressOverflow)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KernelHandoff {
|
||||
allocator: memory::FrameAllocator,
|
||||
address_space: AddressSpace,
|
||||
@@ -134,7 +59,7 @@ pub extern "C" fn _start() -> ! {
|
||||
.to_virtual(handoff_frame.frame_address().start_address())
|
||||
.expect("failed to map kernel handoff");
|
||||
|
||||
let bootstrap_stack_top = bootstrap_stack.top().unwrap();
|
||||
let bootstrap_stack_top = bootstrap_stack.top();
|
||||
let handoff = KernelHandoff {
|
||||
allocator,
|
||||
address_space,
|
||||
@@ -177,10 +102,7 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
||||
MemoryRegionKind::BootloaderReclaimable,
|
||||
);
|
||||
|
||||
println!(
|
||||
"Initializing local ACPI... {:#X}",
|
||||
boot_info.rsdp.as_usize()
|
||||
);
|
||||
println!("Initializing local ACPI...",);
|
||||
|
||||
let acpi = platform::acpi::init(&boot_info, direct_map).expect("failed to initialize ACPI");
|
||||
|
||||
@@ -193,7 +115,45 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
||||
arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space)
|
||||
.expect("failed to initialize interrupt controller");
|
||||
|
||||
println!("interrupt controller: {:?}", interrupt_controller);
|
||||
let mut user_addr_space = address_space
|
||||
.new_user(&mut allocator)
|
||||
.expect("failed to create user address space");
|
||||
|
||||
let user_stack = UserStack::allocate(&mut user_addr_space, &mut allocator)
|
||||
.expect("failed to allocate user stack");
|
||||
|
||||
let user_instruction_pointer = VirtualAddr::new(0x8000);
|
||||
let user_code_page = allocator
|
||||
.alloc()
|
||||
.expect("failed to allocate user code page")
|
||||
.frame_address();
|
||||
user_addr_space
|
||||
.map(
|
||||
user_code_page.start_address(),
|
||||
user_instruction_pointer,
|
||||
PagePermissions::new(true, true, true),
|
||||
&mut allocator,
|
||||
memory::CachePolicy::WriteBack,
|
||||
)
|
||||
.expect("failed to map user code page");
|
||||
|
||||
unsafe {
|
||||
user_addr_space.activate();
|
||||
|
||||
let user_code: [u8; 5] = [
|
||||
0xCC, // INT3
|
||||
0xCD, 0x80, // INT 0x80
|
||||
0xEB, 0xFE, // JMP -2 (loop forever if exit returns)
|
||||
];
|
||||
|
||||
core::ptr::copy_nonoverlapping(
|
||||
user_code.as_ptr(),
|
||||
user_instruction_pointer.as_mut_ptr::<u8>(),
|
||||
user_code.len(),
|
||||
);
|
||||
|
||||
arch::enter_user(user_instruction_pointer, user_stack.top());
|
||||
}
|
||||
|
||||
hcf();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ pub enum MapError {
|
||||
OutOfMemory,
|
||||
PageTableUnavailable,
|
||||
CorruptedPageTable,
|
||||
InvalidUserAddress,
|
||||
InvalidUserMap,
|
||||
}
|
||||
|
||||
impl From<PageTableMapError> for MapError {
|
||||
@@ -46,6 +48,7 @@ pub enum UnmapError {
|
||||
MappingConflict,
|
||||
PageTableUnavailable,
|
||||
CorruptedPageTable,
|
||||
InvalidUserAddress,
|
||||
}
|
||||
|
||||
impl From<PageTableUnmapError> for UnmapError {
|
||||
@@ -79,6 +82,7 @@ impl From<PageTableCreateError> for AddressSpaceCreateError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum AddressSpaceKind {
|
||||
Kernel,
|
||||
User,
|
||||
@@ -168,8 +172,15 @@ impl AddressSpace {
|
||||
Ok(space)
|
||||
}
|
||||
|
||||
pub fn new_user(kernel_space: &AddressSpace, allocator: &mut FrameAllocator) -> Self {
|
||||
todo!()
|
||||
pub fn new_user(&self, allocator: &mut FrameAllocator) -> Result<Self, PageTableCreateError> {
|
||||
let mut user_root = PageTable::new(self.root.direct_map, self.root.config(), allocator)?;
|
||||
|
||||
self.root.copy_kernel_mappings_to(&mut user_root);
|
||||
|
||||
Ok(Self {
|
||||
root: user_root,
|
||||
kind: AddressSpaceKind::User,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn map(
|
||||
@@ -180,11 +191,33 @@ impl AddressSpace {
|
||||
allocator: &mut FrameAllocator,
|
||||
cache_policy: CachePolicy,
|
||||
) -> Result<(), MapError> {
|
||||
let global = self.kind == AddressSpaceKind::Kernel;
|
||||
|
||||
if self.kind == AddressSpaceKind::User {
|
||||
if virtual_addr.as_usize() >= 0x0000_8000_0000_0000 {
|
||||
return Err(MapError::InvalidUserAddress);
|
||||
}
|
||||
|
||||
if !permissions.user_accessible {
|
||||
return Err(MapError::InvalidUserMap);
|
||||
}
|
||||
|
||||
// TODO: a user address space should not be able to map kernel memory
|
||||
// or ACPI memory, or anything like that
|
||||
}
|
||||
|
||||
let frame = FrameAddr::from_start_address(physical_addr)
|
||||
.ok_or(MapError::PhysicalAddressUnaligned)?;
|
||||
|
||||
self.root
|
||||
.map(virtual_addr, frame, permissions, allocator, cache_policy)
|
||||
.map(
|
||||
virtual_addr,
|
||||
frame,
|
||||
permissions,
|
||||
allocator,
|
||||
cache_policy,
|
||||
global,
|
||||
)
|
||||
.map_err(MapError::from)
|
||||
}
|
||||
|
||||
@@ -272,6 +305,10 @@ impl AddressSpace {
|
||||
virtual_addr: VirtualAddr,
|
||||
allocator: &mut FrameAllocator,
|
||||
) -> Result<FrameAddr, UnmapError> {
|
||||
if self.kind == AddressSpaceKind::User && virtual_addr.as_usize() >= 0x0000_8000_0000_0000 {
|
||||
return Err(UnmapError::InvalidUserAddress);
|
||||
}
|
||||
|
||||
unsafe { self.root.unmap(virtual_addr, allocator) }.map_err(UnmapError::from)
|
||||
}
|
||||
|
||||
@@ -293,7 +330,10 @@ impl AddressSpace {
|
||||
/// no CPU or kernel operation can access its paging structures.
|
||||
pub unsafe fn destroy(self, allocator: &mut FrameAllocator) {
|
||||
unsafe {
|
||||
self.root.destroy(allocator);
|
||||
match self.kind {
|
||||
AddressSpaceKind::Kernel => self.root.destroy(allocator),
|
||||
AddressSpaceKind::User => self.root.destroy_user(allocator),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
mod address_space;
|
||||
mod frame;
|
||||
pub mod stack;
|
||||
|
||||
#[allow(unused)]
|
||||
pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError};
|
||||
pub use frame::{FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame};
|
||||
pub use stack::{KernelStack, StackCreateError, UserStack};
|
||||
|
||||
pub struct KernelSegment {
|
||||
pub physical_base: PhysicalAddr,
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
use crate::memory::{
|
||||
AddressSpace, CachePolicy, FRAME_SIZE, FrameAllocator, MapError, OwnedFrame, PagePermissions,
|
||||
VirtualAddr,
|
||||
};
|
||||
|
||||
const STACK_PAGES: usize = 16;
|
||||
const STACK_SIZE: usize = STACK_PAGES * FRAME_SIZE;
|
||||
|
||||
const KERNEL_STACK_TOP: VirtualAddr = VirtualAddr::new(0xFFFF_FFFE_0000_0000);
|
||||
const USER_STACK_TOP: VirtualAddr = VirtualAddr::new(0x0000_7FFF_FFFF_F000);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StackCreateError {
|
||||
AddressOverflow,
|
||||
UnalignedStackTop,
|
||||
OutOfFrames,
|
||||
GuardPageMapped,
|
||||
Map(MapError),
|
||||
}
|
||||
|
||||
struct StackMapping {
|
||||
guard_page: VirtualAddr,
|
||||
mapped_start: VirtualAddr,
|
||||
top: VirtualAddr,
|
||||
}
|
||||
|
||||
impl StackMapping {
|
||||
fn allocate(
|
||||
address_space: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
top: VirtualAddr,
|
||||
permissions: PagePermissions,
|
||||
) -> Result<Self, StackCreateError> {
|
||||
if top.as_usize() % FRAME_SIZE != 0 {
|
||||
return Err(StackCreateError::UnalignedStackTop);
|
||||
}
|
||||
|
||||
let mapped_start = VirtualAddr::new(
|
||||
top.as_usize()
|
||||
.checked_sub(STACK_SIZE)
|
||||
.ok_or(StackCreateError::AddressOverflow)?,
|
||||
);
|
||||
let guard_page = VirtualAddr::new(
|
||||
mapped_start
|
||||
.as_usize()
|
||||
.checked_sub(FRAME_SIZE)
|
||||
.ok_or(StackCreateError::AddressOverflow)?,
|
||||
);
|
||||
|
||||
if address_space.to_physical(guard_page).is_some() {
|
||||
return Err(StackCreateError::GuardPageMapped);
|
||||
}
|
||||
|
||||
let mut mapped_pages = 0;
|
||||
|
||||
while mapped_pages < STACK_PAGES {
|
||||
let virtual_address = VirtualAddr::new(
|
||||
mapped_start
|
||||
.as_usize()
|
||||
.checked_add(mapped_pages * FRAME_SIZE)
|
||||
.ok_or(StackCreateError::AddressOverflow)?,
|
||||
);
|
||||
let frame = match allocator.alloc() {
|
||||
Some(frame) => frame,
|
||||
None => {
|
||||
Self::rollback(address_space, allocator, mapped_start, mapped_pages);
|
||||
return Err(StackCreateError::OutOfFrames);
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(error) = address_space.map(
|
||||
frame.frame_address().start_address(),
|
||||
virtual_address,
|
||||
permissions,
|
||||
allocator,
|
||||
CachePolicy::WriteBack,
|
||||
) {
|
||||
unsafe { allocator.dealloc(frame) };
|
||||
Self::rollback(address_space, allocator, mapped_start, mapped_pages);
|
||||
return Err(StackCreateError::Map(error));
|
||||
}
|
||||
|
||||
let _ = frame.into_raw();
|
||||
mapped_pages += 1;
|
||||
}
|
||||
|
||||
debug_assert!(address_space.to_physical(guard_page).is_none());
|
||||
|
||||
Ok(Self {
|
||||
guard_page,
|
||||
mapped_start,
|
||||
top,
|
||||
})
|
||||
}
|
||||
|
||||
fn rollback(
|
||||
address_space: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
mapped_start: VirtualAddr,
|
||||
mapped_pages: usize,
|
||||
) {
|
||||
for page in (0..mapped_pages).rev() {
|
||||
let virtual_address = VirtualAddr::new(mapped_start.as_usize() + page * FRAME_SIZE);
|
||||
let frame = unsafe {
|
||||
address_space
|
||||
.unmap(virtual_address, allocator)
|
||||
.expect("failed to roll back stack mapping")
|
||||
};
|
||||
unsafe { allocator.dealloc(OwnedFrame::from_raw(frame)) };
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure this stack is not active on any CPU and cannot be accessed by any
|
||||
/// kernel operation while it is being destroyed.
|
||||
unsafe fn destroy(self, address_space: &mut AddressSpace, allocator: &mut FrameAllocator) {
|
||||
for page in (0..STACK_PAGES).rev() {
|
||||
let virtual_address =
|
||||
VirtualAddr::new(self.mapped_start.as_usize() + page * FRAME_SIZE);
|
||||
let frame = unsafe {
|
||||
address_space
|
||||
.unmap(virtual_address, allocator)
|
||||
.expect("stack mapping was unexpectedly missing")
|
||||
};
|
||||
unsafe { allocator.dealloc(OwnedFrame::from_raw(frame)) };
|
||||
}
|
||||
|
||||
debug_assert!(address_space.to_physical(self.guard_page).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KernelStack {
|
||||
mapping: StackMapping,
|
||||
}
|
||||
|
||||
impl KernelStack {
|
||||
pub fn allocate(
|
||||
address_space: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
) -> Result<Self, StackCreateError> {
|
||||
let mapping = StackMapping::allocate(
|
||||
address_space,
|
||||
allocator,
|
||||
KERNEL_STACK_TOP,
|
||||
PagePermissions::new(true, false, false),
|
||||
)?;
|
||||
|
||||
Ok(Self { mapping })
|
||||
}
|
||||
|
||||
pub const fn top(&self) -> VirtualAddr {
|
||||
self.mapping.top
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure this stack is not active on any CPU and cannot be accessed by any
|
||||
/// kernel operation while it is being destroyed.
|
||||
pub unsafe fn destroy(self, address_space: &mut AddressSpace, allocator: &mut FrameAllocator) {
|
||||
unsafe { self.mapping.destroy(address_space, allocator) };
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UserStack {
|
||||
mapping: StackMapping,
|
||||
}
|
||||
|
||||
impl UserStack {
|
||||
pub fn allocate(
|
||||
address_space: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
) -> Result<Self, StackCreateError> {
|
||||
let top = VirtualAddr::new(USER_STACK_TOP.as_usize());
|
||||
let mapping = StackMapping::allocate(
|
||||
address_space,
|
||||
allocator,
|
||||
top,
|
||||
PagePermissions::new(true, false, true),
|
||||
)?;
|
||||
|
||||
Ok(Self { mapping })
|
||||
}
|
||||
|
||||
pub const fn top(&self) -> VirtualAddr {
|
||||
self.mapping.top
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure this stack is not active in any thread and cannot be accessed while
|
||||
/// it is being destroyed.
|
||||
pub unsafe fn destroy(self, address_space: &mut AddressSpace, allocator: &mut FrameAllocator) {
|
||||
unsafe { self.mapping.destroy(address_space, allocator) };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user