8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/TargetInfo.h"
11#include "pedigree/kernel/machine/Disk.h"
12#include "pedigree/kernel/utilities/utility.h"
14#include "modules/system/rawfs/RawFs.h"
15#include "modules/system/rawfs/RawFsDir.h"
16#include "modules/system/rawfs/RawFsFile.h"
19class TrackingDisk final :
public Disk {
22 static constexpr size_t DataSize = (2 * PageSize) + 512;
23 static constexpr size_t PageSlots = 4;
32 m_DataAllocation(new uint8_t[DataSize + PageSize - 1]),
33 m_Data(reinterpret_cast<uint8_t*>(
34 (reinterpret_cast<uintptr_t>(m_DataAllocation) + PageSize - 1) & ~(PageSize - 1))),
35 m_PageAllocation(new uint8_t[(PageSlots * PageSize) + PageSize - 1]),
36 m_PageData(reinterpret_cast<uint8_t*>(
37 (reinterpret_cast<uintptr_t>(m_PageAllocation) + PageSize - 1) & ~(PageSize - 1))),
39 m_BalanceError(false) {
40 for (
size_t i = 0; i < DataSize; ++i) {
41 m_Data[i] =
static_cast<uint8_t
>((i * 29) ^ (i >> 2));
43 ByteSet(m_ReadLocations, 0,
sizeof(m_ReadLocations));
44 ByteSet(m_UnpinLocations, 0,
sizeof(m_UnpinLocations));
45 ByteSet(m_PageReferences, 0,
sizeof(m_PageReferences));
46 ByteSet(m_PageKeys, 0xFF,
sizeof(m_PageKeys));
49 ~TrackingDisk()
override {
50 delete[] m_PageAllocation;
51 delete[] m_DataAllocation;
55 const uint64_t page = pageLocation(location);
56 if (location >= DataSize || page >= DataSize) {
60 const size_t slot = findPage(page,
true);
61 if (slot == PageSlots) {
65 if (m_ReadCount < 8) {
66 m_ReadLocations[m_ReadCount] = location;
69 ++m_PageReferences[slot];
70 const size_t validLength = PageSize < (DataSize - page) ? PageSize : (DataSize - page);
71 return BufferView(m_PageData + (slot * PageSize) + (location - page),
72 validLength - (location - page));
75 size_t getSize()
const override {
79 bool writeFrom(uint64_t location,
const void* buffer,
size_t length)
override {
81 if (!m_WriteSuccess || !buffer || location > DataSize || length > DataSize - location)
83 MemoryCopy(m_Data + location, buffer, length);
97 bool pin(uint64_t location)
override {
98 const size_t slot = findPage(pageLocation(location),
false);
99 if (slot == PageSlots) {
102 ++m_PageReferences[slot];
106 void unpin(uint64_t location)
override {
107 if (m_UnpinCount < 8) {
108 m_UnpinLocations[m_UnpinCount] = location;
112 const size_t slot = findPage(pageLocation(location),
false);
113 if (slot == PageSlots || !m_PageReferences[slot]) {
114 m_BalanceError =
true;
117 --m_PageReferences[slot];
120 void align(uint64_t location)
override {
121 m_AlignmentPoint = location;
124 bool balanced()
const {
125 if (m_BalanceError) {
128 for (
size_t i = 0; i < PageSlots; ++i) {
129 if (m_PageReferences[i]) {
136 const uint8_t* data()
const {
146 uint64_t m_ReadLocations[8];
147 uint64_t m_UnpinLocations[8];
150 uint64_t pageLocation(uint64_t location)
const {
151 const uint64_t alignPoint = m_AlignmentPoint <= location ? m_AlignmentPoint : 0;
152 return location - ((location - alignPoint) % PageSize);
155 size_t findPage(uint64_t page,
bool create) {
156 size_t freeSlot = PageSlots;
157 for (
size_t i = 0; i < PageSlots; ++i) {
158 if (m_PageKeys[i] == page) {
161 if (freeSlot == PageSlots && m_PageKeys[i] == ~uint64_t(0)) {
166 if (!create || freeSlot == PageSlots) {
170 m_PageKeys[freeSlot] = page;
171 uint8_t* destination = m_PageData + (freeSlot * PageSize);
172 ByteSet(destination, 0, PageSize);
173 const size_t validLength = PageSize < (DataSize - page) ? PageSize : (DataSize - page);
174 MemoryCopy(destination, m_Data + page, validLength);
178 uint8_t* m_DataAllocation;
180 uint8_t* m_PageAllocation;
182 uint64_t m_PageKeys[PageSlots];
183 size_t m_PageReferences[PageSlots];
184 uint64_t m_AlignmentPoint;
188bool rawFsNativePageOwnership() {
189 constexpr size_t Start = 512;
190 constexpr size_t TransferSize = TrackingDisk::PageSize;
195 uint8_t* destination =
new uint8_t[TransferSize];
197 const uint64_t bytesRead =
198 file.read(Start, TransferSize,
reinterpret_cast<uintptr_t
>(destination));
199 const bool bytesMatch = !MemoryCompare(destination, disk.data() + Start, TransferSize);
200 delete[] destination;
202 const bool splitAtPage = disk.m_ReadCount == 2 && disk.m_ReadLocations[0] == 0 &&
203 disk.m_ReadLocations[1] == TrackingDisk::PageSize;
204 const bool balanced = disk.m_UnpinCount == 2 && disk.m_UnpinLocations[0] == 0 &&
205 disk.m_UnpinLocations[1] == TrackingDisk::PageSize && disk.balanced();
207 const bool passed = bytesRead == TransferSize && bytesMatch && splitAtPage && balanced;
209 NOTICE(
"HOSTED-WAIT-TEST: PASS rawfs-native-page-ownership");
212 "HOSTED-WAIT-TEST: FAIL rawfs-native-page-ownership: "
213 "one raw read crossed an unowned native page or leaked its "
219bool rawFsParentAlignmentIsolation() {
220 constexpr size_t Start = TrackingDisk::PageSize;
221 constexpr size_t TransferSize = TrackingDisk::PageSize;
226 RawFsFile file(
String(
"aligned-raw-disk"), &filesystem,
nullptr, &disk);
227 uint8_t* destination =
new uint8_t[TransferSize];
229 const uint64_t bytesRead =
230 file.read(Start, TransferSize,
reinterpret_cast<uintptr_t
>(destination));
231 const bool bytesMatch = !MemoryCompare(destination, disk.data() + Start, TransferSize);
232 delete[] destination;
234 const bool splitAtParentPage = disk.m_ReadCount == 2 && disk.m_ReadLocations[0] == Start &&
235 disk.m_ReadLocations[1] == (Start + 512);
236 const bool balanced = disk.m_UnpinCount == 2 && disk.m_UnpinLocations[0] == Start &&
237 disk.m_UnpinLocations[1] == (Start + 512) && disk.balanced();
239 const bool passed = bytesRead == TransferSize && bytesMatch && splitAtParentPage && balanced;
241 NOTICE(
"HOSTED-WAIT-TEST: PASS rawfs-parent-alignment-isolation");
244 "HOSTED-WAIT-TEST: FAIL rawfs-parent-alignment-isolation: "
245 "a parent alignment point exposed bytes beyond one owned page");
250bool filePastEofDoesNotRead() {
254 uint8_t destination = 0;
256 const uint64_t bytesRead =
257 file.read(TrackingDisk::DataSize + 1, 1,
reinterpret_cast<uintptr_t
>(&destination));
258 const uintptr_t block = file.readBlock(TrackingDisk::DataSize + 1);
259 const bool pinned = file.pinBlock(TrackingDisk::DataSize + 1);
261 !bytesRead && !block && !pinned && !disk.m_ReadCount && !disk.m_UnpinCount && disk.balanced();
263 NOTICE(
"HOSTED-WAIT-TEST: PASS file-past-eof-no-read");
266 "HOSTED-WAIT-TEST: FAIL file-past-eof-no-read: "
267 "an out-of-range read reached the backing object");
272bool rawFsTerminalPage() {
273 constexpr uint64_t Start = 2 * TrackingDisk::PageSize;
274 constexpr size_t TransferSize = 512;
279 uint8_t destination[TransferSize] = {};
281 const uint64_t bytesRead =
282 file.read(Start, TransferSize,
reinterpret_cast<uintptr_t
>(destination));
283 const bool bytesMatch = !MemoryCompare(destination, disk.data() + Start, TransferSize);
285 const uintptr_t cached = file.readBlock(Start);
286 bool zeroFilled = cached != 0;
287 for (
size_t i = TransferSize; cached && i < TrackingDisk::PageSize; ++i) {
288 if (
reinterpret_cast<const uint8_t*
>(cached)[i]) {
294 file.unpinBlock(Start);
297 const bool passed = bytesRead == TransferSize && bytesMatch && zeroFilled &&
298 disk.m_ReadCount == 1 && disk.m_UnpinCount == 1 && disk.balanced();
300 NOTICE(
"HOSTED-WAIT-TEST: PASS rawfs-terminal-page");
303 "HOSTED-WAIT-TEST: FAIL rawfs-terminal-page: terminal bytes were rejected, exposed past "
304 "EOF, or leaked a backing reference");
309bool rawFsCheckedWriteback() {
313 auto* directory =
new RawFsDir(
String(
"nested"), &filesystem, root);
314 root->addEntry(directory);
315 auto* file =
new RawFsFile(
String(
"raw-disk"), &filesystem, directory, &disk);
316 directory->addEntry(file);
317 constexpr uint64_t location = 2 * TrackingDisk::PageSize;
318 uint8_t replacement[512];
319 ByteSet(replacement, 0x6d,
sizeof(replacement));
320 disk.m_WriteSuccess = 0;
321 const bool accepted =
322 file->write(location,
sizeof(replacement),
reinterpret_cast<uintptr_t
>(replacement)) ==
324 const bool writeFailed = filesystem.
sync() == Filesystem::SyncStatus::IoError;
325 disk.m_WriteSuccess = 1;
326 disk.m_SyncSuccess = 0;
327 const bool flushFailed = filesystem.
sync() == Filesystem::SyncStatus::IoError;
328 disk.m_SyncSuccess = 1;
329 const bool recovered = filesystem.
sync() == Filesystem::SyncStatus::Success;
330 const bool durable = !MemoryCompare(disk.data() + location, replacement,
sizeof(replacement));
331 const bool closed = filesystem.
shutdown() == Filesystem::SyncStatus::Success &&
332 filesystem.
shutdown() == Filesystem::SyncStatus::Success;
333 const bool passed = accepted && writeFailed && flushFailed && recovered && durable && closed &&
334 disk.m_WriteCount && disk.m_SyncCount && disk.balanced();
336 NOTICE(
"HOSTED-WAIT-TEST: PASS rawfs-checked-writeback");
338 ERROR(
"HOSTED-WAIT-TEST: FAIL rawfs-checked-writeback: write/flush failure or retry was lost");
343EXPORTED_PUBLIC
bool runHostedRawFsContractRegressions() {
344 return rawFsNativePageOwnership() && rawFsParentAlignmentIsolation() &&
345 filePastEofDoesNotRead() && rawFsTerminalPage() && rawFsCheckedWriteback();
virtual void align(uint64_t location)
Sets the page boundary alignment after a specific location on the disk.
virtual BufferView read(uint64_t location)
virtual size_t getSize() const
Gets the size of the disk.
virtual void unpin(uint64_t location)=0
virtual MUST_USE_RESULT bool pin(uint64_t location)=0
Pins a cache page.
virtual size_t getBlockSize() const
Gets the preferred I/O extent of the disk.
virtual MUST_USE_RESULT bool syncData()
virtual SyncStatus shutdown()
virtual SyncStatus sync()
virtual File * getRoot() const
static constexpr size_t getPageSize() noexcept