Compare commits

...

6 commits

39 changed files with 2504 additions and 86 deletions

64
' Normal file
View file

@ -0,0 +1,64 @@
use core::{
ops::{Deref, Index, IndexMut},
slice::SliceIndex,
};
use alloc::{boxed::Box, vec};
use bitfield_struct::bitfield;
use mammoth::physical_box::PhysicalBox;
#[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,
}
#[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 {
type Output = TransferRequestBlock;
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]
}
}

3
rust-toolchain.toml Normal file
View file

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly-2025-10-02"
components = ["rustfmt", "rust-analyzer"]

63
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"
@ -19,6 +13,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "bitfield-struct"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8769c4854c5ada2852ddf6fd09d15cf43d4c2aaeccb4de6432f5402f08a6003b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "convert_case" name = "convert_case"
version = "0.6.0" version = "0.6.0"
@ -32,7 +37,7 @@ dependencies = [
name = "denali" name = "denali"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitfield-struct", "bitfield-struct 0.8.0",
"mammoth", "mammoth",
"pci", "pci",
"yellowstone-yunq", "yellowstone-yunq",
@ -69,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",
] ]
@ -88,15 +92,15 @@ dependencies = [
name = "pci" name = "pci"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitfield-struct", "bitfield-struct 0.8.0",
"mammoth", "mammoth",
] ]
[[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",
@ -104,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",
] ]
@ -137,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",
@ -161,21 +165,21 @@ version = "0.1.0"
dependencies = [ dependencies = [
"mammoth", "mammoth",
"victoriafalls", "victoriafalls",
"voyageurs", "voyageurs_client",
"yellowstone-yunq", "yellowstone-yunq",
] ]
[[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"
@ -192,6 +196,16 @@ dependencies = [
[[package]] [[package]]
name = "voyageurs" name = "voyageurs"
version = "0.1.0" version = "0.1.0"
dependencies = [
"bitfield-struct 0.12.1",
"mammoth",
"pci",
"yellowstone-yunq",
]
[[package]]
name = "voyageurs_client"
version = "0.1.0"
dependencies = [ dependencies = [
"mammoth", "mammoth",
"yellowstone-yunq", "yellowstone-yunq",
@ -206,7 +220,6 @@ dependencies = [
"denali_client", "denali_client",
"mammoth", "mammoth",
"victoriafalls", "victoriafalls",
"voyageurs",
"yellowstone-yunq", "yellowstone-yunq",
"yunq", "yunq",
] ]

View file

@ -1,16 +1,19 @@
[workspace] [workspace]
members = [ members = [
"lib/client/denali_client", "lib/fs/ext2", "lib/client/denali_client",
"lib/mammoth", "lib/pci", "lib/client/voyageurs_client",
"lib/voyageurs", "lib/fs/ext2",
"lib/yellowstone", "lib/mammoth",
"lib/yunq", "lib/pci",
"lib/yunq-test", "lib/yellowstone",
"sys/denali", "lib/yunq",
"sys/teton", "lib/yunq-test",
"sys/victoriafalls", "sys/denali",
"sys/yellowstone", "sys/teton",
"usr/testbed", "sys/victoriafalls",
"sys/voyageurs",
"sys/yellowstone",
"usr/testbed",
] ]
resolver = "2" resolver = "2"

View file

@ -0,0 +1,12 @@
[package]
name = "voyageurs_client"
version = "0.1.0"
edition = "2021"
[dependencies]
mammoth = { path = "../../mammoth" }
yellowstone-yunq = { path = "../../yellowstone" }
yunq = { path = "../../yunq" }
[build-dependencies]
yunqc = { path = "../../../../yunq/rust" }

View file

@ -1,7 +1,7 @@
use std::fs; use std::fs;
fn main() { fn main() {
let input_file = "../../../sys/voyageurs/lib/voyageurs/voyageurs.yunq"; let input_file = "../../../../sys/voyageurs/lib/voyageurs/voyageurs.yunq";
println!("cargo::rerun-if-changed={input_file}"); println!("cargo::rerun-if-changed={input_file}");

View file

@ -239,9 +239,7 @@ fn load_program_segment(
let mem_object = crate::mem::MemoryRegion::new(mem_size)?; let mem_object = crate::mem::MemoryRegion::new(mem_size)?;
for i in mem_object.mut_slice() { mem_object.zero_region();
*i = 0;
}
let file_start = prog_header.offset as usize; let file_start = prog_header.offset as usize;
let file_end = file_start + prog_header.file_size as usize; let file_end = file_start + prog_header.file_size as usize;

View file

@ -13,6 +13,7 @@ mod cap_syscall;
pub mod elf; pub mod elf;
pub mod init; pub mod init;
pub mod mem; pub mod mem;
pub mod physical_box;
pub mod port; pub mod port;
pub mod sync; pub mod sync;
pub mod syscall; pub mod syscall;

View file

@ -3,7 +3,8 @@ use crate::syscall;
use crate::zion::ZError; use crate::zion::ZError;
use alloc::slice; use alloc::slice;
use core::fmt::Debug; use core::fmt::Debug;
use core::ptr::{addr_of, addr_of_mut}; use core::ops::Deref;
use core::ptr::{addr_of, addr_of_mut, read_volatile, write_volatile, NonNull};
#[cfg(feature = "hosted")] #[cfg(feature = "hosted")]
use linked_list_allocator::LockedHeap; use linked_list_allocator::LockedHeap;
@ -29,6 +30,7 @@ pub fn init_heap() {
pub struct MemoryRegion { pub struct MemoryRegion {
mem_cap: Capability, mem_cap: Capability,
virt_addr: u64, virt_addr: u64,
// TODO: This should be a usize probably.
size: u64, size: u64,
} }
@ -94,13 +96,50 @@ impl MemoryRegion {
} }
} }
pub fn zero_region(&self) {
for i in self.mut_slice() {
*i = 0;
}
}
pub fn raw_ptr_at_offset<T>(&self, offset: u64) -> *const T { pub fn raw_ptr_at_offset<T>(&self, offset: u64) -> *const T {
// TODO: Come up with a better safety check here.
// We can't use the size of T because it might not be sized.
assert!(offset + size_of::<T>() as u64 <= self.size); assert!(offset + size_of::<T>() as u64 <= self.size);
(self.virt_addr + offset) as *const T (self.virt_addr + offset) as *const T
} }
pub fn mut_ptr_at_offset<T>(&self, offset: usize) -> *mut T {
assert!(offset + size_of::<T>() <= self.size as usize);
(self.virt_addr as usize + offset) as *mut T
}
/// Creates a reference from a given offset.
///
/// SAFETY: Caller must ensure that the memory pointed to by this
/// pointer must not get mutated while the reference exists.
pub unsafe fn as_ref_at_offset<T>(&self, offset: usize) -> &T {
let ptr: *const T = self.raw_ptr_at_offset(offset as u64);
assert!(ptr.is_aligned(), "");
// SAFETY:
// - We checked alignment.
// - self.vaddr + offset can't be null.
// - It is dereferenceable because it is entirely within this memory region.
&*self.raw_ptr_at_offset::<T>(offset as u64)
}
/// Creates a reference from a given offset.
///
/// SAFETY: Caller must ensure that this is the only reference to the memory pointed
/// to by this pointer.
pub unsafe fn as_mut_ref_at_offset<T>(&self, offset: usize) -> &mut T {
let ptr: *const T = self.raw_ptr_at_offset(offset as u64);
assert!(ptr.is_aligned(), "");
// SAFETY:
// - We checked alignment.
// - self.vaddr + offset can't be null.
// - It is dereferenceable because it is entirely within this memory region.
&mut *self.mut_ptr_at_offset::<T>(offset)
}
pub fn cap(&self) -> &Capability { pub fn cap(&self) -> &Capability {
&self.mem_cap &self.mem_cap
} }
@ -137,28 +176,22 @@ impl Drop for MemoryRegion {
} }
} }
pub struct Volatile<T> { #[repr(transparent)]
/// TODO: This should maybe be MaybeUninit. pub struct Volatile<T: Copy>(T);
data: T,
}
impl<T> Volatile<T> { impl<T: Copy> Volatile<T> {
pub fn read(&self) -> T pub fn read(&self) -> T {
where unsafe { read_volatile(addr_of!(self.0)) }
T: Copy,
{
unsafe { addr_of!(self.data).cast::<T>().read_volatile() }
} }
pub fn write(&mut self, data: T) { pub fn write(&mut self, data: T) {
unsafe { unsafe {
addr_of_mut!(self.data).cast::<T>().write_volatile(data); write_volatile(addr_of_mut!(self.0), data);
} }
} }
pub fn update<F>(&mut self, func: F) pub fn update<F>(&mut self, func: F)
where where
T: Copy,
F: Fn(&mut T), F: Fn(&mut T),
{ {
let mut data = self.read(); let mut data = self.read();
@ -176,6 +209,37 @@ where
} }
} }
#[macro_export]
macro_rules! read_unaligned_volatile {
($struct_ptr:expr, $field:ident) => {
unsafe {
let field_ptr = core::ptr::addr_of!((*$struct_ptr).$field);
core::ptr::read_volatile(field_ptr as *const _)
}
};
}
#[macro_export]
macro_rules! write_unaligned_volatile {
($struct_ptr:expr, $field:ident, $value:expr) => {
unsafe {
let field_ptr = core::ptr::addr_of!((*$struct_ptr).$field);
core::ptr::write_volatile(field_ptr as *mut _, $value);
}
};
}
#[macro_export]
macro_rules! map_unaligned_volatile {
($struct_ptr:expr, $field:ident, $func:expr) => {
unsafe {
let field_ptr = core::ptr::addr_of!((*$struct_ptr).$field);
let value = core::ptr::read_volatile(field_ptr as *const _);
core::ptr::write_volatile(field_ptr as *mut _, ($func)(value));
}
};
}
pub fn map_cap_and_leak(mem_cap: Capability) -> u64 { pub fn map_cap_and_leak(mem_cap: Capability) -> u64 {
let vaddr = syscall::address_space_map(&mem_cap).unwrap(); let vaddr = syscall::address_space_map(&mem_cap).unwrap();
mem_cap.release(); mem_cap.release();

View file

@ -0,0 +1,131 @@
use core::{
marker::PhantomData,
ops::{Deref, DerefMut, Index, IndexMut},
ptr::NonNull,
};
use alloc::{slice, vec::Vec};
use crate::mem::MemoryRegion;
pub struct PhysicalBox<T: ?Sized> {
data: NonNull<T>,
region: MemoryRegion,
physical_address: usize,
_marker: PhantomData<T>,
}
impl<T: ?Sized> PhysicalBox<T> {
pub fn physical_address(&self) -> usize {
self.physical_address
}
}
impl<T: ?Sized> Deref for PhysicalBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
// SAFETY:
// - Alignment: This is page aligned.
// - Dereferenceable: Guaranteed in same allocation.
// - Aliasing: The borrow rules ensure this
unsafe { self.data.as_ref() }
}
}
impl<T: ?Sized> DerefMut for PhysicalBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY:
// - Alignment: This is page aligned.
// - Dereferenceable: Guaranteed in same allocation.
// - Aliasing: The borrow rules ensure this
unsafe { self.data.as_mut() }
}
}
impl<T> PhysicalBox<[T]> {
pub fn default_with_count(default: T, len: usize) -> Self
where
T: Clone,
{
let layout = core::alloc::Layout::array::<T>(len).expect("Layout overflow");
// 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");
let ptr: *mut T = memory_region.mut_ptr_at_offset(0);
for i in 0..len {
unsafe {
ptr.add(i).write(default.clone());
}
}
let slice_ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);
Self {
// UNWRAP: We know this isn't null.
data: NonNull::new(slice_ptr).unwrap(),
region: memory_region,
physical_address: paddr as usize,
_marker: PhantomData,
}
}
pub fn from_vec(mut vec: Vec<T>) -> Self {
let len = vec.len();
let layout = core::alloc::Layout::array::<T>(len).expect("Layout overflow");
// TODO: Implement a function like alloc that takes a layout.
let (memory_region, paddr) =
MemoryRegion::contiguous_physical(layout.size() as u64).expect("Failed to allocate");
let ptr: *mut T = memory_region.mut_ptr_at_offset(0);
for (i, item) in vec.into_iter().enumerate() {
unsafe {
ptr.add(i).write(item);
}
}
let slice_ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);
Self {
// UNWRAP: We know this isn't null.
data: NonNull::new(slice_ptr).unwrap(),
region: memory_region,
physical_address: paddr as usize,
_marker: PhantomData,
}
}
pub fn len(&self) -> usize {
(**self).len()
}
}
impl<I, T> Index<I> for PhysicalBox<[T]>
where
I: slice::SliceIndex<[T]>,
{
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&(**self)[index]
}
}
impl<I, T> IndexMut<I> for PhysicalBox<[T]>
where
I: slice::SliceIndex<[T]>,
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
&mut (**self)[index]
}
}
impl<T: ?Sized> Drop for PhysicalBox<T> {
fn drop(&mut self) {
// SAFETY:
// - We own this data.
unsafe { core::ptr::drop_in_place(self.data.as_ptr()) }
}
}

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,13 +0,0 @@
[package]
name = "voyageurs"
version = "0.1.0"
edition = "2021"
[dependencies]
mammoth = { path = "../mammoth" }
yellowstone-yunq = { path = "../yellowstone" }
yunq = {path = "../yunq"}
[build-dependencies]
yunqc = {path = "../../../yunq/rust"}

