Refactor the initialization of IoPortAllocator

This commit is contained in:
Yuke Peng 2025-04-15 11:13:09 +08:00 committed by Tate, Hongliang Tian
parent a2caedafda
commit 74ffe72cad
5 changed files with 64 additions and 93 deletions

View File

@ -4,10 +4,7 @@ use alloc::vec::Vec;
use align_ext::AlignExt;
use crate::{
boot::memory_region::MemoryRegionType,
io::{IoMemAllocatorBuilder, IoPortAllocatorBuilder},
};
use crate::{boot::memory_region::MemoryRegionType, io::IoMemAllocatorBuilder};
/// Initializes the allocatable MMIO area based on the x86-64 memory distribution map.
///
@ -58,34 +55,5 @@ pub(super) fn construct_io_mem_allocator_builder() -> IoMemAllocatorBuilder {
unsafe { IoMemAllocatorBuilder::new(ranges) }
}
/// Initializes the allocatable PIO area outside OSTD based on the x86-64 port distribution map.
pub(super) fn construct_io_port_allocator_builder() -> IoPortAllocatorBuilder {
/// Port I/O definition reference: https://bochs.sourceforge.io/techspec/PORTS.LST
const MAX_IO_PORT: u16 = u16::MAX;
// SAFETY: `MAX_IO_PORT` is guaranteed not to exceed the maximum value specified by x86-64.
let mut builder = unsafe { IoPortAllocatorBuilder::new(MAX_IO_PORT) };
extern "C" {
fn __sensitive_io_ports_start();
fn __sensitive_io_ports_end();
}
let start = __sensitive_io_ports_start as usize;
let end = __sensitive_io_ports_end as usize;
// Iterate through the sensitive I/O port ranges and remove them from the allocator.
assert!((end - start) % (size_of::<u16>() * 2) == 0);
let io_port_ranges = (end - start) / (size_of::<u16>() * 2);
for i in 0..io_port_ranges {
let range_base_addr = __sensitive_io_ports_start as usize + i * 2 * size_of::<u16>();
let (range_start, range_end) = unsafe {
(
*(range_base_addr as *const u16),
*((range_base_addr + size_of::<u16>()) as *const u16),
)
};
builder.remove(range_start..range_end);
}
builder
}
/// Port I/O definition reference: https://bochs.sourceforge.io/techspec/PORTS.LST
pub const MAX_IO_PORT: u16 = u16::MAX;

View File

