17#include "pedigree/kernel/LockGuard.h"
18#include "pedigree/kernel/Log.h"
19#include "pedigree/kernel/TargetInfo.h"
20#include "pedigree/kernel/panic.h"
21#include "pedigree/kernel/process/Scheduler.h"
22#include "pedigree/kernel/process/TerminationDeferral.h"
23#include "pedigree/kernel/process/Thread.h"
24#include "pedigree/kernel/processor/IoBase.h"
25#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
26#include "pedigree/kernel/processor/Processor.h"
27#include "pedigree/kernel/processor/ProcessorInformation.h"
28#include "pedigree/kernel/processor/VirtualAddressSpace.h"
29#include "pedigree/kernel/time/Time.h"
30#include "pedigree/kernel/utilities/utility.h"
36AhciPort::AhciPort(
IoBase* registers,
size_t port)
37 : m_Registers(registers),
39 m_Control(
"AHCI command storage"),
47 m_AddressesInstalled(false),
48 m_PolledInterrupt(false),
49 m_InterruptCompletions(0),
50 m_MaximumOutstanding(0),
52AhciPort::~AhciPort() {
55uint32_t AhciPort::read(
size_t reg)
const {
56 return m_Registers->
read32(PortBase + m_Port * PortStride + reg);
58void AhciPort::write(
size_t reg, uint32_t value) {
59 m_Registers->
write32(value, PortBase + m_Port * PortStride + reg);
61void AhciPort::waitForProgress() {
69 Time::delay(Time::Multiplier::Millisecond);
72bool AhciPort::waitClear(
size_t reg, uint32_t bits,
size_t milliseconds) {
73 const auto deadline = Time::getTicks() + milliseconds * Time::Multiplier::Millisecond;
75 if (!(read(reg) & bits))
78 }
while (Time::getTicks() < deadline);
79 return !(read(reg) & bits);
81bool AhciPort::stopEngines() {
83 write(Cmd, read(Cmd) & ~Start);
84 if (!waitClear(Cmd, CommandRunning, 500))
86 write(Cmd, read(Cmd) & ~FisEnable);
87 return waitClear(Cmd, FisRunning, 500);
89void AhciPort::acknowledge(uint32_t status) {
91 const uint32_t error = read(Serr);
95 write(PortIs, status);
97bool AhciPort::initialise(uint32_t capabilities, uint32_t version, uint32_t extendedCapabilities) {
100 ERROR(
"AHCI: port " << m_Port <<
" firmware engines did not stop");
104 m_SlotCount = ((capabilities >> 8) & 31U) + 1;
105 m_SupportsNcq = capabilities & (1U << 30);
106 const size_t controlBytes = TableOffset + m_SlotCount * TableStride;
107 if (pageSize < 4096 || pageSize > MaxTransfer || (MaxTransfer % pageSize))
112 if (!memory.allocateRegion(m_Control, (controlBytes + pageSize - 1) / pageSize, constraints,
116 for (
size_t i = 0; i < m_SlotCount; ++i) {
119 if (!memory.allocateRegion(m_Slots[i].data, MaxTransfer / pageSize,
122 ByteSet(m_Slots[i].data.virtualAddress(), 0, m_Slots[i].data.size());
123 for (
size_t page = 0; page < MaxTransfer / pageSize; ++page) {
124 size_t mappingFlags = 0;
126 static_cast<uint8_t*
>(m_Slots[i].data.virtualAddress()) + page * pageSize,
127 m_Slots[i].pages[page], mappingFlags);
128 if (m_Slots[i].pages[page] >= (uint64_t{1} << 32))
135 write(Fb,
static_cast<uint32_t
>(m_Control.
physicalAddress() + FisOffset));
137 m_AddressesInstalled =
true;
139 uint32_t
cmd = read(Cmd) & ~(Atapi | AggressivePower | IccMask);
140 if (
cmd & ColdPresence)
142 if (capabilities & StaggeredSpinup)
145 if (version >= 0x00010300 && (extendedCapabilities & (1U << 3)))
146 write(Devslp, read(Devslp) & ~1U);
147 write(Cmd,
cmd | FisEnable);
148 uint32_t control = read(Sctl) & 0xf0U;
149 control |= (version >= 0x00010300 ? 7U : 3U) << 8;
150 write(Serr, 0xffffffffU);
151 write(Sctl, control | 1U);
152 const auto resetUntil = Time::getTicks() + Time::Multiplier::Millisecond;
153 while (Time::getTicks() < resetUntil)
155 write(Sctl, control);
156 const auto linkDeadline = Time::getTicks() + Time::Multiplier::Second;
157 while ((read(Ssts) & 15U) != 3U && Time::getTicks() < linkDeadline)
159 write(Serr, 0xffffffffU);
160 if ((read(Ssts) & 15U) != 3U)
162 write(Cmd, (read(Cmd) & ~IccMask) | IccActive);
163 if (!waitClear(Tfd, Busy | DataRequest, 30000)) {
164 WARNING(
"AHCI: port " << m_Port <<
" device not ready after COMRESET");
167 if (read(Sig) != SataDisk) {
168 NOTICE(
"AHCI: port " << m_Port <<
" unsupported signature " <<
Hex << read(Sig));
171 if ((read(Cmd) & CommandRunning) || read(Ci) || read(Sact))
173 acknowledge(read(PortIs));
174 write(Cmd, read(Cmd) | Start);
179void AhciPort::enableInterrupts() {
181 acknowledge(read(PortIs));
183 write(PortIe, PortInterrupts);
185void AhciPort::configureDisk(
size_t sectorBytes,
size_t queueDepth) {
186 m_SectorBytes = sectorBytes;
187 m_QueueDepth = m_SupportsNcq ? (queueDepth < m_SlotCount ? queueDepth : m_SlotCount) : 0;
188 NOTICE(
"AHCI: port " << m_Port <<
" NCQ depth " <<
Dec << m_QueueDepth <<
Hex);
190void AhciPort::observe(uint32_t status,
bool fromInterrupt) {
193 uint32_t errors = status & PortErrors;
194 if (read(Tfd) & (TaskError | DeviceFault))
195 errors |= TaskFileError;
196 if ((read(Ssts) & 15U) != 3U)
204 const uint32_t pending = (read(Sact) & m_Queued) | (read(Ci) & ~m_Queued);
205 for (
size_t i = 0; i < m_SlotCount; ++i) {
206 if (!(m_Active & (1U << i)))
208 Slot& slot = m_Slots[i];
209 slot.errors |= errors;
210 if (!slot.done && (slot.errors || !(pending & (1U << i)))) {
214 ++m_InterruptCompletions;
215 slot.completion.release();
219void AhciPort::pollCompletions(
bool interrupts) {
220 const uint32_t status = read(PortIs);
223 if (interrupts && (status & read(PortIe)))
224 m_PolledInterrupt =
true;
225 observe(status,
false);
228bool AhciPort::interrupt(
bool pending) {
230 const bool credited = m_PolledInterrupt;
231 m_PolledInterrupt =
false;
232 const uint32_t status = pending ? read(PortIs) : 0;
235 observe(status,
true);
239bool AhciPort::chooseSlot(
bool queued,
size_t& index) {
244 if (queued || !m_Active) {
245 const size_t count = queued ? m_QueueDepth : 1;
246 for (
size_t i = 0; i < count; ++i) {
247 if (!(m_Active & (1U << i))) {
256bool AhciPort::issueCommand(
size_t index, uint8_t opcode, uint64_t lba, uint16_t sectors,
257 void* buffer,
size_t bytes,
bool writing,
bool queued,
259 Slot& slot = m_Slots[index];
260 const uint32_t mask = 1U << index;
262 const size_t tableOffset = TableOffset + index * TableStride;
264 reinterpret_cast<uintptr_t
>(m_Control.
virtualAddress()) + tableOffset);
267 if (!m_Online || (!queued && (read(Ci) || read(Sact) || (read(Tfd) & (Busy | DataRequest)))))
269 ByteSet(header, 0,
sizeof(*header));
270 ByteSet(table, 0,
sizeof(*table));
271 if (writing && bytes)
272 MemoryCopy(slot.data.virtualAddress(), buffer, bytes);
274 const size_t prds = (bytes + pageSize - 1) / pageSize;
275 header->flags = 5U | (writing ? 1U << 6 : 0U) | (prds << 16);
276 header->table =
static_cast<uint32_t
>(m_Control.
physicalAddress() + tableOffset);
277 table->fis[0] = 0x27;
278 table->fis[1] = 0x80;
279 table->fis[2] = opcode;
280 if (queued || opcode == 0x25 || opcode == 0x35)
281 table->fis[7] = 0x40;
282 for (
size_t i = 0; i < 3; ++i) {
283 table->fis[4 + i] =
static_cast<uint8_t
>(lba >> (i * 8));
284 table->fis[8 + i] =
static_cast<uint8_t
>(lba >> ((i + 3) * 8));
287 table->fis[3] =
static_cast<uint8_t
>(sectors);
288 table->fis[11] =
static_cast<uint8_t
>(sectors >> 8);
289 table->fis[12] =
static_cast<uint8_t
>(index << 3);
291 table->fis[12] =
static_cast<uint8_t
>(sectors);
292 table->fis[13] =
static_cast<uint8_t
>(sectors >> 8);
294 for (
size_t page = 0; page < prds; ++page) {
295 const size_t remaining = bytes - page * pageSize;
296 const size_t count = remaining < pageSize ? remaining : pageSize;
297 table->data[page].address =
static_cast<uint32_t
>(slot.pages[page]);
298 table->data[page].byteCount =
static_cast<uint32_t
>(count - 1);
301 pollCompletions(interrupts);
304 [[maybe_unused]]
const size_t drained = slot.completion.drainAvailable();
309 if (m_Outstanding > m_MaximumOutstanding)
310 m_MaximumOutstanding = m_Outstanding;
316 const size_t timeoutSeconds = (opcode == 0xe7 || opcode == 0xea) ? 120 : 30;
317 slot.deadline = Time::getTicks() + timeoutSeconds * Time::Multiplier::Second;
324bool AhciPort::reapCommand(
size_t index, uint8_t opcode,
void* buffer,
size_t bytes,
bool writing,
325 bool queued,
bool interrupts,
bool interruptProbe) {
326 Slot& slot = m_Slots[index];
327 const uint32_t mask = 1U << index;
329 bool success =
false;
330 bool interruptGrace = interruptProbe;
336 if (!slot.done && !interruptGrace) {
337 pollCompletions(interrupts);
339 if (slot.done || Time::getTicks() >= slot.deadline) {
342 success = slot.done && !slot.errors && m_Online && !(read(queued ? Sact : Ci) & mask) &&
343 (queued || header->transferred == bytes);
345 ERROR(
"AHCI: port " << m_Port <<
" command " <<
Hex << opcode <<
" tag " << index
346 <<
" failed, CI=" << read(Ci) <<
" SACT=" << read(Sact)
347 <<
" TFD=" << read(Tfd) <<
" errors=" << slot.errors);
351 for (
size_t i = 0; i < m_SlotCount; ++i) {
352 if (m_Active & (1U << i)) {
353 m_Slots[i].errors |= TaskFileError;
354 if (!m_Slots[i].done) {
355 m_Slots[i].done =
true;
357 m_Slots[i].completion.
release();
362 panic(
"AHCI: cannot stop failed port DMA; refusing to release memory");
363 }
else if (!writing && bytes) {
364 MemoryCopy(buffer, slot.data.virtualAddress(), bytes);
373 const bool acquired = slot.completion.acquireForCompletion(1, interruptGrace ? 1 : 0,
374 interruptGrace ? 0 : 10000);
379 interruptGrace =
false;
384bool AhciPort::command(uint8_t opcode, uint64_t lba, uint16_t sectors,
void* buffer,
size_t bytes,
385 bool writing,
bool interrupts,
bool interruptProbe) {
386 if (bytes > MaxTransfer || (bytes && (!buffer || (bytes & 1U))) || (lba >> 48))
390 const bool queued = m_QueueDepth && (opcode == 0x25 || opcode == 0x35);
392 opcode = writing ? 0x61 : 0x60;
393 const auto admissionDeadline = Time::getTicks() + 120 * Time::Multiplier::Second;
396 if (!chooseSlot(queued, index))
400 if (Time::getTicks() >= admissionDeadline)
404 if (!issueCommand(index, opcode, lba, sectors, buffer, bytes, writing, queued, interrupts))
410 return reapCommand(index, opcode, buffer, bytes, writing, queued, interrupts, interruptProbe);
413bool AhciPort::readBatch(
Disk::ReadBuffer* buffers,
size_t count,
bool interrupts) {
414 return transferBatch(buffers, count, interrupts,
false);
417bool AhciPort::writeBatch(
Disk::WriteBuffer* buffers,
size_t count,
bool interrupts) {
418 static_assert(Disk::MaxWriteBuffers <= Disk::MaxReadBuffers);
419 if (count > Disk::MaxWriteBuffers || (count && !buffers))
422 for (
size_t i = 0; i < count; ++i) {
423 buffers[i].complete =
false;
424 transfers[i] = {buffers[i].location,
const_cast<void*
>(buffers[i].buffer), buffers[i].length,
427 const bool success = transferBatch(transfers, count, interrupts,
true);
428 for (
size_t i = 0; i < count; ++i)
429 buffers[i].complete = transfers[i].complete;
433bool AhciPort::transferBatch(
Disk::ReadBuffer* buffers,
size_t count,
bool interrupts,
435 if (count > Disk::MaxReadBuffers || (count && !buffers))
437 for (
size_t i = 0; i < count; ++i)
438 buffers[i].complete =
false;
439 for (
size_t i = 0; i < count; ++i) {
440 const auto& buffer = buffers[i];
442 buffer.length > MaxTransfer || !m_SectorBytes || buffer.location % m_SectorBytes ||
443 buffer.length % m_SectorBytes || buffer.location / m_SectorBytes >= (1ULL << 48) ||
444 buffer.length / m_SectorBytes > (1ULL << 48) - buffer.location / m_SectorBytes)
449 for (
size_t i = 0; i < count; ++i) {
450 auto& buffer = buffers[i];
452 command(writing ? 0x35 : 0x25, buffer.location / m_SectorBytes,
453 buffer.length / m_SectorBytes, buffer.buffer, buffer.length, writing, interrupts);
454 if (!buffer.complete)
461 while (next < count) {
462 size_t slots[Disk::MaxReadBuffers];
463 const size_t first = next;
465 bool admitted =
true;
469 const auto admissionDeadline = Time::getTicks() + 120 * Time::Multiplier::Second;
470 while (next < count) {
472 if (!chooseSlot(
true, index)) {
481 if (Time::getTicks() >= admissionDeadline) {
488 auto& buffer = buffers[next];
489 if (!issueCommand(index, writing ? 0x61 : 0x60, buffer.location / m_SectorBytes,
490 buffer.length / m_SectorBytes, buffer.buffer, buffer.length, writing,
495 slots[issued++] = index;
499 bool succeeded = admitted;
500 for (
size_t i = 0; i < issued; ++i) {
501 auto& buffer = buffers[first + i];
502 buffer.complete = reapCommand(slots[i], writing ? 0x61 : 0x60, buffer.buffer, buffer.length,
503 writing, true, interrupts, false);
504 succeeded = buffer.complete && succeeded;
512void AhciPort::shutdown() {
514 if (!m_AddressesInstalled)
516 const auto deadline = Time::getTicks() + 120 * Time::Multiplier::Second;
523 if (Time::getTicks() >= deadline)
524 panic(
"AHCI: command owners did not drain during shutdown");
533 panic(
"AHCI: cannot stop port DMA during shutdown");
538 acknowledge(read(PortIs));
540 m_AddressesInstalled =
false;
542size_t AhciPort::interruptCompletions()
const {
544 return m_InterruptCompletions;
547size_t AhciPort::maximumOutstanding()
const {
549 return m_MaximumOutstanding;
Abstrace base class for hardware I/O capabilities.
virtual void write32(uint32_t value, size_t offset=0)=0
virtual uint32_t read32(size_t offset=0)=0
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
static const size_t continuous
static PhysicalMemoryManager & instance()
static const size_t below4GB
static bool getInterrupts()
static ProcessorInformation & information()
static Scheduler & instance()
static constexpr size_t getPageSize() noexcept
bool eventsDeferred() const
static const size_t KernelMode
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static const size_t Write
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN