The Pedigree Project 0.1
Xhci.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "Xhci.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/Log.h"
5#include "pedigree/kernel/machine/IrqManager.h"
6#include "pedigree/kernel/machine/Machine.h"
7#include "pedigree/kernel/machine/Pci.h"
8#include "pedigree/kernel/machine/PciFunctionState.h"
9#include "pedigree/kernel/panic.h"
10#include "pedigree/kernel/process/Thread.h"
11#include "pedigree/kernel/processor/IoBase.h"
12#include "pedigree/kernel/processor/Processor.h"
13#include "pedigree/kernel/processor/ProcessorInformation.h"
14#include "pedigree/kernel/time/Time.h"
15#include "pedigree/kernel/utilities/utility.h"
16using namespace XhciHw;
17Xhci::Xhci(Device* device)
18 : UsbHub(device),
19 RequestQueue(MakeConstantString("xHCI")),
20 m_Pci(this),
21 m_Events("xHCI events"),
22 m_Erst("xHCI ERST"),
23 m_Dcbaa("xHCI DCBAA"),
24 m_Input("xHCI input context"),
25 m_ScratchPointers("xHCI scratchpad pointers") {}
26Xhci::~Xhci() {
27 shutdown();
28}
29uint32_t Xhci::read(size_t offset) const {
30 return m_Registers->read32(offset);
31}
32void Xhci::write(size_t offset, uint32_t value) {
33 m_Registers->write32(value, offset);
34}
35void Xhci::write64(size_t offset, uint64_t value) {
36 write(offset, value);
37 write(offset + 4, value >> 32);
38}
39bool Xhci::wait(size_t offset, uint32_t mask, uint32_t expected, size_t milliseconds) {
40 const auto deadline = Time::getTicks() + milliseconds * Time::Multiplier::Millisecond;
41 do {
42 const uint32_t value = read(offset);
43 if (value != 0xffffffffU && (value & mask) == expected)
44 return true;
45 Time::delay(Time::Multiplier::Millisecond);
46 } while (Time::getTicks() < deadline);
47 return false;
48}
49bool Xhci::parseCapabilities(uint32_t hcc) {
50 size_t offset = (hcc >> 16) * 4U;
51 for (size_t count = 0; offset && count < 256; ++count) {
52 if (offset + 16 > m_Registers->size())
53 return false;
54 const uint32_t cap = read(offset);
55 if ((cap & 255U) == 1) {
56 write(offset, cap | (1U << 24));
57 if (!wait(offset, (1U << 16) | (1U << 24), 1U << 24, 5000))
58 return false;
59 write(offset + 4, (read(offset + 4) & 0x000e1feeU) | 0xe0000000U);
60 if (read(offset + 4) & 0x0000e011U)
61 return false;
62 } else if ((cap & 255U) == 2) {
63 const uint32_t ports = read(offset + 8);
64 const size_t first = ports & 255U, count = (ports >> 8) & 255U;
65 const size_t psiCount = ports >> 28;
66 const uint8_t major = cap >> 24;
67 if (!first || first - 1 + count > m_PortCount || (major != 2 && major != 3) ||
68 read(offset + 4) != 0x20425355U || offset + 16 + psiCount * 4 > m_Registers->size())
69 return false;
70 for (size_t port = first - 1; port < first - 1 + count; ++port) {
71 auto& target = m_Ports[port];
72 if (target.major)
73 return false;
74 target.major = major;
75 target.slotType = read(offset + 12) & 31U;
76 if (!psiCount) {
77 if (major == 2) {
78 target.speeds[1] = FullSpeed;
79 target.speeds[2] = LowSpeed;
80 target.speeds[3] = HighSpeed;
81 target.validSpeed[1] = target.validSpeed[2] = target.validSpeed[3] = true;
82 } else {
83 const uint8_t minor = (cap >> 16) & 255U;
84 const size_t last = minor >= 0x20 ? 7 : minor >= 0x10 ? 5 : 4;
85 for (size_t id = 4; id <= last; ++id) {
86 target.speeds[id] = SuperSpeed;
87 target.validSpeed[id] = true;
88 }
89 }
90 }
91 for (size_t i = 0; i < psiCount; ++i) {
92 const uint32_t psi = read(offset + 16 + i * 4);
93 const uint8_t id = psi & 15U;
94 if (!id || (psi & 0xc0U))
95 return false;
96 uint64_t rate = psi >> 16;
97 for (size_t exponent = (psi >> 4) & 3U; exponent; --exponent)
98 rate *= 1000;
99 UsbSpeed speed;
100 if (rate == 1500000)
101 speed = LowSpeed;
102 else if (rate == 12000000)
103 speed = FullSpeed;
104 else if (rate == 480000000)
105 speed = HighSpeed;
106 else if (rate >= 5000000000ULL)
107 speed = SuperSpeed;
108 else
109 return false;
110 target.speeds[id] = speed;
111 target.validSpeed[id] = true;
112 }
113 }
114 }
115 const size_t next = (cap >> 8) & 255U;
116 if (!next)
117 return true;
118 offset += next * 4;
119 }
120 return !offset;
121}
122bool Xhci::initialiseController() {
123#if PEDIGREE_USB_SMOKE_TESTS
124 if (!completionRegressions())
125 return false;
126#endif
127 auto& pci = PciBus::instance();
128 PciFunctionState::State pciState{};
129 if (!pci.inspectFunction(m_Pci, pciState)) {
130 ERROR("xHCI: unsupported inherited PCI function state");
131 return false;
132 }
133 const uint32_t bar = pciState.bars[0];
134 if ((bar & 1U) || ((bar & 6U) != 0 && (bar & 6U) != 4))
135 return false;
136 uint64_t base = bar & ~15U;
137 if (bar & 4U)
138 base |= uint64_t{pciState.bars[1]} << 32;
139 Device::Address* mapping = nullptr;
140 for (auto* address : m_Pci->addresses())
141 if (address->m_Name == "bar0" && base && !address->m_IsIoSpace && address->m_Address == base)
142 mapping = address;
143 if (!mapping || mapping->m_Size < 0x500)
144 return false;
145 // Keep firmware DMA live until its ownership semaphore has been released.
146 if (!pci.updateCommand(m_Pci, 0, 2))
147 return false;
148 mapping->map();
149 m_Registers = mapping->m_Io;
150 if (!m_Registers)
151 return false;
152 const uint32_t version = read(0);
153 m_Op = version & 255U;
154 const uint32_t hcs1 = read(4), hcs2 = read(8), hcc = read(16);
155 const size_t portCount = hcs1 >> 24;
156 if (!portCount || portCount > MaxPorts)
157 return false;
158 m_PortCount = portCount;
159 m_SlotCount = (hcs1 & 255U) < MaxSlots ? hcs1 & 255U : MaxSlots;
160 m_ContextSize = hcc & 4U ? 64 : 32;
161 m_Doorbells = read(20) & ~3U;
162 m_Runtime = read(24) & ~31U;
163 if ((version >> 16) < 0x100 || m_Op < 0x20 || !m_SlotCount || !m_PortCount ||
164 m_PortCount > MaxPorts || m_Op + 0x400 + m_PortCount * 16 > m_Registers->size() ||
165 m_Runtime + 0x40 > m_Registers->size() ||
166 m_Doorbells + (m_SlotCount + 1) * 4 > m_Registers->size() || !parseCapabilities(hcc))
167 return false;
168 for (size_t i = 0; i < m_PortCount; ++i)
169 if (!m_Ports[i].major)
170 return false;
171 if (!wait(m_Op + 4, 1U << 11, 0, 1000))
172 return false;
173 m_HardwareOwned = true;
174 write(m_Op, read(m_Op) & ~5U);
175 if (!wait(m_Op + 4, 1, 1, 1000))
176 return false;
177 write(m_Op, read(m_Op) | 2U);
178 if (!wait(m_Op, 2, 0, 1000) || !wait(m_Op + 4, 1U << 11, 0, 1000) || !(read(m_Op + 8) & 1U))
179 return false;
180 if (!pci.updateCommand(m_Pci, 4, 2 | 0x400) || !pci.disableMessageInterrupts(m_Pci, pciState)) {
181 ERROR("xHCI: could not establish masked PCI interrupt state");
182 return false;
183 }
184 if (!m_Commands.initialise() || !allocate(m_Events, 1) || !allocate(m_Erst, 1) ||
185 !allocate(m_Dcbaa, 1) || !allocate(m_Input, 1))
186 return false;
187 const size_t scratchpads = ((hcs2 >> 27) & 31U) | (((hcs2 >> 21) & 31U) << 5);
188 if (scratchpads > 32)
189 return false;
190 auto* dcbaa = static_cast<uint64_t*>(m_Dcbaa.virtualAddress());
191 if (scratchpads) {
192 if (!allocate(m_ScratchPointers, 1))
193 return false;
194 dcbaa[0] = m_ScratchPointers.physicalAddress();
195 auto* pointers = static_cast<uint64_t*>(m_ScratchPointers.virtualAddress());
196 for (size_t i = 0; i < scratchpads; ++i) {
197 m_Scratch[i] = new MemoryRegion("xHCI scratchpad");
198 if (!allocate(*m_Scratch[i], 1))
199 return false;
200 pointers[i] = m_Scratch[i]->physicalAddress();
201 }
202 }
203 auto* erst = static_cast<uint64_t*>(m_Erst.virtualAddress());
204 erst[0] = m_Events.physicalAddress();
205 erst[1] = RingEntries;
206 // Controllers may fetch the segment table as soon as ERSTBA is programmed.
207 FENCE();
208 if (!pci.resourcesUnchanged(m_Pci, pciState) || !pci.updateCommand(m_Pci, 0, 6 | 0x400)) {
209 ERROR("xHCI: PCI resources or DMA command changed during handoff");
210 return false;
211 }
212 write64(m_Op + 0x30, m_Dcbaa.physicalAddress());
213 write64(m_Op + 0x18, m_Commands.address() | 1U);
214 write(m_Op + 0x38, m_SlotCount);
215 write(m_Runtime + 0x28, 1);
216 write64(m_Runtime + 0x30, m_Erst.physicalAddress());
217 write64(m_Runtime + 0x38, m_Events.physicalAddress());
218 write(m_Runtime + 0x24, 0);
219 write(m_Runtime + 0x20, 1);
220 write(m_Op + 4, 0x1c);
221 if (read(m_Op + 4) & ((1U << 12) | 4U))
222 return false;
223 FENCE();
225 for (size_t port = 0; port < m_PortCount; ++port)
226 if (!m_PortChanges[port].configure(*this, 0, port))
227 return false;
228 m_DeliveryThread =
229 new Thread(Processor::information().getCurrentThread()->getParent(), deliveryWorker, this);
230 m_Irq = Machine::instance().getIrqManager()->registerPciIrqHandler(this, m_Pci,
231 IrqPolicy::pciIntxThreaded());
232 if (!m_Irq)
233 return false;
234 {
235 LockGuard<Mutex> lock(m_Lock);
236 if (!pci.updateCommand(m_Pci, 0x400, 6))
237 return false;
238 m_Online = true;
239 write(m_Runtime + 0x20, 3);
240 write(m_Op, 13);
241 (void)read(m_Op);
242 }
243 if (!wait(m_Op + 4, 1, 0, 1000))
244 return false;
245 for (size_t port = 0; port < m_PortCount; ++port) {
246 LockGuard<Mutex> lock(m_Lock);
247 const size_t reg = m_Op + 0x400 + port * 16;
248 const uint32_t status = read(reg);
249 write(reg, (status & 0x0e00c200U) | (1U << 9) | (status & PortChanges));
250 (void)read(reg);
251 if (status & 1U)
252 notifyPortLocked(port);
253 }
254 NOTICE("xHCI: controller ready, " << Dec << m_PortCount << " ports, " << m_SlotCount
255 << " slots, shared INTx" << Hex);
256 return true;
257}
258bool Xhci::command(Trb trb, uint8_t* returnedSlot) {
259 LockGuard<Mutex> serial(m_CommandLock);
260 {
261 LockGuard<Mutex> lock(m_Lock);
262 if (!m_Online)
263 return false;
264 const size_t drained = m_CommandDone.drainAvailable();
265 (void)drained;
266 m_CommandCode = 0;
267 if (!m_Commands.enqueue(&trb, 1, &m_CommandAddress))
268 return false;
269 write(m_Doorbells, 0);
270 (void)read(m_Op + 4);
271 }
272 const auto deadline = Time::getTicks() + 5 * Time::Multiplier::Second;
273 while (!m_CommandDone.acquireForCompletion(1, 0, 10000)) {
274 LockGuard<Mutex> lock(m_Lock);
275 collectEventsLocked(false);
276 if (!m_Online)
277 return false;
278 if (Time::getTicks() >= deadline) {
279 failLocked();
280 return false;
281 }
282 }
283 LockGuard<Mutex> lock(m_Lock);
284 if (returnedSlot)
285 *returnedSlot = m_CommandSlot;
286 if (m_CommandCode != 1)
287 WARNING("xHCI: command " << ((trb.control >> 10) & 63U) << " failed: " << m_CommandCode);
288 return m_Online && m_CommandCode == 1;
289}
IoBase * m_Io
Definition Device.h:110
size_t m_Size
Definition Device.h:104
void map(size_t forcedSize=0, bool bUser=false, bool bWriteCombine=false, bool bWriteThrough=false)
Definition Device.cc:334
virtual Vector< Address * > & addresses()
Definition Device.h:249
Device * getParent() const
Definition Device.h:161
virtual void write32(uint32_t value, size_t offset=0)=0
virtual uint32_t read32(size_t offset=0)=0
virtual size_t size() const =0
virtual irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *pDevice, const IrqPolicy &policy)=0
Special memory entity in the kernel's virtual address space.
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
static ProcessorInformation & information()
virtual void initialise()
MUST_USE_RESULT size_t drainAvailable()
Definition Semaphore.cc:528
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:369
@ Dec
Definition Log.h:144
@ Hex
Definition Log.h:142