@ -6,7 +6,7 @@ pub mod boot;
pub(crate) mod cpu;
pub mod device;
pub(crate) mod ex_table;
mod io;
pub(crate) mod io;
pub mod iommu;
pub(crate) mod irq;
pub(crate) mod kernel;
@ -19,7 +19,7 @@ pub mod timer;
pub mod trap;
use cfg_if::cfg_if;
use io::{construct_io_mem_allocator_builder, construct_io_port_allocator_builder};
use io::construct_io_mem_allocator_builder;
use spin::Once;
use x86::cpuid::{CpuId, FeatureInfo};
@ -81,7 +81,6 @@ pub(crate) unsafe fn late_init_on_bsp() {
kernel::acpi::init();
let io_mem_builder = construct_io_mem_allocator_builder();
let io_port_builder = construct_io_port_allocator_builder();
match kernel::apic::init(&io_mem_builder) {
Ok(_) => {
@ -110,9 +109,12 @@ pub(crate) unsafe fn late_init_on_bsp() {
// Some driver like serial may use PIC
kernel::pic::init();
// SAFETY: All the system device memory and port I/Os have been removed from the builder.
// SAFETY:
// 1. All the system device memory have been removed from the builder.
// 2. All the port I/O regions belonging to the system device are defined using the macros.
// 3. `MAX_IO_PORT` defined in `crate::arch::io` is the maximum value specified by x86-64.
unsafe {
crate::io::init(io_mem_builder, io_port_builder);
crate::io::init(io_mem_builder);
}
}

View File

@ -4,11 +4,14 @@
use core::ops::Range;
use id_alloc::IdAlloc;
use log::{debug, info};
use log::debug;
use spin::Once;
use super::IoPort;
use crate::sync::{LocalIrqDisabled, SpinLock};
use crate::{
io::RawIoPortRange,
sync::{LocalIrqDisabled, SpinLock},
};
/// I/O port allocator that allocates port I/O access to device drivers.
pub struct IoPortAllocator {
@ -50,53 +53,47 @@ impl IoPortAllocator {
}
}
/// Builder for `IoPortAllocator`.
///
/// The builder must contains the port I/O regions according to architecture specification. Also, OSTD
/// must exclude the port I/O regions of the system device before building the `IoPortAllocator`.
pub(crate) struct IoPortAllocatorBuilder {
allocator: IdAlloc,
}
impl IoPortAllocatorBuilder {
/// Initializes port I/O region for devices.
///
/// # Safety
///
/// User must ensure `max_port` doesn't exceed the maximum value specified by architecture.
pub(crate) unsafe fn new(max_port: u16) -> Self {
info!(
"Creating new I/O port allocator builder, max_port: {:#x?}",
max_port
);
Self {
allocator: IdAlloc::with_capacity(max_port as usize),
}
}
/// Removes access to a specific port I/O range.
///
/// All drivers in OSTD must use this method to prevent peripheral drivers from accessing illegal port IO range.
pub(crate) fn remove(&mut self, range: Range<u16>) {
info!("Removing PIO range: {:#x?}", range);
for i in range {
self.allocator.alloc_specific(i as usize);
}
}
}
pub(super) static IO_PORT_ALLOCATOR: Once<IoPortAllocator> = Once::new();
/// Initializes the static `IO_PORT_ALLOCATOR` based on builder.
/// Initializes the static `IO_PORT_ALLOCATOR` and removes the system device I/O port regions.
///
/// # Safety
///
/// User must ensure all the port I/O regions that belong to the system device have been removed by calling the
/// `remove` function.
pub(crate) unsafe fn init(io_port_builder: IoPortAllocatorBuilder) {
/// User must ensure that:
///
/// 1. All the port I/O regions belonging to the system device are defined using the macros
/// `sensitive_io_port` and `reserve_io_port_range`.
///
/// 2. `MAX_IO_PORT` defined in `crate::arch::io` is guaranteed not to exceed the maximum
/// value specified by architecture.
pub(crate) unsafe fn init() {
// SAFETY: `MAX_IO_PORT` is guaranteed not to exceed the maximum value specified by architecture.
let mut allocator = IdAlloc::with_capacity(crate::arch::io::MAX_IO_PORT as usize);
extern "C" {
fn __sensitive_io_ports_start();
fn __sensitive_io_ports_end();
}
let start = __sensitive_io_ports_start as usize;
let end = __sensitive_io_ports_end as usize;
assert!((end - start) % size_of::<RawIoPortRange>() == 0);
// Iterate through the sensitive I/O port ranges and remove them from the allocator.
let io_port_range_count = (end - start) / size_of::<RawIoPortRange>();
for i in 0..io_port_range_count {
let range_base_addr = __sensitive_io_ports_start as usize + i * size_of::<RawIoPortRange>();
// SAFETY: The range is guaranteed to be valid as it is defined in the `.sensitive_io_ports` section.
let port_range = unsafe { *(range_base_addr as *const RawIoPortRange) };
assert!(port_range.begin < port_range.end);
debug!("Removing sensitive I/O port range: {:#x?}", port_range);
for i in port_range.begin..port_range.end {
allocator.alloc_specific(i as usize);
}
}
IO_PORT_ALLOCATOR.call_once(|| IoPortAllocator {
allocator: SpinLock::new(io_port_builder.allocator),
allocator: SpinLock::new(allocator),
});
}

View File

@ -8,7 +8,6 @@ mod allocator;
use core::{marker::PhantomData, mem::size_of};
pub(super) use self::allocator::init;
pub(crate) use self::allocator::IoPortAllocatorBuilder;
use crate::{prelude::*, Error};
/// An I/O port, representing a specific address in the I/O address of x86.

View File

@ -13,19 +13,24 @@ mod io_port;
pub use self::{io_mem::IoMem, io_port::IoPort};
pub(crate) use self::{
io_mem::IoMemAllocatorBuilder,
io_port::{reserve_io_port_range, sensitive_io_port, IoPortAllocatorBuilder, RawIoPortRange},
io_port::{reserve_io_port_range, sensitive_io_port, RawIoPortRange},
};
/// Initializes the static allocator based on builder.
///
/// # Safety
///
/// User must ensure all the memory and port I/O regions that belong to the system device
/// have been removed by calling the corresponding `remove` function.
pub(crate) unsafe fn init(
io_mem_builder: IoMemAllocatorBuilder,
io_port_builder: IoPortAllocatorBuilder,
) {
/// User must ensure that:
///
/// 1. All the memory that belong to the system device have been removed
/// by calling the `remove` function.
///
/// 2. All the port I/O regions belonging to the system device are defined
/// using the macros `sensitive_io_port` and `reserve_io_port_range`.
///
/// 3. `MAX_IO_PORT` defined in `crate::arch::io` is guaranteed not to
/// exceed the maximum value specified by architecture.
pub(crate) unsafe fn init(io_mem_builder: IoMemAllocatorBuilder) {
self::io_mem::init(io_mem_builder);
self::io_port::init(io_port_builder);
self::io_port::init();
}