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/utilities/utility.h"
20#include "modules/subsys/posix/FileDescriptor.h"
21#include "modules/subsys/posix/PosixSubsystem.h"
22#include "modules/subsys/posix/file-syscalls.h"
23#include "modules/system/vfs/File.h"
25#include "modules/system/vfs/Pipe.h"
28constexpr size_t BounceCapacity = PIPE_BUF_MAX + 1;
29constexpr int PreservedErrno = 173;
30constexpr int LinuxRwfNoAppend = 0x20;
32enum class FaultMode { None, WriteAfterFirst, ReadBeforeSecondCopy };
40bool allocateUserMapping(
Process* process,
size_t length, uintptr_t& address) {
42 if (!process->allocateUserRange(Process::UserRegion::Normal, length, address)) {
46 uintptr_t mappedAddress = address;
48 mappedAddress, length, MemoryMappedObject::Read | MemoryMappedObject::Write);
49 if (!mapping || mappedAddress != address) {
51 process->freeUserRange(Process::UserRegion::Normal, address, length);
58class PositionalVectorProbeFile final :
public File {
60 explicit PositionalVectorProbeFile(FaultMode faultMode,
size_t initialSize = 0)
61 :
File(
String(
"positional-vector-probe"), 0, 0, 0, 1, nullptr, initialSize, nullptr),
62 m_FaultMode(faultMode),
68 m_SawRawPointer(false),
69 m_ReadOffsets{0, 0, 0, 0},
70 m_WriteOffsets{0, 0, 0, 0} {}
72 void configure(uintptr_t userBase,
size_t mappingLength,
size_t pageSize) {
73 m_UserBase = userBase;
74 m_MappingLength = mappingLength;
75 m_PageSize = pageSize;
78 size_t readCalls()
const {
82 size_t writeCalls()
const {
86 uint64_t readOffset(
size_t index)
const {
87 return m_ReadOffsets[index];
90 uint64_t writeOffset(
size_t index)
const {
91 return m_WriteOffsets[index];
94 bool sawRawPointer()
const {
95 return m_SawRawPointer;
103 uint64_t
readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
104 const size_t slot = m_ReadCalls;
106 recordPointer(buffer);
108 m_ReadOffsets[slot] = location;
110 ByteSet(
reinterpret_cast<void*
>(buffer),
static_cast<char>(
'a' + slot), size);
111 if (m_FaultMode == FaultMode::ReadBeforeSecondCopy && slot == 1) {
113 m_UserBase + m_PageSize, m_MappingLength - m_PageSize, MemoryMappedObject::Read);
118 uint64_t
writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
119 const size_t slot = m_WriteCalls;
121 recordPointer(buffer);
123 m_WriteOffsets[slot] = location;
125 if (m_FaultMode == FaultMode::WriteAfterFirst && slot == 0) {
127 m_UserBase + m_PageSize, m_MappingLength - m_PageSize, MemoryMappedObject::None);
129 if (location + size > getSize()) {
130 setSize(location + size);
136 void recordPointer(uintptr_t buffer) {
137 if (buffer >= m_UserBase && buffer < m_UserBase + m_MappingLength) {
138 m_SawRawPointer =
true;
142 FaultMode m_FaultMode;
143 uintptr_t m_UserBase;
144 size_t m_MappingLength;
148 bool m_SawRawPointer;
149 uint64_t m_ReadOffsets[4];
150 uint64_t m_WriteOffsets[4];
153struct PositionalVectorContext {
154 PositionalVectorContext(
Process* process, PositionalVectorProbeFile* policyWrite,
155 PositionalVectorProbeFile* policyRead,
156 PositionalVectorProbeFile* faultWrite,
157 PositionalVectorProbeFile* faultRead)
159 policyWrite(policyWrite),
160 policyRead(policyRead),
161 faultWrite(faultWrite),
162 faultRead(faultRead),
170 PositionalVectorProbeFile* policyWrite;
171 PositionalVectorProbeFile* policyRead;
172 PositionalVectorProbeFile* faultWrite;
173 PositionalVectorProbeFile* faultRead;
181int positionalVectorWorker(
void* parameter) {
182 constexpr int PolicyWriteDescriptor = 96;
183 constexpr int PolicyReadDescriptor = 97;
184 constexpr int FaultWriteDescriptor = 98;
185 constexpr int FaultReadDescriptor = 99;
187 PositionalVectorContext* context =
reinterpret_cast<PositionalVectorContext*
>(parameter);
190 const size_t mappingLength = pageSize * 4;
191 uintptr_t address = 0;
192 if (!allocateUserMapping(context->process, mappingLength, address)) {
193 context->returned += 1;
197 context->policyWrite->configure(address, mappingLength, pageSize);
198 context->policyRead->configure(address, mappingLength, pageSize);
199 context->faultWrite->configure(address, mappingLength, pageSize);
200 context->faultRead->configure(address, mappingLength, pageSize);
202 char* userBuffer =
reinterpret_cast<char*
>(address);
203 struct iovec smallVectors[2] = {
205 {userBuffer + 32, 2},
208 ByteSet(userBuffer,
'w', mappingLength);
210 context->results[0] =
211 posix_pwritev(PolicyWriteDescriptor, smallVectors, 2,
static_cast<off_t
>(7));
212 context->errors[0] = thread->
getErrno();
215 context->results[1] =
216 posix_pwritev2(PolicyWriteDescriptor, smallVectors, 2,
static_cast<off_t
>(9), 0);
217 context->errors[1] = thread->
getErrno();
220 context->results[2] = posix_pwritev2(PolicyWriteDescriptor, smallVectors, 2,
221 static_cast<off_t
>(11), LinuxRwfNoAppend);
222 context->errors[2] = thread->
getErrno();
225 context->results[3] =
226 posix_pwritev2(PolicyWriteDescriptor, smallVectors, 2, -1, LinuxRwfNoAppend);
227 context->errors[3] = thread->
getErrno();
229 ByteSet(userBuffer, 0, mappingLength);
231 context->results[4] = posix_preadv(PolicyReadDescriptor, smallVectors, 2,
static_cast<off_t
>(19));
232 context->errors[4] = thread->
getErrno();
233 context->readValues[0] = userBuffer[0];
234 context->readValues[1] = userBuffer[32];
237 context->results[5] = posix_preadv2(PolicyReadDescriptor, smallVectors, 2, -1, 0);
238 context->errors[5] = thread->
getErrno();
239 context->readValues[2] = userBuffer[0];
240 context->readValues[3] = userBuffer[32];
242 struct iovec faultWriteVectors[2] = {
243 {userBuffer, BounceCapacity},
244 {userBuffer + pageSize * 2, BounceCapacity},
246 ByteSet(userBuffer,
'f', mappingLength);
248 context->results[6] =
249 posix_pwritev(FaultWriteDescriptor, faultWriteVectors, 2,
static_cast<off_t
>(31));
250 context->errors[6] = thread->
getErrno();
252 MemoryMappedObject::Read | MemoryMappedObject::Write);
254 struct iovec faultReadVector = {userBuffer, BounceCapacity * 2};
255 ByteSet(userBuffer, 0, mappingLength);
257 context->results[7] =
258 posix_preadv(FaultReadDescriptor, &faultReadVector, 1,
static_cast<off_t
>(41));
259 context->errors[7] = thread->
getErrno();
261 MemoryMappedObject::Read | MemoryMappedObject::Write);
264 context->results[8] =
265 posix_pwritev2(PolicyWriteDescriptor, smallVectors, 2, 0, LinuxRwfNoAppend << 1);
266 context->errors[8] = thread->
getErrno();
269 context->process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
270 context->setup =
true;
271 context->returned += 1;
275bool positionalVectorSemantics(
Process* kernelProcess) {
276 constexpr size_t PolicyWriteDescriptor = 96;
277 constexpr size_t PolicyReadDescriptor = 97;
278 constexpr size_t FaultWriteDescriptor = 98;
279 constexpr size_t FaultReadDescriptor = 99;
283 process->setSubsystem(subsystem);
285 PositionalVectorProbeFile policyWrite(FaultMode::None, 100);
286 PositionalVectorProbeFile policyRead(FaultMode::None);
287 PositionalVectorProbeFile faultWrite(FaultMode::WriteAfterFirst);
288 PositionalVectorProbeFile faultRead(FaultMode::ReadBeforeSecondCopy);
290 PolicyWriteDescriptor,
291 new FileDescriptor(&policyWrite, 123, PolicyWriteDescriptor, 0, O_WRONLY | O_APPEND));
293 PolicyReadDescriptor,
294 new FileDescriptor(&policyRead, 147, PolicyReadDescriptor, 0, O_RDONLY | O_APPEND));
296 FaultWriteDescriptor,
297 new FileDescriptor(&faultWrite, 173, FaultWriteDescriptor, 0, O_WRONLY | O_APPEND));
299 FaultReadDescriptor,
new FileDescriptor(&faultRead, 197, FaultReadDescriptor, 0, O_RDONLY));
301 PositionalVectorContext context(process, &policyWrite, &policyRead, &faultWrite, &faultRead);
303 new Thread(process, positionalVectorWorker, &context,
nullptr,
false,
true,
true);
304 worker->setName(
"hosted positional vector I/O semantics");
305 const bool started =
worker->start();
306 const bool joined = started &&
worker->joinForCompletion();
320 bool passed = started && joined && context.returned == 1 && context.setup && acquired;
321 for (
size_t i = 0; i < 8; ++i) {
322 passed = context.results[i] == (i == 6 || i == 7 ?
static_cast<ssize_t
>(BounceCapacity)
323 : static_cast<ssize_t>(5)) &&
324 context.errors[i] == PreservedErrno && passed;
326 passed = context.results[8] == -1 && context.errors[8] == Error::OperationNotSupported &&
329 policyWrite.writeCalls() == 4 && policyWrite.writeOffset(0) == 7 &&
330 policyWrite.writeOffset(1) == 100 && policyWrite.writeOffset(2) == 11 &&
331 policyWrite.writeOffset(3) == 123 && policyRead.readCalls() == 4 &&
332 policyRead.readOffset(0) == 19 && policyRead.readOffset(1) == 22 &&
333 policyRead.readOffset(2) == 147 && policyRead.readOffset(3) == 150 &&
334 context.readValues[0] ==
'a' && context.readValues[1] ==
'b' &&
335 context.readValues[2] ==
'c' && context.readValues[3] ==
'd' &&
336 faultWrite.writeCalls() == 1 && faultWrite.writeOffset(0) == 31 &&
337 faultRead.readCalls() == 2 && faultRead.readOffset(0) == 41 &&
338 faultRead.readOffset(1) == 41 + BounceCapacity && !policyWrite.sawRawPointer() &&
339 !policyRead.sawRawPointer() && !faultWrite.sawRawPointer() &&
340 !faultRead.sawRawPointer() && passed;
342 policyWriter.reset();
343 policyReader.reset();
346 for (
size_t fd = PolicyWriteDescriptor; fd <= FaultReadDescriptor; ++fd) {
347 passed = closeDescriptor(subsystem, fd) && passed;
353 "HOSTED-SYSCALL-TEST: FAIL positional-vector-io-semantics: offset isolation, append "
354 "policy, partial progress, or usercopy regressed");
358 NOTICE(
"HOSTED-SYSCALL-TEST: PASS positional-vector-io-semantics");
363bool runHostedPositionalVectorIoRegressions(
Process* process) {
364 return positionalVectorSemantics(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)