diff --git a/Cargo.lock b/Cargo.lock index 654ed36c0..a288438f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -464,6 +464,7 @@ name = "jinux-boot" version = "0.1.0" dependencies = [ "anyhow", + "cfg-if", "runner-utils", ] diff --git a/boot/Cargo.toml b/boot/Cargo.toml index 1fa7c73a0..dade9b42c 100644 --- a/boot/Cargo.toml +++ b/boot/Cargo.toml @@ -7,7 +7,9 @@ edition = "2021" [dependencies] runner-utils = "0.0.2" anyhow = "1.0.32" +cfg-if = "1.0" [features] default = ["limine"] -limine= [] \ No newline at end of file +limine = [] +iommu = [] diff --git a/boot/src/main.rs b/boot/src/main.rs index cb47f03b1..c857a62f2 100644 --- a/boot/src/main.rs +++ b/boot/src/main.rs @@ -10,24 +10,12 @@ use std::{ const COMMON_ARGS: &[&str] = &[ "--no-reboot", "-machine", - "q35", + "q35,kernel-irqchip=split", "-enable-kvm", "-cpu", "Icelake-Server,+x2apic", "-m", "2G", - "-device", - "isa-debug-exit,iobase=0xf4,iosize=0x04", - "-device", - "virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0", - "-device", - "virtio-keyboard-pci", - "-device", - "virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off", - "-netdev", - "user,id=net01,hostfwd=tcp::30022-:22,hostfwd=tcp::30080-:8080", - "-object", - "filter-dump,id=filter0,netdev=net01,file=virtio-net.pcap", "-monitor", "vc", "-serial", @@ -36,6 +24,44 @@ const COMMON_ARGS: &[&str] = &[ "none", ]; +cfg_if::cfg_if!( + if #[cfg(feature="iommu")] { + macro_rules! virtio_device_args { + ($($args:tt),*) => { + concat!($($args,)*"disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on",) + }; + } + const OPTION_ARGS: &[&str] = &[ + "-device", + "intel-iommu,intremap=on,device-iotlb=on", + "-device", + "ioh3420,id=pcie.0,chassis=1", + ]; + } else { + macro_rules! virtio_device_args { + ($($args:tt),*) => { + concat!($($args,)*"disable-legacy=on,disable-modern=off",) + }; + } + const OPTION_ARGS: &[&str] = &[]; + } +); + +const DEVICE_ARGS: &[&str] = &[ + "-device", + "isa-debug-exit,iobase=0xf4,iosize=0x04", + "-device", + virtio_device_args!("virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,"), + "-device", + virtio_device_args!("virtio-keyboard-pci,"), + "-device", + virtio_device_args!("virtio-net-pci,netdev=net01,"), + "-netdev", + "user,id=net01,hostfwd=tcp::30022-:22,hostfwd=tcp::30080-:8080", + "-object", + "filter-dump,id=filter0,netdev=net01,file=virtio-net.pcap", +]; + const RUN_ARGS: &[&str] = &[]; const TEST_ARGS: &[&str] = &[]; const TEST_TIMEOUT_SECS: u64 = 30; @@ -62,13 +88,12 @@ fn main() -> anyhow::Result<()> { a.join(str.add(".iso")) }; - #[cfg(windows)] - let mut qemu_cmd = Command::new("qemu-system-x86_64.exe"); - #[cfg(not(windows))] let mut qemu_cmd = Command::new("qemu-system-x86_64"); let binary_kind = runner_utils::binary_kind(&kernel_binary_path); let mut qemu_args = COMMON_ARGS.clone().to_vec(); + qemu_args.extend(DEVICE_ARGS.clone().to_vec().iter()); + qemu_args.extend(OPTION_ARGS.clone().to_vec().iter()); qemu_args.push("-drive"); let binding = create_fs_image(kernel_binary_path.as_path())?; qemu_args.push(binding.as_str()); @@ -110,9 +135,6 @@ fn call_limine_build_script(path: &PathBuf) -> anyhow::Result<()> { fn create_fs_image(path: &Path) -> anyhow::Result { let mut fs_img_path = path.parent().unwrap().to_str().unwrap().to_string(); - #[cfg(windows)] - fs_img_path.push_str("\\fs.img"); - #[cfg(not(windows))] fs_img_path.push_str("/fs.img"); let path = Path::new(fs_img_path.as_str()); if path.exists() { diff --git a/framework/jinux-frame/src/arch/x86/iommu/context_table.rs b/framework/jinux-frame/src/arch/x86/iommu/context_table.rs new file mode 100644 index 000000000..59fd859e4 --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/iommu/context_table.rs @@ -0,0 +1,313 @@ +use core::mem::size_of; + +use alloc::collections::BTreeMap; +use log::warn; +use pod::Pod; + +use crate::{ + bus::pci::PciDeviceLocation, + config::PAGE_SIZE, + vm::{ + page_table::{PageTableConfig, PageTableError}, + Paddr, PageTable, Vaddr, VmAllocOptions, VmFrame, VmFrameVec, VmIo, + }, +}; + +use super::second_stage::{PageTableEntry, PageTableFlags}; + +/// Bit 0 is `Present` bit, indicating whether this entry is present. +/// Bit 63:12 is the context-table pointer pointing to this bus's context-table. +#[derive(Pod, Clone, Copy)] +#[repr(C)] +pub struct RootEntry(u128); + +impl RootEntry { + pub const fn present(&self) -> bool { + // Bit 0 + (self.0 & 0b1) != 0 + } + + pub const fn addr(&self) -> u64 { + (self.0 & 0xFFFF_FFFF_FFFF_F000) as u64 + } +} + +pub struct RootTable { + /// Total 256 bus, each entry is 128 bits. + root_frame: VmFrame, + // TODO: Use radix tree instead. + context_tables: BTreeMap, +} + +#[derive(Debug)] +pub enum ContextTableError { + InvalidDeviceId, + /// Error when modifying the page table + ModificationError(PageTableError), +} + +impl RootTable { + pub fn new() -> Self { + Self { + root_frame: VmFrameVec::allocate(&VmAllocOptions::new(1).uninit(false)) + .unwrap() + .pop() + .unwrap(), + context_tables: BTreeMap::new(), + } + } + + pub fn map( + &mut self, + device: PciDeviceLocation, + vaddr: Vaddr, + paddr: Paddr, + ) -> Result<(), ContextTableError> { + if device.device >= 32 || device.function >= 8 { + return Err(ContextTableError::InvalidDeviceId); + } + + self.get_or_create_context_table(device) + .map(device, vaddr, paddr & !(PAGE_SIZE - 1))?; + + Ok(()) + } + + pub fn unmap( + &mut self, + device: PciDeviceLocation, + vaddr: Vaddr, + ) -> Result<(), ContextTableError> { + if device.device >= 32 || device.function >= 8 { + return Err(ContextTableError::InvalidDeviceId); + } + + self.get_or_create_context_table(device) + .unmap(device, vaddr)?; + + Ok(()) + } + + pub fn paddr(&self) -> Paddr { + self.root_frame.start_paddr() + } + + fn get_or_create_context_table(&mut self, device_id: PciDeviceLocation) -> &mut ContextTable { + let bus_entry = self + .root_frame + .read_val::(device_id.bus as usize * size_of::()) + .unwrap(); + + if !bus_entry.present() { + let table = ContextTable::new(); + let address = table.paddr(); + self.context_tables.insert(address, table); + let entry = RootEntry(address as u128 | 1); + self.root_frame + .write_val::(device_id.bus as usize * size_of::(), &entry) + .unwrap(); + self.context_tables.get_mut(&address).unwrap() + } else { + self.context_tables + .get_mut(&(bus_entry.addr() as usize)) + .unwrap() + } + } + + /// Specify the device page table instead of creating a page table if not exists. + /// + /// This will be useful if we want all the devices to use the same page table. + /// The original page table will be overwritten. + pub fn specify_device_page_table( + &mut self, + device_id: PciDeviceLocation, + page_table: PageTable, + ) { + let context_table = self.get_or_create_context_table(device_id); + + let bus_entry = context_table + .entries_frame + .read_val::( + (device_id.device as usize * 8 + device_id.function as usize) as usize + * size_of::(), + ) + .unwrap(); + if bus_entry.present() { + warn!("IOMMU: Overwritting the existing device page table"); + } + let address = page_table.root_paddr(); + context_table.page_tables.insert(address, page_table); + let entry = ContextEntry(address as u128 | 3 | 0x1_0000_0000_0000_0000); + context_table + .entries_frame + .write_val::( + (device_id.device as usize * 8 + device_id.function as usize) as usize + * size_of::(), + &entry, + ) + .unwrap(); + context_table.page_tables.get_mut(&address).unwrap(); + } +} + +/// Context Entry in the Context Table, used in Intel iommu. +/// +/// The format of context entry: +/// ``` +/// 127--88: Reserved. +/// 87---72: Domain Identifier. +/// 71---71: Reserved. +/// 70---67: Ignored. +/// 66---64: Address Width. +/// 63---12: Second Stage Page Translation Pointer. +/// 11----4: Reserved. +/// 3-----2: Translation Type. +/// 1-----1: Fault Processing Disable. +/// 0-----0: Present +/// ``` +/// +#[derive(Pod, Clone, Copy)] +#[repr(C)] +pub struct ContextEntry(u128); + +impl ContextEntry { + /// Identifier for the domain to which this context-entry maps. Hardware may use the domain + /// identifier to tag its internal caches + pub const fn domain_identifier(&self) -> u64 { + // Bit 87-72 + ((self.0 & 0xFF_FF00_0000_0000_0000_0000) >> 72) as u64 + } + + pub const fn address_width(&self) -> AddressWidth { + // Bit 66-64 + let value = ((self.0 & 0x7_0000_0000_0000_0000) >> 64) as u64; + match value { + 1 => AddressWidth::Level3PageTable, + 2 => AddressWidth::Level4PageTable, + 3 => AddressWidth::Level5PageTable, + _ => AddressWidth::Reserved, + } + } + + /// Get the second stage page translation pointer. + /// + /// This function will not right shift the value after the `and` operation. + pub const fn second_stage_pointer(&self) -> u64 { + // Bit 63~12 + (self.0 & 0xFFFF_FFFF_FFFF_F000) as u64 + } + + /// This field is applicable only for requests-without-PASID, as hardware blocks all requests-with + /// PASID in legacy mode before they can use context table + pub const fn translation_type(&self) -> u64 { + // Bit 3~2 + ((self.0 & 0b1100) >> 2) as u64 + } + + /// Enables or disables recording/reporting of qualified non-recoverable faults. + pub const fn fault_process_disable(&self) -> bool { + // Bit 1 + (self.0 & 0b10) != 0 + } + + pub const fn present(&self) -> bool { + // Bit 0 + (self.0 & 0b1) != 0 + } +} + +#[derive(Debug)] +pub enum AddressWidth { + /// 000b, 100b~111b + Reserved, + /// 001b + Level3PageTable, + /// 010b + Level4PageTable, + /// 011b + Level5PageTable, +} + +pub struct ContextTable { + /// Total 32 devices, each device has 8 functions. + entries_frame: VmFrame, + page_tables: BTreeMap>, +} + +impl ContextTable { + fn new() -> Self { + Self { + entries_frame: VmFrameVec::allocate(&VmAllocOptions::new(1).uninit(false)) + .unwrap() + .pop() + .unwrap(), + page_tables: BTreeMap::new(), + } + } + + fn paddr(&self) -> Paddr { + self.entries_frame.start_paddr() + } + + fn get_or_create_page_table( + &mut self, + device: PciDeviceLocation, + ) -> &mut PageTable { + let bus_entry = self + .entries_frame + .read_val::( + (device.device as usize * 8 + device.function as usize) as usize + * size_of::(), + ) + .unwrap(); + + if !bus_entry.present() { + let table: PageTable = PageTable::new(PageTableConfig { + address_width: crate::vm::page_table::AddressWidth::Level3PageTable, + }); + let address = table.root_paddr(); + self.page_tables.insert(address, table); + let entry = ContextEntry(address as u128 | 3 | 0x1_0000_0000_0000_0000); + self.entries_frame + .write_val::( + (device.device as usize * 8 + device.function as usize) as usize + * size_of::(), + &entry, + ) + .unwrap(); + self.page_tables.get_mut(&address).unwrap() + } else { + self.page_tables + .get_mut(&(bus_entry.second_stage_pointer() as usize)) + .unwrap() + } + } + + fn map( + &mut self, + device: PciDeviceLocation, + vaddr: Vaddr, + paddr: Paddr, + ) -> Result<(), ContextTableError> { + if device.device >= 32 || device.function >= 8 { + return Err(ContextTableError::InvalidDeviceId); + } + self.get_or_create_page_table(device) + .map( + vaddr, + paddr, + PageTableFlags::WRITABLE | PageTableFlags::READABLE | PageTableFlags::LAST_PAGE, + ) + .map_err(|err| ContextTableError::ModificationError(err)) + } + + fn unmap(&mut self, device: PciDeviceLocation, vaddr: Vaddr) -> Result<(), ContextTableError> { + if device.device >= 32 || device.function >= 8 { + return Err(ContextTableError::InvalidDeviceId); + } + + self.get_or_create_page_table(device) + .unmap(vaddr) + .map_err(|err| ContextTableError::ModificationError(err)) + } +} diff --git a/framework/jinux-frame/src/arch/x86/iommu/mod.rs b/framework/jinux-frame/src/arch/x86/iommu/mod.rs new file mode 100644 index 000000000..e1441a046 --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/iommu/mod.rs @@ -0,0 +1,173 @@ +mod context_table; +mod second_stage; + +use log::debug; +use spin::{Mutex, Once}; + +use crate::{ + arch::{ + iommu::{context_table::RootTable, second_stage::PageTableEntry}, + x86::kernel::acpi::{ + dmar::{Dmar, Remapping}, + ACPI_TABLES, + }, + }, + bus::pci::PciDeviceLocation, + vm::{ + paddr_to_vaddr, + page_table::{PageTableConfig, PageTableError}, + Paddr, PageTable, Vaddr, + }, +}; + +use volatile::{ + access::{ReadOnly, ReadWrite, WriteOnly}, + Volatile, +}; + +#[derive(Debug)] +pub enum IommuError { + NoIommu, + ModificationError(PageTableError), +} + +// FIXME: Perform map operations by obtaining ownership of a VmFrame. +/// +/// # Safety +/// +/// Mapping an incorrect address may lead to a kernel data leak. +pub(crate) unsafe fn map(vaddr: Vaddr, paddr: Paddr) -> Result<(), IommuError> { + let Some(table) = PAGE_TABLE.get() else{ + return Err(IommuError::NoIommu); + }; + // The page table of all devices is the same. So we can use any device ID. + table + .lock() + .map( + PciDeviceLocation { + bus: 0, + device: 0, + function: 0, + }, + vaddr, + paddr, + ) + .map_err(|err| match err { + context_table::ContextTableError::InvalidDeviceId => unreachable!(), + context_table::ContextTableError::ModificationError(err) => { + IommuError::ModificationError(err) + } + }) +} + +pub(crate) fn unmap(vaddr: Vaddr) -> Result<(), IommuError> { + let Some(table) = PAGE_TABLE.get() else{ + return Err(IommuError::NoIommu); + }; + // The page table of all devices is the same. So we can use any device ID. + table + .lock() + .unmap( + PciDeviceLocation { + bus: 0, + device: 0, + function: 0, + }, + vaddr, + ) + .map_err(|err| match err { + context_table::ContextTableError::InvalidDeviceId => unreachable!(), + context_table::ContextTableError::ModificationError(err) => { + IommuError::ModificationError(err) + } + }) +} + +pub fn init() -> Result<(), IommuError> { + let mut remapping_reg = RemappingRegisters::new().ok_or(IommuError::NoIommu)?; + + let mut root_table = RootTable::new(); + + // For all PCI Device, use the same page table. + let page_table: PageTable = PageTable::new(PageTableConfig { + address_width: crate::vm::page_table::AddressWidth::Level3PageTable, + }); + for table in PciDeviceLocation::all() { + root_table.specify_device_page_table(table, page_table.clone()) + } + + let paddr = root_table.paddr(); + + // write remapping register + remapping_reg.root_table_address.write(paddr as u64); + // start writing + remapping_reg.global_command.write(0x4000_0000); + // wait until complete + while remapping_reg.global_status.read() & 0x4000_0000 == 0 {} + + // enable iommu + remapping_reg.global_command.write(0x8000_0000); + + debug!("IOMMU registers:{:#x?}", remapping_reg); + + PAGE_TABLE.call_once(|| Mutex::new(root_table)); + Ok(()) +} + +#[derive(Debug)] +#[repr(C)] +struct RemappingRegisters { + version: Volatile<&'static u32, ReadOnly>, + capability: Volatile<&'static u64, ReadOnly>, + extended_capability: Volatile<&'static u64, ReadOnly>, + global_command: Volatile<&'static mut u32, WriteOnly>, + global_status: Volatile<&'static u32, ReadOnly>, + root_table_address: Volatile<&'static mut u64, ReadWrite>, + context_command: Volatile<&'static mut u64, ReadWrite>, +} + +impl RemappingRegisters { + /// Create a instance from base address + fn new() -> Option { + let dmar = Dmar::new()?; + let acpi_table_lock = ACPI_TABLES.get().unwrap().lock(); + + debug!("DMAR:{:#x?}", dmar); + let base_address = { + let mut addr = 0; + for remapping in dmar.remapping_iter() { + match remapping { + Remapping::Drhd(drhd) => addr = drhd.register_base_addr(), + _ => {} + } + } + if addr == 0 { + panic!("There should be a DRHD structure in the DMAR table"); + } + addr + }; + + let vaddr = paddr_to_vaddr(base_address as usize); + // Safety: All offsets and sizes are strictly adhered to in the manual, and the base address is obtained from Drhd. + unsafe { + let version = Volatile::new_read_only(&*(vaddr as *const u32)); + let capability = Volatile::new_read_only(&*((vaddr + 0x08) as *const u64)); + let extended_capability = Volatile::new_read_only(&*((vaddr + 0x10) as *const u64)); + let global_command = Volatile::new_write_only(&mut *((vaddr + 0x18) as *mut u32)); + let global_status = Volatile::new_read_only(&*((vaddr + 0x1C) as *const u32)); + let root_table_address = Volatile::new(&mut *((vaddr + 0x20) as *mut u64)); + let context_command = Volatile::new(&mut *((vaddr + 0x28) as *mut u64)); + Some(Self { + version, + capability, + extended_capability, + global_command, + global_status, + root_table_address, + context_command, + }) + } + } +} + +static PAGE_TABLE: Once> = Once::new(); diff --git a/framework/jinux-frame/src/arch/x86/iommu/second_stage.rs b/framework/jinux-frame/src/arch/x86/iommu/second_stage.rs new file mode 100644 index 000000000..80ed20893 --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/iommu/second_stage.rs @@ -0,0 +1,142 @@ +use pod::Pod; + +use crate::{ + config::ENTRY_COUNT, + vm::page_table::{PageTableEntryTrait, PageTableFlagsTrait}, +}; + +bitflags::bitflags! { + #[derive(Pod)] + #[repr(C)] + pub struct PageTableFlags : u64{ + /// Whether accesses to this page must snoop processor caches. + const SNOOP = 1 << 11; + + const DIRTY = 1 << 9; + + const ACCESSED = 1 << 8; + /// Whether this page table entry is the last entry. + const LAST_PAGE = 1 << 7; + + /// Ignore PAT, 1 if the scalable-mode PASID-table entry is not + /// used for effective memory-type determination. + const IGNORE_PAT = 1 << 6; + + /// Extended Memory Type, ignored by hardware when the + /// Extended Memory Type Enable (EMTE) field is Clear. + /// + /// When the EMTE field is Set, this field is used to compute effective + /// memory-type for second-stage-only and nested translations. + const EMT = 7 << 3; + + const WRITABLE = 1 << 1; + + const READABLE = 1 << 0; + + } +} + +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +pub struct PageTableEntry(u64); + +impl PageTableFlagsTrait for PageTableFlags { + fn new() -> Self { + Self::empty() + } + + fn set_present(self, present: bool) -> Self { + self + } + + fn set_writable(mut self, writable: bool) -> Self { + self.set(Self::WRITABLE, writable); + self + } + + fn set_readable(mut self, readable: bool) -> Self { + self.set(Self::READABLE, readable); + self + } + + fn set_accessible_by_user(self, accessible: bool) -> Self { + self + } + + fn set_executable(self, executable: bool) -> Self { + self + } + + fn is_present(&self) -> bool { + true + } + + fn writable(&self) -> bool { + self.contains(Self::WRITABLE) + } + + fn readable(&self) -> bool { + self.contains(Self::READABLE) + } + + fn executable(&self) -> bool { + true + } + + fn has_accessed(&self) -> bool { + self.contains(Self::ACCESSED) + } + + fn accessible_by_user(&self) -> bool { + true + } + + fn union(&self, other: &Self) -> Self { + (*self).union(*other) + } + + fn remove(&mut self, flags: &Self) { + self.remove(*flags) + } + + fn insert(&mut self, flags: &Self) { + self.insert(*flags) + } +} + +impl PageTableEntry { + const PHYS_MASK: usize = 0xFFFF_FFFF_F000; +} + +impl PageTableEntryTrait for PageTableEntry { + // bit 47~12 + type F = PageTableFlags; + fn new(paddr: crate::vm::Paddr, flags: PageTableFlags) -> Self { + Self(((paddr & Self::PHYS_MASK) as u64 | flags.bits) as u64) + } + + fn paddr(&self) -> crate::vm::Paddr { + (self.0 & Self::PHYS_MASK as u64) as usize + } + + fn flags(&self) -> PageTableFlags { + PageTableFlags::from_bits_truncate(self.0) + } + + fn is_unused(&self) -> bool { + self.paddr() == 0 + } + + fn update(&mut self, paddr: crate::vm::Paddr, flags: Self::F) { + self.0 = (paddr & Self::PHYS_MASK) as u64 | flags.bits + } + + fn clear(&mut self) { + self.0 = 0; + } + + fn page_index(va: crate::vm::Vaddr, level: usize) -> usize { + debug_assert!(level >= 1 && level <= 5); + va >> (12 + 9 * (level - 1)) & (ENTRY_COUNT - 1) + } +} diff --git a/framework/jinux-frame/src/arch/x86/kernel/acpi.rs b/framework/jinux-frame/src/arch/x86/kernel/acpi.rs deleted file mode 100644 index 47bcdeede..000000000 --- a/framework/jinux-frame/src/arch/x86/kernel/acpi.rs +++ /dev/null @@ -1,50 +0,0 @@ -use core::ptr::NonNull; - -use crate::{config, vm::paddr_to_vaddr}; -use acpi::{AcpiHandler, AcpiTables}; -use limine::LimineRsdpRequest; -use log::info; -use spin::{Mutex, Once}; - -/// RSDP information, key is the signature, value is the virtual address of the signature -pub static ACPI_TABLES: Once>> = Once::new(); - -#[derive(Debug, Clone)] -pub struct AcpiMemoryHandler {} - -impl AcpiHandler for AcpiMemoryHandler { - unsafe fn map_physical_region( - &self, - physical_address: usize, - size: usize, - ) -> acpi::PhysicalMapping { - acpi::PhysicalMapping::new( - physical_address, - NonNull::new(paddr_to_vaddr(physical_address) as *mut T).unwrap(), - size, - size, - self.clone(), - ) - } - - fn unmap_physical_region(region: &acpi::PhysicalMapping) {} -} - -static RSDP_REQUEST: LimineRsdpRequest = LimineRsdpRequest::new(0); - -pub fn init() { - let response = RSDP_REQUEST - .get_response() - .get() - .expect("Need RSDP address"); - let rsdp = response.address.as_ptr().unwrap().addr() - config::PHYS_OFFSET; - let acpi_tables = - unsafe { AcpiTables::from_rsdp(AcpiMemoryHandler {}, rsdp as usize).unwrap() }; - - for (signature, sdt) in acpi_tables.sdts.iter() { - info!("ACPI found signature:{:?}", signature); - } - ACPI_TABLES.call_once(|| Mutex::new(acpi_tables)); - - info!("acpi init complete"); -} diff --git a/framework/jinux-frame/src/arch/x86/kernel/acpi/dmar.rs b/framework/jinux-frame/src/arch/x86/kernel/acpi/dmar.rs new file mode 100644 index 000000000..058edf6cf --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/kernel/acpi/dmar.rs @@ -0,0 +1,129 @@ +use core::{fmt::Debug, mem::size_of, slice::Iter}; + +use acpi::{sdt::Signature, AcpiTable}; +use alloc::vec::Vec; + +use crate::vm::paddr_to_vaddr; + +use super::{ + remapping::{Andd, Atsr, Drhd, Rhsa, Rmrr, Satc, Sidp}, + SdtHeaderWrapper, +}; + +/// DMA Remapping structure. When IOMMU is enabled, the structure should be present in the ACPI table, +/// and the user can use the DRHD table in this structure to obtain the register base addresses used to configure functions such as IOMMU. +#[derive(Debug)] +pub struct Dmar { + header: DmarHeader, + /// Actual size is indicated by `length` in header + remapping_structures: Vec, // Followed by `n` entries with format `Remapping Structures` +} + +/// A DMAR structure contains serval remapping structures. Among these structures, +/// one DRHD must exist, the others must not exist at all. +#[derive(Debug)] +pub enum Remapping { + Drhd(Drhd), + Rmrr(Rmrr), + Atsr(Atsr), + Rhsa(Rhsa), + Andd(Andd), + Satc(Satc), + Sidp(Sidp), +} + +#[derive(Debug, Clone, Copy)] +#[repr(u16)] +pub enum RemappingType { + DRHD = 0, + RMRR = 1, + ATSR = 2, + RHSA = 3, + ANDD = 4, + SATC = 5, + SIDP = 6, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +struct DmarHeader { + header: SdtHeaderWrapper, + host_address_width: u8, + flags: u8, + reserved: [u8; 10], +} + +impl AcpiTable for DmarHeader { + fn header(&self) -> &acpi::sdt::SdtHeader { + &self.header.0 + } +} + +impl Dmar { + /// Create a instance from ACPI table. + pub fn new() -> Option { + let acpi_table_lock = super::ACPI_TABLES.get().unwrap().lock(); + // Safety: The DmarHeader is the header for the DMAR structure, it fits all the field described in Intel manual. + let dmar_mapping = unsafe { + if let Some(temp) = acpi_table_lock + .get_sdt::(Signature::DMAR) + .unwrap() + { + temp + } else { + return None; + } + }; + + let physical_address = dmar_mapping.physical_start(); + let len = dmar_mapping.mapped_length(); + // Safety: The target address is the start of the remapping structures, + // and the length is valid since the value is read from the length field in SDTHeader minus the size of DMAR header. + let dmar_slice = unsafe { + core::slice::from_raw_parts_mut( + paddr_to_vaddr(physical_address + size_of::()) as *mut u8, + len - size_of::(), + ) + }; + + let mut remapping_structures = Vec::new(); + let mut index = 0; + let mut remain_length = len - size_of::(); + // Safety: Indexes and offsets are strictly followed by the manual. + unsafe { + while remain_length > 0 { + // Common header: type: u16, length: u16 + let length = *dmar_slice[index as usize + 2..index as usize + 4].as_ptr() as usize; + let typ = *dmar_slice[index as usize..index as usize + 2].as_ptr() as usize; + let bytes = &&dmar_slice[index as usize..index as usize + length]; + let remapping = match typ { + 0 => Remapping::Drhd(Drhd::from_bytes(bytes)), + 1 => Remapping::Rmrr(Rmrr::from_bytes(bytes)), + 2 => Remapping::Atsr(Atsr::from_bytes(bytes)), + 3 => Remapping::Rhsa(Rhsa::from_bytes(bytes)), + 4 => Remapping::Andd(Andd::from_bytes(bytes)), + 5 => Remapping::Satc(Satc::from_bytes(bytes)), + 6 => Remapping::Sidp(Sidp::from_bytes(bytes)), + _ => { + panic!("Unidentified remapping structure type"); + } + }; + // let temp = DeviceScope::from_bytes( + // &bytes[index as usize..index as usize + length], + // ); + remapping_structures.push(remapping); + index += length; + remain_length -= length; + } + } + + Some(Dmar { + header: *dmar_mapping, + remapping_structures: remapping_structures, + }) + } + + pub fn remapping_iter(&self) -> Iter<'_, Remapping> { + self.remapping_structures.iter() + } +} diff --git a/framework/jinux-frame/src/arch/x86/kernel/acpi/mod.rs b/framework/jinux-frame/src/arch/x86/kernel/acpi/mod.rs new file mode 100644 index 000000000..625da2e62 --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/kernel/acpi/mod.rs @@ -0,0 +1,110 @@ +pub mod dmar; +pub mod remapping; + +use core::{ + ops::{Deref, DerefMut}, + ptr::NonNull, +}; + +use crate::{config, vm::paddr_to_vaddr}; +use acpi::{sdt::SdtHeader, AcpiHandler, AcpiTable, AcpiTables}; +use limine::LimineRsdpRequest; +use log::info; +use spin::{Mutex, Once}; + +/// RSDP information, key is the signature, value is the virtual address of the signature +pub static ACPI_TABLES: Once>> = Once::new(); + +/// Sdt header wrapper, user can use this structure to easily derive Debug, get table information without creating a new struture. +/// +/// For example, in DMAR (DMA Remapping) structure, +/// we can use the following code to get some information of DMAR, including address, length: +/// +/// ```rust +/// acpi_table.get_sdt::(Signature::DMAR).unwrap() +/// ``` +/// +#[derive(Clone, Copy)] +pub struct SdtHeaderWrapper(SdtHeader); + +impl AcpiTable for SdtHeaderWrapper { + fn header(&self) -> &acpi::sdt::SdtHeader { + &self.0 + } +} + +impl Deref for SdtHeaderWrapper { + type Target = SdtHeader; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for SdtHeaderWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl core::fmt::Debug for SdtHeaderWrapper { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let length = self.0.length; + let oem_revision = self.0.oem_revision; + let creator_id = self.0.creator_id; + let creator_revision = self.0.creator_revision; + + f.debug_struct("Dmar") + .field("signature", &self.0.signature) + .field("length", &length) + .field("revision", &self.0.revision) + .field("checksum", &self.0.checksum) + .field("oem_id", &self.0.oem_id()) + .field("oem_table_id", &self.0.oem_table_id()) + .field("oem_revision", &oem_revision) + .field("creator_id", &creator_id) + .field("creator_revision", &creator_revision) + .finish() + } +} + +#[derive(Debug, Clone)] +pub struct AcpiMemoryHandler {} + +impl AcpiHandler for AcpiMemoryHandler { + unsafe fn map_physical_region( + &self, + physical_address: usize, + size: usize, + ) -> acpi::PhysicalMapping { + acpi::PhysicalMapping::new( + physical_address, + NonNull::new(paddr_to_vaddr(physical_address) as *mut T).unwrap(), + size, + size, + self.clone(), + ) + } + + fn unmap_physical_region(region: &acpi::PhysicalMapping) {} +} + +static RSDP_REQUEST: LimineRsdpRequest = LimineRsdpRequest::new(0); + +pub fn init() { + let response = RSDP_REQUEST + .get_response() + .get() + .expect("Need RSDP address"); + let rsdp = response.address.as_ptr().unwrap().addr() - config::PHYS_OFFSET; + // Safety: The RSDP is the value provided by bootloader. + let acpi_tables = + unsafe { AcpiTables::from_rsdp(AcpiMemoryHandler {}, rsdp as usize).unwrap() }; + + for (signature, sdt) in acpi_tables.sdts.iter() { + info!("ACPI found signature:{:?}", signature); + } + ACPI_TABLES.call_once(|| Mutex::new(acpi_tables)); + + info!("acpi init complete"); +} diff --git a/framework/jinux-frame/src/arch/x86/kernel/acpi/remapping.rs b/framework/jinux-frame/src/arch/x86/kernel/acpi/remapping.rs new file mode 100644 index 000000000..6d25aa8c8 --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/kernel/acpi/remapping.rs @@ -0,0 +1,283 @@ +//! Remapping structures of DMAR table. +//! This file defines these structures and provides a "Debug" implementation to see the value inside these structures. +//! Most of the introduction are copied from Intel vt-directed-io-specification. + +use core::{fmt::Debug, mem::size_of}; + +use alloc::{string::String, vec::Vec}; + +/// DMA-remapping hardware unit definition (DRHD). +/// +/// A DRHD structure uniquely represents a remapping hardware unit present in the platform. +/// There must be at least one instance of this structure for each +/// PCI segment in the platform. +#[derive(Debug, Clone)] +pub struct Drhd { + header: DrhdHeader, + device_scopes: Vec, +} + +impl Drhd { + pub fn register_base_addr(&self) -> u64 { + self.header.register_base_addr + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct DrhdHeader { + typ: u16, + length: u16, + flags: u8, + size: u8, + segment_num: u16, + register_base_addr: u64, +} + +/// Reserved Memory Region Reporting (RMRR). +/// +/// BIOS allocated reserved memory ranges that may be DMA targets. +/// It may report each such reserved memory region through the RMRR structures, along +/// with the devices that requires access to the specified reserved memory region. +#[derive(Debug, Clone)] +pub struct Rmrr { + header: RmrrHeader, + device_scopes: Vec, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct RmrrHeader { + typ: u16, + length: u16, + reserved: u16, + segment_num: u16, + reserved_memory_region_base_addr: u64, + reserved_memory_region_limit_addr: u64, +} + +/// Root Port ATS Capability Reporting (ATSR). +/// +/// This structure is applicable only for platforms supporting Device-TLBs as reported through the +/// Extended Capability Register. +#[derive(Debug, Clone)] +pub struct Atsr { + header: AtsrHeader, + device_scopes: Vec, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct AtsrHeader { + typ: u16, + length: u16, + flags: u8, + reserved: u8, + segment_num: u16, +} + +/// Remapping Hardware Status Affinity (RHSA). +/// +/// It is applicable for platforms supporting non-uniform memory (NUMA), where Remapping hardware units spans across nodes. +/// This optional structure provides the association between each Remapping hardware unit (identified by its +/// espective Base Address) and the proximity domain to which that hardware unit belongs. +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct Rhsa { + typ: u16, + length: u16, + flags: u32, + register_base_addr: u64, + proximity_domain: u32, +} + +/// ACPI Name-space Device Declaration (ANDD). +/// +/// An ANDD structure uniquely represents an ACPI name-space +/// enumerated device capable of issuing DMA requests in the platform. +#[derive(Debug, Clone)] +pub struct Andd { + header: AnddHeader, + acpi_object_name: String, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct AnddHeader { + typ: u16, + length: u16, + reserved: [u8; 3], + acpi_device_num: u8, +} + +/// SoC Integrated Address Translation Cache (SATC). +/// +/// The SATC reporting structure identifies devices that have address translation cache (ATC), +/// as defined by the PCI Express Base Specification. +#[derive(Debug, Clone)] +pub struct Satc { + header: SatcHeader, + device_scopes: Vec, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct SatcHeader { + typ: u16, + length: u16, + flags: u8, + reserved: u8, + segment_num: u16, +} + +/// SoC Integrated Device Property Reporting (SIDP). +/// +/// The (SIDP) reporting structure identifies devices that have special +/// properties and that may put restrictions on how system software must configure remapping +/// structures that govern such devices in a platform where remapping hardware is enabled. +/// +#[derive(Debug, Clone)] +pub struct Sidp { + header: SidpHeader, + device_scopes: Vec, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct SidpHeader { + typ: u16, + length: u16, + reserved: u16, + segment_num: u16, +} + +/// The Device Scope Structure is made up of Device Scope Entries. Each Device Scope Entry may be +/// used to indicate a PCI endpoint device +#[derive(Debug, Clone)] +pub struct DeviceScope { + header: DeviceScopeHeader, + path: Vec<(u8, u8)>, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct DeviceScopeHeader { + typ: u8, + length: u8, + flags: u8, + reserved: u8, + enum_id: u8, + start_bus_number: u8, +} + +macro_rules! impl_from_bytes { + ($(($struct:tt,$header_struct:tt,$dst_name:ident)),*) => { + $(impl $struct { + /// Create instance from bytes + /// + /// # Safety + /// + /// User must ensure the bytes is valid. + /// + pub unsafe fn from_bytes(bytes: &[u8]) -> Self { + let length = u16_from_slice(&bytes[2..4]) as usize; + debug_assert_eq!(length, bytes.len()); + + let mut index = core::mem::size_of::<$header_struct>(); + let mut remain_length = length - core::mem::size_of::<$header_struct>(); + let mut $dst_name = Vec::new(); + while remain_length > 0 { + let length = *bytes[index + 1..index + 2].as_ptr() as usize; + let temp = DeviceScope::from_bytes( + &bytes[index..index + length], + ); + $dst_name.push(temp); + index += length; + remain_length -= length; + } + + let header = *(bytes.as_ptr() as *const $header_struct); + Self{ + header, + $dst_name + } + } + })* + }; +} + +impl_from_bytes!( + (Drhd, DrhdHeader, device_scopes), + (Rmrr, RmrrHeader, device_scopes), + (Atsr, AtsrHeader, device_scopes), + (Satc, SatcHeader, device_scopes), + (Sidp, SidpHeader, device_scopes) +); + +impl DeviceScope { + /// Create instance from bytes + /// + /// # Safety + /// + /// User must ensure the bytes is valid. + /// + unsafe fn from_bytes(bytes: &[u8]) -> Self { + let length = bytes[1] as u32; + debug_assert_eq!(length, bytes.len() as u32); + let header = *(bytes.as_ptr() as *const DeviceScopeHeader); + + let mut index = size_of::(); + let mut remain_length = length - index as u32; + let mut path = Vec::new(); + while remain_length > 0 { + let temp: (u8, u8) = *(bytes[index..index + 2].as_ptr() as *const (u8, u8)); + path.push(temp); + index += 2; + remain_length -= 2; + } + + Self { header, path } + } +} + +impl Rhsa { + /// Create instance from bytes + /// + /// # Safety + /// + /// User must ensure the bytes is valid. + /// + pub unsafe fn from_bytes(bytes: &[u8]) -> Self { + let length = u16_from_slice(&bytes[2..4]) as u32; + debug_assert_eq!(length, bytes.len() as u32); + let result = *(bytes.as_ptr() as *const Self); + result + } +} + +impl Andd { + /// Create instance from bytes + /// + /// # Safety + /// + /// User must ensure the bytes is valid. + /// + pub unsafe fn from_bytes(bytes: &[u8]) -> Self { + let length = u16_from_slice(&bytes[2..4]) as usize; + debug_assert_eq!(length, bytes.len()); + + let index = core::mem::size_of::(); + let remain_length = length - core::mem::size_of::(); + let string = String::from_utf8(bytes[index..index + length].to_vec()).unwrap(); + + let header = *(bytes.as_ptr() as *const AnddHeader); + Self { + header, + acpi_object_name: string, + } + } +} + +fn u16_from_slice(input: &[u8]) -> u16 { + u16::from_ne_bytes(input[0..size_of::()].try_into().unwrap()) +} diff --git a/framework/jinux-frame/src/arch/x86/mm/mod.rs b/framework/jinux-frame/src/arch/x86/mm/mod.rs index 82ee5f636..11d13d851 100644 --- a/framework/jinux-frame/src/arch/x86/mm/mod.rs +++ b/framework/jinux-frame/src/arch/x86/mm/mod.rs @@ -2,11 +2,11 @@ use alloc::{collections::BTreeMap, fmt, vec::Vec}; use limine::{LimineMemmapEntry, LimineMemmapRequest, LimineMemoryMapEntryType}; use log::debug; use pod::Pod; -use spin::{Mutex, Once}; +use spin::Mutex; use x86_64::structures::paging::PhysFrame; use crate::{ - config::{ENTRY_COUNT, PAGE_SIZE}, + config::ENTRY_COUNT, vm::{ page_table::{table_of, PageTableEntryTrait, PageTableFlagsTrait}, MemoryRegions, MemoryRegionsType, Paddr, diff --git a/framework/jinux-frame/src/arch/x86/mod.rs b/framework/jinux-frame/src/arch/x86/mod.rs index db3c37b03..5509a4f0d 100644 --- a/framework/jinux-frame/src/arch/x86/mod.rs +++ b/framework/jinux-frame/src/arch/x86/mod.rs @@ -1,6 +1,7 @@ mod boot; pub(crate) mod cpu; pub mod device; +pub mod iommu; pub(crate) mod irq; mod kernel; pub(crate) mod mm; @@ -10,7 +11,7 @@ pub(crate) mod timer; use alloc::fmt; use core::fmt::Write; use kernel::apic::ioapic; -use log::info; +use log::{info, warn}; pub(crate) fn before_all_init() { enable_common_cpu_features(); @@ -32,6 +33,10 @@ pub(crate) fn after_all_init() { kernel::pic::enable(); } } + match iommu::init() { + Ok(_) => {} + Err(err) => warn!("IOMMU initialization error:{:?}", err), + } timer::init(); // Some driver like serial may use PIC kernel::pic::init(); diff --git a/framework/jinux-frame/src/bus/mod.rs b/framework/jinux-frame/src/bus/mod.rs new file mode 100644 index 000000000..7652d2c40 --- /dev/null +++ b/framework/jinux-frame/src/bus/mod.rs @@ -0,0 +1 @@ +pub mod pci; diff --git a/framework/jinux-frame/src/bus/pci/mod.rs b/framework/jinux-frame/src/bus/pci/mod.rs new file mode 100644 index 000000000..dab095252 --- /dev/null +++ b/framework/jinux-frame/src/bus/pci/mod.rs @@ -0,0 +1,46 @@ +use core::iter; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct PciDeviceLocation { + pub bus: u8, + /// Max 31 + pub device: u8, + /// Max 7 + pub function: u8, +} + +impl PciDeviceLocation { + pub const MIN_BUS: u8 = 0; + pub const MAX_BUS: u8 = 255; + pub const MIN_DEVICE: u8 = 0; + pub const MAX_DEVICE: u8 = 31; + pub const MIN_FUNCTION: u8 = 0; + pub const MAX_FUNCTION: u8 = 7; + /// By encoding bus, device, and function into u32, user can access a PCI device in x86 by passing in this value. + #[inline(always)] + pub fn encode_as_x86_address_value(self) -> u32 { + // 1 << 31: Configuration enable + (1 << 31) + | ((self.bus as u32) << 16) + | (((self.device as u32) & 0b11111) << 11) + | (((self.function as u32) & 0b111) << 8) + } + + /// Returns an iterator that enumerates all possible PCI device locations. + pub fn all() -> impl Iterator { + iter::from_generator(|| { + for bus in Self::MIN_BUS..=Self::MAX_BUS { + for device in Self::MIN_DEVICE..=Self::MAX_DEVICE { + for function in Self::MIN_FUNCTION..=Self::MAX_FUNCTION { + let loc = PciDeviceLocation { + bus, + device, + function, + }; + yield loc; + } + } + } + }) + } +} diff --git a/framework/jinux-frame/src/lib.rs b/framework/jinux-frame/src/lib.rs index 5efb51168..9b87a162b 100644 --- a/framework/jinux-frame/src/lib.rs +++ b/framework/jinux-frame/src/lib.rs @@ -12,10 +12,13 @@ #![feature(strict_provenance)] #![feature(const_trait_impl)] #![feature(const_ops)] +#![feature(generators)] +#![feature(iter_from_generator)] extern crate alloc; pub mod arch; +pub mod bus; pub mod config; pub mod cpu; mod error; diff --git a/framework/jinux-frame/src/vm/frame.rs b/framework/jinux-frame/src/vm/frame.rs index 4d11c8636..dc116cb6d 100644 --- a/framework/jinux-frame/src/vm/frame.rs +++ b/framework/jinux-frame/src/vm/frame.rs @@ -4,7 +4,7 @@ use core::{ ops::{BitAnd, BitOr, Not}, }; -use crate::{config::PAGE_SIZE, prelude::*, Error}; +use crate::{arch::iommu, config::PAGE_SIZE, prelude::*, Error}; use super::frame_allocator; use super::{Paddr, VmIo}; @@ -33,15 +33,35 @@ impl VmFrameVec { /// For more information, see `VmAllocOptions`. pub fn allocate(options: &VmAllocOptions) -> Result { let page_size = options.page_size; - let frames = if options.is_contiguous { - frame_allocator::alloc_continuous(options.page_size).ok_or(Error::NoMemory)? + let mut flags = VmFrameFlags::empty(); + if options.can_dma { + flags.insert(VmFrameFlags::CAN_DMA); + } + let mut frames = if options.is_contiguous { + frame_allocator::alloc_continuous(options.page_size, flags).ok_or(Error::NoMemory)? } else { let mut frame_list = Vec::new(); for _ in 0..page_size { - frame_list.push(frame_allocator::alloc().ok_or(Error::NoMemory)?); + frame_list.push(frame_allocator::alloc(flags).ok_or(Error::NoMemory)?); } frame_list }; + if options.can_dma { + for frame in frames.iter_mut() { + // Safety: The address is controlled by frame allocator. + unsafe { + if let Err(err) = iommu::map(frame.start_paddr(), frame.start_paddr()) { + match err { + // do nothing + iommu::IommuError::NoIommu => {} + iommu::IommuError::ModificationError(err) => { + panic!("iommu map error:{:?}", err) + } + } + } + } + } + } let frame_vec = Self(frames); if !options.uninit { frame_vec.zero(); @@ -204,6 +224,7 @@ pub struct VmAllocOptions { page_size: usize, is_contiguous: bool, uninit: bool, + can_dma: bool, } impl VmAllocOptions { @@ -213,6 +234,7 @@ impl VmAllocOptions { page_size: len, is_contiguous: false, uninit: false, + can_dma: false, } } @@ -243,13 +265,15 @@ impl VmAllocOptions { /// In a TEE environment, DMAable pages are untrusted pages shared with /// the VMM. pub fn can_dma(&mut self, can_dma: bool) -> &mut Self { - todo!() + self.can_dma = can_dma; + self } } bitflags::bitflags! { pub(crate) struct VmFrameFlags : usize{ const NEED_DEALLOC = 1 << 63; + const CAN_DMA = 1 << 62; } } @@ -314,7 +338,7 @@ impl VmFrame { /// In a TEE environment, DMAable pages are untrusted pages shared with /// the VMM. pub fn can_dma(&self) -> bool { - todo!() + (*self.frame_index & VmFrameFlags::CAN_DMA.bits()) != 0 } fn need_dealloc(&self) -> bool { @@ -371,6 +395,17 @@ impl VmIo for VmFrame { impl Drop for VmFrame { fn drop(&mut self) { if self.need_dealloc() && Arc::strong_count(&self.frame_index) == 1 { + if self.can_dma() { + if let Err(err) = iommu::unmap(self.start_paddr()) { + match err { + // do nothing + iommu::IommuError::NoIommu => {} + iommu::IommuError::ModificationError(err) => { + panic!("iommu map error:{:?}", err) + } + } + } + } unsafe { frame_allocator::dealloc(self.frame_index()); } diff --git a/framework/jinux-frame/src/vm/frame_allocator.rs b/framework/jinux-frame/src/vm/frame_allocator.rs index 9061abc81..8385d2083 100644 --- a/framework/jinux-frame/src/vm/frame_allocator.rs +++ b/framework/jinux-frame/src/vm/frame_allocator.rs @@ -9,16 +9,16 @@ use super::{frame::VmFrameFlags, MemoryRegions, MemoryRegionsType, VmFrame}; static FRAME_ALLOCATOR: Once> = Once::new(); -pub fn alloc() -> Option { +pub(crate) fn alloc(flags: VmFrameFlags) -> Option { FRAME_ALLOCATOR .get() .unwrap() .lock() .alloc(1) - .map(|pa| unsafe { VmFrame::new(pa * PAGE_SIZE, VmFrameFlags::NEED_DEALLOC) }) + .map(|pa| unsafe { VmFrame::new(pa * PAGE_SIZE, flags.union(VmFrameFlags::NEED_DEALLOC)) }) } -pub fn alloc_continuous(frame_count: usize) -> Option> { +pub(crate) fn alloc_continuous(frame_count: usize, flags: VmFrameFlags) -> Option> { FRAME_ALLOCATOR .get() .unwrap() @@ -28,7 +28,10 @@ pub fn alloc_continuous(frame_count: usize) -> Option> { let mut vector = Vec::new(); unsafe { for i in 0..frame_count { - let frame = VmFrame::new((start + i) * PAGE_SIZE, VmFrameFlags::NEED_DEALLOC); + let frame = VmFrame::new( + (start + i) * PAGE_SIZE, + flags.union(VmFrameFlags::NEED_DEALLOC), + ); vector.push(frame); } } @@ -36,8 +39,8 @@ pub fn alloc_continuous(frame_count: usize) -> Option> { }) } -pub(crate) fn alloc_zero() -> Option { - let frame = alloc()?; +pub(crate) fn alloc_zero(flags: VmFrameFlags) -> Option { + let frame = alloc(flags)?; frame.zero(); Some(frame) } diff --git a/framework/jinux-frame/src/vm/memory_set.rs b/framework/jinux-frame/src/vm/memory_set.rs index acb6d4081..57646a0d6 100644 --- a/framework/jinux-frame/src/vm/memory_set.rs +++ b/framework/jinux-frame/src/vm/memory_set.rs @@ -1,4 +1,7 @@ -use super::page_table::{PageTable, PageTableConfig}; +use super::{ + frame::VmFrameFlags, + page_table::{PageTable, PageTableConfig}, +}; use crate::{ arch::mm::{PageTableEntry, PageTableFlags}, config::{PAGE_SIZE, PHYS_OFFSET}, @@ -33,7 +36,7 @@ impl MapArea { pub fn clone(&self) -> Self { let mut mapper = BTreeMap::new(); for (&va, old) in &self.mapper { - let new = frame_allocator::alloc().unwrap(); + let new = frame_allocator::alloc(VmFrameFlags::empty()).unwrap(); unsafe { new.as_slice().copy_from_slice(old.as_slice()); } @@ -94,7 +97,7 @@ impl MapArea { match self.mapper.entry(va) { Entry::Occupied(e) => e.get().start_paddr(), Entry::Vacant(e) => e - .insert(frame_allocator::alloc_zero().unwrap()) + .insert(frame_allocator::alloc_zero(VmFrameFlags::empty()).unwrap()) .start_paddr(), } } diff --git a/framework/jinux-frame/src/vm/page_table.rs b/framework/jinux-frame/src/vm/page_table.rs index 3f27a4088..e35ba804b 100644 --- a/framework/jinux-frame/src/vm/page_table.rs +++ b/framework/jinux-frame/src/vm/page_table.rs @@ -1,4 +1,5 @@ use super::{ + frame::VmFrameFlags, frame_allocator, paddr_to_vaddr, VmAllocOptions, VmFrameVec, {Paddr, Vaddr}, }; use crate::{ @@ -109,7 +110,7 @@ pub struct PageTable { impl PageTable { pub fn new(config: PageTableConfig) -> Self { - let root_frame = frame_allocator::alloc_zero().unwrap(); + let root_frame = frame_allocator::alloc_zero(VmFrameFlags::empty()).unwrap(); Self { root_pa: root_frame.start_paddr(), tables: vec![root_frame],