View file

@ -1,2 +0,0 @@
[toolchain]
channel = "nightly"

View file

@ -6,5 +6,5 @@ edition = "2021"
[dependencies] [dependencies]
mammoth = { path = "../../lib/mammoth" } mammoth = { path = "../../lib/mammoth" }
victoriafalls = { path = "../victoriafalls" } victoriafalls = { path = "../victoriafalls" }
voyageurs = { path = "../../lib/voyageurs" } voyageurs_client = { path = "../../lib/client/voyageurs_client/" }
yellowstone-yunq = { path = "../../lib/yellowstone" } yellowstone-yunq = { path = "../../lib/yellowstone" }

View file

@ -9,7 +9,7 @@ mod psf;
mod terminal; mod terminal;
use mammoth::{debug, define_entry, zion::z_err_t}; use mammoth::{debug, define_entry, zion::z_err_t};
use voyageurs::listener; use voyageurs_client::listener;
define_entry!(); define_entry!();

View file

@ -6,7 +6,7 @@ use alloc::{
string::{String, ToString}, string::{String, ToString},
}; };
use victoriafalls::dir; use victoriafalls::dir;
use voyageurs::listener::KeyboardHandler; use voyageurs_client::listener::KeyboardHandler;
pub struct Terminal { pub struct Terminal {
console: Console, console: Console,

View file

@ -0,0 +1,14 @@
[package]
name = "voyageurs"
version = "0.1.0"
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 = []

View file

@ -0,0 +1,32 @@
#![no_std]
#![no_main]
extern crate alloc;
mod xhci;
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 {
#[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
}

View file

@ -0,0 +1,228 @@
use bitfield_struct::bitfield;
#[derive(Debug)]
#[repr(u8)]
pub enum EndpointState {
/// The endpoint is not operationa.
Disabled = 0,
/// The endpoint is operational, either waiting for a doorbell ring or processing TDs.
Running = 1,
/// The endpoint is halted due to a Halt condition detected on the USB. SW shall issue
/// Reset Endpoint Command to recover from the Halt condition and transition to the Stopped
/// state. SW may manipulate the Transfer Ring while in this state
Halted = 2,
/// The endpoint is not running due to a Stop Endpoint Command or recovering
/// from a Halt condition. SW may manipulate the Transfer Ring while in this state.
Stopped = 3,
/// The endpoint is not running due to a TRB Error. SW may manipulate the Transfer
/// Ring while in this state.
Error = 4,
Unknown = 5,
}
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 {
NotValid = 0,
IsochOut = 1,
BulkOut = 2,
InterruptOut = 3,
Control = 4,
IsochIn = 5,
BulkIn = 6,
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
/// endpoint.
///
/// As Output, a Running to Halted transition is forced by the xHC if a STALL condition is detected
/// on the endpoint. A Running to Error transition is forced by the xHC if a TRB Error condition is
/// detected.
///
/// As Input, this field is initialized to 0 by software.
///
/// Refer to section 4.8.3 for more information on Endpoint State.
#[bits(3)]
pub endpoint_state: EndpointState,
#[bits(5)]
__: u8,
/// Mult. If LEC = 0, then this field indicates the maximum number of bursts within an Interval that
/// this endpoint supports. Mult is a “zero-based” value, where 0 to 3 represents 1 to 4 bursts,
/// respectively. The valid range of values is 0 to 2.111 This field shall be 0 for all endpoint types
/// except for SS Isochronous.
///
/// If LEC = 1, then this field shall be RsvdZ and Mult is calculated as:
/// ROUNDUP(Max ESIT Payload / Max Packet Size / (Max Burst Size + 1)) - 1
#[bits(2)]
pub mult: u8,
/// Max Primary Streams (MaxPStreams). This field identifies the maximum number of Primary
/// Stream IDs this endpoint supports. Valid values are defined below. If the value of this field is 0,
/// then the TR Dequeue Pointer field shall point to a Transfer Ring. If this field is > '0' then the TR
/// Dequeue Pointer field shall point to a Primary Stream Context Array. Refer to section 4.12 for
/// more information.
///
/// A value of 0 indicates that Streams are not supported by this endpoint and the Endpoint
/// Context TR Dequeue Pointer field references a Transfer Ring.
///
/// A value of 1 to 15 indicates that the Primary Stream ID Width is MaxPstreams+1 and the
/// Primary Stream Array contains 2MaxPStreams+1 entries.
///
/// For SS Bulk endpoints, the range of valid values for this field is defined by the MaxPSASize field
/// in the HCCPARAMS1 register (refer to Table 5-13).
///
/// This field shall be '0' for all SS Control, Isoch, and Interrupt endpoints, and for all non-SS
/// endpoints.
#[bits(5)]
pub max_primary_streams: u8,
/// Linear Stream Array (LSA). This field identifies how a Stream ID shall be interpreted.
/// Setting this bit to a value of 1 shall disable Secondary Stream Arrays and a Stream ID shall be
/// interpreted as a linear index into the Primary Stream Array, where valid values for MaxPStreams
/// are 1 to 15.
///
/// A value of 0 shall enable Secondary Stream Arrays, where the low order (MaxPStreams+1) bits
/// of a Stream ID shall be interpreted as a linear index into the Primary Stream Array, where valid
/// values for MaxPStreams are 1 to 7. And the high order bits of a Stream ID shall be interpreted
/// as a linear index into the Secondary Stream Array.
///
/// If MaxPStreams = 0, this field RsvdZ.
///
/// Refer to section 4.12.2 for more information
pub linear_stream_array: bool,
/// Interval. The period between consecutive requests to a USB endpoint to send or receive data.
/// Expressed in 125 μs. increments. The period is calculated as 125 μs. * 2Interval; e.g., an Interval
/// value of 0 means a period of 125 μs. (20 = 1 * 125 μs.), a value of 1 means a period of 250 μs. (21
/// = 2 * 125 μs.), a value of 4 means a period of 2 ms. (24 = 16 * 125 μs.), etc. Refer to Table 6-12
/// for legal Interval field values. See further discussion of this field below. Refer to section 6.2.3.6
/// for more information.
pub interval: u8,
/// Max Endpoint Service Time Interval Payload High (Max ESIT Payload Hi). If LEC = '1', then this
/// field indicates the high order 8 bits of the Max ESIT Payload value. If LEC = '0', then this field
/// shall be RsvdZ. Refer to section 6.2.3.8 for more information.
pub max_esit_payload_hi: u8,
__: bool,
/// Error Count (CErr)112. This field defines a 2-bit down count, which identifies the number of
/// consecutive USB Bus Errors allowed while executing a TD. If this field is programmed with a
/// non-zero value when the Endpoint Context is initialized, the xHC loads this value into an internal
/// Bus Error Counter before executing a USB transaction and decrements it if the transaction fails.
/// If the Bus Error Counter counts from 1 to 0, the xHC ceases execution of the TRB, sets the
/// endpoint to the Halted state, and generates a USB Transaction Error Event for the TRB that
/// caused the internal Bus Error Counter to decrement to 0. If system software programs this field
/// to 0, the xHC shall not count errors for TRBs on the Endpoints Transfer Ring and there shall be
/// no limit on the number of TRB retries. Refer to section 4.10.2.7 for more information on the
/// operation of the Bus Error Counter.
///
/// Note: CErr does not apply to Isoch endpoints and shall be set to 0 if EP Type = Isoch Out ('1') or
/// Isoch In ('5').
#[bits(2)]
pub error_count: u8,
/// Endpoint Type (EP Type). This field identifies whether an Endpoint Context is Valid, and if so,
/// what type of endpoint the context defines.
#[bits(3)]
pub endpoint_type: EndpointType,
__: bool,
/// Host Initiate Disable (HID). This field affects Stream enabled endpoints, allowing the Host
/// Initiated Stream selection feature to be disabled for the endpoint. Setting this bit to a value of
/// 1 shall disable the Host Initiated Stream selection feature. A value of 0 will enable normal
/// Stream operation. Refer to section 4.12.1.1 for more information.
pub host_initiate_disable: bool,
/// Max Burst Size. This field indicates to the xHC the maximum number of consecutive USB
/// transactions that should be executed per scheduling opportunity. This is a “zero-based” value,
/// where 0 to 15 represents burst sizes of 1 to 16, respectively. Refer to section 6.2.3.4 for more
/// information.
pub max_burst_size: u8,
/// Max Packet Size. This field indicates the maximum packet size in bytes that this endpoint is
/// capable of sending or receiving when configured. Refer to section 6.2.3.5 for more information
pub max_packet_size: u16,
}
#[bitfield(u64)]
pub struct TRDequeuePointer {
/// Dequeue Cycle State (DCS). This bit identifies the value of the xHC Consumer Cycle State (CCS)
/// flag for the TRB referenced by the TR Dequeue Pointer. Refer to section 4.9.2 for more
/// information. This field shall be 0 if MaxPStreams > 0
pub dequeue_cycle_state: bool,
#[bits(3)]
__: u8,
/// TR Dequeue Pointer. As Input, this field represents the high order bits of the 64-bit base
/// address of a Transfer Ring or a Stream Context Array associated with this endpoint. If
/// MaxPStreams = '0' then this field shall point to a Transfer Ring. If MaxPStreams > '0' then this
/// field shall point to a Stream Context Array.
///
/// As Output, if MaxPStreams = 0 this field shall be used by the xHC to store the value of the
/// Dequeue Pointer when the endpoint enters the Halted or Stopped states, and the value of the
/// this field shall be undefined when the endpoint is not in the Halted or Stopped states. if
/// MaxPStreams > 0 then this field shall point to a Stream Context Array.
/// The memory structure referenced by this physical memory pointer shall be aligned to a 16-byte
/// boundary.
#[bits(60)]
tr_deque_pointer: u64,
}
impl TRDequeuePointer {
pub fn pointer(self) -> u64 {
self.tr_deque_pointer() << 4
}
pub fn set_pointer(self, tr_deque_pointer: u64) -> TRDequeuePointer {
self.with_tr_deque_pointer(tr_deque_pointer >> 4)
}
}
#[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_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; 3],
}
const _: () = assert!(size_of::<EndpointContext>() == 0x20);

View file

@ -0,0 +1,62 @@
use core::ops::{Index, IndexMut};
use alloc::{boxed::Box, vec};
use mammoth::physical_box::PhysicalBox;
use crate::xhci::data_structures::TrbRing;
#[repr(align(64))]
#[derive(Default, Clone)]
pub struct EventRingSegmentTableEntry {
/// Ring Segment Base Address Hi and Lo. These fields represent the high order bits of the 64-bit
/// base address of the Event Ring Segment.
/// The memory structure referenced by this physical memory pointer shall begin on a 64-byte
/// address boundary.
pub ring_segment_base_address: u64,
/// Ring Segment Size. This field defines the number of TRBs supported by the ring segment, Valid
/// values for this field are 16 to 4096, i.e. an Event Ring segment shall contain at least 16 entries
pub ring_segment_size: u64,
}
impl EventRingSegmentTableEntry {
pub fn from_trb_fing(&mut self, trb_ring: &TrbRing) {
self.ring_segment_base_address = trb_ring.physical_address() as u64;
assert!(self.ring_segment_base_address % 64 == 0);
self.ring_segment_size = trb_ring.len() as u64;
assert!(self.ring_segment_size >= 16);
assert!(self.ring_segment_size <= 4096);
}
}
pub struct EventRingSegmentTable(PhysicalBox<[EventRingSegmentTableEntry]>);
impl EventRingSegmentTable {
pub fn new(size: usize) -> Self {
Self(PhysicalBox::default_with_count(
EventRingSegmentTableEntry::default(),
size,
))
}
pub fn physical_address(&self) -> usize {
self.0.physical_address()
}
pub fn len(&self) -> usize {
self.0.len()
}
}
impl Index<usize> for EventRingSegmentTable {
type Output = EventRingSegmentTableEntry;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl IndexMut<usize> for EventRingSegmentTable {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.0[index]
}
}

View file

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

View file

@ -0,0 +1,133 @@
use bitfield_struct::bitfield;
#[bitfield(u128)]
pub struct SlotContextFields {
/// Route String. This field is used by hubs to route packets to the correct downstream port. The
/// format of the Route String is defined in section 8.9 the USB3 specification.
/// As Input, this field shall be set for all USB devices, irrespective of their speed, to indicate their
/// location in the USB topology.
#[bits(20)]
pub route_string: u32,
/// Speed. This field is deprecated in this version of the specification and shall be Reserved.
/// This field indicates the speed of the device. Refer to the PORTSC Port Speed field in Table 5-27
/// for the definition of the valid values
#[bits(4)]
pub speed: u8,
__: bool,
/// Multi-TT (MTT). This flag is set to '1' by software if this is a High-speed hub that supports
/// Multiple TTs and the Multiple TT Interface has been enabled by software, or if this is a Low-
/// /Full-speed device or Full-speed hub and connected to the xHC through a parent108 High-speed
/// hub that supports Multiple TTs and the Multiple TT Interface of the parent hub has been
/// enabled by software, or 0 if not.
pub multi_tt: bool,
/// Hub. This flag is set to '1' by software if this device is a USB hub, or '0' if it is a USB function
pub hub: bool,
/// Context Entries. This field identifies the index of the last valid Endpoint Context within this
/// Device Context structure. The value of 0 is Reserved and is not a valid entry for this field. Valid
/// entries for this field shall be in the range of 1-31. This field indicates the size of the Device
/// Context structure. For example, ((Context Entries+1) * 32 bytes) = Total bytes for this structure.
///
/// Note, Output Context Entries values are written by the xHC, and Input Context Entries values are
/// written by software.
#[bits(5)]
pub context_entries: u8,
/// Max Exit Latency. The Maximum Exit Latency is in microseconds, and indicates the worst case
/// time it takes to wake up all the links in the path to the device, given the current USB link level
/// power management settings.
///
/// Refer to section 4.23.5.2 for more information on the use of this field.
pub max_exit_latency: u16,
/// Root Hub Port Number. This field identifies the Root Hub Port Number used to access the USB
/// device. Refer to section 4.19.7 for port numbering information.
///
/// Note: Ports are numbered from 1 to MaxPorts
pub root_hub_port_number: u8,
/// Number of Ports. If this device is a hub (Hub = 1), then this field is set by software to identify
/// the number of downstream facing ports supported by the hub. Refer to the bNbrPorts field
/// description in the Hub Descriptor (Table 11-13) of the USB2 spec. If this device is not a hub (Hub
/// = 0), then this field shall be 0
pub number_of_ports: u8,
/// Parent Hub Slot ID. If this device is Low-/Full-speed and connected through a High-speed hub,
/// then this field shall contain the Slot ID of the parent High-speed hub109.
///
/// For SS and SSP bus instance, if this device is connected through a higher rank hub110 then this
/// field shall contain the Slot ID of the parent hub. For example, a Gen1 x1 connected behind a
/// Gen1 x2 hub, or Gen1 x2 device connected behind Gen2 x2 hub.
///
/// This field shall be 0 if any of the following are true:
/// Device is attached to a Root Hub port
/// Device is a High-Speed device
/// Device is the highest rank SS/SSP device supported by xHCI
pub parent_hub_slot_id: u8,
/// Parent Port Number. If this device is Low-/Full-speed and connected through a High-speed
/// hub, then this field shall contain the number of the downstream facing port of the parent High-
/// speed hub109.
/// For SS and SSP bus instance, if this device is connected through a higher rank hub110 then this
/// field shall contain the number of the downstream facing port of the parent hub. For example, a
/// Gen1 x1 connected behind a Gen1 x2 hub, or Gen1 x2 device connected behind Gen2 x2 hub.
/// This field shall be 0 if any of the following are true:
/// Device is attached to a Root Hub port
/// Device is a High-Speed device
/// Device is the highest rank SS/SSP device supported by xH
pub parent_port_number: u8,
/// TT Think Time (TTT). If this is a High-speed hub (Hub = 1 and Speed = High-Speed), then this
/// field shall be set by software to identify the time the TT of the hub requires to proceed to the
/// next full-/low-speed transaction.
/// Value Think Time
/// 0 TT requires at most 8 FS bit times of inter-transaction gap on a full-/low-speed
/// downstream bus.
/// 1 TT requires at most 16 FS bit times.
/// 2 TT requires at most 24 FS bit times.
/// 3 TT requires at most 32 FS bit times.
/// Refer to the TT Think Time sub-field of the wHubCharacteristics field description in the Hub
/// Descriptor (Table 11-13) and section 11.18.2 of the USB2 spec for more information on TT
/// Think Time. If this device is not a High-speed hub (Hub = 0 or Speed != High-speed), then this
/// field shall be 0.
#[bits(2)]
pub tt_think_time: u8,
#[bits(4)]
__: u8,
/// Interrupter Target. This field defines the index of the Interrupter that will receive Bandwidth
/// Request Events and Device Notification Events generated by this slot, or when a Ring Underrun
/// or Ring Overrun condition is reported (refer to section 4.10.3.1). Valid values are between 0 and
/// MaxIntrs-1
#[bits(10)]
pub interrupter_target: u16,
/// USB Device Address. This field identifies the address assigned to the USB device by the xHC,
/// and is set upon the successful completion of a Set Address Command. Refer to the USB2 spec
/// for a more detailed description.
///
/// As Output, this field is invalid if the Slot State = Disabled or Default.
/// As Input, software shall initialize the field to 0.
pub usb_device_address: u8,
#[bits(19)]
__: u32,
/// Slot State. This field is updated by the xHC when a Device Slot transitions from one state to
/// another.
/// Value Slot State
/// 0 Disabled/Enabled
/// 1 Default
/// 2 Addressed
/// 3 Configured
/// 31-4 Reserved
///
/// Slot States are defined in section 4.5.3.
///
/// As Output, since software initializes all fields of the Device Context data structure to 0, this field
/// shall initially indicate the Disabled state.
///
/// As Input, software shall initialize the field to 0.
/// Refer to section 4.5.3 for more information on Slot State.
#[bits(5)]
pub slot_state: u8,
}
#[repr(C)]
pub struct SlotContext {
pub fields: SlotContextFields,
__: u128,
}
const _: () = assert!(size_of::<SlotContext>() == 0x20);

View file

@ -0,0 +1,113 @@
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,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,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) -> &registers::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.");
}
}

