diff options
| author | 2019-07-07 09:42:54 -0700 | |
|---|---|---|
| committer | 2019-07-07 11:45:53 -0700 | |
| commit | 13a8fde3ad2a4a37cf1bb8dcb367b4c8fc8b4d9b (patch) | |
| tree | 5baf26505ec000e221c1119ba4dd2d0bca93de0e /src/core/hle/kernel/svc.cpp | |
| parent | Merge pull request #2674 from lioncash/reporter (diff) | |
| download | yuzu-13a8fde3ad2a4a37cf1bb8dcb367b4c8fc8b4d9b.tar.gz yuzu-13a8fde3ad2a4a37cf1bb8dcb367b4c8fc8b4d9b.tar.xz yuzu-13a8fde3ad2a4a37cf1bb8dcb367b4c8fc8b4d9b.zip | |
Implement MapPhysicalMemory/UnmapPhysicalMemory
This implements svcMapPhysicalMemory/svcUnmapPhysicalMemory for Yuzu,
which can be used to map memory at a desired address by games since
3.0.0.
It also properly parses SystemResourceSize from NPDM, and makes
information available via svcGetInfo.
This is needed for games like Super Smash Bros. and Diablo 3 -- this
PR's implementation does not run into the "ASCII reads" issue mentioned
in the comments of #2626, which was caused by the following bugs in
Yuzu's memory management that this PR also addresses:
* Yuzu's memory coalescing does not properly merge blocks. This results
in a polluted address space/svcQueryMemory results that would be
impossible to replicate on hardware, which can lead to game code making
the wrong assumptions about memory layout.
* This implements better merging for AllocatedMemoryBlocks.
* Yuzu's implementation of svcMirrorMemory unprotected the entire
virtual memory range containing the range being mirrored. This could
lead to games attempting to map data at that unprotected
range/attempting to access that range after yuzu improperly unmapped
it.
* This PR fixes it by simply calling ReprotectRange instead of
Reprotect.
Diffstat (limited to 'src/core/hle/kernel/svc.cpp')
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 110 |
1 files changed, 102 insertions, 8 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 332573a95..abb374892 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -729,8 +729,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha | |||
| 729 | StackRegionBaseAddr = 14, | 729 | StackRegionBaseAddr = 14, |
| 730 | StackRegionSize = 15, | 730 | StackRegionSize = 15, |
| 731 | // 3.0.0+ | 731 | // 3.0.0+ |
| 732 | IsVirtualAddressMemoryEnabled = 16, | 732 | SystemResourceSize = 16, |
| 733 | PersonalMmHeapUsage = 17, | 733 | SystemResourceUsage = 17, |
| 734 | TitleId = 18, | 734 | TitleId = 18, |
| 735 | // 4.0.0+ | 735 | // 4.0.0+ |
| 736 | PrivilegedProcessId = 19, | 736 | PrivilegedProcessId = 19, |
| @@ -756,8 +756,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha | |||
| 756 | case GetInfoType::StackRegionSize: | 756 | case GetInfoType::StackRegionSize: |
| 757 | case GetInfoType::TotalPhysicalMemoryAvailable: | 757 | case GetInfoType::TotalPhysicalMemoryAvailable: |
| 758 | case GetInfoType::TotalPhysicalMemoryUsed: | 758 | case GetInfoType::TotalPhysicalMemoryUsed: |
| 759 | case GetInfoType::IsVirtualAddressMemoryEnabled: | 759 | case GetInfoType::SystemResourceSize: |
| 760 | case GetInfoType::PersonalMmHeapUsage: | 760 | case GetInfoType::SystemResourceUsage: |
| 761 | case GetInfoType::TitleId: | 761 | case GetInfoType::TitleId: |
| 762 | case GetInfoType::UserExceptionContextAddr: | 762 | case GetInfoType::UserExceptionContextAddr: |
| 763 | case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap: | 763 | case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap: |
| @@ -822,8 +822,22 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha | |||
| 822 | *result = process->GetTotalPhysicalMemoryUsed(); | 822 | *result = process->GetTotalPhysicalMemoryUsed(); |
| 823 | return RESULT_SUCCESS; | 823 | return RESULT_SUCCESS; |
| 824 | 824 | ||
| 825 | case GetInfoType::IsVirtualAddressMemoryEnabled: | 825 | case GetInfoType::SystemResourceSize: |
| 826 | *result = process->IsVirtualMemoryEnabled(); | 826 | *result = process->GetSystemResourceSize(); |
| 827 | return RESULT_SUCCESS; | ||
| 828 | |||
| 829 | case GetInfoType::SystemResourceUsage: | ||
| 830 | // On hardware, this returns the amount of system resource memory that has | ||
| 831 | // been used by the kernel. This is problematic for Yuzu to emulate, because | ||
| 832 | // system resource memory is used for page tables -- and yuzu doesn't really | ||
| 833 | // have a way to calculate how much memory is required for page tables for | ||
| 834 | // the current process at any given time. | ||
| 835 | // TODO: Is this even worth implementing? No game should ever use it, since | ||
| 836 | // the amount of remaining page table space should never be relevant except | ||
| 837 | // for diagnostics. Is returning a value other than zero wise? | ||
| 838 | LOG_WARNING(Kernel_SVC, | ||
| 839 | "(STUBBED) Attempted to query system resource usage, returned 0"); | ||
| 840 | *result = 0; | ||
| 827 | return RESULT_SUCCESS; | 841 | return RESULT_SUCCESS; |
| 828 | 842 | ||
| 829 | case GetInfoType::TitleId: | 843 | case GetInfoType::TitleId: |
| @@ -946,6 +960,86 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha | |||
| 946 | } | 960 | } |
| 947 | } | 961 | } |
| 948 | 962 | ||
| 963 | /// Maps memory at a desired address | ||
| 964 | static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | ||
| 965 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | ||
| 966 | |||
| 967 | if (!Common::Is4KBAligned(addr)) { | ||
| 968 | LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); | ||
| 969 | return ERR_INVALID_ADDRESS; | ||
| 970 | } | ||
| 971 | |||
| 972 | if (!Common::Is4KBAligned(size)) { | ||
| 973 | LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); | ||
| 974 | return ERR_INVALID_SIZE; | ||
| 975 | } | ||
| 976 | |||
| 977 | if (size == 0) { | ||
| 978 | LOG_ERROR(Kernel_SVC, "Size is zero"); | ||
| 979 | return ERR_INVALID_SIZE; | ||
| 980 | } | ||
| 981 | |||
| 982 | if (!(addr < addr + size)) { | ||
| 983 | LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); | ||
| 984 | return ERR_INVALID_MEMORY_RANGE; | ||
| 985 | } | ||
| 986 | |||
| 987 | auto* const current_process = Core::CurrentProcess(); | ||
| 988 | auto& vm_manager = current_process->VMManager(); | ||
| 989 | |||
| 990 | if (current_process->GetSystemResourceSize() == 0) { | ||
| 991 | LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); | ||
| 992 | return ERR_INVALID_STATE; | ||
| 993 | } | ||
| 994 | |||
| 995 | if (!vm_manager.IsWithinMapRegion(addr, size)) { | ||
| 996 | LOG_ERROR(Kernel_SVC, "Range not within map region"); | ||
| 997 | return ERR_INVALID_MEMORY_RANGE; | ||
| 998 | } | ||
| 999 | |||
| 1000 | return vm_manager.MapPhysicalMemory(addr, size); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | /// Unmaps memory previously mapped via MapPhysicalMemory | ||
| 1004 | static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | ||
| 1005 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | ||
| 1006 | |||
| 1007 | if (!Common::Is4KBAligned(addr)) { | ||
| 1008 | LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); | ||
| 1009 | return ERR_INVALID_ADDRESS; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | if (!Common::Is4KBAligned(size)) { | ||
| 1013 | LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); | ||
| 1014 | return ERR_INVALID_SIZE; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | if (size == 0) { | ||
| 1018 | LOG_ERROR(Kernel_SVC, "Size is zero"); | ||
| 1019 | return ERR_INVALID_SIZE; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | if (!(addr < addr + size)) { | ||
| 1023 | LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); | ||
| 1024 | return ERR_INVALID_MEMORY_RANGE; | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | auto* const current_process = Core::CurrentProcess(); | ||
| 1028 | auto& vm_manager = current_process->VMManager(); | ||
| 1029 | |||
| 1030 | if (current_process->GetSystemResourceSize() == 0) { | ||
| 1031 | LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); | ||
| 1032 | return ERR_INVALID_STATE; | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | if (!vm_manager.IsWithinMapRegion(addr, size)) { | ||
| 1036 | LOG_ERROR(Kernel_SVC, "Range not within map region"); | ||
| 1037 | return ERR_INVALID_MEMORY_RANGE; | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | return vm_manager.UnmapPhysicalMemory(addr, size); | ||
| 1041 | } | ||
| 1042 | |||
| 949 | /// Sets the thread activity | 1043 | /// Sets the thread activity |
| 950 | static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { | 1044 | static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { |
| 951 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); | 1045 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); |
| @@ -2303,8 +2397,8 @@ static const FunctionDef SVC_Table[] = { | |||
| 2303 | {0x29, SvcWrap<GetInfo>, "GetInfo"}, | 2397 | {0x29, SvcWrap<GetInfo>, "GetInfo"}, |
| 2304 | {0x2A, nullptr, "FlushEntireDataCache"}, | 2398 | {0x2A, nullptr, "FlushEntireDataCache"}, |
| 2305 | {0x2B, nullptr, "FlushDataCache"}, | 2399 | {0x2B, nullptr, "FlushDataCache"}, |
| 2306 | {0x2C, nullptr, "MapPhysicalMemory"}, | 2400 | {0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"}, |
| 2307 | {0x2D, nullptr, "UnmapPhysicalMemory"}, | 2401 | {0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"}, |
| 2308 | {0x2E, nullptr, "GetFutureThreadInfo"}, | 2402 | {0x2E, nullptr, "GetFutureThreadInfo"}, |
| 2309 | {0x2F, nullptr, "GetLastThreadInfo"}, | 2403 | {0x2F, nullptr, "GetLastThreadInfo"}, |
| 2310 | {0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"}, | 2404 | {0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"}, |