From 38725f4973b35612af5960bb1aaf715dcd1b59ea Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Sun, 18 May 2025 19:11:52 -0700 Subject: [PATCH 01/12] Fix build image. --- rust/x86_64-acadia-os.json | 4 ++-- scripts/build_image.sh | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rust/x86_64-acadia-os.json b/rust/x86_64-acadia-os.json index 6e6dec1..1060335 100644 --- a/rust/x86_64-acadia-os.json +++ b/rust/x86_64-acadia-os.json @@ -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", "arch": "x86_64", "target-endian": "little", - "target-pointer-width": "64", - "target-c-int-width": "32", + "target-pointer-width": 64, + "target-c-int-width": 32, "os": "none", "executables": true, "linker-flavor": "ld.lld", diff --git a/scripts/build_image.sh b/scripts/build_image.sh index 2fd5867..9b39927 100644 --- a/scripts/build_image.sh +++ b/scripts/build_image.sh @@ -31,15 +31,15 @@ cleanup() { } trap cleanup EXIT -parted -s $dev mklabel gpt mkpart EFI fat32 1MiB 10MiB mkpart ext2 10MiB 100% set 1 esp on -mkfs.fat -F 12 "${dev}p1" -mke2fs "${dev}p2" +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}p2" +mke2fs "${dev}p3" limine bios-install "${dev}" mkdir -p $EFI_DIR -mount "${dev}p1" $EFI_DIR +mount "${dev}p2" $EFI_DIR mkdir -p $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 mkdir -p $SYSROOT -mount "${dev}p2" $SYSROOT +mount "${dev}p3" $SYSROOT rsync -a "$REPO_ROOT/sysroot" $BUILD_DIR ls $SYSROOT From 924f5a763c65368019dd527a59ee46e341eb49a0 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Sun, 18 May 2025 19:11:52 -0700 Subject: [PATCH 02/12] Rust XHCI scaffolding --- rust/Cargo.lock | 10 ++++++-- rust/Cargo.toml | 25 +++++++++++-------- rust/lib/client/voyageurs_client/Cargo.toml | 12 +++++++++ .../voyageurs_client}/build.rs | 2 +- .../voyageurs_client}/src/lib.rs | 0 .../voyageurs_client}/src/listener.rs | 0 rust/lib/voyageurs/Cargo.toml | 13 ---------- rust/sys/teton/Cargo.toml | 2 +- rust/sys/teton/src/main.rs | 2 +- rust/sys/teton/src/terminal.rs | 2 +- rust/sys/voyageurs/Cargo.toml | 7 ++++++ rust/sys/voyageurs/src/main.rs | 14 +++++++++++ rust/sys/yellowstone/Cargo.toml | 1 - 13 files changed, 59 insertions(+), 31 deletions(-) create mode 100644 rust/lib/client/voyageurs_client/Cargo.toml rename rust/lib/{voyageurs => client/voyageurs_client}/build.rs (82%) rename rust/lib/{voyageurs => client/voyageurs_client}/src/lib.rs (100%) rename rust/lib/{voyageurs => client/voyageurs_client}/src/listener.rs (100%) delete mode 100644 rust/lib/voyageurs/Cargo.toml create mode 100644 rust/sys/voyageurs/Cargo.toml create mode 100644 rust/sys/voyageurs/src/main.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f20f302..432d9ba 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -161,7 +161,7 @@ version = "0.1.0" dependencies = [ "mammoth", "victoriafalls", - "voyageurs", + "voyageurs_client", "yellowstone-yunq", ] @@ -192,6 +192,13 @@ dependencies = [ [[package]] name = "voyageurs" version = "0.1.0" +dependencies = [ + "mammoth", +] + +[[package]] +name = "voyageurs_client" +version = "0.1.0" dependencies = [ "mammoth", "yellowstone-yunq", @@ -206,7 +213,6 @@ dependencies = [ "denali_client", "mammoth", "victoriafalls", - "voyageurs", "yellowstone-yunq", "yunq", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index f1886f6..b31a682 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,16 +1,19 @@ [workspace] members = [ - "lib/client/denali_client", "lib/fs/ext2", - "lib/mammoth", "lib/pci", - "lib/voyageurs", - "lib/yellowstone", - "lib/yunq", - "lib/yunq-test", - "sys/denali", - "sys/teton", - "sys/victoriafalls", - "sys/yellowstone", - "usr/testbed", + "lib/client/denali_client", + "lib/client/voyageurs_client", + "lib/fs/ext2", + "lib/mammoth", + "lib/pci", + "lib/yellowstone", + "lib/yunq", + "lib/yunq-test", + "sys/denali", + "sys/teton", + "sys/victoriafalls", + "sys/voyageurs", + "sys/yellowstone", + "usr/testbed", ] resolver = "2" diff --git a/rust/lib/client/voyageurs_client/Cargo.toml b/rust/lib/client/voyageurs_client/Cargo.toml new file mode 100644 index 0000000..e3e432c --- /dev/null +++ b/rust/lib/client/voyageurs_client/Cargo.toml @@ -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" } diff --git a/rust/lib/voyageurs/build.rs b/rust/lib/client/voyageurs_client/build.rs similarity index 82% rename from rust/lib/voyageurs/build.rs rename to rust/lib/client/voyageurs_client/build.rs index 52b5dfc..f6f76ad 100644 --- a/rust/lib/voyageurs/build.rs +++ b/rust/lib/client/voyageurs_client/build.rs @@ -1,7 +1,7 @@ use std::fs; 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}"); diff --git a/rust/lib/voyageurs/src/lib.rs b/rust/lib/client/voyageurs_client/src/lib.rs similarity index 100% rename from rust/lib/voyageurs/src/lib.rs rename to rust/lib/client/voyageurs_client/src/lib.rs diff --git a/rust/lib/voyageurs/src/listener.rs b/rust/lib/client/voyageurs_client/src/listener.rs similarity index 100% rename from rust/lib/voyageurs/src/listener.rs rename to rust/lib/client/voyageurs_client/src/listener.rs diff --git a/rust/lib/voyageurs/Cargo.toml b/rust/lib/voyageurs/Cargo.toml deleted file mode 100644 index e011e0c..0000000 --- a/rust/lib/voyageurs/Cargo.toml +++ /dev/null @@ -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"} - diff --git a/rust/sys/teton/Cargo.toml b/rust/sys/teton/Cargo.toml index baa787a..3df98f4 100644 --- a/rust/sys/teton/Cargo.toml +++ b/rust/sys/teton/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" [dependencies] mammoth = { path = "../../lib/mammoth" } victoriafalls = { path = "../victoriafalls" } -voyageurs = { path = "../../lib/voyageurs" } +voyageurs_client = { path = "../../lib/client/voyageurs_client/" } yellowstone-yunq = { path = "../../lib/yellowstone" } diff --git a/rust/sys/teton/src/main.rs b/rust/sys/teton/src/main.rs index 982beb7..b44dff6 100644 --- a/rust/sys/teton/src/main.rs +++ b/rust/sys/teton/src/main.rs @@ -9,7 +9,7 @@ mod psf; mod terminal; use mammoth::{debug, define_entry, zion::z_err_t}; -use voyageurs::listener; +use voyageurs_client::listener; define_entry!(); diff --git a/rust/sys/teton/src/terminal.rs b/rust/sys/teton/src/terminal.rs index 5c7d314..2cd46ce 100644 --- a/rust/sys/teton/src/terminal.rs +++ b/rust/sys/teton/src/terminal.rs @@ -6,7 +6,7 @@ use alloc::{ string::{String, ToString}, }; use victoriafalls::dir; -use voyageurs::listener::KeyboardHandler; +use voyageurs_client::listener::KeyboardHandler; pub struct Terminal { console: Console, diff --git a/rust/sys/voyageurs/Cargo.toml b/rust/sys/voyageurs/Cargo.toml new file mode 100644 index 0000000..e44085b --- /dev/null +++ b/rust/sys/voyageurs/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "voyageurs" +version = "0.1.0" +edition = "2024" + +[dependencies] +mammoth = { path = "../../lib/mammoth/" } diff --git a/rust/sys/voyageurs/src/main.rs b/rust/sys/voyageurs/src/main.rs new file mode 100644 index 0000000..bcbd530 --- /dev/null +++ b/rust/sys/voyageurs/src/main.rs @@ -0,0 +1,14 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use mammoth::{debug, define_entry, zion::z_err_t}; + +define_entry!(); + +#[unsafe(no_mangle)] +extern "C" fn main() -> z_err_t { + debug!("In Voyageurs"); + 0 +} diff --git a/rust/sys/yellowstone/Cargo.toml b/rust/sys/yellowstone/Cargo.toml index 6a89d4d..70e9995 100644 --- a/rust/sys/yellowstone/Cargo.toml +++ b/rust/sys/yellowstone/Cargo.toml @@ -7,6 +7,5 @@ edition = "2021" mammoth = { path = "../../lib/mammoth" } denali_client = { path = "../../lib/client/denali_client" } victoriafalls = { path = "../victoriafalls" } -voyageurs = { path = "../../lib/voyageurs" } yellowstone-yunq = { path = "../../lib/yellowstone" } yunq = { path = "../../lib/yunq" } From e0d1f83d8ab6033836470bf13944c86afc8b0e60 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Sun, 18 May 2025 19:26:06 -0700 Subject: [PATCH 03/12] Rust XHCI Register Types. --- rust/Cargo.lock | 1 + rust/sys/voyageurs/Cargo.toml | 1 + rust/sys/voyageurs/src/main.rs | 2 + rust/sys/voyageurs/src/xhci/mod.rs | 1 + .../src/xhci/registers/capabilities.rs | 337 +++++++++++++++ .../voyageurs/src/xhci/registers/doorbell.rs | 65 +++ .../src/xhci/registers/host_controller.rs | 298 +++++++++++++ .../xhci/registers/host_controller_port.rs | 395 ++++++++++++++++++ .../src/xhci/registers/interrupter.rs | 116 +++++ rust/sys/voyageurs/src/xhci/registers/mod.rs | 15 + 10 files changed, 1231 insertions(+) create mode 100644 rust/sys/voyageurs/src/xhci/mod.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/capabilities.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/doorbell.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/host_controller.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/host_controller_port.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/interrupter.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/mod.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 432d9ba..35fccae 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -193,6 +193,7 @@ dependencies = [ name = "voyageurs" version = "0.1.0" dependencies = [ + "bitfield-struct", "mammoth", ] diff --git a/rust/sys/voyageurs/Cargo.toml b/rust/sys/voyageurs/Cargo.toml index e44085b..492a285 100644 --- a/rust/sys/voyageurs/Cargo.toml +++ b/rust/sys/voyageurs/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +bitfield-struct = "0.8.0" mammoth = { path = "../../lib/mammoth/" } diff --git a/rust/sys/voyageurs/src/main.rs b/rust/sys/voyageurs/src/main.rs index bcbd530..e9e5dd6 100644 --- a/rust/sys/voyageurs/src/main.rs +++ b/rust/sys/voyageurs/src/main.rs @@ -3,6 +3,8 @@ extern crate alloc; +mod xhci; + use mammoth::{debug, define_entry, zion::z_err_t}; define_entry!(); diff --git a/rust/sys/voyageurs/src/xhci/mod.rs b/rust/sys/voyageurs/src/xhci/mod.rs new file mode 100644 index 0000000..8993d24 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/mod.rs @@ -0,0 +1 @@ +pub mod registers; diff --git a/rust/sys/voyageurs/src/xhci/registers/capabilities.rs b/rust/sys/voyageurs/src/xhci/registers/capabilities.rs new file mode 100644 index 0000000..751fb9c --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/capabilities.rs @@ -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::() == 0x20); diff --git a/rust/sys/voyageurs/src/xhci/registers/doorbell.rs b/rust/sys/voyageurs/src/xhci/registers/doorbell.rs new file mode 100644 index 0000000..af59cc2 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/doorbell.rs @@ -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, +} diff --git a/rust/sys/voyageurs/src/xhci/registers/host_controller.rs b/rust/sys/voyageurs/src/xhci/registers/host_controller.rs new file mode 100644 index 0000000..db609af --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/host_controller.rs @@ -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 VF’s 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::() == 0x3C); diff --git a/rust/sys/voyageurs/src/xhci/registers/host_controller_port.rs b/rust/sys/voyageurs/src/xhci/registers/host_controller_port.rs new file mode 100644 index 0000000..369c4dc --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/host_controller_port.rs @@ -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 port’s 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. + /// 80h–FEh 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::() == 0x10); diff --git a/rust/sys/voyageurs/src/xhci/registers/interrupter.rs b/rust/sys/voyageurs/src/xhci/registers/interrupter.rs new file mode 100644 index 0000000..69fc188 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/interrupter.rs @@ -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, +} diff --git a/rust/sys/voyageurs/src/xhci/registers/mod.rs b/rust/sys/voyageurs/src/xhci/registers/mod.rs new file mode 100644 index 0000000..37acf46 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/mod.rs @@ -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::*; From d42494f1246614934bb3e5781520f46d8e55595e Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Fri, 5 Dec 2025 21:15:00 -0800 Subject: [PATCH 04/12] Physical Memory Management Improvements. --- rust/lib/mammoth/src/elf.rs | 4 +- rust/lib/mammoth/src/lib.rs | 1 + rust/lib/mammoth/src/mem.rs | 94 ++++++++++++++++--- rust/lib/mammoth/src/physical_box.rs | 131 +++++++++++++++++++++++++++ 4 files changed, 212 insertions(+), 18 deletions(-) create mode 100644 rust/lib/mammoth/src/physical_box.rs diff --git a/rust/lib/mammoth/src/elf.rs b/rust/lib/mammoth/src/elf.rs index 2b8638f..dd33876 100644 --- a/rust/lib/mammoth/src/elf.rs +++ b/rust/lib/mammoth/src/elf.rs @@ -239,9 +239,7 @@ fn load_program_segment( let mem_object = crate::mem::MemoryRegion::new(mem_size)?; - for i in mem_object.mut_slice() { - *i = 0; - } + mem_object.zero_region(); let file_start = prog_header.offset as usize; let file_end = file_start + prog_header.file_size as usize; diff --git a/rust/lib/mammoth/src/lib.rs b/rust/lib/mammoth/src/lib.rs index 2058a6b..ce4f915 100644 --- a/rust/lib/mammoth/src/lib.rs +++ b/rust/lib/mammoth/src/lib.rs @@ -13,6 +13,7 @@ mod cap_syscall; pub mod elf; pub mod init; pub mod mem; +pub mod physical_box; pub mod port; pub mod sync; pub mod syscall; diff --git a/rust/lib/mammoth/src/mem.rs b/rust/lib/mammoth/src/mem.rs index 8d5af40..7b81806 100644 --- a/rust/lib/mammoth/src/mem.rs +++ b/rust/lib/mammoth/src/mem.rs @@ -3,7 +3,8 @@ use crate::syscall; use crate::zion::ZError; use alloc::slice; 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")] use linked_list_allocator::LockedHeap; @@ -29,6 +30,7 @@ pub fn init_heap() { pub struct MemoryRegion { mem_cap: Capability, virt_addr: u64, + // TODO: This should be a usize probably. 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(&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::() as u64 <= self.size); (self.virt_addr + offset) as *const T } + pub fn mut_ptr_at_offset(&self, offset: usize) -> *mut T { + assert!(offset + size_of::() <= 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(&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::(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(&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::(offset) + } + pub fn cap(&self) -> &Capability { &self.mem_cap } @@ -137,28 +176,22 @@ impl Drop for MemoryRegion { } } -pub struct Volatile { - /// TODO: This should maybe be MaybeUninit. - data: T, -} +#[repr(transparent)] +pub struct Volatile(T); -impl Volatile { - pub fn read(&self) -> T - where - T: Copy, - { - unsafe { addr_of!(self.data).cast::().read_volatile() } +impl Volatile { + pub fn read(&self) -> T { + unsafe { read_volatile(addr_of!(self.0)) } } pub fn write(&mut self, data: T) { unsafe { - addr_of_mut!(self.data).cast::().write_volatile(data); + write_volatile(addr_of_mut!(self.0), data); } } pub fn update(&mut self, func: F) where - T: Copy, F: Fn(&mut T), { 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 { let vaddr = syscall::address_space_map(&mem_cap).unwrap(); mem_cap.release(); diff --git a/rust/lib/mammoth/src/physical_box.rs b/rust/lib/mammoth/src/physical_box.rs new file mode 100644 index 0000000..692eed1 --- /dev/null +++ b/rust/lib/mammoth/src/physical_box.rs @@ -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 { + data: NonNull, + region: MemoryRegion, + physical_address: usize, + _marker: PhantomData, +} + +impl PhysicalBox { + pub fn physical_address(&self) -> usize { + self.physical_address + } +} + +impl Deref for PhysicalBox { + 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 DerefMut for PhysicalBox { + 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 PhysicalBox<[T]> { + pub fn default_with_count(default: T, len: usize) -> Self + where + T: Clone, + { + let layout = core::alloc::Layout::array::(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) -> Self { + let len = vec.len(); + let layout = core::alloc::Layout::array::(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 Index for PhysicalBox<[T]> +where + I: slice::SliceIndex<[T]>, +{ + type Output = I::Output; + + fn index(&self, index: I) -> &Self::Output { + &(**self)[index] + } +} + +impl IndexMut for PhysicalBox<[T]> +where + I: slice::SliceIndex<[T]>, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut (**self)[index] + } +} + +impl Drop for PhysicalBox { + fn drop(&mut self) { + // SAFETY: + // - We own this data. + unsafe { core::ptr::drop_in_place(self.data.as_ptr()) } + } +} From e621d144e9e61ae755746e53e68f23efd41c53a3 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Fri, 5 Dec 2025 21:15:00 -0800 Subject: [PATCH 05/12] Rust XHCI Data Structure Types. --- rust/Cargo.lock | 17 +- rust/sys/voyageurs/Cargo.toml | 2 +- .../xhci/data_structures/endpoint_context.rs | 228 ++++++++++++++++++ .../event_ring_segment_table.rs | 62 +++++ .../voyageurs/src/xhci/data_structures/mod.rs | 8 + .../src/xhci/data_structures/slot_context.rs | 133 ++++++++++ .../src/xhci/data_structures/trb_ring.rs | 113 +++++++++ rust/sys/voyageurs/src/xhci/mod.rs | 1 + 8 files changed, 560 insertions(+), 4 deletions(-) create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/event_ring_segment_table.rs create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/mod.rs create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/trb_ring.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 35fccae..aec4469 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -19,6 +19,17 @@ dependencies = [ "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]] name = "convert_case" version = "0.6.0" @@ -32,7 +43,7 @@ dependencies = [ name = "denali" version = "0.1.0" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.8.0", "mammoth", "pci", "yellowstone-yunq", @@ -88,7 +99,7 @@ dependencies = [ name = "pci" version = "0.1.0" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.8.0", "mammoth", ] @@ -193,7 +204,7 @@ dependencies = [ name = "voyageurs" version = "0.1.0" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.12.1", "mammoth", ] diff --git a/rust/sys/voyageurs/Cargo.toml b/rust/sys/voyageurs/Cargo.toml index 492a285..084d083 100644 --- a/rust/sys/voyageurs/Cargo.toml +++ b/rust/sys/voyageurs/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] -bitfield-struct = "0.8.0" +bitfield-struct = "0.12" mammoth = { path = "../../lib/mammoth/" } diff --git a/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs b/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs new file mode 100644 index 0000000..0c62428 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs @@ -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 Endpoint’s 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::() == 0x20); diff --git a/rust/sys/voyageurs/src/xhci/data_structures/event_ring_segment_table.rs b/rust/sys/voyageurs/src/xhci/data_structures/event_ring_segment_table.rs new file mode 100644 index 0000000..89ce405 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/event_ring_segment_table.rs @@ -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 for EventRingSegmentTable { + type Output = EventRingSegmentTableEntry; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl IndexMut for EventRingSegmentTable { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } +} diff --git a/rust/sys/voyageurs/src/xhci/data_structures/mod.rs b/rust/sys/voyageurs/src/xhci/data_structures/mod.rs new file mode 100644 index 0000000..781cbc1 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/mod.rs @@ -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::*; diff --git a/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs b/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs new file mode 100644 index 0000000..ab6df95 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs @@ -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::() == 0x20); diff --git a/rust/sys/voyageurs/src/xhci/data_structures/trb_ring.rs b/rust/sys/voyageurs/src/xhci/data_structures/trb_ring.rs new file mode 100644 index 0000000..2ded382 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/trb_ring.rs @@ -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, + { + trb.into_bits().into() + } + + fn to_trb(self) -> TransferRequestBlock + where + Self: Into, + { + Into::::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 Index for TrbRing +where + I: SliceIndex<[TransferRequestBlock]>, +{ + type Output = I::Output; + + fn index(&self, index: I) -> &Self::Output { + &self.0[index] + } +} + +impl IndexMut for TrbRing +where + I: SliceIndex<[TransferRequestBlock]>, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.0[index] + } +} diff --git a/rust/sys/voyageurs/src/xhci/mod.rs b/rust/sys/voyageurs/src/xhci/mod.rs index 8993d24..008e36e 100644 --- a/rust/sys/voyageurs/src/xhci/mod.rs +++ b/rust/sys/voyageurs/src/xhci/mod.rs @@ -1 +1,2 @@ +pub mod data_structures; pub mod registers; From 3d0db07a5df21a0a8591b60fc9e93994e2d60da2 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Fri, 5 Dec 2025 22:01:13 -0800 Subject: [PATCH 06/12] Rust XHCI Implementation. --- ' | 64 +++++ rust/Cargo.lock | 37 ++- rust/lib/pci/src/device.rs | 10 +- rust/sys/voyageurs/Cargo.toml | 6 + rust/sys/voyageurs/src/main.rs | 20 +- .../src/xhci/device_context_base_array.rs | 22 ++ rust/sys/voyageurs/src/xhci/driver.rs | 227 ++++++++++++++++++ rust/sys/voyageurs/src/xhci/event_ring.rs | 25 ++ rust/sys/voyageurs/src/xhci/mod.rs | 8 +- rust/sys/voyageurs/src/xhci/trb_ring.rs | 49 ++++ 10 files changed, 438 insertions(+), 30 deletions(-) create mode 100644 ' create mode 100644 rust/sys/voyageurs/src/xhci/device_context_base_array.rs create mode 100644 rust/sys/voyageurs/src/xhci/driver.rs create mode 100644 rust/sys/voyageurs/src/xhci/event_ring.rs create mode 100644 rust/sys/voyageurs/src/xhci/trb_ring.rs diff --git a/' b/' new file mode 100644 index 0000000..8a445e9 --- /dev/null +++ b/' @@ -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 Index for TrbRing { + type Output = TransferRequestBlock; + + fn index(&self, index: I) -> &Self::Output { + self.0[index] + } +} + +impl IndexMut for TrbRing +where + I: SliceIndex<[TransferRequestBlock]>, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.0[index] + } +} diff --git a/rust/Cargo.lock b/rust/Cargo.lock index aec4469..ced63d5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - [[package]] name = "bitfield-struct" version = "0.8.0" @@ -80,11 +74,10 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] @@ -105,9 +98,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", @@ -115,18 +108,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -148,9 +141,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -178,15 +171,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "victoriafalls" @@ -206,6 +199,8 @@ version = "0.1.0" dependencies = [ "bitfield-struct 0.12.1", "mammoth", + "pci", + "yellowstone-yunq", ] [[package]] diff --git a/rust/lib/pci/src/device.rs b/rust/lib/pci/src/device.rs index ab1b25d..948cd04 100644 --- a/rust/lib/pci/src/device.rs +++ b/rust/lib/pci/src/device.rs @@ -72,15 +72,15 @@ impl PciDevice { control.capable_address_64(), "We don't handle the non-64bit case for MSI yet." ); - assert!( - control.multi_message_capable() == 0, - "We don't yet handle multi-message capable devices." - ); + + if control.multi_message_capable() != 0 { + mammoth::debug!("WARN: We don't yet handle multi-message capable devices."); + } // FIXME: These probably need to be volatile writes. let header: &mut PciDeviceHeader = self.memory_region.as_mut(); 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 // Vol 3. Section 11.11 diff --git a/rust/sys/voyageurs/Cargo.toml b/rust/sys/voyageurs/Cargo.toml index 084d083..ddd2996 100644 --- a/rust/sys/voyageurs/Cargo.toml +++ b/rust/sys/voyageurs/Cargo.toml @@ -6,3 +6,9 @@ edition = "2024" [dependencies] bitfield-struct = "0.12" mammoth = { path = "../../lib/mammoth/" } +pci = { path = "../../lib/pci" } +yellowstone-yunq = { version = "0.1.0", path = "../../lib/yellowstone" } + +[features] +default = ["debug"] +debug = [] diff --git a/rust/sys/voyageurs/src/main.rs b/rust/sys/voyageurs/src/main.rs index e9e5dd6..adf75de 100644 --- a/rust/sys/voyageurs/src/main.rs +++ b/rust/sys/voyageurs/src/main.rs @@ -5,12 +5,28 @@ extern crate alloc; mod xhci; -use mammoth::{debug, define_entry, zion::z_err_t}; +use mammoth::{cap::Capability, debug, define_entry, zion::z_err_t}; +use pci::PciDevice; +use xhci::driver::XHCIDriver; define_entry!(); #[unsafe(no_mangle)] extern "C" fn main() -> z_err_t { - debug!("In Voyageurs"); + #[cfg(feature = "debug")] + debug!("Voyageurs Starting."); + + let yellowstone = yellowstone_yunq::from_init_endpoint(); + + let xhci_info = yellowstone + .get_xhci_info() + .expect("Failed to get XHCI info from yellowstone."); + + let pci_device = PciDevice::from_cap(Capability::take(xhci_info.xhci_region)).unwrap(); + + let xhci_driver = XHCIDriver::from_pci_device(pci_device); + + loop {} + 0 } diff --git a/rust/sys/voyageurs/src/xhci/device_context_base_array.rs b/rust/sys/voyageurs/src/xhci/device_context_base_array.rs new file mode 100644 index 0000000..efddd4e --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/device_context_base_array.rs @@ -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 + } +} diff --git a/rust/sys/voyageurs/src/xhci/driver.rs b/rust/sys/voyageurs/src/xhci/driver.rs new file mode 100644 index 0000000..0482e6d --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/driver.rs @@ -0,0 +1,227 @@ +use core::slice; + +use mammoth::{map_unaligned_volatile, mem::MemoryRegion}; +use mammoth::{read_unaligned_volatile, write_unaligned_volatile}; + +use super::registers::{self}; +use crate::xhci::device_context_base_array::DeviceContextBaseArray; +use crate::xhci::event_ring::EventRing; +use crate::xhci::registers::HCSParams1; +use crate::xhci::trb_ring::{InputTrbRing, OutputTrbRing}; + +pub struct XHCIDriver { + #[allow(dead_code)] + pci_device: pci::PciDevice, + registers_region: MemoryRegion, + command_ring: OutputTrbRing, + event_ring: EventRing, + device_context_base_array: DeviceContextBaseArray, +} + +impl XHCIDriver { + pub fn from_pci_device(mut pci_device: pci::PciDevice) -> Self { + // BAR0 Size Allocation + // If virtualization is supported, the Capability and Operational Register sets, and + // the Extended Capabilities may reside in a single page of virtual memory, + // however the RTSOFF and DBOFF Registers shall position the Runtime and + // Doorbell Registers to reside on their own respective virtual memory pages. The + // BAR0 size shall provide space that is sufficient to cover the offset between the + // respective register spaces (Capability, Operational, Runtime, etc.) and the + // register spaces themselves (e.g. a minimum of 3 virtual memory pages). + // If virtualization is not supported, all xHCI register spaces may reside on a single + // page pointed to by the BAR0. + let three_pages = 0x3000; + let address = + ((pci_device.header().bars[1] as u64) << 32) | (pci_device.header().bars[0] as u64); + let registers_region = MemoryRegion::direct_physical(address, three_pages).unwrap(); + + let port_cap = pci_device.register_msi().unwrap(); + + let driver = Self { + pci_device, + registers_region, + command_ring: OutputTrbRing::new(), + event_ring: EventRing::new(), + device_context_base_array: DeviceContextBaseArray::new(), + }; + driver.reset(); + driver + } + + fn capabilities(&self) -> ®isters::HostControllerCapabilities { + self.registers_region.as_ref() + } + + fn operational(&self) -> &mut registers::HostControllerOperational { + let cap_length: registers::HostControllerCapabilitiesLengthAndVersion = + read_unaligned_volatile!(self.capabilities(), cap_length_and_version); + let offset = cap_length.cap_length(); + + // TODO: Ensure exclusive access. + unsafe { self.registers_region.as_mut_ref_at_offset(offset as usize) } + } + + fn interrupters(&self) -> &[registers::InterrupterRegisterSet] { + // See Table 5-35: Host Controller Runtime Registers + const INTERRUPTER_OFFSET_FROM_RUNTIME: u32 = 0x20; + let runtime = read_unaligned_volatile!(self.capabilities(), runtime_register_space_offset); + + let interrupter_offset: usize = (runtime + INTERRUPTER_OFFSET_FROM_RUNTIME) as usize; + + let params1: registers::HCSParams1 = + read_unaligned_volatile!(self.capabilities(), params_1); + + // SAFETY: The XHCI spec says so? + unsafe { + slice::from_raw_parts( + self.registers_region + .raw_ptr_at_offset(interrupter_offset as u64), + params1.max_interrupters() as usize, + ) + } + } + + fn doorbells(&self) -> &[registers::Doorbell] { + let doorbell_offset = read_unaligned_volatile!(self.capabilities(), doorbell_offset); + + let params1: registers::HCSParams1 = + read_unaligned_volatile!(self.capabilities(), params_1); + + // SAFETY: The XHCI spec says so? + unsafe { + slice::from_raw_parts( + self.registers_region + .raw_ptr_at_offset(doorbell_offset as u64), + params1.max_device_slots() as usize, + ) + } + } + + fn reset(&self) { + #[cfg(feature = "debug")] + mammoth::debug!("Stopping XHCI Controller."); + + // Stop the host controller. + // TODO: Make this volatile. + self.operational().usb_command = self.operational().usb_command.with_run_stop(false); + + #[cfg(feature = "debug")] + mammoth::debug!("Waiting for controller to halt."); + + // Sleep until the controller is halted. + let mut status: registers::UsbStatus = + read_unaligned_volatile!(self.operational(), usb_status); + while !status.host_controller_halted() { + // TODO: Sleep for how long? + mammoth::syscall::thread_sleep(50).unwrap(); + status = read_unaligned_volatile!(self.operational(), usb_status); + } + + #[cfg(feature = "debug")] + mammoth::debug!("Resetting Controller."); + + map_unaligned_volatile!( + self.operational(), + usb_command, + |c: registers::UsbCommand| c.with_host_controller_reset(true) + ); + + let mut command: registers::UsbCommand = + read_unaligned_volatile!(self.operational(), usb_command); + while command.host_controller_reset() { + // TODO: Sleep for how long? + mammoth::syscall::thread_sleep(50).unwrap(); + command = read_unaligned_volatile!(self.operational(), usb_command); + } + + #[cfg(feature = "debug")] + mammoth::debug!("XHCI Controller Reset, waiting ready."); + + status = read_unaligned_volatile!(self.operational(), usb_status); + while status.controller_not_ready() { + // TODO: Sleep for how long? + mammoth::syscall::thread_sleep(50).unwrap(); + status = read_unaligned_volatile!(self.operational(), usb_status); + } + + #[cfg(feature = "debug")] + mammoth::debug!("XHCI Controller Ready."); + + #[cfg(feature = "debug")] + mammoth::debug!("Setting Command Ring"); + + // TODO: We should reset the command ring here. + write_unaligned_volatile!( + self.operational(), + command_ring_control, + self.command_ring.physical_addr() + ); + + #[cfg(feature = "debug")] + mammoth::debug!("Setting DCBA."); + + write_unaligned_volatile!( + self.operational(), + device_context_base_address_array_pointer, + self.device_context_base_array.physical_addr() + ); + + // We tell the controller that we can support as many slots as it does because + // we allocate a full 4K page to the DCBA, which is 256 entries and the max + // slots are 255. + let params1: registers::HCSParams1 = + read_unaligned_volatile!(self.capabilities(), params_1); + map_unaligned_volatile!( + self.operational(), + configure, + |c: registers::UsbConfigure| c + .with_max_device_slots_enabled(params1.max_device_slots()) + ); + + let params2: registers::HCSParams2 = + read_unaligned_volatile!(self.capabilities(), params_2); + + let max_scratchpad_buffers = + (params2.max_scratchpad_buffers_hi() << 5) | params2.max_scratchpad_buffers_lo(); + assert!( + max_scratchpad_buffers == 0, + "Unsupported scratchpad buffers." + ); + + #[cfg(feature = "debug")] + mammoth::debug!("Setting up initial event ring."); + + let interrupter0 = &self.interrupters()[0]; + write_unaligned_volatile!( + interrupter0, + event_ring_segment_table_base_address, + self.event_ring.segment_table().physical_address() + ); + write_unaligned_volatile!( + interrupter0, + event_ring_segement_table_size, + self.event_ring.segment_table().len() + ); + write_unaligned_volatile!( + interrupter0, + interrupter_moderation, + registers::InterrupterModeration::new() + .with_interrupt_moderation_interval(4000) + .with_interrupt_moderation_counter(0) + ); + write_unaligned_volatile!( + interrupter0, + interrupter_management, + registers::InterrupterManagement::new().with_interrupt_enabled(true) + ); + + map_unaligned_volatile!( + self.operational(), + usb_command, + |c: registers::UsbCommand| c.with_run_stop(true) + ); + + #[cfg(feature = "debug")] + mammoth::debug!("Enabled interrupts and controller."); + } +} diff --git a/rust/sys/voyageurs/src/xhci/event_ring.rs b/rust/sys/voyageurs/src/xhci/event_ring.rs new file mode 100644 index 0000000..c6d2fe5 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/event_ring.rs @@ -0,0 +1,25 @@ +use alloc::vec::Vec; + +use crate::xhci::data_structures::{EventRingSegmentTable, TrbRing}; + +pub struct EventRing { + segment_table: EventRingSegmentTable, + segments: Vec, +} + +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 + } +} diff --git a/rust/sys/voyageurs/src/xhci/mod.rs b/rust/sys/voyageurs/src/xhci/mod.rs index 008e36e..64bc6a9 100644 --- a/rust/sys/voyageurs/src/xhci/mod.rs +++ b/rust/sys/voyageurs/src/xhci/mod.rs @@ -1,2 +1,6 @@ -pub mod data_structures; -pub mod registers; +mod data_structures; +mod device_context_base_array; +pub mod driver; +mod event_ring; +mod registers; +mod trb_ring; diff --git a/rust/sys/voyageurs/src/xhci/trb_ring.rs b/rust/sys/voyageurs/src/xhci/trb_ring.rs new file mode 100644 index 0000000..116b8ea --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/trb_ring.rs @@ -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 + } +} From 6692a3e2c01d71b5f8b870b629ee03e0ec681f7d Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Sun, 18 May 2025 19:11:52 -0700 Subject: [PATCH 07/12] Fix build image. --- rust-toolchain.toml | 3 +++ rust/rust-toolchain.toml | 2 -- rust/x86_64-acadia-os.json | 4 ++-- scripts/build_image.sh | 10 +++++----- 4 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 rust-toolchain.toml delete mode 100644 rust/rust-toolchain.toml diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..66768f4 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2025-10-02" +components = ["rustfmt", "rust-analyzer"] diff --git a/rust/rust-toolchain.toml b/rust/rust-toolchain.toml deleted file mode 100644 index 5d56faf..0000000 --- a/rust/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "nightly" diff --git a/rust/x86_64-acadia-os.json b/rust/x86_64-acadia-os.json index 6e6dec1..1060335 100644 --- a/rust/x86_64-acadia-os.json +++ b/rust/x86_64-acadia-os.json @@ -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", "arch": "x86_64", "target-endian": "little", - "target-pointer-width": "64", - "target-c-int-width": "32", + "target-pointer-width": 64, + "target-c-int-width": 32, "os": "none", "executables": true, "linker-flavor": "ld.lld", diff --git a/scripts/build_image.sh b/scripts/build_image.sh index 2fd5867..9b39927 100644 --- a/scripts/build_image.sh +++ b/scripts/build_image.sh @@ -31,15 +31,15 @@ cleanup() { } trap cleanup EXIT -parted -s $dev mklabel gpt mkpart EFI fat32 1MiB 10MiB mkpart ext2 10MiB 100% set 1 esp on -mkfs.fat -F 12 "${dev}p1" -mke2fs "${dev}p2" +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}p2" +mke2fs "${dev}p3" limine bios-install "${dev}" mkdir -p $EFI_DIR -mount "${dev}p1" $EFI_DIR +mount "${dev}p2" $EFI_DIR mkdir -p $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 mkdir -p $SYSROOT -mount "${dev}p2" $SYSROOT +mount "${dev}p3" $SYSROOT rsync -a "$REPO_ROOT/sysroot" $BUILD_DIR ls $SYSROOT From e5a943ae81246dfc5bf24ec94b8e0f71a48f92bb Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Sun, 18 May 2025 19:11:52 -0700 Subject: [PATCH 08/12] Rust XHCI scaffolding --- rust/Cargo.lock | 10 ++++++-- rust/Cargo.toml | 25 +++++++++++-------- rust/lib/client/voyageurs_client/Cargo.toml | 12 +++++++++ .../voyageurs_client}/build.rs | 2 +- .../voyageurs_client}/src/lib.rs | 0 .../voyageurs_client}/src/listener.rs | 0 rust/lib/voyageurs/Cargo.toml | 13 ---------- rust/sys/teton/Cargo.toml | 2 +- rust/sys/teton/src/main.rs | 2 +- rust/sys/teton/src/terminal.rs | 2 +- rust/sys/voyageurs/Cargo.toml | 7 ++++++ rust/sys/voyageurs/src/main.rs | 14 +++++++++++ rust/sys/yellowstone/Cargo.toml | 1 - 13 files changed, 59 insertions(+), 31 deletions(-) create mode 100644 rust/lib/client/voyageurs_client/Cargo.toml rename rust/lib/{voyageurs => client/voyageurs_client}/build.rs (82%) rename rust/lib/{voyageurs => client/voyageurs_client}/src/lib.rs (100%) rename rust/lib/{voyageurs => client/voyageurs_client}/src/listener.rs (100%) delete mode 100644 rust/lib/voyageurs/Cargo.toml create mode 100644 rust/sys/voyageurs/Cargo.toml create mode 100644 rust/sys/voyageurs/src/main.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f20f302..432d9ba 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -161,7 +161,7 @@ version = "0.1.0" dependencies = [ "mammoth", "victoriafalls", - "voyageurs", + "voyageurs_client", "yellowstone-yunq", ] @@ -192,6 +192,13 @@ dependencies = [ [[package]] name = "voyageurs" version = "0.1.0" +dependencies = [ + "mammoth", +] + +[[package]] +name = "voyageurs_client" +version = "0.1.0" dependencies = [ "mammoth", "yellowstone-yunq", @@ -206,7 +213,6 @@ dependencies = [ "denali_client", "mammoth", "victoriafalls", - "voyageurs", "yellowstone-yunq", "yunq", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index f1886f6..b31a682 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,16 +1,19 @@ [workspace] members = [ - "lib/client/denali_client", "lib/fs/ext2", - "lib/mammoth", "lib/pci", - "lib/voyageurs", - "lib/yellowstone", - "lib/yunq", - "lib/yunq-test", - "sys/denali", - "sys/teton", - "sys/victoriafalls", - "sys/yellowstone", - "usr/testbed", + "lib/client/denali_client", + "lib/client/voyageurs_client", + "lib/fs/ext2", + "lib/mammoth", + "lib/pci", + "lib/yellowstone", + "lib/yunq", + "lib/yunq-test", + "sys/denali", + "sys/teton", + "sys/victoriafalls", + "sys/voyageurs", + "sys/yellowstone", + "usr/testbed", ] resolver = "2" diff --git a/rust/lib/client/voyageurs_client/Cargo.toml b/rust/lib/client/voyageurs_client/Cargo.toml new file mode 100644 index 0000000..e3e432c --- /dev/null +++ b/rust/lib/client/voyageurs_client/Cargo.toml @@ -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" } diff --git a/rust/lib/voyageurs/build.rs b/rust/lib/client/voyageurs_client/build.rs similarity index 82% rename from rust/lib/voyageurs/build.rs rename to rust/lib/client/voyageurs_client/build.rs index 52b5dfc..f6f76ad 100644 --- a/rust/lib/voyageurs/build.rs +++ b/rust/lib/client/voyageurs_client/build.rs @@ -1,7 +1,7 @@ use std::fs; 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}"); diff --git a/rust/lib/voyageurs/src/lib.rs b/rust/lib/client/voyageurs_client/src/lib.rs similarity index 100% rename from rust/lib/voyageurs/src/lib.rs rename to rust/lib/client/voyageurs_client/src/lib.rs diff --git a/rust/lib/voyageurs/src/listener.rs b/rust/lib/client/voyageurs_client/src/listener.rs similarity index 100% rename from rust/lib/voyageurs/src/listener.rs rename to rust/lib/client/voyageurs_client/src/listener.rs diff --git a/rust/lib/voyageurs/Cargo.toml b/rust/lib/voyageurs/Cargo.toml deleted file mode 100644 index e011e0c..0000000 --- a/rust/lib/voyageurs/Cargo.toml +++ /dev/null @@ -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"} - diff --git a/rust/sys/teton/Cargo.toml b/rust/sys/teton/Cargo.toml index baa787a..3df98f4 100644 --- a/rust/sys/teton/Cargo.toml +++ b/rust/sys/teton/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" [dependencies] mammoth = { path = "../../lib/mammoth" } victoriafalls = { path = "../victoriafalls" } -voyageurs = { path = "../../lib/voyageurs" } +voyageurs_client = { path = "../../lib/client/voyageurs_client/" } yellowstone-yunq = { path = "../../lib/yellowstone" } diff --git a/rust/sys/teton/src/main.rs b/rust/sys/teton/src/main.rs index 982beb7..b44dff6 100644 --- a/rust/sys/teton/src/main.rs +++ b/rust/sys/teton/src/main.rs @@ -9,7 +9,7 @@ mod psf; mod terminal; use mammoth::{debug, define_entry, zion::z_err_t}; -use voyageurs::listener; +use voyageurs_client::listener; define_entry!(); diff --git a/rust/sys/teton/src/terminal.rs b/rust/sys/teton/src/terminal.rs index 5c7d314..2cd46ce 100644 --- a/rust/sys/teton/src/terminal.rs +++ b/rust/sys/teton/src/terminal.rs @@ -6,7 +6,7 @@ use alloc::{ string::{String, ToString}, }; use victoriafalls::dir; -use voyageurs::listener::KeyboardHandler; +use voyageurs_client::listener::KeyboardHandler; pub struct Terminal { console: Console, diff --git a/rust/sys/voyageurs/Cargo.toml b/rust/sys/voyageurs/Cargo.toml new file mode 100644 index 0000000..e44085b --- /dev/null +++ b/rust/sys/voyageurs/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "voyageurs" +version = "0.1.0" +edition = "2024" + +[dependencies] +mammoth = { path = "../../lib/mammoth/" } diff --git a/rust/sys/voyageurs/src/main.rs b/rust/sys/voyageurs/src/main.rs new file mode 100644 index 0000000..bcbd530 --- /dev/null +++ b/rust/sys/voyageurs/src/main.rs @@ -0,0 +1,14 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use mammoth::{debug, define_entry, zion::z_err_t}; + +define_entry!(); + +#[unsafe(no_mangle)] +extern "C" fn main() -> z_err_t { + debug!("In Voyageurs"); + 0 +} diff --git a/rust/sys/yellowstone/Cargo.toml b/rust/sys/yellowstone/Cargo.toml index 6a89d4d..70e9995 100644 --- a/rust/sys/yellowstone/Cargo.toml +++ b/rust/sys/yellowstone/Cargo.toml @@ -7,6 +7,5 @@ edition = "2021" mammoth = { path = "../../lib/mammoth" } denali_client = { path = "../../lib/client/denali_client" } victoriafalls = { path = "../victoriafalls" } -voyageurs = { path = "../../lib/voyageurs" } yellowstone-yunq = { path = "../../lib/yellowstone" } yunq = { path = "../../lib/yunq" } From 3020b28c47b6fc7454281fe95f1c4957d039fc15 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Sun, 18 May 2025 19:26:06 -0700 Subject: [PATCH 09/12] Rust XHCI Register Types. --- rust/Cargo.lock | 1 + rust/sys/voyageurs/Cargo.toml | 1 + rust/sys/voyageurs/src/main.rs | 2 + rust/sys/voyageurs/src/xhci/mod.rs | 1 + .../src/xhci/registers/capabilities.rs | 337 +++++++++++++++ .../voyageurs/src/xhci/registers/doorbell.rs | 65 +++ .../src/xhci/registers/host_controller.rs | 298 +++++++++++++ .../xhci/registers/host_controller_port.rs | 395 ++++++++++++++++++ .../src/xhci/registers/interrupter.rs | 116 +++++ rust/sys/voyageurs/src/xhci/registers/mod.rs | 15 + 10 files changed, 1231 insertions(+) create mode 100644 rust/sys/voyageurs/src/xhci/mod.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/capabilities.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/doorbell.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/host_controller.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/host_controller_port.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/interrupter.rs create mode 100644 rust/sys/voyageurs/src/xhci/registers/mod.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 432d9ba..35fccae 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -193,6 +193,7 @@ dependencies = [ name = "voyageurs" version = "0.1.0" dependencies = [ + "bitfield-struct", "mammoth", ] diff --git a/rust/sys/voyageurs/Cargo.toml b/rust/sys/voyageurs/Cargo.toml index e44085b..492a285 100644 --- a/rust/sys/voyageurs/Cargo.toml +++ b/rust/sys/voyageurs/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +bitfield-struct = "0.8.0" mammoth = { path = "../../lib/mammoth/" } diff --git a/rust/sys/voyageurs/src/main.rs b/rust/sys/voyageurs/src/main.rs index bcbd530..e9e5dd6 100644 --- a/rust/sys/voyageurs/src/main.rs +++ b/rust/sys/voyageurs/src/main.rs @@ -3,6 +3,8 @@ extern crate alloc; +mod xhci; + use mammoth::{debug, define_entry, zion::z_err_t}; define_entry!(); diff --git a/rust/sys/voyageurs/src/xhci/mod.rs b/rust/sys/voyageurs/src/xhci/mod.rs new file mode 100644 index 0000000..8993d24 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/mod.rs @@ -0,0 +1 @@ +pub mod registers; diff --git a/rust/sys/voyageurs/src/xhci/registers/capabilities.rs b/rust/sys/voyageurs/src/xhci/registers/capabilities.rs new file mode 100644 index 0000000..751fb9c --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/capabilities.rs @@ -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::() == 0x20); diff --git a/rust/sys/voyageurs/src/xhci/registers/doorbell.rs b/rust/sys/voyageurs/src/xhci/registers/doorbell.rs new file mode 100644 index 0000000..af59cc2 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/doorbell.rs @@ -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, +} diff --git a/rust/sys/voyageurs/src/xhci/registers/host_controller.rs b/rust/sys/voyageurs/src/xhci/registers/host_controller.rs new file mode 100644 index 0000000..db609af --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/host_controller.rs @@ -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 VF’s 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::() == 0x3C); diff --git a/rust/sys/voyageurs/src/xhci/registers/host_controller_port.rs b/rust/sys/voyageurs/src/xhci/registers/host_controller_port.rs new file mode 100644 index 0000000..369c4dc --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/host_controller_port.rs @@ -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 port’s 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. + /// 80h–FEh 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::() == 0x10); diff --git a/rust/sys/voyageurs/src/xhci/registers/interrupter.rs b/rust/sys/voyageurs/src/xhci/registers/interrupter.rs new file mode 100644 index 0000000..69fc188 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/interrupter.rs @@ -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, +} diff --git a/rust/sys/voyageurs/src/xhci/registers/mod.rs b/rust/sys/voyageurs/src/xhci/registers/mod.rs new file mode 100644 index 0000000..37acf46 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/registers/mod.rs @@ -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::*; From 9ce13a85b69b425c0507670f1bacd91f9fdf361c Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Fri, 5 Dec 2025 21:15:00 -0800 Subject: [PATCH 10/12] Physical Memory Management Improvements. --- rust/lib/mammoth/src/elf.rs | 4 +- rust/lib/mammoth/src/lib.rs | 1 + rust/lib/mammoth/src/mem.rs | 94 ++++++++++++++++--- rust/lib/mammoth/src/physical_box.rs | 131 +++++++++++++++++++++++++++ 4 files changed, 212 insertions(+), 18 deletions(-) create mode 100644 rust/lib/mammoth/src/physical_box.rs diff --git a/rust/lib/mammoth/src/elf.rs b/rust/lib/mammoth/src/elf.rs index 2b8638f..dd33876 100644 --- a/rust/lib/mammoth/src/elf.rs +++ b/rust/lib/mammoth/src/elf.rs @@ -239,9 +239,7 @@ fn load_program_segment( let mem_object = crate::mem::MemoryRegion::new(mem_size)?; - for i in mem_object.mut_slice() { - *i = 0; - } + mem_object.zero_region(); let file_start = prog_header.offset as usize; let file_end = file_start + prog_header.file_size as usize; diff --git a/rust/lib/mammoth/src/lib.rs b/rust/lib/mammoth/src/lib.rs index 2058a6b..ce4f915 100644 --- a/rust/lib/mammoth/src/lib.rs +++ b/rust/lib/mammoth/src/lib.rs @@ -13,6 +13,7 @@ mod cap_syscall; pub mod elf; pub mod init; pub mod mem; +pub mod physical_box; pub mod port; pub mod sync; pub mod syscall; diff --git a/rust/lib/mammoth/src/mem.rs b/rust/lib/mammoth/src/mem.rs index 8d5af40..7b81806 100644 --- a/rust/lib/mammoth/src/mem.rs +++ b/rust/lib/mammoth/src/mem.rs @@ -3,7 +3,8 @@ use crate::syscall; use crate::zion::ZError; use alloc::slice; 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")] use linked_list_allocator::LockedHeap; @@ -29,6 +30,7 @@ pub fn init_heap() { pub struct MemoryRegion { mem_cap: Capability, virt_addr: u64, + // TODO: This should be a usize probably. 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(&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::() as u64 <= self.size); (self.virt_addr + offset) as *const T } + pub fn mut_ptr_at_offset(&self, offset: usize) -> *mut T { + assert!(offset + size_of::() <= 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(&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::(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(&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::(offset) + } + pub fn cap(&self) -> &Capability { &self.mem_cap } @@ -137,28 +176,22 @@ impl Drop for MemoryRegion { } } -pub struct Volatile { - /// TODO: This should maybe be MaybeUninit. - data: T, -} +#[repr(transparent)] +pub struct Volatile(T); -impl Volatile { - pub fn read(&self) -> T - where - T: Copy, - { - unsafe { addr_of!(self.data).cast::().read_volatile() } +impl Volatile { + pub fn read(&self) -> T { + unsafe { read_volatile(addr_of!(self.0)) } } pub fn write(&mut self, data: T) { unsafe { - addr_of_mut!(self.data).cast::().write_volatile(data); + write_volatile(addr_of_mut!(self.0), data); } } pub fn update(&mut self, func: F) where - T: Copy, F: Fn(&mut T), { 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 { let vaddr = syscall::address_space_map(&mem_cap).unwrap(); mem_cap.release(); diff --git a/rust/lib/mammoth/src/physical_box.rs b/rust/lib/mammoth/src/physical_box.rs new file mode 100644 index 0000000..692eed1 --- /dev/null +++ b/rust/lib/mammoth/src/physical_box.rs @@ -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 { + data: NonNull, + region: MemoryRegion, + physical_address: usize, + _marker: PhantomData, +} + +impl PhysicalBox { + pub fn physical_address(&self) -> usize { + self.physical_address + } +} + +impl Deref for PhysicalBox { + 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 DerefMut for PhysicalBox { + 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 PhysicalBox<[T]> { + pub fn default_with_count(default: T, len: usize) -> Self + where + T: Clone, + { + let layout = core::alloc::Layout::array::(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) -> Self { + let len = vec.len(); + let layout = core::alloc::Layout::array::(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 Index for PhysicalBox<[T]> +where + I: slice::SliceIndex<[T]>, +{ + type Output = I::Output; + + fn index(&self, index: I) -> &Self::Output { + &(**self)[index] + } +} + +impl IndexMut for PhysicalBox<[T]> +where + I: slice::SliceIndex<[T]>, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut (**self)[index] + } +} + +impl Drop for PhysicalBox { + fn drop(&mut self) { + // SAFETY: + // - We own this data. + unsafe { core::ptr::drop_in_place(self.data.as_ptr()) } + } +} From 0b950987484c39d44e144b9c20765fd5cea6114a Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Fri, 5 Dec 2025 21:15:00 -0800 Subject: [PATCH 11/12] Rust XHCI Data Structure Types. --- rust/Cargo.lock | 17 +- rust/sys/voyageurs/Cargo.toml | 2 +- .../xhci/data_structures/endpoint_context.rs | 228 ++++++++++++++++++ .../event_ring_segment_table.rs | 62 +++++ .../voyageurs/src/xhci/data_structures/mod.rs | 8 + .../src/xhci/data_structures/slot_context.rs | 133 ++++++++++ .../src/xhci/data_structures/trb_ring.rs | 113 +++++++++ rust/sys/voyageurs/src/xhci/mod.rs | 1 + 8 files changed, 560 insertions(+), 4 deletions(-) create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/event_ring_segment_table.rs create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/mod.rs create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs create mode 100644 rust/sys/voyageurs/src/xhci/data_structures/trb_ring.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 35fccae..aec4469 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -19,6 +19,17 @@ dependencies = [ "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]] name = "convert_case" version = "0.6.0" @@ -32,7 +43,7 @@ dependencies = [ name = "denali" version = "0.1.0" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.8.0", "mammoth", "pci", "yellowstone-yunq", @@ -88,7 +99,7 @@ dependencies = [ name = "pci" version = "0.1.0" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.8.0", "mammoth", ] @@ -193,7 +204,7 @@ dependencies = [ name = "voyageurs" version = "0.1.0" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.12.1", "mammoth", ] diff --git a/rust/sys/voyageurs/Cargo.toml b/rust/sys/voyageurs/Cargo.toml index 492a285..084d083 100644 --- a/rust/sys/voyageurs/Cargo.toml +++ b/rust/sys/voyageurs/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] -bitfield-struct = "0.8.0" +bitfield-struct = "0.12" mammoth = { path = "../../lib/mammoth/" } diff --git a/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs b/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs new file mode 100644 index 0000000..0c62428 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/endpoint_context.rs @@ -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 Endpoint’s 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::() == 0x20); diff --git a/rust/sys/voyageurs/src/xhci/data_structures/event_ring_segment_table.rs b/rust/sys/voyageurs/src/xhci/data_structures/event_ring_segment_table.rs new file mode 100644 index 0000000..89ce405 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/event_ring_segment_table.rs @@ -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 for EventRingSegmentTable { + type Output = EventRingSegmentTableEntry; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl IndexMut for EventRingSegmentTable { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } +} diff --git a/rust/sys/voyageurs/src/xhci/data_structures/mod.rs b/rust/sys/voyageurs/src/xhci/data_structures/mod.rs new file mode 100644 index 0000000..781cbc1 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/mod.rs @@ -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::*; diff --git a/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs b/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs new file mode 100644 index 0000000..ab6df95 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/slot_context.rs @@ -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::() == 0x20); diff --git a/rust/sys/voyageurs/src/xhci/data_structures/trb_ring.rs b/rust/sys/voyageurs/src/xhci/data_structures/trb_ring.rs new file mode 100644 index 0000000..2ded382 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/data_structures/trb_ring.rs @@ -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, + { + trb.into_bits().into() + } + + fn to_trb(self) -> TransferRequestBlock + where + Self: Into, + { + Into::::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 Index for TrbRing +where + I: SliceIndex<[TransferRequestBlock]>, +{ + type Output = I::Output; + + fn index(&self, index: I) -> &Self::Output { + &self.0[index] + } +} + +impl IndexMut for TrbRing +where + I: SliceIndex<[TransferRequestBlock]>, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.0[index] + } +} diff --git a/rust/sys/voyageurs/src/xhci/mod.rs b/rust/sys/voyageurs/src/xhci/mod.rs index 8993d24..008e36e 100644 --- a/rust/sys/voyageurs/src/xhci/mod.rs +++ b/rust/sys/voyageurs/src/xhci/mod.rs @@ -1 +1,2 @@ +pub mod data_structures; pub mod registers; From a10c615b95c6b0c360eb4c70749cbf98071bc550 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Fri, 5 Dec 2025 22:01:13 -0800 Subject: [PATCH 12/12] Rust XHCI Implementation. --- ' | 64 +++++ rust/Cargo.lock | 37 ++- rust/lib/pci/src/device.rs | 10 +- rust/sys/voyageurs/Cargo.toml | 6 + rust/sys/voyageurs/src/main.rs | 20 +- .../src/xhci/device_context_base_array.rs | 22 ++ rust/sys/voyageurs/src/xhci/driver.rs | 227 ++++++++++++++++++ rust/sys/voyageurs/src/xhci/event_ring.rs | 25 ++ rust/sys/voyageurs/src/xhci/mod.rs | 8 +- rust/sys/voyageurs/src/xhci/trb_ring.rs | 49 ++++ 10 files changed, 438 insertions(+), 30 deletions(-) create mode 100644 ' create mode 100644 rust/sys/voyageurs/src/xhci/device_context_base_array.rs create mode 100644 rust/sys/voyageurs/src/xhci/driver.rs create mode 100644 rust/sys/voyageurs/src/xhci/event_ring.rs create mode 100644 rust/sys/voyageurs/src/xhci/trb_ring.rs diff --git a/' b/' new file mode 100644 index 0000000..8a445e9 --- /dev/null +++ b/' @@ -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 Index for TrbRing { + type Output = TransferRequestBlock; + + fn index(&self, index: I) -> &Self::Output { + self.0[index] + } +} + +impl IndexMut for TrbRing +where + I: SliceIndex<[TransferRequestBlock]>, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.0[index] + } +} diff --git a/rust/Cargo.lock b/rust/Cargo.lock index aec4469..ced63d5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - [[package]] name = "bitfield-struct" version = "0.8.0" @@ -80,11 +74,10 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] @@ -105,9 +98,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", @@ -115,18 +108,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -148,9 +141,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -178,15 +171,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "victoriafalls" @@ -206,6 +199,8 @@ version = "0.1.0" dependencies = [ "bitfield-struct 0.12.1", "mammoth", + "pci", + "yellowstone-yunq", ] [[package]] diff --git a/rust/lib/pci/src/device.rs b/rust/lib/pci/src/device.rs index ab1b25d..948cd04 100644 --- a/rust/lib/pci/src/device.rs +++ b/rust/lib/pci/src/device.rs @@ -72,15 +72,15 @@ impl PciDevice { control.capable_address_64(), "We don't handle the non-64bit case for MSI yet." ); - assert!( - control.multi_message_capable() == 0, - "We don't yet handle multi-message capable devices." - ); + + if control.multi_message_capable() != 0 { + mammoth::debug!("WARN: We don't yet handle multi-message capable devices."); + } // FIXME: These probably need to be volatile writes. let header: &mut PciDeviceHeader = self.memory_region.as_mut(); 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 // Vol 3. Section 11.11 diff --git a/rust/sys/voyageurs/Cargo.toml b/rust/sys/voyageurs/Cargo.toml index 084d083..ddd2996 100644 --- a/rust/sys/voyageurs/Cargo.toml +++ b/rust/sys/voyageurs/Cargo.toml @@ -6,3 +6,9 @@ edition = "2024" [dependencies] bitfield-struct = "0.12" mammoth = { path = "../../lib/mammoth/" } +pci = { path = "../../lib/pci" } +yellowstone-yunq = { version = "0.1.0", path = "../../lib/yellowstone" } + +[features] +default = ["debug"] +debug = [] diff --git a/rust/sys/voyageurs/src/main.rs b/rust/sys/voyageurs/src/main.rs index e9e5dd6..adf75de 100644 --- a/rust/sys/voyageurs/src/main.rs +++ b/rust/sys/voyageurs/src/main.rs @@ -5,12 +5,28 @@ extern crate alloc; mod xhci; -use mammoth::{debug, define_entry, zion::z_err_t}; +use mammoth::{cap::Capability, debug, define_entry, zion::z_err_t}; +use pci::PciDevice; +use xhci::driver::XHCIDriver; define_entry!(); #[unsafe(no_mangle)] extern "C" fn main() -> z_err_t { - debug!("In Voyageurs"); + #[cfg(feature = "debug")] + debug!("Voyageurs Starting."); + + let yellowstone = yellowstone_yunq::from_init_endpoint(); + + let xhci_info = yellowstone + .get_xhci_info() + .expect("Failed to get XHCI info from yellowstone."); + + let pci_device = PciDevice::from_cap(Capability::take(xhci_info.xhci_region)).unwrap(); + + let xhci_driver = XHCIDriver::from_pci_device(pci_device); + + loop {} + 0 } diff --git a/rust/sys/voyageurs/src/xhci/device_context_base_array.rs b/rust/sys/voyageurs/src/xhci/device_context_base_array.rs new file mode 100644 index 0000000..efddd4e --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/device_context_base_array.rs @@ -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 + } +} diff --git a/rust/sys/voyageurs/src/xhci/driver.rs b/rust/sys/voyageurs/src/xhci/driver.rs new file mode 100644 index 0000000..0482e6d --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/driver.rs @@ -0,0 +1,227 @@ +use core::slice; + +use mammoth::{map_unaligned_volatile, mem::MemoryRegion}; +use mammoth::{read_unaligned_volatile, write_unaligned_volatile}; + +use super::registers::{self}; +use crate::xhci::device_context_base_array::DeviceContextBaseArray; +use crate::xhci::event_ring::EventRing; +use crate::xhci::registers::HCSParams1; +use crate::xhci::trb_ring::{InputTrbRing, OutputTrbRing}; + +pub struct XHCIDriver { + #[allow(dead_code)] + pci_device: pci::PciDevice, + registers_region: MemoryRegion, + command_ring: OutputTrbRing, + event_ring: EventRing, + device_context_base_array: DeviceContextBaseArray, +} + +impl XHCIDriver { + pub fn from_pci_device(mut pci_device: pci::PciDevice) -> Self { + // BAR0 Size Allocation + // If virtualization is supported, the Capability and Operational Register sets, and + // the Extended Capabilities may reside in a single page of virtual memory, + // however the RTSOFF and DBOFF Registers shall position the Runtime and + // Doorbell Registers to reside on their own respective virtual memory pages. The + // BAR0 size shall provide space that is sufficient to cover the offset between the + // respective register spaces (Capability, Operational, Runtime, etc.) and the + // register spaces themselves (e.g. a minimum of 3 virtual memory pages). + // If virtualization is not supported, all xHCI register spaces may reside on a single + // page pointed to by the BAR0. + let three_pages = 0x3000; + let address = + ((pci_device.header().bars[1] as u64) << 32) | (pci_device.header().bars[0] as u64); + let registers_region = MemoryRegion::direct_physical(address, three_pages).unwrap(); + + let port_cap = pci_device.register_msi().unwrap(); + + let driver = Self { + pci_device, + registers_region, + command_ring: OutputTrbRing::new(), + event_ring: EventRing::new(), + device_context_base_array: DeviceContextBaseArray::new(), + }; + driver.reset(); + driver + } + + fn capabilities(&self) -> ®isters::HostControllerCapabilities { + self.registers_region.as_ref() + } + + fn operational(&self) -> &mut registers::HostControllerOperational { + let cap_length: registers::HostControllerCapabilitiesLengthAndVersion = + read_unaligned_volatile!(self.capabilities(), cap_length_and_version); + let offset = cap_length.cap_length(); + + // TODO: Ensure exclusive access. + unsafe { self.registers_region.as_mut_ref_at_offset(offset as usize) } + } + + fn interrupters(&self) -> &[registers::InterrupterRegisterSet] { + // See Table 5-35: Host Controller Runtime Registers + const INTERRUPTER_OFFSET_FROM_RUNTIME: u32 = 0x20; + let runtime = read_unaligned_volatile!(self.capabilities(), runtime_register_space_offset); + + let interrupter_offset: usize = (runtime + INTERRUPTER_OFFSET_FROM_RUNTIME) as usize; + + let params1: registers::HCSParams1 = + read_unaligned_volatile!(self.capabilities(), params_1); + + // SAFETY: The XHCI spec says so? + unsafe { + slice::from_raw_parts( + self.registers_region + .raw_ptr_at_offset(interrupter_offset as u64), + params1.max_interrupters() as usize, + ) + } + } + + fn doorbells(&self) -> &[registers::Doorbell] { + let doorbell_offset = read_unaligned_volatile!(self.capabilities(), doorbell_offset); + + let params1: registers::HCSParams1 = + read_unaligned_volatile!(self.capabilities(), params_1); + + // SAFETY: The XHCI spec says so? + unsafe { + slice::from_raw_parts( + self.registers_region + .raw_ptr_at_offset(doorbell_offset as u64), + params1.max_device_slots() as usize, + ) + } + } + + fn reset(&self) { + #[cfg(feature = "debug")] + mammoth::debug!("Stopping XHCI Controller."); + + // Stop the host controller. + // TODO: Make this volatile. + self.operational().usb_command = self.operational().usb_command.with_run_stop(false); + + #[cfg(feature = "debug")] + mammoth::debug!("Waiting for controller to halt."); + + // Sleep until the controller is halted. + let mut status: registers::UsbStatus = + read_unaligned_volatile!(self.operational(), usb_status); + while !status.host_controller_halted() { + // TODO: Sleep for how long? + mammoth::syscall::thread_sleep(50).unwrap(); + status = read_unaligned_volatile!(self.operational(), usb_status); + } + + #[cfg(feature = "debug")] + mammoth::debug!("Resetting Controller."); + + map_unaligned_volatile!( + self.operational(), + usb_command, + |c: registers::UsbCommand| c.with_host_controller_reset(true) + ); + + let mut command: registers::UsbCommand = + read_unaligned_volatile!(self.operational(), usb_command); + while command.host_controller_reset() { + // TODO: Sleep for how long? + mammoth::syscall::thread_sleep(50).unwrap(); + command = read_unaligned_volatile!(self.operational(), usb_command); + } + + #[cfg(feature = "debug")] + mammoth::debug!("XHCI Controller Reset, waiting ready."); + + status = read_unaligned_volatile!(self.operational(), usb_status); + while status.controller_not_ready() { + // TODO: Sleep for how long? + mammoth::syscall::thread_sleep(50).unwrap(); + status = read_unaligned_volatile!(self.operational(), usb_status); + } + + #[cfg(feature = "debug")] + mammoth::debug!("XHCI Controller Ready."); + + #[cfg(feature = "debug")] + mammoth::debug!("Setting Command Ring"); + + // TODO: We should reset the command ring here. + write_unaligned_volatile!( + self.operational(), + command_ring_control, + self.command_ring.physical_addr() + ); + + #[cfg(feature = "debug")] + mammoth::debug!("Setting DCBA."); + + write_unaligned_volatile!( + self.operational(), + device_context_base_address_array_pointer, + self.device_context_base_array.physical_addr() + ); + + // We tell the controller that we can support as many slots as it does because + // we allocate a full 4K page to the DCBA, which is 256 entries and the max + // slots are 255. + let params1: registers::HCSParams1 = + read_unaligned_volatile!(self.capabilities(), params_1); + map_unaligned_volatile!( + self.operational(), + configure, + |c: registers::UsbConfigure| c + .with_max_device_slots_enabled(params1.max_device_slots()) + ); + + let params2: registers::HCSParams2 = + read_unaligned_volatile!(self.capabilities(), params_2); + + let max_scratchpad_buffers = + (params2.max_scratchpad_buffers_hi() << 5) | params2.max_scratchpad_buffers_lo(); + assert!( + max_scratchpad_buffers == 0, + "Unsupported scratchpad buffers." + ); + + #[cfg(feature = "debug")] + mammoth::debug!("Setting up initial event ring."); + + let interrupter0 = &self.interrupters()[0]; + write_unaligned_volatile!( + interrupter0, + event_ring_segment_table_base_address, + self.event_ring.segment_table().physical_address() + ); + write_unaligned_volatile!( + interrupter0, + event_ring_segement_table_size, + self.event_ring.segment_table().len() + ); + write_unaligned_volatile!( + interrupter0, + interrupter_moderation, + registers::InterrupterModeration::new() + .with_interrupt_moderation_interval(4000) + .with_interrupt_moderation_counter(0) + ); + write_unaligned_volatile!( + interrupter0, + interrupter_management, + registers::InterrupterManagement::new().with_interrupt_enabled(true) + ); + + map_unaligned_volatile!( + self.operational(), + usb_command, + |c: registers::UsbCommand| c.with_run_stop(true) + ); + + #[cfg(feature = "debug")] + mammoth::debug!("Enabled interrupts and controller."); + } +} diff --git a/rust/sys/voyageurs/src/xhci/event_ring.rs b/rust/sys/voyageurs/src/xhci/event_ring.rs new file mode 100644 index 0000000..c6d2fe5 --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/event_ring.rs @@ -0,0 +1,25 @@ +use alloc::vec::Vec; + +use crate::xhci::data_structures::{EventRingSegmentTable, TrbRing}; + +pub struct EventRing { + segment_table: EventRingSegmentTable, + segments: Vec, +} + +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 + } +} diff --git a/rust/sys/voyageurs/src/xhci/mod.rs b/rust/sys/voyageurs/src/xhci/mod.rs index 008e36e..64bc6a9 100644 --- a/rust/sys/voyageurs/src/xhci/mod.rs +++ b/rust/sys/voyageurs/src/xhci/mod.rs @@ -1,2 +1,6 @@ -pub mod data_structures; -pub mod registers; +mod data_structures; +mod device_context_base_array; +pub mod driver; +mod event_ring; +mod registers; +mod trb_ring; diff --git a/rust/sys/voyageurs/src/xhci/trb_ring.rs b/rust/sys/voyageurs/src/xhci/trb_ring.rs new file mode 100644 index 0000000..116b8ea --- /dev/null +++ b/rust/sys/voyageurs/src/xhci/trb_ring.rs @@ -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 + } +}