8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/errors.h"
11#include "pedigree/kernel/process/Process.h"
12#include "pedigree/kernel/process/Thread.h"
13#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
14#include "pedigree/kernel/processor/Processor.h"
15#include "pedigree/kernel/processor/VirtualAddressSpace.h"
16#include "pedigree/kernel/utilities/utility.h"
22#include "modules/subsys/posix/FileDescriptor.h"
23#include "modules/subsys/posix/PosixSubsystem.h"
24#include "modules/subsys/posix/file-syscalls.h"
25#include "modules/system/vfs/File.h"
27#include "modules/system/vfs/Pipe.h"
30constexpr size_t BounceCapacity = PIPE_BUF_MAX + 1;
31constexpr size_t ChunkedLength = BounceCapacity * 2 + 17;
32constexpr size_t FaultLength = BounceCapacity * 2;
33constexpr int PreservedErrno = 147;
35enum class FaultMode { None, WriteAfterFirst, ReadBeforeSecondCopy };
43bool allocateUserMapping(
Process* process,
size_t length, uintptr_t& address) {
45 if (!process->allocateUserRange(Process::UserRegion::Normal, length, address)) {
49 uintptr_t mappedAddress = address;
51 mappedAddress, length, MemoryMappedObject::Read | MemoryMappedObject::Write);
52 if (!mapping || mappedAddress != address) {
54 process->freeUserRange(Process::UserRegion::Normal, address, length);
61class PositionalProbeFile final :
public File {
63 explicit PositionalProbeFile(FaultMode faultMode)
64 :
File(
String(
"positional-io-probe"), 0, 0, 0, 1, nullptr, 65536, nullptr),
65 m_FaultMode(faultMode),
71 m_SawRawPointer(false),
72 m_ReadOffsets{0, 0, 0, 0},
73 m_WriteOffsets{0, 0, 0, 0},
74 m_ReadSizes{0, 0, 0, 0},
75 m_WriteSizes{0, 0, 0, 0},
76 m_FirstWriteValues{0, 0, 0, 0} {}
78 void configure(uintptr_t userBase,
size_t mappingLength,
size_t pageSize) {
79 m_UserBase = userBase;
80 m_MappingLength = mappingLength;
81 m_PageSize = pageSize;
84 size_t readCalls()
const {
88 size_t writeCalls()
const {
92 uint64_t readOffset(
size_t index)
const {
93 return m_ReadOffsets[index];
96 uint64_t writeOffset(
size_t index)
const {
97 return m_WriteOffsets[index];
100 size_t readSize(
size_t index)
const {
101 return m_ReadSizes[index];
104 size_t writeSize(
size_t index)
const {
105 return m_WriteSizes[index];
108 char firstWriteValue(
size_t index)
const {
109 return m_FirstWriteValues[index];
112 bool sawRawPointer()
const {
113 return m_SawRawPointer;
121 uint64_t
readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
122 const size_t slot = m_ReadCalls;
124 recordPointer(buffer);
126 m_ReadOffsets[slot] = location;
127 m_ReadSizes[slot] = size;
129 ByteSet(
reinterpret_cast<void*
>(buffer),
static_cast<char>(
'a' + slot), size);
131 if (m_FaultMode == FaultMode::ReadBeforeSecondCopy && slot == 1) {
133 m_UserBase + m_PageSize, m_MappingLength - m_PageSize, MemoryMappedObject::Read);
138 uint64_t
writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
139 const size_t slot = m_WriteCalls;
141 recordPointer(buffer);
143 m_WriteOffsets[slot] = location;
144 m_WriteSizes[slot] = size;
146 m_FirstWriteValues[slot] = *
reinterpret_cast<const char*
>(buffer);
150 if (m_FaultMode == FaultMode::WriteAfterFirst && slot == 0) {
152 m_UserBase + m_PageSize, m_MappingLength - m_PageSize, MemoryMappedObject::None);
154 if (location + size > getSize()) {
155 setSize(location + size);
161 void recordPointer(uintptr_t buffer) {
162 if (buffer >= m_UserBase && buffer < m_UserBase + m_MappingLength) {
163 m_SawRawPointer =
true;
167 FaultMode m_FaultMode;
168 uintptr_t m_UserBase;
169 size_t m_MappingLength;
173 bool m_SawRawPointer;
174 uint64_t m_ReadOffsets[4];
175 uint64_t m_WriteOffsets[4];
176 size_t m_ReadSizes[4];
177 size_t m_WriteSizes[4];
178 char m_FirstWriteValues[4];
181struct PositionalIoContext {
182 PositionalIoContext(
Process* process, PositionalProbeFile* policyWrite,
183 PositionalProbeFile* policyRead, PositionalProbeFile* faultWrite,
184 PositionalProbeFile* faultRead)
186 policyWrite(policyWrite),
187 policyRead(policyRead),
188 faultWrite(faultWrite),
189 faultRead(faultRead),
190 policyWriteResult(-2),
192 policyReadResult(-2),
194 faultWriteResult(-2),
210 wrongWriteResult(-2),
212 badAddressResult(-2),
219 PositionalProbeFile* policyWrite;
220 PositionalProbeFile* policyRead;
221 PositionalProbeFile* faultWrite;
222 PositionalProbeFile* faultRead;
223 ssize_t policyWriteResult;
224 int policyWriteError;
225 ssize_t policyReadResult;
227 ssize_t faultWriteResult;
229 ssize_t faultReadResult;
231 ssize_t negativeResult;
233 ssize_t overflowResult;
235 ssize_t oversizedResult;
237 ssize_t pipeReadResult;
239 ssize_t pipeWriteResult;
241 ssize_t wrongReadResult;
243 ssize_t wrongWriteResult;
245 ssize_t badAddressResult;
252int positionalIoWorker(
void* parameter) {
253 constexpr int PolicyWriteDescriptor = 90;
254 constexpr int PolicyReadDescriptor = 91;
255 constexpr int FaultWriteDescriptor = 92;
256 constexpr int FaultReadDescriptor = 93;
257 constexpr int PipeReadDescriptor = 94;
258 constexpr int PipeWriteDescriptor = 95;
260 PositionalIoContext* context =
reinterpret_cast<PositionalIoContext*
>(parameter);
263 const size_t mappingLength = pageSize * 4;
264 uintptr_t address = 0;
265 if (!allocateUserMapping(context->process, mappingLength, address)) {
266 context->returned += 1;
270 context->policyWrite->configure(address, mappingLength, pageSize);
271 context->policyRead->configure(address, mappingLength, pageSize);
272 context->faultWrite->configure(address, mappingLength, pageSize);
273 context->faultRead->configure(address, mappingLength, pageSize);
275 char* userBuffer =
reinterpret_cast<char*
>(address);
276 ByteSet(userBuffer,
'w', mappingLength);
278 context->policyWriteResult = posix_pwrite64(PolicyWriteDescriptor, userBuffer, ChunkedLength, 7);
279 context->policyWriteError = thread->
getErrno();
281 ByteSet(userBuffer, 0, mappingLength);
283 context->policyReadResult = posix_pread64(PolicyReadDescriptor, userBuffer, ChunkedLength, 19);
284 context->policyReadError = thread->
getErrno();
285 context->readValues[0] = userBuffer[0];
286 context->readValues[1] = userBuffer[BounceCapacity];
287 context->readValues[2] = userBuffer[BounceCapacity * 2];
289 ByteSet(userBuffer,
'f', mappingLength);
291 context->faultWriteResult = posix_pwrite64(FaultWriteDescriptor, userBuffer, FaultLength, 31);
292 context->faultWriteError = thread->
getErrno();
294 MemoryMappedObject::Read | MemoryMappedObject::Write);
296 ByteSet(userBuffer, 0, mappingLength);
298 context->faultReadResult = posix_pread64(FaultReadDescriptor, userBuffer, FaultLength, 41);
299 context->faultReadError = thread->
getErrno();
301 MemoryMappedObject::Read | MemoryMappedObject::Write);
304 context->negativeResult = posix_pread64(PolicyReadDescriptor, userBuffer, 1, -1);
305 context->negativeError = thread->
getErrno();
307 context->overflowResult = posix_pwrite64(PolicyWriteDescriptor, userBuffer, 2, INT64_MAX);
308 context->overflowError = thread->
getErrno();
310 context->oversizedResult =
311 posix_pread64(PolicyReadDescriptor, userBuffer,
static_cast<size_t>(SSIZE_MAX) + 1, 0);
312 context->oversizedError = thread->
getErrno();
315 context->pipeReadResult = posix_pread64(PipeReadDescriptor, userBuffer, 1, 0);
316 context->pipeReadError = thread->
getErrno();
318 context->pipeWriteResult = posix_pwrite64(PipeWriteDescriptor, userBuffer, 1, 0);
319 context->pipeWriteError = thread->
getErrno();
322 context->wrongReadResult = posix_pread64(PolicyWriteDescriptor, userBuffer, 1, 0);
323 context->wrongReadError = thread->
getErrno();
325 context->wrongWriteResult = posix_pwrite64(PolicyReadDescriptor, userBuffer, 1, 0);
326 context->wrongWriteError = thread->
getErrno();
330 context->badAddressResult =
331 posix_pwrite64(PolicyWriteDescriptor,
reinterpret_cast<const char*
>(kernelStart), 1, 0);
332 context->badAddressError = thread->
getErrno();
335 context->process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
336 context->setup =
true;
337 context->returned += 1;
341bool positionalIoSemantics(
Process* kernelProcess) {
342 constexpr size_t PolicyWriteDescriptor = 90;
343 constexpr size_t PolicyReadDescriptor = 91;
344 constexpr size_t FaultWriteDescriptor = 92;
345 constexpr size_t FaultReadDescriptor = 93;
346 constexpr size_t PipeReadDescriptor = 94;
347 constexpr size_t PipeWriteDescriptor = 95;
351 process->setSubsystem(subsystem);
353 PositionalProbeFile policyWrite(FaultMode::None);
354 PositionalProbeFile policyRead(FaultMode::None);
355 PositionalProbeFile faultWrite(FaultMode::WriteAfterFirst);
356 PositionalProbeFile faultRead(FaultMode::ReadBeforeSecondCopy);
358 PolicyWriteDescriptor,
359 new FileDescriptor(&policyWrite, 123, PolicyWriteDescriptor, 0, O_WRONLY | O_APPEND));
361 PolicyReadDescriptor,
362 new FileDescriptor(&policyRead, 147, PolicyReadDescriptor, 0, O_RDONLY | O_APPEND));
364 FaultWriteDescriptor,
365 new FileDescriptor(&faultWrite, 173, FaultWriteDescriptor, 0, O_WRONLY | O_APPEND));
367 FaultReadDescriptor,
new FileDescriptor(&faultRead, 197, FaultReadDescriptor, 0, O_RDONLY));
375 PositionalIoContext context(process, &policyWrite, &policyRead, &faultWrite, &faultRead);
376 Thread*
worker =
new Thread(process, positionalIoWorker, &context,
nullptr,
false,
true,
true);
377 worker->setName(
"hosted positional I/O semantics");
378 const bool started =
worker->start();
379 const bool joined = started &&
worker->joinForCompletion();
394 started && joined && context.returned == 1 && context.setup && acquired &&
395 context.policyWriteResult ==
static_cast<ssize_t
>(ChunkedLength) &&
396 context.policyWriteError == PreservedErrno &&
397 context.policyReadResult ==
static_cast<ssize_t
>(ChunkedLength) &&
398 context.policyReadError == PreservedErrno && policyWriter->
getOffset() == 123 &&
399 policyReader->
getOffset() == 147 && policyWrite.writeCalls() == 3 &&
400 policyWrite.writeOffset(0) == 7 && policyWrite.writeOffset(1) == 7 + BounceCapacity &&
401 policyWrite.writeOffset(2) == 7 + BounceCapacity * 2 &&
402 policyWrite.writeSize(0) == BounceCapacity && policyWrite.writeSize(1) == BounceCapacity &&
403 policyWrite.writeSize(2) == 17 && policyWrite.firstWriteValue(0) ==
'w' &&
404 policyWrite.firstWriteValue(1) ==
'w' && policyWrite.firstWriteValue(2) ==
'w' &&
405 !policyWrite.sawRawPointer() && policyRead.readCalls() == 3 &&
406 policyRead.readOffset(0) == 19 && policyRead.readOffset(1) == 19 + BounceCapacity &&
407 policyRead.readOffset(2) == 19 + BounceCapacity * 2 &&
408 policyRead.readSize(0) == BounceCapacity && policyRead.readSize(1) == BounceCapacity &&
409 policyRead.readSize(2) == 17 && !policyRead.sawRawPointer() && context.readValues[0] ==
'a' &&
410 context.readValues[1] ==
'b' && context.readValues[2] ==
'c' &&
411 context.faultWriteResult ==
static_cast<ssize_t
>(BounceCapacity) &&
412 context.faultWriteError == PreservedErrno &&
413 context.faultReadResult ==
static_cast<ssize_t
>(BounceCapacity) &&
414 context.faultReadError == PreservedErrno && faultWriter->
getOffset() == 173 &&
415 faultReader->
getOffset() == 197 && faultWrite.writeCalls() == 1 &&
416 faultWrite.writeOffset(0) == 31 && !faultWrite.sawRawPointer() &&
417 faultRead.readCalls() == 2 && faultRead.readOffset(0) == 41 &&
418 faultRead.readOffset(1) == 41 + BounceCapacity && !faultRead.sawRawPointer() &&
419 context.negativeResult == -1 && context.negativeError == Error::InvalidArgument &&
420 context.overflowResult == -1 && context.overflowError == Error::InvalidArgument &&
421 context.oversizedResult == -1 && context.oversizedError == Error::InvalidArgument &&
422 context.pipeReadResult == -1 && context.pipeReadError == Error::IllegalSeek &&
423 context.pipeWriteResult == -1 && context.pipeWriteError == Error::IllegalSeek &&
424 context.wrongReadResult == -1 && context.wrongReadError == Error::BadFileDescriptor &&
425 context.wrongWriteResult == -1 && context.wrongWriteError == Error::BadFileDescriptor &&
426 context.badAddressResult == -1 && context.badAddressError == Error::BadAddress;
428 policyWriter.reset();
429 policyReader.reset();
432 for (
size_t fd = PolicyWriteDescriptor; fd <= PipeWriteDescriptor; ++fd) {
433 passed = closeDescriptor(subsystem, fd) && passed;
439 "HOSTED-SYSCALL-TEST: FAIL positional-io-semantics: "
440 "offset isolation, append override, usercopy, partial progress, or validation regressed");
444 NOTICE(
"HOSTED-SYSCALL-TEST: PASS positional-io-semantics");
449bool runHostedPositionalIoRegressions(
Process* process) {
450 return positionalIoSemantics(process);
Memory-mapped file interface.
uint64_t getOffset() const
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual bool isBytewise() const
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
size_t remove(uintptr_t base, size_t length)
static MemoryMapManager & instance()
static constexpr size_t getPageSize() PURE
bool acquireFileDescriptor(size_t fd, DescriptorLease &descriptor)
bool closeFileDescriptor(size_t fd, const DescriptorLease &descriptor)
void addFileDescriptor(size_t fd, FileDescriptor *pFd)
static ProcessorInformation & information()
void setErrno(size_t err)