17#include "pedigree/kernel/LockGuard.h"
18#include "pedigree/kernel/Log.h"
19#include "pedigree/kernel/TargetInfo.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/process/Scheduler.h"
24#include "pedigree/kernel/process/TerminationDeferral.h"
25#include "pedigree/kernel/process/Thread.h"
26#include "pedigree/kernel/processor/IoBase.h"
27#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
28#include "pedigree/kernel/processor/Processor.h"
29#include "pedigree/kernel/processor/ProcessorInformation.h"
30#include "pedigree/kernel/processor/VirtualAddressSpace.h"
31#include "pedigree/kernel/time/Time.h"
32#include "pedigree/kernel/utilities/StaticString.h"
33#include "pedigree/kernel/utilities/utility.h"
36#include "modules/drivers/common/DmaBuffer.h"
41physical_uintptr_t usableDirectWrite(physical_uintptr_t physical,
size_t bytes) {
43 if (!physical || !bytes || bytes > pageSize || physical % pageSize ||
44 !DriverDma::physicalRangeFits(physical, bytes, 0xffffffffU)) {
51AhciPort::AhciPort(
IoBase* registers,
size_t port,
Device* dmaDevice)
52 : m_Registers(registers),
53 m_DmaDevice(dmaDevice),
55 m_Control(
"AHCI command storage"),
57 m_WritesPending(true),
64 m_AddressesInstalled(false),
65 m_PolledInterrupt(false),
66 m_InterruptCompletions(0),
67 m_MaximumOutstanding(0),
69AhciPort::~AhciPort() {
72uint32_t AhciPort::read(
size_t reg)
const {
73 return m_Registers->
read32(PortBase + m_Port * PortStride + reg);
75void AhciPort::write(
size_t reg, uint32_t value) {
76 m_Registers->
write32(value, PortBase + m_Port * PortStride + reg);
78void AhciPort::waitForProgress() {
86 Time::delay(Time::Multiplier::Millisecond);
89bool AhciPort::waitClear(
size_t reg, uint32_t bits,
size_t milliseconds) {
90 const auto deadline = Time::getTicks() + milliseconds * Time::Multiplier::Millisecond;
92 if (!(read(reg) & bits))
95 }
while (Time::getTicks() < deadline);
96 return !(read(reg) & bits);
98bool AhciPort::stopEngines() {
100 write(Cmd, read(Cmd) & ~Start);
101 if (!waitClear(Cmd, CommandRunning, 500))
103 write(Cmd, read(Cmd) & ~FisEnable);
104 return waitClear(Cmd, FisRunning, 500);
106void AhciPort::acknowledge(uint32_t status) {
108 const uint32_t error = read(Serr);
112 write(PortIs, status);
114bool AhciPort::initialise(uint32_t capabilities, uint32_t version, uint32_t extendedCapabilities) {
116 if (!stopEngines()) {
117 ERROR(
"AHCI: port " << m_Port <<
" firmware engines did not stop");
121 m_SlotCount = ((capabilities >> 8) & 31U) + 1;
122 m_SupportsNcq = capabilities & (1U << 30);
123 const size_t controlBytes = TableOffset + m_SlotCount * TableStride;
124 if (pageSize < 4096 || pageSize > MaxTransfer || (MaxTransfer % pageSize))
129 if (!memory.allocateRegion(m_Control, (controlBytes + pageSize - 1) / pageSize, constraints,
133 for (
size_t i = 0; i < m_SlotCount; ++i) {
136 if (!memory.allocateRegion(m_Slots[i].data, MaxTransfer / pageSize,
139 ByteSet(m_Slots[i].data.virtualAddress(), 0, m_Slots[i].data.size());
140 for (
size_t page = 0; page < MaxTransfer / pageSize; ++page) {
141 size_t mappingFlags = 0;
143 static_cast<uint8_t*
>(m_Slots[i].data.virtualAddress()) + page * pageSize,
144 m_Slots[i].pages[page], mappingFlags);
145 if (m_Slots[i].pages[page] >= (uint64_t{1} << 32))
152 write(Fb,
static_cast<uint32_t
>(m_Control.
physicalAddress() + FisOffset));
154 m_AddressesInstalled =
true;
156 uint32_t
cmd = read(Cmd) & ~(Atapi | AggressivePower | IccMask);
157 if (
cmd & ColdPresence)
159 if (capabilities & StaggeredSpinup)
162 if (version >= 0x00010300 && (extendedCapabilities & (1U << 3)))
163 write(Devslp, read(Devslp) & ~1U);
164 write(Cmd,
cmd | FisEnable);
165 uint32_t control = read(Sctl) & 0xf0U;
166 control |= (version >= 0x00010300 ? 7U : 3U) << 8;
167 write(Serr, 0xffffffffU);
168 write(Sctl, control | 1U);
169 const auto resetUntil = Time::getTicks() + Time::Multiplier::Millisecond;
170 while (Time::getTicks() < resetUntil)
172 write(Sctl, control);
173 const auto linkDeadline = Time::getTicks() + Time::Multiplier::Second;
174 while ((read(Ssts) & 15U) != 3U && Time::getTicks() < linkDeadline)
176 write(Serr, 0xffffffffU);
177 if ((read(Ssts) & 15U) != 3U)
179 write(Cmd, (read(Cmd) & ~IccMask) | IccActive);
180 if (!waitClear(Tfd, Busy | DataRequest, 30000)) {
181 WARNING(
"AHCI: port " << m_Port <<
" device not ready after COMRESET");
184 if (read(Sig) != SataDisk) {
185 NOTICE(
"AHCI: port " << m_Port <<
" unsupported signature " <<
Hex << read(Sig));
188 if ((read(Cmd) & CommandRunning) || read(Ci) || read(Sact))
190 acknowledge(read(PortIs));
191 write(Cmd, read(Cmd) | Start);
196void AhciPort::enableInterrupts() {
198 acknowledge(read(PortIs));
200 write(PortIe, PortInterrupts);
202void AhciPort::configureDisk(
size_t sectorBytes,
size_t queueDepth) {
203 m_SectorBytes = sectorBytes;
204 m_QueueDepth = m_SupportsNcq ? (queueDepth < m_SlotCount ? queueDepth : m_SlotCount) : 0;
205 NOTICE(
"AHCI: port " << m_Port <<
" NCQ depth " <<
Dec << m_QueueDepth <<
Hex);
207void AhciPort::observe(uint32_t status,
bool fromInterrupt) {
210 uint32_t errors = status & PortErrors;
211 if (read(Tfd) & (TaskError | DeviceFault))
212 errors |= TaskFileError;
213 if ((read(Ssts) & 15U) != 3U)
221 const uint32_t pending = (read(Sact) & m_Queued) | (read(Ci) & ~m_Queued);
222 for (
size_t i = 0; i < m_SlotCount; ++i) {
223 if (!(m_Active & (1U << i)))
225 Slot& slot = m_Slots[i];
226 slot.errors |= errors;
227 if (!slot.done && (slot.errors || !(pending & (1U << i)))) {
231 ++m_InterruptCompletions;
232 slot.completion.release();
236void AhciPort::pollCompletions(
bool interrupts) {
237 const uint32_t status = read(PortIs);
240 if (interrupts && (status & read(PortIe)))
241 m_PolledInterrupt =
true;
242 observe(status,
false);
245bool AhciPort::interrupt(
bool pending) {
247 const bool credited = m_PolledInterrupt;
248 m_PolledInterrupt =
false;
249 const uint32_t status = pending ? read(PortIs) : 0;
252 observe(status,
true);
256bool AhciPort::chooseSlot(
bool queued,
size_t& index) {
261 if (queued || !m_Active) {
262 const size_t count = queued ? m_QueueDepth : 1;
263 for (
size_t i = 0; i < count; ++i) {
264 if (!(m_Active & (1U << i))) {
273bool AhciPort::issueCommand(
size_t index, uint8_t opcode, uint64_t lba, uint16_t sectors,
274 void* buffer,
size_t bytes,
bool writing,
bool queued,
bool interrupts,
275 physical_uintptr_t directPhysical,
276 const physical_uintptr_t* directReadPages) {
277 Slot& slot = m_Slots[index];
278 const uint32_t mask = 1U << index;
280 const size_t tableOffset = TableOffset + index * TableStride;
282 reinterpret_cast<uintptr_t
>(m_Control.
virtualAddress()) + tableOffset);
285 if (!m_Online || (!queued && (read(Ci) || read(Sact) || (read(Tfd) & (Busy | DataRequest)))))
287 ByteSet(header, 0,
sizeof(*header));
288 ByteSet(table, 0,
sizeof(*table));
289 if (writing && bytes && !directPhysical) {
290 MemoryCopy(slot.data.virtualAddress(), buffer, bytes);
293 const size_t prds = directPhysical ? 1 : (bytes + pageSize - 1) / pageSize;
294 header->flags = 5U | (writing ? 1U << 6 : 0U) | (prds << 16);
295 header->table =
static_cast<uint32_t
>(m_Control.
physicalAddress() + tableOffset);
296 table->fis[0] = 0x27;
297 table->fis[1] = 0x80;
298 table->fis[2] = opcode;
299 if (queued || opcode == 0x25 || opcode == 0x35)
300 table->fis[7] = 0x40;
301 for (
size_t i = 0; i < 3; ++i) {
302 table->fis[4 + i] =
static_cast<uint8_t
>(lba >> (i * 8));
303 table->fis[8 + i] =
static_cast<uint8_t
>(lba >> ((i + 3) * 8));
306 table->fis[3] =
static_cast<uint8_t
>(sectors);
307 table->fis[11] =
static_cast<uint8_t
>(sectors >> 8);
308 table->fis[12] =
static_cast<uint8_t
>(index << 3);
310 table->fis[12] =
static_cast<uint8_t
>(sectors);
311 table->fis[13] =
static_cast<uint8_t
>(sectors >> 8);
313 for (
size_t page = 0; page < prds; ++page) {
314 const size_t remaining = bytes - page * pageSize;
315 const size_t count = remaining < pageSize ? remaining : pageSize;
316 physical_uintptr_t dataAddress = slot.pages[page];
317 if (directReadPages) {
318 dataAddress = directReadPages[page];
319 }
else if (directPhysical) {
320 dataAddress = directPhysical;
322 table->data[page].address =
static_cast<uint32_t
>(dataAddress);
323 table->data[page].byteCount =
static_cast<uint32_t
>(count - 1);
326 pollCompletions(interrupts);
329 [[maybe_unused]]
const size_t drained = slot.completion.drainAvailable();
334 if (m_Outstanding > m_MaximumOutstanding)
335 m_MaximumOutstanding = m_Outstanding;
341 const size_t timeoutSeconds = (opcode == 0xe7 || opcode == 0xea) ? 120 : 30;
342 slot.deadline = Time::getTicks() + timeoutSeconds * Time::Multiplier::Second;
349bool AhciPort::reapCommand(
size_t index, uint8_t opcode,
void* buffer,
size_t bytes,
bool writing,
350 bool queued,
bool interrupts,
bool interruptProbe,
bool directRead) {
351 Slot& slot = m_Slots[index];
352 const uint32_t mask = 1U << index;
354 bool success =
false;
355 bool interruptGrace = interruptProbe;
361 if (!slot.done && !interruptGrace) {
362 pollCompletions(interrupts);
364 if (slot.done || Time::getTicks() >= slot.deadline) {
367 success = slot.done && !slot.errors && m_Online && !(read(queued ? Sact : Ci) & mask) &&
368 (queued || header->transferred == bytes);
370 ERROR(
"AHCI: port " << m_Port <<
" command " <<
Hex << opcode <<
" tag " << index
371 <<
" failed, CI=" << read(Ci) <<
" SACT=" << read(Sact)
372 <<
" TFD=" << read(Tfd) <<
" errors=" << slot.errors);
376 for (
size_t i = 0; i < m_SlotCount; ++i) {
377 if (m_Active & (1U << i)) {
378 m_Slots[i].errors |= TaskFileError;
379 if (!m_Slots[i].done) {
380 m_Slots[i].done =
true;
382 m_Slots[i].completion.
release();
387 panic(
"AHCI: cannot stop failed port DMA; refusing to release memory");
388 }
else if (!writing && bytes && !directRead) {
389 MemoryCopy(buffer, slot.data.virtualAddress(), bytes);
398 const bool acquired = slot.completion.acquireForCompletion(1, interruptGrace ? 1 : 0,
399 interruptGrace ? 0 : 10000);
404 interruptGrace =
false;
409bool AhciPort::command(uint8_t opcode, uint64_t lba, uint16_t sectors,
void* buffer,
size_t bytes,
410 bool writing,
bool interrupts,
bool interruptProbe,
bool cacheFill,
411 physical_uintptr_t dmaPhysical,
const physical_uintptr_t* directReadPages) {
412 if (bytes > MaxTransfer || (bytes && (!buffer || (bytes & 1U))) || (lba >> 48))
415 physical_uintptr_t directPhysical =
416 writing && opcode == 0x35 ? usableDirectWrite(dmaPhysical, bytes) : 0;
420 if (!directReadPages && cacheFill && opcode == 0x25 && !writing && bytes && bytes <= pageSize &&
421 !(
reinterpret_cast<uintptr_t
>(buffer) & (pageSize - 1))) {
422 physical_uintptr_t physical = 0;
425 if (addressSpace.getMapping(buffer, physical, flags) && physical &&
426 !(physical & (pageSize - 1)) &&
431 if (DriverDma::physicalRangeFits(physical, bytes, 0xffffffffU)) {
432 directPhysical = physical;
433 }
else if (m_DmaDevice &&
434 PciBus::instance().mapDmaPage(m_DmaDevice, physical, bytes, directReadMapping)) {
435 directPhysical = directReadMapping.address();
440 const bool queued = m_QueueDepth && (opcode == 0x25 || opcode == 0x35);
442 opcode = writing ? 0x61 : 0x60;
443 const auto admissionDeadline = Time::getTicks() + 120 * Time::Multiplier::Second;
446 if (!chooseSlot(queued, index))
450 if (Time::getTicks() >= admissionDeadline)
454 const bool flush = (opcode == 0xe7 || opcode == 0xea) && !bytes && !writing;
455 if (flush && !m_WritesPending) {
459 m_WritesPending =
true;
461 if (!issueCommand(index, opcode, lba, sectors, buffer, bytes, writing, queued, interrupts,
462 directPhysical, directReadPages))
468 const bool succeeded =
469 reapCommand(index, opcode, buffer, bytes, writing, queued, interrupts, interruptProbe,
470 directPhysical != 0 || directReadPages !=
nullptr);
473 if (flush && succeeded) {
474 m_WritesPending =
false;
479bool AhciPort::readBatch(
Disk::ReadBuffer* buffers,
size_t count,
bool interrupts) {
480 if (count > Disk::MaxReadBuffers || (count && !buffers)) {
483 for (
size_t i = 0; i < count; ++i) {
484 buffers[i].complete =
false;
487 for (
size_t i = 0; i < count; ++i) {
488 if (!buffers[i].length || buffers[i].length > pageSize) {
493 physical_uintptr_t dmaAddresses[Disk::MaxReadBuffers] = {};
495 for (
size_t i = 0; i < count; ++i) {
496 const uintptr_t address =
reinterpret_cast<uintptr_t
>(buffers[i].buffer);
497 const size_t offset = address & (pageSize - 1);
498 if (!m_DmaDevice || !address || (address & 1U) || buffers[i].length > pageSize - offset) {
501 physical_uintptr_t physical = 0;
503 if (addressSpace.getMapping(
reinterpret_cast<void*
>(address - offset), physical, flags) &&
504 physical && !(physical & (pageSize - 1)) &&
509 PciBus::instance().
mapDmaPage(m_DmaDevice, physical, pageSize, mappings[i])) {
510 dmaAddresses[i] = mappings[i].address() + offset;
513 const physical_uintptr_t* directReadPages[Disk::MaxReadBuffers] = {};
514 for (
size_t i = 0; i < count; ++i) {
515 if (dmaAddresses[i]) {
516 directReadPages[i] = &dmaAddresses[i];
520 return transferBatch(buffers, count, interrupts,
false,
nullptr, directReadPages);
524 constexpr size_t ReadTransfer = 16 * 1024;
525 static_assert(ReadTransfer <= MaxTransfer);
527 const physical_uintptr_t* transferPages[Disk::MaxReadBuffers] = {};
528 size_t ends[Disk::MaxReadBuffers];
530 for (
size_t i = 0; i < count; ++i) {
531 const auto& buffer = buffers[i];
532 const bool direct = directReadPages[i] !=
nullptr;
533 const bool previousDirect = grouped && transferPages[grouped - 1] !=
nullptr;
534 if (grouped && direct == previousDirect && buffer.length == pageSize) {
535 auto& previous = transfers[grouped - 1];
536 const uintptr_t address =
reinterpret_cast<uintptr_t
>(buffer.buffer);
537 const uintptr_t priorAddress =
reinterpret_cast<uintptr_t
>(previous.buffer);
538 if (previous.length % pageSize == 0 && previous.length <= ReadTransfer &&
539 buffer.length <= ReadTransfer - previous.length && buffer.location > previous.location &&
540 buffer.location - previous.location == previous.length && address > priorAddress &&
541 address - priorAddress == previous.length) {
542 previous.length += buffer.length;
543 ends[grouped - 1] = i + 1;
547 transfers[grouped] = buffer;
548 transferPages[grouped] = directReadPages[i];
549 ends[grouped++] = i + 1;
551 const bool success = transferBatch(transfers, grouped, interrupts,
false,
nullptr, transferPages);
553 for (
size_t i = 0; i < grouped; ++i) {
554 while (next < ends[i]) {
555 buffers[next++].complete = transfers[i].complete;
561bool AhciPort::writeBatch(
Disk::WriteBuffer* buffers,
size_t count,
bool interrupts) {
562 static_assert(Disk::MaxWriteBuffers <= Disk::MaxReadBuffers);
563 if (count > Disk::MaxWriteBuffers || (count && !buffers))
566 physical_uintptr_t dmaPhysical[Disk::MaxWriteBuffers] = {};
567 for (
size_t i = 0; i < count; ++i) {
568 buffers[i].complete =
false;
569 transfers[i] = {buffers[i].location,
const_cast<void*
>(buffers[i].buffer), buffers[i].length,
573 const bool success = transferBatch(transfers, count, interrupts,
true, dmaPhysical);
574 for (
size_t i = 0; i < count; ++i)
575 buffers[i].complete = transfers[i].complete;
579bool AhciPort::transferBatch(
Disk::ReadBuffer* buffers,
size_t count,
bool interrupts,
bool writing,
580 const physical_uintptr_t* dmaPhysical,
581 const physical_uintptr_t*
const* directReadPages) {
582 if (count > Disk::MaxReadBuffers || (count && !buffers))
584 for (
size_t i = 0; i < count; ++i)
585 buffers[i].complete =
false;
586 for (
size_t i = 0; i < count; ++i) {
587 const auto& buffer = buffers[i];
588 if (!buffer.buffer || !buffer.length ||
590 !m_SectorBytes || buffer.location % m_SectorBytes || buffer.length % m_SectorBytes ||
591 buffer.location / m_SectorBytes >= (1ULL << 48) ||
592 buffer.length / m_SectorBytes > (1ULL << 48) - buffer.location / m_SectorBytes) {
598 for (
size_t i = 0; i < count; ++i) {
599 auto& buffer = buffers[i];
601 command(writing ? 0x35 : 0x25, buffer.location / m_SectorBytes,
602 buffer.length / m_SectorBytes, buffer.buffer, buffer.length, writing, interrupts,
603 false, false, writing && dmaPhysical ? dmaPhysical[i] : 0,
604 !writing && directReadPages ? directReadPages[i] : nullptr);
605 if (!buffer.complete)
612 while (next < count) {
613 size_t slots[Disk::MaxReadBuffers];
614 const size_t first = next;
616 bool admitted =
true;
620 const auto admissionDeadline = Time::getTicks() + 120 * Time::Multiplier::Second;
621 while (next < count) {
623 if (!chooseSlot(
true, index)) {
632 if (Time::getTicks() >= admissionDeadline) {
639 auto& buffer = buffers[next];
641 m_WritesPending =
true;
643 const physical_uintptr_t directPhysical =
644 writing && dmaPhysical ? usableDirectWrite(dmaPhysical[next], buffer.length) : 0;
645 if (!issueCommand(index, writing ? 0x61 : 0x60, buffer.location / m_SectorBytes,
646 buffer.length / m_SectorBytes, buffer.buffer, buffer.length, writing,
647 true, interrupts, directPhysical,
648 !writing && directReadPages ? directReadPages[next] : nullptr)) {
652 slots[issued++] = index;
656 bool succeeded = admitted;
657 for (
size_t i = 0; i < issued; ++i) {
658 auto& buffer = buffers[first + i];
660 reapCommand(slots[i], writing ? 0x61 : 0x60, buffer.buffer, buffer.length, writing, true,
661 interrupts, false, !writing && directReadPages && directReadPages[first + i]);
662 succeeded = buffer.complete && succeeded;
670void AhciPort::shutdown() {
671 auto reportShutdown = [&](
const char* step) {
673 detail.append(m_Port);
678 reportShutdown(
"locking command state");
680 if (!m_AddressesInstalled)
682 reportShutdown(
"draining active commands");
683 const auto deadline = Time::getTicks() + 120 * Time::Multiplier::Second;
690 if (Time::getTicks() >= deadline)
691 panic(
"AHCI: command owners did not drain during shutdown");
699 reportShutdown(
"stopping DMA engines");
701 panic(
"AHCI: cannot stop port DMA during shutdown");
702 reportShutdown(
"clearing DMA addresses");
707 acknowledge(read(PortIs));
709 m_AddressesInstalled =
false;
711size_t AhciPort::interruptCompletions()
const {
713 return m_InterruptCompletions;
716size_t AhciPort::maximumOutstanding()
const {
718 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
static void setShutdownDetail(const char *detail)
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
bool mapDmaPage(Device *device, physical_uintptr_t physical, size_t bytes, DmaMapping &mapping)
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 CopyOnWrite
static const size_t Borrowed
static const size_t KernelMode
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static const size_t NoAccess
static const size_t Write
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
static const size_t Swapped
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
physical_uintptr_t dmaPhysical