The Pedigree Project 0.1
VirtualAddressSpace.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/Log.h"
21#include "pedigree/kernel/processor/MemoryRegion.h"
22#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
23#include "pedigree/kernel/processor/VirtualAddressSpace.h"
24#include "pedigree/kernel/utilities/utility.h"
25
26physical_uintptr_t VirtualAddressSpace::m_ZeroPage = 0;
27
28#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
29VirtualAddressSpace::CopyOnWritePreCommitHook VirtualAddressSpace::m_CopyOnWritePreCommitHook =
30 nullptr;
31
32void VirtualAddressSpace::setCopyOnWritePreCommitHookForTest(CopyOnWritePreCommitHook hook) {
33 __atomic_store_n(&m_CopyOnWritePreCommitHook, hook, __ATOMIC_RELEASE);
34}
35
36void VirtualAddressSpace::copyOnWritePreCommitForTest(void* virtualAddress) {
37 CopyOnWritePreCommitHook hook = __atomic_load_n(&m_CopyOnWritePreCommitHook, __ATOMIC_ACQUIRE);
38 if (hook) {
39 hook(virtualAddress);
40 }
41}
42#endif
43
44bool VirtualAddressSpace::admitRawMemoryChange(const PreparedMemoryLock& plan, bool privileged,
45 MemoryLockCharge& charge) const {
46 MemoryLockAccount* account = memoryLockAccount();
47 charge = account ? account->charge() : MemoryLockCharge{};
48 if (!account)
49 return true;
50 if (plan.removedPages() > charge.rawPages)
51 return false;
52 const size_t retained = charge.rawPages - plan.removedPages();
53 if (plan.addedPages() > ~size_t(0) - retained)
54 return false;
55 const size_t next = retained + plan.addedPages();
56 if (charge.managedPages > ~size_t(0) - next)
57 return false;
58 if (next > charge.rawPages && !account->permitsTotalPages(charge.managedPages + next, privileged))
59 return false;
60 charge.rawPages = next;
61 return true;
62}
63
64void VirtualAddressSpace::commitRawMemoryChange(PreparedMemoryLock& plan, MemoryLockCharge charge) {
65 plan.commit();
66 if (MemoryLockAccount* account = memoryLockAccount())
67 account->publish(charge, account->futureMode());
68}
69
70bool VirtualAddressSpace::prepareZeroPage() {
71 if (__atomic_load_n(&m_ZeroPage, __ATOMIC_ACQUIRE))
72 return true;
74 MemoryRegion candidate("User zero page");
75 if (!manager.allocateRegion(candidate, 1, 0, KernelMode | Write))
76 return false;
77 ByteSet(candidate.virtualAddress(), 0, PhysicalMemoryManager::getPageSize());
78 physical_uintptr_t physical = 0;
79 size_t flags = 0;
80 getKernelAddressSpace().getMapping(candidate.virtualAddress(), physical, flags);
81 // The first pin represents the region; the second survives its destruction.
82 manager.pin(physical);
83 manager.pin(physical);
84 physical_uintptr_t expected = 0;
85 if (!__atomic_compare_exchange_n(&m_ZeroPage, &expected, physical, false, __ATOMIC_RELEASE,
86 __ATOMIC_ACQUIRE))
87 manager.freePage(physical);
88 return true;
89}
90
91void* VirtualAddressSpace::expandHeap(ssize_t incr, size_t flags) {
92 UserMemoryOperation operation(*this);
93 const uintptr_t oldEnd = reinterpret_cast<uintptr_t>(m_HeapEnd);
94 const uintptr_t start = reinterpret_cast<uintptr_t>(m_Heap);
95 const size_t page = PhysicalMemoryManager::getPageSize();
96 if (!incr)
97 return m_HeapEnd;
98 uintptr_t newEnd = oldEnd;
99 if (incr > 0) {
100 if (static_cast<uintptr_t>(incr) > ~uintptr_t(0) - oldEnd)
101 return nullptr;
102 newEnd += static_cast<uintptr_t>(incr);
103 } else {
104 const uintptr_t amount = static_cast<uintptr_t>(-(incr + 1)) + 1;
105 if (amount > oldEnd - start)
106 return nullptr;
107 newEnd -= amount;
108 }
109 const uintptr_t limit = getDynamicStart() ? getDynamicStart() : getKernelStart();
110 if (newEnd < start || newEnd >= limit || newEnd > ~uintptr_t(0) - (page - 1))
111 return nullptr;
112 const uintptr_t oldPagesEnd = (oldEnd + page - 1) & ~(page - 1);
113 const uintptr_t newPagesEnd = (newEnd + page - 1) & ~(page - 1);
114 if (oldPagesEnd == newPagesEnd) {
115 m_HeapEnd = reinterpret_cast<void*>(newEnd);
116 return reinterpret_cast<void*>(oldEnd);
117 }
118 if (!m_HeapRegionId)
119 m_HeapRegionId = rawUserMemory().nextRegionId();
120 if (!m_HeapRegionId)
121 return nullptr;
122 UserRegion previous{m_HeapRegionId, start, oldPagesEnd - start, UserRegion::Kind::Heap, true};
123 UserRegion replacement{m_HeapRegionId, start, newPagesEnd - start, UserRegion::Kind::Heap, true};
125 if (rawUserMemory().prepareChange(&previous, &replacement, plan) != MemoryLockStatus::Success)
126 return nullptr;
127 MemoryLockCharge charge;
128 if (!admitRawMemoryChange(*plan.get(), operation.privileged(), charge))
129 return nullptr;
130 if (newPagesEnd > oldPagesEnd) {
131 if (!prepareZeroPage())
132 return nullptr;
133 uintptr_t address = oldPagesEnd;
134 for (; address < newPagesEnd; address += page) {
136 if (!map(m_ZeroPage, reinterpret_cast<void*>(address), (flags & ~Write) | CopyOnWrite)) {
138 rollbackHeapExpansion(reinterpret_cast<void*>(oldPagesEnd), (address - oldPagesEnd) / page);
139 return nullptr;
140 }
141 }
142 if (plan.get()->populate() != PopulationStatus::Success) {
143 rollbackHeapExpansion(reinterpret_cast<void*>(oldPagesEnd),
144 (newPagesEnd - oldPagesEnd) / page);
145 return nullptr;
146 }
147 }
148 commitRawMemoryChange(*plan.get(), charge);
149 m_HeapEnd = reinterpret_cast<void*>(newEnd);
150 return reinterpret_cast<void*>(oldEnd);
151}
152
153void VirtualAddressSpace::rollbackHeapExpansion(void* virtualAddress, size_t pageCount) {
154 for (size_t i = 0; i < pageCount; ++i) {
155 size_t flags = 0;
156 physical_uintptr_t physical = 0;
157 if (detachMapping(virtualAddress, physical, flags))
159 virtualAddress = adjust_pointer(virtualAddress, PhysicalMemoryManager::getPageSize());
160 }
161}
162
163bool VirtualAddressSpace::mapHuge(physical_uintptr_t physAddress, void* virtualAddress,
164 size_t count, size_t flags) {
165 for (size_t i = 0; i < count; ++i) {
166 size_t addend = PhysicalMemoryManager::getPageSize() * i;
167 if (!map(physAddress + addend, adjust_pointer(virtualAddress, addend), flags)) {
168 return false;
169 }
170 }
171
172 return true;
173}
Special memory entity in the kernel's virtual address space.
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
virtual void pin(physical_uintptr_t page)=0
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
virtual bool mapHuge(physical_uintptr_t physAddress, void *virtualAddress, size_t count, size_t flags)
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
virtual bool detachMapping(void *virtualAddress, physical_uintptr_t &physical, size_t &flags, size_t requiredFlags=0)
virtual uintptr_t getKernelStart() const =0
void rollbackHeapExpansion(void *virtualAddress, size_t pageCount)
virtual void * expandHeap(ssize_t incr, size_t flags)