View 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
}
}

View file

@ -0,0 +1,6 @@
mod data_structures;
mod device_context_base_array;
pub mod driver;
mod event_ring;
mod registers;
mod trb_ring;

View file

@ -0,0 +1,337 @@
use bitfield_struct::bitfield;
#[bitfield(u32)]
pub struct HostControllerCapabilitiesLengthAndVersion {
/// This register is used as an offset to add to register base to find the beginning of
/// the Operational Register Space.
#[bits(access=RO)]
pub cap_length: u8,
__: u8,
/// This is a two-byte register containing a BCD encoding of the xHCI specification
/// revision number supported by this host controller. The most significant byte of
/// this register represents a major revision and the least significant byte contains
/// the minor revision extensions. e.g. 0100h corresponds to xHCI version 1.0.0, or
/// 0110h corresponds to xHCI version 1.1.0, etc
#[bits(access=RO)]
pub hci_version: u16,
}
#[bitfield(u32)]
pub struct HCSParams1 {
/// Number of Device Slots (MaxSlots). This field specifies the maximum number of Device
/// Context Structures and Doorbell Array entries this host controller can support. Valid values are
/// in the range of 1 to 255. The value of 0 is reserved.
#[bits(access=RO)]
pub max_device_slots: u8,
/// Number of Interrupters (MaxIntrs). This field specifies the number of Interrupters implemented
/// on this host controller. Each Interrupter may be allocated to a MSI or MSI-X vector and controls
/// its generation and moderation.
///
/// The value of this field determines how many Interrupter Register Sets are addressable in the
/// Runtime Register Space (refer to section 5.5). Valid values are in the range of 1h to 400h. A 0 in
/// this field is undefined.
#[bits(11, access=RO)]
pub max_interrupters: u16,
#[bits(5)]
__: u8,
/// Number of Ports (MaxPorts). This field specifies the maximum Port Number value, i.e. the
/// highest numbered Port Register Set that are addressable in the Operational Register Space
/// (refer to Table 5-18). Valid values are in the range of 1h to FFh.
///
/// The value in this field shall reflect the maximum Port Number value assigned by an xHCI
/// Supported Protocol Capability, described in section 7.2. Software shall refer to these capabilities
/// to identify whether a specific Port Number is valid, and the protocol supported by the
/// associated Port Register Set.
#[bits(access=RO)]
pub max_ports: u8,
}
#[bitfield(u32)]
pub struct HCSParams2 {
/// Isochronous Scheduling Threshold (IST). Default = implementation dependent. The value in
/// this field indicates to system software the minimum distance (in time) that it is required to stay
/// ahead of the host controller while adding TRBs, in order to have the host controller process
/// them at the correct time. The value shall be specified in terms of number of
/// frames/microframes.
///
/// If bit [3] of IST is cleared to '0', software can add a TRB no later than IST[2:0] Microframes
/// before that TRB is scheduled to be executed.
///
/// If bit [3] of IST is set to '1', software can add a TRB no later than IST[2:0] Frames before that TRB
/// is scheduled to be executed.
///
/// Refer to Section 4.14.2 for details on how software uses this information for scheduling
/// isochronous transfers.
#[bits(4, access=RO)]
pub isochronous_scheduling_threshold: u8,
/// Event Ring Segment Table Max (ERST Max). Default = implementation dependent. Valid values
/// are 0 15. This field determines the maximum value supported the Event Ring Segment Table
/// Base Size registers (5.5.2.3.1), where:
///
/// The maximum number of Event Ring Segment Table entries = 2 ERST Max.
/// e.g. if the ERST Max = 7, then the xHC Event Ring Segment Table(s) supports up to 128 entries,
/// 15 then 32K entries, etc.
#[bits(4, access=RO)]
pub event_ring_segment_table_max: u8,
#[bits(13)]
__: u16,
/// Max Scratchpad Buffers (Max Scratchpad Bufs Hi). Default = implementation dependent. This
/// 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)]
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
/// executing Save and Restore State operations. If Max Scratchpad Buffers is = 0 then this flag
/// shall be 0. Refer to section 4.23.2 for more information.
///
/// A value of 1 indicates that the xHC requires the integrity of the Scratchpad Buffer space to be
/// maintained across power events.
///
/// A value of 0 indicates that the Scratchpad Buffer space may be freed and reallocated between
/// power events.
#[bits(access=RO)]
pub scratchpad_restore: bool,
/// Max Scratchpad Buffers (Max Scratchpad Bufs Lo). Default = implementation dependent. Valid
/// values for Max Scratchpad Buffers (Hi and Lo) are 0-1023. This field indicates the low 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)]
pub max_scratchpad_buffers_lo: u16,
}
#[bitfield(u32)]
pub struct HCSParams3 {
/// U1 Device Exit Latency. Worst case latency to transition a root hub Port Link State (PLS) from
/// U1 to U0. Applies to all root hub ports.
/// The following are permissible values:
///
/// Value Description
/// 00h Zero
/// 01h Less than 1 μs
/// 02h Less than 2 μs.
/// …
/// 0Ah Less than 10 μs
#[bits(access=RO)]
pub u1_device_exit_latency: u8,
/// U2 Device Exit Latency. Worst case latency to transition from U2 to U0. Applies to all root hub
/// ports.
/// The following are permissible values:
/// Value Description
/// 0000h Zero
/// 0001h Less than 1 μs.
/// 0002h Less than 2 μs.
/// …
/// 07FFh Less than 2047 μs.
/// 0800-FFFFh Reserved
#[bits(access=RO)]
pub u2_device_exit_latency: u8,
__: u16,
}
#[bitfield(u32)]
pub struct HCCParams1 {
/// 64-bit Addressing Capability (AC64). This flag documents the addressing range capability of
/// this implementation. The value of this flag determines whether the xHC has implemented the
/// high order 32 bits of 64 bit register and data structure pointer fields. Values for this flag have the
/// following interpretation:
///
/// Value Description
/// 0 32-bit address memory pointers implemented
/// 1 64-bit address memory pointers implemented
///
/// If 32-bit address memory pointers are implemented, the xHC shall ignore the high order 32 bits
/// of 64 bit data structure pointer fields, and system software shall ignore the high order 32 bits of
/// 64 bit xHC registers.
#[bits(access=RO)]
pub supports_64_bit: bool,
/// BW Negotiation Capability (BNC). This flag identifies whether the xHC has implemented the
/// Bandwidth Negotiation. Values for this flag have the following interpretation:
///
/// Value Description
/// 0 BW Negotiation not implemented
/// 1 BW Negotiation implemented
///
/// Refer to section 4.16 for more information on Bandwidth Negotiation.
#[bits(access=RO)]
pub bandwidth_negotiation: bool,
/// Context Size (CSZ). If this bit is set to 1, then the xHC uses 64 byte Context data structures. If
/// this bit is cleared to 0, then the xHC uses 32 byte Context data structures.
/// Note: This flag does not apply to Stream Contexts.
#[bits(access=RO)]
pub context_size: bool,
/// Port Power Control (PPC). This flag indicates whether the host controller implementation
/// includes port power control. A 1 in this bit indicates the ports have port power switches. A 0 in
/// this bit indicates the port do not have port power switches. The value of this flag affects the
/// functionality of the PP flag in each port status and control register (refer to Section 5.4.8)
#[bits(access=RO)]
pub port_power_control: bool,
/// Port Indicators (PIND). This bit indicates whether the xHC root hub ports support port indicator
/// control. When this bit is a 1, the port status and control registers include a read/writeable field
/// for controlling the state of the port indicator. Refer to Section 5.4.8 for definition of the Port
/// Indicator Control field.
#[bits(access=RO)]
pub port_indicators: bool,
/// Light HC Reset Capability (LHRC). This flag indicates whether the host controller implementation
/// supports a Light Host Controller Reset. A 1 in this bit indicates that Light Host Controller Reset is
/// supported. A 0 in this bit indicates that Light Host Controller Reset is not supported. The value
/// of this flag affects the functionality of the Light Host Controller Reset (LHCRST) flag in the
/// USBCMD register (refer to Section 5.4.1).
#[bits(access=RO)]
pub light_hc_reset_capability: bool,
/// Latency Tolerance Messaging Capability (LTC). This flag indicates whether the host controller
/// implementation supports Latency Tolerance Messaging (LTM). A 1 in this bit indicates that LTM
/// is supported. A 0 in this bit indicates that LTM is not supported. Refer to section 4.13.1 for more
/// information on LTM
#[bits(access=RO)]
pub latency_tolerance_messaging_capability: bool,
/// No Secondary SID Support (NSS). This flag indicates whether the host controller
/// implementation supports Secondary Stream IDs. A 1 in this bit indicates that Secondary Stream
/// ID decoding is not supported. A 0 in this bit indicates that Secondary Stream ID decoding is
/// supported. (refer to Sections 4.12.2 and 6.2.3)
#[bits(access=RO)]
pub no_secondary_sid_support: bool,
/// Parse All Event Data (PAE). This flag indicates whether the host controller implementation
/// Parses all Event Data TRBs while advancing to the next TD after a Short Packet, or it skips all but
/// the first Event Data TRB. A 1 in this bit indicates that all Event Data TRBs are parsed. A 0 in this
/// bit indicates that only the first Event Data TRB is parsed (refer to section 4.10.1.1).
#[bits(access=RO)]
pub parse_all_event_data: bool,
/// Stopped - Short Packet Capability (SPC). This flag indicates that the host controller
/// implementation is capable of generating a Stopped - Short Packet Completion Code. Refer to
/// section 4.6.9 for more information
#[bits(access=RO)]
pub stopped_short_packet_capability: bool,
/// Stopped EDTLA Capability (SEC). This flag indicates that the host controller implementation
/// Stream Context support a Stopped EDTLA field. Refer to sections 4.6.9, 4.12, and 6.4.4.1 for more
/// information.
/// Stopped EDTLA Capability support (i.e. SEC = '1') shall be mandatory for all xHCI 1.1 and xHCI 1.2
/// compliant xHCs.
#[bits(access=RO)]
pub stopped_edtla_capability: bool,
/// Contiguous Frame ID Capability (CFC). This flag indicates that the host controller
/// implementation is capable of matching the Frame ID of consecutive Isoch TDs. Refer to section
/// 4.11.2.5 for more information.
#[bits(access=RO)]
pub contiguous_frame_id_capability: bool,
/// Maximum Primary Stream Array Size (MaxPSASize). This fields identifies the maximum size
/// Primary Stream Array that the xHC supports. The Primary Stream Array size = 2MaxPSASize+1. Valid
/// MaxPSASize values are 0 to 15, where 0 indicates that Streams are not supported
#[bits(4, access=RO)]
pub maximum_primary_stream_array_size: u8,
/// xHCI Extended Capabilities Pointer (xECP). This field indicates the existence of a capabilities list.
/// The value of this field indicates a relative offset, in 32-bit words, from Base to the beginning of
/// the first extended capability.
///
/// For example, using the offset of Base is 1000h and the xECP value of 0068h, we can calculated
/// the following effective address of the first extended capability:
/// 1000h + (0068h << 2) -> 1000h + 01A0h -> 11A0h
#[bits(access=RO)]
pub xhci_extended_capabilities_pointer: u16,
}
#[bitfield(u32)]
pub struct HCCParams2 {
/// U3 Entry Capability (U3C) - RO. This bit indicates whether the xHC Root Hub ports support port
/// Suspend Complete notification. When this bit is '1', PLC shall be asserted on any transition of
/// PLS to the U3 State. Refer to section 4.15.1 for more information.
#[bits(access=RO)]
pub u3_entry_capability: bool,
/// Configure Endpoint Command Max Exit Latency Too Large Capability (CMC) - RO. This bit
/// indicates whether a Configure Endpoint Command is capable of generating a Max Exit Latency
/// Too Large Capability Error. When this bit is '1', a Max Exit Latency Too Large Capability Error
/// may be returned by a Configure Endpoint Command. When this bit is '0', a Max Exit Latency Too
/// Large Capability Error shall not be returned by a Configure Endpoint Command. This capability
/// is enabled by the CME flag in the USBCMD register. Refer to sections 4.23.5.2 and 5.4.1 for more
/// information.
#[bits(access=RO)]
pub configure_endpoint_command_max_exit_latency_too_large_capability: bool,
/// Force Save Context Capability (FSC) - RO. This bit indicates whether the xHC supports the
/// Force Save Context Capability. When this bit is '1', the Save State operation shall save any
/// cached Slot, Endpoint, Stream or other Context information to memory. Refer to
/// Implementation Note “FSC and Context handling by Save and Restore”, and sections 4.23.2 and
/// 5.4.1 for more information.
#[bits(access=RO)]
pub force_save_context_capability: bool,
/// Compliance Transition Capability (CTC) - RO. This bit indicates whether the xHC USB3 Root
/// Hub ports support the Compliance Transition Enabled (CTE) flag. When this bit is 1, USB3 Root
/// Hub port state machine transitions to the Compliance substate shall be explicitly enabled
/// software. When this bit is 0, USB3 Root Hub port state machine transitions to the Compliance
/// substate are automatically enabled. Refer to section 4.19.1.2.4.1 for more information.
#[bits(access=RO)]
pub compliance_transition_capability: bool,
/// Large ESIT Payload Capability (LEC) - RO. This bit indicates whether the xHC supports ESIT
/// Payloads greater than 48K bytes. When this bit is 1, ESIT Payloads greater than 48K bytes are
/// supported. When this bit is 0, ESIT Payloads greater than 48K bytes are not supported. Refer to
/// section 6.2.3.8 for more information.
#[bits(access=RO)]
pub large_esit_payload_capability: bool,
/// Configuration Information Capability (CIC) - RO. This bit indicates if the xHC supports
/// extended Configuration Information. When this bit is 1, the Configuration Value, Interface
/// Number, and Alternate Setting fields in the Input Control Context are supported. When this bit is
/// 0, the extended Input Control Context fields are not supported. Refer to section 6.2.5.1 for more
/// information.
#[bits(access=RO)]
pub configuration_information_capability: bool,
/// Extended TBC Capability78 (ETC) - RO. This bit indicates if the TBC field in an Isoch TRB
/// supports the definition of Burst Counts greater than 65535 bytes. When this bit is 1, the
/// Extended EBC capability is supported by the xHC. When this bit is 0, it is not. Refer to section
/// 4.11.2.3 for more information.
#[bits(access=RO)]
pub extended_tbc_capability: bool,
/// Extended TBC TRB Status Capability (ETC_TSC) - RO. This bit indicates if the TBC/TRBSts field
/// in an Isoch TRB indicates additional information regarding TRB in the TD. When this bit is 1, the
/// Isoch TRB TD Size/TBC field presents TBC value and TBC/TRBSts field presents the TRBSts
/// value. When this bit is 0 then the ETC/ETE values defines the TD Size/TBC field and TBC/RsvdZ
/// field. This capability shall be enabled only if LEC = 1 and ETC=1. Refer to section 4.11.2.3 for
/// more information.
#[bits(access=RO)]
pub extended_tbc_trb_status_capability: bool,
/// Get/Set Extended Property Capability (GSC) RO. This bit indicates support for the Set
/// Extended Property and Get Extended Property commands. When this bit is 1, the xHC supports
/// the Get Extended Property and Set Extended Property commands defined in section 4.6.17 and
/// section 4.6.18. When this bit is 0, the xHC does not support the Get Extended Property and Set
/// Extended Property commands and the xHC does not support any of the associated Extended
/// Capabilities.
///
/// This bit shall only be set to 1 if the xHC supports one or more extended capabilities that
/// require the Get Extended Property and Set Extended Property commands.
#[bits(access=RO)]
pub get_set_extended_property_capability: bool,
/// Virtualization Based Trusted I/O Capability (VTC) RO. This bit when set to 1, indicates that
/// the xHC supports the Virtualization based Trusted IO (VTIO) Capability. When this bit is 0, the
/// VTIO Capability is not supported. This capability is enabled by the VTIOE flag in the USBCMD
/// register.
#[bits(access=RO)]
pub virtualization_based_trusted_io_capability: bool,
#[bits(22)]
__: u32,
}
/// XHCI Spec Section 5.3
/// Note that for 64 bit implementations, the controller requires qword (32bit) accesses.
/// Hence the grouping of parameters here.
///
/// These registers are located at the addresses specified in BAR0 and BAR1 in the PCI Header.
#[repr(C, packed)]
pub struct HostControllerCapabilities {
pub cap_length_and_version: HostControllerCapabilitiesLengthAndVersion,
pub params_1: HCSParams1,
pub params_2: HCSParams2,
pub params_3: HCSParams3,
pub cap_params_1: HCCParams1,
/// This register defines the offset of the Doorbell Array base address from the Base. (RO)
pub doorbell_offset: u32,
/// This register defines the offset of the xHCI Runtime Registers from the Base.
pub runtime_register_space_offset: u32,
pub cap_params_2: HCCParams2,
}
const _: () = assert!(size_of::<HostControllerCapabilities>() == 0x20);

