The Pedigree Project 0.1
AhciController.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 * SPDX-License-Identifier: ISC
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16#include "AhciController.h"
17#include "pedigree/kernel/LockGuard.h"
18#include "pedigree/kernel/Log.h"
19#include "pedigree/kernel/machine/IrqManager.h"
20#include "pedigree/kernel/machine/Machine.h"
21#include "pedigree/kernel/machine/Pci.h"
22#include "pedigree/kernel/panic.h"
23#include "pedigree/kernel/processor/IoBase.h"
24#include "pedigree/kernel/time/Time.h"
25#include "pedigree/kernel/utilities/new"
26
27#include "AhciDisk.h"
28#include "AhciPort.h"
29#include "Registers.h"
30#include "modules/drivers/common/InterruptProbe.h"
31
32using namespace Ahci;
33
34AhciController::AhciController(Device* pci)
36 m_Pci(pci),
37 m_Registers(nullptr),
38 m_Ports{},
39 m_Irq(0),
40 m_Implemented(0),
41 m_OriginalCommand(0),
42 m_PciChanged(false),
43 m_HardwareOwned(false),
44 m_Interrupts(false),
45 m_Stopping(false),
46 m_Shutdown(false) {
47 setSpecificType(String("ahci-controller"));
48}
49AhciController::~AhciController() {
50 shutdown();
51 for (size_t i = 0; i < 32; ++i)
52 delete m_Ports[i];
53}
54bool AhciController::claimOwnership(uint32_t version) {
55 if (version < 0x00010200 || !(m_Registers->read32(Cap2) & 1U))
56 return true;
57 uint32_t ownership = m_Registers->read32(Bohc);
58 m_Registers->write32((ownership & ~(1U << 3)) | (1U << 1), Bohc);
59 const auto start = Time::getTicks();
60 bool busy = (ownership & (1U << 4)) != 0;
61 do {
62 ownership = m_Registers->read32(Bohc);
63 busy |= (ownership & (1U << 4)) != 0;
64 if (!(ownership & ((1U << 4) | 1U)))
65 return true;
66 Time::delay(Time::Multiplier::Millisecond);
67 } while (Time::getTicks() - start < 25 * Time::Multiplier::Millisecond);
68 // AHCI 10.6.3 permits takeover after 25 ms when firmware never declared busy.
69 if (!busy)
70 return true;
71 const auto deadline = start + 2 * Time::Multiplier::Second + 25 * Time::Multiplier::Millisecond;
72 do {
73 ownership = m_Registers->read32(Bohc);
74 if (!(ownership & ((1U << 4) | 1U)))
75 return true;
76 Time::delay(Time::Multiplier::Millisecond);
77 } while (Time::getTicks() < deadline);
78 ERROR("AHCI: firmware did not release controller ownership");
79 return false;
80}
81bool AhciController::reset() {
82 m_Registers->write32(Enable, Ghc);
83 m_Registers->write32(Enable | Reset, Ghc);
84 const auto deadline = Time::getTicks() + Time::Multiplier::Second;
85 while (m_Registers->read32(Ghc) & Reset) {
86 if (Time::getTicks() >= deadline)
87 return false;
88 Time::delay(Time::Multiplier::Millisecond);
89 }
90 m_Registers->write32(Enable, Ghc);
91 return (m_Registers->read32(Ghc) & Enable) != 0;
92}
93bool AhciController::initialiseController() {
94 if (!m_Pci || m_Pci->getPciClassCode() != 1 || m_Pci->getPciSubclassCode() != 6 ||
95 m_Pci->getPciProgInterface() != 1)
96 return false;
97 auto& pci = PciBus::instance();
99 if (!pci.inspectFunction(m_Pci, inherited, false)) {
100 ERROR("AHCI: unsupported PCI state (D0 and valid capabilities required)");
101 return false;
102 }
103 m_OriginalCommand = inherited.command;
104 const uint32_t bar = pci.readConfigSpace(m_Pci, 9);
105 if ((bar & 7U) || !((bar & ~15U)))
106 return false;
107 Device::Address* mapping = nullptr;
108 for (auto* address : m_Pci->addresses()) {
109 if (address->m_Name == "bar5" && !address->m_IsIoSpace && address->m_Address == (bar & ~15U)) {
110 mapping = address;
111 break;
112 }
113 }
114 if (!mapping || mapping->m_Size < PortBase + PortStride)
115 return false;
116 // Keep the original PCI node responsible for its BAR mapping.
117 m_PciChanged = true;
118 if (!pci.updateCommand(m_Pci, 0, 2U | 0x400U))
119 return false;
120 mapping->map();
121 m_Registers = mapping->m_Io;
122 if (!m_Registers || m_Registers->size() < mapping->m_Size)
123 return false;
124 const uint32_t version = m_Registers->read32(Vs);
125 if (version < 0x00010000 || version == 0xffffffffU || !claimOwnership(version))
126 return false;
127 m_HardwareOwned = true;
128 m_Implemented = m_Registers->read32(Pi);
129 if (!m_Implemented)
130 return false;
131 uint32_t speedLimits[32] = {};
132 for (size_t i = 0; i < 32; ++i) {
133 if (!(m_Implemented & (1U << i)))
134 continue;
135 if (mapping->m_Size < PortBase + (i + 1) * PortStride)
136 return false;
137 speedLimits[i] = m_Registers->read32(PortBase + i * PortStride + Sctl) & 0xf0U;
138 }
139 if (!reset()) {
140 ERROR("AHCI: HBA reset did not complete within one second");
141 return false;
142 }
143 const uint32_t capabilities = m_Registers->read32(Cap);
144 const uint32_t extended = version >= 0x00010200 ? m_Registers->read32(Cap2) : 0;
145 if (capabilities & (1U << 7))
146 m_Registers->write32(0, CccCtl);
147 // Attach remapping with bus mastering disabled after the HBA reset.
148 if (!pci.disableMessageInterrupts(m_Pci, inherited) ||
149 !pci.resourcesUnchanged(m_Pci, inherited) || !pci.updateCommand(m_Pci, 4U, 2U | 0x400U)) {
150 return false;
151 }
152 (void)pci.attachDmaRemapping(m_Pci);
153 if (!pci.updateCommand(m_Pci, 0, 6U | 0x400U)) {
154 return false;
155 }
156 for (size_t i = 0; i < 32; ++i) {
157 if (!(m_Implemented & (1U << i)))
158 continue;
159 m_Registers->write32(speedLimits[i], PortBase + i * PortStride + Sctl);
160 auto* port = new AhciPort(m_Registers, i, m_Pci);
161 m_Ports[i] = port;
162 if (!port->initialise(capabilities, version, extended)) {
163 delete port;
164 m_Ports[i] = nullptr;
165 continue;
166 }
167 auto* disk = new AhciDisk(this, i);
168 if (disk->initialise()) {
169 addChild(disk);
170 } else {
171 delete disk;
172 delete port;
173 m_Ports[i] = nullptr;
174 }
175 }
176 if (!getNumChildren())
177 return false;
178 m_Registers->write32(m_Implemented, Is);
179 m_Irq = Machine::instance().getIrqManager()->registerPciMessageIrqHandler(
180 this, m_Pci, IrqPolicy::pciIntxThreaded());
181 if (!m_Irq) {
182 ERROR("AHCI: could not register PCI interrupt");
183 return false;
184 }
185 {
186 LockGuard<Mutex> irqLock(m_IrqLock);
187 for (auto* port : m_Ports)
188 if (port)
189 port->enableInterrupts();
190 m_Registers->write32(m_Implemented, Is);
191 m_Interrupts = true;
192 if (!pci.updateCommand(m_Pci, 0x400U, 6U))
193 return false;
194 m_Registers->write32(Enable | InterruptEnable, Ghc);
195 (void)m_Registers->read32(Ghc);
196 }
197 uint16_t identifyData[256]{};
198 for (size_t i = 0; i < 32; ++i) {
199 if (!m_Ports[i])
200 continue;
201 if (!InterruptProbe::run([&] { return identify(i, identifyData, true); },
202 [&] { return m_Ports[i]->interruptCompletions(); })) {
203 ERROR("AHCI: interrupt delivery probe failed on port " << i);
204 return false;
205 }
206 }
207 for (size_t i = 0; i < getNumChildren(); ++i) {
208 auto* disk = static_cast<AhciDisk*>(getChild(i));
209 disk->publishEndpoint();
210 NOTICE("AHCI: SATA disk ready on port " << disk->port());
211 }
212 NOTICE("AHCI: controller ready with " << getNumChildren() << " SATA disks");
213 return true;
214}
216 LockGuard<Mutex> irqLock(m_IrqLock);
217 if (m_Stopping)
219 if (!m_Interrupts)
220 return IrqDisposition::NotHandled;
221 const uint32_t pending = m_Registers->read32(Is) & m_Implemented;
222 bool handled = pending != 0;
223 for (size_t i = 0; i < 32; ++i) {
224 if (m_Ports[i])
225 handled |= m_Ports[i]->interrupt(pending & (1U << i));
226 else if (pending & (1U << i)) {
227 const size_t base = PortBase + i * PortStride;
228 m_Registers->write32(m_Registers->read32(base + Serr), base + Serr);
229 m_Registers->write32(m_Registers->read32(base + PortIs), base + PortIs);
230 }
231 }
232 if (pending)
233 m_Registers->write32(pending, Is);
234 (void)m_Registers->read32(Is);
235 return handled ? IrqDisposition::Handled : IrqDisposition::NotHandled;
236}
237bool AhciController::identify(size_t port, uint16_t* words, bool interruptProbe) {
238 return port < 32 && m_Ports[port] &&
239 m_Ports[port]->command(0xec, 0, 0, words, 512, false, m_Interrupts, interruptProbe);
240}
241bool AhciController::readWrite(size_t port, uint64_t lba, uint16_t sectors, void* buffer,
242 size_t bytes, bool writing) {
243 return readWrite(port, lba, sectors, buffer, bytes, writing, false);
244}
245bool AhciController::readWrite(size_t port, uint64_t lba, uint16_t sectors, void* buffer,
246 size_t bytes, bool writing, bool cacheFill) {
247 if (port >= 32 || !m_Ports[port] || !sectors ||
248 bytes != static_cast<size_t>(sectors) * m_Ports[port]->sectorBytes() || lba >= (1ULL << 48) ||
249 sectors > (1ULL << 48) - lba)
250 return false;
251 return port < 32 && m_Ports[port] &&
252 m_Ports[port]->command(writing ? 0x35 : 0x25, lba, sectors, buffer, bytes, writing,
253 m_Interrupts, false, cacheFill);
254}
255void AhciController::configureDisk(size_t port, size_t sectorBytes, size_t queueDepth) {
256 if (port < 32 && m_Ports[port])
257 m_Ports[port]->configureDisk(sectorBytes, queueDepth);
258}
259bool AhciController::readBatch(size_t port, Disk::ReadBuffer* buffers, size_t count) {
260 return port < 32 && m_Ports[port] && m_Ports[port]->readBatch(buffers, count, m_Interrupts);
261}
262
263bool AhciController::writeBatch(size_t port, Disk::WriteBuffer* buffers, size_t count) {
264 return port < 32 && m_Ports[port] && m_Ports[port]->writeBatch(buffers, count, m_Interrupts);
265}
266
267bool AhciController::flush(size_t port, bool extended) {
268 return port < 32 && m_Ports[port] &&
269 m_Ports[port]->command(extended ? 0xea : 0xe7, 0, 0, nullptr, 0, false, m_Interrupts);
270}
271size_t AhciController::interruptCompletions() const {
272 size_t count = 0;
273 for (auto* port : m_Ports)
274 if (port)
275 count += port->interruptCompletions();
276 return count;
277}
278void AhciController::shutdown() {
279 if (m_Shutdown)
280 return;
281 // Cache writeback and queued requests still need both hardware and interrupts.
282 Machine::setShutdownDetail("AHCI: draining disk caches");
284 Machine::setShutdownDetail("AHCI: joining request worker");
286 Machine::setShutdownDetail("AHCI: locking interrupt state");
287 {
288 LockGuard<Mutex> irqLock(m_IrqLock);
289 m_Stopping = true;
290 m_Interrupts = false;
291 if (m_HardwareOwned) {
292 m_Registers->write32(Enable, Ghc);
293 (void)m_Registers->read32(Ghc);
294 }
295 }
296 Machine::setShutdownDetail("AHCI: retiring interrupt handler");
297 if (m_Irq && !Machine::instance().getIrqManager()->unregisterHandler(m_Irq, this))
298 panic("AHCI: synchronous interrupt retirement failed");
299 m_Irq = 0;
300 for (auto* port : m_Ports)
301 if (port)
302 port->shutdown();
303 Machine::setShutdownDetail("AHCI: disabling PCI bus mastering");
304 if (m_PciChanged) {
305 // Reset replaced firmware's command state. Never resume its old bus mastering.
306 const uint16_t command =
307 m_HardwareOwned ? (m_OriginalCommand & ~4U) | 0x400U : m_OriginalCommand;
308 if (!PciBus::instance().updateCommand(m_Pci, 0x407U, command & 0x407U))
309 panic("AHCI: failed to verify PCI state during shutdown");
310 }
311 m_Shutdown = true;
312}
313
314size_t AhciController::maximumOutstanding(size_t port) const {
315 return port < 32 && m_Ports[port] ? m_Ports[port]->maximumOutstanding() : 0;
316}
IrqDisposition irq(irq_id_t number) override
IoBase * m_Io
Definition Device.h:115
size_t m_Size
Definition Device.h:109
void map(size_t forcedSize=0, bool bUser=false, bool bWriteCombine=false, bool bWriteThrough=false)
Definition Device.cc:346
size_t getNumChildren()
Definition Device.cc:143
Device * getChild(size_t n)
Definition Device.cc:139
virtual Vector< Address * > & addresses()
Definition Device.h:266
uint8_t getPciClassCode()
Definition Device.h:216
void addChild(Device *pDevice)
Definition Device.cc:135
uint8_t getPciSubclassCode()
Definition Device.h:220
uint8_t getPciProgInterface()
Definition Device.h:232
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 registerPciMessageIrqHandler(IrqHandler *handler, Device *pDevice, const IrqPolicy &intxFallbackPolicy)
Definition IrqManager.cc:25
static void setShutdownDetail(const char *detail)
Definition Machine.cc:38
virtual void destroy()
void shutdownDiskCaches(bool deviceAvailable=true)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
IrqDisposition
Definition IrqHandler.h:31