[Voyageurs] First Pass XHCI Controller, resets the controller.

This commit is contained in:
Drew Galbraith 2024-02-06 20:51:16 -08:00
parent 3bacfea183
commit 2228b5b52e
6 changed files with 372 additions and 3 deletions

View File

@ -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.

View File

@ -1,5 +1,6 @@
add_executable(voyageurs
keyboard/keyboard_driver.cpp
xhci/xhci_driver.cpp
voyageurs_server.cpp
voyageurs.cpp)

View File

@ -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());

128
sys/voyageurs/xhci/xhci.h Normal file
View File

@ -0,0 +1,128 @@
#pragma once
#include <stdint.h>
// 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));

View File

@ -0,0 +1,186 @@
#include "xhci/xhci_driver.h"
#include <mammoth/proc/thread.h>
#include <mammoth/util/debug.h>
#include <mammoth/util/memory_region.h>
#include <zcall.h>
#include "xhci/xhci.h"
glcr::ErrorOr<XhciDriver> 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<XhciPort*>(
reinterpret_cast<uint64_t>(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<PciDeviceHeader*>(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<XhciCapabilities*>(mmio_regions_.vaddr());
uint64_t op_base =
mmio_regions_.vaddr() + (capabilities_->length_and_version & 0xFF);
operational_ = reinterpret_cast<XhciOperational*>(op_base);
runtime_ = reinterpret_cast<XhciRuntime*>(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<XhciCommandTrb*>(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<XhciLinkTrb*>(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<uint64_t*>(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<XhciEventRingSegmentTableEntry*>(
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<XhciCommandTrb*>(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;
}

View File

@ -0,0 +1,51 @@
#pragma once
#include <glacier/status/error_or.h>
#include <mammoth/util/memory_region.h>
#include <yellowstone/yellowstone.yunq.client.h>
#include "xhci/xhci.h"
class XhciDriver {
public:
static glcr::ErrorOr<XhciDriver> 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();
};