From 2228b5b52e64efa2242a9f7edc9c9b0b0cc29309 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Tue, 6 Feb 2024 20:51:16 -0800 Subject: [PATCH] [Voyageurs] First Pass XHCI Controller, resets the controller. --- scripts/qemu.sh | 2 +- sys/voyageurs/CMakeLists.txt | 1 + sys/voyageurs/voyageurs.cpp | 7 +- sys/voyageurs/xhci/xhci.h | 128 ++++++++++++++++++++ sys/voyageurs/xhci/xhci_driver.cpp | 186 +++++++++++++++++++++++++++++ sys/voyageurs/xhci/xhci_driver.h | 51 ++++++++ 6 files changed, 372 insertions(+), 3 deletions(-) create mode 100644 sys/voyageurs/xhci/xhci.h create mode 100644 sys/voyageurs/xhci/xhci_driver.cpp create mode 100644 sys/voyageurs/xhci/xhci_driver.h diff --git a/scripts/qemu.sh b/scripts/qemu.sh index b2ea1cc..c3c6526 100755 --- a/scripts/qemu.sh +++ b/scripts/qemu.sh @@ -20,7 +20,7 @@ if [[ $1 == "debug" ]]; then fi # Use machine q35 to access PCI devices. -qemu-system-x86_64 -machine q35 -d guest_errors -m 1G -serial stdio -hda disk.img ${QEMU_ARGS} +qemu-system-x86_64 -machine q35 -d guest_errors -m 1G -serial stdio -hda disk.img ${QEMU_ARGS} -device nec-usb-xhci,id=xhci -device usb-kbd,bus=xhci.0 popd # Extra options to add to this script in the future. diff --git a/sys/voyageurs/CMakeLists.txt b/sys/voyageurs/CMakeLists.txt index 10a3754..6dc7fce 100644 --- a/sys/voyageurs/CMakeLists.txt +++ b/sys/voyageurs/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable(voyageurs keyboard/keyboard_driver.cpp + xhci/xhci_driver.cpp voyageurs_server.cpp voyageurs.cpp) diff --git a/sys/voyageurs/voyageurs.cpp b/sys/voyageurs/voyageurs.cpp index f3d375f..00e3d0f 100644 --- a/sys/voyageurs/voyageurs.cpp +++ b/sys/voyageurs/voyageurs.cpp @@ -5,6 +5,7 @@ #include "keyboard/keyboard_driver.h" #include "voyageurs_server.h" +#include "xhci/xhci_driver.h" using yellowstone::RegisterEndpointRequest; using yellowstone::YellowstoneClient; @@ -12,6 +13,10 @@ using yellowstone::YellowstoneClient; uint64_t main(uint64_t init_port) { ParseInitPort(init_port); + YellowstoneClient yellowstone(gInitEndpointCap); + + ASSIGN_OR_RETURN(XhciDriver xhci, XhciDriver::InitiateDriver(yellowstone)); + dbgln("Initializing PS/2 Driver."); KeyboardDriver driver; @@ -24,8 +29,6 @@ uint64_t main(uint64_t init_port) { Thread server_thread = server->RunServer(); - YellowstoneClient yellowstone(gInitEndpointCap); - RegisterEndpointRequest req; req.set_endpoint_name("voyageurs"); ASSIGN_OR_RETURN(z_cap_t client_cap, server->CreateClientCap()); diff --git a/sys/voyageurs/xhci/xhci.h b/sys/voyageurs/xhci/xhci.h new file mode 100644 index 0000000..73b9f03 --- /dev/null +++ b/sys/voyageurs/xhci/xhci.h @@ -0,0 +1,128 @@ +#pragma once + +#include + +// TODO: Move to shared lib for denali and voyageurs. +struct PciDeviceHeader { + uint16_t vendor_id; + uint16_t device_id; + uint16_t command_reg; + uint16_t status_reg; + uint8_t revision; + uint8_t prog_interface; + uint8_t subclass; + uint8_t class_code; + uint8_t cache_line_size; + uint8_t latency_timer; + uint8_t header_type; + uint8_t bist; + uint32_t bars[5]; + uint32_t abar; + uint32_t reserved0; + uint32_t subsystem_id; + uint32_t expansion_rom; + uint8_t cap_ptr; + uint8_t reserved1[7]; + uint8_t interrupt_line; + uint8_t interrupt_pin; + uint8_t min_grant; + uint8_t max_latency; +} __attribute__((packed)); + +struct XhciCapabilities { + // NOTE: In qemu access these addresses at anything other than a 32bit offset + // seems to give bogus values so we group the fields more than necessary. + uint32_t length_and_version; + uint32_t hcs_params_1; + uint32_t hcs_params_2; + uint32_t hcs_params_3; + uint16_t capabilites; + uint16_t ext_capabilities_pointer; + uint32_t doorbell_offset; + uint32_t runtime_offset; + uint32_t capabilities2; +} __attribute__((packed)); + +struct XhciOperational { + uint32_t usb_command; + uint32_t usb_status; + uint32_t page_size; + uint32_t reserved; + uint32_t reserved2; + uint32_t device_notification_control; + uint64_t command_ring_control; + uint64_t reserved3; + uint64_t reserved4; + uint64_t device_context_base; + uint32_t configure; +} __attribute__((packed)); + +struct XhciInterrupter { + uint32_t management; + uint32_t moderation; + uint32_t event_ring_segment_table_size; + uint32_t reserved; + uint64_t event_ring_segment_table_base_address; + uint64_t event_ring_dequeue_pointer; +} __attribute__((packed)); + +struct XhciRuntime { + uint32_t microframe_index; + uint32_t reserved1; + uint64_t reserved2; + uint64_t reserved3; + uint64_t reserved4; + XhciInterrupter interrupters[1024]; + +} __attribute__((packed)); + +struct XhciPort { + uint32_t status_and_control; + uint32_t power_management; + uint32_t link_info; + uint32_t lpm_control; +} __attribute__((packed)); + +struct XhciSlotContext { + uint32_t route_speed_entries; + uint32_t latency_port_number; + uint32_t parent_and_interrupt; + uint32_t address_and_state; + uint64_t reserved1; + uint64_t reserved2; +} __attribute__((packed)); + +struct XhciEndpointContext { + uint32_t state; + uint32_t error_and_type; + uint64_t tr_dequeue_ptr; + uint32_t average_trb_length; + uint32_t reserved1; + uint64_t reserved2; +} __attribute__((packed)); + +struct XhciDeviceContext { + XhciSlotContext slot_context; + XhciEndpointContext endpoint_contexts[31]; +} __attribute__((packed)); + +struct XhciCommandTrb { + uint64_t reserved1; + uint32_t reserved2; + uint16_t type_and_cycle; + uint16_t slot_type; +} __attribute__((packed)); + +struct XhciLinkTrb { + uint64_t link_address; + uint32_t interrupter_target; + uint16_t type_and_cycle; + uint16_t reserved; +} __attribute__((packed)); + +struct XhciEventRingSegmentTableEntry { + uint64_t ring_segment_base; + uint16_t ring_segment_size; + uint16_t reserved1; + uint32_t reserved2; +} __attribute__((packed)); diff --git a/sys/voyageurs/xhci/xhci_driver.cpp b/sys/voyageurs/xhci/xhci_driver.cpp new file mode 100644 index 0000000..eb449d1 --- /dev/null +++ b/sys/voyageurs/xhci/xhci_driver.cpp @@ -0,0 +1,186 @@ +#include "xhci/xhci_driver.h" + +#include +#include +#include +#include + +#include "xhci/xhci.h" + +glcr::ErrorOr XhciDriver::InitiateDriver( + yellowstone::YellowstoneClient& yellowstone) { + yellowstone::XhciInfo info; + check(yellowstone.GetXhciInfo(info)); + + mmth::OwnedMemoryRegion pci_region = + mmth::OwnedMemoryRegion::FromCapability(info.mutable_xhci_region()); + XhciDriver driver(glcr::Move(pci_region)); + driver.ParseMmioStructures(); + driver.DumpDebugInfo(); + driver.FreeExistingMemoryStructures(); + driver.ResetController(); + dbgln("XHCI CONTROLLER RESET"); + driver.DumpDebugInfo(); + check(ZThreadSleep(100)); + driver.DumpDebugInfo(); + return driver; +} + +void XhciDriver::DumpDebugInfo() { + dbgln("Code: {x} {x} {x}", pci_device_header_->class_code, + pci_device_header_->subclass, pci_device_header_->prog_interface); + + dbgln("BAR: {x}", pci_device_header_->bars[0] & ~0xFFF); + dbgln("In P: {x} L: {x}", pci_device_header_->interrupt_pin, + pci_device_header_->interrupt_line); + + dbgln("Cap length: {x}", capabilities_->length_and_version & 0xFF); + dbgln("XHCI Version: {x}", + (capabilities_->length_and_version & 0xFFFF0000) >> 16); + dbgln("Max Slots: {x}", capabilities_->hcs_params_1 & 0xFF); + dbgln("Max Interrupters: {x}", (capabilities_->hcs_params_1 & 0x3FF00) >> 8); + uint64_t max_ports = (capabilities_->hcs_params_1 & 0xFF00'0000) >> 24; + dbgln("Params 2: {x}", capabilities_->hcs_params_2); + dbgln("Params 3: {x}", capabilities_->hcs_params_3); + dbgln("Max Ports: {x}", max_ports); + dbgln("Capabilities: {x}", capabilities_->capabilites); + dbgln("Doorbell: {x}", capabilities_->doorbell_offset); + dbgln("Runtime: {x}", capabilities_->runtime_offset); + + dbgln("Op cmd: {x} sts: {x}", operational_->usb_command, + operational_->usb_status); + dbgln("Page size: {x}", operational_->page_size); + dbgln("Device Context Base Array: {x}", operational_->device_context_base); + dbgln("Command Ring Control: {x}", operational_->command_ring_control); + dbgln("Config: {x}", operational_->configure); + + for (uint64_t i = 0; i < max_ports; i++) { + XhciPort* port = reinterpret_cast( + reinterpret_cast(operational_) + 0x400 + (0x10 * i)); + port->status_and_control &= ~0x10000; + dbgln("Port {x}: {x}", i, port->status_and_control); + if ((port->status_and_control & 0x3) == 0x1) { + dbgln("Resetting: {x}", i); + port->status_and_control |= 0x10; + } + } + + dbgln("Int 0 ES: {x}", + runtime_->interrupters[0].event_ring_segment_table_base_address); +} + +XhciDriver::XhciDriver(mmth::OwnedMemoryRegion&& pci_space) + : pci_region_(glcr::Move(pci_space)) {} + +glcr::ErrorCode XhciDriver::ParseMmioStructures() { + pci_device_header_ = reinterpret_cast(pci_region_.vaddr()); + + // TODO: Officially determine the size of this memory. + mmio_regions_ = mmth::OwnedMemoryRegion::DirectPhysical( + pci_device_header_->bars[0] & ~0xFFF, 0x3000); + + capabilities_ = reinterpret_cast(mmio_regions_.vaddr()); + + uint64_t op_base = + mmio_regions_.vaddr() + (capabilities_->length_and_version & 0xFF); + operational_ = reinterpret_cast(op_base); + + runtime_ = reinterpret_cast(mmio_regions_.vaddr() + + capabilities_->runtime_offset); + + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::ResetController() { + // Host Controller Reset + operational_->usb_command |= 0x2; + + while (operational_->usb_command & 0x2) { + dbgln("Waiting Reset"); + RET_ERR(ZThreadSleep(50)); + } + + while (operational_->usb_command & (0x1 << 11)) { + dbgln("Waiting controller ready"); + RET_ERR(ZThreadSleep(50)); + } + + InitiateCommandRing(); + InitiateDeviceContextBaseArray(); + InitiateEventRingSegmentTable(); + + // Run the controller. + operational_->usb_command |= 0x1; + + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::InitiateCommandRing() { + uint64_t command_ring_phys; + command_ring_mem_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &command_ring_phys); + command_trb_ = reinterpret_cast(command_ring_mem_.vaddr()); + + uint64_t number_trbs = 0x1000 / sizeof(XhciCommandTrb); + + // Point the end of the command ring back to the start. + auto* link_trb = + reinterpret_cast(command_trb_ + number_trbs - 1); + link_trb->link_address = command_ring_phys; + // TODO: Cleaner interface for specifying a command type. + link_trb->type_and_cycle = 0x6 << 10; + + operational_->command_ring_control = command_ring_phys; + + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::InitiateDeviceContextBaseArray() { + uint64_t dcba_phys; + device_context_base_array_mem_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &dcba_phys); + device_context_base_array_ = + reinterpret_cast(device_context_base_array_mem_.vaddr()); + + operational_->device_context_base = dcba_phys; + + uint64_t max_slots = (0x1000 / sizeof(uint64_t)) - 1; + + if (max_slots > (capabilities_->hcs_params_1 & 0xFF)) { + max_slots = capabilities_->hcs_params_1 & 0xFF; + } + operational_->configure = + (operational_->configure & ~0xFF) | (max_slots & 0xFF); + + // TODO: Initialize scratchpad if that is needed by the controller. + + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::InitiateEventRingSegmentTable() { + uint64_t erst_phys; + event_ring_segment_table_mem_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &erst_phys); + + event_ring_segment_table_ = reinterpret_cast( + event_ring_segment_table_mem_.vaddr()); + + uint64_t ers_phys; + event_ring_segment_mem_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &ers_phys); + uint64_t ers_size = 0x1000 / sizeof(XhciCommandTrb); + + event_ring_segment_ = + reinterpret_cast(event_ring_segment_mem_.vaddr()); + + event_ring_segment_table_[0].ring_segment_base = ers_phys; + event_ring_segment_table_[0].ring_segment_size = ers_size & 0xFFFF; + + runtime_->interrupters[0].event_ring_segment_table_base_address = erst_phys; + runtime_->interrupters[0].event_ring_dequeue_pointer = erst_phys; + runtime_->interrupters[0].event_ring_segment_table_size = 1; + // Enable interrupts. + runtime_->interrupters[0].management |= 0x2; + operational_->usb_command |= 0x4; + return glcr::OK; +} diff --git a/sys/voyageurs/xhci/xhci_driver.h b/sys/voyageurs/xhci/xhci_driver.h new file mode 100644 index 0000000..d263fc9 --- /dev/null +++ b/sys/voyageurs/xhci/xhci_driver.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +#include "xhci/xhci.h" + +class XhciDriver { + public: + static glcr::ErrorOr InitiateDriver( + yellowstone::YellowstoneClient& yellowstone); + + void DumpDebugInfo(); + + private: + // MMIO Structures. + mmth::OwnedMemoryRegion pci_region_; + PciDeviceHeader* pci_device_header_; + + mmth::OwnedMemoryRegion mmio_regions_; + XhciCapabilities* capabilities_; + XhciOperational* operational_; + // TODO: Extended Capabilities. + XhciRuntime* runtime_; + // TODO: Doorbell Array. + + // Host Memory Regions. + mmth::OwnedMemoryRegion command_ring_mem_; + XhciCommandTrb* command_trb_; + + mmth::OwnedMemoryRegion device_context_base_array_mem_; + uint64_t* device_context_base_array_; + + mmth::OwnedMemoryRegion event_ring_segment_table_mem_; + XhciEventRingSegmentTableEntry* event_ring_segment_table_; + + mmth::OwnedMemoryRegion event_ring_segment_mem_; + XhciCommandTrb* event_ring_segment_; + + XhciDriver(mmth::OwnedMemoryRegion&& pci_space); + + glcr::ErrorCode ParseMmioStructures(); + glcr::ErrorCode FreeExistingMemoryStructures() { return glcr::OK; } + + glcr::ErrorCode ResetController(); + + glcr::ErrorCode InitiateCommandRing(); + glcr::ErrorCode InitiateDeviceContextBaseArray(); + glcr::ErrorCode InitiateEventRingSegmentTable(); +};