summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar bunnei2019-04-15 21:43:56 -0400
committerGravatar GitHub2019-04-15 21:43:56 -0400
commitfc64156533488e73b814c00bb42b5b6079ac8fa8 (patch)
tree36bf55884fdcd294f5318c44931e755fb23d6894 /src/core/hle/kernel
parentMerge pull request #2398 from lioncash/boost (diff)
parentkernel/svc: Implement svcUnmapProcessCodeMemory (diff)
downloadyuzu-fc64156533488e73b814c00bb42b5b6079ac8fa8.tar.gz
yuzu-fc64156533488e73b814c00bb42b5b6079ac8fa8.tar.xz
yuzu-fc64156533488e73b814c00bb42b5b6079ac8fa8.zip
Merge pull request #2393 from lioncash/svc
kernel/svc: Implement svcMapProcessCodeMemory/svcUnmapProcessCodeMemory
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/svc.cpp140
-rw-r--r--src/core/hle/kernel/svc_wrap.h7
-rw-r--r--src/core/hle/kernel/vm_manager.cpp80
-rw-r--r--src/core/hle/kernel/vm_manager.h49
4 files changed, 274 insertions, 2 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index e5d4d6b55..d48a2203a 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1189,6 +1189,142 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
1189 query_address); 1189 query_address);
1190} 1190}
1191 1191
1192static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1193 u64 src_address, u64 size) {
1194 LOG_DEBUG(Kernel_SVC,
1195 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
1196 "src_address=0x{:016X}, size=0x{:016X}",
1197 process_handle, dst_address, src_address, size);
1198
1199 if (!Common::Is4KBAligned(src_address)) {
1200 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1201 src_address);
1202 return ERR_INVALID_ADDRESS;
1203 }
1204
1205 if (!Common::Is4KBAligned(dst_address)) {
1206 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1207 dst_address);
1208 return ERR_INVALID_ADDRESS;
1209 }
1210
1211 if (size == 0 || !Common::Is4KBAligned(size)) {
1212 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
1213 return ERR_INVALID_SIZE;
1214 }
1215
1216 if (!IsValidAddressRange(dst_address, size)) {
1217 LOG_ERROR(Kernel_SVC,
1218 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1219 "size=0x{:016X}).",
1220 dst_address, size);
1221 return ERR_INVALID_ADDRESS_STATE;
1222 }
1223
1224 if (!IsValidAddressRange(src_address, size)) {
1225 LOG_ERROR(Kernel_SVC,
1226 "Source address range overflows the address space (src_address=0x{:016X}, "
1227 "size=0x{:016X}).",
1228 src_address, size);
1229 return ERR_INVALID_ADDRESS_STATE;
1230 }
1231
1232 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1233 auto process = handle_table.Get<Process>(process_handle);
1234 if (!process) {
1235 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1236 process_handle);
1237 return ERR_INVALID_HANDLE;
1238 }
1239
1240 auto& vm_manager = process->VMManager();
1241 if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
1242 LOG_ERROR(Kernel_SVC,
1243 "Source address range is not within the address space (src_address=0x{:016X}, "
1244 "size=0x{:016X}).",
1245 src_address, size);
1246 return ERR_INVALID_ADDRESS_STATE;
1247 }
1248
1249 if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
1250 LOG_ERROR(Kernel_SVC,
1251 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1252 "size=0x{:016X}).",
1253 dst_address, size);
1254 return ERR_INVALID_MEMORY_RANGE;
1255 }
1256
1257 return vm_manager.MapCodeMemory(dst_address, src_address, size);
1258}
1259
1260ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1261 u64 src_address, u64 size) {
1262 LOG_DEBUG(Kernel_SVC,
1263 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
1264 "size=0x{:016X}",
1265 process_handle, dst_address, src_address, size);
1266
1267 if (!Common::Is4KBAligned(dst_address)) {
1268 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1269 dst_address);
1270 return ERR_INVALID_ADDRESS;
1271 }
1272
1273 if (!Common::Is4KBAligned(src_address)) {
1274 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1275 src_address);
1276 return ERR_INVALID_ADDRESS;
1277 }
1278
1279 if (size == 0 || Common::Is4KBAligned(size)) {
1280 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1281 return ERR_INVALID_SIZE;
1282 }
1283
1284 if (!IsValidAddressRange(dst_address, size)) {
1285 LOG_ERROR(Kernel_SVC,
1286 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1287 "size=0x{:016X}).",
1288 dst_address, size);
1289 return ERR_INVALID_ADDRESS_STATE;
1290 }
1291
1292 if (!IsValidAddressRange(src_address, size)) {
1293 LOG_ERROR(Kernel_SVC,
1294 "Source address range overflows the address space (src_address=0x{:016X}, "
1295 "size=0x{:016X}).",
1296 src_address, size);
1297 return ERR_INVALID_ADDRESS_STATE;
1298 }
1299
1300 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1301 auto process = handle_table.Get<Process>(process_handle);
1302 if (!process) {
1303 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1304 process_handle);
1305 return ERR_INVALID_HANDLE;
1306 }
1307
1308 auto& vm_manager = process->VMManager();
1309 if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
1310 LOG_ERROR(Kernel_SVC,
1311 "Source address range is not within the address space (src_address=0x{:016X}, "
1312 "size=0x{:016X}).",
1313 src_address, size);
1314 return ERR_INVALID_ADDRESS_STATE;
1315 }
1316
1317 if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
1318 LOG_ERROR(Kernel_SVC,
1319 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1320 "size=0x{:016X}).",
1321 dst_address, size);
1322 return ERR_INVALID_MEMORY_RANGE;
1323 }
1324
1325 return vm_manager.UnmapCodeMemory(dst_address, src_address, size);
1326}
1327
1192/// Exits the current process 1328/// Exits the current process
1193static void ExitProcess(Core::System& system) { 1329static void ExitProcess(Core::System& system) {
1194 auto* current_process = system.Kernel().CurrentProcess(); 1330 auto* current_process = system.Kernel().CurrentProcess();
@@ -2217,8 +2353,8 @@ static const FunctionDef SVC_Table[] = {
2217 {0x74, nullptr, "MapProcessMemory"}, 2353 {0x74, nullptr, "MapProcessMemory"},
2218 {0x75, nullptr, "UnmapProcessMemory"}, 2354 {0x75, nullptr, "UnmapProcessMemory"},
2219 {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, 2355 {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
2220 {0x77, nullptr, "MapProcessCodeMemory"}, 2356 {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"},
2221 {0x78, nullptr, "UnmapProcessCodeMemory"}, 2357 {0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
2222 {0x79, nullptr, "CreateProcess"}, 2358 {0x79, nullptr, "CreateProcess"},
2223 {0x7A, nullptr, "StartProcess"}, 2359 {0x7A, nullptr, "StartProcess"},
2224 {0x7B, nullptr, "TerminateProcess"}, 2360 {0x7B, nullptr, "TerminateProcess"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index b3690b5f3..865473c6f 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -44,6 +44,13 @@ void SvcWrap(Core::System& system) {
44 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); 44 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
45} 45}
46 46
47template <ResultCode func(Core::System&, u32, u64, u64, u64)>
48void SvcWrap(Core::System& system) {
49 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
50 Param(system, 2), Param(system, 3))
51 .raw);
52}
53
47template <ResultCode func(Core::System&, u32*)> 54template <ResultCode func(Core::System&, u32*)>
48void SvcWrap(Core::System& system) { 55void SvcWrap(Core::System& system) {
49 u32 param = 0; 56 u32 param = 0;
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index ec0a480ce..f0c0c12fc 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -302,6 +302,86 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
302 return MakeResult<VAddr>(heap_region_base); 302 return MakeResult<VAddr>(heap_region_base);
303} 303}
304 304
305ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
306 constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
307 const auto src_check_result = CheckRangeState(
308 src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All,
309 VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
310
311 if (src_check_result.Failed()) {
312 return src_check_result.Code();
313 }
314
315 const auto mirror_result =
316 MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode);
317 if (mirror_result.IsError()) {
318 return mirror_result;
319 }
320
321 // Ensure we lock the source memory region.
322 const auto src_vma_result = CarveVMARange(src_address, size);
323 if (src_vma_result.Failed()) {
324 return src_vma_result.Code();
325 }
326 auto src_vma_iter = *src_vma_result;
327 src_vma_iter->second.attribute = MemoryAttribute::Locked;
328 Reprotect(src_vma_iter, VMAPermission::Read);
329
330 // The destination memory region is fine as is, however we need to make it read-only.
331 return ReprotectRange(dst_address, size, VMAPermission::Read);
332}
333
334ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
335 constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
336 const auto src_check_result = CheckRangeState(
337 src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None,
338 VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute);
339
340 if (src_check_result.Failed()) {
341 return src_check_result.Code();
342 }
343
344 // Yes, the kernel only checks the first page of the region.
345 const auto dst_check_result =
346 CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule,
347 MemoryState::FlagModule, VMAPermission::None, VMAPermission::None,
348 MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
349
350 if (dst_check_result.Failed()) {
351 return dst_check_result.Code();
352 }
353
354 const auto dst_memory_state = std::get<MemoryState>(*dst_check_result);
355 const auto dst_contiguous_check_result = CheckRangeState(
356 dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None,
357 VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
358
359 if (dst_contiguous_check_result.Failed()) {
360 return dst_contiguous_check_result.Code();
361 }
362
363 const auto unmap_result = UnmapRange(dst_address, size);
364 if (unmap_result.IsError()) {
365 return unmap_result;
366 }
367
368 // With the mirrored portion unmapped, restore the original region's traits.
369 const auto src_vma_result = CarveVMARange(src_address, size);
370 if (src_vma_result.Failed()) {
371 return src_vma_result.Code();
372 }
373 auto src_vma_iter = *src_vma_result;
374 src_vma_iter->second.state = MemoryState::Heap;
375 src_vma_iter->second.attribute = MemoryAttribute::None;
376 Reprotect(src_vma_iter, VMAPermission::ReadWrite);
377
378 if (dst_memory_state == MemoryState::ModuleCode) {
379 Core::System::GetInstance().InvalidateCpuInstructionCaches();
380 }
381
382 return unmap_result;
383}
384
305MemoryInfo VMManager::QueryMemory(VAddr address) const { 385MemoryInfo VMManager::QueryMemory(VAddr address) const {
306 const auto vma = FindVMA(address); 386 const auto vma = FindVMA(address);
307 MemoryInfo memory_info{}; 387 MemoryInfo memory_info{};
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 6f484b7bf..288eb9450 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -43,6 +43,9 @@ enum class VMAPermission : u8 {
43 ReadExecute = Read | Execute, 43 ReadExecute = Read | Execute,
44 WriteExecute = Write | Execute, 44 WriteExecute = Write | Execute,
45 ReadWriteExecute = Read | Write | Execute, 45 ReadWriteExecute = Read | Write | Execute,
46
47 // Used as a wildcard when checking permissions across memory ranges
48 All = 0xFF,
46}; 49};
47 50
48constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { 51constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) {
@@ -152,6 +155,9 @@ enum class MemoryState : u32 {
152 FlagUncached = 1U << 24, 155 FlagUncached = 1U << 24,
153 FlagCodeMemory = 1U << 25, 156 FlagCodeMemory = 1U << 25,
154 157
158 // Wildcard used in range checking to indicate all states.
159 All = 0xFFFFFFFF,
160
155 // Convenience flag sets to reduce repetition 161 // Convenience flag sets to reduce repetition
156 IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, 162 IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1,
157 163
@@ -415,6 +421,49 @@ public:
415 /// 421 ///
416 ResultVal<VAddr> SetHeapSize(u64 size); 422 ResultVal<VAddr> SetHeapSize(u64 size);
417 423
424 /// Maps a region of memory as code memory.
425 ///
426 /// @param dst_address The base address of the region to create the aliasing memory region.
427 /// @param src_address The base address of the region to be aliased.
428 /// @param size The total amount of memory to map in bytes.
429 ///
430 /// @pre Both memory regions lie within the actual addressable address space.
431 ///
432 /// @post After this function finishes execution, assuming success, then the address range
433 /// [dst_address, dst_address+size) will alias the memory region,
434 /// [src_address, src_address+size).
435 /// <p>
436 /// What this also entails is as follows:
437 /// 1. The aliased region gains the Locked memory attribute.
438 /// 2. The aliased region becomes read-only.
439 /// 3. The aliasing region becomes read-only.
440 /// 4. The aliasing region is created with a memory state of MemoryState::CodeModule.
441 ///
442 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
443
444 /// Unmaps a region of memory designated as code module memory.
445 ///
446 /// @param dst_address The base address of the memory region aliasing the source memory region.
447 /// @param src_address The base address of the memory region being aliased.
448 /// @param size The size of the memory region to unmap in bytes.
449 ///
450 /// @pre Both memory ranges lie within the actual addressable address space.
451 ///
452 /// @pre The memory region being unmapped has been previously been mapped
453 /// by a call to MapCodeMemory.
454 ///
455 /// @post After execution of the function, if successful. the aliasing memory region
456 /// will be unmapped and the aliased region will have various traits about it
457 /// restored to what they were prior to the original mapping call preceding
458 /// this function call.
459 /// <p>
460 /// What this also entails is as follows:
461 /// 1. The state of the memory region will now indicate a general heap region.
462 /// 2. All memory attributes for the memory region are cleared.
463 /// 3. Memory permissions for the region are restored to user read/write.
464 ///
465 ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
466
418 /// Queries the memory manager for information about the given address. 467 /// Queries the memory manager for information about the given address.
419 /// 468 ///
420 /// @param address The address to query the memory manager about for information. 469 /// @param address The address to query the memory manager about for information.