mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 18:03:25 +00:00
Support IOMMU
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
829575b3a6
commit
a47b98b160
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -464,6 +464,7 @@ name = "jinux-boot"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"cfg-if",
|
||||||
"runner-utils",
|
"runner-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -7,7 +7,9 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
runner-utils = "0.0.2"
|
runner-utils = "0.0.2"
|
||||||
anyhow = "1.0.32"
|
anyhow = "1.0.32"
|
||||||
|
cfg-if = "1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["limine"]
|
default = ["limine"]
|
||||||
limine= []
|
limine = []
|
||||||
|
iommu = []
|
||||||
|
@ -10,24 +10,12 @@ use std::{
|
|||||||
const COMMON_ARGS: &[&str] = &[
|
const COMMON_ARGS: &[&str] = &[
|
||||||
"--no-reboot",
|
"--no-reboot",
|
||||||
"-machine",
|
"-machine",
|
||||||
"q35",
|
"q35,kernel-irqchip=split",
|
||||||
"-enable-kvm",
|
"-enable-kvm",
|
||||||
"-cpu",
|
"-cpu",
|
||||||
"Icelake-Server,+x2apic",
|
"Icelake-Server,+x2apic",
|
||||||
"-m",
|
"-m",
|
||||||
"2G",
|
"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",
|
"-monitor",
|
||||||
"vc",
|
"vc",
|
||||||
"-serial",
|
"-serial",
|
||||||
@ -36,6 +24,44 @@ const COMMON_ARGS: &[&str] = &[
|
|||||||
"none",
|
"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 RUN_ARGS: &[&str] = &[];
|
||||||
const TEST_ARGS: &[&str] = &[];
|
const TEST_ARGS: &[&str] = &[];
|
||||||
const TEST_TIMEOUT_SECS: u64 = 30;
|
const TEST_TIMEOUT_SECS: u64 = 30;
|
||||||
@ -62,13 +88,12 @@ fn main() -> anyhow::Result<()> {
|
|||||||
a.join(str.add(".iso"))
|
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 mut qemu_cmd = Command::new("qemu-system-x86_64");
|
||||||
|
|
||||||
let binary_kind = runner_utils::binary_kind(&kernel_binary_path);
|
let binary_kind = runner_utils::binary_kind(&kernel_binary_path);
|
||||||
let mut qemu_args = COMMON_ARGS.clone().to_vec();
|
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");
|
qemu_args.push("-drive");
|
||||||
let binding = create_fs_image(kernel_binary_path.as_path())?;
|
let binding = create_fs_image(kernel_binary_path.as_path())?;
|
||||||
qemu_args.push(binding.as_str());
|
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<String> {
|
fn create_fs_image(path: &Path) -> anyhow::Result<String> {
|
||||||
let mut fs_img_path = path.parent().unwrap().to_str().unwrap().to_string();
|
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");
|
fs_img_path.push_str("/fs.img");
|
||||||
let path = Path::new(fs_img_path.as_str());
|
let path = Path::new(fs_img_path.as_str());
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
|
313
framework/jinux-frame/src/arch/x86/iommu/context_table.rs
Normal file
313
framework/jinux-frame/src/arch/x86/iommu/context_table.rs
Normal file
@ -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<Paddr, ContextTable>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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::<RootEntry>(device_id.bus as usize * size_of::<RootEntry>())
|
||||||
|
.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::<RootEntry>(device_id.bus as usize * size_of::<RootEntry>(), &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<PageTableEntry>,
|
||||||
|
) {
|
||||||
|
let context_table = self.get_or_create_context_table(device_id);
|
||||||
|
|
||||||
|
let bus_entry = context_table
|
||||||
|
.entries_frame
|
||||||
|
.read_val::<ContextEntry>(
|
||||||
|
(device_id.device as usize * 8 + device_id.function as usize) as usize
|
||||||
|
* size_of::<ContextEntry>(),
|
||||||
|
)
|
||||||
|
.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::<ContextEntry>(
|
||||||
|
(device_id.device as usize * 8 + device_id.function as usize) as usize
|
||||||
|
* size_of::<ContextEntry>(),
|
||||||
|
&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<Paddr, PageTable<PageTableEntry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<PageTableEntry> {
|
||||||
|
let bus_entry = self
|
||||||
|
.entries_frame
|
||||||
|
.read_val::<ContextEntry>(
|
||||||
|
(device.device as usize * 8 + device.function as usize) as usize
|
||||||
|
* size_of::<ContextEntry>(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if !bus_entry.present() {
|
||||||
|
let table: PageTable<PageTableEntry> = 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::<ContextEntry>(
|
||||||
|
(device.device as usize * 8 + device.function as usize) as usize
|
||||||
|
* size_of::<ContextEntry>(),
|
||||||
|
&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))
|
||||||
|
}
|
||||||
|
}
|
173
framework/jinux-frame/src/arch/x86/iommu/mod.rs
Normal file
173
framework/jinux-frame/src/arch/x86/iommu/mod.rs
Normal file
@ -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<PageTableEntry> = 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<Self> {
|
||||||
|
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<Mutex<RootTable>> = Once::new();
|
142
framework/jinux-frame/src/arch/x86/iommu/second_stage.rs
Normal file
142
framework/jinux-frame/src/arch/x86/iommu/second_stage.rs
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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<Mutex<AcpiTables<AcpiMemoryHandler>>> = Once::new();
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct AcpiMemoryHandler {}
|
|
||||||
|
|
||||||
impl AcpiHandler for AcpiMemoryHandler {
|
|
||||||
unsafe fn map_physical_region<T>(
|
|
||||||
&self,
|
|
||||||
physical_address: usize,
|
|
||||||
size: usize,
|
|
||||||
) -> acpi::PhysicalMapping<Self, T> {
|
|
||||||
acpi::PhysicalMapping::new(
|
|
||||||
physical_address,
|
|
||||||
NonNull::new(paddr_to_vaddr(physical_address) as *mut T).unwrap(),
|
|
||||||
size,
|
|
||||||
size,
|
|
||||||
self.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unmap_physical_region<T>(region: &acpi::PhysicalMapping<Self, T>) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
129
framework/jinux-frame/src/arch/x86/kernel/acpi/dmar.rs
Normal file
129
framework/jinux-frame/src/arch/x86/kernel/acpi/dmar.rs
Normal file
@ -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<Remapping>, // 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<Self> {
|
||||||
|
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::<DmarHeader>(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::<DmarHeader>()) as *mut u8,
|
||||||
|
len - size_of::<DmarHeader>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut remapping_structures = Vec::new();
|
||||||
|
let mut index = 0;
|
||||||
|
let mut remain_length = len - size_of::<DmarHeader>();
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
110
framework/jinux-frame/src/arch/x86/kernel/acpi/mod.rs
Normal file
110
framework/jinux-frame/src/arch/x86/kernel/acpi/mod.rs
Normal file
@ -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<Mutex<AcpiTables<AcpiMemoryHandler>>> = 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::<SdtHeaderWrapper>(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<T>(
|
||||||
|
&self,
|
||||||
|
physical_address: usize,
|
||||||
|
size: usize,
|
||||||
|
) -> acpi::PhysicalMapping<Self, T> {
|
||||||
|
acpi::PhysicalMapping::new(
|
||||||
|
physical_address,
|
||||||
|
NonNull::new(paddr_to_vaddr(physical_address) as *mut T).unwrap(),
|
||||||
|
size,
|
||||||
|
size,
|
||||||
|
self.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmap_physical_region<T>(region: &acpi::PhysicalMapping<Self, T>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
283
framework/jinux-frame/src/arch/x86/kernel/acpi/remapping.rs
Normal file
283
framework/jinux-frame/src/arch/x86/kernel/acpi/remapping.rs
Normal file
@ -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<DeviceScope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<DeviceScope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<DeviceScope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<DeviceScope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<DeviceScope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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::<DeviceScopeHeader>();
|
||||||
|
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::<AnddHeader>();
|
||||||
|
let remain_length = length - core::mem::size_of::<AnddHeader>();
|
||||||
|
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::<u16>()].try_into().unwrap())
|
||||||
|
}
|
@ -2,11 +2,11 @@ use alloc::{collections::BTreeMap, fmt, vec::Vec};
|
|||||||
use limine::{LimineMemmapEntry, LimineMemmapRequest, LimineMemoryMapEntryType};
|
use limine::{LimineMemmapEntry, LimineMemmapRequest, LimineMemoryMapEntryType};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use pod::Pod;
|
use pod::Pod;
|
||||||
use spin::{Mutex, Once};
|
use spin::Mutex;
|
||||||
use x86_64::structures::paging::PhysFrame;
|
use x86_64::structures::paging::PhysFrame;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{ENTRY_COUNT, PAGE_SIZE},
|
config::ENTRY_COUNT,
|
||||||
vm::{
|
vm::{
|
||||||
page_table::{table_of, PageTableEntryTrait, PageTableFlagsTrait},
|
page_table::{table_of, PageTableEntryTrait, PageTableFlagsTrait},
|
||||||
MemoryRegions, MemoryRegionsType, Paddr,
|
MemoryRegions, MemoryRegionsType, Paddr,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
mod boot;
|
mod boot;
|
||||||
pub(crate) mod cpu;
|
pub(crate) mod cpu;
|
||||||
pub mod device;
|
pub mod device;
|
||||||
|
pub mod iommu;
|
||||||
pub(crate) mod irq;
|
pub(crate) mod irq;
|
||||||
mod kernel;
|
mod kernel;
|
||||||
pub(crate) mod mm;
|
pub(crate) mod mm;
|
||||||
@ -10,7 +11,7 @@ pub(crate) mod timer;
|
|||||||
use alloc::fmt;
|
use alloc::fmt;
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use kernel::apic::ioapic;
|
use kernel::apic::ioapic;
|
||||||
use log::info;
|
use log::{info, warn};
|
||||||
|
|
||||||
pub(crate) fn before_all_init() {
|
pub(crate) fn before_all_init() {
|
||||||
enable_common_cpu_features();
|
enable_common_cpu_features();
|
||||||
@ -32,6 +33,10 @@ pub(crate) fn after_all_init() {
|
|||||||
kernel::pic::enable();
|
kernel::pic::enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
match iommu::init() {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => warn!("IOMMU initialization error:{:?}", err),
|
||||||
|
}
|
||||||
timer::init();
|
timer::init();
|
||||||
// Some driver like serial may use PIC
|
// Some driver like serial may use PIC
|
||||||
kernel::pic::init();
|
kernel::pic::init();
|
||||||
|
1
framework/jinux-frame/src/bus/mod.rs
Normal file
1
framework/jinux-frame/src/bus/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod pci;
|
46
framework/jinux-frame/src/bus/pci/mod.rs
Normal file
46
framework/jinux-frame/src/bus/pci/mod.rs
Normal file
@ -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<Item = PciDeviceLocation> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -12,10 +12,13 @@
|
|||||||
#![feature(strict_provenance)]
|
#![feature(strict_provenance)]
|
||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
#![feature(const_ops)]
|
#![feature(const_ops)]
|
||||||
|
#![feature(generators)]
|
||||||
|
#![feature(iter_from_generator)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
|
pub mod bus;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
mod error;
|
mod error;
|
||||||
|
@ -4,7 +4,7 @@ use core::{
|
|||||||
ops::{BitAnd, BitOr, Not},
|
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::frame_allocator;
|
||||||
use super::{Paddr, VmIo};
|
use super::{Paddr, VmIo};
|
||||||
@ -33,15 +33,35 @@ impl VmFrameVec {
|
|||||||
/// For more information, see `VmAllocOptions`.
|
/// For more information, see `VmAllocOptions`.
|
||||||
pub fn allocate(options: &VmAllocOptions) -> Result<Self> {
|
pub fn allocate(options: &VmAllocOptions) -> Result<Self> {
|
||||||
let page_size = options.page_size;
|
let page_size = options.page_size;
|
||||||
let frames = if options.is_contiguous {
|
let mut flags = VmFrameFlags::empty();
|
||||||
frame_allocator::alloc_continuous(options.page_size).ok_or(Error::NoMemory)?
|
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 {
|
} else {
|
||||||
let mut frame_list = Vec::new();
|
let mut frame_list = Vec::new();
|
||||||
for _ in 0..page_size {
|
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
|
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);
|
let frame_vec = Self(frames);
|
||||||
if !options.uninit {
|
if !options.uninit {
|
||||||
frame_vec.zero();
|
frame_vec.zero();
|
||||||
@ -204,6 +224,7 @@ pub struct VmAllocOptions {
|
|||||||
page_size: usize,
|
page_size: usize,
|
||||||
is_contiguous: bool,
|
is_contiguous: bool,
|
||||||
uninit: bool,
|
uninit: bool,
|
||||||
|
can_dma: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmAllocOptions {
|
impl VmAllocOptions {
|
||||||
@ -213,6 +234,7 @@ impl VmAllocOptions {
|
|||||||
page_size: len,
|
page_size: len,
|
||||||
is_contiguous: false,
|
is_contiguous: false,
|
||||||
uninit: false,
|
uninit: false,
|
||||||
|
can_dma: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,13 +265,15 @@ impl VmAllocOptions {
|
|||||||
/// In a TEE environment, DMAable pages are untrusted pages shared with
|
/// In a TEE environment, DMAable pages are untrusted pages shared with
|
||||||
/// the VMM.
|
/// the VMM.
|
||||||
pub fn can_dma(&mut self, can_dma: bool) -> &mut Self {
|
pub fn can_dma(&mut self, can_dma: bool) -> &mut Self {
|
||||||
todo!()
|
self.can_dma = can_dma;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
pub(crate) struct VmFrameFlags : usize{
|
pub(crate) struct VmFrameFlags : usize{
|
||||||
const NEED_DEALLOC = 1 << 63;
|
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
|
/// In a TEE environment, DMAable pages are untrusted pages shared with
|
||||||
/// the VMM.
|
/// the VMM.
|
||||||
pub fn can_dma(&self) -> bool {
|
pub fn can_dma(&self) -> bool {
|
||||||
todo!()
|
(*self.frame_index & VmFrameFlags::CAN_DMA.bits()) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn need_dealloc(&self) -> bool {
|
fn need_dealloc(&self) -> bool {
|
||||||
@ -371,6 +395,17 @@ impl VmIo for VmFrame {
|
|||||||
impl Drop for VmFrame {
|
impl Drop for VmFrame {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.need_dealloc() && Arc::strong_count(&self.frame_index) == 1 {
|
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 {
|
unsafe {
|
||||||
frame_allocator::dealloc(self.frame_index());
|
frame_allocator::dealloc(self.frame_index());
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,16 @@ use super::{frame::VmFrameFlags, MemoryRegions, MemoryRegionsType, VmFrame};
|
|||||||
|
|
||||||
static FRAME_ALLOCATOR: Once<Mutex<FrameAllocator>> = Once::new();
|
static FRAME_ALLOCATOR: Once<Mutex<FrameAllocator>> = Once::new();
|
||||||
|
|
||||||
pub fn alloc() -> Option<VmFrame> {
|
pub(crate) fn alloc(flags: VmFrameFlags) -> Option<VmFrame> {
|
||||||
FRAME_ALLOCATOR
|
FRAME_ALLOCATOR
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.lock()
|
.lock()
|
||||||
.alloc(1)
|
.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<Vec<VmFrame>> {
|
pub(crate) fn alloc_continuous(frame_count: usize, flags: VmFrameFlags) -> Option<Vec<VmFrame>> {
|
||||||
FRAME_ALLOCATOR
|
FRAME_ALLOCATOR
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -28,7 +28,10 @@ pub fn alloc_continuous(frame_count: usize) -> Option<Vec<VmFrame>> {
|
|||||||
let mut vector = Vec::new();
|
let mut vector = Vec::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
for i in 0..frame_count {
|
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);
|
vector.push(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,8 +39,8 @@ pub fn alloc_continuous(frame_count: usize) -> Option<Vec<VmFrame>> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn alloc_zero() -> Option<VmFrame> {
|
pub(crate) fn alloc_zero(flags: VmFrameFlags) -> Option<VmFrame> {
|
||||||
let frame = alloc()?;
|
let frame = alloc(flags)?;
|
||||||
frame.zero();
|
frame.zero();
|
||||||
Some(frame)
|
Some(frame)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use super::page_table::{PageTable, PageTableConfig};
|
use super::{
|
||||||
|
frame::VmFrameFlags,
|
||||||
|
page_table::{PageTable, PageTableConfig},
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::mm::{PageTableEntry, PageTableFlags},
|
arch::mm::{PageTableEntry, PageTableFlags},
|
||||||
config::{PAGE_SIZE, PHYS_OFFSET},
|
config::{PAGE_SIZE, PHYS_OFFSET},
|
||||||
@ -33,7 +36,7 @@ impl MapArea {
|
|||||||
pub fn clone(&self) -> Self {
|
pub fn clone(&self) -> Self {
|
||||||
let mut mapper = BTreeMap::new();
|
let mut mapper = BTreeMap::new();
|
||||||
for (&va, old) in &self.mapper {
|
for (&va, old) in &self.mapper {
|
||||||
let new = frame_allocator::alloc().unwrap();
|
let new = frame_allocator::alloc(VmFrameFlags::empty()).unwrap();
|
||||||
unsafe {
|
unsafe {
|
||||||
new.as_slice().copy_from_slice(old.as_slice());
|
new.as_slice().copy_from_slice(old.as_slice());
|
||||||
}
|
}
|
||||||
@ -94,7 +97,7 @@ impl MapArea {
|
|||||||
match self.mapper.entry(va) {
|
match self.mapper.entry(va) {
|
||||||
Entry::Occupied(e) => e.get().start_paddr(),
|
Entry::Occupied(e) => e.get().start_paddr(),
|
||||||
Entry::Vacant(e) => e
|
Entry::Vacant(e) => e
|
||||||
.insert(frame_allocator::alloc_zero().unwrap())
|
.insert(frame_allocator::alloc_zero(VmFrameFlags::empty()).unwrap())
|
||||||
.start_paddr(),
|
.start_paddr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::{
|
use super::{
|
||||||
|
frame::VmFrameFlags,
|
||||||
frame_allocator, paddr_to_vaddr, VmAllocOptions, VmFrameVec, {Paddr, Vaddr},
|
frame_allocator, paddr_to_vaddr, VmAllocOptions, VmFrameVec, {Paddr, Vaddr},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -109,7 +110,7 @@ pub struct PageTable<T: PageTableEntryTrait> {
|
|||||||
|
|
||||||
impl<T: PageTableEntryTrait> PageTable<T> {
|
impl<T: PageTableEntryTrait> PageTable<T> {
|
||||||
pub fn new(config: PageTableConfig) -> Self {
|
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 {
|
Self {
|
||||||
root_pa: root_frame.start_paddr(),
|
root_pa: root_frame.start_paddr(),
|
||||||
tables: vec![root_frame],
|
tables: vec![root_frame],
|
||||||
|
Reference in New Issue
Block a user