diff --git a/' b/' new file mode 100644 index 0000000..d69da02 --- /dev/null +++ b/' @@ -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 +} diff --git a/rust/lib/mammoth/src/mem.rs b/rust/lib/mammoth/src/mem.rs index 6a67976..d35097b 100644 --- a/rust/lib/mammoth/src/mem.rs +++ b/rust/lib/mammoth/src/mem.rs @@ -250,8 +250,8 @@ pub fn map_cap_and_leak(mem_cap: Capability) -> u64 { vaddr } -pub fn map_direct_physical_and_leak(paddr: usize, size: usize) -> *mut T { - let mem_cap = syscall::memory_object_direct_physical(paddr as u64, size as u64).unwrap(); +pub fn map_direct_physical_and_leak(paddr: u64, size: u64) -> *mut T { + let mem_cap = syscall::memory_object_direct_physical(paddr, size).unwrap(); let vaddr = syscall::address_space_map(&mem_cap).unwrap(); mem_cap.release(); vaddr as *mut T diff --git a/rust/lib/mammoth/src/physical_box.rs b/rust/lib/mammoth/src/physical_box.rs index 963b6ec..b3a11c3 100644 --- a/rust/lib/mammoth/src/physical_box.rs +++ b/rust/lib/mammoth/src/physical_box.rs @@ -16,22 +16,6 @@ pub struct PhysicalBox { _marker: PhantomData, } -impl PhysicalBox { - pub fn new(data: T) -> Self { - let (memory_region, paddr) = - MemoryRegion::contiguous_physical(size_of::() as u64).expect("Failed to allocate"); - // UNWRAP: We know this isn't null. - let ptr = NonNull::new(memory_region.mut_ptr_at_offset(0)).unwrap(); - unsafe { ptr.write(data) }; - Self { - data: ptr, - region: memory_region, - physical_address: paddr as usize, - _marker: PhantomData, - } - } -} - impl PhysicalBox { pub fn physical_address(&self) -> usize { self.physical_address @@ -67,10 +51,16 @@ impl PhysicalBox<[T]> { { let layout = core::alloc::Layout::array::(len).expect("Layout overflow"); - // TODO: Implement a function like alloc that takes a layout. + // TODO: Implement a function like alloc that takes a layout. let (memory_region, paddr) = let (memory_region, paddr) = 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); for i in 0..len { unsafe { diff --git a/rust/sys/denali/src/ahci/controller.rs b/rust/sys/denali/src/ahci/controller.rs index 08bd082..6289793 100644 --- a/rust/sys/denali/src/ahci/controller.rs +++ b/rust/sys/denali/src/ahci/controller.rs @@ -29,7 +29,7 @@ impl AhciController { let pci_device = PciDevice::from_cap(pci_memory).unwrap(); let hba_vaddr = - mem::map_direct_physical_and_leak(pci_device.header().bars[5] as usize, 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 mut controller = Self { diff --git a/rust/sys/voyageurs/src/xhci/data_structures/device_context.rs b/rust/sys/voyageurs/src/xhci/data_structures/device_context.rs deleted file mode 100644 index 2d2946b..0000000 --- a/rust/sys/voyageurs/src/xhci/data_structures/device_context.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::xhci::data_structures::{EndpointContext, SlotContext}; - -#[repr(C, align(64))] -#[derive(Default)] -pub struct DeviceContext { - slot_context: SlotContext, - endpoint_context_0: EndpointContext, - endpoint_contexts: [EndpointContext; 30], -} - -const _: () = assert!(size_of::() == 0x400); diff --git a/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs b/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs index c013c87..0c62428 100644 --- a/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs +++ b/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs @@ -1,8 +1,7 @@ -use bitfield_struct::{bitenum, bitfield}; +use bitfield_struct::bitfield; -#[repr(u8)] -#[bitenum] #[derive(Debug)] +#[repr(u8)] pub enum EndpointState { /// The endpoint is not operationa. Disabled = 0, @@ -18,15 +17,29 @@ pub enum EndpointState { /// The endpoint is not running due to a TRB Error. SW may manipulate the Transfer /// Ring while in this state. Error = 4, - #[fallback] Unknown = 5, } -#[repr(u8)] -#[bitenum] +impl EndpointState { + const fn from_bits(value: u8) -> Self { + match value { + 0 => Self::Disabled, + 1 => Self::Running, + 2 => Self::Halted, + 3 => Self::Stopped, + 4 => Self::Error, + _ => Self::Unknown, + } + } + + const fn into_bits(self) -> u8 { + self as u8 + } +} + #[derive(Debug)] +#[repr(u8)] pub enum EndpointType { - #[fallback] NotValid = 0, IsochOut = 1, BulkOut = 2, @@ -37,6 +50,26 @@ pub enum EndpointType { InterruptIn = 7, } +impl EndpointType { + const fn from_bits(value: u8) -> Self { + match value { + 0 => Self::NotValid, + 1 => Self::IsochOut, + 2 => Self::BulkOut, + 3 => Self::InterruptOut, + 4 => Self::Control, + 5 => Self::IsochIn, + 6 => Self::BulkIn, + 7 => Self::InterruptIn, + _ => Self::NotValid, + } + } + + const fn into_bits(self) -> u8 { + self as u8 + } +} + #[bitfield(u64)] pub struct EndpointContextFields { /// Endpoint State (EP State). The Endpoint State identifies the current operational state of the @@ -170,37 +203,26 @@ impl TRDequeuePointer { self.tr_deque_pointer() << 4 } - pub fn set_pointer(&mut self, tr_deque_pointer: u64) { - self.set_tr_deque_pointer(tr_deque_pointer >> 4) - } - - pub fn with_pointer(self, tr_deque_pointer: u64) -> Self { + pub fn set_pointer(self, tr_deque_pointer: u64) -> TRDequeuePointer { self.with_tr_deque_pointer(tr_deque_pointer >> 4) } } -#[bitfield(u64)] -struct AdditionalFields { +#[repr(C, packed)] +pub struct EndpointContext { + pub fields: u64, + pub tr_deque_pointer: TRDequeuePointer, /// Average TRB Length. This field represents the average Length of the TRBs executed by this /// endpoint. The value of this field shall be greater than ‘0’. Refer to section 4.14.1.1 and the /// implementation note TRB Lengths and System Bus Bandwidth for more information. /// The xHC shall use this parameter to calculate system bus bandwidth requirements - pub average_trb_length: u16, + pub average_trb_lenght: u16, /// Max Endpoint Service Time Interval Payload Low (Max ESIT Payload Lo). This field indicates /// the low order 16 bits of the Max ESIT Payload. The Max ESIT Payload represents the total /// number of bytes this endpoint will transfer during an ESIT. This field is only valid for periodic /// endpoints. Refer to section 6.2.3.8 for more information. pub max_esit_payload_lo: u16, - __: u32, -} - -#[repr(C)] -#[derive(Default)] -pub struct EndpointContext { - pub fields: EndpointContextFields, - pub tr_deque_pointer: TRDequeuePointer, - additional_fields: AdditionalFields, - __: u64, + __: [u32; 3], } const _: () = assert!(size_of::() == 0x20); diff --git a/rust/sys/voyageurs/src/xhci/data_structures/input_context.rs b/rust/sys/voyageurs/src/xhci/data_structures/input_context.rs deleted file mode 100644 index 2f9967e..0000000 --- a/rust/sys/voyageurs/src/xhci/data_structures/input_context.rs +++ /dev/null @@ -1,33 +0,0 @@ -use bitfield_struct::bitfield; - -use crate::xhci::data_structures::{EndpointContext, SlotContext}; - -#[bitfield(u32)] -pub struct InputControlContextSettings { - configuration_value: u8, - interface_number: u8, - alternate_setting: u8, - __: u8, -} - -#[repr(C)] -#[derive(Default)] -pub struct InputControlContext { - pub drop_context_flags: u32, - pub add_context_flags: u32, - __: [u32; 5], - settings: InputControlContextSettings, -} - -const _: () = assert!(size_of::() == 0x20); - -#[repr(C)] -#[derive(Default)] -pub struct InputContext { - pub input_control_context: InputControlContext, - pub slot_context: SlotContext, - pub endpoint_context_0: EndpointContext, - pub endpoint_contexts: [EndpointContext; 30], -} - -const _: () = assert!(size_of::() == 0x420); diff --git a/rust/sys/voyageurs/src/xhci/data_structures/mod.rs b/rust/sys/voyageurs/src/xhci/data_structures/mod.rs index 6b33f73..445d62a 100644 --- a/rust/sys/voyageurs/src/xhci/data_structures/mod.rs +++ b/rust/sys/voyageurs/src/xhci/data_structures/mod.rs @@ -1,15 +1,10 @@ -mod device_context; mod endpoint_context; mod event_ring_segment_table; -mod input_context; mod slot_context; mod trb; mod trb_ring_segment; -pub use device_context::*; -pub use endpoint_context::*; pub use event_ring_segment_table::*; -pub use input_context::*; pub use slot_context::*; pub use trb::*; pub use trb_ring_segment::*; diff --git a/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs b/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs index d9f953c..ab6df95 100644 --- a/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs +++ b/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs @@ -125,7 +125,6 @@ pub struct SlotContextFields { } #[repr(C)] -#[derive(Default)] pub struct SlotContext { pub fields: SlotContextFields, __: u128, diff --git a/rust/sys/voyageurs/src/xhci/data_structures/trb.rs b/rust/sys/voyageurs/src/xhci/data_structures/trb.rs index e84afe7..92d8e4e 100644 --- a/rust/sys/voyageurs/src/xhci/data_structures/trb.rs +++ b/rust/sys/voyageurs/src/xhci/data_structures/trb.rs @@ -142,35 +142,6 @@ pub struct TrbLink { impl TypedTrb for TrbLink {} -#[bitfield(u128)] -pub struct TrbTransferEvent { - pub transfer_trb_pointer: u64, - - #[bits(24)] - pub trb_transfer_lenght: u32, - - /// 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, - #[bits(10)] - __: 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::TransferEvent)] - pub trb_type: TrbType, - - #[bits(5)] - pub endpoint_id: u8, - #[bits(3)] - __: u8, - - pub slot_id: u8, -} - -impl TypedTrb for TrbTransferEvent {} - #[bitenum] #[repr(u8)] pub enum CommandCompletionCode { @@ -261,18 +232,3 @@ pub struct TrbEnableSlotCommand { } impl TypedTrb for TrbEnableSlotCommand {} - -#[bitfield(u128)] -pub struct TrbAddressDeviceCommand { - pub input_context_pointer: u64, - __: u32, - #[bits(9)] - __: u16, - pub block_set_address_request: bool, - #[bits(6, default=TrbType::AddressDeviceCommand)] - trb_typ: TrbType, - __: u8, - pub slot_id: u8, -} - -impl TypedTrb for TrbAddressDeviceCommand {} diff --git a/rust/sys/voyageurs/src/xhci/device_context_base_array.rs b/rust/sys/voyageurs/src/xhci/device_context_base_array.rs index 6c32179..efddd4e 100644 --- a/rust/sys/voyageurs/src/xhci/device_context_base_array.rs +++ b/rust/sys/voyageurs/src/xhci/device_context_base_array.rs @@ -1,104 +1,22 @@ -use alloc::boxed::Box; -use mammoth::physical_box::PhysicalBox; +use mammoth::mem::MemoryRegion; -use crate::xhci::{ - data_structures::{ - DeviceContext, EndpointContextFields, EndpointState, EndpointType, InputContext, - TRDequeuePointer, TrbTransferEvent, - }, - registers::DoorbellPointer, - trb_ring::TrbRing, -}; - -struct DeviceContextBaseArray(PhysicalBox<[u64]>); +pub struct DeviceContextBaseArray { + #[allow(dead_code)] + region: MemoryRegion, + physical_addr: usize, +} impl DeviceContextBaseArray { - pub fn new(max_slots: u8) -> Self { - Self(PhysicalBox::default_with_count(0, max_slots as usize + 1)) - } -} - -pub struct DeviceSlot { - device_context: PhysicalBox, - endpoint_0_transfer_ring: TrbRing, -} - -impl DeviceSlot { - fn new() -> Self { + pub fn new() -> Self { + let (region, physical_addr) = MemoryRegion::contiguous_physical(0x1000).unwrap(); + region.zero_region(); Self { - device_context: PhysicalBox::new(DeviceContext::default()), - endpoint_0_transfer_ring: TrbRing::new(), - } - } -} - -pub struct DeviceSlotManager { - device_context_base_array: DeviceContextBaseArray, - slots: Box<[Option]>, - doorbells: Box<[DoorbellPointer]>, -} - -impl DeviceSlotManager { - pub fn new(max_slots: u8, doorbells: Box<[DoorbellPointer]>) -> Self { - assert!( - doorbells.len() == max_slots as usize, - "Got an incorrect doorbell slice size." - ); - Self { - device_context_base_array: DeviceContextBaseArray::new(max_slots), - slots: core::iter::repeat_with(|| None) - .take(max_slots as usize) - .collect(), - doorbells, + region, + physical_addr: physical_addr as usize, } } - pub fn device_context_base_array_physical_address(&self) -> usize { - self.device_context_base_array.0.physical_address() - } - - /// Prepares a slot and an input context for an address device command. - /// - /// Follows section 4.6.5 of the XHCI spec. - pub fn prep_slot_for_address_device( - &mut self, - slot_id: u8, - port_number: u8, - ) -> PhysicalBox { - // TODO: Ensure alignment - let device_slot = DeviceSlot::new(); - let mut input_context = PhysicalBox::new(InputContext::default()); - - // The Add Context flags for the Slot Context and the Endpoint 0 Context shall be set to ‘1’. - input_context.input_control_context.add_context_flags = 0x3; - - // See XHCI 4.5.2 for information - input_context.slot_context.fields = input_context - .slot_context - .fields - .with_root_hub_port_number(port_number) - .with_route_string(0) - .with_context_entries(1) - .with_interrupter_target(0); - - // The Endpoint 0 Context data structure in the - // Input Context shall define valid values for the TR Dequeue Pointer, EP Type, Error - // Count (CErr), and Max Packet Size fields. The MaxPStreams, Max Burst Size, and - // EP State values shall be cleared to '0' - input_context.endpoint_context_0.tr_deque_pointer = TRDequeuePointer::new() - .with_pointer(device_slot.endpoint_0_transfer_ring.physical_base_address() as u64) - .with_dequeue_cycle_state(true); - - input_context.endpoint_context_0.fields = EndpointContextFields::new() - .with_endpoint_type(EndpointType::Control) - .with_max_primary_streams(0) - .with_max_burst_size(0) - .with_endpoint_state(EndpointState::Disabled); - - self.device_context_base_array.0[slot_id as usize] = - device_slot.device_context.physical_address() as u64; - self.slots[slot_id as usize - 1] = Some(device_slot); - - input_context + pub fn physical_addr(&self) -> usize { + self.physical_addr } } diff --git a/rust/sys/voyageurs/src/xhci/driver.rs b/rust/sys/voyageurs/src/xhci/driver.rs index 746ee16..ed347a2 100644 --- a/rust/sys/voyageurs/src/xhci/driver.rs +++ b/rust/sys/voyageurs/src/xhci/driver.rs @@ -10,19 +10,17 @@ use mammoth::write_unaligned_volatile; use super::registers::{self}; use crate::xhci::data_structures::CommandCompletionCode; -use crate::xhci::data_structures::TrbAddressDeviceCommand; 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::DeviceSlotManager; +use crate::xhci::device_context_base_array::DeviceContextBaseArray; use crate::xhci::event_ring::EventRing; -use crate::xhci::registers::DoorbellPointer; use crate::xhci::registers::HostControllerOperationalWrapper; use crate::xhci::registers::PortStatusAndControl; -use crate::xhci::trb_ring::CommandRing; +use crate::xhci::trb_ring::TrbRing; pub struct XHCIDriver { #[allow(dead_code)] @@ -30,9 +28,9 @@ pub struct XHCIDriver { capabilities: registers::HostControllerCapabilities, operational: HostControllerOperationalWrapper, registers_region: MemoryRegion, - command_ring: Mutex, + command_ring: Mutex>, event_ring: Mutex, - device_slot_manager: Mutex, + device_context_base_array: DeviceContextBaseArray, irq_port_cap: Capability, } @@ -47,25 +45,23 @@ impl XHCIDriver { // page pointed to by the BAR0. let three_pages = 0x3000; let address = - ((pci_device.header().bars[1] as usize) << 32) | (pci_device.header().bars[0] as usize); - let registers_region = MemoryRegion::direct_physical(address as u64, three_pages).unwrap(); + ((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 max_slots = capabilities.params_1.max_device_slots(); - let doorbell_physical = address as usize + capabilities.doorbell_offset as usize; - let (command_doorbell, slot_doorbells) = - DoorbellPointer::create_command_and_slots(doorbell_physical, max_slots); + let p1 = capabilities.params_1; + mammoth::debug!("Num Port: {:?}", p1); let mut driver = Self { pci_device, capabilities, operational, registers_region, - command_ring: Mutex::new(CommandRing::new(command_doorbell)), + command_ring: Mutex::new(TrbRing::new()), event_ring: Mutex::new(EventRing::new()), - device_slot_manager: Mutex::new(DeviceSlotManager::new(max_slots, slot_doorbells)), + device_context_base_array: DeviceContextBaseArray::new(), irq_port_cap, }; driver.initialize(); @@ -75,15 +71,32 @@ impl XHCIDriver { 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 = (self.capabilities.runtime_register_space_offset - + INTERRUPTER_OFFSET_FROM_RUNTIME) as usize; + 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), - self.capabilities.params_1.max_interrupters() as usize, + 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, ) } } @@ -93,41 +106,45 @@ impl XHCIDriver { mammoth::debug!("Stopping XHCI Controller."); // Stop the host controller. - self.operational - .update_command(|cmd| cmd.with_run_stop(false)); + 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.read_status(); + 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.read_status(); + status = self.operational.status(); } #[cfg(feature = "debug")] mammoth::debug!("Resetting Controller."); - self.operational - .update_command(|cmd| cmd.with_host_controller_reset(true)); + self.operational.update(|mut o| { + o.usb_command = o.usb_command.with_host_controller_reset(false); + o + }); - let mut command: registers::UsbCommand = self.operational.read_command(); + 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.read_command(); + command = self.operational.command(); } #[cfg(feature = "debug")] mammoth::debug!("XHCI Controller Reset, waiting ready."); - let mut status = self.operational.read_status(); + 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.read_status(); + status = self.operational.status(); } #[cfg(feature = "debug")] @@ -136,29 +153,34 @@ impl XHCIDriver { #[cfg(feature = "debug")] mammoth::debug!("Setting Command Ring"); - self.operational.set_command_ring_dequeue_pointer( - self.command_ring.lock().trb_ring.physical_base_address(), - true, - ); + 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."); - self.operational - .set_device_context_base_address_array_pointer( - self.device_slot_manager - .lock() - .device_context_base_array_physical_address(), - ); - // 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. - self.operational.update_configure(|cfg| { - cfg.with_max_device_slots_enabled(self.capabilities.params_1.max_device_slots()) + 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!( - self.capabilities.params_2.max_scratchpad_buffers() == 0, + max_scratchpad_buffers == 0, "Unsupported scratchpad buffers." ); @@ -189,8 +211,13 @@ impl XHCIDriver { registers::InterrupterManagement::new().with_interrupt_enabled(true) ); - self.operational - .update_command(|cmd| cmd.with_run_stop(true).with_interrupter_enable(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."); @@ -209,6 +236,7 @@ impl XHCIDriver { 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") @@ -216,8 +244,7 @@ impl XHCIDriver { TrbType::CommandCompletionEvent => { self.command_ring .lock() - .trb_ring - .handle_completion(TrbCommandCompletion::from_trb(trb)); + .handle_commpletion(TrbCommandCompletion::from_trb(trb)); } TrbType::PortStatusChangeEvent => { let trb = TrbPortStatusChangeEvent::from_trb(trb); @@ -231,14 +258,13 @@ impl XHCIDriver { } } } - event_ring.update_dequeue_pointer(&mut self.interrupters()[0]); } } async fn send_command(&self, trb: impl TypedTrb) -> TrbCommandCompletion { - // Split the future and the await so the lock is dropped before we await. - let future = { self.command_ring.lock().enqueue_command(trb) }; - future.await + let fut = self.command_ring.lock().enqueue_trb(trb.to_trb()); + self.doorbells()[0].ring_command(); + fut.await } pub async fn startup(&self) { @@ -283,16 +309,16 @@ impl XHCIDriver { .status_and_control; #[cfg(feature = "debug")] - mammoth::debug!("Port status change for port {}", port_id); + 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. status= {:?}", - port_status - ); + mammoth::debug!("Unknown port status event, not handling."); return; } - self.operational .update_port_status(port_index, |s| s.clear_change_bits()); @@ -301,27 +327,5 @@ impl XHCIDriver { let resp = self.send_command(TrbEnableSlotCommand::new()).await; assert!(resp.completion_code() == CommandCompletionCode::Success.into_bits()); - - let slot = resp.slot_id(); - - #[cfg(feature = "debug")] - mammoth::debug!("Creating slot data structures in slot {}.", slot); - - let input_context = self - .device_slot_manager - .lock() - .prep_slot_for_address_device(slot, port_id); - - #[cfg(feature = "debug")] - mammoth::debug!("Sending address device."); - - let resp = self - .send_command( - TrbAddressDeviceCommand::new() - .with_slot_id(slot) - .with_input_context_pointer(input_context.physical_address() as u64), - ) - .await; - assert!(resp.completion_code() == CommandCompletionCode::Success.into_bits()); } } diff --git a/rust/sys/voyageurs/src/xhci/event_ring.rs b/rust/sys/voyageurs/src/xhci/event_ring.rs index 4ab72e8..6c60bfb 100644 --- a/rust/sys/voyageurs/src/xhci/event_ring.rs +++ b/rust/sys/voyageurs/src/xhci/event_ring.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use crate::xhci::{ - data_structures::{EventRingSegmentTable, TransferRequestBlock, TrbRingSegment}, + data_structures::{EventRingSegmentTable, TransferRequestBlock, TrbRingSegment, TrbType}, registers::InterrupterRegisterSet, trb_ring::TrbPointer, }; diff --git a/rust/sys/voyageurs/src/xhci/registers/capabilities.rs b/rust/sys/voyageurs/src/xhci/registers/capabilities.rs index d9bfed2..8e50c2f 100644 --- a/rust/sys/voyageurs/src/xhci/registers/capabilities.rs +++ b/rust/sys/voyageurs/src/xhci/registers/capabilities.rs @@ -83,7 +83,7 @@ pub struct HCSParams2 { /// field indicates the high order 5 bits of the number of Scratchpad Buffers system software shall /// reserve for the xHC. Refer to section 4.20 for more information. #[bits(5, access=RO)] - max_scratchpad_buffers_hi: u16, + pub max_scratchpad_buffers_hi: u16, /// Scratchpad Restore (SPR). Default = implementation dependent. If Max Scratchpad Buffers is > /// ‘0’ then this flag indicates whether the xHC uses the Scratchpad Buffers for saving state when @@ -103,13 +103,7 @@ pub struct HCSParams2 { /// bits of the number of Scratchpad Buffers system software shall reserve for the xHC. Refer to /// section 4.20 for more information #[bits(5, access=RO)] - max_scratchpad_buffers_lo: u16, -} - -impl HCSParams2 { - pub fn max_scratchpad_buffers(&self) -> u16 { - (self.max_scratchpad_buffers_hi()) << 5 | self.max_scratchpad_buffers_lo() - } + pub max_scratchpad_buffers_lo: u16, } #[bitfield(u32)] @@ -326,7 +320,7 @@ pub struct HCCParams2 { /// Hence the grouping of parameters here. /// /// These registers are located at the addresses specified in BAR0 and BAR1 in the PCI Header. -#[repr(C)] +#[repr(C, packed)] #[derive(Copy, Clone)] pub struct HostControllerCapabilities { pub cap_length_and_version: HostControllerCapabilitiesLengthAndVersion, diff --git a/rust/sys/voyageurs/src/xhci/registers/doorbell.rs b/rust/sys/voyageurs/src/xhci/registers/doorbell.rs index fd01f8e..276a5c0 100644 --- a/rust/sys/voyageurs/src/xhci/registers/doorbell.rs +++ b/rust/sys/voyageurs/src/xhci/registers/doorbell.rs @@ -1,8 +1,4 @@ -use core::ptr::NonNull; - -use alloc::{boxed::Box, vec::Vec}; use bitfield_struct::bitfield; -use volatile::VolatileRef; /// The Doorbell Array is organized as an array of up to 256 Doorbell Registers. One /// 32-bit Doorbell Register is defined in the array for each Device Slot. System @@ -68,41 +64,18 @@ pub struct Doorbell { db_stream_id: u16, } -#[repr(transparent)] -pub struct DoorbellPointer(VolatileRef<'static, Doorbell>); - -impl DoorbellPointer { - // Construct a new doorbell pointer. - fn new(doorbell: NonNull) -> Self { - // SAFETY: - // - We allocate this memory in the create - Self(unsafe { VolatileRef::new(doorbell) }) - } - - pub fn create_command_and_slots( - doorbell_physical: usize, - max_slots: u8, - ) -> (Self, Box<[Self]>) { - // Add one for the command doorbell. - let doorbell_cnt = max_slots as usize + 1; - let doorbell_array_size = size_of::() * doorbell_cnt; - let doorbells: NonNull = NonNull::new( - mammoth::mem::map_direct_physical_and_leak(doorbell_physical, doorbell_array_size), - ) - .unwrap(); - let first = DoorbellPointer::new(doorbells); - let remainder = (1..=max_slots) - .map(|offset| { - // SAFETY: We just allocated the array of this size above. - DoorbellPointer::new(unsafe { doorbells.add(offset as usize) }) - }) - .collect(); - (first, remainder) - } - +impl Doorbell { pub fn ring(&mut self, target: u8) { - self.0 - .as_mut_ptr() - .write(Doorbell::new().with_db_target(target)) + // 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) } } diff --git a/rust/sys/voyageurs/src/xhci/registers/host_controller.rs b/rust/sys/voyageurs/src/xhci/registers/host_controller.rs index 957c9ec..e9887d8 100644 --- a/rust/sys/voyageurs/src/xhci/registers/host_controller.rs +++ b/rust/sys/voyageurs/src/xhci/registers/host_controller.rs @@ -250,68 +250,7 @@ pub struct UsbStatus { __: u32, } -impl UsbStatus { - // Returns a copy of this object that can be written without overwritting flags that are RW1C. - fn preserving_flags(&self) -> UsbStatus { - self.with_host_system_error(false) - .with_event_interrupt(false) - .with_port_change_detect(false) - .with_save_restore_error(false) - } -} - -/// Internal data structure to ensure 64 bit reads and writes. -#[bitfield(u64)] -struct CommandAndStatus { - #[bits(32)] - usb_command: UsbCommand, - #[bits(32)] - usb_status: UsbStatus, -} - -impl CommandAndStatus { - fn update_command(&self, f: impl Fn(UsbCommand) -> UsbCommand) -> CommandAndStatus { - CommandAndStatus::new() - .with_usb_command(f(self.usb_command())) - .with_usb_status(self.usb_status().preserving_flags()) - } - - fn update_status(&self, f: impl Fn(UsbStatus) -> UsbStatus) -> CommandAndStatus { - self.with_usb_status(f(self.usb_status()).preserving_flags()) - } -} - -#[bitfield(u64)] -struct PageSize { - ///Page Size – RO. Default = Implementation defined. This field defines the page size supported by - /// the xHC implementation. This xHC supports a page size of 2^(n+12) if bit n is Set. For example, if - /// bit 0 is Set, the xHC supports 4k byte page sizes. - /// - /// For a Virtual Function, this register reflects the page size selected in the System Page Size field - /// of the SR-IOV Extended Capability structure. For the Physical Function 0, this register reflects - /// the implementation dependent default xHC page size. - /// - /// Various xHC resources reference PAGESIZE to describe their minimum alignment requirements. - /// - /// The maximum possible page size is 128M. - #[bits(access=RO)] - page_size: u32, - __: u32, -} - -#[bitfield(u64)] -struct DeviceNotificationControl { - __: u32, - /// This register is used by software to enable or disable the reporting of the - /// reception of specific USB Device Notification Transaction Packets. A Notification - /// Enable (Nx, where x = 0 to 15) flag is defined for each of the 16 possible de vice - /// notification types. If a flag is set for a specific notification type, a Device - /// Notification Event shall be generated when the respective notification packet is - /// received. After reset all notifications are disabled. Refer to section 6.4.2.7 - device_notification_control: u32, -} - -#[bitfield(u64)] +#[bitfield(u32)] pub struct UsbConfigure { /// Max Device Slots Enabled (MaxSlotsEn) – RW. Default = ‘0’. This field specifies the maximum /// number of enabled Device Slots. Valid values are in the range of 0 to MaxSlots. Enabled Devices @@ -334,9 +273,6 @@ pub struct UsbConfigure { #[bits(22)] __: u32, - - // Pad to 64 bits for the purposes of reads and writes. - __: u32, } /// XHCI Spec Section 5.4 @@ -345,41 +281,44 @@ pub struct UsbConfigure { /// Operational Base shall be Dword aligned and is calculated by adding the value /// 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 -#[repr(C)] +#[repr(C, packed)] #[derive(Copy, Clone)] pub struct HostControllerOperational { - command_and_status: CommandAndStatus, - page_size: PageSize, - device_notification_control: DeviceNotificationControl, - /// Bit 0: Ring Cycle State (RW) + pub usb_command: UsbCommand, + pub usb_status: UsbStatus, + pub page_size: u32, + __: u32, + ___: u32, + pub device_notification_control: u32, + /// Bit 0: Ring Cycle State (RO) /// Bit 1: Command Stop (RW1S) /// Bit 2: Command Abort (RW1S) /// Bit 3: Command Ring Running (RO) - command_ring_control: u64, - __: u64, - ___: u64, + pub command_ring_control: u64, + ____: u64, + _____: u64, /// The Device Context Base Address Array Pointer Register identifies the base /// address of the Device Context Base Address Array. /// The memory structure referenced by this physical memory pointer is assumed to /// be physically contiguous and 64-byte aligned. - device_context_base_address_array_pointer: u64, - configure: UsbConfigure, + pub device_context_base_address_array_pointer: u64, + pub configure: UsbConfigure, } -const _: () = assert!(size_of::() == 0x40); +const _: () = assert!(size_of::() == 0x3C); pub struct HostControllerOperationalWrapper { + // TODO: Fix alignment of this type so we can do more targetted reads and writes. operational: Mutex>, // TODO: This should maybe be its own structure. ports: Vec>>, } -#[allow(dead_code)] 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, MAP_SIZE); + map_direct_physical_and_leak(mmio_address as u64, MAP_SIZE as u64); // SAFETY: // - The pointer is valid. @@ -392,11 +331,7 @@ impl HostControllerOperationalWrapper { .read() }; - assert!( - capabilities.cap_params_1.supports_64_bit(), - "We only support 64 bit XHCI" - ); - + 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. @@ -404,7 +339,6 @@ impl HostControllerOperationalWrapper { // 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 cap_length_and_version = capabilities.cap_length_and_version; let operational_ptr = unsafe { (caps_ptr as *mut u8).add(cap_length_and_version.cap_length() as usize) as *mut HostControllerOperational @@ -416,15 +350,15 @@ impl HostControllerOperationalWrapper { 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::(); - let max_ports = capabilities.params_1.max_ports(); + let params_1 = capabilities.params_1; assert!( - max_ports as usize <= max_ports_we_support, + 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..max_ports { + 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(), @@ -442,53 +376,16 @@ impl HostControllerOperationalWrapper { (operational, capabilities) } - pub fn read_command(&self) -> UsbCommand { - let locked = self.operational.lock(); - let op = locked.as_ptr(); - map_field!(op.command_and_status).read().usb_command() + pub fn update(&self, f: impl Fn(HostControllerOperational) -> HostControllerOperational) { + self.operational.lock().as_mut_ptr().update(f); } - pub fn update_command(&self, f: impl Fn(UsbCommand) -> UsbCommand) { - let mut locked = self.operational.lock(); - let op = locked.as_mut_ptr(); - map_field!(op.command_and_status).update(|c_and_s| c_and_s.update_command(f)); + pub fn status(&self) -> UsbStatus { + self.operational.lock().as_ptr().read().usb_status } - pub fn read_status(&self) -> UsbStatus { - let locked = self.operational.lock(); - let op = locked.as_ptr(); - map_field!(op.command_and_status).read().usb_status() - } - - pub fn update_status(&self, f: impl Fn(UsbStatus) -> UsbStatus) { - let mut locked = self.operational.lock(); - let op = locked.as_mut_ptr(); - map_field!(op.command_and_status).update(|c_and_s| c_and_s.update_status(f)); - } - - pub fn set_device_context_base_address_array_pointer(&self, pointer: usize) { - let mut locked = self.operational.lock(); - let op = locked.as_mut_ptr(); - map_field!(op.device_context_base_address_array_pointer).write(pointer as u64); - } - - pub fn set_command_ring_dequeue_pointer(&self, pointer: usize, cycle_bit: bool) { - // TODO: Assert that the command ring is not running here. - let mut locked = self.operational.lock(); - let op = locked.as_mut_ptr(); - map_field!(op.command_ring_control).write(pointer as u64 | cycle_bit as u64); - } - - pub fn read_configure(&self) -> UsbConfigure { - let locked = self.operational.lock(); - let op = locked.as_ptr(); - map_field!(op.configure).read() - } - - pub fn update_configure(&self, f: impl Fn(UsbConfigure) -> UsbConfigure) { - let mut locked = self.operational.lock(); - let op = locked.as_mut_ptr(); - map_field!(op.configure).update(f); + pub fn command(&self) -> UsbCommand { + self.operational.lock().as_ptr().read().usb_command } pub fn get_port(&self, index: usize) -> HostControllerUsbPort { diff --git a/rust/sys/voyageurs/src/xhci/trb_ring.rs b/rust/sys/voyageurs/src/xhci/trb_ring.rs index e5789da..64df389 100644 --- a/rust/sys/voyageurs/src/xhci/trb_ring.rs +++ b/rust/sys/voyageurs/src/xhci/trb_ring.rs @@ -3,12 +3,7 @@ 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, TrbCommandCompletion, TrbLink, TrbRingSegment, TypedTrb, - }, - registers::DoorbellPointer, -}; +use crate::xhci::data_structures::{TransferRequestBlock, TrbLink, TrbRingSegment, TypedTrb}; struct TrbFutureState { /// Physical Address for the enqueued TRB. @@ -147,7 +142,7 @@ impl TrbRing { } } - pub fn handle_completion(&mut self, completion_trb: T) { + 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(); @@ -166,25 +161,3 @@ impl TrbRing { } } } - -pub struct CommandRing { - pub trb_ring: TrbRing, - doorbell: DoorbellPointer, -} - -impl CommandRing { - pub fn new(doorbell: DoorbellPointer) -> Self { - Self { - trb_ring: TrbRing::new(), - doorbell, - } - } - - // We have to explicitly return a future her - pub fn enqueue_command(&mut self, command: impl TypedTrb) -> TrbFuture { - let fut = self.trb_ring.enqueue_trb(command.to_trb()); - // Command Doorbell is always 0. - self.doorbell.ring(0); - fut - } -}