The Pedigree Project 0.1
NvmeDisk.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "NvmeDisk.h"
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"
8
9#include "NvmeController.h"
10
11NvmeDisk::NvmeDisk(NvmeController* controller, uint32_t nsid)
12 : ScsiDisk(controller && PciBus::instance().hasDmaRemapping(controller->pciDevice())
13 ? 0
14 : PhysicalMemoryManager::below4GB),
15 m_Controller(controller),
16 m_Nsid(nsid),
17 m_Blocks(0),
18 m_Bytes(0),
19 m_BlockBytes(0) {
20 m_pParent = controller;
21 setSpecificType(String("nvme-disk"));
22}
23Device* NvmeDisk::dmaDevice() const {
24 return m_Controller ? m_Controller->pciDevice() : nullptr;
25}
26NvmeDisk::~NvmeDisk() {
29}
30bool NvmeDisk::initialise() {
31 uint8_t data[Nvme::PageSize]{};
32 if (!m_Controller || !m_Controller->identify(m_Nsid, 0, data))
33 return false;
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);
38 }
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];
43 // Extended LBA formats, protection information, and shared/multipath
44 // namespaces require protocols this initial NVM transport does not provide.
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");
48 return false;
49 }
50 const size_t blockBytes = size_t{1} << lbads;
51 if (blocks > ~size_t{0} / blockBytes || TargetInfo::getPageSize() % blockBytes ||
52 m_Controller->maxTransfer() < TargetInfo::getPageSize())
53 return false;
54 m_Blocks = blocks;
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);
59 return true;
60}
62 name.assign(m_Controller->model());
63}
64size_t NvmeDisk::getBlockSize() const {
66}
67size_t NvmeDisk::validPageLength(uint64_t location) const {
68 if (!m_BlockBytes || location >= m_Bytes || location % m_BlockBytes)
69 return 0;
70 const size_t remaining = m_Bytes - location;
71 return remaining < TargetInfo::getPageSize() ? remaining : TargetInfo::getPageSize();
72}
73uint64_t NvmeDisk::doRead(uint64_t location) {
74 const size_t bytes = getCacheFillLength(location);
75 if (!m_BlockBytes || !bytes || bytes % m_BlockBytes)
76 return 0;
77 const uintptr_t existing = getCache().lookup(location);
78 if (existing) {
79 getCache().release(location);
80 return bytes;
81 }
82 bool existed = false;
83 const uintptr_t page = getCache().insert(location, &existed);
84 if (!page)
85 return 0;
86 if (existed)
87 return bytes;
88 ByteSet(reinterpret_cast<void*>(page), 0, TargetInfo::getPageSize());
89 // Ordinary eviction cannot retire this Editing page during the synchronous read.
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");
94 return 0;
95 }
96 getCache().markNoLongerEditing(location);
97 return bytes;
98}
99bool NvmeDisk::transferBuffer(uint64_t location, void* buffer, size_t length, bool writing) {
100#if CRIPPLE_HDD
101 if (writing) {
102 return false;
103 }
104#endif
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()) {
108 return false;
109 }
110 return m_Controller->readWrite(m_Nsid, location / m_BlockBytes, length / m_BlockBytes, buffer,
111 length, writing, !writing);
112}
113bool NvmeDisk::transferWriteBuffers(WriteBuffer* buffers, size_t count) {
114 if (count > MaxWriteBuffers || (count && !buffers)) {
115 return false;
116 }
117 bool succeeded = true;
118 for (size_t i = 0; i < count; ++i) {
119 auto& buffer = buffers[i];
120 buffer.complete = false;
121#if !CRIPPLE_HDD
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);
129 }
130#endif
131 succeeded = buffer.complete && succeeded;
132 }
133 return succeeded;
134}
135uint64_t NvmeDisk::doWrite(uint64_t location) {
136#if CRIPPLE_HDD
137 return 0;
138#else
139 if (!validPageLength(location))
140 return 0;
141 const uintptr_t page = getCache().lookup(location);
142 if (!page)
143 return 0;
144 CachePageGuard guard(getCache(), location);
145 return doWriteDirect(location, page);
146#endif
147}
148uint64_t NvmeDisk::doWriteDirect(uint64_t location, uintptr_t page) {
149#if CRIPPLE_HDD
150 return 0;
151#else
152 const size_t bytes = validPageLength(location);
153 if (!page || !bytes)
154 return 0;
155 return m_Controller->readWrite(m_Nsid, location / m_BlockBytes, bytes / m_BlockBytes,
156 reinterpret_cast<void*>(page), bytes, true)
157 ? bytes
158 : 0;
159#endif
160}
161uint64_t NvmeDisk::doWriteDirectPhysical(uint64_t location, uintptr_t page,
162 physical_uintptr_t physical) {
163#if CRIPPLE_HDD
164 return 0;
165#else
166 const size_t bytes = validPageLength(location);
167 if (!page || !physical || !bytes) {
168 return 0;
169 }
170 return m_Controller->readWrite(m_Nsid, location / m_BlockBytes, bytes / m_BlockBytes,
171 reinterpret_cast<void*>(page), bytes, true, false, physical)
172 ? bytes
173 : 0;
174#endif
175}
176uint64_t NvmeDisk::doSync(uint64_t location) {
177#if CRIPPLE_HDD
178 return 0;
179#else
180 const size_t bytes = location == SyncWholeDevice ? bool(m_BlockBytes) : validPageLength(location);
181 return bytes && m_Controller->flush(m_Nsid) ? bytes : 0;
182#endif
183}
void release(uintptr_t key)
Definition Cache.cc:1570
uintptr_t insert(uintptr_t key, bool *alreadyExisted=nullptr)
Definition Cache.cc:890
void markNoLongerEditing(uintptr_t key, size_t length=0)
Definition Cache.cc:2646
uintptr_t lookup(uintptr_t key)
Definition Cache.cc:737
void retireEndpoint()
Definition Disk.cc:133
void getName(String &name) override
Definition NvmeDisk.cc:61
size_t getBlockSize() const override
Gets the preferred I/O extent of the disk.
Definition NvmeDisk.cc:64
uint64_t doWriteDirect(uint64_t location, uintptr_t page) override
Definition NvmeDisk.cc:148
Definition Pci.h:31
size_t getCacheFillLength(uint64_t location) const
Definition ScsiDisk.cc:1389
void shutdownCache(bool deviceAvailable=true)
Definition ScsiDisk.cc:268
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124