View file

@ -0,0 +1,65 @@
use bitfield_struct::bitfield;
/// 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
/// software utilizes the Doorbell Register to notify the xHC that it has Device Slot
/// related work for the xHC to perform.
///
/// The number of Doorbell Registers implemented by a particular instantiation of a
/// host controller is documented in the Number of Device Slots (MaxSlots) field of
/// the HCSPARAMS1 register (section 5.3.3).
///
/// These registers are pointed to by the Doorbell Offset Register (DBOFF) in the
/// xHC Capability register space. The Doorbell Array base address shall be Dword
/// 430 aligned and is calculated by adding the value in the DBOFF register (section
/// 5.3.7) to “Base” (the base address of the xHCI Capability register address space).
/// Refer to section 4.7 for more information on Doorbell registers.
#[bitfield(u32)]
pub struct Doorbell {
/// DB Target RW. Doorbell Target. This field defines the target of the doorbell reference. The
/// table below defines the xHC notification that is generated by ringing the doorbell. Note that
/// Doorbell Register 0 is dedicated to Command Ring and decodes this field differently than the
/// other Doorbell Registers.
///
/// Device Context Doorbells (1-255)
/// Value Definition
/// 0 Reserved
/// 1 Control EP 0 Enqueue Pointer Update
/// 2 EP 1 OUT Enqueue Pointer Update
/// 3 EP 1 IN Enqueue Pointer Update
/// 4 EP 2 OUT Enqueue Pointer Update
/// 5 EP 2 IN Enqueue Pointer Update
/// … ...
/// 30 EP 15 OUT Enqueue Pointer Update
/// 31 EP 15 IN Enqueue Pointer Update
/// 32:247 Reserved
/// 248:255 Vendor Defined
///
/// Host Controller Doorbell (0)
/// Value Definition
/// 0 Command Doorbell
/// 1:247 Reserved
/// 248:255 Vendor Defined
///
/// This field returns 0 when read and should be treated as “undefined” by software.
/// When the Command Doorbell is written, the DB Stream ID field shall be cleared to 0.
db_target: u8,
_reserved: u8,
/// DB Stream ID - RW. Doorbell Stream ID. If the endpoint of a Device Context Doorbell defines
/// Streams, then this field shall be used to identify which Stream of the endpoint the doorbell
/// reference is targeting. System software is responsible for ensuring that the value written to this
/// field is valid.
///
/// If the endpoint defines Streams (MaxPStreams > 0), then 0, 65535 (No Stream) and 65534
/// (Prime) are reserved Stream ID values and shall not be written to this field.
///
/// If the endpoint does not define Streams (MaxPStreams = 0) and a non-'0' value is written to this
/// field, the doorbell reference shall be ignored.
///
/// This field only applies to Device Context Doorbells and shall be cleared to 0 for Host Controller
/// Command Doorbells.
///
/// This field returns 0 when read
db_stream_id: u16,
}

