Merge pull request #4 from ParrotXray/feat/mm

feat: Implement higher half kernel with complete memory management
This commit is contained in:
ParrotXray 2025-10-13 20:14:04 +08:00 committed by GitHub
commit 10433a4fb7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 1413 additions and 154 deletions

View File

@ -7,16 +7,38 @@
// - 滾動 - OK
// 2. 內存管理
// - 物理內存分配器
// - 虛擬內存分配器
// - 堆分配器
// - 物理內存分配器 - OK
// - 虛擬內存分配器 - OK
// - 堆分配器 - OK
// 3. 頁表管理
// - 修改頁表
// - 映射新的內存區域
// - 取消映射
// - 修改頁表 - OK
// - 映射新的內存區域 - OK
// - 取消映射 - OK
// 4. 進程管理
// - 任務切換
// - 調度器
// - 用戶態/內核態切換
// - 用戶態/內核態切換
// ==================== 高半核地址空間佈局 ====================
//
// 0x0000_0000_0000 ┌─────────────────────┐
// │ 用戶空間 │
// │ (0 - 128 TiB) │
// 0x0000_7FFF_FFFF ├─────────────────────┤
// │ (不可訪問區) │
// 0xFFFF_8000_0000 ├─────────────────────┤ ← 高半核開始
// │ 物理記憶體直接映射 │
// 0xFFFF_C000_0000 ├─────────────────────┤ ← 內核空間開始
// │ 內核代碼/數據 │
// 0xFFFF_C000_1000 ├─────────────────────┤
// │ 內核堆 │
// 0xFFFF_C000_2000 ├─────────────────────┤
// │ 動態分配區 │
// 0xFFFF_D000_0000 ├─────────────────────┤
// │ 內核棧區 │
// 0xFFFF_E000_0000 ├─────────────────────┤
// │ 設備映射區 (MMIO) │
// 0xFFFF_FFFF_FFFF └─────────────────────┘

View File

