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"
30#include "modules/drivers/common/InterruptProbe.h"
34AhciController::AhciController(
Device* pci)
43 m_HardwareOwned(false),
47 setSpecificType(
String(
"ahci-controller"));
49AhciController::~AhciController() {
51 for (
size_t i = 0; i < 32; ++i)
54bool AhciController::claimOwnership(uint32_t version) {
55 if (version < 0x00010200 || !(m_Registers->
read32(Cap2) & 1U))
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;
62 ownership = m_Registers->
read32(Bohc);
63 busy |= (ownership & (1U << 4)) != 0;
64 if (!(ownership & ((1U << 4) | 1U)))
66 Time::delay(Time::Multiplier::Millisecond);
67 }
while (Time::getTicks() - start < 25 * Time::Multiplier::Millisecond);
71 const auto deadline = start + 2 * Time::Multiplier::Second + 25 * Time::Multiplier::Millisecond;
73 ownership = m_Registers->
read32(Bohc);
74 if (!(ownership & ((1U << 4) | 1U)))
76 Time::delay(Time::Multiplier::Millisecond);
77 }
while (Time::getTicks() < deadline);
78 ERROR(
"AHCI: firmware did not release controller ownership");
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)
88 Time::delay(Time::Multiplier::Millisecond);
90 m_Registers->
write32(Enable, Ghc);
91 return (m_Registers->
read32(Ghc) & Enable) != 0;
93bool AhciController::initialiseController() {
97 auto& pci = PciBus::instance();
99 if (!pci.inspectFunction(m_Pci, inherited)) {
100 ERROR(
"AHCI: unsupported PCI state (D0, valid capabilities and PIC INTx required)");
103 m_OriginalCommand = inherited.command;
104 const uint32_t bar = pci.readConfigSpace(m_Pci, 9);
105 if ((bar & 7U) || !((bar & ~15U)))
108 for (
auto* address : m_Pci->
addresses()) {
109 if (address->m_Name ==
"bar5" && !address->m_IsIoSpace && address->m_Address == (bar & ~15U)) {
114 if (!mapping || mapping->
m_Size < PortBase + PortStride)
118 if (!pci.updateCommand(m_Pci, 0, 2U | 0x400U))
121 m_Registers = mapping->
m_Io;
122 if (!m_Registers || m_Registers->
size() < mapping->
m_Size)
124 const uint32_t version = m_Registers->
read32(Vs);
125 if (version < 0x00010000 || version == 0xffffffffU || !claimOwnership(version))
127 m_HardwareOwned =
true;
128 m_Implemented = m_Registers->
read32(Pi);
131 uint32_t speedLimits[32] = {};
132 for (
size_t i = 0; i < 32; ++i) {
133 if (!(m_Implemented & (1U << i)))
135 if (mapping->
m_Size < PortBase + (i + 1) * PortStride)
137 speedLimits[i] = m_Registers->
read32(PortBase + i * PortStride + Sctl) & 0xf0U;
140 ERROR(
"AHCI: HBA reset did not complete within one second");
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);
148 if (!pci.disableMessageInterrupts(m_Pci, inherited) ||
149 !pci.resourcesUnchanged(m_Pci, inherited) || !pci.updateCommand(m_Pci, 0, 6U | 0x400U))
151 for (
size_t i = 0; i < 32; ++i) {
152 if (!(m_Implemented & (1U << i)))
154 m_Registers->
write32(speedLimits[i], PortBase + i * PortStride + Sctl);
155 auto* port =
new AhciPort(m_Registers, i);
157 if (!port->initialise(capabilities, version, extended)) {
159 m_Ports[i] =
nullptr;
163 if (disk->initialise()) {
168 m_Ports[i] =
nullptr;
173 m_Registers->
write32(m_Implemented, Is);
175 IrqPolicy::pciIntxThreaded());
177 ERROR(
"AHCI: could not register PCI INTx");
182 for (
auto* port : m_Ports)
184 port->enableInterrupts();
185 m_Registers->
write32(m_Implemented, Is);
187 if (!pci.updateCommand(m_Pci, 0x400U, 6U))
189 m_Registers->
write32(Enable | InterruptEnable, Ghc);
190 (void)m_Registers->
read32(Ghc);
192 uint16_t identifyData[256]{};
193 for (
size_t i = 0; i < 32; ++i) {
196 if (!InterruptProbe::run([&] {
return identify(i, identifyData,
true); },
197 [&] {
return m_Ports[i]->interruptCompletions(); })) {
198 ERROR(
"AHCI: interrupt delivery probe failed on port " << i);
204 disk->publishEndpoint();
205 NOTICE(
"AHCI: SATA disk ready on port " << disk->port());
207 NOTICE(
"AHCI: controller ready with " <<
getNumChildren() <<
" SATA disks, shared INTx");
215 return IrqDisposition::NotHandled;
216 const uint32_t pending = m_Registers->
read32(Is) & m_Implemented;
217 bool handled = pending != 0;
218 for (
size_t i = 0; i < 32; ++i) {
220 handled |= m_Ports[i]->interrupt(pending & (1U << i));
221 else if (pending & (1U << i)) {
222 const size_t base = PortBase + i * PortStride;
223 m_Registers->
write32(m_Registers->
read32(base + Serr), base + Serr);
224 m_Registers->
write32(m_Registers->
read32(base + PortIs), base + PortIs);
228 m_Registers->
write32(pending, Is);
229 (void)m_Registers->
read32(Is);
230 return handled ? IrqDisposition::Handled : IrqDisposition::NotHandled;
232bool AhciController::identify(
size_t port, uint16_t* words,
bool interruptProbe) {
233 return port < 32 && m_Ports[port] &&
234 m_Ports[port]->command(0xec, 0, 0, words, 512,
false, m_Interrupts, interruptProbe);
236bool AhciController::readWrite(
size_t port, uint64_t lba, uint16_t sectors,
void* buffer,
237 size_t bytes,
bool writing) {
238 if (port >= 32 || !m_Ports[port] || !sectors ||
239 bytes !=
static_cast<size_t>(sectors) * m_Ports[port]->sectorBytes() || lba >= (1ULL << 48) ||
240 sectors > (1ULL << 48) - lba)
242 return port < 32 && m_Ports[port] &&
243 m_Ports[port]->command(writing ? 0x35 : 0x25, lba, sectors, buffer, bytes, writing,
246void AhciController::configureDisk(
size_t port,
size_t sectorBytes,
size_t queueDepth) {
247 if (port < 32 && m_Ports[port])
248 m_Ports[port]->configureDisk(sectorBytes, queueDepth);
250bool AhciController::readBatch(
size_t port,
Disk::ReadBuffer* buffers,
size_t count) {
251 return port < 32 && m_Ports[port] && m_Ports[port]->readBatch(buffers, count, m_Interrupts);
254bool AhciController::writeBatch(
size_t port,
Disk::WriteBuffer* buffers,
size_t count) {
255 return port < 32 && m_Ports[port] && m_Ports[port]->writeBatch(buffers, count, m_Interrupts);
258bool AhciController::flush(
size_t port,
bool extended) {
259 return port < 32 && m_Ports[port] &&
260 m_Ports[port]->command(extended ? 0xea : 0xe7, 0, 0, nullptr, 0, false, m_Interrupts);
262size_t AhciController::interruptCompletions()
const {
264 for (
auto* port : m_Ports)
266 count += port->interruptCompletions();
269void AhciController::shutdown() {
278 m_Interrupts =
false;
279 if (m_HardwareOwned) {
280 m_Registers->
write32(Enable, Ghc);
281 (void)m_Registers->
read32(Ghc);
284 if (m_Irq && !Machine::instance().getIrqManager()->unregisterHandler(m_Irq,
this))
285 panic(
"AHCI: synchronous interrupt retirement failed");
287 for (
auto* port : m_Ports)
292 const uint16_t command =
293 m_HardwareOwned ? (m_OriginalCommand & ~4U) | 0x400U : m_OriginalCommand;
294 if (!PciBus::instance().updateCommand(m_Pci, 0x407U, command & 0x407U))
295 panic(
"AHCI: failed to verify PCI state during shutdown");
300size_t AhciController::maximumOutstanding(
size_t port)
const {
301 return port < 32 && m_Ports[port] ? m_Ports[port]->maximumOutstanding() : 0;
IrqDisposition irq(irq_id_t number) override
void map(size_t forcedSize=0, bool bUser=false, bool bWriteCombine=false, bool bWriteThrough=false)
Device * getChild(size_t n)
virtual Vector< Address * > & addresses()
uint8_t getPciClassCode()
void addChild(Device *pDevice)
uint8_t getPciSubclassCode()
uint8_t getPciProgInterface()
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
void shutdownDiskCaches()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN