Rust XHCI Implementation.

This commit is contained in:
Drew 2025-12-05 22:01:13 -08:00
parent 0b95098748
commit 2c271360ce
25 changed files with 1288 additions and 202 deletions

51
' Normal file
View file

@ -0,0 +1,51 @@
#![no_std]
#![no_main]
extern crate alloc;
mod xhci;
use alloc::sync::Arc;
use mammoth::{
cap::Capability,
debug, define_entry,
sync::Mutex,
task::{Executor, Task},
zion::z_err_t,
};
use pci::PciDevice;
use xhci::driver::XHCIDriver;
define_entry!();
#[unsafe(no_mangle)]
extern "C" fn main() -> z_err_t {
#[cfg(feature = "debug")]
debug!("Voyageurs Starting.");
let yellowstone = yellowstone_yunq::from_init_endpoint();
let xhci_info = yellowstone
.get_xhci_info()
.expect("Failed to get XHCI info from yellowstone.");
let pci_device = PciDevice::from_cap(Capability::take(xhci_info.xhci_region)).unwrap();
let xhci_driver = Arc::new(XHCIDriver::from_pci_device(pci_device));
let executor = Arc::new(Mutex::new(Executor::new()));
let driver_clone = xhci_driver.clone();
let interrupt_thread = mammoth::thread::spawn(move || driver_clone.interrupt_loop());
let driver_clone = xhci_driver.clone();
executor
.clone()
.lock()
.spawn(Task::new((|| xhci_driver.clone().startup())()));
executor.clone().lock().run();
interrupt_thread.join().unwrap();
0
}

44
rust/Cargo.lock generated
View file

@ -2,12 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]] [[package]]
name = "bitfield-struct" name = "bitfield-struct"
version = "0.8.0" version = "0.8.0"
@ -80,11 +74,10 @@ dependencies = [
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.12" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [ dependencies = [
"autocfg",
"scopeguard", "scopeguard",
] ]
@ -105,9 +98,9 @@ dependencies = [
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.20" version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn", "syn",
@ -115,18 +108,18 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.86" version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.36" version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -148,9 +141,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.72" version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -178,15 +171,15 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.11.0" version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]] [[package]]
name = "victoriafalls" name = "victoriafalls"
@ -200,12 +193,21 @@ dependencies = [
"yunqc", "yunqc",
] ]
[[package]]
name = "volatile"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8ca9a5d4debca0633e697c88269395493cebf2e10db21ca2dbde37c1356452"
[[package]] [[package]]
name = "voyageurs" name = "voyageurs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitfield-struct 0.12.1", "bitfield-struct 0.12.1",
"mammoth", "mammoth",
"pci",
"volatile",
"yellowstone-yunq",
] ]
[[package]] [[package]]

View file

@ -78,6 +78,10 @@ impl MemoryRegion {
}) })
} }
pub fn vaddr(&self) -> usize {
self.virt_addr as usize
}
pub fn slice<T>(&self) -> &[T] { pub fn slice<T>(&self) -> &[T] {
unsafe { unsafe {
slice::from_raw_parts( slice::from_raw_parts(
@ -246,11 +250,11 @@ pub fn map_cap_and_leak(mem_cap: Capability) -> u64 {
vaddr vaddr
} }
pub fn map_direct_physical_and_leak(paddr: u64, size: u64) -> u64 { pub fn map_direct_physical_and_leak<T>(paddr: u64, size: u64) -> *mut T {
let mem_cap = syscall::memory_object_direct_physical(paddr, size).unwrap(); let mem_cap = syscall::memory_object_direct_physical(paddr, size).unwrap();
let vaddr = syscall::address_space_map(&mem_cap).unwrap(); let vaddr = syscall::address_space_map(&mem_cap).unwrap();
mem_cap.release(); mem_cap.release();
vaddr vaddr as *mut T
} }
pub fn map_physical_and_leak(size: u64) -> (u64, u64) { pub fn map_physical_and_leak(size: u64) -> (u64, u64) {

View file

@ -4,12 +4,13 @@ use core::{
ptr::NonNull, ptr::NonNull,
}; };
use alloc::{slice, vec::Vec}; use alloc::{boxed::Box, slice, vec::Vec};
use crate::mem::MemoryRegion; use crate::mem::MemoryRegion;
pub struct PhysicalBox<T: ?Sized> { pub struct PhysicalBox<T: ?Sized> {
data: NonNull<T>, data: NonNull<T>,
#[allow(dead_code)]
region: MemoryRegion, region: MemoryRegion,
physical_address: usize, physical_address: usize,
_marker: PhantomData<T>, _marker: PhantomData<T>,
@ -54,6 +55,12 @@ impl<T> PhysicalBox<[T]> {
let (memory_region, paddr) = let (memory_region, paddr) =
MemoryRegion::contiguous_physical(layout.size() as u64).expect("Failed to allocate"); MemoryRegion::contiguous_physical(layout.size() as u64).expect("Failed to allocate");
crate::debug!(
"Physical box allocated: v {:0x} p {:0x}",
memory_region.vaddr(),
paddr
);
let ptr: *mut T = memory_region.mut_ptr_at_offset(0); let ptr: *mut T = memory_region.mut_ptr_at_offset(0);
for i in 0..len { for i in 0..len {
unsafe { unsafe {
@ -122,6 +129,13 @@ where
} }
} }
/// SAFETY: We are the only owner of this pointer.
unsafe impl<T: ?Sized> Send for PhysicalBox<T> where Box<T>: Send {}
/// SAFETY: You must have a mutable reference to this
/// type to modify the data at the pointer.
unsafe impl<T: ?Sized> Sync for PhysicalBox<T> where Box<T>: Sync {}
impl<T: ?Sized> Drop for PhysicalBox<T> { impl<T: ?Sized> Drop for PhysicalBox<T> {
fn drop(&mut self) { fn drop(&mut self) {
// SAFETY: // SAFETY:

View file

@ -72,15 +72,15 @@ impl PciDevice {
control.capable_address_64(), control.capable_address_64(),
"We don't handle the non-64bit case for MSI yet." "We don't handle the non-64bit case for MSI yet."
); );
assert!(
control.multi_message_capable() == 0, if control.multi_message_capable() != 0 {
"We don't yet handle multi-message capable devices." mammoth::debug!("WARN: We don't yet handle multi-message capable devices.");
); }
// FIXME: These probably need to be volatile writes. // FIXME: These probably need to be volatile writes.
let header: &mut PciDeviceHeader = self.memory_region.as_mut(); let header: &mut PciDeviceHeader = self.memory_region.as_mut();
header.command = header.command.with_interrupt_disable(true); header.command = header.command.with_interrupt_disable(true);
msi_cap.msi_control = control.with_msi_enable(true); msi_cap.msi_control = control.with_msi_enable(true).with_multi_message_enable(0);
// For setting addr and data field, see intel ref // For setting addr and data field, see intel ref
// Vol 3. Section 11.11 // Vol 3. Section 11.11

View file

@ -1,3 +1,5 @@
use core::ffi::c_void;
use alloc::sync::Arc; use alloc::sync::Arc;
use mammoth::{ use mammoth::{
cap::Capability, cap::Capability,
@ -27,7 +29,8 @@ impl AhciController {
let pci_device = PciDevice::from_cap(pci_memory).unwrap(); let pci_device = PciDevice::from_cap(pci_memory).unwrap();
let hba_vaddr = let hba_vaddr =
mem::map_direct_physical_and_leak(pci_device.header().bars[5] as u64, 0x1100); mem::map_direct_physical_and_leak(pci_device.header().bars[5] as u64, 0x1100)
as *mut c_void as u64;
let hba = unsafe { (hba_vaddr as *mut AhciHba).as_mut().unwrap() }; let hba = unsafe { (hba_vaddr as *mut AhciHba).as_mut().unwrap() };
let mut controller = Self { let mut controller = Self {
pci_device: Mutex::new(pci_device), pci_device: Mutex::new(pci_device),

View file

@ -6,3 +6,10 @@ edition = "2024"
[dependencies] [dependencies]
bitfield-struct = "0.12" bitfield-struct = "0.12"
mammoth = { path = "../../lib/mammoth/" } mammoth = { path = "../../lib/mammoth/" }
pci = { path = "../../lib/pci" }
volatile = "0.6.1"
yellowstone-yunq = { version = "0.1.0", path = "../../lib/yellowstone" }
[features]
default = ["debug"]
debug = []

View file

@ -5,12 +5,47 @@ extern crate alloc;
mod xhci; mod xhci;
use mammoth::{debug, define_entry, zion::z_err_t}; use alloc::sync::Arc;
use mammoth::{
cap::Capability,
debug, define_entry,
sync::Mutex,
task::{Executor, Task},
zion::z_err_t,
};
use pci::PciDevice;
use xhci::driver::XHCIDriver;
define_entry!(); define_entry!();
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn main() -> z_err_t { extern "C" fn main() -> z_err_t {
debug!("In Voyageurs"); #[cfg(feature = "debug")]
debug!("Voyageurs Starting.");
let yellowstone = yellowstone_yunq::from_init_endpoint();
let xhci_info = yellowstone
.get_xhci_info()
.expect("Failed to get XHCI info from yellowstone.");
let pci_device = PciDevice::from_cap(Capability::take(xhci_info.xhci_region)).unwrap();
let xhci_driver = Arc::new(XHCIDriver::from_pci_device(pci_device));
let executor = Arc::new(Mutex::new(Executor::new()));
let driver_clone = xhci_driver.clone();
let spawner = executor.clone().lock().new_spawner();
let interrupt_thread = mammoth::thread::spawn(move || driver_clone.interrupt_loop(spawner));
executor
.clone()
.lock()
.spawn(Task::new(async move { xhci_driver.startup().await }));
executor.clone().lock().run();
interrupt_thread.join().unwrap();
0 0
} }

View file

@ -1,9 +1,8 @@
use core::ops::{Index, IndexMut}; use core::ops::{Index, IndexMut};
use alloc::{boxed::Box, vec};
use mammoth::physical_box::PhysicalBox; use mammoth::physical_box::PhysicalBox;
use crate::xhci::data_structures::TrbRing; use crate::xhci::data_structures::TrbRingSegment;
#[repr(align(64))] #[repr(align(64))]
#[derive(Default, Clone)] #[derive(Default, Clone)]
@ -19,10 +18,16 @@ pub struct EventRingSegmentTableEntry {
} }
impl EventRingSegmentTableEntry { impl EventRingSegmentTableEntry {
pub fn from_trb_fing(&mut self, trb_ring: &TrbRing) { pub fn from_trb_ring(&mut self, trb_ring: &TrbRingSegment) {
mammoth::debug!("RSTE: {:0x}", self as *const _ as usize);
self.ring_segment_base_address = trb_ring.physical_address() as u64; self.ring_segment_base_address = trb_ring.physical_address() as u64;
assert!(self.ring_segment_base_address % 64 == 0); assert!(self.ring_segment_base_address % 64 == 0);
self.ring_segment_size = trb_ring.len() as u64; unsafe {
core::ptr::write_volatile(
&mut self.ring_segment_size as *mut u64,
trb_ring.len() as u64,
)
};
assert!(self.ring_segment_size >= 16); assert!(self.ring_segment_size >= 16);
assert!(self.ring_segment_size <= 4096); assert!(self.ring_segment_size <= 4096);
} }

View file

@ -1,8 +1,10 @@
mod endpoint_context; mod endpoint_context;
mod event_ring_segment_table; mod event_ring_segment_table;
mod slot_context; mod slot_context;
mod trb_ring; mod trb;
mod trb_ring_segment;
pub use event_ring_segment_table::*; pub use event_ring_segment_table::*;
pub use slot_context::*; pub use slot_context::*;
pub use trb_ring::*; pub use trb::*;
pub use trb_ring_segment::*;

View file

@ -0,0 +1,234 @@
use bitfield_struct::{bitenum, bitfield};
#[bitenum]
#[repr(u8)]
#[derive(Debug, Eq, PartialEq)]
pub enum TrbType {
#[fallback]
Reserved = 0,
Normal = 1,
SetupStage = 2,
DataStage = 3,
StatusStage = 4,
Isoch = 5,
Link = 6,
EventData = 7,
NoOp = 8,
EnableSlotCommand = 9,
DisableSlotCommand = 10,
AddressDeviceCommand = 11,
ConfigureEndpointCommand = 12,
EvaluateContextCommand = 13,
ResetEndpointCommand = 14,
StopEndpointCommand = 15,
SetTRDequeuePointerCommand = 16,
ResetDeviceCommand = 17,
ForceEventCommand = 18,
NegotiateBandwidthCommand = 19,
SetLatencyToleranceValueCommand = 20,
GetPortBandwidthCommand = 21,
ForceHeaderCommand = 22,
NoOpCommand = 23,
GetExtendedPropertyCommand = 24,
SetExtendedPropertyCommand = 25,
TransferEvent = 32,
CommandCompletionEvent = 33,
PortStatusChangeEvent = 34,
BandwidthRequestEvent = 35,
DoorbellEvent = 36,
HostControllerEvent = 37,
DeviceNotificationEvent = 38,
MFINDEXWrapEvent = 39,
}
#[bitfield(u128)]
pub struct TransferRequestBlock {
pub parameter: u64,
pub status: u32,
pub cycle: bool,
evaluate_next: bool,
flag_2: bool,
flag_3: bool,
flag_4: bool,
flag_5: bool,
flag_6: bool,
flag_7: bool,
flag_8: bool,
flag_9: bool,
#[bits(6)]
pub trb_type: TrbType,
control: u16,
}
impl TransferRequestBlock {}
pub trait TypedTrb
where
Self: Into<u128> + From<u128> + Copy,
{
fn from_trb(trb: TransferRequestBlock) -> Self {
trb.into_bits().into()
}
fn to_trb(self) -> TransferRequestBlock {
Into::<u128>::into(self).into()
}
}
#[bitfield(u128)]
pub struct TrbNoOp {
__: u64,
#[bits(22)]
__: u32,
#[bits(10, default = 0)]
interrupter_target: u16,
cycle: bool,
evaluate_next: bool,
__: bool,
__: bool,
chain: bool,
#[bits(default = true)]
interrupt_on_completion: bool,
#[bits(4)]
__: u8,
#[bits(6, default = TrbType::NoOpCommand)]
trb_type: TrbType,
__: u16,
}
impl TypedTrb for TrbNoOp {}
#[bitfield(u128)]
pub struct TrbLink {
/// Ring Segment Pointer Hi and Lo. These fields represent the high order bits of the 64-bit base
/// address of the next Ring Segment.
/// The memory structure referenced by this physical memory pointer shall begin on a 16-byte
/// address boundary.
pub ring_segment_pointer: u64,
#[bits(22)]
__: u32,
/// Interrupter Target. This field defines the index of the Interrupter that will receive Transfer
/// Events generated by this TRB. Valid values are between 0 and MaxIntrs-1.
/// This field is ignored by the xHC on Command Rings.
#[bits(10)]
pub interrupter_target: u16,
/// Cycle bit (C). This bit is used to mark the Enqueue Pointer location of a Transfer or Command
/// Ring.
pub cycle: bool,
/// Toggle Cycle (TC). When set to 1, the xHC shall toggle its interpretation of the Cycle bit. When
/// cleared to 0, the xHC shall continue to the next segment using its current interpretation of the
/// Cycle bit.
pub toggle_cycle: bool,
__: bool,
__: bool,
/// Chain bit (CH). Set to 1 by software to associate this TRB with the next TRB on the Ring. A
/// Transfer Descriptor (TD) is defined as one or more TRBs. The Chain bit is used to identify the
/// TRBs that comprise a TD. Refer to section 4.11.7 for more information on Link TRB placement
/// within a TD. On a Command Ring this bit is ignored by the xHC.
#[bits(default = true)]
chain: bool,
/// Interrupt On Completion (IOC). If this bit is set to 1, it specifies that when this TRB completes,
/// the Host Controller shall notify the system of the completion by placing an Event TRB on the
/// Event ring and sending an interrupt at the next interrupt threshold.
pub interrupt_on_completion: bool,
#[bits(4)]
__: u8,
/// TRB Type. This field is set to Link TRB type. Refer to Table 6-91 for the definition of the Type
/// TRB IDs.
#[bits(6, default = TrbType::Link)]
trb_type: TrbType,
__: u16,
}
impl TypedTrb for TrbLink {}
#[bitenum]
#[repr(u8)]
pub enum CommandCompletionCode {
#[fallback]
Invalid = 0,
Success = 1,
}
#[bitfield(u128)]
pub struct TrbCommandCompletion {
/// Command TRB Pointer Hi and Lo. This field represents the high order bits of the 64-bit address
/// of the Command TRB that generated this event. Note that this field is not valid for some
/// Completion Code values. Refer to Table 6-90 for specific cases.
///
/// The memory structure referenced by this physical memory pointer shall be aligned on a 16-byte
/// address boundary.
pub command_trb_pointer: u64,
/// Command Completion Parameter. This field may optionally be set by a command. Refer to
/// section 4.6.6.1 for specific usage. If a command does not utilize this field it shall be treated as
/// RsvdZ.
#[bits(24)]
pub command_completion_parameter: u64,
/// Completion Code. This field encodes the completion status of the command that generated the
/// event. Refer to the respective command definition for a list of the possible Completion Codes
/// associated with the command. Refer to section 6.4.5 for an enumerated list of possible error
/// conditions.
pub completion_code: u8,
/// Cycle bit (C). This bit is used to mark the Dequeue Pointer of an Event Ring
pub cycle_bit: bool,
#[bits(9)]
__: u16,
/// TRB Type. This field identifies the type of the TRB. Refer to Table 6-91 for the definition of the
/// Command Completion Event TRB type ID
#[bits(6, default=TrbType::CommandCompletionEvent)]
pub trb_type: TrbType,
/// VF ID. The ID of the Virtual Function that generated the event. Note that this field is valid only if
/// Virtual Functions are enabled. If they are not enabled this field shall be cleared to 0.
pub vf_id: u8,
/// Slot ID. The Slot ID field shall be updated by the xHC to reflect the slot associated with the
/// command that generated the event, with the following exceptions:
///
/// - The Slot ID shall be cleared to 0 for No Op, Set Latency Tolerance Value, Get Port Bandwidth,
/// and Force Event Commands.
///
/// - The Slot ID shall be set to the ID of the newly allocated Device Slot for the Enable Slot
/// Command.
///
/// - The value of Slot ID shall be vendor defined when generated by a vendor defined command.
///
/// This value is used as an index in the Device Context Base Address Array to select the Device
/// Context of the source device. If this Event is due to a Host Controller Command, then this field
/// shall be cleared to 0.
pub slot_id: u8,
}
impl TypedTrb for TrbCommandCompletion {}
#[bitfield(u128)]
pub struct TrbPortStatusChangeEvent {
#[bits(24)]
__: u32,
pub port_id: u8,
__: u32,
#[bits(24)]
__: u32,
pub completion_code: u8,
#[bits(10)]
__: u16,
#[bits(6, default=TrbType::PortStatusChangeEvent)]
trb_type: TrbType,
__: u16,
}
impl TypedTrb for TrbPortStatusChangeEvent {}
#[bitfield(u128)]
pub struct TrbEnableSlotCommand {
__: u64,
__: u32,
#[bits(10)]
__: u16,
#[bits(6, default=TrbType::EnableSlotCommand)]
trb_type: TrbType,
#[bits(5)]
slot_type: u8,
#[bits(11)]
__: u16,
}
impl TypedTrb for TrbEnableSlotCommand {}

View file

@ -1,113 +0,0 @@
use core::{
ops::{Index, IndexMut},
slice::SliceIndex,
};
use bitfield_struct::{bitenum, bitfield};
use mammoth::physical_box::PhysicalBox;
#[bitenum]
#[repr(u8)]
#[derive(Debug)]
pub enum TrbType {
#[fallback]
Reserved = 0,
NoOp = 8,
}
#[bitfield(u128)]
pub struct TransferRequestBlock {
parameter: u64,
status: u32,
cycle: bool,
evaluate_next: bool,
flag_2: bool,
flag_3: bool,
flag_4: bool,
flag_5: bool,
flag_6: bool,
flag_7: bool,
flag_8: bool,
flag_9: bool,
#[bits(6)]
trb_type: u8,
control: u16,
}
impl TransferRequestBlock {}
trait TypedTrb {
fn from_trb(trb: TransferRequestBlock) -> Self
where
Self: From<u128>,
{
trb.into_bits().into()
}
fn to_trb(self) -> TransferRequestBlock
where
Self: Into<u128>,
{
Into::<u128>::into(self).into()
}
}
#[bitfield(u128)]
pub struct TrbNoOp {
__: u64,
__: u32,
cycle: bool,
evaluate_next: bool,
__: bool,
__: bool,
chain: bool,
#[bits(default = true)]
interrupt_on_completion: bool,
#[bits(4)]
__: u8,
#[bits(6, default = TrbType::NoOp)]
trb_type: TrbType,
__: u16,
}
impl TypedTrb for TrbNoOp {}
#[repr(transparent)]
pub struct TrbRing(PhysicalBox<[TransferRequestBlock]>);
impl TrbRing {
pub fn new(size: usize) -> Self {
Self(PhysicalBox::default_with_count(
TransferRequestBlock::default(),
size,
))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn physical_address(&self) -> usize {
self.0.physical_address()
}
}
impl<I> Index<I> for TrbRing
where
I: SliceIndex<[TransferRequestBlock]>,
{
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&self.0[index]
}
}
impl<I> IndexMut<I> for TrbRing
where
I: SliceIndex<[TransferRequestBlock]>,
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
&mut self.0[index]
}
}

View file

@ -0,0 +1,48 @@
use core::{
ops::{Index, IndexMut},
slice::SliceIndex,
};
use mammoth::physical_box::PhysicalBox;
use crate::xhci::data_structures::TransferRequestBlock;
#[repr(transparent)]
pub struct TrbRingSegment(PhysicalBox<[TransferRequestBlock]>);
impl TrbRingSegment {
pub fn new(size: usize) -> Self {
Self(PhysicalBox::default_with_count(
TransferRequestBlock::default(),
size,
))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn physical_address(&self) -> usize {
self.0.physical_address()
}
}
impl<I> Index<I> for TrbRingSegment
where
I: SliceIndex<[TransferRequestBlock]>,
{
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&self.0[index]
}
}
impl<I> IndexMut<I> for TrbRingSegment
where
I: SliceIndex<[TransferRequestBlock]>,
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
&mut self.0[index]
}
}

View file

@ -0,0 +1,22 @@
use mammoth::mem::MemoryRegion;
pub struct DeviceContextBaseArray {
#[allow(dead_code)]
region: MemoryRegion,
physical_addr: usize,
}
impl DeviceContextBaseArray {
pub fn new() -> Self {
let (region, physical_addr) = MemoryRegion::contiguous_physical(0x1000).unwrap();
region.zero_region();
Self {
region,
physical_addr: physical_addr as usize,
}
}
pub fn physical_addr(&self) -> usize {
self.physical_addr
}
}

View file

@ -0,0 +1,331 @@
use core::slice;
use alloc::sync::Arc;
use mammoth::cap::Capability;
use mammoth::mem::MemoryRegion;
use mammoth::sync::Mutex;
use mammoth::task::Spawner;
use mammoth::task::Task;
use mammoth::write_unaligned_volatile;
use super::registers::{self};
use crate::xhci::data_structures::CommandCompletionCode;
use crate::xhci::data_structures::TrbCommandCompletion;
use crate::xhci::data_structures::TrbEnableSlotCommand;
use crate::xhci::data_structures::TrbNoOp;
use crate::xhci::data_structures::TrbPortStatusChangeEvent;
use crate::xhci::data_structures::TrbType;
use crate::xhci::data_structures::TypedTrb;
use crate::xhci::device_context_base_array::DeviceContextBaseArray;
use crate::xhci::event_ring::EventRing;
use crate::xhci::registers::HostControllerOperationalWrapper;
use crate::xhci::registers::PortStatusAndControl;
use crate::xhci::trb_ring::TrbRing;
pub struct XHCIDriver {
#[allow(dead_code)]
pci_device: pci::PciDevice,
capabilities: registers::HostControllerCapabilities,
operational: HostControllerOperationalWrapper,
registers_region: MemoryRegion,
command_ring: Mutex<TrbRing<TrbCommandCompletion>>,
event_ring: Mutex<EventRing>,
device_context_base_array: DeviceContextBaseArray,
irq_port_cap: Capability,
}
impl XHCIDriver {
pub fn from_pci_device(mut pci_device: pci::PciDevice) -> Self {
// however the RTSOFF and DBOFF Registers shall position the Runtime and
// Doorbell Registers to reside on their own respective virtual memory pages. The
// BAR0 size shall provide space that is sufficient to cover the offset between the
// respective register spaces (Capability, Operational, Runtime, etc.) and the
// register spaces themselves (e.g. a minimum of 3 virtual memory pages).
// If virtualization is not supported, all xHCI register spaces may reside on a single
// page pointed to by the BAR0.
let three_pages = 0x3000;
let address =
((pci_device.header().bars[1] as u64) << 32) | (pci_device.header().bars[0] as u64);
let registers_region = MemoryRegion::direct_physical(address, three_pages).unwrap();
let irq_port_cap = pci_device.register_msi().unwrap();
let (operational, capabilities) = HostControllerOperationalWrapper::new(address as usize);
let p1 = capabilities.params_1;
mammoth::debug!("Num Port: {:?}", p1);
let mut driver = Self {
pci_device,
capabilities,
operational,
registers_region,
command_ring: Mutex::new(TrbRing::new()),
event_ring: Mutex::new(EventRing::new()),
device_context_base_array: DeviceContextBaseArray::new(),
irq_port_cap,
};
driver.initialize();
driver
}
fn interrupters(&self) -> &mut [registers::InterrupterRegisterSet] {
// See Table 5-35: Host Controller Runtime Registers
const INTERRUPTER_OFFSET_FROM_RUNTIME: u32 = 0x20;
let runtime = self.capabilities.runtime_register_space_offset;
let interrupter_offset = (runtime + INTERRUPTER_OFFSET_FROM_RUNTIME) as usize;
let params1 = self.capabilities.params_1;
// SAFETY: The XHCI spec says so?
unsafe {
slice::from_raw_parts_mut(
self.registers_region.mut_ptr_at_offset(interrupter_offset),
params1.max_interrupters() as usize,
)
}
}
fn doorbells(&self) -> &mut [registers::Doorbell] {
let doorbell_offset = self.capabilities.doorbell_offset;
let params1 = self.capabilities.params_1;
// SAFETY: The XHCI spec says so?
unsafe {
slice::from_raw_parts_mut(
self.registers_region
.mut_ptr_at_offset(doorbell_offset as usize),
params1.max_device_slots() as usize,
)
}
}
fn initialize(&mut self) {
#[cfg(feature = "debug")]
mammoth::debug!("Stopping XHCI Controller.");
// Stop the host controller.
self.operational.update(|mut o| {
o.usb_command = o.usb_command.with_run_stop(false);
o
});
#[cfg(feature = "debug")]
mammoth::debug!("Waiting for controller to halt.");
// Sleep until the controller is halted.
let mut status = self.operational.status();
while !status.host_controller_halted() {
// TODO: Sleep for how long?
mammoth::syscall::thread_sleep(50).unwrap();
status = self.operational.status();
}
#[cfg(feature = "debug")]
mammoth::debug!("Resetting Controller.");
self.operational.update(|mut o| {
o.usb_command = o.usb_command.with_host_controller_reset(false);
o
});
let mut command: registers::UsbCommand = self.operational.command();
while command.host_controller_reset() {
// TODO: Sleep for how long?
mammoth::syscall::thread_sleep(50).unwrap();
command = self.operational.command();
}
#[cfg(feature = "debug")]
mammoth::debug!("XHCI Controller Reset, waiting ready.");
let mut status = self.operational.status();
while status.controller_not_ready() {
// TODO: Sleep for how long?
mammoth::syscall::thread_sleep(50).unwrap();
status = self.operational.status();
}
#[cfg(feature = "debug")]
mammoth::debug!("XHCI Controller Ready.");
#[cfg(feature = "debug")]
mammoth::debug!("Setting Command Ring");
self.operational.update(|mut o| {
// TODO: Split this struct to make it clearer that we are setting the cycle bit here.
o.command_ring_control = (self.command_ring.lock().physical_base_address() | 1) as u64;
o
});
#[cfg(feature = "debug")]
mammoth::debug!("Setting DCBA.");
let params1 = self.capabilities.params_1;
self.operational.update(|mut o| {
o.device_context_base_address_array_pointer =
self.device_context_base_array.physical_addr() as u64;
// We tell the controller that we can support as many slots as it does because
// we allocate a full 4K page to the DCBA, which is 256 entries and the max
// slots are 255.
o.configure = o
.configure
.with_max_device_slots_enabled(params1.max_device_slots());
o
});
let params2 = self.capabilities.params_2;
let max_scratchpad_buffers =
(params2.max_scratchpad_buffers_hi() << 5) | params2.max_scratchpad_buffers_lo();
assert!(
max_scratchpad_buffers == 0,
"Unsupported scratchpad buffers."
);
#[cfg(feature = "debug")]
mammoth::debug!("Setting up initial event ring.");
let interrupter0 = &mut self.interrupters()[0];
// SAFETY:
// - The HC was halted above.
// - THe segment table is size 1.
unsafe {
let event_ring = self.event_ring.lock();
interrupter0.set_event_ring(
event_ring.segment_table(),
event_ring.erdp_physical_address(),
);
}
write_unaligned_volatile!(
interrupter0,
interrupter_moderation,
registers::InterrupterModeration::new()
.with_interrupt_moderation_interval(4000)
.with_interrupt_moderation_counter(0)
);
write_unaligned_volatile!(
interrupter0,
interrupter_management,
registers::InterrupterManagement::new().with_interrupt_enabled(true)
);
self.operational.update(|mut o| {
o.usb_command = o
.usb_command
.with_run_stop(true)
.with_interrupter_enable(true);
o
});
#[cfg(feature = "debug")]
mammoth::debug!("Enabled interrupts and controller.");
}
pub fn interrupt_loop(self: Arc<Self>, spawner: Spawner) {
loop {
let _ = mammoth::syscall::port_recv(&self.irq_port_cap, &mut [], &mut []).unwrap();
#[cfg(feature = "debug")]
mammoth::debug!("Received Interrupt.");
self.interrupters()[0].interrupter_management = self.interrupters()[0]
.interrupter_management
.with_interrupt_pending(true);
// TODO: Make event ring own its interrupter.
let mut event_ring = self.event_ring.lock();
while let Some(trb) = event_ring.get_next() {
event_ring.update_dequeue_pointer(&mut self.interrupters()[0]);
match trb.trb_type() {
TrbType::TransferEvent => {
todo!("Handle Transfer")
}
TrbType::CommandCompletionEvent => {
self.command_ring
.lock()
.handle_commpletion(TrbCommandCompletion::from_trb(trb));
}
TrbType::PortStatusChangeEvent => {
let trb = TrbPortStatusChangeEvent::from_trb(trb);
let self_clone = self.clone();
spawner.spawn(Task::new(async move {
self_clone.port_status_change(trb).await
}));
}
_ => {
panic!("Unhandled event type: {:?}", trb.trb_type());
}
}
}
}
}
async fn send_command(&self, trb: impl TypedTrb) -> TrbCommandCompletion {
let fut = self.command_ring.lock().enqueue_trb(trb.to_trb());
self.doorbells()[0].ring_command();
fut.await
}
pub async fn startup(&self) {
#[cfg(feature = "debug")]
mammoth::debug!("Sending no op command.");
let result = self.send_command(TrbNoOp::new()).await;
assert!(result.completion_code() == CommandCompletionCode::Success.into_bits());
#[cfg(feature = "debug")]
mammoth::debug!("Successfully tested no op command.");
#[cfg(feature = "debug")]
mammoth::debug!("Resetting all connected ports.");
for port_index in 0..self.operational.num_ports() {
self.operational
.update_port_status(port_index, |p| p.clear_change_bits());
}
for port_index in 0..self.operational.num_ports() {
let status = self.operational.get_port(port_index).status_and_control;
if status.port_power() && status.current_connect_status() {
mammoth::debug!("Resetting port {}", port_index);
self.operational.update_port_status(port_index, |_| {
PortStatusAndControl::new()
.with_port_reset(true)
.with_port_power(true)
});
}
}
}
async fn port_status_change(self: Arc<Self>, status_change: TrbPortStatusChangeEvent) {
// Ports are indexed from 1.
let port_id = status_change.port_id();
let port_index = (port_id - 1) as usize;
let port_status = self
.operational
.get_port(port_index as usize)
.status_and_control;
#[cfg(feature = "debug")]
mammoth::debug!(
"Port status change for port {}, status= {:?}",
port_id,
port_status
);
if !port_status.port_reset_change() {
mammoth::debug!("Unknown port status event, not handling.");
return;
}
self.operational
.update_port_status(port_index, |s| s.clear_change_bits());
#[cfg(feature = "debug")]
mammoth::debug!("Enabling slot.");
let resp = self.send_command(TrbEnableSlotCommand::new()).await;
assert!(resp.completion_code() == CommandCompletionCode::Success.into_bits());
}
}

View file

@ -0,0 +1,75 @@
use alloc::vec::Vec;
use crate::xhci::{
data_structures::{EventRingSegmentTable, TransferRequestBlock, TrbRingSegment, TrbType},
registers::InterrupterRegisterSet,
trb_ring::TrbPointer,
};
pub struct EventRing {
segment_table: EventRingSegmentTable,
segments: Vec<TrbRingSegment>,
cycle_bit: bool,
trb_pointer: TrbPointer,
}
impl EventRing {
pub fn new() -> Self {
// Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it
// to 1...
let cycle_bit = true;
let mut event_ring = Self {
segment_table: EventRingSegmentTable::new(1),
segments: [TrbRingSegment::new(100)].into(),
cycle_bit,
trb_pointer: TrbPointer::default(),
};
event_ring.segment_table[0].from_trb_ring(&event_ring.segments[0]);
event_ring
}
pub fn segment_table(&self) -> &EventRingSegmentTable {
&self.segment_table
}
pub fn erdp_physical_address(&self) -> usize {
self.segments[self.trb_pointer.segment_index].physical_address()
+ self.trb_pointer.segment_physical_offset()
}
fn current_trb(&self) -> TransferRequestBlock {
// TODO: These should be volatile reads.
self.segments[self.trb_pointer.segment_index][self.trb_pointer.segment_offset]
}
fn increment_pointer(&mut self) {
self.trb_pointer.segment_offset += 1;
if self.trb_pointer.segment_offset == self.segments[self.trb_pointer.segment_index].len() {
self.trb_pointer.segment_index += 1;
self.trb_pointer.segment_offset = 0;
if self.trb_pointer.segment_index == self.segments.len() {
// Wrap around to front.
self.trb_pointer.segment_index = 0;
self.cycle_bit = !self.cycle_bit;
}
}
}
pub fn get_next(&mut self) -> Option<TransferRequestBlock> {
let curr = self.current_trb();
if curr.cycle() != self.cycle_bit {
None
} else {
self.increment_pointer();
Some(curr)
}
}
pub fn update_dequeue_pointer(&self, interrupter: &mut InterrupterRegisterSet) {
interrupter.update_dequeue_pointer(self.erdp_physical_address());
}
}

View file

@ -1,2 +1,7 @@
pub mod data_structures; mod data_structures;
pub mod registers; mod device_context_base_array;
pub mod driver;
mod event_ring;
mod registers;
mod trb_ring;

View file

@ -321,6 +321,7 @@ pub struct HCCParams2 {
/// ///
/// These registers are located at the addresses specified in BAR0 and BAR1 in the PCI Header. /// These registers are located at the addresses specified in BAR0 and BAR1 in the PCI Header.
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Copy, Clone)]
pub struct HostControllerCapabilities { pub struct HostControllerCapabilities {
pub cap_length_and_version: HostControllerCapabilitiesLengthAndVersion, pub cap_length_and_version: HostControllerCapabilitiesLengthAndVersion,
pub params_1: HCSParams1, pub params_1: HCSParams1,

View file

@ -63,3 +63,19 @@ pub struct Doorbell {
/// This field returns 0 when read /// This field returns 0 when read
db_stream_id: u16, db_stream_id: u16,
} }
impl Doorbell {
pub fn ring(&mut self, target: u8) {
// SAFETY:
// - We know this is a valid reference.
unsafe {
core::ptr::write_volatile(
self as *mut _,
Doorbell::new().with_db_target(target).with_db_stream_id(0),
);
}
}
pub fn ring_command(&mut self) {
self.ring(0)
}
}

View file

@ -1,4 +1,13 @@
use core::ptr::NonNull;
use alloc::vec::Vec;
use bitfield_struct::bitfield; use bitfield_struct::bitfield;
use mammoth::{mem::map_direct_physical_and_leak, sync::Mutex};
use volatile::{VolatilePtr, VolatileRef, map_field};
use crate::xhci::registers::{
HostControllerCapabilities, HostControllerUsbPort, PortStatusAndControl,
};
#[bitfield(u32)] #[bitfield(u32)]
pub struct UsbCommand { pub struct UsbCommand {
@ -273,6 +282,7 @@ pub struct UsbConfigure {
/// of the Capability Registers Length (CAPLENGTH) register (refer to Section 5.3.1) /// of the Capability Registers Length (CAPLENGTH) register (refer to Section 5.3.1)
/// to the Capability Base address. All registers are multiples of 32 bits in length /// to the Capability Base address. All registers are multiples of 32 bits in length
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Copy, Clone)]
pub struct HostControllerOperational { pub struct HostControllerOperational {
pub usb_command: UsbCommand, pub usb_command: UsbCommand,
pub usb_status: UsbStatus, pub usb_status: UsbStatus,
@ -296,3 +306,103 @@ pub struct HostControllerOperational {
} }
const _: () = assert!(size_of::<HostControllerOperational>() == 0x3C); const _: () = assert!(size_of::<HostControllerOperational>() == 0x3C);
pub struct HostControllerOperationalWrapper {
// TODO: Fix alignment of this type so we can do more targetted reads and writes.
operational: Mutex<VolatileRef<'static, HostControllerOperational>>,
// TODO: This should maybe be its own structure.
ports: Vec<Mutex<VolatileRef<'static, HostControllerUsbPort>>>,
}
impl HostControllerOperationalWrapper {
pub fn new(mmio_address: usize) -> (Self, HostControllerCapabilities) {
const MAP_SIZE: usize = 0x1000;
let caps_ptr: *mut HostControllerCapabilities =
map_direct_physical_and_leak(mmio_address as u64, MAP_SIZE as u64);
// SAFETY:
// - The pointer is valid.
// - No other thread has access in this block.
let capabilities = unsafe {
VolatilePtr::new(
// UNWRAP: We just constructed this object with a non-null value.
NonNull::new(caps_ptr).unwrap(),
)
.read()
};
let cap_length_and_version = capabilities.cap_length_and_version;
// TODO: I don't think we acutally handle this properly.
// SAFETY: XHCI Spec says that this resides in a single page of memory which we mapped
// above.
//
// BAR0 Size Allocation
// If virtualization is supported, the Capability and Operational Register sets, and
// the Extended Capabilities may reside in a single page of virtual memory,
let operational_ptr = unsafe {
(caps_ptr as *mut u8).add(cap_length_and_version.cap_length() as usize)
as *mut HostControllerOperational
};
const PORT_OFFSET: usize = 0x400;
// FIXME: This calculation is cursed.
let ports_addr = unsafe { (operational_ptr as *mut u8).add(PORT_OFFSET) as usize };
let ports_space = MAP_SIZE - cap_length_and_version.cap_length() as usize - PORT_OFFSET;
let max_ports_we_support = ports_space / size_of::<HostControllerUsbPort>();
let params_1 = capabilities.params_1;
assert!(
params_1.max_ports() as usize <= max_ports_we_support,
"TODO: Support more ports."
);
let mut ports = Vec::new();
let ports_addr = ports_addr as *mut HostControllerUsbPort;
for port_index in 0..params_1.max_ports() {
ports.push(unsafe {
Mutex::new(VolatileRef::new(
NonNull::new(ports_addr.add(port_index as usize)).unwrap(),
))
});
}
let operational = Self {
operational: Mutex::new(unsafe {
VolatileRef::new(NonNull::new(operational_ptr).unwrap())
}),
ports,
};
(operational, capabilities)
}
pub fn update(&self, f: impl Fn(HostControllerOperational) -> HostControllerOperational) {
self.operational.lock().as_mut_ptr().update(f);
}
pub fn status(&self) -> UsbStatus {
self.operational.lock().as_ptr().read().usb_status
}
pub fn command(&self) -> UsbCommand {
self.operational.lock().as_ptr().read().usb_command
}
pub fn get_port(&self, index: usize) -> HostControllerUsbPort {
self.ports[index].lock().as_ptr().read()
}
pub fn update_port_status(
&self,
index: usize,
f: impl Fn(PortStatusAndControl) -> PortStatusAndControl,
) {
let mut port_ref = self.ports[index].lock();
let ptr = port_ref.as_mut_ptr();
map_field!(ptr.status_and_control).update(f);
}
pub fn num_ports(&self) -> usize {
self.ports.len()
}
}

View file

@ -9,13 +9,15 @@ use bitfield_struct::bitfield;
/// XHCI Spec 5.4.8 /// XHCI Spec 5.4.8
#[bitfield(u32)] #[bitfield(u32)]
pub struct PortStatusAndControl { pub struct PortStatusAndControl {
/// A host controller shall implement one or more port registers. The number of /// Current Connect Status (CCS) ROS. Default = 0. 1 = A device is connected81 to the port. 0 =
/// port registers implemented by a particular instantiation of a host controller is /// A device is not connected. This value reflects the current state of the port, and may not
/// documented in the HCSPARAMS1 register (Section 5.3.3). Software uses this /// correspond directly to the event that caused the Connect Status Change (CSC) bit to be set to 1.
/// information as an input parameter to determine how many ports need to be ///
/// serviced. All ports have the structure defined below. /// Refer to sections 4.19.3 and 4.19.4 for more details on the Connect Status Change (CSC)
/// assertion conditions.
/// This flag is 0 if PP is 0.
#[bits(access=RO)] #[bits(access=RO)]
current_connect_status: bool, pub current_connect_status: bool,
/// Port Enabled/Disabled (PED) RW1CS. Default = 0. 1 = Enabled. 0 = Disabled. /// Port Enabled/Disabled (PED) RW1CS. Default = 0. 1 = Enabled. 0 = Disabled.
/// Ports may only be enabled by the xHC. Software cannot enable a port by writing a 1 to this flag. /// Ports may only be enabled by the xHC. Software cannot enable a port by writing a 1 to this flag.
@ -43,9 +45,9 @@ pub struct PortStatusAndControl {
/// from 1 to 0 after a successful reset. Refer to Port Reset (PR) bit for more information on how /// from 1 to 0 after a successful reset. Refer to Port Reset (PR) bit for more information on how
/// the PED bit is managed. /// the PED bit is managed.
/// ///
/// Note that when software writes this bit to a 1, it shall also write a 0 to the PR bit82. /// Note that when software writes this bit to a 1, it shall also write a 0 to the PR bit.
/// This flag is 0 if PP is 0. /// This flag is 0 if PP is 0.
port_enabled_disabled: bool, pub port_enabled_disabled: bool,
__: bool, __: bool,
@ -53,7 +55,7 @@ pub struct PortStatusAndControl {
/// condition. 0 = This port does not have an over-current condition. This bit shall automatically /// condition. 0 = This port does not have an over-current condition. This bit shall automatically
/// transition from a 1 to a 0 when the over-current condition is removed. /// transition from a 1 to a 0 when the over-current condition is removed.
#[bits(access=RO)] #[bits(access=RO)]
over_current_active: bool, pub over_current_active: bool,
/// Port Reset (PR) RW1S. Default = 0. 1 = Port Reset signaling is asserted. 0 = Port is not in /// Port Reset (PR) RW1S. Default = 0. 1 = Port Reset signaling is asserted. 0 = Port is not in
/// Reset. When software writes a 1 to this bit generating a 0 to 1 transition, the bus reset /// Reset. When software writes a 1 to this bit generating a 0 to 1 transition, the bus reset
@ -65,7 +67,7 @@ pub struct PortStatusAndControl {
/// the Enabled state. Refer to sections 4.15.2.3 and 4.19.1.1. /// the Enabled state. Refer to sections 4.15.2.3 and 4.19.1.1.
/// ///
/// This flag is 0 if PP is 0. /// This flag is 0 if PP is 0.
port_reset: bool, pub port_reset: bool,
/// Port Link State (PLS) RWS. Default = RxDetect (5). This field is used to power manage the port /// Port Link State (PLS) RWS. Default = RxDetect (5). This field is used to power manage the port
/// and reflects its current link state. /// and reflects its current link state.
@ -128,7 +130,7 @@ pub struct PortStatusAndControl {
/// USB2 LPM ECR for more information on USB link power management operation. Refer to section /// USB2 LPM ECR for more information on USB link power management operation. Refer to section
/// 7.2 for supported USB protocols /// 7.2 for supported USB protocols
#[bits(4)] #[bits(4)]
port_link_status: u8, pub port_link_status: u8,
/// Port Power (PP) RWS. Default = 1. This flag reflects a port's logical, power control state. /// Port Power (PP) RWS. Default = 1. This flag reflects a port's logical, power control state.
/// Because host controllers can implement different methods of port power switching, this flag may /// Because host controllers can implement different methods of port power switching, this flag may
/// or may not represent whether (VBus) power is actually applied to the port. When PP equals a '0' /// or may not represent whether (VBus) power is actually applied to the port. When PP equals a '0'
@ -156,7 +158,7 @@ pub struct PortStatusAndControl {
/// ///
/// Refer to section 5.1.2 in the SSIC Spec for more information. /// Refer to section 5.1.2 in the SSIC Spec for more information.
/// Refer to section 4.19.4 for more information. /// Refer to section 4.19.4 for more information.
port_power: bool, pub port_power: bool,
/// Port Speed (Port Speed) ROS. Default = 0. This field identifies the speed of the connected /// Port Speed (Port Speed) ROS. Default = 0. This field identifies the speed of the connected
/// USB Device. This field is only relevant if a device is connected (CCS = 1) in all other cases this /// USB Device. This field is only relevant if a device is connected (CCS = 1) in all other cases this
/// field shall indicate Undefined Speed. Refer to section 4.19.3. /// field shall indicate Undefined Speed. Refer to section 4.19.3.
@ -168,7 +170,7 @@ pub struct PortStatusAndControl {
/// ///
/// Note: This field is invalid on a USB2 protocol port until after the port is reset. /// Note: This field is invalid on a USB2 protocol port until after the port is reset.
#[bits(4)] #[bits(4)]
port_speed: u8, pub port_speed: u8,
/// Port Indicator Control (PIC) RWS. Default = 0. Writing to these bits has no effect if the Port /// Port Indicator Control (PIC) RWS. Default = 0. Writing to these bits has no effect if the Port
/// Indicators (PIND) bit in the HCCPARAMS1 register is a 0. If PIND bit is a 1, then the bit /// Indicators (PIND) bit in the HCCPARAMS1 register is a 0. If PIND bit is a 1, then the bit
/// encodings are: /// encodings are:
@ -182,11 +184,11 @@ pub struct PortStatusAndControl {
/// Refer to the USB2 Specification section 11.5.3 for a description on how these bits shall be used. /// Refer to the USB2 Specification section 11.5.3 for a description on how these bits shall be used.
/// This field is 0 if PP is 0 /// This field is 0 if PP is 0
#[bits(2)] #[bits(2)]
port_indicator_control: u8, pub port_indicator_control: u8,
/// Port Link State Write Strobe (LWS) RW. Default = 0. When this bit is set to 1 on a write /// Port Link State Write Strobe (LWS) RW. Default = 0. When this bit is set to 1 on a write
/// reference to this register, this flag enables writes to the PLS field. When 0, write data in PLS field /// reference to this register, this flag enables writes to the PLS field. When 0, write data in PLS field
/// is ignored. Reads to this bit return 0 /// is ignored. Reads to this bit return 0
port_link_state_write_strobe: bool, pub port_link_state_write_strobe: bool,
/// Connect Status Change (CSC) RW1CS. Default = 0. 1 = Change in CCS. 0 = No change. This /// Connect Status Change (CSC) RW1CS. Default = 0. 1 = Change in CCS. 0 = No change. This
/// flag indicates a change has occurred in the ports Current Connect Status (CCS) or Cold Attach /// flag indicates a change has occurred in the ports Current Connect Status (CCS) or Cold Attach
/// Status (CAS) bits. Note that this flag shall not be set if the CCS transition was due to software /// Status (CAS) bits. Note that this flag shall not be set if the CCS transition was due to software
@ -196,7 +198,7 @@ pub struct PortStatusAndControl {
/// before system software has cleared the changed condition, root hub hardware will be “setting” /// before system software has cleared the changed condition, root hub hardware will be “setting”
/// an already-set bit (i.e., the bit will remain 1). Software shall clear this bit by writing a 1 to it. /// an already-set bit (i.e., the bit will remain 1). Software shall clear this bit by writing a 1 to it.
/// Refer to section 4.19.2 for more information on change bit usage. /// Refer to section 4.19.2 for more information on change bit usage.
connect_status_change: bool, pub connect_status_change: bool,
/// Port Enabled/Disabled Change (PEC) RW1CS. Default = 0. 1 = change in PED. 0 = No /// Port Enabled/Disabled Change (PEC) RW1CS. Default = 0. 1 = change in PED. 0 = No
/// change. Note that this flag shall not be set if the PED transition was due to software setting PP to /// change. Note that this flag shall not be set if the PED transition was due to software setting PP to
/// 0. Software shall clear this bit by writing a 1 to it. Refer to section 4.19.2 for more information /// 0. Software shall clear this bit by writing a 1 to it. Refer to section 4.19.2 for more information
@ -207,7 +209,7 @@ pub struct PortStatusAndControl {
/// ///
/// Specification for the definition of a Port Error). /// Specification for the definition of a Port Error).
/// For a USB3 protocol port, this bit shall never be set to 1. /// For a USB3 protocol port, this bit shall never be set to 1.
port_enabled_disabled_change: bool, pub port_enabled_disabled_change: bool,
/// Warm Port Reset Change (WRC) RW1CS/RsvdZ. Default = 0. This bit is set when Warm Reset /// Warm Port Reset Change (WRC) RW1CS/RsvdZ. Default = 0. This bit is set when Warm Reset
/// processing on this port completes. 0 = No change. 1 = Warm Reset complete. Note that this /// processing on this port completes. 0 = No change. 1 = Warm Reset complete. Note that this
/// flag shall not be set to 1 if the Warm Reset processing was forced to terminate due to software /// flag shall not be set to 1 if the Warm Reset processing was forced to terminate due to software
@ -215,18 +217,18 @@ pub struct PortStatusAndControl {
/// Refer to section 4.19.2 for more information on change bit usage. /// Refer to section 4.19.2 for more information on change bit usage.
/// ///
/// This bit only applies to USB3 protocol ports. For USB2 protocol ports it shall be RsvdZ. /// This bit only applies to USB3 protocol ports. For USB2 protocol ports it shall be RsvdZ.
warm_port_reset_change: bool, pub warm_port_reset_change: bool,
/// Over-current Change (OCC) RW1CS. Default = 0. This bit shall be set to a 1 when there is a 0 /// Over-current Change (OCC) RW1CS. Default = 0. This bit shall be set to a 1 when there is a 0
/// to 1 or 1 to 0 transition of Over-current Active (OCA). Software shall clear this bit by writing a /// to 1 or 1 to 0 transition of Over-current Active (OCA). Software shall clear this bit by writing a
/// 1 to it. Refer to section 4.19.2 for more information on change bit usage. /// 1 to it. Refer to section 4.19.2 for more information on change bit usage.
over_current_change: bool, pub over_current_change: bool,
/// Port Reset Change (PRC) RW1CS. Default = 0. This flag is set to 1 due to a '1' to '0' transition /// Port Reset Change (PRC) RW1CS. Default = 0. This flag is set to 1 due to a '1' to '0' transition
/// of Port Reset (PR). e.g. when any reset processing (Warm or Hot) on this port is complete. Note /// of Port Reset (PR). e.g. when any reset processing (Warm or Hot) on this port is complete. Note
/// that this flag shall not be set to 1 if the reset processing was forced to terminate due to software /// that this flag shall not be set to 1 if the reset processing was forced to terminate due to software
/// clearing PP or PED to '0'. 0 = No change. 1 = Reset complete. Software shall clear this bit by /// clearing PP or PED to '0'. 0 = No change. 1 = Reset complete. Software shall clear this bit by
/// writing a '1' to it. Refer to section 4.19.5. Refer to section 4.19.2 for more information on change /// writing a '1' to it. Refer to section 4.19.5. Refer to section 4.19.2 for more information on change
/// bit usage /// bit usage
port_reset_change: bool, pub port_reset_change: bool,
/// Port Link State Change (PLC) RW1CS. Default = 0. This flag is set to 1 due to the following /// Port Link State Change (PLC) RW1CS. Default = 0. This flag is set to 1 due to the following
/// PLS transitions: /// PLS transitions:
/// ///
@ -256,7 +258,7 @@ pub struct PortStatusAndControl {
/// writing a '1' to it. Refer to “PLC Condition:” references in section 4.19.1 /// writing a '1' to it. Refer to “PLC Condition:” references in section 4.19.1
/// for the specific port state transitions that set this flag. Refer to section /// for the specific port state transitions that set this flag. Refer to section
/// 4.19.2 for more information on change bit usage. /// 4.19.2 for more information on change bit usage.
port_link_state_change: bool, pub port_link_state_change: bool,
/// Port Config Error Change (CEC) RW1CS/RsvdZ. Default = 0. This flag indicates that the port /// Port Config Error Change (CEC) RW1CS/RsvdZ. Default = 0. This flag indicates that the port
/// failed to configure its link partner. 0 = No change. 1 = Port Config Error detected. Software shall /// failed to configure its link partner. 0 = No change. 1 = Port Config Error detected. Software shall
/// clear this bit by writing a '1' to it. Refer to section 4.19.2 for more information on change bit /// clear this bit by writing a '1' to it. Refer to section 4.19.2 for more information on change bit
@ -264,7 +266,7 @@ pub struct PortStatusAndControl {
/// ///
/// Note: This flag is valid only for USB3 protocol ports. For USB2 protocol ports this bit shall be /// Note: This flag is valid only for USB3 protocol ports. For USB2 protocol ports this bit shall be
/// RsvdZ. /// RsvdZ.
port_config_error_change: bool, pub port_config_error_change: bool,
/// Cold Attach Status (CAS) RO. Default = 0. 1 = Far-end Receiver Terminations were detected /// Cold Attach Status (CAS) RO. Default = 0. 1 = Far-end Receiver Terminations were detected
/// in the Disconnected state and the Root Hub Port State Machine was unable to advance to the /// in the Disconnected state and the Root Hub Port State Machine was unable to advance to the
/// Enabled state. Refer to sections 4.19.8 for more details on the Cold Attach Status (CAS) assertion /// Enabled state. Refer to sections 4.19.8 for more details on the Cold Attach Status (CAS) assertion
@ -272,31 +274,48 @@ pub struct PortStatusAndControl {
/// transitions to 1. /// transitions to 1.
/// This flag is 0 if PP is 0 or for USB2 protocol ports /// This flag is 0 if PP is 0 or for USB2 protocol ports
#[bits(access=RO)] #[bits(access=RO)]
cold_attach_status: bool, pub cold_attach_status: bool,
/// Wake on Connect Enable (WCE) RWS. Default = 0. Writing this bit to a 1 enables the port to /// Wake on Connect Enable (WCE) RWS. Default = 0. Writing this bit to a 1 enables the port to
/// be sensitive to device connects as system wake-up events96. Refer to section 4.15 for operational /// be sensitive to device connects as system wake-up events96. Refer to section 4.15 for operational
/// model. /// model.
wake_on_connect_enable: bool, pub wake_on_connect_enable: bool,
/// Wake on Disconnect Enable (WDE) RWS. Default = 0. Writing this bit to a 1 enables the port /// Wake on Disconnect Enable (WDE) RWS. Default = 0. Writing this bit to a 1 enables the port
/// to be sensitive to device disconnects as system wake-up events. Refer to section 4.15 for /// to be sensitive to device disconnects as system wake-up events. Refer to section 4.15 for
/// operational model. /// operational model.
wake_on_disconnect_enable: bool, pub wake_on_disconnect_enable: bool,
/// Wake on Over-current Enable (WOE) RWS. Default = 0. Writing this bit to a 1 enables the /// Wake on Over-current Enable (WOE) RWS. Default = 0. Writing this bit to a 1 enables the
/// port to be sensitive to over-current conditions as system wake-up events96. Refer to section 4.15 /// port to be sensitive to over-current conditions as system wake-up events96. Refer to section 4.15
/// for operational model. /// for operational model.
wake_on_overcurrent_enable: bool, pub wake_on_overcurrent_enable: bool,
__: bool, __: bool,
__: bool, __: bool,
/// Device Removable97 (DR) - RO. This flag indicates if this port has a removable device attached. /// Device Removable97 (DR) - RO. This flag indicates if this port has a removable device attached.
/// 1 = Device is non-removable. 0 = Device is removable. /// 1 = Device is non-removable. 0 = Device is removable.
#[bits(access=RO)] #[bits(access=RO)]
device_removable: bool, pub device_removable: bool,
/// Warm Port Reset (WPR) RW1S/RsvdZ. Default = 0. When software writes a 1 to this bit, the /// Warm Port Reset (WPR) RW1S/RsvdZ. Default = 0. When software writes a 1 to this bit, the
/// Warm Reset sequence as defined in the USB3 Specification is initiated and the PR flag is set to 1. /// Warm Reset sequence as defined in the USB3 Specification is initiated and the PR flag is set to 1.
/// Once initiated, the PR, PRC, and WRC flags shall reflect the progress of the Warm Reset /// Once initiated, the PR, PRC, and WRC flags shall reflect the progress of the Warm Reset
/// sequence. This flag shall always return 0 when read. Refer to section 4.19.5.1. /// sequence. This flag shall always return 0 when read. Refer to section 4.19.5.1.
/// This flag only applies to USB3 protocol ports. For USB2 protocol ports it shall be RsvdZ. /// This flag only applies to USB3 protocol ports. For USB2 protocol ports it shall be RsvdZ.
warm_port_reset: bool, pub warm_port_reset: bool,
}
impl PortStatusAndControl {
pub fn clear_change_bits(&self) -> Self {
PortStatusAndControl::new()
.with_connect_status_change(true)
.with_port_enabled_disabled_change(true)
.with_warm_port_reset_change(true)
.with_over_current_change(true)
.with_port_reset_change(true)
.with_port_link_state_change(true)
.with_port_config_error_change(true)
.with_port_power(self.port_power())
.with_wake_on_connect_enable(self.wake_on_connect_enable())
.with_wake_on_disconnect_enable(self.wake_on_disconnect_enable())
.with_wake_on_overcurrent_enable(self.wake_on_overcurrent_enable())
}
} }
/// XHCI Spec 5.4.9 /// XHCI Spec 5.4.9
@ -384,12 +403,13 @@ pub struct PortLinkInfo {
/// from the HostControllerOperation address. /// from the HostControllerOperation address.
/// ///
/// Where MAX_PORTS is HostControllerCapabilities.params_1.max_ports /// Where MAX_PORTS is HostControllerCapabilities.params_1.max_ports
#[repr(C, packed)] #[repr(C)]
#[derive(Copy, Clone)]
pub struct HostControllerUsbPort { pub struct HostControllerUsbPort {
status_and_control: PortStatusAndControl, pub status_and_control: PortStatusAndControl,
power_management_status_and_control: PortPowerManagementStatusAndControl, pub power_management_status_and_control: PortPowerManagementStatusAndControl,
link_info: PortLinkInfo, pub link_info: PortLinkInfo,
hardware_lpm_control: u32, pub hardware_lpm_control: u32,
} }
const _: () = assert!(size_of::<HostControllerUsbPort>() == 0x10); const _: () = assert!(size_of::<HostControllerUsbPort>() == 0x10);

View file

@ -1,5 +1,7 @@
use bitfield_struct::bitfield; use bitfield_struct::bitfield;
use crate::xhci::data_structures::EventRingSegmentTable;
/// The Interrupter Management register allows system software to enable, disable, /// The Interrupter Management register allows system software to enable, disable,
/// and detect xHC interrupts. /// and detect xHC interrupts.
/// ///
@ -49,16 +51,6 @@ pub struct InterrupterModeration {
/// XHCI 5.5.2.3.1 /// XHCI 5.5.2.3.1
#[bitfield(u32)] #[bitfield(u32)]
pub struct EventRingSegmentTableSize { pub struct EventRingSegmentTableSize {
/// Event Ring Segment Table Size RW. Default = 0. This field identifies the number of valid
/// Event Ring Segment Table entries in the Event Ring Segment Table pointed to by the Event Ring
/// Segment Table Base Address register. The maximum value supported by an xHC
/// implementation for this register is defined by the ERST Max field in the HCSPARAMS2 register
/// (5.3.4).
/// For Secondary Interrupters: Writing a value of 0 to this field disables the Event Ring. Any events
/// targeted at this Event Ring when it is disabled shall result in undefined behavior of the Event
/// Ring.
/// For the Primary Interrupter: Writing a value of 0 to this field shall result in undefined behavior
/// of the Event Ring. The Primary Event Ring cannot be disabled.
pub event_ring_segment_table_size: u16, pub event_ring_segment_table_size: u16,
_reserved: u16, _reserved: u16,
} }
@ -98,9 +90,18 @@ pub struct EventRingDequePointer {
pub struct InterrupterRegisterSet { pub struct InterrupterRegisterSet {
pub interrupter_management: InterrupterManagement, pub interrupter_management: InterrupterManagement,
pub interrupter_moderation: InterrupterModeration, pub interrupter_moderation: InterrupterModeration,
pub event_ring_segement_table_size: EventRingSegmentTableSize, /// Event Ring Segment Table Size RW. Default = 0. This field identifies the number of valid
_reserved: u32, /// Event Ring Segment Table entries in the Event Ring Segment Table pointed to by the Event Ring
/// Segment Table Base Address register. The maximum value supported by an xHC
/// implementation for this register is defined by the ERST Max field in the HCSPARAMS2 register
/// (5.3.4).
/// For Secondary Interrupters: Writing a value of 0 to this field disables the Event Ring. Any events
/// targeted at this Event Ring when it is disabled shall result in undefined behavior of the Event
/// Ring.
/// For the Primary Interrupter: Writing a value of 0 to this field shall result in undefined behavior
/// of the Event Ring. The Primary Event Ring cannot be disabled.
event_ring_segment_table_size: u32,
___: u32,
/// Event Ring Segment Table Base Address Register RW. Default = 0. This field defines the /// Event Ring Segment Table Base Address Register RW. Default = 0. This field defines the
/// high order bits of the start address of the Event Ring Segment Table. /// high order bits of the start address of the Event Ring Segment Table.
/// Writing this register sets the Event Ring State Machine:EREP Advancement to the Start state. /// Writing this register sets the Event Ring State Machine:EREP Advancement to the Start state.
@ -111,6 +112,54 @@ pub struct InterrupterRegisterSet {
/// NOTE: This must be aligned such that bits 0:5 are 0. /// NOTE: This must be aligned such that bits 0:5 are 0.
/// ///
/// XHCI 5.5.2.3.2 /// XHCI 5.5.2.3.2
pub event_ring_segment_table_base_address: u64, event_ring_segment_table_base_address: u64,
pub event_ring_deque_pointer: u64, event_ring_deque_pointer: u64,
} }
impl InterrupterRegisterSet {
/// SAFETY:
/// - For the primary interrupter HC must be halted.
/// - The event rings size must be at most ERST_MAX from HCSPARAMS2
pub unsafe fn set_event_ring(
&mut self,
event_ring_segment_table: &EventRingSegmentTable,
event_ring_dequeue_pointer: usize,
) {
// NOTE: We must write the size before the base address otherwise qemu is unhappy.
// Not sure if this is required by the spec.
// SAFETY:
// - We know this address is valid and we have a mut reference to it.
unsafe {
core::ptr::write_volatile(
core::ptr::addr_of!(self.event_ring_segment_table_size) as *mut _,
event_ring_segment_table.len() as u32,
);
core::ptr::write_volatile(
core::ptr::addr_of!(self.event_ring_segment_table_base_address) as *mut _,
event_ring_segment_table.physical_address(),
);
core::ptr::write_volatile(
core::ptr::addr_of!(self.event_ring_deque_pointer) as *mut _,
event_ring_dequeue_pointer,
);
}
}
pub fn update_dequeue_pointer(&mut self, event_ring_dequeue_pointer: usize) {
// SAFETY:
// - We know this address is valid and we have a mut pointer to it.
unsafe {
// TODO: Preserve lower bits, also update it to make it clear that we
// are clearing the EHB bit.
core::ptr::write_volatile(
core::ptr::addr_of!(self.event_ring_deque_pointer) as *mut _,
event_ring_dequeue_pointer | (1 << 3),
)
}
}
}
const _: () = assert!(size_of::<InterrupterRegisterSet>() == 0x20);

View file

@ -0,0 +1,163 @@
use core::task::{Poll, Waker};
use alloc::{collections::vec_deque::VecDeque, sync::Arc, vec::Vec};
use mammoth::sync::Mutex;
use crate::xhci::data_structures::{TransferRequestBlock, TrbLink, TrbRingSegment, TypedTrb};
struct TrbFutureState<T> {
/// Physical Address for the enqueued TRB.
/// Used for sanity checking.
physical_address: usize,
waker: Option<Waker>,
response: Option<T>,
}
#[derive(Clone)]
pub struct TrbFuture<T: TypedTrb> {
state: Arc<Mutex<TrbFutureState<T>>>,
}
impl<T: TypedTrb> TrbFuture<T> {
fn new(paddr: usize) -> Self {
Self {
state: Arc::new(Mutex::new(TrbFutureState {
physical_address: paddr,
waker: None,
response: None,
})),
}
}
}
impl<T: TypedTrb> Future for TrbFuture<T> {
type Output = T;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
let mut state = self.state.lock();
match state.response {
Some(trb) => Poll::Ready(trb),
None => {
state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
}
#[derive(Default, Copy, Clone, Debug)]
pub struct TrbPointer {
/// Index into the vector of trb segments.
pub segment_index: usize,
/// Index into the specific segment.
/// This is a TransferRequestBlock index,
/// to get the physical_offset use segment_physical_offset()
pub segment_offset: usize,
}
impl TrbPointer {
pub fn segment_physical_offset(&self) -> usize {
self.segment_offset * size_of::<TransferRequestBlock>()
}
}
pub struct TrbRing<T: TypedTrb> {
segments: Vec<TrbRingSegment>,
enqueue_pointer: TrbPointer,
cycle_bit: bool,
pending_futures: VecDeque<TrbFuture<T>>,
}
impl<T: TypedTrb> TrbRing<T> {
pub fn new() -> Self {
Self {
// TODO: What size and count should this be.
segments: alloc::vec![TrbRingSegment::new(100)],
enqueue_pointer: TrbPointer::default(),
// Start with this as true so we are flipping bits from 0 (default) to 1
// to mark the enqueue pointer.
cycle_bit: true,
pending_futures: VecDeque::new(),
}
}
pub fn physical_base_address(&self) -> usize {
self.segments[0].physical_address()
}
fn physical_address_of_enqueue_pointer(&self) -> usize {
self.segments[self.enqueue_pointer.segment_index].physical_address()
+ self.enqueue_pointer.segment_physical_offset()
}
pub fn enqueue_trb(&mut self, trb: TransferRequestBlock) -> TrbFuture<T> {
let paddr = self.physical_address_of_enqueue_pointer();
*self.next_trb_ref() = trb.with_cycle(self.cycle_bit);
self.advance_enqueue_pointer();
let future = TrbFuture::new(paddr);
self.pending_futures.push_back(future.clone());
future
}
fn next_trb_ref(&mut self) -> &mut TransferRequestBlock {
&mut self.segments[self.enqueue_pointer.segment_index][self.enqueue_pointer.segment_offset]
}
fn advance_enqueue_pointer(&mut self) {
self.enqueue_pointer.segment_offset += 1;
if self.enqueue_pointer.segment_offset
== self.segments[self.enqueue_pointer.segment_index].len() - 1
{
// We have reached the end of the segment, insert a link trb.
// Increment the segment index with wrapping.
let next_segment_index =
if self.enqueue_pointer.segment_index + 1 == self.segments.len() {
0
} else {
self.enqueue_pointer.segment_index + 1
};
let next_segment_pointer = self.segments[next_segment_index].physical_address();
let toggle_cycle = next_segment_index == 0;
*self.next_trb_ref() = TrbLink::new()
.with_ring_segment_pointer(next_segment_pointer as u64)
.with_cycle(self.cycle_bit)
.with_toggle_cycle(toggle_cycle)
.to_trb();
// Flip toggle cycle bit if necessary.
self.cycle_bit ^= toggle_cycle;
self.enqueue_pointer = TrbPointer {
segment_index: next_segment_index,
segment_offset: 0,
};
}
}
pub fn handle_commpletion(&mut self, completion_trb: T) {
let trb = completion_trb.to_trb();
let paddr = trb.parameter() as usize;
let completion = self.pending_futures.pop_front().unwrap();
let mut completion = completion.state.lock();
// TODO: Handle recovery scenarios here.
assert!(
completion.physical_address == paddr,
"Got an unexpected command completion. Expected: {:0x}, Got: {:0x}",
completion.physical_address,
paddr
);
completion.response = Some(completion_trb);
if let Some(waker) = &completion.waker {
waker.wake_by_ref();
}
}
}

View file

@ -18,7 +18,7 @@ if [[ $1 == "debug" ]]; then
fi fi
# Use machine q35 to access PCI devices. # Use machine q35 to access PCI devices.
qemu-system-x86_64 -machine q35 -d guest_errors -m 1G -serial stdio -hda ${BUILD_DIR}/disk.img ${QEMU_ARGS} -device nec-usb-xhci,id=xhci -device usb-kbd,bus=xhci.0 ~/.local/bin/qemu-system-x86_64 -machine q35 -d guest_errors -m 1G -serial stdio -hda ${BUILD_DIR}/disk.img ${QEMU_ARGS} -device nec-usb-xhci,id=xhci -device usb-kbd,bus=xhci.0
popd popd
# Extra options to add to this script in the future. # Extra options to add to this script in the future.

View file

@ -18,6 +18,8 @@ void DriverManager::WriteMessage(uint64_t irq_num, IpcMessage&& message) {
return; return;
} }
dbgln("IRQ offset {x}", offset);
driver_list_[offset]->Send(glcr::Move(message)); driver_list_[offset]->Send(glcr::Move(message));
} }