Implement `IoPortAllocator`
This commit is contained in:
parent
f89b248f3b
commit
d359cc44d6
|
|
@ -4,7 +4,10 @@ use alloc::vec::Vec;
|
|||
|
||||
use align_ext::AlignExt;
|
||||
|
||||
use crate::{boot::memory_region::MemoryRegionType, io::IoMemAllocatorBuilder};
|
||||
use crate::{
|
||||
boot::memory_region::MemoryRegionType,
|
||||
io::{IoMemAllocatorBuilder, IoPortAllocatorBuilder},
|
||||
};
|
||||
|
||||
/// Initializes the allocatable MMIO area based on the x86-64 memory distribution map.
|
||||
///
|
||||
|
|
@ -54,3 +57,12 @@ pub(super) fn construct_io_mem_allocator_builder() -> IoMemAllocatorBuilder {
|
|||
// SAFETY: The range is guaranteed not to access physical memory.
|
||||
unsafe { IoMemAllocatorBuilder::new(ranges) }
|
||||
}
|
||||
|
||||
/// Initializes the allocatable PIO area 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.
|
||||
unsafe { IoPortAllocatorBuilder::new(MAX_IO_PORT) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub mod task;
|
|||
pub mod timer;
|
||||
pub mod trap;
|
||||
|
||||
use allocator::construct_io_mem_allocator_builder;
|
||||
use allocator::{construct_io_mem_allocator_builder, construct_io_port_allocator_builder};
|
||||
use cfg_if::cfg_if;
|
||||
use spin::Once;
|
||||
use x86::cpuid::{CpuId, FeatureInfo};
|
||||
|
|
@ -81,6 +81,7 @@ 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(_) => {
|
||||
|
|
@ -109,9 +110,9 @@ pub(crate) unsafe fn late_init_on_bsp() {
|
|||
// Some driver like serial may use PIC
|
||||
kernel::pic::init();
|
||||
|
||||
// SAFETY: All the system device memory I/Os have been removed from the builder.
|
||||
// SAFETY: All the system device memory and port I/Os have been removed from the builder.
|
||||
unsafe {
|
||||
crate::io::init(io_mem_builder);
|
||||
crate::io::init(io_mem_builder, io_port_builder);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! I/O port allocator.
|
||||
use core::ops::Range;
|
||||
|
||||
use id_alloc::IdAlloc;
|
||||
use log::{debug, info};
|
||||
use spin::Once;
|
||||
|
||||
use super::IoPort;
|
||||
use crate::sync::{LocalIrqDisabled, SpinLock};
|
||||
|
||||
/// I/O port allocator that allocates port I/O access to device drivers.
|
||||
pub struct IoPortAllocator {
|
||||
/// Each ID indicates whether a Port I/O (1B) is allocated.
|
||||
///
|
||||
/// Instead of using `RangeAllocator` like `IoMemAllocator` does, it is more reasonable to use `IdAlloc`,
|
||||
/// as PIO space includes only a small region; for example, x86 module in OSTD allows just 65536 I/O ports.
|
||||
allocator: SpinLock<IdAlloc, LocalIrqDisabled>,
|
||||
}
|
||||
|
||||
impl IoPortAllocator {
|
||||
/// Acquires the `IoPort`. Return None if any region in `port` cannot be allocated.
|
||||
pub fn acquire<T, A>(&self, port: u16) -> Option<IoPort<T, A>> {
|
||||
let mut allocator = self.allocator.lock();
|
||||
let mut range = port..(port + size_of::<T>() as u16);
|
||||
if range.any(|i| allocator.is_allocated(i as usize)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
for i in range.clone() {
|
||||
allocator.alloc_specific(i as usize);
|
||||
}
|
||||
|
||||
// SAFETY: The created IoPort is guaranteed not to access system device I/O
|
||||
unsafe { Some(IoPort::new(port)) }
|
||||
}
|
||||
|
||||
/// Recycles an PIO range.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must have ownership of the PIO region through the `IoPortAllocator::acquire` interface.
|
||||
pub(in crate::io) unsafe fn recycle(&self, range: Range<u16>) {
|
||||
debug!("Recycling MMIO range: {:#x?}", range);
|
||||
|
||||
self.allocator
|
||||
.lock()
|
||||
.free_consecutive(range.start as usize..range.end as usize);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// # 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) {
|
||||
IO_PORT_ALLOCATOR.call_once(|| IoPortAllocator {
|
||||
allocator: SpinLock::new(io_port_builder.allocator),
|
||||
});
|
||||
}
|
||||
|
|
@ -2,9 +2,14 @@
|
|||
|
||||
//! I/O port and its allocator that allocates port I/O (PIO) to device drivers.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::arch::device::io_port::{IoPortReadAccess, IoPortWriteAccess, PortRead, PortWrite};
|
||||
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.
|
||||
///
|
||||
|
|
@ -25,6 +30,15 @@ pub struct IoPort<T, A> {
|
|||
}
|
||||
|
||||
impl<T, A> IoPort<T, A> {
|
||||
/// Acquires an `IoPort` instance for the given range.
|
||||
pub fn acquire(port: u16) -> Result<IoPort<T, A>> {
|
||||
allocator::IO_PORT_ALLOCATOR
|
||||
.get()
|
||||
.unwrap()
|
||||
.acquire(port)
|
||||
.ok_or(Error::AccessDenied)
|
||||
}
|
||||
|
||||
/// Create an I/O port.
|
||||
///
|
||||
/// # Safety
|
||||
|
|
@ -55,3 +69,15 @@ impl<T: PortWrite, A: IoPortWriteAccess> IoPort<T, A> {
|
|||
unsafe { PortWrite::write_to_port(self.port, value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A> Drop for IoPort<T, A> {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: The caller have ownership of the PIO region.
|
||||
unsafe {
|
||||
allocator::IO_PORT_ALLOCATOR
|
||||
.get()
|
||||
.unwrap()
|
||||
.recycle(self.port..(self.port + size_of::<T>() as u16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,16 +10,19 @@
|
|||
mod io_mem;
|
||||
mod io_port;
|
||||
|
||||
pub(crate) use self::io_mem::IoMemAllocatorBuilder;
|
||||
pub use self::{io_mem::IoMem, io_port::IoPort};
|
||||
pub(crate) use self::{io_mem::IoMemAllocatorBuilder, io_port::IoPortAllocatorBuilder};
|
||||
|
||||
/// Initializes the static allocator based on builder.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// User must ensure all the memory I/O regions that belong to the system device have been removed by calling the
|
||||
/// `remove` function.
|
||||
pub(crate) unsafe fn init(builder: IoMemAllocatorBuilder) {
|
||||
self::io_mem::init(builder);
|
||||
// TODO: IoPort initialization
|
||||
/// 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,
|
||||
) {
|
||||
self::io_mem::init(io_mem_builder);
|
||||
self::io_port::init(io_port_builder);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue