37 using Status = VirtualAddressSpace::RemapStatus;
44 m_Moved(request.source != request.destination),
45 m_Preserved(request.oldLength < request.newLength ? request.oldLength : request.newLength),
54 for (
size_t i = m_UsedSpares; i < m_SpareCount; ++i) {
59 Status prepare(ssize_t& remaining) {
60 const size_t destinationPages = m_Moved ? m_Request.newLength / PageSize : 0;
61 const size_t count = (m_Request.oldLength - m_Preserved) / PageSize + destinationPages;
63 if (!permitAllocation(remaining)) {
64 return Status::NoMemory;
68 return Status::NoMemory;
71 if (destinationPages) {
72 if (!permitAllocation(remaining)) {
73 return Status::NoMemory;
76 if (!m_AllowedVictim) {
77 return Status::NoMemory;
79 ByteSet(m_AllowedVictim.get(), 0, destinationPages);
81 for (
size_t i = 0; i < m_Request.victimCount; ++i) {
82 const auto& range = m_Request.victims[i];
83 if (!m_Moved || !m_Request.replace || !range.length || (range.base % PageSize) ||
84 (range.length % PageSize) || range.base < m_Request.destination ||
85 range.base - m_Request.destination > m_Request.newLength ||
86 range.length > m_Request.newLength - (range.base - m_Request.destination)) {
87 return Status::InvalidRange;
89 const size_t first = (range.base - m_Request.destination) / PageSize;
90 for (
size_t j = 0; j < range.length / PageSize; ++j) {
91 if (m_AllowedVictim.get()[first + j]) {
92 return Status::InvalidRange;
94 m_AllowedVictim.get()[first + j] = 1;
97 m_Request.victims =
nullptr;
98 m_Request.victimCount = 0;
99 const size_t spares = m_Moved ? tableCapacity(m_Request.destination, m_Preserved) : 0;
101 if (!permitAllocation(remaining)) {
102 return Status::NoMemory;
106 return Status::NoMemory;
108 for (
size_t i = 0; i < spares; ++i) {
109 if (!permitAllocation(remaining)) {
110 return Status::NoMemory;
114 return Status::NoMemory;
116 m_Spares.get()[m_SpareCount++] = page;
120 return Status::Success;
123 Status
commit(VirtualAddressSpace::RemapAdmission admission,
void* context)
override {
124 if (m_Committed || !admission) {
125 return Status::InvalidRange;
127 size_t detachedCount = 0, removedPages = 0;
130 mutation.lock(m_Space.
m_Lock);
131 for (
size_t offset = 0; offset < m_Request.oldLength; offset += PageSize) {
132 uint64_t* leaf =
nullptr;
133 const auto status = lookup(m_Request.source + offset, leaf);
134 if (status != Status::Success) {
138 for (
size_t offset = 0; offset < m_Request.newLength; offset += PageSize) {
139 uint64_t* leaf =
nullptr;
140 const auto status = lookup(m_Request.destination + offset, leaf);
141 if (status != Status::Success) {
144 if (!m_Moved && offset < m_Request.oldLength) {
147 if (leaf && (*leaf & LeafFlags) &&
148 (!m_Moved || !m_Request.replace || !m_AllowedVictim.get()[offset / PageSize])) {
149 return Status::InvalidRange;
154 if (!admission(context)) {
155 return Status::Retry;
157 for (
size_t offset = m_Preserved; offset < m_Request.oldLength; offset += PageSize) {
158 removedPages += detach(m_Request.source + offset, m_Detached.get()[detachedCount++]);
161 for (
size_t offset = 0; offset < m_Request.newLength; offset += PageSize) {
162 removedPages += detach(m_Request.destination + offset, m_Detached.get()[detachedCount++]);
164 for (
size_t offset = 0; offset < m_Preserved; offset += PageSize) {
165 uint64_t* source =
nullptr;
166 lookup(m_Request.source + offset, source);
167 if (!source || !(*source & LeafFlags)) {
170 uint64_t* destination = ensureLeaf(m_Request.destination + offset);
171 const uint64_t value = __atomic_exchange_n(source, uint64_t{0}, __ATOMIC_ACQ_REL);
172 __atomic_store_n(destination, value, __ATOMIC_RELEASE);
175 for (
size_t offset = 0; offset < m_Request.oldLength; offset += PageSize) {
176 if (!m_Space.
invalidateMapping(
reinterpret_cast<void*
>(m_Request.source + offset),
178 mutation.panicInvalidationFailure();
181 if (m_Moved || m_Request.newLength > m_Request.oldLength) {
182 const size_t begin = m_Moved ? 0 : m_Request.oldLength;
183 for (
size_t offset = begin; offset < m_Request.newLength; offset += PageSize) {
184 if (!m_Space.
invalidateMapping(
reinterpret_cast<void*
>(m_Request.destination + offset),
186 mutation.panicInvalidationFailure();
197 process->trackPages(-
static_cast<ssize_t
>(removedPages), 0, 0);
199 return Status::Success;
202 const Detached* detachedPages()
const override {
203 return m_Committed ? m_Detached.get() :
nullptr;
205 size_t detachedPageCount()
const override {
206 return m_Committed ? (m_Request.oldLength - m_Preserved) / PageSize +
207 (m_Moved ? m_Request.newLength / PageSize : 0)
212 Status lookup(uintptr_t address, uint64_t*& leaf) {
215 for (
size_t shift = 39; shift > 12; shift -= 9) {
216 uint64_t* entry = TABLE_ENTRY(table, (address >> shift) & 511);
217 if (!(*entry & PAGE_PRESENT)) {
218 return Status::Success;
220 if (*entry & PAGE_2MB) {
221 return Status::Unsupported;
223 table = PAGE_GET_PHYSICAL_ADDRESS(entry);
225 leaf = TABLE_ENTRY(table, (address >> 12) & 511);
226 if ((*leaf & PAGE_SWAPPED) || ((*leaf & LeafFlags) && !(*leaf & PAGE_USER))) {
227 return Status::Unsupported;
229 return Status::Success;
232 bool detach(uintptr_t address, Detached& result) {
233 result = {address, 0, 0,
false};
234 uint64_t* leaf =
nullptr;
235 lookup(address, leaf);
236 if (!leaf || !(*leaf & LeafFlags)) {
239 const uint64_t value = __atomic_exchange_n(leaf, uint64_t{0}, __ATOMIC_ACQ_REL);
240 result.physical = value & ~0x8780000000000FFFULL;
241 result.flags = m_Space.
fromFlags(value & 0x8780000000000FFFULL,
true);
242 result.mapped =
true;
246 uint64_t* ensureLeaf(uintptr_t address) {
248 for (
size_t shift = 39; shift > 12; shift -= 9) {
249 uint64_t* entry = TABLE_ENTRY(table, (address >> shift) & 511);
250 if (!(*entry & PAGE_PRESENT)) {
251 assert(m_UsedSpares < m_SpareCount);
252 *entry = m_Spares.get()[m_UsedSpares++] | PAGE_PRESENT | PAGE_WRITE | PAGE_USER;
254 table = PAGE_GET_PHYSICAL_ADDRESS(entry);
256 return TABLE_ENTRY(table, (address >> 12) & 511);
266 size_t m_SpareCount, m_UsedSpares;
273 const auto validRange = [
this](uintptr_t base,
size_t length) {
274 if (!length || (base % PageSize) || (length % PageSize) || length > ~uintptr_t{0} - base) {
277 const uintptr_t end = base + length;
281 if (
m_bKernelSpace || !validRange(request.source, request.oldLength) ||
282 !validRange(request.destination, request.newLength) ||
283 (request.victimCount && !request.victims)) {
284 return RemapStatus::InvalidRange;
286 if (request.oldLength / PageSize > MaximumRemapPages ||
287 request.newLength / PageSize > MaximumRemapPages || request.victimCount > MaximumRemapPages) {
288 return RemapStatus::NoMemory;
290 if (request.source != request.destination &&
291 request.source < request.destination + request.newLength &&
292 request.destination < request.source + request.oldLength) {
293 return RemapStatus::InvalidRange;
296 __atomic_exchange_n(&m_RemapPreparationFailure, ssize_t{-1}, __ATOMIC_ACQ_REL);
297 if (!permitAllocation(remaining)) {
298 return RemapStatus::NoMemory;
302 return RemapStatus::NoMemory;
305 const auto status = prepared->prepare(remaining);
306 if (status == RemapStatus::Success) {
307 plan = pedigree_std::move(owner);