Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
af24a5a783
|
|||
|
35d18495ec
|
@@ -117,26 +117,29 @@ copy-iso-files:
|
|||||||
partition-iso: copy-iso-files
|
partition-iso: copy-iso-files
|
||||||
# Make empty ISO of 64M in size
|
# Make empty ISO of 64M in size
|
||||||
dd if=/dev/zero of=${IMAGE_PATH} bs=1M count=0 seek=${ISO_SIZE}
|
dd if=/dev/zero of=${IMAGE_PATH} bs=1M count=0 seek=${ISO_SIZE}
|
||||||
|
ifneq (${UEFI},)
|
||||||
parted -s ${IMAGE_PATH} mklabel gpt
|
parted -s ${IMAGE_PATH} mklabel gpt
|
||||||
parted -s ${IMAGE_PATH} mkpart BIOSBOOT 1024s 2047s
|
|
||||||
parted -s ${IMAGE_PATH} set 1 bios_grub on
|
|
||||||
|
|
||||||
parted -s ${IMAGE_PATH} mkpart ESP fat${ESP_BITS} 2048s 262144s
|
parted -s ${IMAGE_PATH} mkpart ESP fat${ESP_BITS} 2048s 262144s
|
||||||
|
parted -s ${IMAGE_PATH} set 1 esp on
|
||||||
|
else
|
||||||
|
parted -s ${IMAGE_PATH} mklabel msdos
|
||||||
|
parted -s ${IMAGE_PATH} mkpart primary fat${ESP_BITS} 2048s 262144s
|
||||||
|
parted -s ${IMAGE_PATH} set 1 boot on
|
||||||
|
endif
|
||||||
|
|
||||||
# Make ISO with 1 partition starting at sector 2048 that is 32768 sectors, or 16MiB, in size
|
# Make ISO with 1 partition starting at sector 2048 that is 32768 sectors, or 16MiB, in size
|
||||||
# Then a second partition spanning the rest of the disk
|
# Then a second partition spanning the rest of the disk
|
||||||
parted -s ${IMAGE_PATH} mkpart primary 262145s 100%
|
parted -s ${IMAGE_PATH} mkpart primary 262145s 100%
|
||||||
parted -s ${IMAGE_PATH} set 2 esp on
|
|
||||||
|
|
||||||
build-iso: partition-iso
|
build-iso: partition-iso
|
||||||
ifeq (${ARCH},x86_64)
|
ifeq (${UEFI},)
|
||||||
# Install the Limine bootloader for bios installs
|
# install limine for legacy bios
|
||||||
./limine/limine bios-install ${IMAGE_PATH}
|
./limine/limine bios-install ${IMAGE_PATH}
|
||||||
endif
|
endif
|
||||||
|
|
||||||
sudo losetup -Pf --show ${IMAGE_PATH} > loopback_dev
|
sudo losetup -Pf --show ${IMAGE_PATH} > loopback_dev
|
||||||
sudo mkfs.fat -F ${ESP_BITS} `cat loopback_dev`p2
|
sudo mkfs.fat -F ${ESP_BITS} `cat loopback_dev`p1
|
||||||
sudo mount `cat loopback_dev`p2 ${ARTIFACTS_PATH}/mnt
|
sudo mount `cat loopback_dev`p1 ${ARTIFACTS_PATH}/mnt
|
||||||
sudo cp -r ${ISO_PATH}/* ${ARTIFACTS_PATH}/mnt
|
sudo cp -r ${ISO_PATH}/* ${ARTIFACTS_PATH}/mnt
|
||||||
sync
|
sync
|
||||||
sudo umount ${ARTIFACTS_PATH}/mnt
|
sudo umount ${ARTIFACTS_PATH}/mnt
|
||||||
@@ -158,13 +161,13 @@ compile-bootloader:
|
|||||||
compile-binaries:
|
compile-binaries:
|
||||||
cargo build ${CARGO_OPTS}
|
cargo build ${CARGO_OPTS}
|
||||||
|
|
||||||
ovmf-x86_64: ovmf
|
ovmf-x86_64:
|
||||||
mkdir -p ovmf/ovmf-x86_64
|
mkdir -p ovmf/ovmf-x86_64
|
||||||
@if [ ! -d "ovmf/ovmf-x86_64/OVMF.fd" ]; then \
|
@if [ ! -d "ovmf/ovmf-x86_64/OVMF.fd" ]; then \
|
||||||
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: ovmf
|
ovmf-aarch64:
|
||||||
mkdir -p ovmf/ovmf-aarch64
|
mkdir -p ovmf/ovmf-aarch64
|
||||||
@if [ ! -d "ovmf/ovmf-aarch64/OVMF.fd" ]; then \
|
@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; \
|
cd ovmf/ovmf-aarch64 && curl -o OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEAARCH64_QEMU_EFI.fd; \
|
||||||
@@ -174,10 +177,14 @@ ovmf-aarch64: ovmf
|
|||||||
# 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"
|
||||||
|
|
||||||
run: build ${RUN_OPTS} run-${ARCH}
|
run: build ${RUN_OPTS} run-${ARCH}
|
||||||
|
run-serial: build ${RUN_OPTS} run-${ARCH}-serial
|
||||||
|
|
||||||
run-x86_64:
|
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:
|
||||||
|
qemu-system-x86_64 ${QEMU_OPTS} -boot d -display none -serial stdio -monitor none -no-reboot -no-shutdown
|
||||||
|
|
||||||
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}'
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -50,16 +50,17 @@ pub(super) const KERNEL_DATA_SELECTOR: u16 = 2 * 8;
|
|||||||
pub(super) const TSS_SELECTOR: u16 = 3 * 8;
|
pub(super) const TSS_SELECTOR: u16 = 3 * 8;
|
||||||
|
|
||||||
#[repr(align(16))]
|
#[repr(align(16))]
|
||||||
|
#[allow(dead_code)] // field 0 is read, rust just cant tell
|
||||||
struct ExceptionStack([u8; DOUBLE_FAULT_STACK_SIZE]);
|
struct ExceptionStack([u8; DOUBLE_FAULT_STACK_SIZE]);
|
||||||
|
|
||||||
static mut GDT: Gdt = Gdt::new();
|
static mut GDT: Gdt = Gdt::new();
|
||||||
static mut TSS: TaskStateSegment = TaskStateSegment::new();
|
static mut TSS: TaskStateSegment = TaskStateSegment::new();
|
||||||
static mut DOUBLE_FAULT_STACK: ExceptionStack = ExceptionStack([0; DOUBLE_FAULT_STACK_SIZE]);
|
static mut DOUBLE_FAULT_STACK: ExceptionStack = ExceptionStack([0; DOUBLE_FAULT_STACK_SIZE]);
|
||||||
|
|
||||||
pub fn init() {
|
const _: () = assert!(core::mem::size_of::<TaskStateSegment>() == 104);
|
||||||
const _: () = assert!(core::mem::size_of::<TaskStateSegment>() == 104);
|
const _: () = assert!(core::mem::size_of::<GdtPointer>() == 10);
|
||||||
const _: () = assert!(core::mem::size_of::<GdtPointer>() == 10);
|
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let stack_bottom = core::ptr::addr_of_mut!(DOUBLE_FAULT_STACK) as u64;
|
let stack_bottom = core::ptr::addr_of_mut!(DOUBLE_FAULT_STACK) as u64;
|
||||||
let stack_top = stack_bottom + DOUBLE_FAULT_STACK_SIZE as u64;
|
let stack_top = stack_bottom + DOUBLE_FAULT_STACK_SIZE as u64;
|
||||||
|
|||||||
@@ -19,22 +19,10 @@ macro_rules! fatal_with_error_code {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "x86-interrupt" fn debug_handler(frame: InterruptStackFrame) {
|
|
||||||
fatal_exception("DEBUG EXCEPTION", &frame, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "x86-interrupt" fn non_maskable_interrupt_handler(frame: InterruptStackFrame) {
|
|
||||||
fatal_exception("NON-MASKABLE INTERRUPT", &frame, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) {
|
extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) {
|
||||||
report_exception("BREAKPOINT", &frame, None);
|
report_exception("BREAKPOINT", &frame, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "x86-interrupt" fn double_fault_handler(frame: InterruptStackFrame, error_code: u64) {
|
|
||||||
fatal_exception("DOUBLE FAULT", &frame, Some(error_code));
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "x86-interrupt" fn page_fault_handler(frame: InterruptStackFrame, error_code: u64) {
|
extern "x86-interrupt" fn page_fault_handler(frame: InterruptStackFrame, error_code: u64) {
|
||||||
report_exception("PAGE FAULT", &frame, Some(error_code));
|
report_exception("PAGE FAULT", &frame, Some(error_code));
|
||||||
println!("Faulting address: {:#X}", read_cr2());
|
println!("Faulting address: {:#X}", read_cr2());
|
||||||
@@ -43,12 +31,15 @@ extern "x86-interrupt" fn page_fault_handler(frame: InterruptStackFrame, error_c
|
|||||||
}
|
}
|
||||||
|
|
||||||
fatal_without_error_code!(divide_error_handler, "DIVIDE ERROR");
|
fatal_without_error_code!(divide_error_handler, "DIVIDE ERROR");
|
||||||
|
fatal_without_error_code!(debug_handler, "DEBUG EXCEPTION");
|
||||||
|
fatal_without_error_code!(non_maskable_interrupt_handler, "NON-MASKABLE INTERRUPT");
|
||||||
fatal_without_error_code!(invalid_opcode_handler, "INVALID OPCODE");
|
fatal_without_error_code!(invalid_opcode_handler, "INVALID OPCODE");
|
||||||
fatal_without_error_code!(device_not_available_handler, "DEVICE NOT AVAILABLE");
|
fatal_without_error_code!(device_not_available_handler, "DEVICE NOT AVAILABLE");
|
||||||
fatal_without_error_code!(x87_floating_point_handler, "X87 FLOATING-POINT EXCEPTION");
|
fatal_without_error_code!(x87_floating_point_handler, "X87 FLOATING-POINT EXCEPTION");
|
||||||
fatal_without_error_code!(machine_check_handler, "MACHINE CHECK");
|
fatal_without_error_code!(machine_check_handler, "MACHINE CHECK");
|
||||||
fatal_without_error_code!(simd_floating_point_handler, "SIMD FLOATING-POINT EXCEPTION");
|
fatal_without_error_code!(simd_floating_point_handler, "SIMD FLOATING-POINT EXCEPTION");
|
||||||
|
|
||||||
|
fatal_with_error_code!(double_fault_handler, "DOUBLE FAULT");
|
||||||
fatal_with_error_code!(invalid_tss_handler, "INVALID TSS");
|
fatal_with_error_code!(invalid_tss_handler, "INVALID TSS");
|
||||||
fatal_with_error_code!(segment_not_present_handler, "SEGMENT NOT PRESENT");
|
fatal_with_error_code!(segment_not_present_handler, "SEGMENT NOT PRESENT");
|
||||||
fatal_with_error_code!(stack_segment_fault_handler, "STACK-SEGMENT FAULT");
|
fatal_with_error_code!(stack_segment_fault_handler, "STACK-SEGMENT FAULT");
|
||||||
@@ -134,10 +125,10 @@ fn report_exception(name: &'static str, frame: &InterruptStackFrame, error_code:
|
|||||||
"kernel"
|
"kernel"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
println!("RIP: {:#X}", frame.instruction_pointer.as_u64());
|
println!("RIP: {:#X}", frame.instruction_pointer.as_usize());
|
||||||
println!("CS: {:#X}", frame.code_segment);
|
println!("CS: {:#X}", frame.code_segment);
|
||||||
println!("FLAGS: {:#X}", frame.cpu_flags);
|
println!("FLAGS: {:#X}", frame.cpu_flags);
|
||||||
println!("RSP: {:#X}", frame.stack_pointer.as_u64());
|
println!("RSP: {:#X}", frame.stack_pointer.as_usize());
|
||||||
println!("SS: {:#X}", frame.stack_segment);
|
println!("SS: {:#X}", frame.stack_segment);
|
||||||
|
|
||||||
if let Some(error_code) = error_code {
|
if let Some(error_code) = error_code {
|
||||||
|
|||||||
@@ -89,11 +89,11 @@ impl Idt {
|
|||||||
|
|
||||||
static mut IDT: Idt = Idt::new();
|
static mut IDT: Idt = Idt::new();
|
||||||
|
|
||||||
pub fn idt_init() {
|
const _: () = assert!(core::mem::size_of::<IdtEntry>() == 16);
|
||||||
const _: () = assert!(core::mem::size_of::<IdtEntry>() == 16);
|
const _: () = assert!(core::mem::size_of::<IdtPointer>() == 10);
|
||||||
const _: () = assert!(core::mem::size_of::<IdtPointer>() == 10);
|
const _: () = assert!(core::mem::size_of::<InterruptStackFrame>() == 40);
|
||||||
const _: () = assert!(core::mem::size_of::<InterruptStackFrame>() == 40);
|
|
||||||
|
|
||||||
|
pub fn idt_init() {
|
||||||
let mut idt = Idt::new();
|
let mut idt = Idt::new();
|
||||||
|
|
||||||
exceptions::install(&mut idt);
|
exceptions::install(&mut idt);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod gdt;
|
mod gdt;
|
||||||
mod interrupts;
|
mod interrupts;
|
||||||
|
pub mod paging;
|
||||||
pub mod port;
|
pub mod port;
|
||||||
|
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|||||||
@@ -0,0 +1,643 @@
|
|||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
memory::{
|
||||||
|
DirectMap, FrameAllocator, KernelImage, MemoryRegion, MemoryRegionKind, PhysicalAddr,
|
||||||
|
PhysicalFrame, VirtualAddr,
|
||||||
|
},
|
||||||
|
println,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PAGE_SIZE: usize = 4096;
|
||||||
|
pub const PAGE_TABLE_ENTRIES: usize = 512;
|
||||||
|
|
||||||
|
const ADDRESS_MASK: usize = 0x000F_FFFF_FFFF_F000;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
struct PageTableEntry(u64);
|
||||||
|
|
||||||
|
impl PageTableEntry {
|
||||||
|
const PRESENT: u64 = 1 << 0;
|
||||||
|
const WRITABLE: u64 = 1 << 1;
|
||||||
|
const USER_ACCESSIBLE: u64 = 1 << 2;
|
||||||
|
const LARGE_PAGE: u64 = 1 << 7;
|
||||||
|
const NX: u64 = 1 << 63;
|
||||||
|
|
||||||
|
// TODO: validate that page is withing the CPU's mappable address space
|
||||||
|
const fn new(physical_address: PhysicalAddr, permissions: PagePermissions) -> Self {
|
||||||
|
let mut value = physical_address.as_usize() as u64 | Self::PRESENT;
|
||||||
|
|
||||||
|
if permissions.writable {
|
||||||
|
value |= Self::WRITABLE;
|
||||||
|
}
|
||||||
|
if permissions.user_accessible {
|
||||||
|
value |= Self::USER_ACCESSIBLE;
|
||||||
|
}
|
||||||
|
if !permissions.executable {
|
||||||
|
value |= Self::NX;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_table(frame: PhysicalFrame, user_accessible: bool) -> Self {
|
||||||
|
let mut value = frame.start_address().as_usize() as u64 | Self::PRESENT | Self::WRITABLE;
|
||||||
|
if user_accessible {
|
||||||
|
value |= Self::USER_ACCESSIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn null() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn physical_address(&self) -> PhysicalAddr {
|
||||||
|
PhysicalAddr::new(self.0 as usize & ADDRESS_MASK)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_present(&self) -> bool {
|
||||||
|
self.0 & Self::PRESENT != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_user_accessible(&self) -> bool {
|
||||||
|
self.0 & Self::USER_ACCESSIBLE != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_huge(&self) -> bool {
|
||||||
|
self.0 & Self::LARGE_PAGE != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame(&self) -> Option<PhysicalFrame> {
|
||||||
|
if !self.is_present() || self.is_huge() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicalFrame::from_start_address(self.physical_address())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, align(4096))]
|
||||||
|
struct PageTable {
|
||||||
|
entries: [PageTableEntry; PAGE_TABLE_ENTRIES],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageTable {
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.entries.iter().all(|&entry| !entry.is_present())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _: () = assert!(core::mem::size_of::<PageTable>() == 4096);
|
||||||
|
|
||||||
|
fn translate_huge_page(
|
||||||
|
entry: PageTableEntry,
|
||||||
|
virtual_addr: usize,
|
||||||
|
page_size: usize,
|
||||||
|
) -> Option<PhysicalAddr> {
|
||||||
|
let physical_base = entry.physical_address().as_usize() & !(page_size - 1);
|
||||||
|
|
||||||
|
let offset = virtual_addr & (page_size - 1);
|
||||||
|
|
||||||
|
physical_base.checked_add(offset).map(PhysicalAddr::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PageError {
|
||||||
|
NotCanonical,
|
||||||
|
Unaligned,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct PagePermissions {
|
||||||
|
pub writable: bool,
|
||||||
|
pub executable: bool,
|
||||||
|
pub user_accessible: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PagePermissions {
|
||||||
|
// TODO: give more fine grained permissions
|
||||||
|
pub const HHDM: Self = Self::new(true, false, false);
|
||||||
|
pub const KERNEL: Self = Self::new(true, true, false);
|
||||||
|
pub const KERNEL_DATA: Self = Self::new(true, false, false);
|
||||||
|
|
||||||
|
pub const fn new(writable: bool, executable: bool, user_accessible: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
writable,
|
||||||
|
executable,
|
||||||
|
user_accessible,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Page {
|
||||||
|
start: VirtualAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Page {
|
||||||
|
pub fn from_start_address(start: VirtualAddr) -> Result<Self, PageError> {
|
||||||
|
if !is_canonical_48_bit(start.as_usize()) {
|
||||||
|
return Err(PageError::NotCanonical);
|
||||||
|
}
|
||||||
|
|
||||||
|
if start.as_usize() & (4096 - 1) != 0 {
|
||||||
|
return Err(PageError::Unaligned);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { start })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p4_index(addr: usize) -> usize {
|
||||||
|
(addr >> 39) & 0x1FF
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p3_index(addr: usize) -> usize {
|
||||||
|
(addr >> 30) & 0x1FF
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p2_index(addr: usize) -> usize {
|
||||||
|
(addr >> 21) & 0x1FF
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p1_index(addr: usize) -> usize {
|
||||||
|
(addr >> 12) & 0x1FF
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_offset(addr: usize) -> usize {
|
||||||
|
addr & 0xFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn read_cr3() -> PhysicalFrame {
|
||||||
|
let value: usize;
|
||||||
|
unsafe {
|
||||||
|
asm!(
|
||||||
|
"mov {}, cr3",
|
||||||
|
out(reg) value,
|
||||||
|
options(nomem, nostack, preserves_flags),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicalFrame::from_start_address(PhysicalAddr::new(value & ADDRESS_MASK))
|
||||||
|
.expect("CR3 contains an unaligned page-table address")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MapError {
|
||||||
|
MisalignedPage,
|
||||||
|
PageAlreadyMapped,
|
||||||
|
ParentIsHuge,
|
||||||
|
FrameAllocatorError,
|
||||||
|
AddressOverflow,
|
||||||
|
UserPageInKernelHalf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum UnmapError {
|
||||||
|
PageNotWithinHHDM,
|
||||||
|
PageNotPresent,
|
||||||
|
ParentIsHuge,
|
||||||
|
ParentNotPresent,
|
||||||
|
FrameAllocatorError,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageTableRoot = PhysicalFrame;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct NewTable {
|
||||||
|
parent: PhysicalFrame,
|
||||||
|
index: usize,
|
||||||
|
child: PhysicalFrame,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AddressSpaceError {
|
||||||
|
MalformedMemmap,
|
||||||
|
Map(MapError),
|
||||||
|
FrameAllocatorError,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AddressSpace {
|
||||||
|
root: PageTableRoot,
|
||||||
|
direct_map: DirectMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressSpace {
|
||||||
|
pub fn new<I: Iterator<Item = MemoryRegion> + Clone>(
|
||||||
|
direct_map: DirectMap,
|
||||||
|
memmap: I,
|
||||||
|
kernel_address: KernelImage,
|
||||||
|
allocator: &mut FrameAllocator,
|
||||||
|
) -> Result<Self, AddressSpaceError> {
|
||||||
|
let frame = allocator
|
||||||
|
.alloc()
|
||||||
|
.ok_or(AddressSpaceError::FrameAllocatorError)?;
|
||||||
|
let mut space = AddressSpace {
|
||||||
|
root: frame,
|
||||||
|
direct_map,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: cleanup page tables if any allocations fail
|
||||||
|
// map hhdm
|
||||||
|
for region in memmap.clone() {
|
||||||
|
if region.kind == MemoryRegionKind::Reserved
|
||||||
|
|| region.kind == MemoryRegionKind::BadMemory
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Mapping: {:?} at {:#X} with length {:#X}",
|
||||||
|
region.kind,
|
||||||
|
region.start.as_usize(),
|
||||||
|
region.length
|
||||||
|
);
|
||||||
|
|
||||||
|
space
|
||||||
|
.map_range(
|
||||||
|
direct_map
|
||||||
|
.translate(region.start)
|
||||||
|
.ok_or(AddressSpaceError::MalformedMemmap)?,
|
||||||
|
region.start,
|
||||||
|
region.length,
|
||||||
|
PagePermissions::HHDM,
|
||||||
|
allocator,
|
||||||
|
)
|
||||||
|
.map_err(|err| AddressSpaceError::Map(err))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// map kernel
|
||||||
|
println!(
|
||||||
|
"Mapping kernel at {:#X} with length {:#X}",
|
||||||
|
kernel_address.virtual_base.as_usize(),
|
||||||
|
kernel_address.length
|
||||||
|
);
|
||||||
|
|
||||||
|
space
|
||||||
|
.map_range(
|
||||||
|
kernel_address.virtual_base,
|
||||||
|
kernel_address.physical_base,
|
||||||
|
kernel_address.length,
|
||||||
|
PagePermissions::KERNEL,
|
||||||
|
allocator,
|
||||||
|
)
|
||||||
|
.map_err(|err| AddressSpaceError::Map(err))?;
|
||||||
|
|
||||||
|
Ok(space)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
let cr3 = unsafe { read_cr3() };
|
||||||
|
|
||||||
|
cr3.start_address() == self.root.start_address()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn table(&self, frame: PhysicalFrame) -> Option<&PageTable> {
|
||||||
|
let virtual_addr = self.direct_map.translate(frame.start_address())?;
|
||||||
|
|
||||||
|
Some(unsafe { &*(virtual_addr.as_ptr::<PageTable>()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn table_mut(&mut self, frame: PhysicalFrame) -> Option<&mut PageTable> {
|
||||||
|
let virtual_addr = self.direct_map.translate(frame.start_address())?;
|
||||||
|
|
||||||
|
Some(unsafe { &mut *(virtual_addr.as_mut_ptr::<PageTable>()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(&self, addr: VirtualAddr) -> Option<PhysicalAddr> {
|
||||||
|
let addr = addr.as_usize();
|
||||||
|
|
||||||
|
if !is_canonical_48_bit(addr) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let p4 = self.table(self.root)?;
|
||||||
|
let p4_entry = p4.entries[p4_index(addr)];
|
||||||
|
|
||||||
|
if !p4_entry.is_present() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let p3 = self.table(p4_entry.frame()?)?;
|
||||||
|
let p3_entry = p3.entries[p3_index(addr)];
|
||||||
|
|
||||||
|
if !p3_entry.is_present() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if p3_entry.is_huge() {
|
||||||
|
return translate_huge_page(p3_entry, addr, 1 << 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
let p2 = self.table(p3_entry.frame()?)?;
|
||||||
|
let p2_entry = p2.entries[p2_index(addr)];
|
||||||
|
|
||||||
|
if !p2_entry.is_present() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if p2_entry.is_huge() {
|
||||||
|
return translate_huge_page(p2_entry, addr, 1 << 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
let p1 = self.table(p2_entry.frame()?)?;
|
||||||
|
let p1_entry = p1.entries[p1_index(addr)];
|
||||||
|
|
||||||
|
if !p1_entry.is_present() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let physical_base = p1_entry.physical_address().as_usize();
|
||||||
|
|
||||||
|
physical_base
|
||||||
|
.checked_add(page_offset(addr))
|
||||||
|
.map(PhysicalAddr::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_next_level(
|
||||||
|
&self,
|
||||||
|
parent: PhysicalFrame,
|
||||||
|
index: usize,
|
||||||
|
) -> Result<PhysicalFrame, UnmapError> {
|
||||||
|
let parent_table = self.table(parent).ok_or(UnmapError::PageNotWithinHHDM)?;
|
||||||
|
|
||||||
|
// TODO: encode some level so we dont spuriously think a page is huge
|
||||||
|
if parent_table.entries[index].is_huge() {
|
||||||
|
return Err(UnmapError::ParentIsHuge);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(parent_table.entries[index]
|
||||||
|
.frame()
|
||||||
|
.ok_or(UnmapError::ParentNotPresent)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_next_level_or_allocate(
|
||||||
|
&mut self,
|
||||||
|
parent: PhysicalFrame,
|
||||||
|
index: usize,
|
||||||
|
user_accessible: bool,
|
||||||
|
allocator: &mut FrameAllocator,
|
||||||
|
) -> Result<(PhysicalFrame, bool), MapError> {
|
||||||
|
let parent_table = self
|
||||||
|
.table_mut(parent)
|
||||||
|
.ok_or(MapError::FrameAllocatorError)?;
|
||||||
|
|
||||||
|
let entry = &mut parent_table.entries[index];
|
||||||
|
|
||||||
|
if !entry.is_present() {
|
||||||
|
let frame = allocator.alloc().ok_or(MapError::FrameAllocatorError)?;
|
||||||
|
*entry = PageTableEntry::new_table(frame, user_accessible);
|
||||||
|
return Ok((frame, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.is_huge() {
|
||||||
|
return Err(MapError::ParentIsHuge);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: we upgrade intermediate entries, and dont carefully rollback if we fail
|
||||||
|
if user_accessible && !entry.is_user_accessible() {
|
||||||
|
entry.0 |= PageTableEntry::USER_ACCESSIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry
|
||||||
|
.frame()
|
||||||
|
.map(|frame| (frame, false))
|
||||||
|
.ok_or(MapError::FrameAllocatorError)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rollback_tables(
|
||||||
|
&mut self,
|
||||||
|
new_tables: &[Option<NewTable>],
|
||||||
|
count: usize,
|
||||||
|
allocator: &mut FrameAllocator,
|
||||||
|
) {
|
||||||
|
for table in new_tables[..count].iter().rev().flatten() {
|
||||||
|
self.table_mut(table.parent).unwrap().entries[table.index] = PageTableEntry::null();
|
||||||
|
|
||||||
|
unsafe { allocator.dealloc(table.child) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map(
|
||||||
|
&mut self,
|
||||||
|
page: Page,
|
||||||
|
frame: PhysicalFrame,
|
||||||
|
permissions: PagePermissions,
|
||||||
|
allocator: &mut FrameAllocator,
|
||||||
|
) -> Result<(), MapError> {
|
||||||
|
if permissions.user_accessible && p4_index(page.start.as_usize()) >= 256 {
|
||||||
|
return Err(MapError::UserPageInKernelHalf);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_tables: [Option<NewTable>; 3] = [None; 3];
|
||||||
|
let mut new_table_count = 0;
|
||||||
|
|
||||||
|
let result = (|| {
|
||||||
|
let p4_entry_index = p4_index(page.start.as_usize());
|
||||||
|
let (pdpt_frame, allocated) = self.get_next_level_or_allocate(
|
||||||
|
self.root,
|
||||||
|
p4_entry_index,
|
||||||
|
permissions.user_accessible,
|
||||||
|
allocator,
|
||||||
|
)?;
|
||||||
|
if allocated {
|
||||||
|
new_tables[new_table_count] = Some(NewTable {
|
||||||
|
parent: self.root,
|
||||||
|
index: p4_entry_index,
|
||||||
|
child: pdpt_frame,
|
||||||
|
});
|
||||||
|
new_table_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let p3_entry_index = p3_index(page.start.as_usize());
|
||||||
|
let (pd_frame, allocated) = self.get_next_level_or_allocate(
|
||||||
|
pdpt_frame,
|
||||||
|
p3_entry_index,
|
||||||
|
permissions.user_accessible,
|
||||||
|
allocator,
|
||||||
|
)?;
|
||||||
|
if allocated {
|
||||||
|
new_tables[new_table_count] = Some(NewTable {
|
||||||
|
parent: pdpt_frame,
|
||||||
|
index: p3_entry_index,
|
||||||
|
child: pd_frame,
|
||||||
|
});
|
||||||
|
new_table_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let p2_entry_index = p2_index(page.start.as_usize());
|
||||||
|
let (pt_frame, allocated) = self.get_next_level_or_allocate(
|
||||||
|
pd_frame,
|
||||||
|
p2_entry_index,
|
||||||
|
permissions.user_accessible,
|
||||||
|
allocator,
|
||||||
|
)?;
|
||||||
|
if allocated {
|
||||||
|
new_tables[new_table_count] = Some(NewTable {
|
||||||
|
parent: pd_frame,
|
||||||
|
index: p2_entry_index,
|
||||||
|
child: pt_frame,
|
||||||
|
});
|
||||||
|
new_table_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pt_table = self
|
||||||
|
.table_mut(pt_frame)
|
||||||
|
.ok_or(MapError::FrameAllocatorError)?;
|
||||||
|
let entry = &mut pt_table.entries[p1_index(page.start.as_usize())];
|
||||||
|
if entry.is_present() {
|
||||||
|
return Err(MapError::PageAlreadyMapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
*entry = PageTableEntry::new(frame.start_address(), permissions);
|
||||||
|
Ok(())
|
||||||
|
})();
|
||||||
|
|
||||||
|
if result.is_err() {
|
||||||
|
self.rollback_tables(&new_tables, new_table_count, allocator);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.flush_tlb_if_active(page);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_range(
|
||||||
|
&mut self,
|
||||||
|
virtual_start: VirtualAddr,
|
||||||
|
physical_start: PhysicalAddr,
|
||||||
|
length: usize,
|
||||||
|
permissions: PagePermissions,
|
||||||
|
allocator: &mut FrameAllocator,
|
||||||
|
) -> Result<(), MapError> {
|
||||||
|
if length % PAGE_SIZE != 0 {
|
||||||
|
return Err(MapError::MisalignedPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
let virtual_start =
|
||||||
|
Page::from_start_address(virtual_start).map_err(|_| MapError::MisalignedPage)?;
|
||||||
|
let physical_start =
|
||||||
|
PhysicalFrame::from_start_address(physical_start).ok_or(MapError::MisalignedPage)?;
|
||||||
|
virtual_start
|
||||||
|
.start
|
||||||
|
.as_usize()
|
||||||
|
.checked_add(length)
|
||||||
|
.ok_or(MapError::AddressOverflow)?;
|
||||||
|
physical_start
|
||||||
|
.start_address()
|
||||||
|
.as_usize()
|
||||||
|
.checked_add(length)
|
||||||
|
.ok_or(MapError::AddressOverflow)?;
|
||||||
|
|
||||||
|
for page_idx in 0..length / PAGE_SIZE {
|
||||||
|
let offset = page_idx * PAGE_SIZE;
|
||||||
|
let page =
|
||||||
|
Page::from_start_address(VirtualAddr::new(virtual_start.start.as_usize() + offset))
|
||||||
|
.unwrap();
|
||||||
|
let frame = PhysicalFrame::from_start_address(PhysicalAddr::new(
|
||||||
|
physical_start.start_address().as_usize() + offset,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Err(error) = self.map(page, frame, permissions, allocator) {
|
||||||
|
for rollback_idx in (0..page_idx).rev() {
|
||||||
|
let rollback_page = Page::from_start_address(VirtualAddr::new(
|
||||||
|
virtual_start.start.as_usize() + rollback_idx * PAGE_SIZE,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
self.unmap(rollback_page, allocator)
|
||||||
|
.expect("failed to roll back a mapped page");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmap(
|
||||||
|
&mut self,
|
||||||
|
page: Page,
|
||||||
|
allocator: &mut FrameAllocator,
|
||||||
|
) -> Result<PhysicalFrame, UnmapError> {
|
||||||
|
let pdpt_frame = self.get_next_level(self.root, p4_index(page.start.as_usize()))?;
|
||||||
|
|
||||||
|
let pd_frame = self.get_next_level(pdpt_frame, p3_index(page.start.as_usize()))?;
|
||||||
|
|
||||||
|
let pt_frame = self.get_next_level(pd_frame, p2_index(page.start.as_usize()))?;
|
||||||
|
let pt_table = self
|
||||||
|
.table_mut(pt_frame)
|
||||||
|
.ok_or(UnmapError::FrameAllocatorError)?;
|
||||||
|
|
||||||
|
let entry = pt_table.entries[p1_index(page.start.as_usize())];
|
||||||
|
|
||||||
|
if !entry.is_present() {
|
||||||
|
return Err(UnmapError::PageNotPresent);
|
||||||
|
}
|
||||||
|
|
||||||
|
let frame = entry.frame().ok_or(UnmapError::FrameAllocatorError)?;
|
||||||
|
pt_table.entries[p1_index(page.start.as_usize())] = PageTableEntry::null();
|
||||||
|
|
||||||
|
if pt_table.is_empty() {
|
||||||
|
self.table_mut(pd_frame).unwrap().entries[p2_index(page.start.as_usize())] =
|
||||||
|
PageTableEntry::null();
|
||||||
|
unsafe { allocator.dealloc(pt_frame) };
|
||||||
|
|
||||||
|
if self.table(pd_frame).unwrap().is_empty() {
|
||||||
|
self.table_mut(pdpt_frame).unwrap().entries[p3_index(page.start.as_usize())] =
|
||||||
|
PageTableEntry::null();
|
||||||
|
unsafe { allocator.dealloc(pd_frame) };
|
||||||
|
|
||||||
|
if self.table(pdpt_frame).unwrap().is_empty() {
|
||||||
|
self.table_mut(self.root).unwrap().entries[p4_index(page.start.as_usize())] =
|
||||||
|
PageTableEntry::null();
|
||||||
|
unsafe { allocator.dealloc(pdpt_frame) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.flush_tlb_if_active(page);
|
||||||
|
|
||||||
|
Ok(frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: unmap range
|
||||||
|
|
||||||
|
fn flush_tlb_if_active(&self, page: Page) {
|
||||||
|
if self.is_active() {
|
||||||
|
println!("Flushing TLB");
|
||||||
|
unsafe {
|
||||||
|
asm!("invlpg [{}]", in(reg) page.start.as_usize(), options(nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn activate(&self) -> PageTableRoot {
|
||||||
|
println!("Activating");
|
||||||
|
unsafe {
|
||||||
|
asm!(
|
||||||
|
"mov cr3, {}",
|
||||||
|
in(reg) self.root.start_address().as_usize(),
|
||||||
|
options(nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_canonical_48_bit(addr: usize) -> bool {
|
||||||
|
let upper = addr >> 48;
|
||||||
|
let sign_bit = (addr >> 47) & 1;
|
||||||
|
|
||||||
|
if sign_bit == 0 {
|
||||||
|
upper == 0
|
||||||
|
} else {
|
||||||
|
upper == 0xFFFF
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
-72
@@ -14,46 +14,46 @@ pub unsafe fn read_u8(port: u16) -> u8 {
|
|||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
// #[inline(always)]
|
||||||
pub unsafe fn read_u8_slice(port: u16, slice: &mut [u8]) {
|
// pub unsafe fn read_u8_slice(port: u16, slice: &mut [u8]) {
|
||||||
unsafe {
|
// unsafe {
|
||||||
asm!(
|
// asm!(
|
||||||
"rep insb",
|
// "rep insb",
|
||||||
in("dx") port,
|
// in("dx") port,
|
||||||
inout("rdi") slice.as_mut_ptr() => _,
|
// inout("rdi") slice.as_mut_ptr() => _,
|
||||||
inout("rcx") slice.len() => _,
|
// inout("rcx") slice.len() => _,
|
||||||
options(nostack, preserves_flags),
|
// options(nostack, preserves_flags),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[inline(always)]
|
// #[inline(always)]
|
||||||
pub unsafe fn read_u16(port: u16) -> u16 {
|
// pub unsafe fn read_u16(port: u16) -> u16 {
|
||||||
let value: u16;
|
// let value: u16;
|
||||||
unsafe {
|
// unsafe {
|
||||||
asm!(
|
// asm!(
|
||||||
"in ax, dx",
|
// "in ax, dx",
|
||||||
in("dx") port,
|
// in("dx") port,
|
||||||
out("ax") value,
|
// out("ax") value,
|
||||||
options(nomem, nostack, preserves_flags),
|
// options(nomem, nostack, preserves_flags),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
value
|
// value
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[inline(always)]
|
// #[inline(always)]
|
||||||
pub unsafe fn read_u32(port: u16) -> u32 {
|
// pub unsafe fn read_u32(port: u16) -> u32 {
|
||||||
let value: u32;
|
// let value: u32;
|
||||||
unsafe {
|
// unsafe {
|
||||||
asm!(
|
// asm!(
|
||||||
"in eax, dx",
|
// "in eax, dx",
|
||||||
in("dx") port,
|
// in("dx") port,
|
||||||
out("eax") value,
|
// out("eax") value,
|
||||||
options(nomem, nostack, preserves_flags),
|
// options(nomem, nostack, preserves_flags),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
value
|
// value
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn write_u8(port: u16, value: u8) {
|
pub unsafe fn write_u8(port: u16, value: u8) {
|
||||||
@@ -67,39 +67,39 @@ pub unsafe fn write_u8(port: u16, value: u8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
// #[inline(always)]
|
||||||
pub unsafe fn write_u8_slice(port: u16, slice: &[u8]) {
|
// pub unsafe fn write_u8_slice(port: u16, slice: &[u8]) {
|
||||||
unsafe {
|
// unsafe {
|
||||||
asm!(
|
// asm!(
|
||||||
"rep outsb",
|
// "rep outsb",
|
||||||
in("dx") port,
|
// in("dx") port,
|
||||||
inout("rsi") slice.as_ptr() => _,
|
// inout("rsi") slice.as_ptr() => _,
|
||||||
inout("rcx") slice.len() => _,
|
// inout("rcx") slice.len() => _,
|
||||||
options(nostack, preserves_flags),
|
// options(nostack, preserves_flags),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[inline(always)]
|
// #[inline(always)]
|
||||||
pub unsafe fn write_u16(port: u16, value: u16) {
|
// pub unsafe fn write_u16(port: u16, value: u16) {
|
||||||
unsafe {
|
// unsafe {
|
||||||
asm!(
|
// asm!(
|
||||||
"out dx, ax",
|
// "out dx, ax",
|
||||||
in("dx") port,
|
// in("dx") port,
|
||||||
in("ax") value,
|
// in("ax") value,
|
||||||
options(nomem, nostack, preserves_flags),
|
// options(nomem, nostack, preserves_flags),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[inline(always)]
|
// #[inline(always)]
|
||||||
pub unsafe fn write_u32(port: u16, value: u32) {
|
// pub unsafe fn write_u32(port: u16, value: u32) {
|
||||||
unsafe {
|
// unsafe {
|
||||||
asm!(
|
// asm!(
|
||||||
"out dx, eax",
|
// "out dx, eax",
|
||||||
in("dx") port,
|
// in("dx") port,
|
||||||
in("eax") value,
|
// in("eax") value,
|
||||||
options(nomem, nostack, preserves_flags),
|
// options(nomem, nostack, preserves_flags),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
+31
-9
@@ -3,6 +3,8 @@ 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};
|
||||||
|
|
||||||
/// 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.
|
||||||
/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler.
|
/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler.
|
||||||
@@ -32,17 +34,17 @@ static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new();
|
|||||||
static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
|
static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
|
||||||
|
|
||||||
pub struct BootInfo {
|
pub struct BootInfo {
|
||||||
pub kernel_address: crate::memory::PhysicalAddr,
|
pub kernel_address: KernelImage,
|
||||||
pub hhdm_offset: u64,
|
pub hhdm_offset: usize,
|
||||||
entries: &'static [&'static limine_api::memmap::Entry],
|
entries: &'static [&'static limine_api::memmap::Entry],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BootInfo {
|
impl BootInfo {
|
||||||
pub fn memory_regions(&self) -> impl Iterator<Item = crate::memory::MemoryRegion> + '_ {
|
pub fn memory_regions(&self) -> impl Iterator<Item = crate::memory::MemoryRegion> + Clone + '_ {
|
||||||
use crate::memory::{MemoryRegion, MemoryRegionKind};
|
use crate::memory::{MemoryRegion, MemoryRegionKind};
|
||||||
self.entries.iter().map(|&entry| MemoryRegion {
|
self.entries.iter().map(|&entry| MemoryRegion {
|
||||||
start: crate::memory::PhysicalAddr::new(entry.base),
|
start: crate::memory::PhysicalAddr::new(entry.base as usize),
|
||||||
length: entry.length,
|
length: entry.length as usize,
|
||||||
kind: match entry.type_ {
|
kind: match entry.type_ {
|
||||||
limine_api::memmap::MEMMAP_USABLE => MemoryRegionKind::Usable,
|
limine_api::memmap::MEMMAP_USABLE => MemoryRegionKind::Usable,
|
||||||
limine_api::memmap::MEMMAP_RESERVED => MemoryRegionKind::Reserved,
|
limine_api::memmap::MEMMAP_RESERVED => MemoryRegionKind::Reserved,
|
||||||
@@ -69,6 +71,7 @@ pub enum BootError {
|
|||||||
FailedToGetKernelAddress,
|
FailedToGetKernelAddress,
|
||||||
FailedToGetHHDMAddress,
|
FailedToGetHHDMAddress,
|
||||||
FailedToGetMemmap,
|
FailedToGetMemmap,
|
||||||
|
FailedToLocateKernel,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_boot_info() -> Result<BootInfo, BootError> {
|
pub fn load_boot_info() -> Result<BootInfo, BootError> {
|
||||||
@@ -78,8 +81,7 @@ pub fn load_boot_info() -> Result<BootInfo, BootError> {
|
|||||||
|
|
||||||
let kernel_address = KERNEL_ADDRESS_REQUEST
|
let kernel_address = KERNEL_ADDRESS_REQUEST
|
||||||
.response()
|
.response()
|
||||||
.ok_or(BootError::FailedToGetKernelAddress)?
|
.ok_or(BootError::FailedToGetKernelAddress)?;
|
||||||
.physical_base;
|
|
||||||
let hhdm_offset = HHDM_REQUEST
|
let hhdm_offset = HHDM_REQUEST
|
||||||
.response()
|
.response()
|
||||||
.ok_or(BootError::FailedToGetHHDMAddress)?
|
.ok_or(BootError::FailedToGetHHDMAddress)?
|
||||||
@@ -90,9 +92,29 @@ pub fn load_boot_info() -> Result<BootInfo, BootError> {
|
|||||||
.ok_or(BootError::FailedToGetMemmap)?
|
.ok_or(BootError::FailedToGetMemmap)?
|
||||||
.entries();
|
.entries();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
let kernel_length = kernel_length.ok_or(BootError::FailedToLocateKernel)?;
|
||||||
|
|
||||||
Ok(BootInfo {
|
Ok(BootInfo {
|
||||||
kernel_address: crate::memory::PhysicalAddr::new(kernel_address),
|
kernel_address: KernelImage {
|
||||||
hhdm_offset,
|
physical_base: PhysicalAddr::new(kernel_address.physical_base as usize),
|
||||||
|
virtual_base: VirtualAddr::new(kernel_address.virtual_base as usize),
|
||||||
|
length: kernel_length,
|
||||||
|
},
|
||||||
|
hhdm_offset: hhdm_offset as usize,
|
||||||
entries: memmap,
|
entries: memmap,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-4
@@ -8,20 +8,85 @@ mod boot;
|
|||||||
mod debug;
|
mod debug;
|
||||||
mod memory;
|
mod memory;
|
||||||
|
|
||||||
use crate::debug::serial;
|
use crate::{
|
||||||
|
arch::paging,
|
||||||
|
debug::serial,
|
||||||
|
memory::{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();
|
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 addr = 0xDEADBEEF as *mut u32;
|
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(
|
||||||
|
direct_map,
|
||||||
|
boot_info.memory_regions(),
|
||||||
|
boot_info.kernel_address,
|
||||||
|
&mut allocator,
|
||||||
|
)
|
||||||
|
.expect("failed to create page table");
|
||||||
|
|
||||||
|
// safety: trust me bro
|
||||||
|
unsafe { page_table.activate() };
|
||||||
|
|
||||||
|
let frame = allocator.alloc().unwrap();
|
||||||
|
|
||||||
|
let direct_mapped = direct_map.translate(frame.start_address()).unwrap();
|
||||||
|
let translated = page_table.translate(direct_mapped).unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", translated);
|
||||||
|
|
||||||
|
let new_virtual = VirtualAddr::new(0x8000_0000);
|
||||||
|
|
||||||
|
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,
|
||||||
|
&mut allocator,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
page_table.translate(new_virtual),
|
||||||
|
Some(frame.start_address())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
page_table.translate(VirtualAddr::new(new_virtual.as_usize() + 123)),
|
||||||
|
Some(PhysicalAddr::new(frame.start_address().as_usize() + 123))
|
||||||
|
);
|
||||||
|
|
||||||
|
// write to the page and read it back via HHDM
|
||||||
unsafe {
|
unsafe {
|
||||||
*addr = 0xDEADBEEF;
|
core::ptr::write_bytes(new_virtual.as_mut_ptr::<u8>(), 0xFF, 0x1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let slice = unsafe { core::slice::from_raw_parts(direct_mapped.as_ptr::<u8>(), 0x1000) };
|
||||||
|
|
||||||
|
assert!(slice.iter().all(|&byte| byte == 0xFF));
|
||||||
|
|
||||||
|
println!("{:#X}", slice[0]);
|
||||||
|
|
||||||
|
let unmapped_frame = page_table.unmap(page, &mut allocator).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(unmapped_frame, frame);
|
||||||
|
|
||||||
|
unsafe { allocator.dealloc(unmapped_frame) };
|
||||||
|
|
||||||
|
assert!(page_table.translate(new_virtual).is_none());
|
||||||
|
|
||||||
hcf();
|
hcf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,289 @@
|
|||||||
|
use crate::memory::{DirectMap, MemoryRegion, MemoryRegionKind, PhysicalAddr, VirtualAddr};
|
||||||
|
|
||||||
|
pub const FRAME_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
pub fn align_up_to_frame(addr: usize) -> Option<usize> {
|
||||||
|
addr.checked_add(FRAME_SIZE as usize - 1)
|
||||||
|
.map(|addr| addr & !(FRAME_SIZE - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn align_down_to_frame(addr: usize) -> usize {
|
||||||
|
addr & !(FRAME_SIZE - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum FrameState {
|
||||||
|
Reserved = 0b00,
|
||||||
|
Free = 0b01,
|
||||||
|
Allocated = 0b10,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Bitmap {
|
||||||
|
start: VirtualAddr,
|
||||||
|
frame_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bitmap {
|
||||||
|
fn new(start: VirtualAddr, frame_count: usize) -> Self {
|
||||||
|
Self { start, frame_count }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self, frame_idx: usize) -> FrameState {
|
||||||
|
assert!(frame_idx < self.frame_count, "frame index out of bounds");
|
||||||
|
|
||||||
|
let byte_idx = frame_idx / 4;
|
||||||
|
let shift = (frame_idx % 4) * 2;
|
||||||
|
let byte = unsafe { self.start.as_ptr::<u8>().add(byte_idx).read() };
|
||||||
|
|
||||||
|
match (byte >> shift) & 0b11 {
|
||||||
|
0b00 => FrameState::Reserved,
|
||||||
|
0b01 => FrameState::Free,
|
||||||
|
0b10 => FrameState::Allocated,
|
||||||
|
_ => panic!("invalid frame state"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_state(&mut self, frame_idx: usize, state: FrameState) {
|
||||||
|
assert!(frame_idx < self.frame_count, "frame index out of bounds");
|
||||||
|
|
||||||
|
let byte_idx = frame_idx / 4;
|
||||||
|
let shift = (frame_idx % 4) * 2;
|
||||||
|
let ptr = unsafe { self.start.as_mut_ptr::<u8>().add(byte_idx) };
|
||||||
|
let byte = unsafe { ptr.read() };
|
||||||
|
let mask = 0b11 << shift;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ptr.write((byte & !mask) | ((state as u8) << shift));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FrameAllocatorInitError {
|
||||||
|
AddressOverflow,
|
||||||
|
NoUsableFrames,
|
||||||
|
NoBitmapStorage,
|
||||||
|
BitmapOutsideDirectMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
// very very simple linked list frame/page allocator
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FrameAllocator {
|
||||||
|
bitmap: Bitmap,
|
||||||
|
next_search: usize,
|
||||||
|
allocatable_frames: usize,
|
||||||
|
free_frames: usize,
|
||||||
|
direct_map: DirectMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FrameAllocator {
|
||||||
|
pub fn new<I>(regions: I, direct_map: DirectMap) -> Result<Self, FrameAllocatorInitError>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = MemoryRegion> + Clone,
|
||||||
|
{
|
||||||
|
let mut highest_frame: Option<usize> = None;
|
||||||
|
|
||||||
|
for region in regions.clone() {
|
||||||
|
if region.kind != MemoryRegionKind::Usable {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let range = Self::usable_frame_range(region)?;
|
||||||
|
if range.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
highest_frame = Some(highest_frame.map_or(range.end, |current| current.max(range.end)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let highest_frame = highest_frame.ok_or(FrameAllocatorInitError::NoUsableFrames)?;
|
||||||
|
|
||||||
|
let bitmap_bytes = highest_frame.div_ceil(4);
|
||||||
|
let bitmap_frame_count = bitmap_bytes.div_ceil(FRAME_SIZE);
|
||||||
|
let bitmap_storage_bytes = bitmap_frame_count
|
||||||
|
.checked_mul(FRAME_SIZE)
|
||||||
|
.ok_or(FrameAllocatorInitError::AddressOverflow)?;
|
||||||
|
|
||||||
|
let mut bitmap_start_frame: Option<usize> = None;
|
||||||
|
|
||||||
|
for region in regions.clone() {
|
||||||
|
if region.kind != MemoryRegionKind::Usable {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let range = Self::usable_frame_range(region)?;
|
||||||
|
if range.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if range.len() < bitmap_frame_count {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap_start_frame = Some(range.start);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if bitmap_start_frame.is_none() {
|
||||||
|
return Err(FrameAllocatorInitError::NoBitmapStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bitmap_start_frame = bitmap_start_frame.unwrap();
|
||||||
|
|
||||||
|
let bitmap_physical_addr = PhysicalAddr::new(bitmap_start_frame * FRAME_SIZE);
|
||||||
|
let bitmap_virtual = direct_map
|
||||||
|
.translate(bitmap_physical_addr)
|
||||||
|
.ok_or(FrameAllocatorInitError::BitmapOutsideDirectMap)?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// set everyting to unavailable
|
||||||
|
core::ptr::write_bytes(bitmap_virtual.as_mut_ptr::<u8>(), 0, bitmap_storage_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut allocatable_frames = 0;
|
||||||
|
let mut free_frames = 0;
|
||||||
|
let mut bitmap = Bitmap::new(bitmap_virtual, highest_frame);
|
||||||
|
|
||||||
|
let bitmap_end_frame = bitmap_start_frame
|
||||||
|
.checked_add(bitmap_frame_count)
|
||||||
|
.ok_or(FrameAllocatorInitError::AddressOverflow)?;
|
||||||
|
|
||||||
|
for region in regions {
|
||||||
|
if region.kind != MemoryRegionKind::Usable {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for frame_idx in Self::usable_frame_range(region)? {
|
||||||
|
let is_bitmap_storage =
|
||||||
|
frame_idx >= bitmap_start_frame && frame_idx < bitmap_end_frame;
|
||||||
|
if is_bitmap_storage {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap.set_state(frame_idx, FrameState::Free);
|
||||||
|
allocatable_frames += 1;
|
||||||
|
free_frames += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
bitmap,
|
||||||
|
next_search: bitmap_start_frame + bitmap_frame_count,
|
||||||
|
allocatable_frames,
|
||||||
|
free_frames,
|
||||||
|
direct_map,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_free_in(&self, start: usize, end: usize) -> Option<usize> {
|
||||||
|
(start..end).find(|&index| self.bitmap.state(index) == FrameState::Free)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_free_frame(&self) -> Option<usize> {
|
||||||
|
self.find_free_in(self.next_search, self.bitmap.frame_count)
|
||||||
|
.or_else(|| self.find_free_in(0, self.next_search))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc_nozero(&mut self) -> Option<PhysicalFrame> {
|
||||||
|
if self.free_frames == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let frame_idx = self.find_free_frame()?;
|
||||||
|
|
||||||
|
self.bitmap.set_state(frame_idx, FrameState::Allocated);
|
||||||
|
self.free_frames -= 1;
|
||||||
|
self.next_search = frame_idx.saturating_add(1);
|
||||||
|
|
||||||
|
Some(PhysicalFrame::from_index(frame_idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc(&mut self) -> Option<PhysicalFrame> {
|
||||||
|
let frame = self.alloc_nozero()?;
|
||||||
|
|
||||||
|
let start = self
|
||||||
|
.direct_map
|
||||||
|
.translate(frame.start_address())
|
||||||
|
.expect("frame is outside the direct map");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::ptr::write_bytes(start.as_mut_ptr::<u8>(), 0, FRAME_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure:
|
||||||
|
/// - The frame is currently owned by the caller
|
||||||
|
/// - it is not currently in use
|
||||||
|
/// - it has not been freed
|
||||||
|
pub unsafe fn dealloc(&mut self, frame: PhysicalFrame) {
|
||||||
|
let frame_idx = frame.index();
|
||||||
|
|
||||||
|
match self.bitmap.state(frame_idx) {
|
||||||
|
FrameState::Allocated => {
|
||||||
|
self.bitmap.set_state(frame_idx, FrameState::Free);
|
||||||
|
self.free_frames += 1;
|
||||||
|
self.next_search = self.next_search.min(frame_idx);
|
||||||
|
}
|
||||||
|
FrameState::Free => panic!("attempted to free free frame"),
|
||||||
|
FrameState::Reserved => {
|
||||||
|
panic!("attempted to free reserved frame");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn free_frames(&self) -> usize {
|
||||||
|
self.free_frames
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn allocatable_frames(&self) -> usize {
|
||||||
|
self.allocatable_frames
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usable_frame_range(
|
||||||
|
region: MemoryRegion,
|
||||||
|
) -> Result<core::ops::Range<usize>, FrameAllocatorInitError> {
|
||||||
|
let region_end = region
|
||||||
|
.start
|
||||||
|
.as_usize()
|
||||||
|
.checked_add(region.length)
|
||||||
|
.ok_or(FrameAllocatorInitError::AddressOverflow)?;
|
||||||
|
|
||||||
|
let start = align_up_to_frame(region.start.as_usize())
|
||||||
|
.ok_or(FrameAllocatorInitError::AddressOverflow)?;
|
||||||
|
let end = align_down_to_frame(region_end);
|
||||||
|
|
||||||
|
Ok((start / FRAME_SIZE)..(end / FRAME_SIZE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct PhysicalFrame(PhysicalAddr);
|
||||||
|
|
||||||
|
impl PhysicalFrame {
|
||||||
|
pub fn from_start_address(address: PhysicalAddr) -> Option<Self> {
|
||||||
|
if address.as_usize() % FRAME_SIZE != 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Self(address))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_address(&self) -> PhysicalAddr {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_index(index: usize) -> Self {
|
||||||
|
Self(PhysicalAddr::new(index * FRAME_SIZE))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(&self) -> usize {
|
||||||
|
self.0.as_usize() / FRAME_SIZE
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
-9
@@ -1,32 +1,47 @@
|
|||||||
|
mod frame;
|
||||||
|
|
||||||
|
pub use frame::{FRAME_SIZE, FrameAllocator, PhysicalFrame};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct KernelImage {
|
||||||
|
pub physical_base: PhysicalAddr,
|
||||||
|
pub virtual_base: VirtualAddr,
|
||||||
|
pub length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct PhysicalAddr(u64);
|
pub struct PhysicalAddr(usize);
|
||||||
|
|
||||||
impl PhysicalAddr {
|
impl PhysicalAddr {
|
||||||
pub fn new(addr: u64) -> Self {
|
pub fn new(addr: usize) -> Self {
|
||||||
Self(addr)
|
Self(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn as_u64(self) -> u64 {
|
pub const fn as_usize(self) -> usize {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct VirtualAddr(u64);
|
pub struct VirtualAddr(usize);
|
||||||
|
|
||||||
impl VirtualAddr {
|
impl VirtualAddr {
|
||||||
pub fn new(addr: u64) -> Self {
|
pub fn new(addr: usize) -> Self {
|
||||||
Self(addr)
|
Self(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn as_u64(self) -> u64 {
|
pub const fn as_usize(self) -> usize {
|
||||||
self.0
|
self.0 as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn as_mut_ptr<T>(self) -> *mut T {
|
pub unsafe fn as_mut_ptr<T>(self) -> *mut T {
|
||||||
self.as_u64() as *mut T
|
self.as_usize() as *mut T
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_ptr<T>(self) -> *const T {
|
||||||
|
self.as_usize() as *const T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +61,23 @@ pub enum MemoryRegionKind {
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct MemoryRegion {
|
pub struct MemoryRegion {
|
||||||
pub start: PhysicalAddr,
|
pub start: PhysicalAddr,
|
||||||
pub length: u64,
|
pub length: usize,
|
||||||
pub kind: MemoryRegionKind,
|
pub kind: MemoryRegionKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct DirectMap {
|
||||||
|
offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirectMap {
|
||||||
|
pub fn new(offset: usize) -> Self {
|
||||||
|
Self { offset }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(self, addr: PhysicalAddr) -> Option<VirtualAddr> {
|
||||||
|
addr.as_usize()
|
||||||
|
.checked_add(self.offset)
|
||||||
|
.map(VirtualAddr::new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user