View file

@ -0,0 +1,298 @@
use bitfield_struct::bitfield;
#[bitfield(u32)]
pub struct UsbCommand {
/// Run/Stop (R/S) RW. Default = 0. 1 = Run. 0 = Stop. When set to a 1, the xHC proceeds with
/// execution of the schedule. The xHC continues execution as long as this bit is set to a 1. When
/// this bit is cleared to 0, the xHC completes any current or queued commands or TDs, and any
/// USB transactions associated with them, then halts.
///
/// Refer to section 5.4.1.1 for more information on how R/S shall be managed.
///
/// The xHC shall halt within 16 ms. after software clears the Run/Stop bit if the above conditions
/// have been met.
///
/// The HCHalted (HCH) bit in the USBSTS register indicates when the xHC has finished its pending
/// pipelined transactions and has entered the stopped state. Software shall not write a 1 to this
/// flag unless the xHC is in the Halted state (i.e. HCH in the USBSTS register is 1). Doing so may
/// yield undefined results. Writing a 0 to this flag when the xHC is in the Running state (i.e. HCH =
/// 0) and any Event Rings are in the Event Ring Full state (refer to section 4.9.4) may result in lost
/// events.
///
/// When this register is exposed by a Virtual Function (VF), this bit only controls the run state of
/// the xHC instance presented by the selected VF. Refer to section 8 for more information.
pub run_stop: bool,
/// Host Controller Reset (HCRST) RW. Default = 0. This control bit is used by software to reset
/// the host controller. The effects of this bit on the xHC and the Root Hub registers are similar to a
/// Chip Hardware Reset.
///
/// When software writes a 1 to this bit, the Host Controller resets its internal pipelines, timers,
/// counters, state machines, etc. to their initial value. Any transaction currently in progress on the
/// USB is immediately terminated. A USB reset shall not be driven on USB2 downstream ports,
/// however a Hot or Warm Reset79 shall be initiated on USB3 Root Hub downstream ports.
///
/// PCI Configuration registers are not affected by this reset. All operational registers, including port
/// registers and port state machines are set to their initial values. Software shall reinitialize the
/// host controller as described in Section 4.2 in order to return the host controller to an
/// operational state.
///
/// This bit is cleared to 0 by the Host Controller when the reset process is complete. Software
/// cannot terminate the reset process early by writing a 0 to this bit and shall not write any xHC
/// Operational or Runtime registers until while HCRST is 1. Note, the completion of the xHC reset
/// process is not gated by the Root Hub port reset process.
///
/// Software shall not set this bit to 1 when the HCHalted (HCH) bit in the USBSTS register is a 0.
/// Attempting to reset an actively running host controller may result in undefined behavior.
///
/// When this register is exposed by a Virtual Function (VF), this bit only resets the xHC instance
/// presented by the selected VF. Refer to section 8 for more information
pub host_controller_reset: bool,
/// Interrupter Enable (INTE) RW. Default = 0. This bit provides system software with a means of
/// enabling or disabling the host system interrupts generated by Interrupters. When this bit is a 1,
/// then Interrupter host system interrupt generation is allowed, e.g. the xHC shall issue an interrupt
/// at the next interrupt threshold if the host system interrupt mechanism (e.g. MSI, MSI-X, etc.) is
/// enabled. The interrupt is acknowledged by a host system interrupt specific mechanism.
///
/// When this register is exposed by a Virtual Function (VF), this bit only enables the set of
/// Interrupters assigned to the selected VF. Refer to section 7.7.2 for more information.
pub interrupter_enable: bool,
/// Host System Error Enable (HSEE) RW. Default = 0. When this bit is a 1, and the HSE bit in
/// the USBSTS register is a 1, the xHC shall assert out-of-band error signaling to the host. The
/// signaling is acknowledged by software clearing the HSE bit. Refer to section 4.10.2.6 for more
/// information.
/// When this register is exposed by a Virtual Function (VF), the effect of the assertion of this bit on
/// the Physical Function (PF0) is determined by the VMM. Refer to section 8 for more information
pub host_system_error_enable: bool,
#[bits(3)]
__: u8,
/// Light Host Controller Reset (LHCRST) RO or RW. Optional normative. Default = 0. If the Light
/// HC Reset Capability (LHRC) bit in the HCCPARAMS1 register is 1, then this flag allows the driver
/// to reset the xHC without affecting the state of the ports.
///
/// A system software read of this bit as 0 indicates the Light Host Controller Reset has completed
/// and it is safe for software to re-initialize the xHC. A software read of this bit as a 1 indicates the
/// Light Host Controller Reset has not yet completed.
///
/// If not implemented, a read of this flag shall always return a 0.
///
/// All registers in the Aux Power well shall maintain the values that had been asserted prior to the
/// Light Host Controller Reset. Refer to section 4.23.1 for more information.
///
/// When this register is exposed by a Virtual Function (VF), this bit only generates a Light Reset to
/// the xHC instance presented by the selected VF, e.g. Disable the VFs device slots and set the
/// associated VF Run bit to Stopped. Refer to section 8 for more information.
pub light_host_controller_reset: bool,
/// Controller Save State (CSS) - RW. Default = 0. When written by software with 1 and HCHalted
/// (HCH) = 1, then the xHC shall save any internal state (that may be restored by a subsequent
/// Restore State operation) and if FSC = '1' any cached Slot, Endpoint, Stream, or other Context
/// information (so that software may save it). When written by software with 1 and HCHalted
/// (HCH) = 0, or written with 0, no Save State operation shall be performed. This flag always
/// returns 0 when read. Refer to the Save State Status (SSS) flag in the USBSTS register for
/// information on Save State completion. Refer to section 4.23.2 for more information on xHC
///
/// Save/Restore operation. Note that undefined behavior may occur if a Save State operation is
/// initiated while Restore State Status (RSS) = 1.
///
/// When this register is exposed by a Virtual Function (VF), this bit only controls saving the state of
/// the xHC instance presented by the selected VF. Refer to section 8 for more information.
pub controller_save_state: bool,
/// Controller Restore State (CRS) - RW. Default = 0. When set to 1, and HCHalted (HCH) = 1,
/// then the xHC shall perform a Restore State operation and restore its internal state. When set to
/// 1 and Run/Stop (R/S) = 1 or HCHalted (HCH) = 0, or when cleared to 0, no Restore State
/// operation shall be performed. This flag always returns 0 when read. Refer to the Restore State
/// Status (RSS) flag in the USBSTS register for information on Restore State completion. Refer to
/// section 4.23.2 for more information. Note that undefined behavior may occur if a Restore State
/// operation is initiated while Save State Status (SSS) = 1.
/// When this register is exposed by a Virtual Function (VF), this bit only controls restoring the state
/// of the xHC instance presented by the selected VF. Refer to section 8 for more information.
pub controller_restore_state: bool,
/// Enable Wrap Event (EWE) - RW. Default = 0. When set to 1, the xHC shall generate a MFINDEX
/// Wrap Event every time the MFINDEX register transitions from 03FFFh to 0. When cleared to 0
/// no MFINDEX Wrap Events are generated. Refer to section 4.14.2 for more information.
///
/// When this register is exposed by a Virtual Function (VF), the generation of MFINDEX Wrap
/// Events to VFs shall be emulated by the VMM.
pub enable_wrap_event: bool,
/// Enable U3 MFINDEX Stop (EU3S) - RW. Default = 0. When set to 1, the xHC may stop the
/// MFINDEX counting action if all Root Hub ports are in the U3, Disconnected, Disabled, or
/// Powered-off state. When cleared to 0 the xHC may stop the MFINDEX counting action if all
/// Root Hub ports are in the Disconnected, Disabled, Training, or Powered-off state. Refer to
/// section 4.14.2 for more information
pub enable_u3_mfindex_stop: bool,
___: bool,
/// CEM Enable (CME) - RW. Default = '0'. When set to '1', a Max Exit Latency Too Large Capability
/// Error may be returned by a Configure Endpoint Command. When cleared to '0', a Max Exit
/// Latency Too Large Capability Error shall not be returned by a Configure Endpoint Command.
/// This bit is Reserved if CMC = 0. Refer to section 4.23.5.2.2 for more information.
pub cem_enable: bool,
/// Extended TBC Enable (ETE). This flag indicates that the host controller implementation is
/// enabled to support Transfer Burst Count (TBC) values greater that 4 in isoch TDs. When this bit
/// is 1, the Isoch TRB TD Size/TBC field presents the TBC value, and the TBC/RsvdZ field is RsvdZ.
/// When this bit is 0, the TDSize/TCB field presents the TD Size value, and the TBC/RsvdZ field
/// presents the TBC value. This bit may be set only if ETC = 1. Refer to section 4.11.2.3 for more
/// information.
pub extended_tbc_enable: bool,
/// Extended TBC TRB Status Enable (TSC_EN). This flag indicates that the host controller
/// implementation is enabled to support ETC_TSC capability. When this is 1, TRBSts field in the
/// TRB updated to indicate if it is last transfer TRB in the TD. This bit may be set only if
/// ETC_TSC=1. Refer to section 4.11.2.3 for more information.
pub extended_tbc_trb_status_enable: bool,
/// VTIO Enable (VTIOE) RW. Default = 0. When set to 1, XHCI HW will enable its VTIO
/// capability and begin to use the information provided via that VTIO Registers to determine its
/// DMA-ID. When cleared to 0, XHCI HW will use the Primary DMA-ID for all accesses. This bit
/// may be set only if VTC = 1.
pub vtio_enable: bool,
#[bits(15)]
____: u16,
}
#[bitfield(u32)]
pub struct UsbStatus {
/// HCHalted (HCH) RO. Default = 1. This bit is a 0 whenever the Run/Stop (R/S) bit is a 1. The
/// xHC sets this bit to 1 after it has stopped executing as a result of the Run/Stop (R/S) bit being
/// cleared to 0, either by software or by the xHC hardware (e.g. internal error).
///
/// If this bit is '1', then SOFs, microSOFs, or Isochronous Timestamp Packets (ITP) shall not be
/// generated by the xHC, and any received Transaction Packet shall be dropped.
///
/// When this register is exposed by a Virtual Function (VF), this bit only reflects the Halted state of
/// the xHC instance presented by the selected VF. Refer to section 8 for more information
#[bits(access=RO)]
pub host_controller_halted: bool,
__: bool,
/// Host System Error (HSE) RW1C. Default = 0. The xHC sets this bit to 1 when a serious error
/// is detected, either internal to the xHC or during a host system access involving the xHC module.
/// (In a PCI system, conditions that set this bit to 1 include PCI Parity error, PCI Master Abort, and
/// PCI Target Abort.) When this error occurs, the xHC clears the Run/Stop (R/S) bit in the USBCMD
/// register to prevent further execution of the scheduled TDs. If the HSEE bit in the USBCMD
/// register is a 1, the xHC shall also assert out-of-band error signaling to the host. Refer to section
/// 4.10.2.6 for more information.
/// When this register is exposed by a Virtual Function (VF), the assertion of this bit affects all VFs
/// and reflects the Host System Error state of the Physical Function (PF0). Refer to section 8 for
/// more information.
pub host_system_error: bool,
/// Event Interrupt (EINT) RW1C. Default = 0. The xHC sets this bit to 1 when the Interrupt
/// Pending (IP) bit of any Interrupter transitions from 0 to 1. Refer to section 7.1.2 for use.
/// Software that uses EINT shall clear it prior to clearing any IP flags. A race condition may occur if
/// software clears the IP flags then clears the EINT flag, and between the operations another IP 0
/// to '1' transition occurs. In this case the new IP transition shall be lost.
/// When this register is exposed by a Virtual Function (VF), this bit is the logical 'OR' of the IP bits
/// for the Interrupters assigned to the selected VF. And it shall be cleared to 0 when all associated
/// interrupter IP bits are cleared, i.e. all the VFs Interrupter Event Ring(s) are empty. Refer to
/// section 8 for more information
pub event_interrupt: bool,
/// Port Change Detect (PCD) RW1C. Default = 0. The xHC sets this bit to a 1 when any port has
/// a change bit transition from a 0 to a 1.
///
/// This bit is allowed to be maintained in the Aux Power well. Alternatively, it is also acceptable
/// that on a D3 to D0 transition of the xHC, this bit is loaded with the OR of all of the PORTSC
/// change bits. Refer to section 4.19.3.
///
/// This bit provides system software an efficient means of determining if there has been Root Hub
/// port activity. Refer to section 4.15.2.3 for more information.
///
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
/// bit as a function of the Root Hub Ports associated with the Device Slots assigned to the selected
/// VF. Refer to section 8 for more information.
pub port_change_detect: bool,
#[bits(3)]
__: u8,
/// Save State Status (SSS) - RO. Default = 0. When the Controller Save State (CSS) flag in the
/// USBCMD register is written with 1 this bit shall be set to 1 and remain 1 while the xHC saves
/// its internal state. When the Save State operation is complete, this bit shall be cleared to 0.
/// Refer to section 4.23.2 for more information.
///
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
/// bit as a function of the saving the state for the selected VF. Refer to section 8 for more
/// information.
#[bits(access=RO)]
pub save_state_status: bool,
/// Restore State Status (RSS) - RO. Default = 0. When the Controller Restore State (CRS) flag in
/// the USBCMD register is written with 1 this bit shall be set to 1 and remain 1 while the xHC
/// restores its internal state. When the Restore State operation is complete, this bit shall be
/// cleared to 0. Refer to section 4.23.2 for more information.
///
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
/// bit as a function of the restoring the state for the selected VF. Refer to section 8 for more
/// information.
#[bits(access=RO)]
pub restore_state_status: bool,
/// Save/Restore Error (SRE) - RW1C. Default = 0. If an error occurs during a Save or Restore
/// operation this bit shall be set to 1. This bit shall be cleared to 0 when a Save or Restore
/// operation is initiated or when written with 1. Refer to section 4.23.2 for more information.
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
/// bit as a function of the Save/Restore completion status for the selected VF. Refer to section 8
/// for more information.
pub save_restore_error: bool,
/// Controller Not Ready (CNR) RO. Default = 1. 0 = Ready and 1 = Not Ready. Software shall
/// not write any Doorbell or Operational register of the xHC, other than the USBSTS register, until
/// CNR = 0. This flag is set by the xHC after a Chip Hardware Reset and cleared when the xHC is
/// ready to begin accepting register writes. This flag shall remain cleared (0) until the next Chip
/// Hardware Reset.
#[bits(access=RO)]
pub controller_not_ready: bool,
/// Host Controller Error (HCE) RO. Default = 0. 0 = No internal xHC error conditions exist and 1
/// = Internal xHC error condition. This flag shall be set to indicate that an internal error condition
/// has been detected which requires software to reset and reinitialize the xHC. Refer to section
/// 4.24.1 for more information.
#[bits(access=RO)]
pub host_controller_error: bool,
#[bits(19)]
__: u32,
}
#[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
/// Slots are allocated contiguously. e.g. A value of 16 specifies that Device Slots 1 to 16 are active.
///
/// A value of 0 disables all Device Slots. A disabled Device Slot shall not respond to Doorbell
/// Register references.
///
/// This field shall not be modified by software if the xHC is running (Run/Stop (R/S) = 1)
pub max_device_slots_enabled: u8,
/// U3 Entry Enable (U3E) RW. Default = '0'. When set to '1', the xHC shall assert the PLC flag ('1')
/// when a Root Hub port transitions to the U3 State. Refer to section 4.15.1 for more information.
pub u3_entry_enable: bool,
/// Configuration Information Enable (CIE) - RW. Default = '0'. When set to '1', the software shall
/// initialize the Configuration Value, Interface Number, and Alternate Setting fields in the Input
/// Control Context when it is associated with a Configure Endpoint Command. When this bit is '0',
/// the extended Input Control Context fields are not supported. Refer to section 6.2.5.1 for more
/// information.
pub configuration_information_enable: bool,
#[bits(22)]
__: u32,
}
/// XHCI Spec Section 5.4
///
/// > The base address of this register space is referred to as Operational Base. The
/// 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, packed)]
pub struct HostControllerOperational {
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)
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.
pub device_context_base_address_array_pointer: u64,
pub configure: UsbConfigure,
}
const _: () = assert!(size_of::<HostControllerOperational>() == 0x3C);

