Merge pull request #4 from ParrotXray/feat/mm
feat: Implement higher half kernel with complete memory management
This commit is contained in:
commit
10433a4fb7
36
TODO.txt
36
TODO.txt
@ -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 └─────────────────────┘
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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!");
|
||||
}
|
||||
@ -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!();
|
||||
|
||||
@ -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!())
|
||||
74
kernel/src/klibc/malloc.rs
Normal file
74
kernel/src/klibc/malloc.rs
Normal 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);
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
pub mod print;
|
||||
pub mod logger;
|
||||
pub mod string;
|
||||
pub mod string;
|
||||
pub mod malloc;
|
||||
@ -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)*)))
|
||||
};
|
||||
}
|
||||
@ -16,7 +16,7 @@ use x86_64::{
|
||||
};
|
||||
|
||||
mod hal;
|
||||
mod libs;
|
||||
mod klibc;
|
||||
pub mod arch;
|
||||
pub mod mm;
|
||||
pub mod tty;
|
||||
|
||||
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -1,2 +1,3 @@
|
||||
pub mod frame;
|
||||
pub mod heap;
|
||||
pub mod heap;
|
||||
pub mod pmm;
|
||||
293
kernel/src/mm/allocator/pmm.rs
Normal file
293
kernel/src/mm/allocator/pmm.rs
Normal 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())
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
pub mod allocator;
|
||||
pub mod paging;
|
||||
pub mod vmm;
|
||||
pub mod vmm;
|
||||
pub mod vma;
|
||||
@ -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
174
kernel/src/mm/vma.rs
Normal 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)
|
||||
);
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user