feat: major overhaul and cleanup of paging and mm related code
This commit is contained in:
@@ -5,3 +5,8 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
limine = "0.6.5"
|
||||
|
||||
[[bin]]
|
||||
name = "dusk"
|
||||
test = false
|
||||
bench = false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ARTIFACTS_PATH ?= bin
|
||||
IMAGE_NAME ?= dusk.iso
|
||||
MODE ?= release
|
||||
MODE ?= debug
|
||||
ARCH ?= x86_64
|
||||
MEMORY ?= 512M
|
||||
# In MB
|
||||
@@ -36,18 +36,9 @@ ifneq (${GDB},)
|
||||
QEMU_OPTS += -s -S
|
||||
endif
|
||||
|
||||
ifeq (${ARCH},aarch64)
|
||||
LIMINE_BOOT_VARIATION := AA64
|
||||
UEFI := true
|
||||
endif
|
||||
|
||||
ifneq (${UEFI},)
|
||||
RUN_OPTS := ovmf-${ARCH}
|
||||
ifeq (${ARCH},aarch64)
|
||||
QEMU_OPTS += -M virt -bios ovmf/ovmf-${ARCH}/OVMF.fd
|
||||
else
|
||||
QEMU_OPTS += -bios ovmf/ovmf-${ARCH}/OVMF.fd
|
||||
endif
|
||||
QEMU_OPTS += -bios ovmf/ovmf-${ARCH}/OVMF.fd
|
||||
endif
|
||||
|
||||
.PHONY: all build
|
||||
@@ -57,7 +48,7 @@ all: build
|
||||
build: prepare-bin-files compile-bootloader compile-binaries run-scripts build-iso
|
||||
|
||||
check:
|
||||
cargo check
|
||||
cargo check -Zjson-target-spec
|
||||
|
||||
prepare-bin-files:
|
||||
# Remove ISO and everything in the bin directory
|
||||
@@ -69,35 +60,11 @@ prepare-bin-files:
|
||||
mkdir -p ${ISO_PATH}
|
||||
# mkdir -p ${INITRAMFS_PATH}
|
||||
mkdir -p ${ARTIFACTS_PATH}/mnt
|
||||
|
||||
#copy-initramfs-files:
|
||||
# echo "Hello World from Initramfs" > ${INITRAMFS_PATH}/example.txt
|
||||
# echo "Second file for testing" > ${INITRAMFS_PATH}/example2.txt
|
||||
# mkdir -p ${INITRAMFS_PATH}/firstdir/seconddirbutlonger/
|
||||
# mkdir ${INITRAMFS_PATH}/mnt/
|
||||
# echo "Nexted file reads!!" > ${INITRAMFS_PATH}/firstdir/seconddirbutlonger/yeah.txt
|
||||
|
||||
#compile-initramfs: copy-initramfs-files
|
||||
# # Make squashfs without compression temporaily so I can get it working before I have to write a gzip driver
|
||||
# mksquashfs ${INITRAMFS_PATH} ${ARTIFACTS_PATH}/initramfs.img ${MKSQUASHFS_OPTS}
|
||||
|
||||
run-scripts:
|
||||
# Place the build ID into the binary so it can be read at runtime
|
||||
@HASH=$$(md5sum ${KERNEL_FILE} | cut -c1-12) && \
|
||||
sed -i "s/__BUILD_ID__/$${HASH}/" ${KERNEL_FILE}
|
||||
|
||||
#ifeq (${EXPORT_SYMBOLS},true)
|
||||
# nm ${KERNEL_FILE} > scripts/symbols.table
|
||||
# @if [ ! -d "scripts/rustc_demangle" ]; then \
|
||||
# git clone "https://github.com/juls0730/rustc_demangle.py" "scripts/rustc_demangle"; \
|
||||
# fi
|
||||
# python scripts/demangle-symbols.py
|
||||
# mv scripts/symbols.table ${INITRAMFS_PATH}/
|
||||
#endif
|
||||
|
||||
# python scripts/font.py
|
||||
# mv scripts/font.psf ${INITRAMFS_PATH}/
|
||||
|
||||
#python scripts/initramfs-test.py 100 ${INITRAMFS_PATH}/
|
||||
|
||||
copy-iso-files:
|
||||
@@ -167,12 +134,6 @@ ovmf-x86_64:
|
||||
cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \
|
||||
fi
|
||||
|
||||
ovmf-aarch64:
|
||||
mkdir -p ovmf/ovmf-aarch64
|
||||
@if [ ! -d "ovmf/ovmf-aarch64/OVMF.fd" ]; then \
|
||||
cd ovmf/ovmf-aarch64 && curl -o OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEAARCH64_QEMU_EFI.fd; \
|
||||
fi
|
||||
|
||||
# In debug mode, open a terminal and run this command:
|
||||
# gdb target/x86_64-unknown-none/debug/CappuccinOS.elf -ex "target remote :1234"
|
||||
|
||||
@@ -183,7 +144,7 @@ run-x86_64:
|
||||
tmux new-session -d -s qemu 'qemu-system-x86_64 ${QEMU_OPTS}'
|
||||
|
||||
run-x86_64-serial:
|
||||
qemu-system-x86_64 ${QEMU_OPTS} -boot d -display none -serial stdio -monitor none -no-reboot -no-shutdown
|
||||
qemu-system-x86_64 ${QEMU_OPTS} -boot d -display none -serial stdio -monitor none -no-reboot
|
||||
|
||||
line-count:
|
||||
cloc --quiet --exclude-dir=bin --include-lang=Rust --csv src/ | tail -n 1 | awk -F, '{print $$5}'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# DuskOS
|
||||
|
||||
A simple microkernel and operating system written in Rust for x86_64.
|
||||
A simple work-in-progress microkernel and operating system written in Rust for
|
||||
x86_64.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=src/arch/x86_64/linker.ld");
|
||||
}
|
||||
@@ -3,3 +3,6 @@ mod x86_64;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use x86_64::*;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub(crate) use x86_64::{PageTableCreateError, PageTableMapError, PageTableUnmapError};
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
use core::arch::asm;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CpuFeaturesError {
|
||||
CpuidFeaturesNotSupported,
|
||||
InvalidPhysicalAddressWidth,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct CpuFeatures {
|
||||
pub nx_supported: bool,
|
||||
pub nx_enabled: bool,
|
||||
pub physical_address_bits: u8,
|
||||
pub virtual_address_bits: u8,
|
||||
}
|
||||
|
||||
pub fn detect_features_and_enable() -> Result<CpuFeatures, CpuFeaturesError> {
|
||||
let mut features = CpuFeatures {
|
||||
nx_supported: false,
|
||||
nx_enabled: false,
|
||||
physical_address_bits: 0,
|
||||
virtual_address_bits: 0,
|
||||
};
|
||||
|
||||
let cpuid_result = core::arch::x86_64::__cpuid_count(0x80000000, 0);
|
||||
|
||||
if cpuid_result.eax < 0x80000008 {
|
||||
return Err(CpuFeaturesError::CpuidFeaturesNotSupported);
|
||||
}
|
||||
|
||||
let cpuid_result = core::arch::x86_64::__cpuid_count(0x80000001, 0);
|
||||
|
||||
features.nx_supported = cpuid_result.edx & (1 << 20) != 0;
|
||||
|
||||
let cpuid_result = core::arch::x86_64::__cpuid_count(0x80000008, 0);
|
||||
|
||||
features.physical_address_bits = (cpuid_result.eax & 0xFF) as u8;
|
||||
if !(12..=52).contains(&features.physical_address_bits) {
|
||||
return Err(CpuFeaturesError::InvalidPhysicalAddressWidth);
|
||||
}
|
||||
|
||||
features.virtual_address_bits = (cpuid_result.eax >> 8 & 0xFF) as u8;
|
||||
|
||||
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 {
|
||||
return Err(CpuFeaturesError::CpuidFeaturesNotSupported);
|
||||
}
|
||||
|
||||
// mother efer
|
||||
let efer = unsafe { read_msr(0xC0000080) };
|
||||
|
||||
unsafe {
|
||||
write_msr(0xC0000080, efer | (1 << 11));
|
||||
}
|
||||
|
||||
features.nx_enabled = unsafe { read_msr(0xC0000080) } & (1 << 11) != 0;
|
||||
}
|
||||
|
||||
Ok(features)
|
||||
}
|
||||
|
||||
unsafe fn read_msr(msr: u32) -> u64 {
|
||||
let low: u32;
|
||||
let high: u32;
|
||||
|
||||
unsafe {
|
||||
asm!(
|
||||
"rdmsr",
|
||||
in("ecx") msr,
|
||||
out("eax") low,
|
||||
out("edx") high,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
}
|
||||
|
||||
((high as u64) << 32) | low as u64
|
||||
}
|
||||
|
||||
unsafe fn write_msr(msr: u32, value: u64) {
|
||||
unsafe {
|
||||
asm!(
|
||||
"wrmsr",
|
||||
in("ecx") msr,
|
||||
in("eax") value as u32,
|
||||
in("edx") (value >> 32) as u32,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,10 @@ SECTIONS
|
||||
/* that is the beginning of the region. */
|
||||
/* Additionally, leave space for the ELF headers by adding SIZEOF_HEADERS to the */
|
||||
/* base load address. */
|
||||
. = 0xffffffff80000000 + SIZEOF_HEADERS;
|
||||
. = 0xffffffff80000000;
|
||||
|
||||
__text_start = .;
|
||||
. += SIZEOF_HEADERS;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
@@ -33,6 +36,8 @@ SECTIONS
|
||||
|
||||
/* Move to the next memory page for .rodata */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
__text_end = .;
|
||||
__rodata_start = .;
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
@@ -40,7 +45,9 @@ SECTIONS
|
||||
|
||||
/* Move to the next memory page for .data */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
__rodata_end = .;
|
||||
__data_start = .;
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
|
||||
@@ -56,6 +63,11 @@ SECTIONS
|
||||
*(.dynamic)
|
||||
} :data :dynamic
|
||||
|
||||
.got : {
|
||||
*(.got .got.*)
|
||||
*(.got.plt .got.plt.*)
|
||||
} :data
|
||||
|
||||
/* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
|
||||
/* unnecessary zeros will be written to the binary. */
|
||||
/* If you need, for example, .init_array and .fini_array, those should be placed */
|
||||
@@ -65,6 +77,9 @@ SECTIONS
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
__data_end = .;
|
||||
|
||||
/* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
|
||||
/* Also discard the program interpreter section since we do not need one. This is */
|
||||
/* more or less equivalent to the --no-dynamic-linker linker flag, except that it */
|
||||
|
||||
+16
-2
@@ -1,20 +1,34 @@
|
||||
mod cpu;
|
||||
mod gdt;
|
||||
mod interrupts;
|
||||
pub mod paging;
|
||||
mod paging;
|
||||
pub mod port;
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
pub use interrupts::disable_interrupts;
|
||||
pub(crate) use paging::{
|
||||
MapError as PageTableMapError, PageTableCreateError, UnmapError as PageTableUnmapError,
|
||||
};
|
||||
pub use paging::{PageTable, PagingConfig};
|
||||
|
||||
pub struct ArchState {
|
||||
pub paging: PagingConfig,
|
||||
}
|
||||
|
||||
use crate::println;
|
||||
|
||||
pub fn init() {
|
||||
pub fn init() -> ArchState {
|
||||
disable_interrupts();
|
||||
println!("Loading GDT...");
|
||||
gdt::init();
|
||||
println!("Loading IDT...");
|
||||
interrupts::init();
|
||||
println!("Detecting CPU features...");
|
||||
let cpu_features = cpu::detect_features_and_enable();
|
||||
let paging =
|
||||
PagingConfig::from_features(cpu_features.expect("required CPU features are not supported"));
|
||||
ArchState { paging }
|
||||
}
|
||||
|
||||
pub fn halt() {
|
||||
|
||||
+484
-529
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,22 @@
|
||||
{
|
||||
"arch": "x86_64",
|
||||
"cpu": "x86-64",
|
||||
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
||||
"llvm-target": "x86_64-unknown-none",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": 64,
|
||||
"target-c-int-width": 32,
|
||||
"features": "-mmx,-sse,+soft-float",
|
||||
"rustc-abi": "softfloat",
|
||||
"os": "DawnOS",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"pre-link-args": {
|
||||
"ld.lld": ["-melf_x86_64", "--script=./src/arch/x86_64/linker.ld"]
|
||||
},
|
||||
"panic-strategy": "abort",
|
||||
"exe-suffix": ".elf",
|
||||
"disable-redzone": true
|
||||
}
|
||||
"arch": "x86_64",
|
||||
"cpu": "x86-64",
|
||||
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
||||
"llvm-target": "x86_64-unknown-none",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": 64,
|
||||
"target-c-int-width": 32,
|
||||
"features": "-mmx,-sse,+soft-float",
|
||||
"rustc-abi": "softfloat",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [
|
||||
"-melf_x86_64",
|
||||
"--script=./src/arch/x86_64/linker.ld"
|
||||
]
|
||||
},
|
||||
"panic-strategy": "abort",
|
||||
"exe-suffix": ".elf",
|
||||
"disable-redzone": true
|
||||
}
|
||||
+79
-18
@@ -3,7 +3,10 @@ use ::limine as limine_api;
|
||||
use limine_api::request::{ExecutableAddressRequest, HhdmRequest, MemmapRequest};
|
||||
use limine_api::{BaseRevision, RequestsEndMarker, RequestsStartMarker};
|
||||
|
||||
use crate::memory::{KernelImage, PhysicalAddr, VirtualAddr};
|
||||
use crate::memory::{
|
||||
KernelMemoryLayout, KernelSegment, PagePermissions, PhysicalAddr, VirtualAddr,
|
||||
};
|
||||
use crate::println;
|
||||
|
||||
/// Sets the base revision to the latest revision supported by the crate.
|
||||
/// See specification for further info.
|
||||
@@ -33,8 +36,19 @@ static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new();
|
||||
#[unsafe(link_section = ".requests_end_marker")]
|
||||
static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
|
||||
|
||||
unsafe extern "C" {
|
||||
static __text_start: u64;
|
||||
static __text_end: u64;
|
||||
|
||||
static __rodata_start: u64;
|
||||
static __rodata_end: u64;
|
||||
|
||||
static __data_start: u64;
|
||||
static __data_end: u64;
|
||||
}
|
||||
|
||||
pub struct BootInfo {
|
||||
pub kernel_address: KernelImage,
|
||||
pub kernel_layout: KernelMemoryLayout,
|
||||
pub hhdm_offset: usize,
|
||||
entries: &'static [&'static limine_api::memmap::Entry],
|
||||
}
|
||||
@@ -92,27 +106,74 @@ pub fn load_boot_info() -> Result<BootInfo, BootError> {
|
||||
.ok_or(BootError::FailedToGetMemmap)?
|
||||
.entries();
|
||||
|
||||
let mut kernel_length = None;
|
||||
for &entry in memmap.iter() {
|
||||
if entry.type_ != limine_api::memmap::MEMMAP_EXECUTABLE_AND_MODULES {
|
||||
continue;
|
||||
}
|
||||
let mut segment_physical = kernel_address.physical_base as usize;
|
||||
let mut segment_virtual = core::ptr::addr_of!(__text_start) as usize;
|
||||
|
||||
if entry.base != kernel_address.physical_base {
|
||||
continue;
|
||||
}
|
||||
let mut segment_length = 0;
|
||||
|
||||
kernel_length = Some(entry.length as usize);
|
||||
break;
|
||||
let kernel_text_segment = KernelSegment {
|
||||
physical_base: PhysicalAddr::new(segment_physical),
|
||||
virtual_base: VirtualAddr::new(segment_virtual),
|
||||
length: (core::ptr::addr_of!(__text_end) as usize)
|
||||
- (core::ptr::addr_of!(__text_start) as usize),
|
||||
permissions: PagePermissions::new(false, true, false),
|
||||
};
|
||||
|
||||
segment_length += kernel_text_segment.length;
|
||||
|
||||
segment_virtual = core::ptr::addr_of!(__rodata_start) as usize;
|
||||
segment_physical = kernel_address.physical_base as usize
|
||||
+ (segment_virtual - kernel_address.virtual_base as usize);
|
||||
|
||||
let kernel_rodata_segment = KernelSegment {
|
||||
physical_base: PhysicalAddr::new(segment_physical),
|
||||
virtual_base: VirtualAddr::new(segment_virtual),
|
||||
length: (core::ptr::addr_of!(__rodata_end) as usize)
|
||||
- (core::ptr::addr_of!(__rodata_start) as usize),
|
||||
permissions: PagePermissions::new(false, false, false),
|
||||
};
|
||||
|
||||
segment_length += kernel_rodata_segment.length;
|
||||
|
||||
segment_virtual = core::ptr::addr_of!(__data_start) as usize;
|
||||
segment_physical = kernel_address.physical_base as usize
|
||||
+ (segment_virtual - kernel_address.virtual_base as usize);
|
||||
|
||||
let kernel_data_segment = KernelSegment {
|
||||
physical_base: PhysicalAddr::new(segment_physical),
|
||||
virtual_base: VirtualAddr::new(segment_virtual),
|
||||
length: (core::ptr::addr_of!(__data_end) as usize)
|
||||
- (core::ptr::addr_of!(__data_start) as usize),
|
||||
permissions: PagePermissions::new(true, false, false),
|
||||
};
|
||||
|
||||
segment_length += kernel_data_segment.length;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let mut kernel_length = None;
|
||||
for &entry in memmap.iter() {
|
||||
if entry.type_ != limine_api::memmap::MEMMAP_EXECUTABLE_AND_MODULES {
|
||||
continue;
|
||||
}
|
||||
|
||||
if entry.base != kernel_address.physical_base {
|
||||
continue;
|
||||
}
|
||||
|
||||
kernel_length = Some(entry.length as usize);
|
||||
break;
|
||||
}
|
||||
debug_assert_eq!(segment_length, kernel_length.unwrap());
|
||||
}
|
||||
|
||||
let kernel_length = kernel_length.ok_or(BootError::FailedToLocateKernel)?;
|
||||
|
||||
Ok(BootInfo {
|
||||
kernel_address: KernelImage {
|
||||
physical_base: PhysicalAddr::new(kernel_address.physical_base as usize),
|
||||
virtual_base: VirtualAddr::new(kernel_address.virtual_base as usize),
|
||||
length: kernel_length,
|
||||
kernel_layout: KernelMemoryLayout {
|
||||
segments: [
|
||||
kernel_text_segment,
|
||||
kernel_rodata_segment,
|
||||
kernel_data_segment,
|
||||
],
|
||||
},
|
||||
hhdm_offset: hhdm_offset as usize,
|
||||
entries: memmap,
|
||||
|
||||
+18
-11
@@ -9,33 +9,38 @@ mod debug;
|
||||
mod memory;
|
||||
|
||||
use crate::{
|
||||
arch::paging,
|
||||
debug::serial,
|
||||
memory::{PhysicalAddr, VirtualAddr},
|
||||
memory::{AddressSpace, PagePermissions, PhysicalAddr, VirtualAddr},
|
||||
};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
serial::init().unwrap();
|
||||
|
||||
arch::init();
|
||||
let arch_state = arch::init();
|
||||
let boot_info = boot::load_boot_info().unwrap();
|
||||
let direct_map = memory::DirectMap::new(boot_info.hhdm_offset);
|
||||
|
||||
let mut allocator = memory::FrameAllocator::new(boot_info.memory_regions(), direct_map)
|
||||
.expect("failed to create frame allocator");
|
||||
|
||||
let mut page_table = paging::AddressSpace::new(
|
||||
println!("Initializing page table...");
|
||||
let mut page_table = AddressSpace::new_kernel(
|
||||
direct_map,
|
||||
boot_info.memory_regions(),
|
||||
boot_info.kernel_address,
|
||||
&boot_info.kernel_layout,
|
||||
arch_state.paging,
|
||||
&mut allocator,
|
||||
)
|
||||
.expect("failed to create page table");
|
||||
|
||||
println!("Activating page table...");
|
||||
|
||||
// safety: trust me bro
|
||||
unsafe { page_table.activate() };
|
||||
|
||||
println!("Allocating a frame...");
|
||||
|
||||
let frame = allocator.alloc().unwrap();
|
||||
|
||||
let direct_mapped = direct_map.translate(frame.start_address()).unwrap();
|
||||
@@ -47,13 +52,15 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
assert!(page_table.translate(new_virtual).is_none());
|
||||
|
||||
let page = paging::Page::from_start_address(new_virtual).unwrap();
|
||||
|
||||
page_table
|
||||
.map(
|
||||
page,
|
||||
frame,
|
||||
paging::PagePermissions::KERNEL_DATA,
|
||||
frame.start_address(),
|
||||
new_virtual,
|
||||
PagePermissions {
|
||||
writable: true,
|
||||
executable: false,
|
||||
user_accessible: true,
|
||||
},
|
||||
&mut allocator,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -79,7 +86,7 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
println!("{:#X}", slice[0]);
|
||||
|
||||
let unmapped_frame = page_table.unmap(page, &mut allocator).unwrap();
|
||||
let unmapped_frame = unsafe { page_table.unmap(new_virtual, &mut allocator) }.unwrap();
|
||||
|
||||
assert_eq!(unmapped_frame, frame);
|
||||
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
use crate::{
|
||||
arch::{PageTable, PageTableCreateError, PageTableMapError, PageTableUnmapError, PagingConfig},
|
||||
memory::{
|
||||
DirectMap, FRAME_SIZE, FrameAllocator, KernelMemoryLayout, MemoryRegion, MemoryRegionKind,
|
||||
PagePermissions, PhysicalAddr, PhysicalFrame, VirtualAddr,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MapError {
|
||||
InvalidVirtualAddress,
|
||||
VirtualAddressUnaligned,
|
||||
PhysicalAddressTooLarge,
|
||||
PhysicalAddressUnaligned,
|
||||
RangeLengthUnaligned,
|
||||
AddressOverflow,
|
||||
AlreadyMapped,
|
||||
MappingConflict,
|
||||
UnsupportedPermissions,
|
||||
OutsideAddressSpace,
|
||||
OutOfMemory,
|
||||
PageTableUnavailable,
|
||||
CorruptedPageTable,
|
||||
}
|
||||
|
||||
impl From<PageTableMapError> for MapError {
|
||||
fn from(error: PageTableMapError) -> Self {
|
||||
match error {
|
||||
PageTableMapError::InvalidVirtualAddress => Self::InvalidVirtualAddress,
|
||||
PageTableMapError::VirtualAddressUnaligned => Self::VirtualAddressUnaligned,
|
||||
PageTableMapError::PhysicalAddressTooLarge => Self::PhysicalAddressTooLarge,
|
||||
PageTableMapError::PageAlreadyMapped => Self::AlreadyMapped,
|
||||
PageTableMapError::HugePageConflict => Self::MappingConflict,
|
||||
PageTableMapError::NoExecuteUnsupported => Self::UnsupportedPermissions,
|
||||
PageTableMapError::OutOfFrames => Self::OutOfMemory,
|
||||
PageTableMapError::PageTableOutsideDirectMap => Self::PageTableUnavailable,
|
||||
PageTableMapError::InvalidPageTableEntry => Self::CorruptedPageTable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UnmapError {
|
||||
InvalidVirtualAddress,
|
||||
VirtualAddressUnaligned,
|
||||
NotMapped,
|
||||
MappingConflict,
|
||||
OutsideAddressSpace,
|
||||
PageTableUnavailable,
|
||||
CorruptedPageTable,
|
||||
}
|
||||
|
||||
impl From<PageTableUnmapError> for UnmapError {
|
||||
fn from(error: PageTableUnmapError) -> Self {
|
||||
match error {
|
||||
PageTableUnmapError::InvalidVirtualAddress => Self::InvalidVirtualAddress,
|
||||
PageTableUnmapError::VirtualAddressUnaligned => Self::VirtualAddressUnaligned,
|
||||
PageTableUnmapError::PageNotMapped => Self::NotMapped,
|
||||
PageTableUnmapError::HugePageConflict => Self::MappingConflict,
|
||||
PageTableUnmapError::PageTableOutsideDirectMap => Self::PageTableUnavailable,
|
||||
PageTableUnmapError::InvalidPageTableEntry => Self::CorruptedPageTable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AddressSpaceCreateError {
|
||||
AddressOutsideDirectMap,
|
||||
PhysicalAddressTooLarge,
|
||||
OutOfMemory,
|
||||
Map(MapError),
|
||||
}
|
||||
|
||||
impl From<PageTableCreateError> for AddressSpaceCreateError {
|
||||
fn from(error: PageTableCreateError) -> Self {
|
||||
match error {
|
||||
PageTableCreateError::PhysicalAddressTooLarge => Self::PhysicalAddressTooLarge,
|
||||
PageTableCreateError::OutOfFrames => Self::OutOfMemory,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AddressSpaceKind {
|
||||
Kernel,
|
||||
User,
|
||||
}
|
||||
|
||||
pub struct AddressSpace {
|
||||
root: PageTable,
|
||||
kind: AddressSpaceKind,
|
||||
}
|
||||
|
||||
impl AddressSpace {
|
||||
pub fn new_kernel<I: Iterator<Item = MemoryRegion> + Clone>(
|
||||
direct_map: DirectMap,
|
||||
memory_regions: I,
|
||||
layout: &KernelMemoryLayout,
|
||||
paging_config: PagingConfig,
|
||||
allocator: &mut FrameAllocator,
|
||||
) -> Result<Self, AddressSpaceCreateError> {
|
||||
let mut space = AddressSpace {
|
||||
root: PageTable::new(direct_map, paging_config, allocator)?,
|
||||
kind: AddressSpaceKind::Kernel,
|
||||
};
|
||||
|
||||
// map hhdm
|
||||
for region in memory_regions.clone() {
|
||||
if region.kind == MemoryRegionKind::Reserved
|
||||
|| region.kind == MemoryRegionKind::BadMemory
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let res = space.map_range(
|
||||
region.start,
|
||||
direct_map
|
||||
.translate(region.start)
|
||||
.ok_or(AddressSpaceCreateError::AddressOutsideDirectMap)?,
|
||||
region.length,
|
||||
PagePermissions {
|
||||
writable: true,
|
||||
executable: false,
|
||||
user_accessible: false,
|
||||
},
|
||||
allocator,
|
||||
);
|
||||
|
||||
if let Err(err) = res {
|
||||
space.destroy(allocator);
|
||||
return Err(AddressSpaceCreateError::Map(err));
|
||||
}
|
||||
}
|
||||
|
||||
let mut old_segment_stop: Option<usize> = None;
|
||||
|
||||
for segment in layout.segments.iter() {
|
||||
if let Some(stop) = old_segment_stop {
|
||||
debug_assert_eq!(segment.physical_base.as_usize(), stop);
|
||||
}
|
||||
|
||||
old_segment_stop = Some(segment.physical_base.as_usize() + segment.length);
|
||||
|
||||
let res = space.map_range(
|
||||
segment.physical_base,
|
||||
segment.virtual_base,
|
||||
segment.length,
|
||||
segment.permissions,
|
||||
allocator,
|
||||
);
|
||||
|
||||
if let Err(err) = res {
|
||||
space.destroy(allocator);
|
||||
return Err(AddressSpaceCreateError::Map(err));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(space)
|
||||
}
|
||||
|
||||
pub fn new_user(kernel_space: &AddressSpace, allocator: &mut FrameAllocator) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn map(
|
||||
&mut self,
|
||||
physical_addr: PhysicalAddr,
|
||||
virtual_addr: VirtualAddr,
|
||||
permissions: PagePermissions,
|
||||
allocator: &mut FrameAllocator,
|
||||
) -> Result<(), MapError> {
|
||||
let frame = PhysicalFrame::from_start_address(physical_addr)
|
||||
.ok_or(MapError::PhysicalAddressUnaligned)?;
|
||||
|
||||
self.root
|
||||
.map(virtual_addr, frame, permissions, allocator)
|
||||
.map_err(MapError::from)
|
||||
}
|
||||
|
||||
pub fn map_range(
|
||||
&mut self,
|
||||
physical_start: PhysicalAddr,
|
||||
virtual_start: VirtualAddr,
|
||||
length: usize,
|
||||
permissions: PagePermissions,
|
||||
allocator: &mut FrameAllocator,
|
||||
) -> Result<(), MapError> {
|
||||
if length == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if physical_start.as_usize() % FRAME_SIZE != 0 {
|
||||
return Err(MapError::PhysicalAddressUnaligned);
|
||||
}
|
||||
|
||||
if virtual_start.as_usize() % FRAME_SIZE != 0 {
|
||||
return Err(MapError::VirtualAddressUnaligned);
|
||||
}
|
||||
|
||||
if length % FRAME_SIZE != 0 {
|
||||
return Err(MapError::RangeLengthUnaligned);
|
||||
}
|
||||
|
||||
let last_offset = length - FRAME_SIZE;
|
||||
|
||||
physical_start
|
||||
.as_usize()
|
||||
.checked_add(last_offset)
|
||||
.ok_or(MapError::AddressOverflow)?;
|
||||
virtual_start
|
||||
.as_usize()
|
||||
.checked_add(last_offset)
|
||||
.ok_or(MapError::AddressOverflow)?;
|
||||
|
||||
let page_count = length / FRAME_SIZE;
|
||||
let mut mapped_pages = 0;
|
||||
|
||||
while mapped_pages < page_count {
|
||||
let offset = mapped_pages * FRAME_SIZE;
|
||||
let physical_addr = PhysicalAddr::new(physical_start.as_usize() + offset);
|
||||
let virtual_addr = VirtualAddr::new(virtual_start.as_usize() + offset);
|
||||
|
||||
if let Err(err) = self.map(physical_addr, virtual_addr, permissions, allocator) {
|
||||
for rollback_idx in (0..mapped_pages).rev() {
|
||||
let rollback_offset = rollback_idx * FRAME_SIZE;
|
||||
let rollback_physical_addr =
|
||||
PhysicalAddr::new(physical_start.as_usize() + rollback_offset);
|
||||
|
||||
unsafe {
|
||||
self.unmap(
|
||||
VirtualAddr::new(virtual_start.as_usize() + rollback_offset),
|
||||
allocator,
|
||||
)
|
||||
.expect("failed to roll back a mapped page");
|
||||
}
|
||||
}
|
||||
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
mapped_pages += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure:
|
||||
/// - The caller must ensure that the page is not currently in use
|
||||
pub unsafe fn unmap(
|
||||
&mut self,
|
||||
virtual_addr: VirtualAddr,
|
||||
allocator: &mut FrameAllocator,
|
||||
) -> Result<PhysicalFrame, UnmapError> {
|
||||
unsafe { self.root.unmap(virtual_addr, allocator) }.map_err(UnmapError::from)
|
||||
}
|
||||
|
||||
pub fn translate(&self, virtual_addr: VirtualAddr) -> Option<PhysicalAddr> {
|
||||
self.root.translate(virtual_addr)
|
||||
}
|
||||
|
||||
pub unsafe fn activate(&self) {
|
||||
unsafe { self.root.activate() }
|
||||
}
|
||||
|
||||
pub fn destroy(self, allocator: &mut FrameAllocator) {
|
||||
todo!("destroy address space")
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -19,6 +19,7 @@ enum FrameState {
|
||||
Allocated = 0b10,
|
||||
}
|
||||
|
||||
// 64 KiB per GiB
|
||||
#[derive(Debug)]
|
||||
struct Bitmap {
|
||||
start: VirtualAddr,
|
||||
@@ -68,7 +69,7 @@ pub enum FrameAllocatorInitError {
|
||||
BitmapOutsideDirectMap,
|
||||
}
|
||||
|
||||
// very very simple linked list frame/page allocator
|
||||
// very very simple bitmap frame/page allocator
|
||||
#[derive(Debug)]
|
||||
pub struct FrameAllocator {
|
||||
bitmap: Bitmap,
|
||||
|
||||
+25
-2
@@ -1,12 +1,35 @@
|
||||
mod address_space;
|
||||
mod frame;
|
||||
|
||||
pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError};
|
||||
pub use frame::{FRAME_SIZE, FrameAllocator, PhysicalFrame};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct KernelImage {
|
||||
pub struct KernelSegment {
|
||||
pub physical_base: PhysicalAddr,
|
||||
pub virtual_base: VirtualAddr,
|
||||
pub length: usize,
|
||||
pub permissions: PagePermissions,
|
||||
}
|
||||
|
||||
pub struct KernelMemoryLayout {
|
||||
pub segments: [KernelSegment; 3],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct PagePermissions {
|
||||
pub writable: bool,
|
||||
pub executable: bool,
|
||||
pub user_accessible: bool,
|
||||
}
|
||||
|
||||
impl PagePermissions {
|
||||
pub const fn new(writable: bool, executable: bool, user_accessible: bool) -> Self {
|
||||
Self {
|
||||
writable,
|
||||
executable,
|
||||
user_accessible,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
|
||||
Reference in New Issue
Block a user