View file

@ -0,0 +1,395 @@
use bitfield_struct::bitfield;
/// A host controller shall implement one or more port registers. The number of
/// port registers implemented by a particular instantiation of a host controller is
/// documented in the HCSPARAMS1 register (Section 5.3.3). Software uses this
/// information as an input parameter to determine how many ports need to be
/// serviced.
///
/// XHCI Spec 5.4.8
#[bitfield(u32)]
pub struct PortStatusAndControl {
/// A host controller shall implement one or more port registers. The number of
/// port registers implemented by a particular instantiation of a host controller is
/// documented in the HCSPARAMS1 register (Section 5.3.3). Software uses this
/// information as an input parameter to determine how many ports need to be
/// serviced. All ports have the structure defined below.
#[bits(access=RO)]
current_connect_status: bool,
/// 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.
///
/// A port may be disabled by software writing a 1 to this flag.
///
/// This flag shall automatically be cleared to 0 by a disconnect event or other fault condition.
/// Note that the bit status does not change until the port state actually changes. There may be a
/// delay in disabling or enabling a port due to other host controller or bus events.
///
/// When the port is disabled (PED = 0) downstream propagation of data is blocked on this port,
/// except for reset.
///
/// For USB2 protocol ports:
/// When the port is in the Disabled state, software shall reset the port (PR = 1) to transition PED to
/// 1 and the port to the Enabled state.
///
/// For USB3 protocol ports:
/// When the port is in the Polling state (after detecting an attach), the port shall automatically
/// transition to the Enabled state and set PED to 1 upon the completion of successful link training.
/// When the port is in the Disabled state, software shall write a 5 (RxDetect) to the PLS field to
/// transition the port to the Disconnected state. Refer to section 4.19.1.2.
///
/// PED shall automatically be cleared to 0 when PR is set to 1, and set to 1 when PR transitions
/// from 1 to 0 after a successful reset. Refer to Port Reset (PR) bit for more information on how
/// 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.
/// This flag is 0 if PP is 0.
port_enabled_disabled: bool,
__: bool,
/// Over-current Active (OCA) RO. Default = 0. 1 = This port currently has an over-current
/// 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.
#[bits(access=RO)]
over_current_active: bool,
/// 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
/// sequence is initiated83; USB2 protocol ports shall execute the bus reset sequence as defined in
/// the USB2 Spec. USB3 protocol ports shall execute the Hot Reset sequence as defined in the
/// USB3 Spec. PR remains set until reset signaling is completed by the root hub.
///
/// Note that software shall write a 1 to this flag to transition a USB2 port from the Polling state to
/// the Enabled state. Refer to sections 4.15.2.3 and 4.19.1.1.
///
/// This flag is 0 if PP is 0.
port_reset: bool,
/// Port Link State (PLS) RWS. Default = RxDetect (5). This field is used to power manage the port
/// and reflects its current link state.
///
/// When the port is in the Enabled state, system software may set the link U state by writing this
/// field. System software may also write this field to force a Disabled to Disconnected state
/// transition of the port.
///
/// Write Value Description
/// 0 The link shall transition to a U0 state from any of the U states.
/// 285 USB2 protocol ports only. The link should transition to the U2 State.
/// 384 The link shall transition to a U3 state from the U0 state. This action
/// selectively suspends the device connected to this port. While the Port
/// Link State = U3, the hub does not propagate downstream-directed
/// traffic to this port, but the hub shall respond to resume signaling from
/// the port.
/// 5 USB3 protocol ports only. If the port is in the Disabled state (PLS =
/// Disabled, PP = 1), then the link shall transition to a RxDetect state and
/// the port shall transition to the Disconnected state, else ignored.
/// 10 USB3 protocol ports only. Shall enable a link transition to the
/// Compliance state, i.e. CTE = 1. Refer to section 4.19.1.2.4.1 for more
/// information.
/// 185,4,6-9,11-14 Ignored.
/// 15 USB2 protocol ports only. If the port is in the U3 state (PLS = U3), then
/// the link shall remain in the U3 state and the port shall transition to the
/// Resume substate, else ignored. Refer to section 4.15.2 for more
/// information.
///
/// Note: The Port Link State Write Strobe (LWS) shall also be set to 1 to write this
/// field.
///
/// For USB2 protocol ports: Writing a value of '2' to this field shall request LPM, asserting L1
/// signaling on the USB2 bus. Software may read this field to determine if the transition to the U2
/// state was successful. Writing a value of '0' shall deassert L1 signaling on the USB. Writing a value
/// of '1' shall have no effect. The U1 state shall never be reported by a USB2 protocol port.
///
/// Read Value Meaning
/// 0 Link is in the U0 State
/// 1 Link is in the U1 State
/// 2 Link is in the U2 State
/// 3 Link is in the U3 State (Device Suspended)
/// 4 Link is in the Disabled State86
/// 5 Link is in the RxDetect State87
/// 6 Link is in the Inactive State88
/// 7 Link is in the Polling State
/// 8 Link is in the Recovery State
/// 9 Link is in the Hot Reset State
/// 10 Link is in the Compliance Mode State
/// 11 Link is in the Test Mode89 State
/// 12-14 Reserved
/// 15 Link is in the Resume State90
///
/// This field is undefined if PP = 0.
///
/// Note: Transitions between different states are not reflected until the transition is complete. Refer
/// to section 4.19 for PLS transition conditions.
/// 409
///
/// Refer to sections 4.15.2 and 4.23.5 for more information on the use of this field. Refer to the
/// USB2 LPM ECR for more information on USB link power management operation. Refer to section
/// 7.2 for supported USB protocols
#[bits(4)]
port_link_status: u8,
/// 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
/// or may not represent whether (VBus) power is actually applied to the port. When PP equals a '0'
/// the port is nonfunctional and shall not report attaches, detaches, or Port Link State (PLS)
/// changes. However, the port shall report over-current conditions when PP = 0 if PPC = 0. After
/// modifying PP, software shall read PP and confirm that it is reached its target state before
/// modifying it again91, undefined behavior may occur if this procedure is not followed.
///
/// 0 = This port is in the Powered-off state.
/// 1 = This port is not in the Powered-off state.
///
/// If the Port Power Control (PPC) flag in the HCCPARAMS1 register is '1', then xHC has port power
/// control switches and this bit represents the current setting of the switch ('0' = off, '1' = on).
///
/// If the Port Power Control (PPC) flag in the HCCPARAMS1 register is '0', then xHC does not have
/// port power control switches and each port is hard wired to power, and not affected by this bit.
/// When an over-current condition is detected on a powered port, the xHC shall transition the PP
/// bit in each affected port from a 1 to 0 (removing power from the port).
///
/// Note: If this is an SSIC Port, then the DSP Disconnect process is initiated by '1' to '0' transition of
/// PP. After an SSIC USP disconnect process, the port may be disabled by setting PED = 1. As noted,
/// the SSIC spec does not define a mechanism for the USP to request DSP to be re-enabled for a
/// subsequent re-connect. If PED is set to 1 without a prior negotiated disconnect with the USP,
/// subsequent re-enabling of the port requires DSP to issue a WPR to bring USP back to Rx.Detect.
///
/// Refer to section 5.1.2 in the SSIC Spec for more information.
/// Refer to section 4.19.4 for more information.
port_power: bool,
/// 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
/// field shall indicate Undefined Speed. Refer to section 4.19.3.
///
/// Value Meaning
/// 0 Undefined Speed
/// 1 -15 Protocol Speed ID (PSI), refer to section 7.2.1 for the definition of PSIV
/// field in the PSI Dword
///
/// Note: This field is invalid on a USB2 protocol port until after the port is reset.
#[bits(4)]
port_speed: u8,
/// 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
/// encodings are:
///
/// Value Meaning
/// 0 Port indicators are off
/// 1 Amber
/// 2 Green
/// 3 Undefined
///
/// 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
#[bits(2)]
port_indicator_control: u8,
/// 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
/// is ignored. Reads to this bit return 0
port_link_state_write_strobe: bool,
/// 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
/// Status (CAS) bits. Note that this flag shall not be set if the CCS transition was due to software
/// setting PP to 0, or the CAS transition was due to software setting WPR to 1. The xHC sets this
/// bit to 1 for all changes to the port device connect status92, even if system software has not
/// cleared an existing Connect Status Change. For example, the insertion status changes twice
/// 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.
/// Refer to section 4.19.2 for more information on change bit usage.
connect_status_change: bool,
/// 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
/// 0. Software shall clear this bit by writing a 1 to it. Refer to section 4.19.2 for more information
/// on change bit usage.
///
/// For a USB2 protocol port, this bit shall be set to 1 only when the port is disabled due to the
/// appropriate conditions existing at the EOF2 point (refer to section 11.8.1 of the USB2
///
/// Specification for the definition of a Port Error).
/// For a USB3 protocol port, this bit shall never be set to 1.
port_enabled_disabled_change: bool,
/// 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
/// flag shall not be set to 1 if the Warm Reset processing was forced to terminate due to software
/// clearing PP or PED to '0'. Software shall clear this bit by writing a '1' to it. Refer to section 4.19.5.1.
/// 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.
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
/// 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.
over_current_change: bool,
/// 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
/// 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
/// writing a '1' to it. Refer to section 4.19.5. Refer to section 4.19.2 for more information on change
/// bit usage
port_reset_change: bool,
/// Port Link State Change (PLC) RW1CS. Default = 0. This flag is set to 1 due to the following
/// PLS transitions:
///
/// Transition Condition
/// U3 -> Resume: Wakeup signaling from a device
/// Resume -> Recovery -> U0: Device Resume complete (USB3 protocol ports
/// only)
/// Resume -> U0: Device Resume complete (USB2 protocol ports
/// only)
/// U3 -> Recovery -> U0: Software Resume complete (USB3 protocol ports
/// only)
/// U3 -> U0: Software Resume complete (USB2 protocol ports
/// only)
/// U2 -> U0: L1 Resume complete (USB2 protocol ports only)93
/// U0 -> U0: L1 Entry Reject (USB2 protocol ports only)93
/// Any state -> Inactive: Error (USB3 protocol ports only).
/// Note: PLC is asserted only on the first LTSSM
/// SS.Inactive.Disconnect.Detect to SS.Inactive.Quiet
/// substate transition after entering the SS.Inactive
/// state.
/// Any State -> U3: U3 Entry complete. Note: PLC is asserted only if
/// U3E = 1.
///
/// Note that this flag shall not be set if the PLS transition was due to software
/// setting PP to 0. Refer to section 4.23.5 for more information. '0' = No
/// change. '1' = Link Status Changed. Software shall clear this bit by
/// 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
/// 4.19.2 for more information on change bit usage.
port_link_state_change: bool,
/// 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
/// clear this bit by writing a '1' to it. Refer to section 4.19.2 for more information on change bit
/// usage.
///
/// Note: This flag is valid only for USB3 protocol ports. For USB2 protocol ports this bit shall be
/// RsvdZ.
port_config_error_change: bool,
/// 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
/// Enabled state. Refer to sections 4.19.8 for more details on the Cold Attach Status (CAS) assertion
/// conditions. Software shall clear this bit by writing a '1' to WPR or the xHC shall clear this bit if CCS
/// transitions to 1.
/// This flag is 0 if PP is 0 or for USB2 protocol ports
#[bits(access=RO)]
cold_attach_status: bool,
/// 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
/// model.
wake_on_connect_enable: bool,
/// 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
/// operational model.
wake_on_disconnect_enable: bool,
/// 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
/// for operational model.
wake_on_overcurrent_enable: bool,
__: bool,
__: bool,
/// Device Removable97 (DR) - RO. This flag indicates if this port has a removable device attached.
/// 1 = Device is non-removable. 0 = Device is removable.
#[bits(access=RO)]
device_removable: bool,
/// 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.
/// 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.
/// This flag only applies to USB3 protocol ports. For USB2 protocol ports it shall be RsvdZ.
warm_port_reset: bool,
}
/// XHCI Spec 5.4.9
/// NOTE: This definition is USB3 only.
#[bitfield(u32)]
pub struct PortPowerManagementStatusAndControl {
/// U1 Timeout RWS. Default = 0. Timeout value for U1 inactivity timer. If equal to FFh, the port
/// is disabled from initiating U1 entry. This field shall be set to 0 by the assertion of PR to 1. Refer
/// to section 4.19.4.1 for more information on U1 Timeout operation. The following are
/// permissible values:
///
/// Value Description
/// 00h Zero (default)
/// 01h 1 μs.
/// 02h 2 μs.
/// …
/// 7Fh 127 μs.
/// 80hFEh Reserved
/// FFh Infinite
u1_timeout: u8,
/// U2 Timeout RWS. Default = 0. Timeout value for U2 inactivity timer. If equal to FFh, the port
/// is disabled from initiating U2 entry. This field shall be set to 0 by the assertion of PR to 1. Refer
/// to section 4.19.4.1 for more information on U2 Timeout operation. The following are
/// permissible values:
///
/// Value Description
/// 00h Zero (default)
/// 01h 256 μs
/// 02h 512 μs
/// …
/// FEh 65,024 ms
/// FFh Infinite
///
/// A U2 Inactivity Timeout LMP shall be sent by the xHC to the device connected on this port when
/// this field is written. Refer to Sections 8.4.3 and 10.4.2.10 of the USB3 specification for more
/// details
u2_timeout: u8,
/// Force Link PM Accept (FLA) - RW. Default = 0. When this bit is set to 1, the port shall generate
/// a Set Link Function LMP with the Force_LinkPM_Accept bit asserted (1). When this bit is cleared
/// to 0, the port shall generate a Set Link Function LMP with the Force_LinkPM_Accept bit de-
/// asserted (0).
/// This flag shall be set to 0 by the assertion of PR to 1 or when CCS = transitions from 0 to 1.
/// Writes to this flag have no effect if PP = 0.
/// The Set Link Function LMP is sent by the xHC to the device connected on this port when this bit
/// transitions from 0 to 1 or 1 to 0. Refer to Sections 8.4.2 and 10.14.2.2 of the USB3
/// specification for more details.
/// Improper use of the SS Force_LinkPM_Accept functionality can impact the performance of the
/// link significantly. This bit shall only be used for compliance and testing purposes. Software shall
/// ensure that there are no pending packets at the link level before setting this bit.
/// This flag is 0 if PP is 0
force_link_pm_accept: bool,
#[bits(15)]
__: u16,
}
/// XHCI Spec 5.4.10
///
/// NOTE: This definition is USB3 only.
#[bitfield(u32)]
pub struct PortLinkInfo {
/// Link Error Count RW. Default = 0. This field returns the number of link errors detected by the
/// port. This value shall be reset to 0 by the assertion of a Chip Hardware Reset, HCRST, when PR
/// transitions from 1 to 0, or when reset by software by writing 0 to it. This register will increment
/// by one each time a port transitions from U0 to Recovery to recover an error event and will
/// saturate at max
link_error_count: u16,
/// Rx Lane Count (RLC) - RO. Default = '0'. This field that identifies the number of Receive Lanes
/// negotiated by the port. This is a "zero-based" value, where 0 to 15 represents Lane Counts of 1
/// to 16, respectively. This value is valid only when CCS = '1'. RLC shall equal '0' for a simplex
/// Sublink. Refer to section 7.2.1 for more information.
#[bits(4)]
rx_lane_count: u8,
/// Tx Lane Count (TLC) - RO. Default = '0'. This field that identifies the number of Transmit Lanes
/// negotiated by the port. This is a "zero-based" value, where 0 to 15 represents Lane Counts of 1
/// to 16, respectively. This value is valid only when CCS = '1'. TLC shall equal '0' for a simplex
/// Sublink. Refer to section 7.2.1 for more information.
#[bits(4)]
tx_lane_count: u8,
__: u8,
}
/// XHCI Spec 5.4.8 - 5.4.11
///
/// These registers are an array of size MAX_PORTS located at offset 0x400
/// from the HostControllerOperation address.
///
/// Where MAX_PORTS is HostControllerCapabilities.params_1.max_ports
#[repr(C, packed)]
pub struct HostControllerUsbPort {
status_and_control: PortStatusAndControl,
power_management_status_and_control: PortPowerManagementStatusAndControl,
link_info: PortLinkInfo,
hardware_lpm_control: u32,
}
const _: () = assert!(size_of::<HostControllerUsbPort>() == 0x10);

