diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | src/common/free_region_manager.h | 55 | ||||
| -rw-r--r-- | src/common/host_memory.cpp | 191 | ||||
| -rw-r--r-- | src/common/host_memory.h | 15 | ||||
| -rw-r--r-- | src/common/settings.cpp | 17 | ||||
| -rw-r--r-- | src/common/settings.h | 11 | ||||
| -rw-r--r-- | src/common/settings_enums.h | 2 | ||||
| -rw-r--r-- | src/common/signal_chain.cpp | 42 | ||||
| -rw-r--r-- | src/common/signal_chain.h | 19 | ||||
| -rw-r--r-- | src/common/wall_clock.cpp | 4 |
10 files changed, 320 insertions, 46 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 2651daadb..bbc55eb56 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -52,6 +52,7 @@ add_library(common STATIC | |||
| 52 | fiber.cpp | 52 | fiber.cpp |
| 53 | fiber.h | 53 | fiber.h |
| 54 | fixed_point.h | 54 | fixed_point.h |
| 55 | free_region_manager.h | ||
| 55 | fs/file.cpp | 56 | fs/file.cpp |
| 56 | fs/file.h | 57 | fs/file.h |
| 57 | fs/fs.cpp | 58 | fs/fs.cpp |
| @@ -166,6 +167,13 @@ if (WIN32) | |||
| 166 | target_link_libraries(common PRIVATE ntdll) | 167 | target_link_libraries(common PRIVATE ntdll) |
| 167 | endif() | 168 | endif() |
| 168 | 169 | ||
| 170 | if (NOT WIN32) | ||
| 171 | target_sources(common PRIVATE | ||
| 172 | signal_chain.cpp | ||
| 173 | signal_chain.h | ||
| 174 | ) | ||
| 175 | endif() | ||
| 176 | |||
| 169 | if(ANDROID) | 177 | if(ANDROID) |
| 170 | target_sources(common | 178 | target_sources(common |
| 171 | PRIVATE | 179 | PRIVATE |
| @@ -200,7 +208,7 @@ if(ARCHITECTURE_x86_64) | |||
| 200 | target_link_libraries(common PRIVATE xbyak::xbyak) | 208 | target_link_libraries(common PRIVATE xbyak::xbyak) |
| 201 | endif() | 209 | endif() |
| 202 | 210 | ||
| 203 | if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX)) | 211 | if (HAS_NCE) |
| 204 | target_sources(common | 212 | target_sources(common |
| 205 | PRIVATE | 213 | PRIVATE |
| 206 | arm64/native_clock.cpp | 214 | arm64/native_clock.cpp |
diff --git a/src/common/free_region_manager.h b/src/common/free_region_manager.h new file mode 100644 index 000000000..2e590d609 --- /dev/null +++ b/src/common/free_region_manager.h | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <mutex> | ||
| 7 | #include <boost/icl/interval_set.hpp> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | class FreeRegionManager { | ||
| 12 | public: | ||
| 13 | explicit FreeRegionManager() = default; | ||
| 14 | ~FreeRegionManager() = default; | ||
| 15 | |||
| 16 | void SetAddressSpace(void* start, size_t size) { | ||
| 17 | this->FreeBlock(start, size); | ||
| 18 | } | ||
| 19 | |||
| 20 | std::pair<void*, size_t> FreeBlock(void* block_ptr, size_t size) { | ||
| 21 | std::scoped_lock lk(m_mutex); | ||
| 22 | |||
| 23 | // Check to see if we are adjacent to any regions. | ||
| 24 | auto start_address = reinterpret_cast<uintptr_t>(block_ptr); | ||
| 25 | auto end_address = start_address + size; | ||
| 26 | auto it = m_free_regions.find({start_address - 1, end_address + 1}); | ||
| 27 | |||
| 28 | // If we are, join with them, ensuring we stay in bounds. | ||
| 29 | if (it != m_free_regions.end()) { | ||
| 30 | start_address = std::min(start_address, it->lower()); | ||
| 31 | end_address = std::max(end_address, it->upper()); | ||
| 32 | } | ||
| 33 | |||
| 34 | // Free the relevant region. | ||
| 35 | m_free_regions.insert({start_address, end_address}); | ||
| 36 | |||
| 37 | // Return the adjusted pointers. | ||
| 38 | block_ptr = reinterpret_cast<void*>(start_address); | ||
| 39 | size = end_address - start_address; | ||
| 40 | return {block_ptr, size}; | ||
| 41 | } | ||
| 42 | |||
| 43 | void AllocateBlock(void* block_ptr, size_t size) { | ||
| 44 | std::scoped_lock lk(m_mutex); | ||
| 45 | |||
| 46 | auto address = reinterpret_cast<uintptr_t>(block_ptr); | ||
| 47 | m_free_regions.subtract({address, address + size}); | ||
| 48 | } | ||
| 49 | |||
| 50 | private: | ||
| 51 | std::mutex m_mutex; | ||
| 52 | boost::icl::interval_set<uintptr_t> m_free_regions; | ||
| 53 | }; | ||
| 54 | |||
| 55 | } // namespace Common | ||
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index ba22595e0..3a9ea6eb4 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp | |||
| @@ -21,15 +21,18 @@ | |||
| 21 | #include <boost/icl/interval_set.hpp> | 21 | #include <boost/icl/interval_set.hpp> |
| 22 | #include <fcntl.h> | 22 | #include <fcntl.h> |
| 23 | #include <sys/mman.h> | 23 | #include <sys/mman.h> |
| 24 | #include <sys/random.h> | ||
| 24 | #include <unistd.h> | 25 | #include <unistd.h> |
| 25 | #include "common/scope_exit.h" | 26 | #include "common/scope_exit.h" |
| 26 | 27 | ||
| 27 | #endif // ^^^ Linux ^^^ | 28 | #endif // ^^^ Linux ^^^ |
| 28 | 29 | ||
| 29 | #include <mutex> | 30 | #include <mutex> |
| 31 | #include <random> | ||
| 30 | 32 | ||
| 31 | #include "common/alignment.h" | 33 | #include "common/alignment.h" |
| 32 | #include "common/assert.h" | 34 | #include "common/assert.h" |
| 35 | #include "common/free_region_manager.h" | ||
| 33 | #include "common/host_memory.h" | 36 | #include "common/host_memory.h" |
| 34 | #include "common/logging/log.h" | 37 | #include "common/logging/log.h" |
| 35 | 38 | ||
| @@ -141,7 +144,7 @@ public: | |||
| 141 | Release(); | 144 | Release(); |
| 142 | } | 145 | } |
| 143 | 146 | ||
| 144 | void Map(size_t virtual_offset, size_t host_offset, size_t length) { | 147 | void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { |
| 145 | std::unique_lock lock{placeholder_mutex}; | 148 | std::unique_lock lock{placeholder_mutex}; |
| 146 | if (!IsNiechePlaceholder(virtual_offset, length)) { | 149 | if (!IsNiechePlaceholder(virtual_offset, length)) { |
| 147 | Split(virtual_offset, length); | 150 | Split(virtual_offset, length); |
| @@ -160,7 +163,7 @@ public: | |||
| 160 | } | 163 | } |
| 161 | } | 164 | } |
| 162 | 165 | ||
| 163 | void Protect(size_t virtual_offset, size_t length, bool read, bool write) { | 166 | void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) { |
| 164 | DWORD new_flags{}; | 167 | DWORD new_flags{}; |
| 165 | if (read && write) { | 168 | if (read && write) { |
| 166 | new_flags = PAGE_READWRITE; | 169 | new_flags = PAGE_READWRITE; |
| @@ -186,6 +189,11 @@ public: | |||
| 186 | } | 189 | } |
| 187 | } | 190 | } |
| 188 | 191 | ||
| 192 | void EnableDirectMappedAddress() { | ||
| 193 | // TODO | ||
| 194 | UNREACHABLE(); | ||
| 195 | } | ||
| 196 | |||
| 189 | const size_t backing_size; ///< Size of the backing memory in bytes | 197 | const size_t backing_size; ///< Size of the backing memory in bytes |
| 190 | const size_t virtual_size; ///< Size of the virtual address placeholder in bytes | 198 | const size_t virtual_size; ///< Size of the virtual address placeholder in bytes |
| 191 | 199 | ||
| @@ -353,6 +361,55 @@ private: | |||
| 353 | 361 | ||
| 354 | #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv | 362 | #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv |
| 355 | 363 | ||
| 364 | #ifdef ARCHITECTURE_arm64 | ||
| 365 | |||
| 366 | static void* ChooseVirtualBase(size_t virtual_size) { | ||
| 367 | constexpr uintptr_t Map39BitSize = (1ULL << 39); | ||
| 368 | constexpr uintptr_t Map36BitSize = (1ULL << 36); | ||
| 369 | |||
| 370 | // This is not a cryptographic application, we just want something random. | ||
| 371 | std::mt19937_64 rng; | ||
| 372 | |||
| 373 | // We want to ensure we are allocating at an address aligned to the L2 block size. | ||
| 374 | // For Qualcomm devices, we must also allocate memory above 36 bits. | ||
| 375 | const size_t lower = Map36BitSize / HugePageSize; | ||
| 376 | const size_t upper = (Map39BitSize - virtual_size) / HugePageSize; | ||
| 377 | const size_t range = upper - lower; | ||
| 378 | |||
| 379 | // Try up to 64 times to allocate memory at random addresses in the range. | ||
| 380 | for (int i = 0; i < 64; i++) { | ||
| 381 | // Calculate a possible location. | ||
| 382 | uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize; | ||
| 383 | |||
| 384 | // Try to map. | ||
| 385 | // Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here. | ||
| 386 | void* map_pointer = | ||
| 387 | mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE, | ||
| 388 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); | ||
| 389 | |||
| 390 | // If we successfully mapped, we're done. | ||
| 391 | if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) { | ||
| 392 | return map_pointer; | ||
| 393 | } | ||
| 394 | |||
| 395 | // Unmap if necessary, and try again. | ||
| 396 | if (map_pointer != MAP_FAILED) { | ||
| 397 | munmap(map_pointer, virtual_size); | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | return MAP_FAILED; | ||
| 402 | } | ||
| 403 | |||
| 404 | #else | ||
| 405 | |||
| 406 | static void* ChooseVirtualBase(size_t virtual_size) { | ||
| 407 | return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, | ||
| 408 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); | ||
| 409 | } | ||
| 410 | |||
| 411 | #endif | ||
| 412 | |||
| 356 | class HostMemory::Impl { | 413 | class HostMemory::Impl { |
| 357 | public: | 414 | public: |
| 358 | explicit Impl(size_t backing_size_, size_t virtual_size_) | 415 | explicit Impl(size_t backing_size_, size_t virtual_size_) |
| @@ -415,8 +472,7 @@ public: | |||
| 415 | } | 472 | } |
| 416 | } | 473 | } |
| 417 | #else | 474 | #else |
| 418 | virtual_base = static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE, | 475 | virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size)); |
| 419 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0)); | ||
| 420 | if (virtual_base == MAP_FAILED) { | 476 | if (virtual_base == MAP_FAILED) { |
| 421 | LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); | 477 | LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); |
| 422 | throw std::bad_alloc{}; | 478 | throw std::bad_alloc{}; |
| @@ -424,7 +480,7 @@ public: | |||
| 424 | madvise(virtual_base, virtual_size, MADV_HUGEPAGE); | 480 | madvise(virtual_base, virtual_size, MADV_HUGEPAGE); |
| 425 | #endif | 481 | #endif |
| 426 | 482 | ||
| 427 | placeholders.add({0, virtual_size}); | 483 | free_manager.SetAddressSpace(virtual_base, virtual_size); |
| 428 | good = true; | 484 | good = true; |
| 429 | } | 485 | } |
| 430 | 486 | ||
| @@ -432,14 +488,29 @@ public: | |||
| 432 | Release(); | 488 | Release(); |
| 433 | } | 489 | } |
| 434 | 490 | ||
| 435 | void Map(size_t virtual_offset, size_t host_offset, size_t length) { | 491 | void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { |
| 436 | { | 492 | // Intersect the range with our address space. |
| 437 | std::scoped_lock lock{placeholder_mutex}; | 493 | AdjustMap(&virtual_offset, &length); |
| 438 | placeholders.subtract({virtual_offset, virtual_offset + length}); | 494 | |
| 495 | // We are removing a placeholder. | ||
| 496 | free_manager.AllocateBlock(virtual_base + virtual_offset, length); | ||
| 497 | |||
| 498 | // Deduce mapping protection flags. | ||
| 499 | int flags = PROT_NONE; | ||
| 500 | if (True(perms & MemoryPermission::Read)) { | ||
| 501 | flags |= PROT_READ; | ||
| 439 | } | 502 | } |
| 503 | if (True(perms & MemoryPermission::Write)) { | ||
| 504 | flags |= PROT_WRITE; | ||
| 505 | } | ||
| 506 | #ifdef ARCHITECTURE_arm64 | ||
| 507 | if (True(perms & MemoryPermission::Execute)) { | ||
| 508 | flags |= PROT_EXEC; | ||
| 509 | } | ||
| 510 | #endif | ||
| 440 | 511 | ||
| 441 | void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE, | 512 | void* ret = mmap(virtual_base + virtual_offset, length, flags, MAP_SHARED | MAP_FIXED, fd, |
| 442 | MAP_SHARED | MAP_FIXED, fd, host_offset); | 513 | host_offset); |
| 443 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); | 514 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); |
| 444 | } | 515 | } |
| 445 | 516 | ||
| @@ -447,47 +518,54 @@ public: | |||
| 447 | // The method name is wrong. We're still talking about the virtual range. | 518 | // The method name is wrong. We're still talking about the virtual range. |
| 448 | // We don't want to unmap, we want to reserve this memory. | 519 | // We don't want to unmap, we want to reserve this memory. |
| 449 | 520 | ||
| 450 | { | 521 | // Intersect the range with our address space. |
| 451 | std::scoped_lock lock{placeholder_mutex}; | 522 | AdjustMap(&virtual_offset, &length); |
| 452 | auto it = placeholders.find({virtual_offset - 1, virtual_offset + length + 1}); | ||
| 453 | 523 | ||
| 454 | if (it != placeholders.end()) { | 524 | // Merge with any adjacent placeholder mappings. |
| 455 | size_t prev_upper = virtual_offset + length; | 525 | auto [merged_pointer, merged_size] = |
| 456 | virtual_offset = std::min(virtual_offset, it->lower()); | 526 | free_manager.FreeBlock(virtual_base + virtual_offset, length); |
| 457 | length = std::max(it->upper(), prev_upper) - virtual_offset; | ||
| 458 | } | ||
| 459 | |||
| 460 | placeholders.add({virtual_offset, virtual_offset + length}); | ||
| 461 | } | ||
| 462 | 527 | ||
| 463 | void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE, | 528 | void* ret = mmap(merged_pointer, merged_size, PROT_NONE, |
| 464 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); | 529 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); |
| 465 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); | 530 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); |
| 466 | } | 531 | } |
| 467 | 532 | ||
| 468 | void Protect(size_t virtual_offset, size_t length, bool read, bool write) { | 533 | void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) { |
| 469 | int flags = 0; | 534 | // Intersect the range with our address space. |
| 535 | AdjustMap(&virtual_offset, &length); | ||
| 536 | |||
| 537 | int flags = PROT_NONE; | ||
| 470 | if (read) { | 538 | if (read) { |
| 471 | flags |= PROT_READ; | 539 | flags |= PROT_READ; |
| 472 | } | 540 | } |
| 473 | if (write) { | 541 | if (write) { |
| 474 | flags |= PROT_WRITE; | 542 | flags |= PROT_WRITE; |
| 475 | } | 543 | } |
| 544 | #ifdef HAS_NCE | ||
| 545 | if (execute) { | ||
| 546 | flags |= PROT_EXEC; | ||
| 547 | } | ||
| 548 | #endif | ||
| 476 | int ret = mprotect(virtual_base + virtual_offset, length, flags); | 549 | int ret = mprotect(virtual_base + virtual_offset, length, flags); |
| 477 | ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno)); | 550 | ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno)); |
| 478 | } | 551 | } |
| 479 | 552 | ||
| 553 | void EnableDirectMappedAddress() { | ||
| 554 | virtual_base = nullptr; | ||
| 555 | } | ||
| 556 | |||
| 480 | const size_t backing_size; ///< Size of the backing memory in bytes | 557 | const size_t backing_size; ///< Size of the backing memory in bytes |
| 481 | const size_t virtual_size; ///< Size of the virtual address placeholder in bytes | 558 | const size_t virtual_size; ///< Size of the virtual address placeholder in bytes |
| 482 | 559 | ||
| 483 | u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)}; | 560 | u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)}; |
| 484 | u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)}; | 561 | u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)}; |
| 562 | u8* virtual_map_base{reinterpret_cast<u8*>(MAP_FAILED)}; | ||
| 485 | 563 | ||
| 486 | private: | 564 | private: |
| 487 | /// Release all resources in the object | 565 | /// Release all resources in the object |
| 488 | void Release() { | 566 | void Release() { |
| 489 | if (virtual_base != MAP_FAILED) { | 567 | if (virtual_map_base != MAP_FAILED) { |
| 490 | int ret = munmap(virtual_base, virtual_size); | 568 | int ret = munmap(virtual_map_base, virtual_size); |
| 491 | ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno)); | 569 | ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno)); |
| 492 | } | 570 | } |
| 493 | 571 | ||
| @@ -502,10 +580,29 @@ private: | |||
| 502 | } | 580 | } |
| 503 | } | 581 | } |
| 504 | 582 | ||
| 505 | int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create | 583 | void AdjustMap(size_t* virtual_offset, size_t* length) { |
| 584 | if (virtual_base != nullptr) { | ||
| 585 | return; | ||
| 586 | } | ||
| 587 | |||
| 588 | // If we are direct mapped, we want to make sure we are operating on a region | ||
| 589 | // that is in range of our virtual mapping. | ||
| 590 | size_t intended_start = *virtual_offset; | ||
| 591 | size_t intended_end = intended_start + *length; | ||
| 592 | size_t address_space_start = reinterpret_cast<size_t>(virtual_map_base); | ||
| 593 | size_t address_space_end = address_space_start + virtual_size; | ||
| 594 | |||
| 595 | if (address_space_start > intended_end || intended_start > address_space_end) { | ||
| 596 | *virtual_offset = 0; | ||
| 597 | *length = 0; | ||
| 598 | } else { | ||
| 599 | *virtual_offset = std::max(intended_start, address_space_start); | ||
| 600 | *length = std::min(intended_end, address_space_end) - *virtual_offset; | ||
| 601 | } | ||
| 602 | } | ||
| 506 | 603 | ||
| 507 | boost::icl::interval_set<size_t> placeholders; ///< Mapped placeholders | 604 | int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create |
| 508 | std::mutex placeholder_mutex; ///< Mutex for placeholders | 605 | FreeRegionManager free_manager{}; |
| 509 | }; | 606 | }; |
| 510 | 607 | ||
| 511 | #else // ^^^ Linux ^^^ vvv Generic vvv | 608 | #else // ^^^ Linux ^^^ vvv Generic vvv |
| @@ -518,11 +615,13 @@ public: | |||
| 518 | throw std::bad_alloc{}; | 615 | throw std::bad_alloc{}; |
| 519 | } | 616 | } |
| 520 | 617 | ||
| 521 | void Map(size_t virtual_offset, size_t host_offset, size_t length) {} | 618 | void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {} |
| 522 | 619 | ||
| 523 | void Unmap(size_t virtual_offset, size_t length) {} | 620 | void Unmap(size_t virtual_offset, size_t length) {} |
| 524 | 621 | ||
| 525 | void Protect(size_t virtual_offset, size_t length, bool read, bool write) {} | 622 | void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {} |
| 623 | |||
| 624 | void EnableDirectMappedAddress() {} | ||
| 526 | 625 | ||
| 527 | u8* backing_base{nullptr}; | 626 | u8* backing_base{nullptr}; |
| 528 | u8* virtual_base{nullptr}; | 627 | u8* virtual_base{nullptr}; |
| @@ -535,15 +634,16 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) | |||
| 535 | try { | 634 | try { |
| 536 | // Try to allocate a fastmem arena. | 635 | // Try to allocate a fastmem arena. |
| 537 | // The implementation will fail with std::bad_alloc on errors. | 636 | // The implementation will fail with std::bad_alloc on errors. |
| 538 | impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), | 637 | impl = |
| 539 | AlignUp(virtual_size, PageAlignment) + | 638 | std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), |
| 540 | 3 * HugePageSize); | 639 | AlignUp(virtual_size, PageAlignment) + HugePageSize); |
| 541 | backing_base = impl->backing_base; | 640 | backing_base = impl->backing_base; |
| 542 | virtual_base = impl->virtual_base; | 641 | virtual_base = impl->virtual_base; |
| 543 | 642 | ||
| 544 | if (virtual_base) { | 643 | if (virtual_base) { |
| 545 | virtual_base += 2 * HugePageSize - 1; | 644 | // Ensure the virtual base is aligned to the L2 block size. |
| 546 | virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1); | 645 | virtual_base = reinterpret_cast<u8*>( |
| 646 | Common::AlignUp(reinterpret_cast<uintptr_t>(virtual_base), HugePageSize)); | ||
| 547 | virtual_base_offset = virtual_base - impl->virtual_base; | 647 | virtual_base_offset = virtual_base - impl->virtual_base; |
| 548 | } | 648 | } |
| 549 | 649 | ||
| @@ -562,7 +662,8 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default; | |||
| 562 | 662 | ||
| 563 | HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; | 663 | HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; |
| 564 | 664 | ||
| 565 | void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) { | 665 | void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, |
| 666 | MemoryPermission perms) { | ||
| 566 | ASSERT(virtual_offset % PageAlignment == 0); | 667 | ASSERT(virtual_offset % PageAlignment == 0); |
| 567 | ASSERT(host_offset % PageAlignment == 0); | 668 | ASSERT(host_offset % PageAlignment == 0); |
| 568 | ASSERT(length % PageAlignment == 0); | 669 | ASSERT(length % PageAlignment == 0); |
| @@ -571,7 +672,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) { | |||
| 571 | if (length == 0 || !virtual_base || !impl) { | 672 | if (length == 0 || !virtual_base || !impl) { |
| 572 | return; | 673 | return; |
| 573 | } | 674 | } |
| 574 | impl->Map(virtual_offset + virtual_base_offset, host_offset, length); | 675 | impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); |
| 575 | } | 676 | } |
| 576 | 677 | ||
| 577 | void HostMemory::Unmap(size_t virtual_offset, size_t length) { | 678 | void HostMemory::Unmap(size_t virtual_offset, size_t length) { |
| @@ -584,14 +685,22 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) { | |||
| 584 | impl->Unmap(virtual_offset + virtual_base_offset, length); | 685 | impl->Unmap(virtual_offset + virtual_base_offset, length); |
| 585 | } | 686 | } |
| 586 | 687 | ||
| 587 | void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) { | 688 | void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write, |
| 689 | bool execute) { | ||
| 588 | ASSERT(virtual_offset % PageAlignment == 0); | 690 | ASSERT(virtual_offset % PageAlignment == 0); |
| 589 | ASSERT(length % PageAlignment == 0); | 691 | ASSERT(length % PageAlignment == 0); |
| 590 | ASSERT(virtual_offset + length <= virtual_size); | 692 | ASSERT(virtual_offset + length <= virtual_size); |
| 591 | if (length == 0 || !virtual_base || !impl) { | 693 | if (length == 0 || !virtual_base || !impl) { |
| 592 | return; | 694 | return; |
| 593 | } | 695 | } |
| 594 | impl->Protect(virtual_offset + virtual_base_offset, length, read, write); | 696 | impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); |
| 697 | } | ||
| 698 | |||
| 699 | void HostMemory::EnableDirectMappedAddress() { | ||
| 700 | if (impl) { | ||
| 701 | impl->EnableDirectMappedAddress(); | ||
| 702 | virtual_size += reinterpret_cast<uintptr_t>(virtual_base); | ||
| 703 | } | ||
| 595 | } | 704 | } |
| 596 | 705 | ||
| 597 | } // namespace Common | 706 | } // namespace Common |
diff --git a/src/common/host_memory.h b/src/common/host_memory.h index 447975ded..cebfacab2 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h | |||
| @@ -4,11 +4,20 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "common/virtual_buffer.h" | 9 | #include "common/virtual_buffer.h" |
| 9 | 10 | ||
| 10 | namespace Common { | 11 | namespace Common { |
| 11 | 12 | ||
| 13 | enum class MemoryPermission : u32 { | ||
| 14 | Read = 1 << 0, | ||
| 15 | Write = 1 << 1, | ||
| 16 | ReadWrite = Read | Write, | ||
| 17 | Execute = 1 << 2, | ||
| 18 | }; | ||
| 19 | DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission) | ||
| 20 | |||
| 12 | /** | 21 | /** |
| 13 | * A low level linear memory buffer, which supports multiple mappings | 22 | * A low level linear memory buffer, which supports multiple mappings |
| 14 | * Its purpose is to rebuild a given sparse memory layout, including mirrors. | 23 | * Its purpose is to rebuild a given sparse memory layout, including mirrors. |
| @@ -31,11 +40,13 @@ public: | |||
| 31 | HostMemory(HostMemory&& other) noexcept; | 40 | HostMemory(HostMemory&& other) noexcept; |
| 32 | HostMemory& operator=(HostMemory&& other) noexcept; | 41 | HostMemory& operator=(HostMemory&& other) noexcept; |
| 33 | 42 | ||
| 34 | void Map(size_t virtual_offset, size_t host_offset, size_t length); | 43 | void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms); |
| 35 | 44 | ||
| 36 | void Unmap(size_t virtual_offset, size_t length); | 45 | void Unmap(size_t virtual_offset, size_t length); |
| 37 | 46 | ||
| 38 | void Protect(size_t virtual_offset, size_t length, bool read, bool write); | 47 | void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false); |
| 48 | |||
| 49 | void EnableDirectMappedAddress(); | ||
| 39 | 50 | ||
| 40 | [[nodiscard]] u8* BackingBasePointer() noexcept { | 51 | [[nodiscard]] u8* BackingBasePointer() noexcept { |
| 41 | return backing_base; | 52 | return backing_base; |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 3e829253f..4666bd0a0 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -41,6 +41,7 @@ SWITCHABLE(AspectRatio, true); | |||
| 41 | SWITCHABLE(AstcDecodeMode, true); | 41 | SWITCHABLE(AstcDecodeMode, true); |
| 42 | SWITCHABLE(AstcRecompression, true); | 42 | SWITCHABLE(AstcRecompression, true); |
| 43 | SWITCHABLE(AudioMode, true); | 43 | SWITCHABLE(AudioMode, true); |
| 44 | SWITCHABLE(CpuBackend, true); | ||
| 44 | SWITCHABLE(CpuAccuracy, true); | 45 | SWITCHABLE(CpuAccuracy, true); |
| 45 | SWITCHABLE(FullscreenMode, true); | 46 | SWITCHABLE(FullscreenMode, true); |
| 46 | SWITCHABLE(GpuAccuracy, true); | 47 | SWITCHABLE(GpuAccuracy, true); |
| @@ -155,6 +156,22 @@ bool IsFastmemEnabled() { | |||
| 155 | return true; | 156 | return true; |
| 156 | } | 157 | } |
| 157 | 158 | ||
| 159 | static bool is_nce_enabled = false; | ||
| 160 | |||
| 161 | void SetNceEnabled(bool is_39bit) { | ||
| 162 | const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce; | ||
| 163 | is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit; | ||
| 164 | if (is_nce_selected && !is_nce_enabled) { | ||
| 165 | LOG_WARNING( | ||
| 166 | Common, | ||
| 167 | "Program does not utilize 39-bit address space, unable to natively execute code"); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | bool IsNceEnabled() { | ||
| 172 | return is_nce_enabled; | ||
| 173 | } | ||
| 174 | |||
| 158 | bool IsDockedMode() { | 175 | bool IsDockedMode() { |
| 159 | return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; | 176 | return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; |
| 160 | } | 177 | } |
diff --git a/src/common/settings.h b/src/common/settings.h index 6425cd98f..98341ad96 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -63,6 +63,7 @@ SWITCHABLE(AspectRatio, true); | |||
| 63 | SWITCHABLE(AstcDecodeMode, true); | 63 | SWITCHABLE(AstcDecodeMode, true); |
| 64 | SWITCHABLE(AstcRecompression, true); | 64 | SWITCHABLE(AstcRecompression, true); |
| 65 | SWITCHABLE(AudioMode, true); | 65 | SWITCHABLE(AudioMode, true); |
| 66 | SWITCHABLE(CpuBackend, true); | ||
| 66 | SWITCHABLE(CpuAccuracy, true); | 67 | SWITCHABLE(CpuAccuracy, true); |
| 67 | SWITCHABLE(FullscreenMode, true); | 68 | SWITCHABLE(FullscreenMode, true); |
| 68 | SWITCHABLE(GpuAccuracy, true); | 69 | SWITCHABLE(GpuAccuracy, true); |
| @@ -179,6 +180,14 @@ struct Values { | |||
| 179 | &use_speed_limit}; | 180 | &use_speed_limit}; |
| 180 | 181 | ||
| 181 | // Cpu | 182 | // Cpu |
| 183 | SwitchableSetting<CpuBackend, true> cpu_backend{ | ||
| 184 | linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic, | ||
| 185 | #ifdef HAS_NCE | ||
| 186 | CpuBackend::Nce, | ||
| 187 | #else | ||
| 188 | CpuBackend::Dynarmic, | ||
| 189 | #endif | ||
| 190 | "cpu_backend", Category::Cpu}; | ||
| 182 | SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto, | 191 | SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto, |
| 183 | CpuAccuracy::Auto, CpuAccuracy::Paranoid, | 192 | CpuAccuracy::Auto, CpuAccuracy::Paranoid, |
| 184 | "cpu_accuracy", Category::Cpu}; | 193 | "cpu_accuracy", Category::Cpu}; |
| @@ -569,6 +578,8 @@ bool IsGPULevelExtreme(); | |||
| 569 | bool IsGPULevelHigh(); | 578 | bool IsGPULevelHigh(); |
| 570 | 579 | ||
| 571 | bool IsFastmemEnabled(); | 580 | bool IsFastmemEnabled(); |
| 581 | void SetNceEnabled(bool is_64bit); | ||
| 582 | bool IsNceEnabled(); | ||
| 572 | 583 | ||
| 573 | bool IsDockedMode(); | 584 | bool IsDockedMode(); |
| 574 | 585 | ||
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 11429d7a8..d6351e57e 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h | |||
| @@ -129,6 +129,8 @@ ENUM(ShaderBackend, Glsl, Glasm, SpirV); | |||
| 129 | 129 | ||
| 130 | ENUM(GpuAccuracy, Normal, High, Extreme); | 130 | ENUM(GpuAccuracy, Normal, High, Extreme); |
| 131 | 131 | ||
| 132 | ENUM(CpuBackend, Dynarmic, Nce); | ||
| 133 | |||
| 132 | ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); | 134 | ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); |
| 133 | 135 | ||
| 134 | ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); | 136 | ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); |
diff --git a/src/common/signal_chain.cpp b/src/common/signal_chain.cpp new file mode 100644 index 000000000..2e4fecc48 --- /dev/null +++ b/src/common/signal_chain.cpp | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <dlfcn.h> | ||
| 5 | |||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/dynamic_library.h" | ||
| 8 | #include "common/scope_exit.h" | ||
| 9 | #include "common/signal_chain.h" | ||
| 10 | |||
| 11 | namespace Common { | ||
| 12 | |||
| 13 | template <typename T> | ||
| 14 | T* LookupLibcSymbol(const char* name) { | ||
| 15 | #if defined(__BIONIC__) | ||
| 16 | Common::DynamicLibrary provider("libc.so"); | ||
| 17 | if (!provider.IsOpen()) { | ||
| 18 | UNREACHABLE_MSG("Failed to open libc!"); | ||
| 19 | } | ||
| 20 | #else | ||
| 21 | // For other operating environments, we assume the symbol is not overridden. | ||
| 22 | const char* base = nullptr; | ||
| 23 | Common::DynamicLibrary provider(base); | ||
| 24 | #endif | ||
| 25 | |||
| 26 | void* sym = provider.GetSymbolAddress(name); | ||
| 27 | if (sym == nullptr) { | ||
| 28 | sym = dlsym(RTLD_DEFAULT, name); | ||
| 29 | } | ||
| 30 | if (sym == nullptr) { | ||
| 31 | UNREACHABLE_MSG("Unable to find symbol {}!", name); | ||
| 32 | } | ||
| 33 | |||
| 34 | return reinterpret_cast<T*>(sym); | ||
| 35 | } | ||
| 36 | |||
| 37 | int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact) { | ||
| 38 | static auto libc_sigaction = LookupLibcSymbol<decltype(sigaction)>("sigaction"); | ||
| 39 | return libc_sigaction(signum, act, oldact); | ||
| 40 | } | ||
| 41 | |||
| 42 | } // namespace Common | ||
diff --git a/src/common/signal_chain.h b/src/common/signal_chain.h new file mode 100644 index 000000000..8d06a1bd1 --- /dev/null +++ b/src/common/signal_chain.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #ifndef _WIN32 | ||
| 7 | |||
| 8 | #include <signal.h> | ||
| 9 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | // Android's ART overrides sigaction with its own wrapper. This is problematic for SIGSEGV | ||
| 13 | // in particular, because ART's handler accesses tpidr_el0, which conflicts with NCE. | ||
| 14 | // This extracts the libc symbol and calls it directly. | ||
| 15 | int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact); | ||
| 16 | |||
| 17 | } // namespace Common | ||
| 18 | |||
| 19 | #endif | ||
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index caca9a123..012fdc1e0 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "common/x64/rdtsc.h" | 10 | #include "common/x64/rdtsc.h" |
| 11 | #endif | 11 | #endif |
| 12 | 12 | ||
| 13 | #if defined(ARCHITECTURE_arm64) && defined(__linux__) | 13 | #ifdef HAS_NCE |
| 14 | #include "common/arm64/native_clock.h" | 14 | #include "common/arm64/native_clock.h" |
| 15 | #endif | 15 | #endif |
| 16 | 16 | ||
| @@ -68,7 +68,7 @@ std::unique_ptr<WallClock> CreateOptimalClock() { | |||
| 68 | // - Is not more precise than 1 GHz (1ns resolution) | 68 | // - Is not more precise than 1 GHz (1ns resolution) |
| 69 | return std::make_unique<StandardWallClock>(); | 69 | return std::make_unique<StandardWallClock>(); |
| 70 | } | 70 | } |
| 71 | #elif defined(ARCHITECTURE_arm64) && defined(__linux__) | 71 | #elif defined(HAS_NCE) |
| 72 | return std::make_unique<Arm64::NativeClock>(); | 72 | return std::make_unique<Arm64::NativeClock>(); |
| 73 | #else | 73 | #else |
| 74 | return std::make_unique<StandardWallClock>(); | 74 | return std::make_unique<StandardWallClock>(); |