diff options
| author | 2021-12-09 00:29:09 -0800 | |
|---|---|---|
| committer | 2021-12-09 00:29:09 -0800 | |
| commit | 46366c6dcaa47fe7bccb021c90b77d0d8be23b6f (patch) | |
| tree | 3267f43c67c15e88524cb9d6106726eb8e89ea9d /src/core/hle/kernel/svc.cpp | |
| parent | Merge pull request #7545 from Morph1984/qt-deprecated-warn (diff) | |
| parent | Update k_code_memory.h (diff) | |
| download | yuzu-46366c6dcaa47fe7bccb021c90b77d0d8be23b6f.tar.gz yuzu-46366c6dcaa47fe7bccb021c90b77d0d8be23b6f.tar.xz yuzu-46366c6dcaa47fe7bccb021c90b77d0d8be23b6f.zip | |
Merge pull request #7519 from itsmeft24/master
kernel: svc: Implement ProcessMemory and CodeMemory SVCs
Diffstat (limited to 'src/core/hle/kernel/svc.cpp')
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 214 |
1 files changed, 210 insertions, 4 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f0cd8471e..b37db918e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| 19 | #include "core/hle/kernel/k_client_port.h" | 19 | #include "core/hle/kernel/k_client_port.h" |
| 20 | #include "core/hle/kernel/k_client_session.h" | 20 | #include "core/hle/kernel/k_client_session.h" |
| 21 | #include "core/hle/kernel/k_code_memory.h" | ||
| 21 | #include "core/hle/kernel/k_event.h" | 22 | #include "core/hle/kernel/k_event.h" |
| 22 | #include "core/hle/kernel/k_handle_table.h" | 23 | #include "core/hle/kernel/k_handle_table.h" |
| 23 | #include "core/hle/kernel/k_memory_block.h" | 24 | #include "core/hle/kernel/k_memory_block.h" |
| @@ -1197,6 +1198,22 @@ constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { | |||
| 1197 | } | 1198 | } |
| 1198 | } | 1199 | } |
| 1199 | 1200 | ||
| 1201 | constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1202 | return perm == Svc::MemoryPermission::ReadWrite; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1206 | return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1210 | return perm == Svc::MemoryPermission::None; | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1214 | return perm == Svc::MemoryPermission::None; | ||
| 1215 | } | ||
| 1216 | |||
| 1200 | } // Anonymous namespace | 1217 | } // Anonymous namespace |
| 1201 | 1218 | ||
| 1202 | static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, | 1219 | static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, |
| @@ -1306,6 +1323,195 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces | |||
| 1306 | return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm)); | 1323 | return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm)); |
| 1307 | } | 1324 | } |
| 1308 | 1325 | ||
| 1326 | static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 1327 | VAddr src_address, u64 size) { | ||
| 1328 | LOG_TRACE(Kernel_SVC, | ||
| 1329 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 1330 | dst_address, process_handle, src_address, size); | ||
| 1331 | |||
| 1332 | // Validate the address/size. | ||
| 1333 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 1334 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 1335 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1336 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1337 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 1338 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 1339 | |||
| 1340 | // Get the processes. | ||
| 1341 | KProcess* dst_process = system.CurrentProcess(); | ||
| 1342 | KScopedAutoObject src_process = | ||
| 1343 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 1344 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 1345 | |||
| 1346 | // Get the page tables. | ||
| 1347 | auto& dst_pt = dst_process->PageTable(); | ||
| 1348 | auto& src_pt = src_process->PageTable(); | ||
| 1349 | |||
| 1350 | // Validate that the mapping is in range. | ||
| 1351 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 1352 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 1353 | ResultInvalidMemoryRegion); | ||
| 1354 | |||
| 1355 | // Create a new page group. | ||
| 1356 | KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); | ||
| 1357 | KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); | ||
| 1358 | |||
| 1359 | // Map the group. | ||
| 1360 | R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, | ||
| 1361 | KMemoryPermission::UserReadWrite)); | ||
| 1362 | |||
| 1363 | return ResultSuccess; | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 1367 | VAddr src_address, u64 size) { | ||
| 1368 | LOG_TRACE(Kernel_SVC, | ||
| 1369 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 1370 | dst_address, process_handle, src_address, size); | ||
| 1371 | |||
| 1372 | // Validate the address/size. | ||
| 1373 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 1374 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 1375 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1376 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1377 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 1378 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 1379 | |||
| 1380 | // Get the processes. | ||
| 1381 | KProcess* dst_process = system.CurrentProcess(); | ||
| 1382 | KScopedAutoObject src_process = | ||
| 1383 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 1384 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 1385 | |||
| 1386 | // Get the page tables. | ||
| 1387 | auto& dst_pt = dst_process->PageTable(); | ||
| 1388 | auto& src_pt = src_process->PageTable(); | ||
| 1389 | |||
| 1390 | // Validate that the mapping is in range. | ||
| 1391 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 1392 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 1393 | ResultInvalidMemoryRegion); | ||
| 1394 | |||
| 1395 | // Unmap the memory. | ||
| 1396 | R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); | ||
| 1397 | |||
| 1398 | return ResultSuccess; | ||
| 1399 | } | ||
| 1400 | |||
| 1401 | static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { | ||
| 1402 | LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}", | ||
| 1403 | static_cast<void*>(out), address, size); | ||
| 1404 | // Get kernel instance. | ||
| 1405 | auto& kernel = system.Kernel(); | ||
| 1406 | |||
| 1407 | // Validate address / size. | ||
| 1408 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1409 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1410 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1411 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1412 | |||
| 1413 | // Create the code memory. | ||
| 1414 | |||
| 1415 | KCodeMemory* code_mem = KCodeMemory::Create(kernel); | ||
| 1416 | R_UNLESS(code_mem != nullptr, ResultOutOfResource); | ||
| 1417 | |||
| 1418 | // Verify that the region is in range. | ||
| 1419 | R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size), | ||
| 1420 | ResultInvalidCurrentMemory); | ||
| 1421 | |||
| 1422 | // Initialize the code memory. | ||
| 1423 | R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); | ||
| 1424 | |||
| 1425 | // Register the code memory. | ||
| 1426 | KCodeMemory::Register(kernel, code_mem); | ||
| 1427 | |||
| 1428 | // Add the code memory to the handle table. | ||
| 1429 | R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem)); | ||
| 1430 | |||
| 1431 | code_mem->Close(); | ||
| 1432 | |||
| 1433 | return ResultSuccess; | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, | ||
| 1437 | VAddr address, size_t size, Svc::MemoryPermission perm) { | ||
| 1438 | |||
| 1439 | LOG_TRACE(Kernel_SVC, | ||
| 1440 | "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " | ||
| 1441 | "permission=0x{:X}", | ||
| 1442 | code_memory_handle, operation, address, size, perm); | ||
| 1443 | |||
| 1444 | // Validate the address / size. | ||
| 1445 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1446 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1447 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1448 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1449 | |||
| 1450 | // Get the code memory from its handle. | ||
| 1451 | KScopedAutoObject code_mem = | ||
| 1452 | system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle); | ||
| 1453 | R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); | ||
| 1454 | |||
| 1455 | // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. | ||
| 1456 | // This enables homebrew usage of these SVCs for JIT. | ||
| 1457 | |||
| 1458 | // Perform the operation. | ||
| 1459 | switch (static_cast<CodeMemoryOperation>(operation)) { | ||
| 1460 | case CodeMemoryOperation::Map: { | ||
| 1461 | // Check that the region is in range. | ||
| 1462 | R_UNLESS( | ||
| 1463 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 1464 | ResultInvalidMemoryRegion); | ||
| 1465 | |||
| 1466 | // Check the memory permission. | ||
| 1467 | R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1468 | |||
| 1469 | // Map the memory. | ||
| 1470 | R_TRY(code_mem->Map(address, size)); | ||
| 1471 | } break; | ||
| 1472 | case CodeMemoryOperation::Unmap: { | ||
| 1473 | // Check that the region is in range. | ||
| 1474 | R_UNLESS( | ||
| 1475 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 1476 | ResultInvalidMemoryRegion); | ||
| 1477 | |||
| 1478 | // Check the memory permission. | ||
| 1479 | R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1480 | |||
| 1481 | // Unmap the memory. | ||
| 1482 | R_TRY(code_mem->Unmap(address, size)); | ||
| 1483 | } break; | ||
| 1484 | case CodeMemoryOperation::MapToOwner: { | ||
| 1485 | // Check that the region is in range. | ||
| 1486 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 1487 | KMemoryState::GeneratedCode), | ||
| 1488 | ResultInvalidMemoryRegion); | ||
| 1489 | |||
| 1490 | // Check the memory permission. | ||
| 1491 | R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1492 | |||
| 1493 | // Map the memory to its owner. | ||
| 1494 | R_TRY(code_mem->MapToOwner(address, size, perm)); | ||
| 1495 | } break; | ||
| 1496 | case CodeMemoryOperation::UnmapFromOwner: { | ||
| 1497 | // Check that the region is in range. | ||
| 1498 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 1499 | KMemoryState::GeneratedCode), | ||
| 1500 | ResultInvalidMemoryRegion); | ||
| 1501 | |||
| 1502 | // Check the memory permission. | ||
| 1503 | R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1504 | |||
| 1505 | // Unmap the memory from its owner. | ||
| 1506 | R_TRY(code_mem->UnmapFromOwner(address, size)); | ||
| 1507 | } break; | ||
| 1508 | default: | ||
| 1509 | return ResultInvalidEnumValue; | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | return ResultSuccess; | ||
| 1513 | } | ||
| 1514 | |||
| 1309 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, | 1515 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, |
| 1310 | VAddr page_info_address, Handle process_handle, | 1516 | VAddr page_info_address, Handle process_handle, |
| 1311 | VAddr address) { | 1517 | VAddr address) { |
| @@ -2600,8 +2806,8 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2600 | {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, | 2806 | {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, |
| 2601 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, | 2807 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, |
| 2602 | {0x4A, nullptr, "SetUnsafeLimit"}, | 2808 | {0x4A, nullptr, "SetUnsafeLimit"}, |
| 2603 | {0x4B, nullptr, "CreateCodeMemory"}, | 2809 | {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"}, |
| 2604 | {0x4C, nullptr, "ControlCodeMemory"}, | 2810 | {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"}, |
| 2605 | {0x4D, nullptr, "SleepSystem"}, | 2811 | {0x4D, nullptr, "SleepSystem"}, |
| 2606 | {0x4E, nullptr, "ReadWriteRegister"}, | 2812 | {0x4E, nullptr, "ReadWriteRegister"}, |
| 2607 | {0x4F, nullptr, "SetProcessActivity"}, | 2813 | {0x4F, nullptr, "SetProcessActivity"}, |
| @@ -2641,8 +2847,8 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2641 | {0x71, nullptr, "ManageNamedPort"}, | 2847 | {0x71, nullptr, "ManageNamedPort"}, |
| 2642 | {0x72, nullptr, "ConnectToPort"}, | 2848 | {0x72, nullptr, "ConnectToPort"}, |
| 2643 | {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"}, | 2849 | {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"}, |
| 2644 | {0x74, nullptr, "MapProcessMemory"}, | 2850 | {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"}, |
| 2645 | {0x75, nullptr, "UnmapProcessMemory"}, | 2851 | {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"}, |
| 2646 | {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, | 2852 | {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, |
| 2647 | {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, | 2853 | {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, |
| 2648 | {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, | 2854 | {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, |