View file

@ -0,0 +1,116 @@
use bitfield_struct::bitfield;
/// The Interrupter Management register allows system software to enable, disable,
/// and detect xHC interrupts.
///
/// XHCI 5.5.2.1
#[bitfield(u32)]
pub struct InterrupterManagement {
/// Interrupt Pending (IP) - RW1C. Default = 0. This flag represents the current state of the
/// Interrupter. If IP = 1, an interrupt is pending for this Interrupter. A 0 value indicates that no
/// interrupt is pending for the Interrupter. Refer to section 4.17.3 for the conditions that modify
/// the state of this flag.
pub interrupt_pending: bool,
/// Interrupt Enable (IE) RW. Default = 0. This flag specifies whether the Interrupter is capable of
/// generating an interrupt. When this bit and the IP bit are set (1), the Interrupter shall generate
/// an interrupt when the Interrupter Moderation Counter reaches 0. If this bit is 0, then the
/// Interrupter is prohibited from generating interrupts
pub interrupt_enabled: bool,
#[bits(30)]
_reserved: u32,
}
/// The Interrupter Moderation Register controls the “interrupt moderation” feature
/// of an Interrupter, allowing system software to throttle the interrupt rate
/// generated by the xHC
///
/// XHCI 5.5.2.2
#[bitfield(u32)]
pub struct InterrupterModeration {
/// Interrupt Moderation Interval (IMODI) RW. Default = 4000 (~1ms). Minimum inter-interrupt
/// interval. The interval is specified in 250ns increments. A value of 0 disables interrupt throttling
/// logic and interrupts shall be generated immediately if IP = 0, EHB = 0, and the Event Ring is
/// not empty
pub interrupt_moderation_interval: u16,
/// Interrupt Moderation Counter (IMODC) RW. Default = undefined. Down counter. Loaded with
/// the IMODI value whenever IP is cleared to 0, counts down to 0, and stops. The associated
/// interrupt shall be signaled whenever this counter is 0, the Event Ring is not empty, the IE and IP
/// flags = 1, and EHB = 0.
/// This counter may be directly written by software at any time to alter the interrupt rate
pub interrupt_moderation_counter: u16,
}
/// The Event Ring Segment Table Size Register defines the number of segments
/// supported by the Event Ring Segment Table.
///
/// XHCI 5.5.2.3.1
#[bitfield(u32)]
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,
_reserved: u16,
}
/// The Event Ring Dequeue Pointer Register is written by software to define the
/// Event Ring Dequeue Pointer location to the xHC. Software updates this pointer
/// when it is finished the evaluation of an Event(s) on the Event Ring.
///
/// XHCI 5.5.2.3.3
#[bitfield(u64)]
pub struct EventRingDequePointer {
/// Dequeue ERST Segment Index (DESI) RW. Default = 0. This field may be used by the xHC to
/// accelerate checking the Event Ring full condition. This field is written with the low order 3 bits of
/// the offset of the ERST entry which defines the Event Ring segment that the Event Ring Dequeue
/// Pointer resides in. Refer to section 6.5 for the definition of an ERST entry.
#[bits(3)]
pub dequeue_erst_segment_index: u8,
/// Event Handler Busy (EHB) - RW1C. Default = 0. This flag shall be set to 1 when the IP bit is set
/// to 1 and cleared to 0 by software when the Dequeue Pointer register is written. Refer to
/// section 4.17.2 for more information
pub event_handler_busy: bool,
/// Event Ring Dequeue Pointer - RW. Default = 0. This field defines the high order bits of the 64-
/// bit address of the current Event Ring Dequeue Pointer
#[bits(60)]
pub event_ring_dequeue_pointer: u64,
}
/// This is an array of registers starting at offset 0x20 of the Runtime Base.
/// The Runtime Base shall be 32-byte aligned and is calculated by adding the
/// value Runtime Register Space Offset register (refer to Section 5.3.8) to
/// the Capability Base address. All Runtime registers are multiples of 32 bits in length.
///
/// XHCI Spec 5.5.2
#[repr(C, packed)]
pub struct InterrupterRegisterSet {
pub interrupter_management: InterrupterManagement,
pub interrupter_moderation: InterrupterModeration,
pub event_ring_segement_table_size: EventRingSegmentTableSize,
_reserved: u32,
/// 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.
/// Writing this register sets the Event Ring State Machine:EREP Advancement to the Start state.
/// Refer to Figure 4-12 for more information.
/// For Secondary Interrupters: This field may be modified at any time.
/// For the Primary Interrupter: This field shall not be modified if HCHalted (HCH) = 0.
///
/// NOTE: This must be aligned such that bits 0:5 are 0.
///
/// XHCI 5.5.2.3.2
pub event_ring_segment_table_base_address: u64,
pub event_ring_deque_pointer: u64,
}

