18#include "pedigree/kernel/Log.h"
19#include "pedigree/kernel/TargetInfo.h"
20#include "pedigree/kernel/machine/Pci.h"
21#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
22#include "pedigree/kernel/utilities/utility.h"
24#include "AhciController.h"
27 :
ScsiDisk(controller &&
PciBus::instance().hasDmaRemapping(controller->pciDevice())
30 m_Controller(controller),
35 m_ExtendedFlush(false),
38 m_pParent = controller;
39 setSpecificType(
String(
"ahci-disk"));
42Device* AhciDisk::dmaDevice()
const {
43 return m_Controller ? m_Controller->pciDevice() :
nullptr;
46AhciDisk::~AhciDisk() {
52bool AhciDisk::initialise() {
55 uint16_t words[256] = {};
56 if (!m_Controller || !m_Controller->identify(m_Port, words))
59 const auto* raw =
reinterpret_cast<const uint8_t*
>(words);
60 if (raw[510] == 0xa5) {
62 for (
size_t i = 0; i <
sizeof(words); ++i)
65 WARNING(
"AHCI: IDENTIFY checksum failed on port " << m_Port);
69 for (
size_t i = 0; i < 256; ++i)
70 words[i] = LITTLE_TO_HOST16(words[i]);
72 if ((words[0] & 0x8000) || (words[49] & 0x0300) != 0x0300 || (words[83] & 0xc000) != 0x4000 ||
73 !(words[83] & (1U << 10))) {
74 WARNING(
"AHCI: port " << m_Port <<
" requires an ATA disk with DMA and LBA48");
78 uint64_t sectorBytes = 512;
79 if ((words[106] & 0xc000) == 0x4000 && (words[106] & (1U << 12))) {
80 const uint32_t sectorWords =
81 static_cast<uint32_t
>(words[117]) | (
static_cast<uint32_t
>(words[118]) << 16);
82 if (sectorWords <= 256) {
83 WARNING(
"AHCI: invalid logical sector size on port " << m_Port);
86 sectorBytes =
static_cast<uint64_t
>(sectorWords) * 2;
88 if (sectorBytes < 512 || (sectorBytes & (sectorBytes - 1))) {
89 WARNING(
"AHCI: port " << m_Port <<
" has unsupported logical sector size " << sectorBytes);
93 if (!pageBytes || pageBytes > 65536 || (pageBytes % sectorBytes))
96 const uint64_t sectors =
97 static_cast<uint64_t
>(words[100]) | (
static_cast<uint64_t
>(words[101]) << 16) |
98 (
static_cast<uint64_t
>(words[102]) << 32) | (
static_cast<uint64_t
>(words[103]) << 48);
99 if (!sectors || sectors > (uint64_t{1} << 48) || sectors > (~
size_t{0} / sectorBytes)) {
100 WARNING(
"AHCI: invalid or unaddressable LBA48 capacity on port " << m_Port);
103 const bool supportsFlush = words[83] & (1U << 12);
104 m_ExtendedFlush = words[83] & (1U << 13);
105 if (!supportsFlush && !m_ExtendedFlush) {
106 WARNING(
"AHCI: port " << m_Port <<
" lacks a supported cache flush command");
110 for (
size_t i = 0; i < 40; ++i) {
111 const uint8_t ch = words[27 + i / 2] >> ((i & 1) ? 0 : 8);
112 m_Model[i] = ch >= 32 && ch <= 126 ? static_cast<char>(ch) :
' ';
115 while (length && m_Model[length - 1] ==
' ')
119 const bool cacheSupported = words[82] & (1U << 5);
120 const bool enabledValid = (words[87] & 0xc000) == 0x4000;
121 const bool cacheEnabled = words[85] & (1U << 5);
122 const char* cacheState = !cacheSupported ?
"unsupported"
123 : !enabledValid ?
"unknown"
124 : cacheEnabled ?
"enabled"
127 m_SectorBytes =
static_cast<size_t>(sectorBytes);
128 m_Controller->configureDisk(
129 m_Port, m_SectorBytes,
130 words[76] != 0xffff && (words[76] & (1U << 8)) ? (words[75] & 31U) + 1 : 0);
131 m_Sectors =
static_cast<size_t>(sectors);
132 m_Bytes = m_Sectors * sectorBytes;
133 m_Initialised =
true;
134 NOTICE(
"AHCI: disk port " << m_Port <<
" model '" << m_Model <<
"', " <<
Dec << m_Sectors
135 <<
" sectors of " << m_SectorBytes <<
" bytes, " << m_Bytes
136 <<
" bytes; write cache " << cacheState <<
", flush "
137 << (m_ExtendedFlush ?
"EXT" :
"legacy") <<
Hex);
142 name.assign(m_Model[0] ? m_Model :
"AHCI Disk");
149size_t AhciDisk::getBlockCount()
const {
158 return m_SectorBytes;
161size_t AhciDisk::validPageLength(uint64_t location)
const {
162 if (!m_Initialised || location >= m_Bytes || (location % m_SectorBytes))
164 const uint64_t remaining = m_Bytes - location;
166 return remaining < pageBytes ? static_cast<size_t>(remaining) : pageBytes;
169bool AhciDisk::transferReadBuffers(
Disk::ReadBuffer* buffers,
size_t count) {
170 if (count > Disk::MaxReadBuffers || (count && !buffers))
172 for (
size_t i = 0; i < count; ++i)
173 buffers[i].complete =
false;
178 for (
size_t i = 0; i < count; ++i) {
180 buffers[i].location >= m_Bytes || buffers[i].length > m_Bytes - buffers[i].location ||
181 buffers[i].location % m_SectorBytes || buffers[i].length % m_SectorBytes)
184 return m_Controller->readBatch(m_Port, buffers, count);
191 return m_Initialised && m_Controller->writeBatch(m_Port, buffers, count);
195uint64_t AhciDisk::doRead(uint64_t location) {
197 if (!m_Initialised || !bytes)
200 const uintptr_t existing = getCache().
lookup(location);
205 bool existed =
false;
206 const uintptr_t page = getCache().
insert(location, &existed);
214 if (!m_Controller->readWrite(m_Port, location / m_SectorBytes,
215 static_cast<uint16_t
>(bytes / m_SectorBytes),
216 reinterpret_cast<void*
>(page), bytes,
false,
true)) {
217 if (!getCache().discardEditing(location))
218 FATAL(
"AHCI: failed to discard an incomplete cache fill");
225uint64_t AhciDisk::doWrite(uint64_t location) {
229 if (!validPageLength(location))
231 const uintptr_t page = getCache().
lookup(location);
240bool AhciDisk::transferBuffer(uint64_t location,
void* buffer,
size_t length,
bool writing) {
245 if (!m_Initialised || !buffer || !length || location >= m_Bytes || length > m_Bytes - location ||
247 length / m_SectorBytes > 0xffff)
249 return m_Controller->readWrite(m_Port, location / m_SectorBytes,
250 static_cast<uint16_t
>(length / m_SectorBytes), buffer, length,
258 const size_t bytes = validPageLength(location);
261 return m_Controller->readWrite(m_Port, location / m_SectorBytes,
262 static_cast<uint16_t
>(bytes / m_SectorBytes),
263 reinterpret_cast<void*
>(page), bytes,
true)
269uint64_t AhciDisk::doWriteDirectPhysical(uint64_t location, uintptr_t page,
270 physical_uintptr_t physical) {
274 const size_t bytes = validPageLength(location);
275 if (!page || !physical || !bytes) {
278 WriteBuffer buffer = {location,
reinterpret_cast<const void*
>(page), bytes,
false, physical};
279 return transferWriteBuffers(&buffer, 1) && buffer.complete ? bytes : 0;
283uint64_t AhciDisk::doSync(uint64_t location) {
288 location == SyncWholeDevice ? (m_Initialised ? 1 : 0) : validPageLength(location);
291 return m_Controller->flush(m_Port, m_ExtendedFlush) ? bytes : 0;
size_t getNativeBlockSize() const override
size_t getBlockSize() const override
Gets the preferred I/O extent of the disk.
size_t getSize() const override
Gets the size of the disk.
uint64_t doWriteDirect(uint64_t location, uintptr_t page) override
void getName(String &name) override
void release(uintptr_t key)
uintptr_t insert(uintptr_t key, bool *alreadyExisted=nullptr)
void markNoLongerEditing(uintptr_t key, size_t length=0)
uintptr_t lookup(uintptr_t key)
size_t getCacheFillLength(uint64_t location) const
void shutdownCache(bool deviceAvailable=true)
static constexpr size_t getPageSize() noexcept