forked from INotFail/Elyz
feat: add pmm, add vmm, add allocator, create memory management foundation
This commit is contained in:
@@ -10,6 +10,7 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
limine = "0.5"
|
||||
embedded-graphics = "0.8"
|
||||
bitflags = "2.11.0"
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
57
kernel/src/debug.rs
Normal file
57
kernel/src/debug.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
pub mod serial;
|
||||
|
||||
use embedded_graphics::pixelcolor::Rgb888;
|
||||
use embedded_graphics::prelude::RgbColor;
|
||||
|
||||
pub enum LogLevel {
|
||||
Info, Warn, Error,
|
||||
}
|
||||
|
||||
impl LogLevel {
|
||||
pub fn serial_color_code(&self) -> &str {
|
||||
match self {
|
||||
LogLevel::Info => "\x1b[32m",
|
||||
LogLevel::Warn => "\x1b[33m",
|
||||
LogLevel::Error => "\x1b[31m",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn console_color(&self) -> Rgb888 {
|
||||
match self {
|
||||
LogLevel::Info => Rgb888::GREEN,
|
||||
LogLevel::Warn => Rgb888::YELLOW,
|
||||
LogLevel::Error => Rgb888::RED,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log {
|
||||
($console:expr, $level:expr, $module:expr, $($arg:tt)*) => {{
|
||||
use core::fmt::Write;
|
||||
use embedded_graphics::pixelcolor::Rgb888;
|
||||
|
||||
// Visual screen output
|
||||
$console.set_color($level.console_color());
|
||||
let _ = write!($console, "[ LOG ] ");
|
||||
|
||||
$console.set_color(Rgb888::WHITE);
|
||||
let _ = write!($console, "{:<6} | ", $module);
|
||||
let _ = writeln!($console, $($arg)*);
|
||||
|
||||
// Serial debug output
|
||||
let mut sp = unsafe { $crate::debug::serial::SerialPort::init() };
|
||||
let _ = writeln!(
|
||||
sp,
|
||||
"{}[{:>5}]\x1b[0m {:<8} | {}",
|
||||
$level.serial_color_code(),
|
||||
"LOG",
|
||||
$module,
|
||||
format_args!($($arg)*)
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! info { ($c:expr, $m:expr, $($p:tt)*) => { $crate::log!($c, $crate::debug::LogLevel::Info, $m, $($p)*) }; }
|
||||
#[macro_export] macro_rules! warn { ($c:expr, $m:expr, $($p:tt)*) => { $crate::log!($c, $crate::debug::LogLevel::Warn, $m, $($p)*) }; }
|
||||
#[macro_export] macro_rules! error { ($c:expr, $m:expr, $($p:tt)*) => { $crate::log!($c, $crate::debug::LogLevel::Error, $m, $($p)*) }; }
|
||||
53
kernel/src/debug/serial.rs
Normal file
53
kernel/src/debug/serial.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use core::arch::asm;
|
||||
|
||||
pub struct SerialPort(u16);
|
||||
|
||||
impl SerialPort {
|
||||
pub const COM1: u16 = 0x3F8;
|
||||
|
||||
pub unsafe fn init() -> Self {
|
||||
let port = Self::COM1;
|
||||
// В новых версиях Rust даже внутри unsafe fn
|
||||
// вызовы других unsafe функций требуют явного блока
|
||||
unsafe {
|
||||
outb(port + 1, 0x00); // Disable interrupts
|
||||
outb(port + 3, 0x80); // Enable DLAB
|
||||
outb(port + 0, 0x03); // Divisor 3 (38400 baud)
|
||||
outb(port + 1, 0x00);
|
||||
outb(port + 3, 0x03); // 8 bits, no parity, 1 stop bit
|
||||
outb(port + 2, 0xC7); // Enable FIFO
|
||||
outb(port + 4, 0x0B); // IRQs enabled, RTS/DSR set
|
||||
}
|
||||
SerialPort(port)
|
||||
}
|
||||
|
||||
fn is_transmit_empty(&self) -> bool {
|
||||
unsafe { (inb(self.0 + 5) & 0x20) != 0 }
|
||||
}
|
||||
|
||||
pub fn send(&self, data: u8) {
|
||||
while !self.is_transmit_empty() {}
|
||||
unsafe { outb(self.0, data); }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Write for SerialPort {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
for byte in s.bytes() { self.send(byte); }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn outb(port: u16, val: u8) {
|
||||
unsafe {
|
||||
asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn inb(port: u16) -> u8 {
|
||||
let res: u8;
|
||||
unsafe {
|
||||
asm!("in al, dx", out("al") res, in("dx") port, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
res
|
||||
}
|
||||
@@ -1,10 +1,25 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::arch::asm;
|
||||
use core::fmt::{self, Write};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use limine::BaseRevision;
|
||||
use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker};
|
||||
use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker, HhdmRequest, MemoryMapRequest, ExecutableAddressRequest};
|
||||
|
||||
use crate::mem::paging::{PageTable, PageTableFlags};
|
||||
use crate::mem::address::{PhysAddr, VirtAddr};
|
||||
|
||||
mod mem;
|
||||
#[macro_use]
|
||||
pub mod debug;
|
||||
|
||||
pub mod allocator {
|
||||
pub use crate::mem::allocator::ALLOCATOR;
|
||||
}
|
||||
|
||||
use embedded_graphics::{
|
||||
mono_font::{ascii::FONT_6X10, MonoTextStyle},
|
||||
@@ -14,7 +29,7 @@ use embedded_graphics::{
|
||||
Drawable,
|
||||
};
|
||||
|
||||
// Отрисовка пикселей
|
||||
/// Low-level pixel rendering engine
|
||||
struct FramebufferDisplay<'a> {
|
||||
framebuffer: &'a limine::framebuffer::Framebuffer<'a>,
|
||||
}
|
||||
@@ -46,82 +61,70 @@ impl OriginDimensions for FramebufferDisplay<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// Терминальный вывод. Следит за положением курсора и умеет двигать текст
|
||||
/// High-level console for terminal-like output
|
||||
struct Console<'a> {
|
||||
display: FramebufferDisplay<'a>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
current_color: Rgb888,
|
||||
}
|
||||
|
||||
impl<'a> Console<'a> {
|
||||
pub fn new(display: FramebufferDisplay<'a>) -> Self {
|
||||
Self { display, x: 10, y: 20 } // Начальный отступ
|
||||
Self { display, x: 10, y: 20, current_color: Rgb888::WHITE }
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: Rgb888) {
|
||||
self.current_color = color;
|
||||
}
|
||||
|
||||
// Метод для очистки экрана
|
||||
pub fn clear(&mut self) {
|
||||
let size = self.display.size();
|
||||
let fb = self.display.framebuffer;
|
||||
unsafe {
|
||||
// Быстрая очистка через заполнение памяти нулями
|
||||
core::ptr::write_bytes(fb.addr(), 0, fb.pitch() as usize * fb.height() as usize);
|
||||
}
|
||||
self.x = 10;
|
||||
self.y = 20;
|
||||
}
|
||||
|
||||
// Логика скроллинга. копирует пиксели снизу вверх
|
||||
/// Shifts pixels upward to make room for new lines
|
||||
fn scroll(&mut self) {
|
||||
let fb = self.display.framebuffer;
|
||||
let pitch = fb.pitch() as usize;
|
||||
let height = fb.height() as usize;
|
||||
let font_height = 10; // Высота FONT_6X10
|
||||
let font_height = 10;
|
||||
let shift = font_height * pitch;
|
||||
|
||||
unsafe {
|
||||
let addr = fb.addr();
|
||||
// Копируем всё содержимое экрана на одну строку вверх
|
||||
core::ptr::copy(
|
||||
addr.add(shift),
|
||||
addr,
|
||||
pitch * (height - font_height)
|
||||
);
|
||||
// Очищаем последнюю строку
|
||||
core::ptr::write_bytes(
|
||||
addr.add(pitch * (height - font_height)),
|
||||
0,
|
||||
shift
|
||||
);
|
||||
core::ptr::copy(addr.add(shift), addr, pitch * (height - font_height));
|
||||
core::ptr::write_bytes(addr.add(pitch * (height - font_height)), 0, shift);
|
||||
}
|
||||
self.y -= font_height as i32;
|
||||
}
|
||||
}
|
||||
|
||||
// Вывод текста. write!(console, "Hello {}", name)
|
||||
impl fmt::Write for Console<'_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
let style = MonoTextStyle::new(&FONT_6X10, Rgb888::WHITE);
|
||||
|
||||
let style = MonoTextStyle::new(&FONT_6X10, self.current_color);
|
||||
|
||||
for c in s.chars() {
|
||||
if c == '\n' {
|
||||
self.x = 10;
|
||||
self.y += 10;
|
||||
} else {
|
||||
// Если текст выходит за границы экрана — перенос строки
|
||||
if self.x + 6 > self.display.framebuffer.width() as i32 {
|
||||
self.x = 10;
|
||||
self.y += 10;
|
||||
}
|
||||
|
||||
// Рисуем символ (превращаем char в временную строку &str)
|
||||
let mut buf = [0u8; 4];
|
||||
let s_char = c.encode_utf8(&mut buf);
|
||||
let _ = Text::new(s_char, Point::new(self.x, self.y), style).draw(&mut self.display);
|
||||
|
||||
self.x += 6; // Ширина FONT_6X10
|
||||
self.x += 6;
|
||||
}
|
||||
|
||||
// Если достигли низа экрана, то скроллим
|
||||
if self.y > self.display.framebuffer.height() as i32 - 10 {
|
||||
self.scroll();
|
||||
}
|
||||
@@ -130,64 +133,113 @@ impl fmt::Write for Console<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// boot
|
||||
#[used]
|
||||
#[unsafe(link_section = ".requests")]
|
||||
// --- Limine Requests ---
|
||||
|
||||
#[used] #[unsafe(link_section = ".requests")]
|
||||
static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new();
|
||||
|
||||
#[used] #[unsafe(link_section = ".requests")]
|
||||
static HHDM_REQUEST: HhdmRequest = HhdmRequest::new();
|
||||
|
||||
#[used] #[unsafe(link_section = ".requests")]
|
||||
static KERNEL_ADDR_REQUEST: ExecutableAddressRequest = ExecutableAddressRequest::new();
|
||||
|
||||
#[used] #[unsafe(link_section = ".requests")]
|
||||
static BASE_REVISION: BaseRevision = BaseRevision::new();
|
||||
|
||||
#[used]
|
||||
#[unsafe(link_section = ".requests")]
|
||||
#[used] #[unsafe(link_section = ".requests")]
|
||||
static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
||||
|
||||
#[used]
|
||||
#[unsafe(link_section = ".requests_start_marker")]
|
||||
#[used] #[unsafe(link_section = ".requests_start_marker")]
|
||||
static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new();
|
||||
#[used]
|
||||
#[unsafe(link_section = ".requests_end_marker")]
|
||||
|
||||
#[used] #[unsafe(link_section = ".requests_end_marker")]
|
||||
static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn kmain() -> ! {
|
||||
assert!(BASE_REVISION.is_supported());
|
||||
|
||||
if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() {
|
||||
if let Some(fb) = framebuffer_response.framebuffers().next() {
|
||||
|
||||
// Инициализируем консоль
|
||||
let mut console = Console::new(FramebufferDisplay { framebuffer: &fb });
|
||||
console.clear();
|
||||
let fb_res = FRAMEBUFFER_REQUEST.get_response().expect("Limine: No Framebuffer");
|
||||
let mmap_res = MEMORY_MAP_REQUEST.get_response().expect("Limine: No Memory Map");
|
||||
let hhdm_res = HHDM_REQUEST.get_response().expect("Limine: No HHDM");
|
||||
let kaddr_res = KERNEL_ADDR_REQUEST.get_response().expect("Limine: No Kernel Address");
|
||||
let hhdm_offset = hhdm_res.offset();
|
||||
|
||||
let _ = writeln!(console, "RINA Kernel");
|
||||
let _ = writeln!(console, "-----------------------");
|
||||
let _ = writeln!(console, "Framebuffer: {}x{}", fb.width(), fb.height());
|
||||
let _ = writeln!(console, "Memory mapping... OK");
|
||||
let _ = writeln!(console, "Hello, Arina! Terminal mode active :3");
|
||||
|
||||
// Тест скроллинга: выведем много строк
|
||||
for i in 0..5 {
|
||||
let _ = writeln!(console, "System log entry #{}", i);
|
||||
}
|
||||
let fb = fb_res.framebuffers().next().expect("Limine: No active framebuffer found");
|
||||
let mut console = Console::new(FramebufferDisplay { framebuffer: &fb });
|
||||
console.clear();
|
||||
|
||||
info!(console, "BOOT", "RINA Kernel Starting...");
|
||||
|
||||
// Initialize Physical Memory Manager
|
||||
unsafe { mem::pmm::BitmapPMM::init(&mmap_res, hhdm_offset); }
|
||||
info!(console, "MEM", "Physical Memory Manager initialized.");
|
||||
|
||||
// Prepare Kernel Page Tables (P4)
|
||||
let p4_phys = mem::pmm::alloc_page().expect("OOM: Failed to allocate P4 table");
|
||||
let p4 = unsafe { &mut *p4_phys.to_virt(hhdm_offset).as_mut_ptr::<PageTable>() };
|
||||
unsafe { core::ptr::write_bytes(p4 as *mut _ as *mut u8, 0, 4096); }
|
||||
|
||||
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
|
||||
|
||||
// Mapping Strategy: HHDM + Identity Mapping for active segments
|
||||
for entry in mmap_res.entries() {
|
||||
let phys = PhysAddr(entry.base);
|
||||
let virt_hhdm = phys.to_virt(hhdm_offset);
|
||||
|
||||
p4.map_region(virt_hhdm, phys, entry.length, flags, hhdm_offset);
|
||||
|
||||
if entry.entry_type != limine::memory_map::EntryType::RESERVED {
|
||||
p4.map_region(VirtAddr(entry.base), phys, entry.length, flags, hhdm_offset);
|
||||
}
|
||||
}
|
||||
|
||||
hcf();
|
||||
}
|
||||
// Map the kernel itself based on provided executable addresses
|
||||
p4.map_region(
|
||||
VirtAddr(kaddr_res.virtual_base()),
|
||||
PhysAddr(kaddr_res.physical_base()),
|
||||
0x1000 * 1024, // 4MB default
|
||||
flags,
|
||||
hhdm_offset
|
||||
);
|
||||
|
||||
info!(console, "MMU", "Switching to Kernel Page Tables...");
|
||||
unsafe { p4.activate(p4_phys); }
|
||||
|
||||
// Initialize Global Heap
|
||||
let heap_start = 0xFFFF_9000_0000_0000;
|
||||
let heap_size = 8 * 1024 * 1024;
|
||||
|
||||
for i in (0..heap_size).step_by(4096) {
|
||||
let frame = mem::pmm::alloc_page().expect("OOM: Heap allocation failed");
|
||||
p4.map_page(VirtAddr(heap_start + i as u64), frame, flags, hhdm_offset);
|
||||
}
|
||||
|
||||
{
|
||||
let mut allocator = allocator::ALLOCATOR.lock();
|
||||
allocator.init(heap_start as usize, heap_size);
|
||||
}
|
||||
|
||||
info!(console, "HEAP", "Global Allocator is online.");
|
||||
|
||||
// Integrity Test
|
||||
let mut test_vec = Vec::new();
|
||||
test_vec.push(42);
|
||||
info!(console, "CORE", "Vec test passed. Pointer: {:?}", test_vec.as_ptr());
|
||||
|
||||
#[panic_handler]
|
||||
fn rust_panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
// В будущем тут будет выводиться текст ошибки
|
||||
hcf();
|
||||
}
|
||||
|
||||
fn hcf() -> ! {
|
||||
loop {
|
||||
unsafe {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
asm!("hlt");
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
asm!("wfi");
|
||||
#[cfg(target_arch = "loongarch64")]
|
||||
asm!("idle 0");
|
||||
}
|
||||
unsafe { asm!("hlt", options(nomem, nostack, preserves_flags)); }
|
||||
}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn rust_panic(info: &core::panic::PanicInfo) -> ! {
|
||||
let mut sp = unsafe { debug::serial::SerialPort::init() };
|
||||
let _ = writeln!(sp, "KERNEL PANIC: {:?}", info);
|
||||
hcf();
|
||||
}
|
||||
|
||||
36
kernel/src/mem/address.rs
Normal file
36
kernel/src/mem/address.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct PhysAddr(pub u64);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct VirtAddr(pub u64);
|
||||
|
||||
impl PhysAddr {
|
||||
/// Convert physical address to virtual via HHDM offset
|
||||
pub fn to_virt(self, hhdm_offset: u64) -> VirtAddr {
|
||||
VirtAddr(self.0 + hhdm_offset)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_aligned(self) -> bool { self.0 % 4096 == 0 }
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn align_down(self) -> Self { Self(self.0 & !0xFFF) }
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn align_up(self) -> Self { Self((self.0 + 4095) & !0xFFF) }
|
||||
}
|
||||
|
||||
impl VirtAddr {
|
||||
#[allow(dead_code)]
|
||||
pub fn to_phys(self, hhdm_offset: u64) -> Option<PhysAddr> {
|
||||
if self.0 < hhdm_offset { return None; }
|
||||
Some(PhysAddr(self.0 - hhdm_offset))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn as_ptr<T>(self) -> *const T { self.0 as *const T }
|
||||
|
||||
pub fn as_mut_ptr<T>(self) -> *mut T { self.0 as *mut T }
|
||||
}
|
||||
89
kernel/src/mem/allocator.rs
Normal file
89
kernel/src/mem/allocator.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use core::ptr::null_mut;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
pub struct Locked<A> {
|
||||
inner: core::cell::UnsafeCell<A>,
|
||||
lock: AtomicBool,
|
||||
}
|
||||
|
||||
pub struct LockedGuard<'a, A> {
|
||||
lock: &'a AtomicBool,
|
||||
data: &'a mut A,
|
||||
}
|
||||
|
||||
unsafe impl<A> Sync for Locked<A> {}
|
||||
|
||||
impl<A> Locked<A> {
|
||||
pub const fn new(inner: A) -> Self {
|
||||
Self {
|
||||
inner: core::cell::UnsafeCell::new(inner),
|
||||
lock: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> LockedGuard<'_, A> {
|
||||
while self.lock.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
LockedGuard {
|
||||
lock: &self.lock,
|
||||
data: unsafe { &mut *self.inner.get() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Drop for LockedGuard<'_, A> {
|
||||
fn drop(&mut self) {
|
||||
self.lock.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Deref for LockedGuard<'_, A> {
|
||||
type Target = A;
|
||||
fn deref(&self) -> &Self::Target { self.data }
|
||||
}
|
||||
|
||||
impl<A> DerefMut for LockedGuard<'_, A> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { self.data }
|
||||
}
|
||||
|
||||
pub struct BumpAllocator {
|
||||
start: usize,
|
||||
end: usize,
|
||||
next: usize,
|
||||
}
|
||||
|
||||
impl BumpAllocator {
|
||||
pub const fn new() -> Self {
|
||||
Self { start: 0, end: 0, next: 0 }
|
||||
}
|
||||
|
||||
pub fn init(&mut self, start: usize, size: usize) {
|
||||
self.start = start;
|
||||
self.next = start;
|
||||
self.end = start + size;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl GlobalAlloc for Locked<BumpAllocator> {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let mut bump = self.lock();
|
||||
|
||||
let alloc_start = (bump.next + layout.align() - 1) & !(layout.align() - 1);
|
||||
let alloc_end = alloc_start + layout.size();
|
||||
|
||||
if alloc_end > bump.end {
|
||||
null_mut()
|
||||
} else {
|
||||
bump.next = alloc_end;
|
||||
alloc_start as *mut u8
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
pub static ALLOCATOR: Locked<BumpAllocator> = Locked::new(BumpAllocator::new());
|
||||
19
kernel/src/mem/mod.rs
Normal file
19
kernel/src/mem/mod.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
pub mod pmm;
|
||||
pub mod address;
|
||||
pub mod paging;
|
||||
pub mod allocator;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn init(memmap: &limine::response::MemoryMapResponse, hhdm_offset: u64) {
|
||||
unsafe {
|
||||
pmm::BitmapPMM::init(memmap, hhdm_offset);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_stats() -> (usize, usize) {
|
||||
unsafe {
|
||||
let pmm = pmm::get_pmm_unchecked();
|
||||
(pmm.used_pages(), pmm.total_pages())
|
||||
}
|
||||
}
|
||||
77
kernel/src/mem/paging.rs
Normal file
77
kernel/src/mem/paging.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use crate::mem::address::{PhysAddr, VirtAddr};
|
||||
use crate::mem::pmm;
|
||||
use core::arch::asm;
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PageTableFlags: u64 {
|
||||
const PRESENT = 1 << 0;
|
||||
const WRITABLE = 1 << 1;
|
||||
const USER = 1 << 2;
|
||||
const WRITE_THROUGH = 1 << 3;
|
||||
const NO_CACHE = 1 << 4;
|
||||
const ACCESSED = 1 << 5;
|
||||
const DIRTY = 1 << 6;
|
||||
const HUGE_PAGE = 1 << 7;
|
||||
const GLOBAL = 1 << 8;
|
||||
const NO_EXECUTE = 1 << 63;
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(4096))]
|
||||
pub struct PageTable {
|
||||
entries: [u64; 512],
|
||||
}
|
||||
|
||||
impl PageTable {
|
||||
pub fn map_region(&mut self, virt: VirtAddr, phys: PhysAddr, size: u64, flags: PageTableFlags, hhdm: u64) {
|
||||
let pages = size.div_ceil(4096);
|
||||
for i in 0..pages {
|
||||
let offset = i * 4096;
|
||||
self.map_page(VirtAddr(virt.0 + offset), PhysAddr(phys.0 + offset), flags, hhdm);
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads the page table into the CR3 register
|
||||
pub unsafe fn activate(&self, phys_addr: PhysAddr) {
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov cr3, {0}",
|
||||
"jmp 2f",
|
||||
"2:",
|
||||
in(reg) phys_addr.0,
|
||||
options(nostack, preserves_flags)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_page(&mut self, virt: VirtAddr, phys: PhysAddr, flags: PageTableFlags, hhdm_offset: u64) {
|
||||
let p4_idx = (virt.0 >> 39) & 0x1FF;
|
||||
let p3_idx = (virt.0 >> 30) & 0x1FF;
|
||||
let p2_idx = (virt.0 >> 21) & 0x1FF;
|
||||
let p1_idx = (virt.0 >> 12) & 0x1FF;
|
||||
|
||||
let p3 = self.get_or_create_next_table(p4_idx as usize, hhdm_offset);
|
||||
let p2 = p3.get_or_create_next_table(p3_idx as usize, hhdm_offset);
|
||||
let p1 = p2.get_or_create_next_table(p2_idx as usize, hhdm_offset);
|
||||
|
||||
p1.entries[p1_idx as usize] = phys.0 | flags.bits();
|
||||
|
||||
unsafe {
|
||||
asm!("invlpg [{}]", in(reg) virt.0, options(nostack, preserves_flags));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_create_next_table(&mut self, index: usize, hhdm: u64) -> &mut Self {
|
||||
if self.entries[index] & PageTableFlags::PRESENT.bits() == 0 {
|
||||
let pt_phys = pmm::alloc_page().expect("VMM: Table allocation failed");
|
||||
let pt_virt = pt_phys.to_virt(hhdm);
|
||||
unsafe { core::ptr::write_bytes(pt_virt.as_mut_ptr::<u8>(), 0, 4096); }
|
||||
|
||||
self.entries[index] = pt_phys.0 | (PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER).bits();
|
||||
}
|
||||
let next_pt_phys = PhysAddr(self.entries[index] & 0x000F_FFFF_FFFF_F000);
|
||||
unsafe { &mut *next_pt_phys.to_virt(hhdm).as_mut_ptr() }
|
||||
}
|
||||
}
|
||||
128
kernel/src/mem/pmm.rs
Normal file
128
kernel/src/mem/pmm.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
use crate::mem::address::{PhysAddr};
|
||||
|
||||
pub const PAGE_SIZE: u64 = 4096;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct BitmapPMM {
|
||||
bitmap: &'static mut [u8],
|
||||
total_pages: usize,
|
||||
used_pages: usize,
|
||||
last_idx: usize,
|
||||
}
|
||||
|
||||
static mut PMM: Option<BitmapPMM> = None;
|
||||
|
||||
impl BitmapPMM {
|
||||
#[allow(dead_code)]
|
||||
pub fn used_pages(&self) -> usize { self.used_pages }
|
||||
#[allow(dead_code)]
|
||||
pub fn total_pages(&self) -> usize { self.total_pages }
|
||||
|
||||
pub unsafe fn init(mmap: &limine::response::MemoryMapResponse, hhdm_offset: u64) {
|
||||
let max_addr = mmap.entries().iter()
|
||||
.map(|e| e.base + e.length)
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
let total_pages = (max_addr / PAGE_SIZE) as usize;
|
||||
let bitmap_size = total_pages.div_ceil(8);
|
||||
|
||||
let bitmap_phys_addr = mmap.entries().iter()
|
||||
.find(|e| e.entry_type == limine::memory_map::EntryType::USABLE && e.length >= bitmap_size as u64)
|
||||
.map(|e| e.base)
|
||||
.expect("PMM: Insufficient memory for bitmap");
|
||||
|
||||
let bitmap_virt_ptr = (bitmap_phys_addr + hhdm_offset) as *mut u8;
|
||||
// Оборачиваем небезопасные операции
|
||||
let bitmap_slice = unsafe { core::slice::from_raw_parts_mut(bitmap_virt_ptr, bitmap_size) };
|
||||
bitmap_slice.fill(0xFF);
|
||||
|
||||
let mut pmm = Self {
|
||||
bitmap: bitmap_slice,
|
||||
total_pages,
|
||||
used_pages: total_pages,
|
||||
last_idx: 0,
|
||||
};
|
||||
|
||||
for entry in mmap.entries() {
|
||||
if entry.entry_type == limine::memory_map::EntryType::USABLE {
|
||||
for addr in (entry.base..entry.base + entry.length).step_by(PAGE_SIZE as usize) {
|
||||
pmm.free_frame(PhysAddr(addr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for addr in (bitmap_phys_addr..bitmap_phys_addr + bitmap_size as u64).step_by(PAGE_SIZE as usize) {
|
||||
pmm.lock_frame(PhysAddr(addr));
|
||||
}
|
||||
|
||||
pmm.lock_frame(PhysAddr(0));
|
||||
|
||||
unsafe {
|
||||
core::ptr::write(core::ptr::addr_of_mut!(PMM), Some(pmm));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn free_frame(&mut self, phys_addr: PhysAddr) {
|
||||
let idx = (phys_addr.0 / PAGE_SIZE) as usize;
|
||||
if idx < self.total_pages {
|
||||
let byte_idx = idx / 8;
|
||||
let bit_idx = idx % 8;
|
||||
if (self.bitmap[byte_idx] & (1 << bit_idx)) != 0 {
|
||||
self.bitmap[byte_idx] &= !(1 << bit_idx);
|
||||
self.used_pages -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock_frame(&mut self, phys_addr: PhysAddr) {
|
||||
let idx = (phys_addr.0 / PAGE_SIZE) as usize;
|
||||
if idx < self.total_pages {
|
||||
let byte_idx = idx / 8;
|
||||
let bit_idx = idx % 8;
|
||||
if (self.bitmap[byte_idx] & (1 << bit_idx)) == 0 {
|
||||
self.bitmap[byte_idx] |= 1 << bit_idx;
|
||||
self.used_pages += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc_frame(&mut self) -> Option<PhysAddr> {
|
||||
for i in self.last_idx..self.total_pages {
|
||||
let byte_idx = i / 8;
|
||||
if self.bitmap[byte_idx] == 0xFF { continue; }
|
||||
|
||||
let bit_idx = i % 8;
|
||||
if (self.bitmap[byte_idx] & (1 << bit_idx)) == 0 {
|
||||
let addr = PhysAddr(i as u64 * PAGE_SIZE);
|
||||
self.lock_frame(addr);
|
||||
self.last_idx = i;
|
||||
return Some(addr);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn get_pmm_unchecked() -> &'static BitmapPMM {
|
||||
let pmm_ptr = core::ptr::addr_of!(PMM);
|
||||
unsafe { (*pmm_ptr).as_ref().expect("PMM: Not initialized") }
|
||||
}
|
||||
|
||||
pub fn alloc_page() -> Option<PhysAddr> {
|
||||
unsafe {
|
||||
let pmm_ptr = core::ptr::addr_of_mut!(PMM);
|
||||
(*pmm_ptr).as_mut()?.alloc_frame()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn free_page(addr: PhysAddr) {
|
||||
unsafe {
|
||||
let pmm_ptr = core::ptr::addr_of_mut!(PMM);
|
||||
if let Some(pmm) = (*pmm_ptr).as_mut() {
|
||||
pmm.free_frame(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user