feat: frame allocation
This commit is contained in:
@@ -50,6 +50,7 @@ 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();
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
+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),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
+6
-6
@@ -33,16 +33,16 @@ static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
|
|||||||
|
|
||||||
pub struct BootInfo {
|
pub struct BootInfo {
|
||||||
pub kernel_address: crate::memory::PhysicalAddr,
|
pub kernel_address: crate::memory::PhysicalAddr,
|
||||||
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,
|
||||||
@@ -91,8 +91,8 @@ pub fn load_boot_info() -> Result<BootInfo, BootError> {
|
|||||||
.entries();
|
.entries();
|
||||||
|
|
||||||
Ok(BootInfo {
|
Ok(BootInfo {
|
||||||
kernel_address: crate::memory::PhysicalAddr::new(kernel_address),
|
kernel_address: crate::memory::PhysicalAddr::new(kernel_address as usize),
|
||||||
hhdm_offset,
|
hhdm_offset: hhdm_offset as usize,
|
||||||
entries: memmap,
|
entries: memmap,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-3
@@ -15,13 +15,40 @@ 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 mut allocator = memory::FrameAllocator::new(
|
||||||
|
boot_info.memory_regions(),
|
||||||
|
memory::DirectMap::new(boot_info.hhdm_offset),
|
||||||
|
)
|
||||||
|
.expect("failed to create frame allocator");
|
||||||
|
|
||||||
|
let initial = allocator.free_frames();
|
||||||
|
|
||||||
|
let first = allocator.alloc().unwrap();
|
||||||
|
let second = allocator.alloc().unwrap();
|
||||||
|
let third = allocator.alloc().unwrap();
|
||||||
|
|
||||||
|
assert_ne!(first, second);
|
||||||
|
assert_ne!(second, third);
|
||||||
|
assert_eq!(allocator.free_frames(), initial - 3);
|
||||||
|
|
||||||
let addr = 0xDEADBEEF as *mut u32;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
*addr = 0xDEADBEEF;
|
allocator.dealloc(second);
|
||||||
|
allocator.dealloc(first);
|
||||||
|
allocator.dealloc(third);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_eq!(allocator.free_frames(), initial);
|
||||||
|
|
||||||
|
let a = allocator.alloc().unwrap();
|
||||||
|
let b = allocator.alloc().unwrap();
|
||||||
|
let c = allocator.alloc().unwrap();
|
||||||
|
|
||||||
|
assert_ne!(a, b);
|
||||||
|
assert_ne!(b, c);
|
||||||
|
assert_ne!(a, c);
|
||||||
|
|
||||||
hcf();
|
hcf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,354 @@
|
|||||||
|
use core::ops::Range;
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Bitmap {
|
||||||
|
start: VirtualAddr,
|
||||||
|
bit_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 = available, 0 = unavailable
|
||||||
|
impl Bitmap {
|
||||||
|
fn new(start: VirtualAddr, bit_count: usize) -> Self {
|
||||||
|
Self { start, bit_count }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self, idx: usize) -> bool {
|
||||||
|
assert!(idx < self.bit_count, "index out of bounds");
|
||||||
|
|
||||||
|
let byte = idx / 8;
|
||||||
|
let bit = idx % 8;
|
||||||
|
|
||||||
|
unsafe { self.start.as_mut_ptr::<u8>().add(byte).read() & (1 << bit) != 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_available(&mut self, idx: usize, available: bool) {
|
||||||
|
assert!(idx < self.bit_count, "index out of bounds");
|
||||||
|
|
||||||
|
let byte = idx / 8;
|
||||||
|
let bit = idx % 8;
|
||||||
|
|
||||||
|
let val = unsafe { self.start.as_mut_ptr::<u8>().add(byte).read() };
|
||||||
|
unsafe {
|
||||||
|
self.start.as_mut_ptr::<u8>().add(byte).write(if available {
|
||||||
|
val | (1 << bit)
|
||||||
|
} else {
|
||||||
|
val & !(1 << bit)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn fill_available(&mut self, range: Range<usize>, available: bool) -> bool {
|
||||||
|
// if range.start > range.end {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if range.end > self.bit_count {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if range.is_empty() {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let ptr = unsafe { self.start.as_mut_ptr::<u8>() };
|
||||||
|
// let first_byte = range.start / 8;
|
||||||
|
// let last_byte = (range.end - 1) / 8;
|
||||||
|
// let start_bit = range.start % 8;
|
||||||
|
// let end_bit = range.end % 8;
|
||||||
|
|
||||||
|
// unsafe {
|
||||||
|
// if first_byte == last_byte {
|
||||||
|
// let width = range.end - range.start;
|
||||||
|
// let mask = (((1u16 << width) - 1) << start_bit) as u8;
|
||||||
|
// let byte = ptr.add(first_byte).read();
|
||||||
|
|
||||||
|
// ptr.add(first_byte)
|
||||||
|
// .write(if available { byte | mask } else { byte & !mask });
|
||||||
|
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let mut full_start = first_byte;
|
||||||
|
|
||||||
|
// if start_bit != 0 {
|
||||||
|
// let mask = u8::MAX << start_bit;
|
||||||
|
// let byte = ptr.add(first_byte).read();
|
||||||
|
|
||||||
|
// ptr.add(first_byte)
|
||||||
|
// .write(if available { byte | mask } else { byte & !mask });
|
||||||
|
|
||||||
|
// full_start += 1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let full_end = range.end / 8;
|
||||||
|
|
||||||
|
// ptr.add(full_start)
|
||||||
|
// .write_bytes(if available { u8::MAX } else { 0 }, full_end - full_start);
|
||||||
|
|
||||||
|
// if end_bit != 0 {
|
||||||
|
// let mask = (1u8 << end_bit) - 1;
|
||||||
|
// let byte = ptr.add(full_end).read();
|
||||||
|
|
||||||
|
// ptr.add(full_end)
|
||||||
|
// .write(if available { byte | mask } else { byte & !mask });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// true
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FrameAllocatorInitError {
|
||||||
|
AddressOverflow,
|
||||||
|
NoUsableFrames,
|
||||||
|
NoBitmapStorage,
|
||||||
|
BitmapOutsideDirectMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
// very very simple linked list frame/page allocator
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FrameAllocator {
|
||||||
|
bitmap: Bitmap,
|
||||||
|
bitmap_start_frame: usize,
|
||||||
|
bitmap_frame_count: usize,
|
||||||
|
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(8);
|
||||||
|
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_available(frame_idx, true);
|
||||||
|
allocatable_frames += 1;
|
||||||
|
free_frames += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
bitmap,
|
||||||
|
bitmap_start_frame: bitmap_start_frame,
|
||||||
|
bitmap_frame_count,
|
||||||
|
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.is_available(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_free_frame(&self) -> Option<usize> {
|
||||||
|
self.find_free_in(self.next_search, self.bitmap.bit_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_available(frame_idx, false);
|
||||||
|
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();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
frame_idx < self.bitmap.bit_count,
|
||||||
|
"frame index out of bounds"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!self.is_bitmap_storage(frame_idx),
|
||||||
|
"attempted to free frame allocator bitmap"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!self.bitmap.is_available(frame_idx),
|
||||||
|
"frame is already free"
|
||||||
|
);
|
||||||
|
|
||||||
|
self.bitmap.set_available(frame_idx, true);
|
||||||
|
self.free_frames += 1;
|
||||||
|
self.next_search = self.next_search.min(frame_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn free_frames(&self) -> usize {
|
||||||
|
self.free_frames
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn allocatable_frames(&self) -> usize {
|
||||||
|
self.allocatable_frames
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_bitmap_storage(&self, index: usize) -> bool {
|
||||||
|
index >= self.bitmap_start_frame
|
||||||
|
&& index < (self.bitmap_start_frame + self.bitmap_frame_count)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
-9
@@ -1,32 +1,36 @@
|
|||||||
|
mod frame;
|
||||||
|
|
||||||
|
pub use frame::{FRAME_SIZE, FrameAllocator, PhysicalFrame};
|
||||||
|
|
||||||
#[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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +50,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