From 8e78950ac723394b15c5fd9b2b8d41eda628e787 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Thu, 22 Feb 2024 18:16:08 -0800 Subject: [PATCH] [Voyageurs] Send AddressDevice Command to move port to 'Addressed' State. --- sys/voyageurs/CMakeLists.txt | 1 + sys/voyageurs/xhci/device_slot.cpp | 47 ++++++++++++++++++++++ sys/voyageurs/xhci/device_slot.h | 37 ++++++++++++++++++ sys/voyageurs/xhci/trb.cpp | 45 ++++++++++++++-------- sys/voyageurs/xhci/trb.h | 26 +++++++++++++ sys/voyageurs/xhci/trb_ring.cpp | 9 +++++ sys/voyageurs/xhci/trb_ring.h | 1 + sys/voyageurs/xhci/xhci.h | 33 ++++++++++++---- sys/voyageurs/xhci/xhci_driver.cpp | 62 +++++++++++++++++++++++++----- sys/voyageurs/xhci/xhci_driver.h | 8 ++++ 10 files changed, 236 insertions(+), 33 deletions(-) create mode 100644 sys/voyageurs/xhci/device_slot.cpp create mode 100644 sys/voyageurs/xhci/device_slot.h diff --git a/sys/voyageurs/CMakeLists.txt b/sys/voyageurs/CMakeLists.txt index 8ef1302..3c74de6 100644 --- a/sys/voyageurs/CMakeLists.txt +++ b/sys/voyageurs/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable(voyageurs keyboard/keyboard_driver.cpp + xhci/device_slot.cpp xhci/trb.cpp xhci/trb_ring.cpp xhci/xhci_driver.cpp diff --git a/sys/voyageurs/xhci/device_slot.cpp b/sys/voyageurs/xhci/device_slot.cpp new file mode 100644 index 0000000..35b8b87 --- /dev/null +++ b/sys/voyageurs/xhci/device_slot.cpp @@ -0,0 +1,47 @@ +#include "xhci/device_slot.h" + +#include "xhci/trb.h" + +void DeviceSlot::EnableAndInitializeDataStructures(uint8_t slot_index, + uint64_t* output_context) { + enabled_ = true; + slot_index_ = slot_index; + + context_memory_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &context_phys_); + + device_context_ = + reinterpret_cast(context_memory_.vaddr()); + *output_context = context_phys_; + input_context_ = reinterpret_cast(context_memory_.vaddr() + + kInputSlotContextOffset); + + control_endpoint_transfer_trb_ = glcr::MakeUnique(); +} + +XhciTrb DeviceSlot::CreateAddressDeviceCommand(uint8_t root_port, + uint32_t route_string, + uint16_t max_packet_size) { + // Initialize Slot Context and Endpoint 0 Context. + input_context_->input.add_contexts = 0x3; + // Set context_entries to 1. XHCI 4.3.3 + input_context_->slot_context.route_speed_entries = (0x1 << 27) | route_string; + input_context_->slot_context.latency_port_number = root_port << 16; + + // Initialize Control Endpoint. + input_context_->endpoint_contexts[0].state = 0; + constexpr uint16_t kCerr = 0x3 << 1; + constexpr uint16_t kTypeControl = 0x4 << 3; + input_context_->endpoint_contexts[0].error_and_type = + kCerr | kTypeControl | (max_packet_size << 16); + + input_context_->endpoint_contexts[0].tr_dequeue_ptr = + control_endpoint_transfer_trb_->PhysicalAddress() | 0x1; + + return ::CreateAddressDeviceCommand(context_phys_ + kInputSlotContextOffset, + slot_index_); +} + +uint8_t DeviceSlot::State() { + return device_context_->slot_context.address_and_state >> 27; +} diff --git a/sys/voyageurs/xhci/device_slot.h b/sys/voyageurs/xhci/device_slot.h new file mode 100644 index 0000000..d014021 --- /dev/null +++ b/sys/voyageurs/xhci/device_slot.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include "xhci/trb_ring.h" +#include "xhci/xhci.h" + +class DeviceSlot { + public: + DeviceSlot() = default; + DeviceSlot(const DeviceSlot&) = delete; + DeviceSlot(DeviceSlot&&) = delete; + + void EnableAndInitializeDataStructures(uint8_t slot_index_, + uint64_t* output_context); + + XhciTrb CreateAddressDeviceCommand(uint8_t root_port, uint32_t route_string, + uint16_t max_packet_size); + + uint8_t State(); + + private: + bool enabled_ = false; + + uint8_t slot_index_ = 0; + + uint64_t context_phys_ = 0; + mmth::OwnedMemoryRegion context_memory_; + + static constexpr uint64_t kInputSlotContextOffset = 0x400; + + XhciDeviceContext* device_context_; + XhciInputContext* input_context_; + + glcr::UniquePtr control_endpoint_transfer_trb_; +}; diff --git a/sys/voyageurs/xhci/trb.cpp b/sys/voyageurs/xhci/trb.cpp index 61323fe..ebd3883 100644 --- a/sys/voyageurs/xhci/trb.cpp +++ b/sys/voyageurs/xhci/trb.cpp @@ -1,26 +1,27 @@ #include "xhci/trb.h" -constexpr uint8_t kTrb_Normal = 1; -constexpr uint8_t kTrb_SetupStage = 2; -constexpr uint8_t kTrb_DataStage = 3; -constexpr uint8_t kTrb_StatusStage = 4; -constexpr uint8_t kTrb_Isoch = 5; -constexpr uint8_t kTrb_Link = 6; -constexpr uint8_t kTrb_EventData = 7; -constexpr uint8_t kTrb_NoOp = 8; -constexpr uint8_t kTrb_EnableSlot = 9; -constexpr uint8_t kTrb_DisableSlot = 10; -constexpr uint8_t kTrb_NoOpCommand = 23; - constexpr uint8_t kTrb_TypeOffset = 10; -constexpr uint8_t kTrb_Cycle = 1; +constexpr uint16_t kTrb_Cycle = 1; +constexpr uint16_t kTrb_BSR = (1 << 9); + +namespace { + +uint16_t TypeToInt(TrbType type) { + return static_cast(type) << kTrb_TypeOffset; +} + +} // namespace + +TrbType GetType(const XhciTrb& trb) { + return TrbType(trb.type_and_cycle >> kTrb_TypeOffset); +} XhciTrb CreateLinkTrb(uint64_t physical_address) { return { .parameter = physical_address, .status = 0, - .type_and_cycle = kTrb_Link << kTrb_TypeOffset, + .type_and_cycle = TypeToInt(TrbType::Link), .control = 0, }; } @@ -30,18 +31,30 @@ XhciTrb CreateEnableSlotTrb() { .parameter = 0, .status = 0, // FIXME: Accept Cycle Bit as a parameter. - .type_and_cycle = kTrb_EnableSlot << kTrb_TypeOffset | kTrb_Cycle, + .type_and_cycle = (uint16_t)(TypeToInt(TrbType::EnableSlot) | kTrb_Cycle), // FIXME: Specify slot type if necessary. (XHCI Table 7-9)? .control = 0, }; } +XhciTrb CreateAddressDeviceCommand(uint64_t input_context, uint8_t slot_id) { + return { + .parameter = input_context, + .status = 0, + // Always cycle the device straight to addressed. + .type_and_cycle = + (uint16_t)(TypeToInt(TrbType::AddressDevice) | kTrb_Cycle), + .control = (uint16_t)(slot_id << 8), + }; +} + XhciTrb CreateNoOpCommandTrb() { return { .parameter = 0, .status = 0, // FIXME: Accept Cycle Bit as a parameter. - .type_and_cycle = kTrb_NoOpCommand << kTrb_TypeOffset | kTrb_Cycle, + .type_and_cycle = + (uint16_t)(TypeToInt(TrbType::NoOpCommand) | kTrb_Cycle), .control = 0, }; } diff --git a/sys/voyageurs/xhci/trb.h b/sys/voyageurs/xhci/trb.h index 22abc22..c02286c 100644 --- a/sys/voyageurs/xhci/trb.h +++ b/sys/voyageurs/xhci/trb.h @@ -2,7 +2,33 @@ #include "xhci/xhci.h" +enum class TrbType : uint8_t { + Reserved = 0, + + // Transfers + Normal = 1, + SetupStage = 2, + DataStage = 3, + StatusStage = 4, + Isoch = 5, + Link = 6, + EventData = 7, + NoOp = 8, + + // Commands + EnableSlot = 9, + AddressDevice = 11, + NoOpCommand = 23, + + // Events + CommandCompletion = 33, + PortStatusChange = 34, +}; + +TrbType GetType(const XhciTrb& trb); + XhciTrb CreateLinkTrb(uint64_t physical_address); XhciTrb CreateEnableSlotTrb(); +XhciTrb CreateAddressDeviceCommand(uint64_t input_context, uint8_t slot_id); XhciTrb CreateNoOpCommandTrb(); diff --git a/sys/voyageurs/xhci/trb_ring.cpp b/sys/voyageurs/xhci/trb_ring.cpp index c578412..8965b90 100644 --- a/sys/voyageurs/xhci/trb_ring.cpp +++ b/sys/voyageurs/xhci/trb_ring.cpp @@ -20,6 +20,15 @@ TrbRing::TrbRing() { trb_list_[trb_list_.size() - 1] = CreateLinkTrb(phys_address_); } +XhciTrb TrbRing::GetTrbFromPhysical(uint64_t address) { + uint64_t offset = address - phys_address_; + if (offset >= 0x1000) { + crash("Invalid offset in GetTrbFromPhysical", glcr::INVALID_ARGUMENT); + } + offset /= sizeof(XhciTrb); + return trb_list_[offset]; +} + void TrbRingWriter::EnqueueTrb(const XhciTrb& trb) { uint64_t ptr = enqueue_ptr_++; if (enqueue_ptr_ == trb_list_.size()) { diff --git a/sys/voyageurs/xhci/trb_ring.h b/sys/voyageurs/xhci/trb_ring.h index 3d6c998..5c88ce1 100644 --- a/sys/voyageurs/xhci/trb_ring.h +++ b/sys/voyageurs/xhci/trb_ring.h @@ -10,6 +10,7 @@ class TrbRing { TrbRing(); uint64_t PhysicalAddress() { return phys_address_; } + XhciTrb GetTrbFromPhysical(uint64_t address); protected: uint64_t phys_address_; diff --git a/sys/voyageurs/xhci/xhci.h b/sys/voyageurs/xhci/xhci.h index 2593ff3..2304249 100644 --- a/sys/voyageurs/xhci/xhci.h +++ b/sys/voyageurs/xhci/xhci.h @@ -43,6 +43,13 @@ struct XhciCapabilities { uint32_t capabilities2; } __attribute__((packed)); +struct XhciPort { + uint32_t status_and_control; + uint32_t power_management; + uint32_t link_info; + uint32_t lpm_control; +} __attribute__((packed)); + struct XhciOperational { uint32_t usb_command; uint32_t usb_status; @@ -55,6 +62,7 @@ struct XhciOperational { uint64_t reserved4; uint64_t device_context_base; uint32_t configure; + XhciPort ports[255]; } __attribute__((packed)); struct XhciInterrupter { @@ -79,13 +87,6 @@ struct XhciDoorbells { uint32_t doorbell[256]; } __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; @@ -109,6 +110,24 @@ struct XhciDeviceContext { XhciEndpointContext endpoint_contexts[31]; } __attribute__((packed)); +struct XhciInputControlContext { + uint32_t drop_contexts; + uint32_t add_contexts; + uint64_t reserved1; + uint64_t reserved2; + uint32_t reserved3; + uint8_t configuration_value; + uint8_t interface_number; + uint8_t alternate_setting; + uint8_t reserved4; +} __attribute__((packed)); + +struct XhciInputContext { + XhciInputControlContext input; + XhciSlotContext slot_context; + XhciEndpointContext endpoint_contexts[31]; +} __attribute__((packed)); + struct XhciTrb { uint64_t parameter; uint32_t status; diff --git a/sys/voyageurs/xhci/xhci_driver.cpp b/sys/voyageurs/xhci/xhci_driver.cpp index 4881925..ff0a11d 100644 --- a/sys/voyageurs/xhci/xhci_driver.cpp +++ b/sys/voyageurs/xhci/xhci_driver.cpp @@ -42,23 +42,18 @@ void XhciDriver::InterruptLoop() { } while (event_ring_.HasNext()) { XhciTrb trb = event_ring_.Read(); - uint16_t type = trb.type_and_cycle >> 10; - switch (type) { - case 33: - dbgln( - "Command Completion Event. TRB Ptr: {x}, Status: {x}, Param: {x} " - "Slot ID: {x}", - trb.parameter, trb.status >> 24, trb.status & 0xFFFFFF, - trb.control >> 8); + switch (GetType(trb)) { + case TrbType::CommandCompletion: + HandleCommandCompletion(trb); break; - case 34: + case TrbType::PortStatusChange: dbgln("Port Status Change Event on Port {x}, enabling slot.", ((trb.parameter >> 24) & 0xFF) - 1); command_ring_.EnqueueTrb(CreateEnableSlotTrb()); doorbells_->doorbell[0] = 0; break; default: - dbgln("Unknown TRB Type {x} received.", type); + dbgln("Unknown TRB Type {x} received.", (uint8_t)GetType(trb)); break; } } @@ -125,6 +120,9 @@ glcr::ErrorCode XhciDriver::ParseMmioStructures() { capabilities_ = reinterpret_cast(mmio_regions_.vaddr()); + uint8_t max_device_slots = capabilities_->hcs_params_1 & 0xFF; + devices_ = glcr::Array(max_device_slots); + uint64_t op_base = mmio_regions_.vaddr() + (capabilities_->length_and_version & 0xFF); operational_ = reinterpret_cast(op_base); @@ -247,3 +245,47 @@ glcr::ErrorCode XhciDriver::NoOpCommand() { doorbells_->doorbell[0] = 0; return glcr::OK; } + +void XhciDriver::HandleCommandCompletion( + const XhciTrb& command_completion_trb) { + uint8_t status = command_completion_trb.status >> 24; + if (status != 1) { + dbgln("Command Completion Status: {x}", command_completion_trb.status); + check(glcr::INTERNAL); + } + + XhciTrb orig_trb = + command_ring_.GetTrbFromPhysical(command_completion_trb.parameter); + uint8_t slot = command_completion_trb.control >> 8; + switch (GetType(orig_trb)) { + case TrbType::EnableSlot: + dbgln("Slot Enabled: {x}", slot); + InitializeSlot(slot); + break; + case TrbType::AddressDevice: + dbgln("Device Addressed: {x}", slot); + dbgln("State: {x}", devices_[slot - 1].State()); + break; + case TrbType::NoOpCommand: + dbgln("No-op Command Completed"); + break; + default: + dbgln("Unhandled Command Completion Type: {x}", + (uint8_t)(GetType(orig_trb))); + } +} + +void XhciDriver::InitializeSlot(uint8_t slot_index) { + // TODO: Consider making this array one longer and ignore the first value. + devices_[slot_index - 1].EnableAndInitializeDataStructures( + slot_index, &(device_context_base_array_[slot_index])); + XhciPort* port = + reinterpret_cast(reinterpret_cast(operational_) + + 0x400 + (0x10 * (slot_index - 1))); + uint8_t port_speed = (port->status_and_control >> 10) & 0xF; + uint8_t max_packet_size = 8; + XhciTrb address_device = devices_[slot_index - 1].CreateAddressDeviceCommand( + 0x5, 0, max_packet_size); + command_ring_.EnqueueTrb(address_device); + doorbells_->doorbell[0] = 0; +} diff --git a/sys/voyageurs/xhci/xhci_driver.h b/sys/voyageurs/xhci/xhci_driver.h index c4efede..8033f2b 100644 --- a/sys/voyageurs/xhci/xhci_driver.h +++ b/sys/voyageurs/xhci/xhci_driver.h @@ -1,11 +1,13 @@ #pragma once +#include #include #include #include #include #include +#include "xhci/device_slot.h" #include "xhci/trb_ring.h" #include "xhci/xhci.h" @@ -45,6 +47,8 @@ class XhciDriver { TrbRingReader event_ring_; Thread interrupt_thread_; + glcr::Array devices_; + XhciDriver(mmth::OwnedMemoryRegion&& pci_space); glcr::ErrorCode ParseMmioStructures(); @@ -60,4 +64,8 @@ class XhciDriver { glcr::ErrorCode InitiateDevices(); glcr::ErrorCode NoOpCommand(); + + void HandleCommandCompletion(const XhciTrb& command_completion_trb); + + void InitializeSlot(uint8_t slot_index); };