feat: major overhaul and cleanup of paging and mm related code
This commit is contained in:
@@ -5,3 +5,8 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
limine = "0.6.5"
|
limine = "0.6.5"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "dusk"
|
||||||
|
test = false
|
||||||
|
bench = false
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
ARTIFACTS_PATH ?= bin
|
ARTIFACTS_PATH ?= bin
|
||||||
IMAGE_NAME ?= dusk.iso
|
IMAGE_NAME ?= dusk.iso
|
||||||
MODE ?= release
|
MODE ?= debug
|
||||||
ARCH ?= x86_64
|
ARCH ?= x86_64
|
||||||
MEMORY ?= 512M
|
MEMORY ?= 512M
|
||||||
# In MB
|
# In MB
|
||||||
@@ -36,18 +36,9 @@ ifneq (${GDB},)
|
|||||||
QEMU_OPTS += -s -S
|
QEMU_OPTS += -s -S
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq (${ARCH},aarch64)
|
|
||||||
LIMINE_BOOT_VARIATION := AA64
|
|
||||||
UEFI := true
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq (${UEFI},)
|
ifneq (${UEFI},)
|
||||||
RUN_OPTS := ovmf-${ARCH}
|
RUN_OPTS := ovmf-${ARCH}
|
||||||
ifeq (${ARCH},aarch64)
|
QEMU_OPTS += -bios ovmf/ovmf-${ARCH}/OVMF.fd
|
||||||
QEMU_OPTS += -M virt -bios ovmf/ovmf-${ARCH}/OVMF.fd
|
|
||||||
else
|
|
||||||
QEMU_OPTS += -bios ovmf/ovmf-${ARCH}/OVMF.fd
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: all build
|
.PHONY: all build
|
||||||
@@ -57,7 +48,7 @@ all: build
|
|||||||
build: prepare-bin-files compile-bootloader compile-binaries run-scripts build-iso
|
build: prepare-bin-files compile-bootloader compile-binaries run-scripts build-iso
|
||||||
|
|
||||||
check:
|
check:
|
||||||
cargo check
|
cargo check -Zjson-target-spec
|
||||||
|
|
||||||
prepare-bin-files:
|
prepare-bin-files:
|
||||||
# Remove ISO and everything in the bin directory
|
# Remove ISO and everything in the bin directory
|
||||||
@@ -69,35 +60,11 @@ prepare-bin-files:
|
|||||||
mkdir -p ${ISO_PATH}
|
mkdir -p ${ISO_PATH}
|
||||||
# mkdir -p ${INITRAMFS_PATH}
|
# mkdir -p ${INITRAMFS_PATH}
|
||||||
mkdir -p ${ARTIFACTS_PATH}/mnt
|
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:
|
run-scripts:
|
||||||
# Place the build ID into the binary so it can be read at runtime
|
# Place the build ID into the binary so it can be read at runtime
|
||||||
@HASH=$$(md5sum ${KERNEL_FILE} | cut -c1-12) && \
|
@HASH=$$(md5sum ${KERNEL_FILE} | cut -c1-12) && \
|
||||||
sed -i "s/__BUILD_ID__/$${HASH}/" ${KERNEL_FILE}
|
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}/
|
#python scripts/initramfs-test.py 100 ${INITRAMFS_PATH}/
|
||||||
|
|
||||||
copy-iso-files:
|
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; \
|
cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \
|
||||||
fi
|
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:
|
# In debug mode, open a terminal and run this command:
|
||||||
# gdb target/x86_64-unknown-none/debug/CappuccinOS.elf -ex "target remote :1234"
|
# 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}'
|
tmux new-session -d -s qemu 'qemu-system-x86_64 ${QEMU_OPTS}'
|
||||||
|
|
||||||
run-x86_64-serial:
|
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:
|
line-count:
|
||||||
cloc --quiet --exclude-dir=bin --include-lang=Rust --csv src/ | tail -n 1 | awk -F, '{print $$5}'
|
cloc --quiet --exclude-dir=bin --include-lang=Rust --csv src/ | tail -n 1 | awk -F, '{print $$5}'
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
# DuskOS
|
# 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")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use 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. */
|
/* that is the beginning of the region. */
|
||||||
/* Additionally, leave space for the ELF headers by adding SIZEOF_HEADERS to the */
|
/* Additionally, leave space for the ELF headers by adding SIZEOF_HEADERS to the */
|
||||||
/* base load address. */
|
/* base load address. */
|
||||||
. = 0xffffffff80000000 + SIZEOF_HEADERS;
|
. = 0xffffffff80000000;
|
||||||
|
|
||||||
|
__text_start = .;
|
||||||
|
. += SIZEOF_HEADERS;
|
||||||
|
|
||||||
.text : {
|
.text : {
|
||||||
*(.text .text.*)
|
*(.text .text.*)
|
||||||
@@ -33,6 +36,8 @@ SECTIONS
|
|||||||
|
|
||||||
/* Move to the next memory page for .rodata */
|
/* Move to the next memory page for .rodata */
|
||||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||||
|
__text_end = .;
|
||||||
|
__rodata_start = .;
|
||||||
|
|
||||||
.rodata : {
|
.rodata : {
|
||||||
*(.rodata .rodata.*)
|
*(.rodata .rodata.*)
|
||||||
@@ -40,7 +45,9 @@ SECTIONS
|
|||||||
|
|
||||||
/* Move to the next memory page for .data */
|
/* Move to the next memory page for .data */
|
||||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||||
|
__rodata_end = .;
|
||||||
|
__data_start = .;
|
||||||
|
|
||||||
.data : {
|
.data : {
|
||||||
*(.data .data.*)
|
*(.data .data.*)
|
||||||
|
|
||||||
@@ -56,6 +63,11 @@ SECTIONS
|
|||||||
*(.dynamic)
|
*(.dynamic)
|
||||||
} :data :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 */
|
/* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
|
||||||
/* unnecessary zeros will be written to the binary. */
|
/* unnecessary zeros will be written to the binary. */
|
||||||
/* If you need, for example, .init_array and .fini_array, those should be placed */
|
/* If you need, for example, .init_array and .fini_array, those should be placed */
|
||||||
@@ -65,6 +77,9 @@ SECTIONS
|
|||||||
*(COMMON)
|
*(COMMON)
|
||||||
} :data
|
} :data
|
||||||
|
|
||||||
|
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||||
|
__data_end = .;
|
||||||
|
|
||||||
/* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
|
/* 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 */
|
/* 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 */
|
/* 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 gdt;
|
||||||
mod interrupts;
|
mod interrupts;
|
||||||
pub mod paging;
|
mod paging;
|
||||||
pub mod port;
|
pub mod port;
|
||||||
|
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
pub use interrupts::disable_interrupts;
|
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;
|
use crate::println;
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() -> ArchState {
|
||||||
disable_interrupts();
|
disable_interrupts();
|
||||||
println!("Loading GDT...");
|
println!("Loading GDT...");
|
||||||
gdt::init();
|
gdt::init();
|
||||||
println!("Loading IDT...");
|
println!("Loading IDT...");
|
||||||
interrupts::init();
|
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() {
|
pub fn halt() {
|
||||||
|
|||||||
+484
-529
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,22 @@
|
|||||||
{
|
{
|
||||||
"arch": "x86_64",
|
"arch": "x86_64",
|
||||||
"cpu": "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",
|
"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",
|
"llvm-target": "x86_64-unknown-none",
|
||||||
"target-endian": "little",
|
"target-endian": "little",
|
||||||
"target-pointer-width": 64,
|
"target-pointer-width": 64,
|
||||||
"target-c-int-width": 32,
|
"target-c-int-width": 32,
|
||||||
"features": "-mmx,-sse,+soft-float",
|
"features": "-mmx,-sse,+soft-float",
|
||||||
"rustc-abi": "softfloat",
|
"rustc-abi": "softfloat",
|
||||||
"os": "DawnOS",
|
"linker": "rust-lld",
|
||||||
"linker": "rust-lld",
|
"linker-flavor": "ld.lld",
|
||||||
"linker-flavor": "ld.lld",
|
"pre-link-args": {
|
||||||
"pre-link-args": {
|
"ld.lld": [
|
||||||
"ld.lld": ["-melf_x86_64", "--script=./src/arch/x86_64/linker.ld"]
|
"-melf_x86_64",
|
||||||
},
|
"--script=./src/arch/x86_64/linker.ld"
|
||||||
"panic-strategy": "abort",
|
]
|
||||||
"exe-suffix": ".elf",
|
},
|
||||||
"disable-redzone": true
|
"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::request::{ExecutableAddressRequest, HhdmRequest, MemmapRequest};
|
||||||
use limine_api::{BaseRevision, RequestsEndMarker, RequestsStartMarker};
|
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.
|
/// Sets the base revision to the latest revision supported by the crate.
|
||||||
/// See specification for further info.
|
/// See specification for further info.
|
||||||
@@ -33,8 +36,19 @@ static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new();
|
|||||||
#[unsafe(link_section = ".requests_end_marker")]
|
#[unsafe(link_section = ".requests_end_marker")]
|
||||||
static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
|
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 struct BootInfo {
|
||||||
pub kernel_address: KernelImage,
|
pub kernel_layout: KernelMemoryLayout,
|
||||||
pub hhdm_offset: usize,
|
pub hhdm_offset: usize,
|
||||||
entries: &'static [&'static limine_api::memmap::Entry],
|
entries: &'static [&'static limine_api::memmap::Entry],
|
||||||
}
|
}
|
||||||
@@ -92,27 +106,74 @@ pub fn load_boot_info() -> Result<BootInfo, BootError> {
|
|||||||
.ok_or(BootError::FailedToGetMemmap)?
|
.ok_or(BootError::FailedToGetMemmap)?
|
||||||
.entries();
|
.entries();
|
||||||
|
|
||||||
let mut kernel_length = None;
|
let mut segment_physical = kernel_address.physical_base as usize;
|
||||||
for &entry in memmap.iter() {
|
let mut segment_virtual = core::ptr::addr_of!(__text_start) as usize;
|
||||||
if entry.type_ != limine_api::memmap::MEMMAP_EXECUTABLE_AND_MODULES {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry.base != kernel_address.physical_base {
|
let mut segment_length = 0;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
kernel_length = Some(entry.length as usize);
|
let kernel_text_segment = KernelSegment {
|
||||||
break;
|
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 {
|
Ok(BootInfo {
|
||||||
kernel_address: KernelImage {
|
kernel_layout: KernelMemoryLayout {
|
||||||
physical_base: PhysicalAddr::new(kernel_address.physical_base as usize),
|
segments: [
|
||||||
virtual_base: VirtualAddr::new(kernel_address.virtual_base as usize),
|
kernel_text_segment,
|
||||||
length: kernel_length,
|
kernel_rodata_segment,
|
||||||
|
kernel_data_segment,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
hhdm_offset: hhdm_offset as usize,
|
hhdm_offset: hhdm_offset as usize,
|
||||||
entries: memmap,
|
entries: memmap,
|
||||||
|
|||||||
+18
-11
@@ -9,33 +9,38 @@ mod debug;
|
|||||||
mod memory;
|
mod memory;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::paging,
|
|
||||||
debug::serial,
|
debug::serial,
|
||||||
memory::{PhysicalAddr, VirtualAddr},
|
memory::{AddressSpace, PagePermissions, PhysicalAddr, VirtualAddr},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn _start() -> ! {
|
pub extern "C" fn _start() -> ! {
|
||||||
serial::init().unwrap();
|
serial::init().unwrap();
|
||||||
|
|
||||||
arch::init();
|
let arch_state = arch::init();
|
||||||
let boot_info = boot::load_boot_info().unwrap();
|
let boot_info = boot::load_boot_info().unwrap();
|
||||||
let direct_map = memory::DirectMap::new(boot_info.hhdm_offset);
|
let direct_map = memory::DirectMap::new(boot_info.hhdm_offset);
|
||||||
|
|
||||||
let mut allocator = memory::FrameAllocator::new(boot_info.memory_regions(), direct_map)
|
let mut allocator = memory::FrameAllocator::new(boot_info.memory_regions(), direct_map)
|
||||||
.expect("failed to create frame allocator");
|
.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,
|
direct_map,
|
||||||
boot_info.memory_regions(),
|
boot_info.memory_regions(),
|
||||||
boot_info.kernel_address,
|
&boot_info.kernel_layout,
|
||||||
|
arch_state.paging,
|
||||||
&mut allocator,
|
&mut allocator,
|
||||||
)
|
)
|
||||||
.expect("failed to create page table");
|
.expect("failed to create page table");
|
||||||
|
|
||||||
|
println!("Activating page table...");
|
||||||
|
|
||||||
// safety: trust me bro
|
// safety: trust me bro
|
||||||
unsafe { page_table.activate() };
|
unsafe { page_table.activate() };
|
||||||
|
|
||||||
|
println!("Allocating a frame...");
|
||||||
|
|
||||||
let frame = allocator.alloc().unwrap();
|
let frame = allocator.alloc().unwrap();
|
||||||
|
|
||||||
let direct_mapped = direct_map.translate(frame.start_address()).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());
|
assert!(page_table.translate(new_virtual).is_none());
|
||||||
|
|
||||||
let page = paging::Page::from_start_address(new_virtual).unwrap();
|
|
||||||
|
|
||||||
page_table
|
page_table
|
||||||
.map(
|
.map(
|
||||||
page,
|
frame.start_address(),
|
||||||
frame,
|
new_virtual,
|
||||||
paging::PagePermissions::KERNEL_DATA,
|
PagePermissions {
|
||||||
|
writable: true,
|
||||||
|
executable: false,
|
||||||
|
user_accessible: true,
|
||||||
|
},
|
||||||
&mut allocator,
|
&mut allocator,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -79,7 +86,7 @@ pub extern "C" fn _start() -> ! {
|
|||||||
|
|
||||||
println!("{:#X}", slice[0]);
|
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);
|
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,
|
Allocated = 0b10,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 64 KiB per GiB
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Bitmap {
|
struct Bitmap {
|
||||||
start: VirtualAddr,
|
start: VirtualAddr,
|
||||||
@@ -68,7 +69,7 @@ pub enum FrameAllocatorInitError {
|
|||||||
BitmapOutsideDirectMap,
|
BitmapOutsideDirectMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
// very very simple linked list frame/page allocator
|
// very very simple bitmap frame/page allocator
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FrameAllocator {
|
pub struct FrameAllocator {
|
||||||
bitmap: Bitmap,
|
bitmap: Bitmap,
|
||||||
|
|||||||
+25
-2
@@ -1,12 +1,35 @@
|
|||||||
|
mod address_space;
|
||||||
mod frame;
|
mod frame;
|
||||||
|
|
||||||
|
pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError};
|
||||||
pub use frame::{FRAME_SIZE, FrameAllocator, PhysicalFrame};
|
pub use frame::{FRAME_SIZE, FrameAllocator, PhysicalFrame};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
pub struct KernelSegment {
|
||||||
pub struct KernelImage {
|
|
||||||
pub physical_base: PhysicalAddr,
|
pub physical_base: PhysicalAddr,
|
||||||
pub virtual_base: VirtualAddr,
|
pub virtual_base: VirtualAddr,
|
||||||
pub length: usize,
|
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)]
|
#[repr(transparent)]
|
||||||
|
|||||||
Reference in New Issue
Block a user