[Voyageurs] XHCI Event Segment working with polling.

This commit is contained in:
Drew Galbraith 2024-02-22 13:25:49 -08:00
parent b41784b938
commit 4cb0b0b2ae
8 changed files with 181 additions and 21 deletions

View File

@ -15,7 +15,7 @@ uint64_t main(uint64_t init_port) {
YellowstoneClient yellowstone(gInitEndpointCap);
ASSIGN_OR_RETURN(XhciDriver xhci, XhciDriver::InitiateDriver(yellowstone));
ASSIGN_OR_RETURN(auto xhci, XhciDriver::InitiateDriver(yellowstone));
dbgln("Initializing PS/2 Driver.");
KeyboardDriver driver;

View File

@ -1,10 +1,47 @@
#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;
XhciTrb CreateLinkTrb(uint64_t physical_address) {
return {
.parameter = physical_address,
.status = 0,
.type_and_cycle = 6 << 10,
.type_and_cycle = kTrb_Link << kTrb_TypeOffset,
.control = 0,
};
}
XhciTrb CreateEnableSlotTrb() {
return {
.parameter = 0,
.status = 0,
// FIXME: Accept Cycle Bit as a parameter.
.type_and_cycle = kTrb_EnableSlot << kTrb_TypeOffset | kTrb_Cycle,
// FIXME: Specify slot type if necessary. (XHCI Table 7-9)?
.control = 0,
};
}
XhciTrb CreateNoOpCommandTrb() {
return {
.parameter = 0,
.status = 0,
// FIXME: Accept Cycle Bit as a parameter.
.type_and_cycle = kTrb_NoOpCommand << kTrb_TypeOffset | kTrb_Cycle,
.control = 0,
};
}

View File

@ -3,3 +3,6 @@
#include "xhci/xhci.h"
XhciTrb CreateLinkTrb(uint64_t physical_address);
XhciTrb CreateEnableSlotTrb();
XhciTrb CreateNoOpCommandTrb();

View File

@ -7,6 +7,12 @@
TrbRing::TrbRing() {
uint64_t number_trbs = 0x1000 / sizeof(XhciTrb);
page_ = mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &phys_address_);
// Zero out data.
uint64_t* page_ptr = reinterpret_cast<uint64_t*>(page_.vaddr());
for (uint64_t i = 0; i < 0x1000 / sizeof(uint64_t); i++) {
page_ptr[i] = 0;
}
trb_list_ = glcr::ArrayView<XhciTrb>(
reinterpret_cast<XhciTrb*>(page_.vaddr()), number_trbs);
@ -22,3 +28,9 @@ void TrbRingWriter::EnqueueTrb(const XhciTrb& trb) {
trb_list_[ptr] = trb;
}
bool TrbRingReader::HasNext() {
return (trb_list_[dequeue_ptr_].type_and_cycle & 0x1) == cycle_bit_;
}
XhciTrb TrbRingReader::Read() { return trb_list_[dequeue_ptr_++]; }

View File

@ -24,3 +24,15 @@ class TrbRingWriter : public TrbRing {
private:
uint64_t enqueue_ptr_ = 0;
};
class TrbRingReader : public TrbRing {
public:
bool HasNext();
XhciTrb Read();
uint64_t DequeuePtr() { return phys_address_ + dequeue_ptr_; }
private:
uint64_t dequeue_ptr_ = 0;
uint8_t cycle_bit_ = 1;
};

View File

@ -73,7 +73,10 @@ struct XhciRuntime {
uint64_t reserved3;
uint64_t reserved4;
XhciInterrupter interrupters[1024];
} __attribute__((packed));
struct XhciDoorbells {
uint32_t doorbell[256];
} __attribute__((packed));
struct XhciPort {

View File

@ -1,29 +1,67 @@
#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/trb.h"
#include "xhci/xhci.h"
glcr::ErrorOr<XhciDriver> XhciDriver::InitiateDriver(
void interrupt_thread(void* void_driver) {
XhciDriver* driver = static_cast<XhciDriver*>(void_driver);
driver->InterruptLoop();
crash("Driver returned from interrupt loop", glcr::INTERNAL);
}
glcr::ErrorOr<glcr::UniquePtr<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();
// Have to make this a heap object so that the reference passed to the
// interrupt loop remains valid.
glcr::UniquePtr<XhciDriver> driver(new XhciDriver(glcr::Move(pci_region)));
driver->ParseMmioStructures();
driver->FreeExistingMemoryStructures();
driver->ResetController();
driver->StartInterruptThread();
dbgln("XHCI CONTROLLER RESET");
driver.DumpDebugInfo();
check(ZThreadSleep(100));
driver.DumpDebugInfo();
return driver;
driver->NoOpCommand();
driver->InitiateDevices();
return glcr::Move(driver);
}
void XhciDriver::InterruptLoop() {
while (true) {
while ((runtime_->interrupters[0].management & 0x1) != 0x1) {
check(ZThreadSleep(50));
}
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. {x}", trb.parameter);
break;
case 34:
dbgln("Port Status Change Event, enabling slot.");
command_ring_.EnqueueTrb(CreateEnableSlotTrb());
doorbells_->doorbell[0] = 0;
break;
default:
dbgln("Unknown TRB Type {x} received.", type);
break;
}
}
runtime_->interrupters[0].event_ring_dequeue_pointer =
event_ring_.DequeuePtr() | 0x8;
runtime_->interrupters[0].management |= 0x1;
}
}
void XhciDriver::DumpDebugInfo() {
@ -62,6 +100,7 @@ void XhciDriver::DumpDebugInfo() {
if ((port->status_and_control & 0x3) == 0x1) {
dbgln("Resetting: {x}", i);
port->status_and_control |= 0x10;
doorbells_->doorbell[0] = 0;
}
}
@ -88,10 +127,22 @@ glcr::ErrorCode XhciDriver::ParseMmioStructures() {
runtime_ = reinterpret_cast<XhciRuntime*>(mmio_regions_.vaddr() +
capabilities_->runtime_offset);
doorbells_ = reinterpret_cast<XhciDoorbells*>(mmio_regions_.vaddr() +
capabilities_->doorbell_offset);
return glcr::OK;
}
glcr::ErrorCode XhciDriver::ResetController() {
// Stop the Host Controller.
// FIXME: Do this before freeing existing structures.
operational_->usb_command &= ~0x1;
while ((operational_->usb_status & 0x1) != 0x1) {
dbgln("Waiting XHCI Halt.");
RET_ERR(ZThreadSleep(50));
}
// Host Controller Reset
operational_->usb_command |= 0x2;
@ -115,8 +166,13 @@ glcr::ErrorCode XhciDriver::ResetController() {
return glcr::OK;
}
void XhciDriver::StartInterruptThread() {
interrupt_thread_ = Thread(interrupt_thread, this);
}
glcr::ErrorCode XhciDriver::InitiateCommandRing() {
operational_->command_ring_control = command_ring_.PhysicalAddress();
dbgln("CRC: {x}", operational_->command_ring_control);
return glcr::OK;
}
@ -155,11 +211,35 @@ glcr::ErrorCode XhciDriver::InitiateEventRingSegmentTable() {
event_ring_.PhysicalAddress();
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_dequeue_pointer =
event_ring_.PhysicalAddress() | 0x8;
runtime_->interrupters[0].event_ring_segment_table_size = 1;
runtime_->interrupters[0].event_ring_segment_table_base_address = erst_phys;
// Enable interrupts.
runtime_->interrupters[0].management |= 0x2;
runtime_->interrupters[0].moderation = 4000;
operational_->usb_command |= 0x4;
return glcr::OK;
}
glcr::ErrorCode XhciDriver::InitiateDevices() {
uint64_t max_ports = (capabilities_->hcs_params_1 & 0xFF00'0000) >> 24;
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;
}
}
return glcr::OK;
}
glcr::ErrorCode XhciDriver::NoOpCommand() {
command_ring_.EnqueueTrb(CreateNoOpCommandTrb());
doorbells_->doorbell[0] = 0;
return glcr::OK;
}

View File

@ -1,6 +1,8 @@
#pragma once
#include <glacier/memory/unique_ptr.h>
#include <glacier/status/error_or.h>
#include <mammoth/proc/thread.h>
#include <mammoth/util/memory_region.h>
#include <yellowstone/yellowstone.yunq.client.h>
@ -9,22 +11,27 @@
class XhciDriver {
public:
static glcr::ErrorOr<XhciDriver> InitiateDriver(
static glcr::ErrorOr<glcr::UniquePtr<XhciDriver>> InitiateDriver(
yellowstone::YellowstoneClient& yellowstone);
XhciDriver(const XhciDriver&) = delete;
XhciDriver(XhciDriver&&) = default;
void DumpDebugInfo();
void InterruptLoop();
private:
// MMIO Structures.
mmth::OwnedMemoryRegion pci_region_;
PciDeviceHeader* pci_device_header_;
mmth::OwnedMemoryRegion mmio_regions_;
XhciCapabilities* capabilities_;
XhciOperational* operational_;
volatile XhciCapabilities* capabilities_;
volatile XhciOperational* operational_;
// TODO: Extended Capabilities.
XhciRuntime* runtime_;
// TODO: Doorbell Array.
volatile XhciRuntime* runtime_;
volatile XhciDoorbells* doorbells_;
// Host Memory Regions.
TrbRingWriter command_ring_;
@ -35,7 +42,8 @@ class XhciDriver {
mmth::OwnedMemoryRegion event_ring_segment_table_mem_;
XhciEventRingSegmentTableEntry* event_ring_segment_table_;
TrbRing event_ring_;
TrbRingReader event_ring_;
Thread interrupt_thread_;
XhciDriver(mmth::OwnedMemoryRegion&& pci_space);
@ -43,8 +51,13 @@ class XhciDriver {
glcr::ErrorCode FreeExistingMemoryStructures() { return glcr::OK; }
glcr::ErrorCode ResetController();
void StartInterruptThread();
glcr::ErrorCode InitiateCommandRing();
glcr::ErrorCode InitiateDeviceContextBaseArray();
glcr::ErrorCode InitiateEventRingSegmentTable();
glcr::ErrorCode InitiateDevices();
glcr::ErrorCode NoOpCommand();
};