diff options
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 70 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc_wrap.h | 7 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 29 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.h | 26 |
4 files changed, 131 insertions, 1 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index e5d4d6b55..0aa2e358e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -1189,6 +1189,74 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address, | |||
| 1189 | query_address); | 1189 | query_address); |
| 1190 | } | 1190 | } |
| 1191 | 1191 | ||
| 1192 | static 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 | |||
| 1192 | /// Exits the current process | 1260 | /// Exits the current process |
| 1193 | static void ExitProcess(Core::System& system) { | 1261 | static void ExitProcess(Core::System& system) { |
| 1194 | auto* current_process = system.Kernel().CurrentProcess(); | 1262 | auto* current_process = system.Kernel().CurrentProcess(); |
| @@ -2217,7 +2285,7 @@ static const FunctionDef SVC_Table[] = { | |||
| 2217 | {0x74, nullptr, "MapProcessMemory"}, | 2285 | {0x74, nullptr, "MapProcessMemory"}, |
| 2218 | {0x75, nullptr, "UnmapProcessMemory"}, | 2286 | {0x75, nullptr, "UnmapProcessMemory"}, |
| 2219 | {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, | 2287 | {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, |
| 2220 | {0x77, nullptr, "MapProcessCodeMemory"}, | 2288 | {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"}, |
| 2221 | {0x78, nullptr, "UnmapProcessCodeMemory"}, | 2289 | {0x78, nullptr, "UnmapProcessCodeMemory"}, |
| 2222 | {0x79, nullptr, "CreateProcess"}, | 2290 | {0x79, nullptr, "CreateProcess"}, |
| 2223 | {0x7A, nullptr, "StartProcess"}, | 2291 | {0x7A, nullptr, "StartProcess"}, |
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 | ||
| 47 | template <ResultCode func(Core::System&, u32, u64, u64, u64)> | ||
| 48 | void 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 | |||
| 47 | template <ResultCode func(Core::System&, u32*)> | 54 | template <ResultCode func(Core::System&, u32*)> |
| 48 | void SvcWrap(Core::System& system) { | 55 | void 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..76b491c47 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -302,6 +302,35 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) { | |||
| 302 | return MakeResult<VAddr>(heap_region_base); | 302 | return MakeResult<VAddr>(heap_region_base); |
| 303 | } | 303 | } |
| 304 | 304 | ||
| 305 | ResultCode 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 | |||
| 305 | MemoryInfo VMManager::QueryMemory(VAddr address) const { | 334 | MemoryInfo VMManager::QueryMemory(VAddr address) const { |
| 306 | const auto vma = FindVMA(address); | 335 | const auto vma = FindVMA(address); |
| 307 | MemoryInfo memory_info{}; | 336 | MemoryInfo memory_info{}; |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 6f484b7bf..27120d3b1 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 | ||
| 48 | constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { | 51 | constexpr 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,26 @@ 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 | |||
| 418 | /// Queries the memory manager for information about the given address. | 444 | /// Queries the memory manager for information about the given address. |
| 419 | /// | 445 | /// |
| 420 | /// @param address The address to query the memory manager about for information. | 446 | /// @param address The address to query the memory manager about for information. |