summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/process.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/process.cpp')
-rw-r--r--src/core/hle/kernel/process.cpp152
1 files changed, 143 insertions, 9 deletions
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index a7892c652..124047a53 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -7,11 +7,11 @@
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/make_unique.h" 8#include "common/make_unique.h"
9 9
10#include "core/hle/kernel/memory.h"
10#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
11#include "core/hle/kernel/resource_limit.h" 12#include "core/hle/kernel/resource_limit.h"
12#include "core/hle/kernel/thread.h" 13#include "core/hle/kernel/thread.h"
13#include "core/hle/kernel/vm_manager.h" 14#include "core/hle/kernel/vm_manager.h"
14#include "core/mem_map.h"
15#include "core/memory.h" 15#include "core/memory.h"
16 16
17namespace Kernel { 17namespace Kernel {
@@ -36,8 +36,7 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) {
36 process->codeset = std::move(code_set); 36 process->codeset = std::move(code_set);
37 process->flags.raw = 0; 37 process->flags.raw = 0;
38 process->flags.memory_region = MemoryRegion::APPLICATION; 38 process->flags.memory_region = MemoryRegion::APPLICATION;
39 process->address_space = Common::make_unique<VMManager>(); 39 Memory::InitLegacyAddressSpace(process->vm_manager);
40 Memory::InitLegacyAddressSpace(*process->address_space);
41 40
42 return process; 41 return process;
43} 42}
@@ -93,9 +92,11 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
93 mapping.unk_flag = false; 92 mapping.unk_flag = false;
94 } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF 93 } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
95 // Kernel version 94 // Kernel version
96 int minor = descriptor & 0xFF; 95 kernel_version = descriptor & 0xFFFF;
97 int major = (descriptor >> 8) & 0xFF; 96
98 LOG_INFO(Loader, "ExHeader kernel version ignored: %d.%d", major, minor); 97 int minor = kernel_version & 0xFF;
98 int major = (kernel_version >> 8) & 0xFF;
99 LOG_INFO(Loader, "ExHeader kernel version: %d.%d", major, minor);
99 } else { 100 } else {
100 LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x%08X", descriptor); 101 LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x%08X", descriptor);
101 } 102 }
@@ -103,20 +104,153 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
103} 104}
104 105
105void Process::Run(s32 main_thread_priority, u32 stack_size) { 106void Process::Run(s32 main_thread_priority, u32 stack_size) {
107 memory_region = GetMemoryRegion(flags.memory_region);
108
106 auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { 109 auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) {
107 auto vma = address_space->MapMemoryBlock(segment.addr, codeset->memory, 110 auto vma = vm_manager.MapMemoryBlock(segment.addr, codeset->memory,
108 segment.offset, segment.size, memory_state).Unwrap(); 111 segment.offset, segment.size, memory_state).Unwrap();
109 address_space->Reprotect(vma, permissions); 112 vm_manager.Reprotect(vma, permissions);
113 misc_memory_used += segment.size;
110 }; 114 };
111 115
116 // Map CodeSet segments
112 MapSegment(codeset->code, VMAPermission::ReadExecute, MemoryState::Code); 117 MapSegment(codeset->code, VMAPermission::ReadExecute, MemoryState::Code);
113 MapSegment(codeset->rodata, VMAPermission::Read, MemoryState::Code); 118 MapSegment(codeset->rodata, VMAPermission::Read, MemoryState::Code);
114 MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private); 119 MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private);
115 120
116 address_space->LogLayout(); 121 // Allocate and map stack
122 vm_manager.MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size,
123 std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, MemoryState::Locked
124 ).Unwrap();
125 misc_memory_used += stack_size;
126
127 vm_manager.LogLayout(Log::Level::Debug);
117 Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); 128 Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority);
118} 129}
119 130
131VAddr Process::GetLinearHeapBase() const {
132 return (kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_SIZE)
133 + memory_region->base;
134}
135
136VAddr Process::GetLinearHeapLimit() const {
137 return GetLinearHeapBase() + memory_region->size;
138}
139
140ResultVal<VAddr> Process::HeapAllocate(VAddr target, u32 size, VMAPermission perms) {
141 if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || target + size < target) {
142 return ERR_INVALID_ADDRESS;
143 }
144
145 if (heap_memory == nullptr) {
146 // Initialize heap
147 heap_memory = std::make_shared<std::vector<u8>>();
148 heap_start = heap_end = target;
149 }
150
151 // If necessary, expand backing vector to cover new heap extents.
152 if (target < heap_start) {
153 heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
154 heap_start = target;
155 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
156 }
157 if (target + size > heap_end) {
158 heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
159 heap_end = target + size;
160 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
161 }
162 ASSERT(heap_end - heap_start == heap_memory->size());
163
164 CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start, size, MemoryState::Private));
165 vm_manager.Reprotect(vma, perms);
166
167 heap_used += size;
168
169 return MakeResult<VAddr>(heap_end - size);
170}
171
172ResultCode Process::HeapFree(VAddr target, u32 size) {
173 if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || target + size < target) {
174 return ERR_INVALID_ADDRESS;
175 }
176
177 ResultCode result = vm_manager.UnmapRange(target, size);
178 if (result.IsError()) return result;
179
180 heap_used -= size;
181
182 return RESULT_SUCCESS;
183}
184
185ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) {
186 auto& linheap_memory = memory_region->linear_heap_memory;
187
188 VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size();
189 // Games and homebrew only ever seem to pass 0 here (which lets the kernel decide the address),
190 // but explicit addresses are also accepted and respected.
191 if (target == 0) {
192 target = heap_end;
193 }
194
195 if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
196 target > heap_end || target + size < target) {
197
198 return ERR_INVALID_ADDRESS;
199 }
200
201 // Expansion of the linear heap is only allowed if you do an allocation immediatelly at its
202 // end. It's possible to free gaps in the middle of the heap and then reallocate them later,
203 // but expansions are only allowed at the end.
204 if (target == heap_end) {
205 linheap_memory->insert(linheap_memory->end(), size, 0);
206 vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
207 }
208
209 // TODO(yuriks): As is, this lets processes map memory allocated by other processes from the
210 // same region. It is unknown if or how the 3DS kernel checks against this.
211 size_t offset = target - GetLinearHeapBase();
212 CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, linheap_memory, offset, size, MemoryState::Continuous));
213 vm_manager.Reprotect(vma, perms);
214
215 linear_heap_used += size;
216
217 return MakeResult<VAddr>(target);
218}
219
220ResultCode Process::LinearFree(VAddr target, u32 size) {
221 auto& linheap_memory = memory_region->linear_heap_memory;
222
223 if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
224 target + size < target) {
225
226 return ERR_INVALID_ADDRESS;
227 }
228
229 VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size();
230 if (target + size > heap_end) {
231 return ERR_INVALID_ADDRESS_STATE;
232 }
233
234 ResultCode result = vm_manager.UnmapRange(target, size);
235 if (result.IsError()) return result;
236
237 linear_heap_used -= size;
238
239 if (target + size == heap_end) {
240 // End of linear heap has been freed, so check what's the last allocated block in it and
241 // reduce the size.
242 auto vma = vm_manager.FindVMA(target);
243 ASSERT(vma != vm_manager.vma_map.end());
244 ASSERT(vma->second.type == VMAType::Free);
245 VAddr new_end = vma->second.base;
246 if (new_end >= GetLinearHeapBase()) {
247 linheap_memory->resize(new_end - GetLinearHeapBase());
248 }
249 }
250
251 return RESULT_SUCCESS;
252}
253
120Kernel::Process::Process() {} 254Kernel::Process::Process() {}
121Kernel::Process::~Process() {} 255Kernel::Process::~Process() {}
122 256