Rust XHCI Implementation.
This commit is contained in:
parent
0b95098748
commit
a10c615b95
10 changed files with 438 additions and 30 deletions
|
|
@ -6,3 +6,9 @@ edition = "2024"
|
|||
[dependencies]
|
||||
bitfield-struct = "0.12"
|
||||
mammoth = { path = "../../lib/mammoth/" }
|
||||
pci = { path = "../../lib/pci" }
|
||||
yellowstone-yunq = { version = "0.1.0", path = "../../lib/yellowstone" }
|
||||
|
||||
[features]
|
||||
default = ["debug"]
|
||||
debug = []
|
||||
|
|
|
|||
|
|
@ -5,12 +5,28 @@ extern crate alloc;
|
|||
|
||||
mod xhci;
|
||||
|
||||
use mammoth::{debug, define_entry, zion::z_err_t};
|
||||
use mammoth::{cap::Capability, debug, define_entry, zion::z_err_t};
|
||||
use pci::PciDevice;
|
||||
use xhci::driver::XHCIDriver;
|
||||
|
||||
define_entry!();
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
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 = XHCIDriver::from_pci_device(pci_device);
|
||||
|
||||
loop {}
|
||||
|
||||
0
|
||||
}
|
||||
|
|
|
|||
22
rust/sys/voyageurs/src/xhci/device_context_base_array.rs
Normal file
22
rust/sys/voyageurs/src/xhci/device_context_base_array.rs
Normal 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
|
||||
}
|
||||
}
|
||||
227
rust/sys/voyageurs/src/xhci/driver.rs
Normal file
227
rust/sys/voyageurs/src/xhci/driver.rs
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
use core::slice;
|
||||
|
||||
use mammoth::{map_unaligned_volatile, mem::MemoryRegion};
|
||||
use mammoth::{read_unaligned_volatile, write_unaligned_volatile};
|
||||
|
||||
use super::registers::{self};
|
||||
use crate::xhci::device_context_base_array::DeviceContextBaseArray;
|
||||
use crate::xhci::event_ring::EventRing;
|
||||
use crate::xhci::registers::HCSParams1;
|
||||
use crate::xhci::trb_ring::{InputTrbRing, OutputTrbRing};
|
||||
|
||||
pub struct XHCIDriver {
|
||||
#[allow(dead_code)]
|
||||
pci_device: pci::PciDevice,
|
||||
registers_region: MemoryRegion,
|
||||
command_ring: OutputTrbRing,
|
||||
event_ring: EventRing,
|
||||
device_context_base_array: DeviceContextBaseArray,
|
||||
}
|
||||
|
||||
impl XHCIDriver {
|
||||
pub fn from_pci_device(mut pci_device: pci::PciDevice) -> Self {
|
||||
// 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,
|
||||
// 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 port_cap = pci_device.register_msi().unwrap();
|
||||
|
||||
let driver = Self {
|
||||
pci_device,
|
||||
registers_region,
|
||||
command_ring: OutputTrbRing::new(),
|
||||
event_ring: EventRing::new(),
|
||||
device_context_base_array: DeviceContextBaseArray::new(),
|
||||
};
|
||||
driver.reset();
|
||||
driver
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> ®isters::HostControllerCapabilities {
|
||||
self.registers_region.as_ref()
|
||||
}
|
||||
|
||||
fn operational(&self) -> &mut registers::HostControllerOperational {
|
||||
let cap_length: registers::HostControllerCapabilitiesLengthAndVersion =
|
||||
read_unaligned_volatile!(self.capabilities(), cap_length_and_version);
|
||||
let offset = cap_length.cap_length();
|
||||
|
||||
// TODO: Ensure exclusive access.
|
||||
unsafe { self.registers_region.as_mut_ref_at_offset(offset as usize) }
|
||||
}
|
||||
|
||||
fn interrupters(&self) -> &[registers::InterrupterRegisterSet] {
|
||||
// See Table 5-35: Host Controller Runtime Registers
|
||||
const INTERRUPTER_OFFSET_FROM_RUNTIME: u32 = 0x20;
|
||||
let runtime = read_unaligned_volatile!(self.capabilities(), runtime_register_space_offset);
|
||||
|
||||
let interrupter_offset: usize = (runtime + INTERRUPTER_OFFSET_FROM_RUNTIME) as usize;
|
||||
|
||||
let params1: registers::HCSParams1 =
|
||||
read_unaligned_volatile!(self.capabilities(), params_1);
|
||||
|
||||
// SAFETY: The XHCI spec says so?
|
||||
unsafe {
|
||||
slice::from_raw_parts(
|
||||
self.registers_region
|
||||
.raw_ptr_at_offset(interrupter_offset as u64),
|
||||
params1.max_interrupters() as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn doorbells(&self) -> &[registers::Doorbell] {
|
||||
let doorbell_offset = read_unaligned_volatile!(self.capabilities(), doorbell_offset);
|
||||
|
||||
let params1: registers::HCSParams1 =
|
||||
read_unaligned_volatile!(self.capabilities(), params_1);
|
||||
|
||||
// SAFETY: The XHCI spec says so?
|
||||
unsafe {
|
||||
slice::from_raw_parts(
|
||||
self.registers_region
|
||||
.raw_ptr_at_offset(doorbell_offset as u64),
|
||||
params1.max_device_slots() as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&self) {
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Stopping XHCI Controller.");
|
||||
|
||||
// Stop the host controller.
|
||||
// TODO: Make this volatile.
|
||||
self.operational().usb_command = self.operational().usb_command.with_run_stop(false);
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Waiting for controller to halt.");
|
||||
|
||||
// Sleep until the controller is halted.
|
||||
let mut status: registers::UsbStatus =
|
||||
read_unaligned_volatile!(self.operational(), usb_status);
|
||||
while !status.host_controller_halted() {
|
||||
// TODO: Sleep for how long?
|
||||
mammoth::syscall::thread_sleep(50).unwrap();
|
||||
status = read_unaligned_volatile!(self.operational(), usb_status);
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Resetting Controller.");
|
||||
|
||||
map_unaligned_volatile!(
|
||||
self.operational(),
|
||||
usb_command,
|
||||
|c: registers::UsbCommand| c.with_host_controller_reset(true)
|
||||
);
|
||||
|
||||
let mut command: registers::UsbCommand =
|
||||
read_unaligned_volatile!(self.operational(), usb_command);
|
||||
while command.host_controller_reset() {
|
||||
// TODO: Sleep for how long?
|
||||
mammoth::syscall::thread_sleep(50).unwrap();
|
||||
command = read_unaligned_volatile!(self.operational(), usb_command);
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("XHCI Controller Reset, waiting ready.");
|
||||
|
||||
status = read_unaligned_volatile!(self.operational(), usb_status);
|
||||
while status.controller_not_ready() {
|
||||
// TODO: Sleep for how long?
|
||||
mammoth::syscall::thread_sleep(50).unwrap();
|
||||
status = read_unaligned_volatile!(self.operational(), usb_status);
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("XHCI Controller Ready.");
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Setting Command Ring");
|
||||
|
||||
// TODO: We should reset the command ring here.
|
||||
write_unaligned_volatile!(
|
||||
self.operational(),
|
||||
command_ring_control,
|
||||
self.command_ring.physical_addr()
|
||||
);
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Setting DCBA.");
|
||||
|
||||
write_unaligned_volatile!(
|
||||
self.operational(),
|
||||
device_context_base_address_array_pointer,
|
||||
self.device_context_base_array.physical_addr()
|
||||
);
|
||||
|
||||
// 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.
|
||||
let params1: registers::HCSParams1 =
|
||||
read_unaligned_volatile!(self.capabilities(), params_1);
|
||||
map_unaligned_volatile!(
|
||||
self.operational(),
|
||||
configure,
|
||||
|c: registers::UsbConfigure| c
|
||||
.with_max_device_slots_enabled(params1.max_device_slots())
|
||||
);
|
||||
|
||||
let params2: registers::HCSParams2 =
|
||||
read_unaligned_volatile!(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 = &self.interrupters()[0];
|
||||
write_unaligned_volatile!(
|
||||
interrupter0,
|
||||
event_ring_segment_table_base_address,
|
||||
self.event_ring.segment_table().physical_address()
|
||||
);
|
||||
write_unaligned_volatile!(
|
||||
interrupter0,
|
||||
event_ring_segement_table_size,
|
||||
self.event_ring.segment_table().len()
|
||||
);
|
||||
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)
|
||||
);
|
||||
|
||||
map_unaligned_volatile!(
|
||||
self.operational(),
|
||||
usb_command,
|
||||
|c: registers::UsbCommand| c.with_run_stop(true)
|
||||
);
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Enabled interrupts and controller.");
|
||||
}
|
||||
}
|
||||
25
rust/sys/voyageurs/src/xhci/event_ring.rs
Normal file
25
rust/sys/voyageurs/src/xhci/event_ring.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use alloc::vec::Vec;
|
||||
|
||||
use crate::xhci::data_structures::{EventRingSegmentTable, TrbRing};
|
||||
|
||||
pub struct EventRing {
|
||||
segment_table: EventRingSegmentTable,
|
||||
segments: Vec<TrbRing>,
|
||||
}
|
||||
|
||||
impl EventRing {
|
||||
pub fn new() -> Self {
|
||||
let mut event_ring = Self {
|
||||
segment_table: EventRingSegmentTable::new(1),
|
||||
segments: [TrbRing::new(100)].into(),
|
||||
};
|
||||
|
||||
event_ring.segment_table[0].from_trb_fing(&event_ring.segments[0]);
|
||||
|
||||
event_ring
|
||||
}
|
||||
|
||||
pub fn segment_table(&self) -> &EventRingSegmentTable {
|
||||
&self.segment_table
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +1,6 @@
|
|||
pub mod data_structures;
|
||||
pub mod registers;
|
||||
mod data_structures;
|
||||
mod device_context_base_array;
|
||||
pub mod driver;
|
||||
mod event_ring;
|
||||
mod registers;
|
||||
mod trb_ring;
|
||||
|
|
|
|||
49
rust/sys/voyageurs/src/xhci/trb_ring.rs
Normal file
49
rust/sys/voyageurs/src/xhci/trb_ring.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use mammoth::mem::MemoryRegion;
|
||||
|
||||
struct TrbRing {
|
||||
region: MemoryRegion,
|
||||
physical_addr: u64,
|
||||
}
|
||||
|
||||
impl TrbRing {
|
||||
fn new() -> Self {
|
||||
let (region, physical_addr) = MemoryRegion::contiguous_physical(0x1000).unwrap();
|
||||
region.zero_region();
|
||||
Self {
|
||||
region,
|
||||
physical_addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputTrbRing {
|
||||
ring: TrbRing,
|
||||
}
|
||||
|
||||
impl OutputTrbRing {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ring: TrbRing::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn physical_addr(&self) -> u64 {
|
||||
self.ring.physical_addr
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputTrbRing {
|
||||
ring: TrbRing,
|
||||
}
|
||||
|
||||
impl InputTrbRing {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ring: TrbRing::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn physical_addr(&self) -> u64 {
|
||||
self.ring.physical_addr
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue