The Pedigree Project 0.1
EhciFirmware.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/Pci.h"
5#include "pedigree/kernel/process/TerminationDeferral.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/time/Time.h"
8
9#include "Ehci.h"
10
11namespace {
12struct FirmwareConfig {
13 Device* device;
14 bool read32(uint16_t offset, uint32_t& value) {
15 return PciBus::instance().readConfig32(device, offset, value);
16 }
17 bool write32(uint16_t offset, uint32_t value) {
18 return PciBus::instance().writeConfig32(device, offset, value);
19 }
20 bool write8(uint16_t offset, uint8_t value) {
21 return PciBus::instance().writeConfig8(device, offset, value);
22 }
23 void delay() {
24 Time::delay(Time::Multiplier::Millisecond);
25 }
26};
27} // namespace
28
29bool Ehci::acquireFirmware(uint16_t capability) {
30 FirmwareConfig config{this};
31 const auto result = m_Handoff.acquire(capability, config);
32 if (result == EhciHandoff::AcquireResult::Acquired ||
33 result == EhciHandoff::AcquireResult::AlreadyUnowned)
34 return true;
35 const char* reason = "configuration access failed";
36 switch (result) {
37 case EhciHandoff::AcquireResult::InvalidCapability:
38 reason = "invalid legacy capability";
39 break;
40 case EhciHandoff::AcquireResult::OwnershipTimeout:
41 reason = "BIOS ownership did not clear within 1 second";
42 break;
43 case EhciHandoff::AcquireResult::OwnershipMismatch:
44 reason = "OS ownership was not retained";
45 break;
46 case EhciHandoff::AcquireResult::SmiDisableFailure:
47 reason = "legacy SMI controls did not disable";
48 break;
49 default:
50 break;
51 }
52 ERROR("EHCI: firmware handoff failed: " << reason);
53 return false;
54}
55
56void Ehci::returnToFirmware() {
57 if (!m_Handoff.wasBiosOwned())
58 return;
59 FirmwareConfig config{this};
60 if (m_HardwareTouched) {
61 // shutdownController has stopped DMA and detached every OS schedule. Give
62 // firmware its decoder/DMA permissions back, never its stale queue pointers.
63 constexpr uint16_t CommandMask = 2 | 4 | 0x400;
64 if (!PciBus::instance().updateCommand(this, CommandMask, m_FirmwarePciCommand & CommandMask)) {
65 ERROR("EHCI: firmware recovery could not restore PCI access; controller remains stopped");
66 return;
67 }
68 }
69 const auto result = m_Handoff.acquired() ? m_Handoff.release(config, m_ControllerStopped)
70 : m_Handoff.withdrawRequest(config);
71 if (result == EhciHandoff::ReleaseResult::FirmwareConfirmed)
72 NOTICE("EHCI: firmware reclaimed controller; legacy input recovery requested");
73 else if (result == EhciHandoff::ReleaseResult::Released)
74 WARNING("EHCI: OS ownership released; firmware did not confirm reclaim within 1 second");
75 else if (result != EhciHandoff::ReleaseResult::NotOwned)
76 ERROR("EHCI: firmware recovery failed; legacy USB input may remain unavailable");
77}
78
79bool Ehci::beginStartupActivity() {
80 LockGuard<Spinlock> guard(m_StartupLock);
81 return m_Startup.begin();
82}
83
84void Ehci::endStartupActivity(UsbStartup::Outcome outcome) {
85 LockGuard<Spinlock> guard(m_StartupLock);
86 if (m_Startup.finish(outcome))
87 m_RecoveryWake.release();
88}
89
90void Ehci::completeInitialPort(size_t port) {
91 LockGuard<Spinlock> guard(m_StartupLock);
92 const uint16_t bit = uint16_t{1} << port;
93 if (!(m_InitialPortMask & bit))
94 return;
95 m_InitialPortMask &= ~bit;
96 if (!m_InitialPortMask && m_Startup.finish(UsbStartup::Outcome::Neutral))
97 m_RecoveryWake.release();
98}
99
100void Ehci::startFirmwareRecovery() {
101#if THREADS
102 if (!m_Handoff.wasBiosOwned())
103 return;
104 m_RecoveryThread.adopt(
105 new Thread(Processor::information().getCurrentThread()->getParent(), recoverFirmware, this));
106 m_RecoveryThread->setName("EHCI firmware recovery");
107#endif
108}
109
110int Ehci::recoverFirmware(void* parameter) {
111 TerminationDeferral lifetime;
112 auto* controller = static_cast<Ehci*>(parameter);
113 const bool woken = controller->m_RecoveryWake.acquireForCompletion();
114 (void)woken;
115 if (controller->m_RecoveryStopping)
116 return 0;
117 WARNING("EHCI: startup failed before a device driver became usable; returning to firmware");
118 controller->shutdownController();
119 controller->returnToFirmware();
120 return 0;
121}
Device * getParent() const
Definition Device.h:166
ReleaseResult withdrawRequest(Config &config, size_t pollLimit=DefaultPollLimit)
AcquireResult acquire(uint16_t offset, Config &config, size_t pollLimit=DefaultPollLimit)
Definition EhciHandoff.h:72
ReleaseResult release(Config &config, bool callerQuiescedOrNeverTouched, size_t pollLimit=DefaultPollLimit)
Definition Ehci.h:57
static ProcessorInformation & information()
void release(size_t n=1)
Definition Semaphore.cc:549
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:372