View file

@ -0,0 +1,15 @@
/// This mod contains XHCI Register Definitions
///
/// These are generally hardware backed registers
/// defined at fixed addresses.
mod capabilities;
mod doorbell;
mod host_controller;
mod host_controller_port;
mod interrupter;
pub use capabilities::*;
pub use doorbell::*;
pub use host_controller::*;
pub use host_controller_port::*;
pub use interrupter::*;

View 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
}
}

View file

@ -7,6 +7,5 @@ edition = "2021"
mammoth = { path = "../../lib/mammoth" } mammoth = { path = "../../lib/mammoth" }
denali_client = { path = "../../lib/client/denali_client" } denali_client = { path = "../../lib/client/denali_client" }
victoriafalls = { path = "../victoriafalls" } victoriafalls = { path = "../victoriafalls" }
voyageurs = { path = "../../lib/voyageurs" }
yellowstone-yunq = { path = "../../lib/yellowstone" } yellowstone-yunq = { path = "../../lib/yellowstone" }
yunq = { path = "../../lib/yunq" } yunq = { path = "../../lib/yunq" }

View file

@ -3,8 +3,8 @@
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64", "arch": "x86_64",
"target-endian": "little", "target-endian": "little",
"target-pointer-width": "64", "target-pointer-width": 64,
"target-c-int-width": "32", "target-c-int-width": 32,
"os": "none", "os": "none",
"executables": true, "executables": true,
"linker-flavor": "ld.lld", "linker-flavor": "ld.lld",

View file

@ -31,15 +31,15 @@ cleanup() {
} }
trap cleanup EXIT trap cleanup EXIT
parted -s $dev mklabel gpt mkpart EFI fat32 1MiB 10MiB mkpart ext2 10MiB 100% set 1 esp on parted -s $dev mklabel gpt mkpart BIOS ext2 1MiB 2MiB mkpart EFI fat32 2MiB 11MiB mkpart ext2 11MiB 100% set 1 bios_grub on set 2 esp on
mkfs.fat -F 12 "${dev}p1" mkfs.fat -F 12 "${dev}p2"
mke2fs "${dev}p2" mke2fs "${dev}p3"
limine bios-install "${dev}" limine bios-install "${dev}"
mkdir -p $EFI_DIR mkdir -p $EFI_DIR
mount "${dev}p1" $EFI_DIR mount "${dev}p2" $EFI_DIR
mkdir -p $EFI_DIR/EFI/BOOT mkdir -p $EFI_DIR/EFI/BOOT
cp /usr/share/limine/BOOTX64.EFI $EFI_DIR/EFI/BOOT cp /usr/share/limine/BOOTX64.EFI $EFI_DIR/EFI/BOOT
@ -52,7 +52,7 @@ cp $REPO_ROOT/sysroot/bin/denali $EFI_DIR/sys/denali
cp $REPO_ROOT/sysroot/bin/victoriafalls $EFI_DIR/sys/victoriafalls cp $REPO_ROOT/sysroot/bin/victoriafalls $EFI_DIR/sys/victoriafalls
mkdir -p $SYSROOT mkdir -p $SYSROOT
mount "${dev}p2" $SYSROOT mount "${dev}p3" $SYSROOT
rsync -a "$REPO_ROOT/sysroot" $BUILD_DIR rsync -a "$REPO_ROOT/sysroot" $BUILD_DIR
ls $SYSROOT ls $SYSROOT