The Pedigree Project 0.1
MemoryMappedFile-usercopy.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/process/Thread.h"
3#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
4#include "pedigree/kernel/processor/Processor.h"
5#include "pedigree/kernel/processor/ProcessorInformation.h"
6
7#include "File.h"
8#include "MemoryMappedFile.h"
9
10MemoryMapManager::UserPageCopyStatus MemoryMapManager::copyUserPage(VirtualAddressSpace& space,
11 uintptr_t address,
12 void* kernelBuffer,
13 size_t bytes, bool write) {
14#if !X64 || HOSTED
15 (void)space;
16 (void)address;
17 (void)kernelBuffer;
18 (void)bytes;
19 (void)write;
20 return UserPageCopyStatus::Unsupported;
21#else
22 const size_t pageSize = PhysicalMemoryManager::getPageSize();
23 constexpr uintptr_t UserEnd = 0x0000800000000000ULL;
24 if (!kernelBuffer || !bytes || bytes > pageSize || address < space.getUserStart() ||
25 address >= UserEnd || address >= space.getKernelStart() || bytes > UserEnd - address ||
26 bytes > space.getKernelStart() - address || (address & (pageSize - 1)) > pageSize - bytes)
27 return UserPageCopyStatus::Inaccessible;
28
29 OperationGuard operation(*this);
30 const uintptr_t pageAddress = address & ~(pageSize - 1);
31 void* page = reinterpret_cast<void*>(pageAddress);
32 MemoryMappedObject* selected = nullptr;
33 auto* objects = m_MmObjectLists.lookup(&space);
34 if (objects) {
35 for (auto* object : *objects) {
36 if (object->matches(pageAddress)) {
37 selected = object;
38 break;
39 }
40 }
41 }
42 if (selected) {
43 const auto required = write ? MemoryMappedObject::Write : MemoryMappedObject::Read;
44 if (!(selected->permissions() & required) || selected->beyondBackingEnd(pageAddress))
45 return UserPageCopyStatus::Inaccessible;
46 if (selected->backingFile() && selected->backingFile()->isDirectPhysicalMapping())
47 return UserPageCopyStatus::Unsupported;
48 } else if (!space.rawUserMemory().covers(pageAddress, pageSize) &&
49 space.runtimeMappingPages(pageAddress, pageSize) != 1) {
50 return UserPageCopyStatus::Inaccessible;
51 }
52
53 if (selected) {
54 const auto access = selected->prepareResidentAccess(space, pageAddress);
55 if (access != PopulationStatus::Success)
56 return access == PopulationStatus::NoMemory ? UserPageCopyStatus::NoMemory
57 : access == PopulationStatus::Inaccessible ? UserPageCopyStatus::Inaccessible
58 : UserPageCopyStatus::IoError;
59 }
60 bool prepare = !space.isMapped(page);
61 if (!prepare) {
62 physical_uintptr_t physical = 0;
63 size_t flags = 0;
64 space.getMapping(page, physical, flags);
68 return UserPageCopyStatus::Inaccessible;
69 if (write) {
71 return UserPageCopyStatus::Inaccessible;
73 if (!space.handleCopyOnWriteFault(page, true))
74 return UserPageCopyStatus::NoMemory;
75 } else {
76 prepare = !(flags & VirtualAddressSpace::Write);
77 }
78 }
79 }
80
81 if (prepare) {
82 if (!selected)
83 return UserPageCopyStatus::Inaccessible;
84 // A read must retain lazy private CoW; eager-lock population intentionally does not.
85 PopulationStatus status = PopulationStatus::NoMemory;
86 auto* thread = Processor::information().getCurrentThread();
87 const size_t previousError = thread ? thread->getErrno() : 0;
88 if (thread)
89 thread->setErrno(0);
90 const bool prepared = selected->trap(space, pageAddress, write, &status);
91 if (thread)
92 thread->setErrno(previousError);
93 if (!prepared) {
94 switch (status) {
95 case PopulationStatus::NoMemory:
96 return UserPageCopyStatus::NoMemory;
97 case PopulationStatus::Inaccessible:
98 return UserPageCopyStatus::Inaccessible;
99 default:
100 return UserPageCopyStatus::IoError;
101 }
102 }
103 }
104
105 if (write && selected && selected->backingFile()) {
106 uintptr_t identity = 0;
107 size_t fileOffset = 0;
108 if (selected->sharedBacking(pageAddress, identity, fileOffset)) {
109 selected->backingFile()->markPageExternallyWritable(fileOffset);
110 }
111 }
112
113 switch (space.copyResidentUserPage(address, kernelBuffer, bytes, write)) {
114 case VirtualAddressSpace::ResidentCopyStatus::Success:
115 return UserPageCopyStatus::Success;
116 case VirtualAddressSpace::ResidentCopyStatus::Unsupported:
117 return UserPageCopyStatus::Unsupported;
118 default:
119 return UserPageCopyStatus::Inaccessible;
120 }
121#endif
122}
Memory-mapped file interface.
void markPageExternallyWritable(size_t offset)
Definition File.cc:1611
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
virtual bool trap(VirtualAddressSpace &space, uintptr_t address, bool bWrite, PopulationStatus *population=nullptr)=0
Permissions permissions() const
static ProcessorInformation & information()
bool covers(uintptr_t base, size_t length) const
virtual bool isMapped(void *virtualAddress)=0
virtual uintptr_t getUserStart() const =0
virtual bool handleCopyOnWriteFault(void *virtualAddress, bool userMode)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
virtual ResidentCopyStatus copyResidentUserPage(uintptr_t userAddress, void *kernelBuffer, size_t bytes, bool write)
virtual uintptr_t getKernelStart() const =0