diff options
| author | 2022-03-07 20:28:39 -0800 | |
|---|---|---|
| committer | 2022-03-07 20:28:39 -0800 | |
| commit | 1f378960660a72ad0cf166c1efd08d4e956e07eb (patch) | |
| tree | d277eaeb0c384df0b6bad4868e23a7da859f1f0b /src | |
| parent | Merge pull request #7930 from asLody/dma-semaphore (diff) | |
| parent | hle: kernel: KPageTable: Improve implementations of MapCodeMemory and UnmapCo... (diff) | |
| download | yuzu-1f378960660a72ad0cf166c1efd08d4e956e07eb.tar.gz yuzu-1f378960660a72ad0cf166c1efd08d4e956e07eb.tar.xz yuzu-1f378960660a72ad0cf166c1efd08d4e956e07eb.zip | |
Merge pull request #7974 from bunnei/improve-code-mem
Kernel Memory Updates (Part 5): Revamp MapCodeMemory and UnmapCodeMemory.
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/kernel/k_page_table.cpp | 159 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_page_table.h | 4 |
2 files changed, 116 insertions, 47 deletions
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index dfea0b6e2..0602de1f7 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -285,72 +285,141 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory | |||
| 285 | return ResultSuccess; | 285 | return ResultSuccess; |
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { | 288 | ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { |
| 289 | // Validate the mapping request. | ||
| 290 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), | ||
| 291 | ResultInvalidMemoryRegion); | ||
| 292 | |||
| 293 | // Lock the table. | ||
| 289 | KScopedLightLock lk(general_lock); | 294 | KScopedLightLock lk(general_lock); |
| 290 | 295 | ||
| 291 | const std::size_t num_pages{size / PageSize}; | 296 | // Verify that the source memory is normal heap. |
| 297 | KMemoryState src_state{}; | ||
| 298 | KMemoryPermission src_perm{}; | ||
| 299 | std::size_t num_src_allocator_blocks{}; | ||
| 300 | R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks, | ||
| 301 | src_address, size, KMemoryState::All, KMemoryState::Normal, | ||
| 302 | KMemoryPermission::All, KMemoryPermission::UserReadWrite, | ||
| 303 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 292 | 304 | ||
| 293 | KMemoryState state{}; | 305 | // Verify that the destination memory is unmapped. |
| 294 | KMemoryPermission perm{}; | 306 | std::size_t num_dst_allocator_blocks{}; |
| 295 | CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size, | 307 | R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All, |
| 296 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, | 308 | KMemoryState::Free, KMemoryPermission::None, |
| 297 | KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, | 309 | KMemoryPermission::None, KMemoryAttribute::None, |
| 298 | KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); | 310 | KMemoryAttribute::None)); |
| 299 | 311 | ||
| 300 | if (IsRegionMapped(dst_addr, size)) { | 312 | // Map the code memory. |
| 301 | return ResultInvalidCurrentMemory; | 313 | { |
| 302 | } | 314 | // Determine the number of pages being operated on. |
| 315 | const std::size_t num_pages = size / PageSize; | ||
| 303 | 316 | ||
| 304 | KPageLinkedList page_linked_list; | 317 | // Create page groups for the memory being mapped. |
| 305 | AddRegionToPages(src_addr, num_pages, page_linked_list); | 318 | KPageLinkedList pg; |
| 319 | AddRegionToPages(src_address, num_pages, pg); | ||
| 306 | 320 | ||
| 307 | { | 321 | // Reprotect the source as kernel-read/not mapped. |
| 308 | auto block_guard = detail::ScopeExit( | 322 | const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | |
| 309 | [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); }); | 323 | KMemoryPermission::NotMapped); |
| 324 | R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions)); | ||
| 310 | 325 | ||
| 311 | CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, | 326 | // Ensure that we unprotect the source pages on failure. |
| 312 | OperationType::ChangePermissions)); | 327 | auto unprot_guard = SCOPE_GUARD({ |
| 313 | CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None)); | 328 | ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions) |
| 329 | .IsSuccess()); | ||
| 330 | }); | ||
| 314 | 331 | ||
| 315 | block_guard.Cancel(); | 332 | // Map the alias pages. |
| 316 | } | 333 | R_TRY(MapPages(dst_address, pg, new_perm)); |
| 317 | 334 | ||
| 318 | block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None, | 335 | // We successfully mapped the alias pages, so we don't need to unprotect the src pages on |
| 319 | KMemoryAttribute::Locked); | 336 | // failure. |
| 320 | block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode); | 337 | unprot_guard.Cancel(); |
| 338 | |||
| 339 | // Apply the memory block updates. | ||
| 340 | block_manager->Update(src_address, num_pages, src_state, new_perm, | ||
| 341 | KMemoryAttribute::Locked); | ||
| 342 | block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm, | ||
| 343 | KMemoryAttribute::None); | ||
| 344 | } | ||
| 321 | 345 | ||
| 322 | return ResultSuccess; | 346 | return ResultSuccess; |
| 323 | } | 347 | } |
| 324 | 348 | ||
| 325 | ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { | 349 | ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { |
| 350 | // Validate the mapping request. | ||
| 351 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), | ||
| 352 | ResultInvalidMemoryRegion); | ||
| 353 | |||
| 354 | // Lock the table. | ||
| 326 | KScopedLightLock lk(general_lock); | 355 | KScopedLightLock lk(general_lock); |
| 327 | 356 | ||
| 328 | if (!size) { | 357 | // Verify that the source memory is locked normal heap. |
| 329 | return ResultSuccess; | 358 | std::size_t num_src_allocator_blocks{}; |
| 359 | R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size, | ||
| 360 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, | ||
| 361 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 362 | KMemoryAttribute::Locked)); | ||
| 363 | |||
| 364 | // Verify that the destination memory is aliasable code. | ||
| 365 | std::size_t num_dst_allocator_blocks{}; | ||
| 366 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 367 | std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, | ||
| 368 | KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, | ||
| 369 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 370 | |||
| 371 | // Determine whether any pages being unmapped are code. | ||
| 372 | bool any_code_pages = false; | ||
| 373 | { | ||
| 374 | KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address); | ||
| 375 | while (true) { | ||
| 376 | // Get the memory info. | ||
| 377 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 378 | |||
| 379 | // Check if the memory has code flag. | ||
| 380 | if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) { | ||
| 381 | any_code_pages = true; | ||
| 382 | break; | ||
| 383 | } | ||
| 384 | |||
| 385 | // Check if we're done. | ||
| 386 | if (dst_address + size - 1 <= info.GetLastAddress()) { | ||
| 387 | break; | ||
| 388 | } | ||
| 389 | |||
| 390 | // Advance. | ||
| 391 | ++it; | ||
| 392 | } | ||
| 330 | } | 393 | } |
| 331 | 394 | ||
| 332 | const std::size_t num_pages{size / PageSize}; | 395 | // Ensure that we maintain the instruction cache. |
| 396 | bool reprotected_pages = false; | ||
| 397 | SCOPE_EXIT({ | ||
| 398 | if (reprotected_pages && any_code_pages) { | ||
| 399 | system.InvalidateCpuInstructionCacheRange(dst_address, size); | ||
| 400 | } | ||
| 401 | }); | ||
| 333 | 402 | ||
| 334 | CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size, | 403 | // Unmap. |
| 335 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, | 404 | { |
| 336 | KMemoryPermission::None, KMemoryAttribute::Mask, | 405 | // Determine the number of pages being operated on. |
| 337 | KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); | 406 | const std::size_t num_pages = size / PageSize; |
| 338 | 407 | ||
| 339 | KMemoryState state{}; | 408 | // Unmap the aliased copy of the pages. |
| 340 | CASCADE_CODE(CheckMemoryState( | 409 | R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); |
| 341 | &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias, | ||
| 342 | KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, | ||
| 343 | KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); | ||
| 344 | CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None, | ||
| 345 | KMemoryPermission::None, KMemoryAttribute::Mask, | ||
| 346 | KMemoryAttribute::None)); | ||
| 347 | CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); | ||
| 348 | 410 | ||
| 349 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free); | 411 | // Try to set the permissions for the source pages back to what they should be. |
| 350 | block_manager->Update(src_addr, num_pages, KMemoryState::Normal, | 412 | R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, |
| 351 | KMemoryPermission::UserReadWrite); | 413 | OperationType::ChangePermissions)); |
| 352 | 414 | ||
| 353 | system.InvalidateCpuInstructionCacheRange(dst_addr, size); | 415 | // Apply the memory block updates. |
| 416 | block_manager->Update(dst_address, num_pages, KMemoryState::None); | ||
| 417 | block_manager->Update(src_address, num_pages, KMemoryState::Normal, | ||
| 418 | KMemoryPermission::UserReadWrite); | ||
| 419 | |||
| 420 | // Note that we reprotected pages. | ||
| 421 | reprotected_pages = true; | ||
| 422 | } | ||
| 354 | 423 | ||
| 355 | return ResultSuccess; | 424 | return ResultSuccess; |
| 356 | } | 425 | } |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 194177332..aea1b8f63 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -36,8 +36,8 @@ public: | |||
| 36 | KMemoryManager::Pool pool); | 36 | KMemoryManager::Pool pool); |
| 37 | ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, | 37 | ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, |
| 38 | KMemoryPermission perm); | 38 | KMemoryPermission perm); |
| 39 | ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 39 | ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); |
| 40 | ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 40 | ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); |
| 41 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, | 41 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, |
| 42 | VAddr src_addr); | 42 | VAddr src_addr); |
| 43 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); | 43 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); |