mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 09:53:24 +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"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
"runner-utils",
|
||||
]
|
||||
|
||||
|
@ -7,7 +7,9 @@ edition = "2021"
|
||||
[dependencies]
|
||||
runner-utils = "0.0.2"
|
||||
anyhow = "1.0.32"
|
||||
cfg-if = "1.0"
|
||||
|
||||
[features]
|
||||
default = ["limine"]
|
||||
limine= []
|
||||
limine = []
|
||||
iommu = []
|
||||
|
@ -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<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");
|
||||
let path = Path::new(fs_img_path.as_str());
|
||||
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 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,
|
||||
|
@ -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();
|
||||
|
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(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;
|
||||
|
@ -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<Self> {
|
||||
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());
|
||||
}
|
||||
|
@ -9,16 +9,16 @@ use super::{frame::VmFrameFlags, MemoryRegions, MemoryRegionsType, VmFrame};
|
||||
|
||||
static FRAME_ALLOCATOR: Once<Mutex<FrameAllocator>> = Once::new();
|
||||
|
||||
pub fn alloc() -> Option<VmFrame> {
|
||||
pub(crate) fn alloc(flags: VmFrameFlags) -> Option<VmFrame> {
|
||||
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<Vec<VmFrame>> {
|
||||
pub(crate) fn alloc_continuous(frame_count: usize, flags: VmFrameFlags) -> Option<Vec<VmFrame>> {
|
||||
FRAME_ALLOCATOR
|
||||
.get()
|
||||
.unwrap()
|
||||
@ -28,7 +28,10 @@ pub fn alloc_continuous(frame_count: usize) -> Option<Vec<VmFrame>> {
|
||||
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<Vec<VmFrame>> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_zero() -> Option<VmFrame> {
|
||||
let frame = alloc()?;
|
||||
pub(crate) fn alloc_zero(flags: VmFrameFlags) -> Option<VmFrame> {
|
||||
let frame = alloc(flags)?;
|
||||
frame.zero();
|
||||
Some(frame)
|
||||
}
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
@ -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<T: PageTableEntryTrait> {
|
||||
|
||||
impl<T: PageTableEntryTrait> PageTable<T> {
|
||||
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],
|
||||
|
Reference in New Issue
Block a user