feat: major overhaul and cleanup of paging and mm related code

This commit is contained in:
Zoe
2026-08-21 11:18:40 -05:00
parent af24a5a783
commit afcef918a3
15 changed files with 1040 additions and 628 deletions
+5
View File
@@ -5,3 +5,8 @@ edition = "2024"
[dependencies]
limine = "0.6.5"
[[bin]]
name = "dusk"
test = false
bench = false
+4 -43
View File
@@ -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}'
+2 -1
View File
@@ -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.
+3
View File
@@ -0,0 +1,3 @@
fn main() {
println!("cargo:rerun-if-changed=src/arch/x86_64/linker.ld");
}
+3
View File
@@ -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};
+92
View File
@@ -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),
);
}
}
+17 -2
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+21 -19
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+269
View File
@@ -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
View File
@@ -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
View File
@@ -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)]