3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/TargetInfo.h"
5#include "pedigree/kernel/machine/Pci.h"
6#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
7#include "pedigree/kernel/utilities/utility.h"
9#include "NvmeController.h"
12 :
ScsiDisk(controller &&
PciBus::instance().hasDmaRemapping(controller->pciDevice())
15 m_Controller(controller),
20 m_pParent = controller;
21 setSpecificType(
String(
"nvme-disk"));
23Device* NvmeDisk::dmaDevice()
const {
24 return m_Controller ? m_Controller->pciDevice() :
nullptr;
26NvmeDisk::~NvmeDisk() {
30bool NvmeDisk::initialise() {
31 uint8_t data[Nvme::PageSize]{};
32 if (!m_Controller || !m_Controller->identify(m_Nsid, 0, data))
34 uint64_t blocks = 0, capacity = 0;
35 for (
size_t i = 0; i < 8; ++i) {
36 blocks |= uint64_t{data[i]} << (8 * i);
37 capacity |= uint64_t{data[8 + i]} << (8 * i);
39 const uint8_t format = data[26] & 15U;
40 const size_t formatOffset = 128 + 4 * format;
41 const uint16_t metadata = data[formatOffset] | (uint16_t{data[formatOffset + 1]} << 8);
42 const uint8_t lbads = data[formatOffset + 2];
45 if ((data[26] & 0xe0U) || format > data[25] || metadata || (data[29] & 7U) || (data[30] & 1U) ||
46 (lbads != 9 && lbads != 12) || !blocks || !capacity || capacity > blocks) {
47 WARNING(
"NVMe: namespace " << m_Nsid <<
" unsupported format, protection, or sharing");
50 const size_t blockBytes =
size_t{1} << lbads;
55 m_BlockBytes = blockBytes;
56 m_Bytes = m_Blocks * m_BlockBytes;
57 NOTICE(
"NVMe: namespace " <<
Dec << m_Nsid <<
", " << m_Blocks <<
" blocks of " << m_BlockBytes
58 <<
" bytes, " << m_Bytes <<
" bytes" <<
Hex);
62 name.assign(m_Controller->model());
67size_t NvmeDisk::validPageLength(uint64_t location)
const {
68 if (!m_BlockBytes || location >= m_Bytes || location % m_BlockBytes)
70 const size_t remaining = m_Bytes - location;
73uint64_t NvmeDisk::doRead(uint64_t location) {
75 if (!m_BlockBytes || !bytes || bytes % m_BlockBytes)
77 const uintptr_t existing = getCache().
lookup(location);
83 const uintptr_t page = getCache().
insert(location, &existed);
90 if (!m_Controller->readWrite(m_Nsid, location / m_BlockBytes, bytes / m_BlockBytes,
91 reinterpret_cast<void*
>(page), bytes,
false,
true)) {
92 if (!getCache().discardEditing(location))
93 FATAL(
"NVMe: failed to discard incomplete cache fill");
99bool NvmeDisk::transferBuffer(uint64_t location,
void* buffer,
size_t length,
bool writing) {
105 if (!m_Controller || !m_BlockBytes || !buffer || !length || location >= m_Bytes ||
106 length > m_Bytes - location || location % m_BlockBytes || length % m_BlockBytes ||
107 length > m_Controller->maxTransfer()) {
110 return m_Controller->readWrite(m_Nsid, location / m_BlockBytes, length / m_BlockBytes, buffer,
111 length, writing, !writing);
113bool NvmeDisk::transferWriteBuffers(WriteBuffer* buffers,
size_t count) {
114 if (count > MaxWriteBuffers || (count && !buffers)) {
117 bool succeeded =
true;
118 for (
size_t i = 0; i < count; ++i) {
119 auto& buffer = buffers[i];
120 buffer.complete =
false;
122 if (m_Controller && m_BlockBytes && buffer.buffer && buffer.length &&
123 buffer.location < m_Bytes && buffer.length <= m_Bytes - buffer.location &&
124 !(buffer.location % m_BlockBytes) && !(buffer.length % m_BlockBytes) &&
125 buffer.length <= m_Controller->maxTransfer()) {
126 buffer.complete = m_Controller->readWrite(
127 m_Nsid, buffer.location / m_BlockBytes, buffer.length / m_BlockBytes,
128 const_cast<void*
>(buffer.buffer), buffer.length,
true,
false, buffer.dmaPhysical);
131 succeeded = buffer.complete && succeeded;
135uint64_t NvmeDisk::doWrite(uint64_t location) {
139 if (!validPageLength(location))
141 const uintptr_t page = getCache().
lookup(location);
152 const size_t bytes = validPageLength(location);
155 return m_Controller->readWrite(m_Nsid, location / m_BlockBytes, bytes / m_BlockBytes,
156 reinterpret_cast<void*
>(page), bytes,
true)
161uint64_t NvmeDisk::doWriteDirectPhysical(uint64_t location, uintptr_t page,
162 physical_uintptr_t physical) {
166 const size_t bytes = validPageLength(location);
167 if (!page || !physical || !bytes) {
170 return m_Controller->readWrite(m_Nsid, location / m_BlockBytes, bytes / m_BlockBytes,
171 reinterpret_cast<void*
>(page), bytes,
true,
false, physical)
176uint64_t NvmeDisk::doSync(uint64_t location) {
180 const size_t bytes = location == SyncWholeDevice ? bool(m_BlockBytes) : validPageLength(location);
181 return bytes && m_Controller->flush(m_Nsid) ? bytes : 0;
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)
void getName(String &name) override
size_t getBlockSize() const override
Gets the preferred I/O extent of the disk.
uint64_t doWriteDirect(uint64_t location, uintptr_t page) override
size_t getCacheFillLength(uint64_t location) const
void shutdownCache(bool deviceAvailable=true)
static constexpr size_t getPageSize() noexcept