@ -104,6 +104,8 @@ pub fn kernel_data_selector() -> SegmentSelector {
// 初始化 GDT
pub fn init() {
GDT.0.load();
let gdt_addr = &GDT.0 as *const _ as u64;
log_debug!("GDT address: {:#018x}", gdt_addr);
unsafe {
CS::set_reg(GDT.1.kernel_code_selector);

View File

@ -1,8 +1,10 @@
// kernel/src/kernel/asm/amd64/isr
use x86_64::structures::idt::{InterruptStackFrame, PageFaultErrorCode};
use x86_64::VirtAddr;
use crate::kprintln;
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
use crate::hal::cpu;
use crate::mm::paging;
/// Divide Error (#DE)
pub extern "x86-interrupt" fn divide_error_handler(stack_frame: InterruptStackFrame) {
@ -10,7 +12,7 @@ pub extern "x86-interrupt" fn divide_error_handler(stack_frame: InterruptStackFr
log_error!("EXCEPTION: DIVIDE ERROR (#DE)");
log_error!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -41,7 +43,7 @@ pub extern "x86-interrupt" fn overflow_handler(stack_frame: InterruptStackFrame)
log_error!("EXCEPTION: OVERFLOW (#OF)");
log_error!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -51,7 +53,7 @@ pub extern "x86-interrupt" fn bound_range_handler(stack_frame: InterruptStackFra
log_error!("EXCEPTION: BOUND RANGE EXCEEDED (#BR)");
log_error!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -61,7 +63,7 @@ pub extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: InterruptStack
log_error!("EXCEPTION: INVALID OPCODE (#UD)");
log_error!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -71,7 +73,7 @@ pub extern "x86-interrupt" fn device_not_available_handler(stack_frame: Interrup
log_error!("EXCEPTION: DEVICE NOT AVAILABLE (#NM)");
log_error!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -97,7 +99,7 @@ pub extern "x86-interrupt" fn invalid_tss_handler(
log_fatal!("Error Code: {:#x}", error_code);
log_fatal!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -111,7 +113,7 @@ pub extern "x86-interrupt" fn segment_not_present_handler(
log_fatal!("Error Code: {:#x}", error_code);
log_fatal!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -125,7 +127,7 @@ pub extern "x86-interrupt" fn stack_segment_fault_handler(
log_fatal!("Error Code: {:#x}", error_code);
log_fatal!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -139,7 +141,7 @@ pub extern "x86-interrupt" fn general_protection_fault_handler(
log_fatal!("Error Code: {:#x}", error_code);
log_fatal!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -148,7 +150,6 @@ pub extern "x86-interrupt" fn page_fault_handler(
stack_frame: InterruptStackFrame,
error_code: PageFaultErrorCode,
) {
use x86_64::registers::control::Cr2;
kprintln!();
log_fatal!("EXCEPTION: PAGE FAULT (#PF)");
@ -160,8 +161,14 @@ pub extern "x86-interrupt" fn page_fault_handler(
log_fatal!("Reserved Write: {}", error_code.contains(PageFaultErrorCode::MALFORMED_TABLE));
log_fatal!("Instruction Fetch: {}", error_code.contains(PageFaultErrorCode::INSTRUCTION_FETCH));
log_fatal!("{:#?}", stack_frame);
paging::handle_page_fault(
VirtAddr::new(cpu::cpu_r_cr2()),
error_code.bits()
);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -171,7 +178,7 @@ pub extern "x86-interrupt" fn x87_floating_point_handler(stack_frame: InterruptS
log_error!("EXCEPTION: x87 FLOATING POINT (#MF)");
log_error!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -185,7 +192,7 @@ pub extern "x86-interrupt" fn alignment_check_handler(
log_error!("Error Code: {:#x}", error_code);
log_error!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -203,7 +210,7 @@ pub extern "x86-interrupt" fn simd_floating_point_handler(stack_frame: Interrupt
kprintln!("EXCEPTION: SIMD FLOATING POINT (#XM/#XF)");
kprintln!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}
@ -213,7 +220,7 @@ pub extern "x86-interrupt" fn virtualization_handler(stack_frame: InterruptStack
log_warn!("EXCEPTION: VIRTUALIZATION (#VE)");
log_warn!("{:#?}", stack_frame);
loop {
crate::hal::cpu::cpu_halt();
cpu::cpu_halt();
}
}

View File

@ -2,20 +2,21 @@
use x86_64::registers::control::{Cr0, Cr0Flags, Cr2, Cr3, Cr4, Cr4Flags};
use x86_64::instructions::{interrupts, hlt};
use core::arch::asm;
use raw_cpuid::CpuId;
use x86_64::{PhysAddr, structures::paging::PhysFrame};
use x86_64::VirtAddr;
/// 64 位元暫存器類型
/// 64-bit register type
#[allow(dead_code)]
pub type Reg64 = u64;
/// 32 位元暫存器類型
/// 32-bit register type
#[allow(dead_code)]
pub type Reg32 = u32;
/// 16 位元暫存器類型
/// 16-bit register type
#[allow(dead_code)]
pub type Reg16 = u16;
/// 通用目的暫存器結構 (64-bit)
/// General purpose register structure (64-bit)
#[allow(dead_code)]
#[repr(C, packed)]
pub struct GpRegs {
@ -37,7 +38,7 @@ pub struct GpRegs {
pub r15: Reg64,
}
/// 段暫存器結構
/// Segment register structure
#[allow(dead_code)]
#[repr(C, packed)]
pub struct SgReg {
@ -49,21 +50,21 @@ pub struct SgReg {
pub cs: Reg16,
}
/// 讀取 CR0 暫存器
/// Read CR0 register
#[allow(dead_code)]
#[inline]
pub fn cpu_r_cr0() -> u64 {
Cr0::read_raw()
}
/// 讀取 CR2 暫存器
/// Read CR2 register
#[allow(dead_code)]
#[inline]
pub fn cpu_r_cr2() -> u64 {
Cr2::read_raw()
}
/// 讀取 CR3 暫存器
/// Read CR3 register
#[allow(dead_code)]
#[inline]
pub fn cpu_r_cr3_frame() -> PhysFrame {
@ -89,21 +90,21 @@ pub fn cpu_r_cr3_addr() -> PhysAddr {
}
/// 讀取 CR4 暫存器
/// Read CR4 register
#[allow(dead_code)]
#[inline]
pub fn cpu_r_cr4() -> u64 {
Cr4::read_raw()
}
/// 寫入 CR0 暫存器
/// Write to CR0 register
#[allow(dead_code)]
#[inline]
pub fn cpu_w_cr0(val: Cr0Flags) {
unsafe { Cr0::write(val); }
}
/// 寫入 CR3 暫存器
/// Write to CR3 register
#[allow(dead_code)]
#[inline]
pub fn cpu_w_cr3(val: u64) {
@ -113,29 +114,25 @@ pub fn cpu_w_cr3(val: u64) {
}
}
/// 寫入 CR4 暫存器
/// Write to CR4 register
#[allow(dead_code)]
#[inline]
pub fn cpu_w_cr4(val: Cr4Flags) {
unsafe { Cr4::write(val); }
}
// ============ CPU 資訊 ============
/// 獲取 CPU 供應商 ID
/// Get the CPU vendor ID
///
/// # 參數
/// * `model_out` - 輸出緩衝區,至少需要 13 bytes
/// # 返回
/// 字符串切片,表示 CPU 供應商資訊
/// # Parameters
/// * `model_out` - Output buffer, requires at least 13 bytes
/// # Returns
/// A string slice representing the CPU vendor information
#[allow(dead_code)]
pub fn cpu_get_model(model_out: &mut [u8]) -> &str {
if model_out.len() < 13 {
return "Buffer too small";
}
// 使用 raw_cpuid crate
use raw_cpuid::CpuId;
let cpuid = CpuId::new();
if let Some(vendor) = cpuid.get_vendor_info() {
@ -157,28 +154,25 @@ pub fn cpu_get_model(model_out: &mut [u8]) -> &str {
}
}
/// 檢查是否支持品牌字串
/// Check if brand string is supported
#[allow(dead_code)]
pub fn cpu_brand_string_supported() -> bool {
use raw_cpuid::CpuId;
let cpuid = CpuId::new();
cpuid.get_processor_brand_string().is_some()
}
/// 獲取 CPU 品牌字串
/// Get the CPU brand string
///
/// # 參數
/// * `brand_out` - 輸出緩衝區,至少需要 49 bytes
/// # Parameters
/// * `brand_out` - Output buffer, requires at least 49 bytes
///
/// # 返回
/// 字符串切片,表示 CPU 品牌
/// # Returns
/// A string slice representing the CPU brand
#[allow(dead_code)]
pub fn cpu_get_brand(brand_out: &mut [u8]) -> &str {
if brand_out.len() < 49 {
return "Buffer too small";
}
use raw_cpuid::CpuId;
let cpuid = CpuId::new();
if let Some(brand) = cpuid.get_processor_brand_string() {
@ -197,10 +191,10 @@ pub fn cpu_get_brand(brand_out: &mut [u8]) -> &str {
}
}
/// 讀取 CPU 時間戳計數器 (TSC)
/// Read the CPU Time Stamp Counter (TSC)
///
/// # 返回
/// 時間戳計數值
/// # Return
/// The timestamp count value
#[allow(dead_code)]
#[inline]
pub fn cpu_rdtsc() -> u64 {
@ -217,57 +211,57 @@ pub fn cpu_rdtsc() -> u64 {
}
}
/// 執行 CPU 暫停指令 (減少功耗)
/// Execute CPU pause instruction (reduce power consumption)
#[allow(dead_code)]
#[inline]
pub fn cpu_pause() {
core::hint::spin_loop();
}
/// 停止 CPU 執行,直到下一個中斷發生
/// Stop CPU execution until the next interrupt occurs
#[allow(dead_code)]
#[inline]
pub fn cpu_halt() {
hlt();
}
/// 停止 CPU 並進入低功耗模式 (等同於 cpu_halt)
/// Stop the CPU and enter low power mode (equivalent to cpu_halt)
#[allow(dead_code)]
#[inline]
pub fn cpu_idle() {
hlt();
}
/// 啟用中斷
/// Enable interrupts
#[allow(dead_code)]
#[inline]
pub fn cpu_enable_interrupts() {
interrupts::enable();
}
/// 禁用中斷
/// Disable interrupts
#[allow(dead_code)]
#[inline]
pub fn cpu_disable_interrupts() {
interrupts::disable();
}
/// 檢查中斷是否啟用
/// Check if interrupts are enabled
#[allow(dead_code)]
#[inline]
pub fn cpu_interrupts_enabled() -> bool {
interrupts::are_enabled()
}
/// 在禁用中斷的情況下執行閉包
/// Execute closure with interrupts disabled
///
/// # 範例
/// # Example
/// ```
/// cpu_without_interrupts(|| {
/// // 臨界區代碼
/// // 中斷被禁用
/// // Critical section code
/// // Interrupts disabled
/// });
/// // 中斷恢復到之前的狀態
/// // Restore interrupts to their previous state
/// ```
#[allow(dead_code)]
#[inline]
@ -278,14 +272,14 @@ where
interrupts::without_interrupts(f)
}
/// 無條件觸發斷點異常 (用於調試)
/// Unconditionally trigger a breakpoint exception (for debugging)
#[allow(dead_code)]
#[inline]
pub fn cpu_breakpoint() {
x86_64::instructions::interrupts::int3();
}
/// 讀取 RFLAGS 寄存器
/// Read the RFLAGS register
#[allow(dead_code)]
#[inline]
pub fn cpu_read_flags() -> u64 {

View File

@ -1,11 +1,11 @@
// kernel/src/hal/io.rs
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
/// 向 I/O 端口寫入一個字節
/// Write a byte to an I/O port
///
/// # 參數
/// * `port` - 端口號
/// * `value` - 要寫入的值
/// # Parameters
/// * `port` - Port number
/// * `value` - Value to write
#[allow(dead_code)]
#[inline]
pub fn io_port_wb(port: u16, value: u8) {
@ -14,12 +14,12 @@ pub fn io_port_wb(port: u16, value: u8) {
}
}
/// 從 I/O 端口讀取一個字節
/// Read a byte from an I/O port
///
/// # 參數
/// * `port` - 端口號
/// # 返回
/// 讀取到的值
/// # Parameters
/// * `port` - port number
/// # Returns
/// The value read
#[allow(dead_code)]
#[inline]
pub fn io_port_rb(port: u16) -> u8 {
@ -28,11 +28,11 @@ pub fn io_port_rb(port: u16) -> u8 {
}
}
/// 向 I/O 端口寫入一個字 (16-bit)
/// Write a word (16-bit) to an I/O port
///
/// # 參數
/// * `port` - 端口號
/// * `value` - 要寫入的值
/// # Parameters
/// * `port` - Port number
/// * `value` - Value to be written
#[allow(dead_code)]
#[inline]
pub fn io_port_ww(port: u16, value: u16) {
@ -41,12 +41,12 @@ pub fn io_port_ww(port: u16, value: u16) {
}
}
/// 從 I/O 端口讀取一個字 (16-bit)
/// Read a word (16-bit) from an I/O port
///
/// # 參數
/// * `port` - 端口號
/// # 返回
/// 讀取到的值
/// # Parameters
/// * `port` - port number
/// # Returns
/// The value read
#[allow(dead_code)]
#[inline]
pub fn io_port_rw(port: u16) -> u16 {
@ -55,11 +55,11 @@ pub fn io_port_rw(port: u16) -> u16 {
}
}
/// 向 I/O 端口寫入一個雙字 (32-bit)
/// Write a double word (32-bit) to an I/O port
///
/// # 參數
/// * `port` - 端口號
/// * `value` - 要寫入的值
/// # Parameters
/// * `port` - Port number
/// * `value` - Value to be written
#[allow(dead_code)]
#[inline]
pub fn io_port_wl(port: u16, value: u32) {
@ -68,12 +68,12 @@ pub fn io_port_wl(port: u16, value: u32) {
}
}
/// 從 I/O 端口讀取一個雙字 (32-bit)
/// Read a double word (32-bit) from an I/O port
///
/// # 參數
/// * `port` - 端口號
/// # 返回
/// 讀取到的值
/// # Parameters
/// * `port` - port number
/// # Returns
/// The value read
#[allow(dead_code)]
#[inline]
pub fn io_port_rl(port: u16) -> u32 {

View File

@ -1,18 +1,20 @@
// kernel/src/k_init.rs (Updated with Logger)
use bootloader_api::BootInfo;
use x86_64::structures::paging::OffsetPageTable;
use x86_64::VirtAddr;
use bootloader_api::info::MemoryRegionKind;
use x86_64::structures::paging::{OffsetPageTable, Page };
use x86_64::{PhysAddr, VirtAddr};
use crate::mm::{allocator::{heap, frame, pmm}, vmm, paging, vma};
use crate::arch::amd64::{gdt, idt};
use crate::mm::allocator::{heap, frame};
use crate::tty::tty;
use crate::kprintln;
use crate::kernel::k_main;
use crate::libs::logger::{init as logger_init, LogLevel, LoggerConfig};
use crate::klibc::logger::{init, LogLevel, LoggerConfig};
use crate::klibc::malloc;
use crate::{log_debug, log_error, log_info, log_trace, log_warn};
use crate::hal::acpi;
fn _logger_init() {
logger_init(
init(
LoggerConfig::new()
.with_level(LogLevel::Trace)
.with_location(true)
@ -40,9 +42,50 @@ fn _memory_init(
frame::BootInfoFrameAllocator::init(memory_regions)
};
heap::init_heap(&mut mapper, &mut frame_allocator)
heap::init(&mut mapper, &mut frame_allocator)
.expect("Heap initialization failed");
unsafe {
let mut usable_start = u64::MAX;
let mut usable_end = 0u64;
let mut total_usable = 0u64;
for region in memory_regions.iter() {
if region.kind == bootloader_api::info::MemoryRegionKind::Usable {
usable_start = usable_start.min(region.start);
usable_end = usable_end.max(region.end);
total_usable += region.end - region.start;
}
}
let bitmap_size = ((usable_end - usable_start) / 4096 + 7) / 8;
let bitmap_pages = (bitmap_size as usize + 4095) / 4096;
if let Some(bitmap_addr) = malloc::kmalloc(
bitmap_pages * 4096,
&mut mapper,
&mut frame_allocator
) {
pmm::init_pmm(
PhysAddr::new(usable_start),
total_usable as usize,
bitmap_addr.as_mut_ptr()
);
for region in memory_regions.iter() {
if region.kind != bootloader_api::info::MemoryRegionKind::Usable {
pmm::mark_region_used(
PhysAddr::new(region.start),
(region.end - region.start) as usize
);
}
}
} else {
log_error!("Failed to allocate PMM bitmap!");
}
}
(mapper, frame_allocator)
}
@ -70,7 +113,6 @@ fn _boot_report(memory_regions: &bootloader_api::info::MemoryRegions, physical_m
log_debug!("Memory Regions:");
let mut total_usable = 0u64;
for region in memory_regions.iter() {
use bootloader_api::info::MemoryRegionKind;
let kind_str = match region.kind {
MemoryRegionKind::Usable => {
total_usable += region.end - region.start;
@ -87,8 +129,13 @@ fn _boot_report(memory_regions: &bootloader_api::info::MemoryRegions, physical_m
log_info!("Total Usable Memory: {} MiB", total_usable / (1024 * 1024));
kprintln!();
log_info!("Heap Start: {:#x}", heap::HEAP_START);
log_info!("Heap Size: {} KiB", heap::HEAP_SIZE / 1024);
log_info!("Heap Start: {:#x}", vma::HEAP_START);
log_info!("Heap Size: {} KiB", vma::HEAP_SIZE / 1024);
if let Some(stats) = pmm::get_memory_stats() {
log_info!("Total Memory: {} MiB", stats.total_memory / (1024 * 1024));
log_info!("Free Memory: {} MiB", stats.free_memory / (1024 * 1024));
}
}
fn _acpi_init(rsdp_addr: Option<u64>, physical_memory_offset: u64) {
@ -116,6 +163,9 @@ fn _post_init() {
pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
if let Some(framebuffer) = boot_info.framebuffer.as_mut() {
_display_init(framebuffer);
_logger_init();
_critical_init();
@ -126,21 +176,19 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
let rsdp_addr = boot_info.rsdp_addr.into_option();
let (_mapper, _frame_allocator) = _memory_init(
let (mut mapper, mut frame_allocator) = _memory_init(
&boot_info.memory_regions,
physical_memory_offset
);
_display_init(framebuffer);
_logger_init();
_boot_report(&boot_info.memory_regions, physical_memory_offset);
_acpi_init(rsdp_addr, physical_memory_offset);
_post_init();
_test_memory_management(&mut mapper, &mut frame_allocator);
kprintln!();
kprintln!("========================================");
kprintln!(" Kernel Initialization Complete! ");
@ -159,4 +207,67 @@ pub fn kernel_emergency_cleanup() {
log_error!("Emergency cleanup triggered");
// 在 panic 前調用,做最後的清理工作
// 比如刷新緩衝區、保存日誌等
}
fn _test_memory_management(
mapper: &mut OffsetPageTable,
frame_allocator: &mut frame::BootInfoFrameAllocator
) {
kprintln!();
log_info!("Testing Memory Management System...");
// Physical memory allocation
log_debug!("Test 1: Physical frame allocation");
if let Some(frame) = pmm::allocate_frame() {
log_debug!(" Allocated frame at: {:#x}", frame.start_address().as_u64());
pmm::deallocate_frame(frame);
log_debug!(" Deallocated frame");
}
// Virtual memory allocation
log_debug!("Test 2: Virtual memory allocation (kmalloc)");
if let Some(vaddr) = malloc::kmalloc(8192, mapper, frame_allocator) {
log_debug!(" Allocated 8KB at: {:#x}", vaddr.as_u64());
// Test Write
unsafe {
let ptr = vaddr.as_mut_ptr::<u64>();
*ptr = 0xDEADBEEF;
log_debug!(" Written test value: {:#x}", *ptr);
}
malloc::kfree(vaddr, 8192, mapper, frame_allocator);
log_debug!(" Freed memory");
}
// Page table mapping
log_debug!("Test 3: Page table mapping");
let test_vaddr = VirtAddr::new(0x5000_0000_0000);
let test_page = Page::containing_address(test_vaddr);
if let Some(test_frame) = pmm::allocate_frame() {
if paging::PageTableManager::map_page(
test_page,
test_frame,
paging::kernel_data(),
mapper,
frame_allocator
).is_ok() {
log_debug!(" Mapped page {:#x} to frame {:#x}",
test_vaddr.as_u64(), test_frame.start_address().as_u64());
// Testing Address Translation
if let Some(phys) = paging::PageTableManager::translate_addr(test_vaddr, mapper) {
log_debug!(" Translation check: {:#x} -> {:#x}", test_vaddr.as_u64(), phys.as_u64());
}
// Unmap
if paging::PageTableManager::unmap_page(test_page, mapper).is_ok() {
log_debug!(" Unmapped page");
}
}
pmm::deallocate_frame(test_frame);
}
log_info!("Memory tests completed!");
}

View File

@ -2,6 +2,7 @@
use crate::hal::cpu;
use crate::kprintln;
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
use crate::mm::{vma, vmm};
pub fn _kernel_main() -> ! {
kprintln!();
@ -21,6 +22,10 @@ pub fn _kernel_main() -> ! {
log_debug!("CR3: 0x{:016x}", cpu::cpu_r_cr3());
log_debug!("CR4: 0x{:016x}", cpu::cpu_r_cr4());
vma::print_info();
let vmm_stats = vmm::get_vmm_stats();
vmm_stats.print();
// kprintln!();
// kprintln!("=== Logger Level Demonstration ===");
// kprintln!();

View File

@ -157,8 +157,8 @@ fn fmt_to_buffer<'a>(buf: &'a mut [u8], args: fmt::Arguments) -> Result<&'a str,
#[macro_export]
macro_rules! log_trace {
($($arg:tt)*) => {
$crate::libs::logger::_log(
$crate::libs::logger::LogLevel::Trace,
$crate::klibc::logger::_log(
$crate::klibc::logger::LogLevel::Trace,
format_args!($($arg)*),
Some(file!()),
Some(line!())
@ -170,8 +170,8 @@ macro_rules! log_trace {
#[macro_export]
macro_rules! log_debug {
($($arg:tt)*) => {
$crate::libs::logger::_log(
$crate::libs::logger::LogLevel::Debug,
$crate::klibc::logger::_log(
$crate::klibc::logger::LogLevel::Debug,
format_args!($($arg)*),
Some(file!()),
Some(line!())
@ -183,8 +183,8 @@ macro_rules! log_debug {
#[macro_export]
macro_rules! log_info {
($($arg:tt)*) => {
$crate::libs::logger::_log(
$crate::libs::logger::LogLevel::Info,
$crate::klibc::logger::_log(
$crate::klibc::logger::LogLevel::Info,
format_args!($($arg)*),
Some(file!()),
Some(line!())
@ -196,8 +196,8 @@ macro_rules! log_info {
#[macro_export]
macro_rules! log_warn {
($($arg:tt)*) => {
$crate::libs::logger::_log(
$crate::libs::logger::LogLevel::Warn,
$crate::klibc::logger::_log(
$crate::klibc::logger::LogLevel::Warn,
format_args!($($arg)*),
Some(file!()),
Some(line!())
@ -209,8 +209,8 @@ macro_rules! log_warn {
#[macro_export]
macro_rules! log_error {
($($arg:tt)*) => {
$crate::libs::logger::_log(
$crate::libs::logger::LogLevel::Error,
$crate::klibc::logger::_log(
$crate::klibc::logger::LogLevel::Error,
format_args!($($arg)*),
Some(file!()),
Some(line!())
@ -222,8 +222,8 @@ macro_rules! log_error {
#[macro_export]
macro_rules! log_fatal {
($($arg:tt)*) => {
$crate::libs::logger::_log(
$crate::libs::logger::LogLevel::Fatal,
$crate::klibc::logger::_log(
$crate::klibc::logger::LogLevel::Fatal,
format_args!($($arg)*),
Some(file!()),
Some(line!())

View File

@ -0,0 +1,74 @@
use x86_64::structures::paging::{FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, Size4KiB};
use x86_64::VirtAddr;
use crate::mm::vmm;
use crate::mm::vmm::VMM;
/// Allocate and map kernel memory
pub fn kmalloc<A>(
size: usize,
mapper: &mut OffsetPageTable,
frame_allocator: &mut A,
) -> Option<VirtAddr>
where
A: FrameAllocator<Size4KiB>,
{
if size == 0 {
return None;
}
let page_count = (size + 4095) / 4096;
let vaddr = vmm::VMM.lock().allocate_pages(page_count)?;
let start_page = Page::containing_address(vaddr);
for i in 0..page_count {
let page = start_page + i as u64;
let frame = frame_allocator.allocate_frame()?;
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
unsafe {
mapper
.map_to(page, frame, flags, frame_allocator)
.ok()?
.flush();
}
}
Some(vaddr)
}
/// Release kernel memory
///
/// # Important
/// You must pass in the correct size; it should be the same size as when using kmalloc.
pub fn kfree<A>(
addr: VirtAddr,
size: usize,
mapper: &mut OffsetPageTable,
_frame_allocator: &mut A,
)
where
A: FrameAllocator<Size4KiB>,
{
if size == 0 {
return;
}
let page_count = (size + 4095) / 4096;
let start_page: Page::<Size4KiB> = Page::containing_address(addr);
// Unmap page tables
for i in 0..page_count {
let page = start_page + i as u64;
if let Ok((frame, flush)) = mapper.unmap(page) {
flush.flush();
// Release the frame back to PMM
crate::mm::allocator::pmm::deallocate_frame(frame);
}
}
// Freeing virtual address space
VMM.lock().deallocate_pages(addr, page_count);
}

View File

@ -1,3 +1,4 @@
pub mod print;
pub mod logger;
pub mod string;
pub mod string;
pub mod malloc;

View File

@ -1,4 +1,4 @@
// src/libs/libc/print.rs
// src/klibc/libc/print.rs
use core::fmt;
use crate::tty::tty;
@ -19,15 +19,15 @@ pub fn _print(args: fmt::Arguments) {
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::libs::print::_print(format_args!($($arg)*)));
($($arg:tt)*) => ($crate::klibc::print::_print(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! kprintln {
() => {
$crate::libs::print::_print(format_args!("\n"))
$crate::klibc::print::_print(format_args!("\n"))
};
($($arg:tt)*) => {
$crate::libs::print::_print(format_args!("{}\n", format_args!($($arg)*)))
$crate::klibc::print::_print(format_args!("{}\n", format_args!($($arg)*)))
};
}

View File

@ -16,7 +16,7 @@ use x86_64::{
};
mod hal;
mod libs;
mod klibc;
pub mod arch;
pub mod mm;
pub mod tty;

View File

@ -1,5 +1,5 @@
// kernel/src/mm/allocator/frame.rs
// kernel/src/mm/frame
use bootloader_api::info::{MemoryRegion, MemoryRegionKind, MemoryRegions};
use x86_64::{
structures::paging::{FrameAllocator, PhysFrame, Size4KiB},
@ -35,8 +35,10 @@ impl BootInfoFrameAllocator {
// Convert to an iterator of the frame start address
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
let filtered_addresses = frame_addresses.filter(|&addr| addr >= 0x100000);
// Create `PhysFrame` type from the starting address
frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
filtered_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
}
}

View File

@ -1,4 +1,4 @@
// kernel/src/mm/heap.rs
// kernel/src/mm/allocator/heap.rs
use linked_list_allocator::LockedHeap;
use x86_64::{
structures::paging::{
@ -8,45 +8,35 @@ use x86_64::{
};
use x86_64::structures::paging::PageTable;
use crate::hal::cpu;
use crate::mm::vma;
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();
/// 堆的起始虛擬位址
pub const HEAP_START: usize = 0x_4444_4444_0000;
/// 堆的大小100 KiB
pub const HEAP_SIZE: usize = 100 * 1024;
/// Initialize the heap
///
/// This function will:
/// 1. Allocate physical frames for the heap
/// 2. Map these frames in the page table
/// 3. Initialize the heap allocator
pub fn init_heap(
pub fn init(
mapper: &mut impl Mapper<Size4KiB>,
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> Result<(), MapToError<Size4KiB>> {
// Calculate the page range required for the heap
// 使用 kernel_vm 中定義的高半核地址
let heap_start = vma::HEAP_START;
let heap_size = vma::HEAP_SIZE;
// Calculate page range
let page_range = {
let heap_start = VirtAddr::new(HEAP_START as u64);
let heap_end = heap_start + HEAP_SIZE as u64 - 1u64;
let heap_end = heap_start + heap_size as u64 - 1u64;
let heap_start_page = Page::containing_address(heap_start);
let heap_end_page = Page::containing_address(heap_end);
Page::range_inclusive(heap_start_page, heap_end_page)
};
// Allocate a physical frame for each page and map it
// Allocate a physical frame for each page and map
for page in page_range {
// Allocate a physical frame
let frame = frame_allocator
.allocate_frame()
.ok_or(MapToError::FrameAllocationFailed)?;
// Set page flags: exists + writable
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
// Map page to frame
unsafe {
mapper.map_to(page, frame, flags, frame_allocator)?.flush();
}
@ -54,16 +44,16 @@ pub fn init_heap(
// Initialize the heap allocator
unsafe {
ALLOCATOR.lock().init(HEAP_START as *mut u8, HEAP_SIZE);
ALLOCATOR.lock().init(heap_start.as_mut_ptr(), heap_size);
}
Ok(())
}
pub unsafe fn get_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable {
let phys = cpu::cpu_r_cr3_addr().as_u64();
let phys = cpu::cpu_r_cr3_addr().as_u64();
let virt = physical_memory_offset + phys;
let page_table_ptr: *mut PageTable = virt.as_mut_ptr();
&mut *page_table_ptr
}
}

View File

@ -1,2 +1,3 @@
pub mod frame;
pub mod heap;
pub mod heap;
pub mod pmm;

View File

@ -0,0 +1,293 @@
// kernel/src/mm/allocator/pmm.rs
// Physical Memory Manager with NULL pointer protection
use x86_64::{
structures::paging::{PhysFrame, Size4KiB, PageSize},
PhysAddr,
};
use spin::Mutex;
use crate::{log_debug, log_error, log_info, log_trace, log_warn};
/// Physical memory allocator status
pub struct PhysicalMemoryManager {
/// Bitmap, each bit represents a 4KB page frame
bitmap: &'static mut [u8],
/// Total page frames
total_frames: usize,
/// Number of allocated page frames
allocated_frames: usize,
/// Memory start address
memory_start: PhysAddr,
}
#[derive(Debug, Clone, Copy)]
pub struct MemoryStats {
pub total_frames: usize,
pub allocated_frames: usize,
pub free_frames: usize,
pub total_memory: usize,
pub used_memory: usize,
pub free_memory: usize,
}
/// Global Physical Memory Manager
static PMM: Mutex<Option<PhysicalMemoryManager>> = Mutex::new(None);
impl PhysicalMemoryManager {
/// Initialize the physical memory manager
///
/// # Safety measures
/// - Automatically mark page frame 0x0 as used (to prevent null pointers)
/// - Mark the first 1MB of real mode memory as used
///
/// # Parameters
/// * `memory_start` - The starting address of available memory
/// * `memory_size` - The size of available memory (bytes)
/// * `bitmap_addr` - The address where the bitmap is stored
pub unsafe fn new(
memory_start: PhysAddr,
memory_size: usize,
bitmap_addr: *mut u8,
) -> Self {
let total_frames = memory_size / 4096;
let bitmap_size = (total_frames + 7) / 8;
let bitmap = core::slice::from_raw_parts_mut(bitmap_addr, bitmap_size);
bitmap.fill(0);
let mut pmm = Self {
bitmap,
total_frames,
allocated_frames: 0,
memory_start,
};
pmm.mark_reserved_regions();
pmm
}
/// Mark system reserved area
fn mark_reserved_regions(&mut self) {
// Mark the system reserved area NULL pointer protection: Mark the first page frame (0x0000 - 0x0FFF)
if self.memory_start.as_u64() == 0 {
self.mark_frame_at_index(0);
log_debug!("Marked frame 0 (NULL pointer protection)");
}
// - IVT (Interrupt Vector Table): 0x00000 - 0x003FF
// - BDA (BIOS Data Area): 0x00400 - 0x004FF
// - EBDA (Extended BIOS Data Area): 0x80000 - 0x9FFFF
// - Video memory: 0xA0000 - 0xBFFFF
// - BIOS ROM: 0xF0000 - 0xFFFFF
let low_memory_end = 0x100000u64; // 1MB
if self.memory_start.as_u64() < low_memory_end {
let frames_to_reserve = ((low_memory_end - self.memory_start.as_u64()) / 4096) as usize;
for frame_idx in 0..frames_to_reserve.min(self.total_frames) {
self.mark_frame_at_index(frame_idx);
}
log_debug!("Reserved low memory: {} frames (0x0 - 0x100000)", frames_to_reserve);
}
}
/// Directly mark the page frame of the specified index
fn mark_frame_at_index(&mut self, frame_idx: usize) {
if frame_idx < self.total_frames {
let byte_idx = frame_idx / 8;
let bit = frame_idx % 8;
if byte_idx < self.bitmap.len() {
if (self.bitmap[byte_idx] & (1 << bit)) == 0 {
self.bitmap[byte_idx] |= 1 << bit;
self.allocated_frames += 1;
}
}
}
}
/// Allocate a physical page frame
///
/// # Guarantees
/// - Never return a page frame at address 0x0
/// - Never return a page frame in the lower 1MB region
///
/// # Panics
/// Panics if allocating to page frame address 0 (this shouldn't happen)
pub fn allocate_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
for (byte_idx, byte) in self.bitmap.iter_mut().enumerate() {
if *byte != 0xFF {
for bit in 0..8 {
if (*byte & (1 << bit)) == 0 {
*byte |= 1 << bit;
self.allocated_frames += 1;
let frame_idx = byte_idx * 8 + bit;
let frame_addr = self.memory_start + (frame_idx * 4096) as u64;
if frame_addr.as_u64() == 0 {
panic!("FATAL: PMM allocated NULL frame at index {}! This is a critical bug!", frame_idx);
}
if frame_addr.as_u64() < 0x100000 {
panic!("FATAL: PMM allocated reserved low memory frame at {:#x}! This is a critical bug!", frame_addr.as_u64());
}
return Some(PhysFrame::containing_address(frame_addr));
}
}
}
}
None
}
/// Free a physical page frame
///
/// # Safety check
/// - Do not free a page frame at address 0
/// - Do not free a page frame below 1MB
pub fn deallocate_frame(&mut self, frame: PhysFrame<Size4KiB>) {
let frame_addr = frame.start_address();
if frame_addr.as_u64() < 0x100000 {
log_warn!("Attempt to free reserved frame at {:#x} - ignored", frame_addr.as_u64());
return;
}
let offset = (frame_addr.as_u64() - self.memory_start.as_u64()) as usize;
let frame_idx = offset / 4096;
let byte_idx = frame_idx / 8;
let bit = frame_idx % 8;
if byte_idx < self.bitmap.len() {
self.bitmap[byte_idx] &= !(1 << bit);
self.allocated_frames = self.allocated_frames.saturating_sub(1);
}
}
/// Mark page frame as used
pub fn mark_frame_used(&mut self, frame: PhysFrame<Size4KiB>) {
let frame_addr = frame.start_address();
let offset = (frame_addr.as_u64() - self.memory_start.as_u64()) as usize;
let frame_idx = offset / 4096;
self.mark_frame_at_index(frame_idx);
}
/// Mark memory area as used
pub fn mark_region_used(&mut self, start: PhysAddr, size: usize) {
if size == 0 {
return;
}
let start_frame = PhysFrame::<Size4KiB>::containing_address(start);
let end_frame = PhysFrame::<Size4KiB>::containing_address(start + size as u64 - 1u64);
for frame_addr in (start_frame.start_address().as_u64()..=end_frame.start_address().as_u64())
.step_by(4096)
{
let frame = PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(frame_addr));
self.mark_frame_used(frame);
}
}
/// Check if the page frame is available
pub fn is_frame_free(&self, frame: PhysFrame<Size4KiB>) -> bool {
let frame_addr = frame.start_address();
if frame_addr.as_u64() < 0x100000 {
return false;
}
let offset = (frame_addr.as_u64() - self.memory_start.as_u64()) as usize;
let frame_idx = offset / 4096;
let byte_idx = frame_idx / 8;
let bit = frame_idx % 8;
if byte_idx < self.bitmap.len() {
(self.bitmap[byte_idx] & (1 << bit)) == 0
} else {
false
}
}
/// Get memory usage statistics
pub fn get_stats(&self) -> MemoryStats {
MemoryStats {
total_frames: self.total_frames,
allocated_frames: self.allocated_frames,
free_frames: self.total_frames - self.allocated_frames,
total_memory: self.total_frames * 4096,
used_memory: self.allocated_frames * 4096,
free_memory: (self.total_frames - self.allocated_frames) * 4096,
}
}
}
/// Initialize the global physical memory manager
pub unsafe fn init_pmm(
memory_start: PhysAddr,
memory_size: usize,
bitmap_addr: *mut u8,
) {
let pmm = PhysicalMemoryManager::new(memory_start, memory_size, bitmap_addr);
*PMM.lock() = Some(pmm);
}
/// Allocate physical page frames
///
/// # Guarantee
/// Never return a page frame at address 0x0 or in the lower 1MB range
///
/// # Panics
/// Panic if the internal allocator returns an invalid page frame
pub fn allocate_frame() -> Option<PhysFrame<Size4KiB>> {
let frame = PMM.lock().as_mut()?.allocate_frame()?;
if frame.start_address().as_u64() == 0 {
panic!("CRITICAL: PMM returned NULL frame!");
}
if frame.start_address().as_u64() < 0x100000 {
panic!("CRITICAL: PMM returned reserved low memory frame at {:#x}!",
frame.start_address().as_u64());
}
Some(frame)
}
/// Release physical page frame
pub fn deallocate_frame(frame: PhysFrame<Size4KiB>) {
if let Some(pmm) = PMM.lock().as_mut() {
pmm.deallocate_frame(frame);
}
}
/// Mark page frame as used
pub fn mark_frame_used(frame: PhysFrame<Size4KiB>) {
if let Some(pmm) = PMM.lock().as_mut() {
pmm.mark_frame_used(frame);
}
}
/// Mark memory area as used
pub fn mark_region_used(start: PhysAddr, size: usize) {
if let Some(pmm) = PMM.lock().as_mut() {
pmm.mark_region_used(start, size);
}
}
/// Check if the page frame is available
pub fn is_frame_free(frame: PhysFrame<Size4KiB>) -> bool {
PMM.lock()
.as_ref()
.map(|pmm| pmm.is_frame_free(frame))
.unwrap_or(false)
}
/// Get memory statistics
pub fn get_memory_stats() -> Option<MemoryStats> {
PMM.lock().as_ref().map(|pmm| pmm.get_stats())
}

View File

@ -1,3 +1,4 @@
pub mod allocator;
pub mod paging;
pub mod vmm;
pub mod vmm;
pub mod vma;

View File

@ -0,0 +1,263 @@
// kernel/src/mm/paging.rs
use x86_64::{
structures::paging::{
Page, PageTable, PageTableFlags, PhysFrame, Size4KiB,
Mapper, FrameAllocator, OffsetPageTable, PageTableIndex,
mapper::{MapToError, UnmapError},
},
VirtAddr, PhysAddr,
};
use x86_64::structures::paging::Translate;
use crate::log_error;
use crate::mm::vma;
/// Page table manager
pub struct PageTableManager;
impl PageTableManager {
/// Map a single page
///
/// # Parameters
/// * `page` - Virtual page
/// * `frame` - Physical page frame
/// * `flags` - Page table flags
/// * `mapper` - Page table mapper
/// * `frame_allocator` - Physical page frame allocator
pub fn map_page<A>(
page: Page,
frame: PhysFrame,
flags: PageTableFlags,
mapper: &mut OffsetPageTable,
frame_allocator: &mut A,
) -> Result<(), MapToError<Size4KiB>>
where
A: FrameAllocator<Size4KiB>,
{
unsafe {
mapper
.map_to(page, frame, flags, frame_allocator)?
.flush();
}
Ok(())
}
/// Unmap a single page
///
/// # Parameters
/// * `page` - The virtual page to unmap
/// * `mapper` - The page table mapper
pub fn unmap_page(
page: Page,
mapper: &mut OffsetPageTable,
) -> Result<PhysFrame, UnmapError> {
let (frame, flush) = mapper.unmap(page)?;
flush.flush();
Ok(frame)
}
/// Map memory region
///
/// # Parameters
/// * `virt_start` - Virtual start address
/// * `phys_start` - Physical start address
/// * `size` - Size (bytes)
/// * `flags` - Page table flags
/// * `mapper` - Page table mapper
/// * `frame_allocator` - Physical page frame allocator
pub fn map_region<A>(
virt_start: VirtAddr,
phys_start: PhysAddr,
size: usize,
flags: PageTableFlags,
mapper: &mut OffsetPageTable,
frame_allocator: &mut A,
) -> Result<(), MapToError<Size4KiB>>
where
A: FrameAllocator<Size4KiB>,
{
let page_count = (size + 4095) / 4096;
for i in 0..page_count {
let page_addr = virt_start + (i * 4096) as u64;
let frame_addr = phys_start + (i * 4096) as u64;
let page = Page::containing_address(page_addr);
let frame = PhysFrame::containing_address(frame_addr);
Self::map_page(page, frame, flags, mapper, frame_allocator)?;
}
Ok(())
}
/// Unmap memory region
///
/// # Parameters
/// * `virt_start` - Virtual start address
/// * `size` - Size (bytes)
/// * `mapper` - Page table mapper
pub fn unmap_region(
virt_start: VirtAddr,
size: usize,
mapper: &mut OffsetPageTable,
) -> Result<(), UnmapError> {
let page_count = (size + 4095) / 4096;
for i in 0..page_count {
let page_addr = virt_start + (i * 4096) as u64;
let page = Page::containing_address(page_addr);
Self::unmap_page(page, mapper)?;
}
Ok(())
}
/// Modify page table flags
///
/// # Parameters
/// * `page` - Virtual page
/// * `flags` - New page table flags
/// * `mapper` - Page table mapper
pub fn update_flags(
page: Page,
flags: PageTableFlags,
mapper: &mut OffsetPageTable,
) -> Result<(), ()> {
unsafe {
if let Ok(entry) = mapper.update_flags(page, flags) {
entry.flush();
Ok(())
} else {
Err(())
}
}
}
/// Query the physical address corresponding to a virtual address
///
/// # Parameters
/// * `virt_addr` - Virtual address
/// * `mapper` - Page table mapper
pub fn translate_addr(
virt_addr: VirtAddr,
mapper: &OffsetPageTable,
) -> Option<PhysAddr> {
mapper.translate_addr(virt_addr)
}
/// Check if the page is mapped
///
/// # Parameters
/// * `page` - Virtual page
/// * `mapper` - Page table mapper
pub fn is_mapped(
page: Page,
mapper: &OffsetPageTable,
) -> bool {
mapper.translate_page(page).is_ok()
}
/// Create a new page table (for the process)
///
/// # Parameters
/// * `frame_allocator` - Physical page frame allocator
pub fn create_page_table<A>(
frame_allocator: &mut A,
) -> Option<PhysFrame>
where
A: FrameAllocator<Size4KiB>,
{
let frame = frame_allocator.allocate_frame()?;
let phys_addr = frame.start_address();
let virt_addr = vma::phys_to_virt(phys_addr.as_u64());
unsafe {
let page_table = &mut *(virt_addr.as_mut_ptr::<PageTable>());
page_table.zero();
}
Some(frame)
}
/// Get the page table entry
/// Get the page table entry
pub fn get_entry<'a>(
page: Page,
mapper: &'a OffsetPageTable,
) -> Option<&'a x86_64::structures::paging::page_table::PageTableEntry> {
unsafe {
mapper.translate_page(page).ok()?;
let l4_table = mapper.level_4_table();
let p4_index = page.p4_index();
let p3_index = page.p3_index();
let p2_index = page.p2_index();
let p1_index = page.p1_index();
// Level 4 -> Level 3
let l4_entry = &l4_table[p4_index];
let l3_table_addr = vma::phys_to_virt(l4_entry.addr().as_u64());
let l3_table = &*(l3_table_addr.as_ptr::<PageTable>());
// Level 3 -> Level 2
let l3_entry = &l3_table[p3_index];
if l3_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
return Some(l3_entry); // 1GB huge page
}
let l2_table_addr = vma::phys_to_virt(l3_entry.addr().as_u64());
let l2_table = &*(l2_table_addr.as_ptr::<PageTable>());
// Level 2 -> Level 1
let l2_entry = &l2_table[p2_index];
if l2_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
return Some(l2_entry); // 2MB huge page
}
let l1_table_addr = vma::phys_to_virt(l2_entry.addr().as_u64());
let l1_table = &*(l1_table_addr.as_ptr::<PageTable>());
// return Level 1 entry
Some(&l1_table[p1_index])
}
}
}
/// Kernel code segment flag (executable, not writable)
pub const KERNEL_CODE: PageTableFlags = PageTableFlags::PRESENT;
/// Kernel data segment flag (non-executable, writable)
pub fn kernel_data() -> PageTableFlags {
PageTableFlags::PRESENT | PageTableFlags::WRITABLE
}
/// User code segment flags (executable, non-writable, user accessible)
pub fn user_code() -> PageTableFlags {
PageTableFlags::PRESENT | PageTableFlags::USER_ACCESSIBLE
}
/// User data segment flags (non-executable, writable, user accessible)
pub fn user_data() -> PageTableFlags {
PageTableFlags::PRESENT
| PageTableFlags::WRITABLE
| PageTableFlags::USER_ACCESSIBLE
}
/// Device mapping flag (non-cacheable)
pub fn device_memory() -> PageTableFlags {
PageTableFlags::PRESENT
| PageTableFlags::WRITABLE
| PageTableFlags::NO_CACHE
}
/// Page table error handling
pub fn handle_page_fault(virt_addr: VirtAddr, error_code: u64) {
// TODO: 實現頁面錯誤處理
// - 檢查 VMA (Virtual Memory Area) 是否合法
// - 按需分頁: if !present && valid_vma { allocate_page() }
// - COW: if write && cow_page { copy_page() }
log_error!("Page fault at {:#x}, error code: {:#x}", virt_addr.as_u64(), error_code);
}

174
kernel/src/mm/vma.rs Normal file
View File

@ -0,0 +1,174 @@
// kernel/src/mm/vma.rs
use x86_64::VirtAddr;
use crate::log_info;
/// Upper core base address
///
/// All kernel-related virtual addresses begin here
pub const HIGHER_HALF_BASE: u64 = 0xFFFF_C000_0000_0000;
/// Physical memory direct mapping base address
///
/// The bootloader will map all physical memory here
/// Physical address P → virtual address (P + PHYS_MEM_OFFSET)
pub const PHYS_MEM_OFFSET: u64 = 0xFFFF_8000_0000_0000;
/// Kernel heap start address
///
/// Used for dynamic allocation of small objects (Vec, String, Box, etc.)
pub const HEAP_START: VirtAddr = VirtAddr::new_truncate(HIGHER_HALF_BASE + 0x1000);
/// Kernel heap size
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
/// Starting address of the kernel's dynamic allocation area
///
/// Used for kmalloc/kfree allocation of large objects
pub const KERNEL_DYNAMIC_START: VirtAddr = VirtAddr::new_truncate(HIGHER_HALF_BASE + 0x10_0000);
/// Kernel dynamically allocates area size
pub const KERNEL_DYNAMIC_SIZE: usize = 128 * 1024 * 1024; // 128 MiB
/// End address of the kernel's dynamically allocated area
pub const KERNEL_DYNAMIC_END: VirtAddr = VirtAddr::new_truncate(
KERNEL_DYNAMIC_START.as_u64() + KERNEL_DYNAMIC_SIZE as u64
);
/// Kernel stack area starting address
pub const KERNEL_STACK_START: VirtAddr = VirtAddr::new_truncate(0xFFFF_D000_0000_0000);
/// Kernel stack area size
pub const KERNEL_STACK_SIZE: usize = 64 * 1024 * 1024; // 64 MiB
/// End address of the kernel stack area
pub const KERNEL_STACK_END: VirtAddr = VirtAddr::new_truncate(
KERNEL_STACK_START.as_u64() + KERNEL_STACK_SIZE as u64
);
/// Device mapping area start address (MMIO)
pub const DEVICE_MAPPING_START: VirtAddr = VirtAddr::new_truncate(0xFFFF_E000_0000_0000);
/// Device mapping area size
pub const DEVICE_MAPPING_SIZE: usize = 256 * 1024 * 1024; // 256 MiB
/// End address of the device mapping area
pub const DEVICE_MAPPING_END: VirtAddr = VirtAddr::new_truncate(
DEVICE_MAPPING_START.as_u64() + DEVICE_MAPPING_SIZE as u64
);
/// Check if the address is in kernel space
#[inline]
pub fn is_kernel_address(addr: VirtAddr) -> bool {
addr.as_u64() >= HIGHER_HALF_BASE
}
/// Check if the address is in user space
#[inline]
pub fn is_user_address(addr: VirtAddr) -> bool {
addr.as_u64() < 0x0000_8000_0000_0000
}
/// Physical addresses are translated into virtual addresses (via direct mapping)
#[inline]
pub fn phys_to_virt(phys: u64) -> VirtAddr {
VirtAddr::new(phys + PHYS_MEM_OFFSET)
}
/// Convert virtual address to physical address (if in direct mapped area)
#[inline]
pub fn virt_to_phys(virt: VirtAddr) -> Option<u64> {
let addr = virt.as_u64();
if addr >= PHYS_MEM_OFFSET && addr < HIGHER_HALF_BASE {
Some(addr - PHYS_MEM_OFFSET)
} else {
None
}
}
/// Check if the address is within the kernel heap range
#[inline]
pub fn is_in_heap(addr: VirtAddr) -> bool {
let addr_u64 = addr.as_u64();
addr_u64 >= HEAP_START.as_u64()
&& addr_u64 < HEAP_START.as_u64() + HEAP_SIZE as u64
}
/// Check if the address is in the dynamically allocated area
#[inline]
pub fn is_in_dynamic_region(addr: VirtAddr) -> bool {
let addr_u64 = addr.as_u64();
addr_u64 >= KERNEL_DYNAMIC_START.as_u64()
&& addr_u64 < KERNEL_DYNAMIC_END.as_u64()
}
/// Check if the address is in the kernel stack area
#[inline]
pub fn is_in_kernel_stack(addr: VirtAddr) -> bool {
let addr_u64 = addr.as_u64();
addr_u64 >= KERNEL_STACK_START.as_u64()
&& addr_u64 < KERNEL_STACK_END.as_u64()
}
/// Check if the address is in the device mapping area
#[inline]
pub fn is_in_device_region(addr: VirtAddr) -> bool {
let addr_u64 = addr.as_u64();
addr_u64 >= DEVICE_MAPPING_START.as_u64()
&& addr_u64 < DEVICE_MAPPING_END.as_u64()
}
/// Get the name of the region to which the address belongs
pub fn get_region_name(addr: VirtAddr) -> &'static str {
let addr_u64 = addr.as_u64();
if is_user_address(addr) {
"User Space"
} else if addr_u64 >= PHYS_MEM_OFFSET && addr_u64 < HIGHER_HALF_BASE {
"Physical Memory Map"
} else if is_in_heap(addr) {
"Kernel Heap"
} else if is_in_dynamic_region(addr) {
"Kernel Dynamic"
} else if is_in_kernel_stack(addr) {
"Kernel Stack"
} else if is_in_device_region(addr) {
"Device Mapping"
} else {
"Reserved"
}
}
/// Print hhk address space layout
pub fn print_info() {
log_info!("Higher Half Kernel Memory Layout:");
log_info!(" User Space: {:#018x} - {:#018x}",
0x0u64,
0x0000_7FFF_FFFF_FFFFu64
);
log_info!(" Physical Map: {:#018x} - {:#018x}",
PHYS_MEM_OFFSET,
HIGHER_HALF_BASE - 1
);
log_info!(" Kernel Base: {:#018x}", HIGHER_HALF_BASE);
log_info!(" Heap: {:#018x} - {:#018x} ({} KiB)",
HEAP_START.as_u64(),
HEAP_START.as_u64() + HEAP_SIZE as u64,
HEAP_SIZE / 1024
);
log_info!(" Dynamic: {:#018x} - {:#018x} ({} MiB)",
KERNEL_DYNAMIC_START.as_u64(),
KERNEL_DYNAMIC_END.as_u64(),
KERNEL_DYNAMIC_SIZE / (1024 * 1024)
);
log_info!(" Kernel Stack: {:#018x} - {:#018x} ({} MiB)",
KERNEL_STACK_START.as_u64(),
KERNEL_STACK_END.as_u64(),
KERNEL_STACK_SIZE / (1024 * 1024)
);
log_info!(" Device Mapping: {:#018x} - {:#018x} ({} MiB)",
DEVICE_MAPPING_START.as_u64(),
DEVICE_MAPPING_END.as_u64(),
DEVICE_MAPPING_SIZE / (1024 * 1024)
);
}

View File

@ -0,0 +1,319 @@
// kernel/src/mm/vmm.rs
use x86_64::{
structures::paging::{
Page, PageTableFlags, PhysFrame, Size4KiB,
Mapper, FrameAllocator, OffsetPageTable,
},
VirtAddr, PhysAddr,
};
use spin::Mutex;
use alloc::collections::BTreeMap;
use crate::log_info;
use crate::mm::vma;
/// Memory block information
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct MemoryBlock {
/// Starting address
start: VirtAddr,
/// Size (pages)
page_count: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct VmmStats {
pub next_vaddr: VirtAddr,
pub end_vaddr: VirtAddr,
pub allocated_pages: usize,
pub free_pages: usize,
pub used_from_new: usize,
pub allocated_blocks_count: usize,
pub free_blocks_count: usize,
}
impl VmmStats {
pub fn print(&self) {
log_info!("Virtual Memory Statistics:");
log_info!(" Allocated: {} pages ({} KiB) in {} blocks",
self.allocated_pages,
self.allocated_pages * 4,
self.allocated_blocks_count
);
log_info!(" Free: {} pages ({} KiB) in {} blocks",
self.free_pages,
self.free_pages * 4,
self.free_blocks_count
);
log_info!(" New Usage: {} pages ({} KiB)",
self.used_from_new,
self.used_from_new * 4
);
}
}
pub static VMM: Mutex<VirtualMemoryManager> = Mutex::new(VirtualMemoryManager::new());
impl MemoryBlock {
fn new(start: VirtAddr, page_count: usize) -> Self {
Self { start, page_count }
}
fn size_bytes(&self) -> usize {
self.page_count * 4096
}
fn end(&self) -> VirtAddr {
self.start + self.size_bytes() as u64
}
}
/// Virtual memory manager (with free list)
pub struct VirtualMemoryManager {
/// The next allocatable virtual address (for unused areas)
next_vaddr: VirtAddr,
/// End address of the dynamically allocated area
end_vaddr: VirtAddr,
/// Free blockchain table (key: starting address, value: size)
free_blocks: BTreeMap<u64, MemoryBlock>,
/// Allocated block (key: start address, value: size)
allocated_blocks: BTreeMap<u64, MemoryBlock>,
}
impl VirtualMemoryManager {
pub const fn new() -> Self {
Self {
next_vaddr: vma::KERNEL_DYNAMIC_START,
end_vaddr: vma::KERNEL_DYNAMIC_END,
free_blocks: BTreeMap::new(),
allocated_blocks: BTreeMap::new(),
}
}
/// Allocate contiguous virtual memory pages
///
/// Strategy:
/// 1. First find the most suitable block in the free list (First Fit)
/// 2. If no suitable block is found, allocate from an unused area
pub fn allocate_pages(&mut self, count: usize) -> Option<VirtAddr> {
if count == 0 {
return None;
}
// 策略 1: 從空閒鏈表中找First Fit
if let Some(addr) = self.allocate_from_free_list(count) {
return Some(addr);
}
// 策略 2: 從未使用區域分配
self.allocate_from_new_region(count)
}
/// Allocate from the free list
fn allocate_from_free_list(&mut self, count: usize) -> Option<VirtAddr> {
// Find the first block that is large enough
let mut found_block: Option<(u64, MemoryBlock)> = None;
for (&addr, &block) in self.free_blocks.iter() {
if block.page_count >= count {
found_block = Some((addr, block));
break;
}
}
if let Some((addr, block)) = found_block {
// Remove this free block
self.free_blocks.remove(&addr);
let alloc_addr = block.start;
let alloc_block = MemoryBlock::new(alloc_addr, count);
// Record Assigned
self.allocated_blocks.insert(alloc_addr.as_u64(), alloc_block);
// If there is free space, add it back to the free list
if block.page_count > count {
let remaining_start = alloc_addr + (count * 4096) as u64;
let remaining_count = block.page_count - count;
let remaining_block = MemoryBlock::new(remaining_start, remaining_count);
self.free_blocks.insert(remaining_start.as_u64(), remaining_block);
}
return Some(alloc_addr);
}
None
}
/// Unused zone allocation
fn allocate_from_new_region(&mut self, count: usize) -> Option<VirtAddr> {
let size = count * 4096;
let start = self.next_vaddr;
let end = start + size as u64;
if end > self.end_vaddr {
return None;
}
let block = MemoryBlock::new(start, count);
self.allocated_blocks.insert(start.as_u64(), block);
self.next_vaddr = end;
Some(start)
}
/// Release virtual memory page
///
/// Strategy:
/// 1. Remove from allocated list
/// 2. Add to free list
/// 3. Attempt to merge adjacent free blocks
pub fn deallocate_pages(&mut self, addr: VirtAddr, count: usize) {
let addr_u64 = addr.as_u64();
// Check whether it has actually been allocated
if let Some(&block) = self.allocated_blocks.get(&addr_u64) {
// Verify size matches
if block.page_count != count {
crate::log_warn!(
"VMM: deallocate size mismatch at {:#x}: expected {}, got {}",
addr_u64, block.page_count, count
);
}
// Remove from allocated list
self.allocated_blocks.remove(&addr_u64);
// Add to free list
let free_block = MemoryBlock::new(addr, count);
self.free_blocks.insert(addr_u64, free_block);
// Try to merge adjacent blocks
self.coalesce_free_blocks(addr_u64);
} else {
crate::log_warn!("VMM: attempt to free unallocated memory at {:#x}", addr_u64);
}
}
/// Merge adjacent free blocks
fn coalesce_free_blocks(&mut self, addr: u64) {
let current_block = match self.free_blocks.get(&addr) {
Some(&block) => block,
None => return,
};
// Try to merge with the following block
let next_addr = current_block.end().as_u64();
if let Some(&next_block) = self.free_blocks.get(&next_addr) {
// merge
self.free_blocks.remove(&addr);
self.free_blocks.remove(&next_addr);
let merged = MemoryBlock::new(
current_block.start,
current_block.page_count + next_block.page_count
);
self.free_blocks.insert(addr, merged);
crate::log_debug!("VMM: merged blocks at {:#x}", addr);
}
// Try merging with the previous block
// Find the previous block
let mut prev_addr_opt: Option<u64> = None;
for (&prev_addr, &prev_block) in self.free_blocks.iter() {
if prev_block.end().as_u64() == addr {
prev_addr_opt = Some(prev_addr);
break;
}
}
if let Some(prev_addr) = prev_addr_opt {
let prev_block = self.free_blocks[&prev_addr];
let current_block = self.free_blocks[&addr];
self.free_blocks.remove(&prev_addr);
self.free_blocks.remove(&addr);
let merged = MemoryBlock::new(
prev_block.start,
prev_block.page_count + current_block.page_count
);
self.free_blocks.insert(prev_addr, merged);
crate::log_debug!("VMM: merged with previous block at {:#x}", prev_addr);
}
}
/// Get statistics
pub fn get_stats(&self) -> VmmStats {
let mut allocated_pages = 0;
for block in self.allocated_blocks.values() {
allocated_pages += block.page_count;
}
let mut free_pages = 0;
for block in self.free_blocks.values() {
free_pages += block.page_count;
}
let used_from_new =
((self.next_vaddr.as_u64() - vma::KERNEL_DYNAMIC_START.as_u64()) / 4096) as usize;
VmmStats {
next_vaddr: self.next_vaddr,
end_vaddr: self.end_vaddr,
allocated_pages,
free_pages,
used_from_new,
allocated_blocks_count: self.allocated_blocks.len(),
free_blocks_count: self.free_blocks.len(),
}
}
}
/// Map device memory (MMIO)
pub fn map_device_memory<A>(
phys_addr: PhysAddr,
size: usize,
mapper: &mut OffsetPageTable,
frame_allocator: &mut A,
) -> Option<VirtAddr>
where
A: FrameAllocator<Size4KiB>,
{
let page_count = (size + 4095) / 4096;
let vaddr = VMM.lock().allocate_pages(page_count)?;
let start_page: Page::<Size4KiB> = Page::containing_address(vaddr);
let start_frame: PhysFrame::<Size4KiB> = PhysFrame::containing_address(phys_addr);
for i in 0..page_count {
let page = start_page + i as u64;
let frame = PhysFrame::containing_address(
start_frame.start_address() + (i * 4096) as u64
);
let flags = PageTableFlags::PRESENT
| PageTableFlags::WRITABLE
| PageTableFlags::NO_CACHE;
unsafe {
mapper
.map_to(page, frame, flags, frame_allocator)
.ok()?
.flush();
}
}
Some(vaddr)
}
/// Get virtual memory statistics
pub fn get_vmm_stats() -> VmmStats {
VMM.lock().get_stats()
}