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/utilities/utility.h"
6
7#include "NvmeController.h"
8
9NvmeDisk::NvmeDisk(NvmeController* controller, uint32_t nsid)
10 : m_Controller(controller), m_Nsid(nsid), m_Blocks(0), m_Bytes(0), m_BlockBytes(0) {
11 m_pParent = controller;
12 setSpecificType(String("nvme-disk"));
13}
14NvmeDisk::~NvmeDisk() {
17}
18bool NvmeDisk::initialise() {
19 uint8_t data[Nvme::PageSize]{};
20 if (!m_Controller || !m_Controller->identify(m_Nsid, 0, data))
21 return false;
22 uint64_t blocks = 0, capacity = 0;
23 for (size_t i = 0; i < 8; ++i) {
24 blocks |= uint64_t{data[i]} << (8 * i);
25 capacity |= uint64_t{data[8 + i]} << (8 * i);
26 }
27 const uint8_t format = data[26] & 15U;
28 const size_t formatOffset = 128 + 4 * format;
29 const uint16_t metadata = data[formatOffset] | (uint16_t{data[formatOffset + 1]} << 8);
30 const uint8_t lbads = data[formatOffset + 2];
31 // Extended LBA formats, protection information, and shared/multipath
32 // namespaces require protocols this initial NVM transport does not provide.
33 if ((data[26] & 0xe0U) || format > data[25] || metadata || (data[29] & 7U) || (data[30] & 1U) ||
34 (lbads != 9 && lbads != 12) || !blocks || !capacity || capacity > blocks) {
35 WARNING("NVMe: namespace " << m_Nsid << " unsupported format, protection, or sharing");
36 return false;
37 }
38 const size_t blockBytes = size_t{1} << lbads;
39 if (blocks > ~size_t{0} / blockBytes || TargetInfo::getPageSize() % blockBytes ||
40 m_Controller->maxTransfer() < TargetInfo::getPageSize())
41 return false;
42 m_Blocks = blocks;
43 m_BlockBytes = blockBytes;
44 m_Bytes = m_Blocks * m_BlockBytes;
45 NOTICE("NVMe: namespace " << Dec << m_Nsid << ", " << m_Blocks << " blocks of " << m_BlockBytes
46 << " bytes, " << m_Bytes << " bytes" << Hex);
47 return true;
48}
50 name.assign(m_Controller->model());
51}
52size_t NvmeDisk::getBlockSize() const {
54}
55size_t NvmeDisk::validPageLength(uint64_t location) const {
56 if (!m_BlockBytes || location >= m_Bytes || location % m_BlockBytes)
57 return 0;
58 const size_t remaining = m_Bytes - location;
59 return remaining < TargetInfo::getPageSize() ? remaining : TargetInfo::getPageSize();
60}
61uint64_t NvmeDisk::doRead(uint64_t location) {
62 const size_t bytes = getCacheFillLength(location);
63 if (!m_BlockBytes || !bytes || bytes % m_BlockBytes)
64 return 0;
65 const uintptr_t existing = getCache().lookup(location);
66 if (existing) {
67 getCache().release(location);
68 return bytes;
69 }
70 bool existed = false;
71 const uintptr_t page = getCache().insert(location, &existed);
72 if (!page)
73 return 0;
74 if (existed)
75 return bytes;
76 ByteSet(reinterpret_cast<void*>(page), 0, TargetInfo::getPageSize());
77 if (!m_Controller->readWrite(m_Nsid, location / m_BlockBytes, bytes / m_BlockBytes,
78 reinterpret_cast<void*>(page), bytes, false)) {
79 if (!getCache().discardEditing(location))
80 FATAL("NVMe: failed to discard incomplete cache fill");
81 return 0;
82 }
83 getCache().markNoLongerEditing(location);
84 return bytes;
85}
86uint64_t NvmeDisk::doWrite(uint64_t location) {
87#if CRIPPLE_HDD
88 return 0;
89#else
90 if (!validPageLength(location))
91 return 0;
92 const uintptr_t page = getCache().lookup(location);
93 if (!page)
94 return 0;
95 CachePageGuard guard(getCache(), location);
96 return doWriteDirect(location, page);
97#endif
98}
99uint64_t NvmeDisk::doWriteDirect(uint64_t location, uintptr_t page) {
100#if CRIPPLE_HDD
101 return 0;
102#else
103 const size_t bytes = validPageLength(location);
104 if (!page || !bytes)
105 return 0;
106 return m_Controller->readWrite(m_Nsid, location / m_BlockBytes, bytes / m_BlockBytes,
107 reinterpret_cast<void*>(page), bytes, true)
108 ? bytes
109 : 0;
110#endif
111}
112uint64_t NvmeDisk::doSync(uint64_t location) {
113#if CRIPPLE_HDD
114 return 0;
115#else
116 const size_t bytes = location == SyncWholeDevice ? bool(m_BlockBytes) : validPageLength(location);
117 return bytes && m_Controller->flush(m_Nsid) ? bytes : 0;
118#endif
119}
void release(uintptr_t key)
Definition Cache.cc:1412
uintptr_t insert(uintptr_t key, bool *alreadyExisted=nullptr)
Definition Cache.cc:783
void markNoLongerEditing(uintptr_t key, size_t length=0)
Definition Cache.cc:2394
uintptr_t lookup(uintptr_t key)
Definition Cache.cc:709
void retireEndpoint()
Definition Disk.cc:133
void getName(String &name) override
Definition NvmeDisk.cc:49
size_t getBlockSize() const override
Gets the preferred I/O extent of the disk.
Definition NvmeDisk.cc:52
uint64_t doWriteDirect(uint64_t location, uintptr_t page) override
Definition NvmeDisk.cc:99
size_t getCacheFillLength(uint64_t location) const
Definition ScsiDisk.cc:1335
void shutdownCache()
Definition ScsiDisk.cc:269
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
@ Dec
Definition Log.h:144
@ Hex
Definition Log.h:142