diff --git a/app/src/main/cpp/skyline/common/address_space.h b/app/src/main/cpp/skyline/common/address_space.h index 39cf2b39..feec6d91 100644 --- a/app/src/main/cpp/skyline/common/address_space.h +++ b/app/src/main/cpp/skyline/common/address_space.h @@ -6,6 +6,7 @@ #include #include #include +#include "segment_table.h" namespace skyline { template @@ -76,16 +77,6 @@ namespace skyline { FlatAddressSpaceMap(VaType vaLimit, std::function unmapCallback = {}); FlatAddressSpaceMap() = default; - - void Map(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo = {}) { - std::scoped_lock lock(blockMutex); - MapLocked(virt, phys, size, extraInfo); - } - - void Unmap(VaType virt, VaType size) { - std::scoped_lock lock(blockMutex); - UnmapLocked(virt, size); - } }; /** @@ -98,12 +89,37 @@ namespace skyline { /** * @brief FlatMemoryManager specialises FlatAddressSpaceMap to focus on pointers as PAs, adding read/write functions and sparse mapping support */ - template requires AddressSpaceValid + template requires AddressSpaceValid class FlatMemoryManager : public FlatAddressSpaceMap { private: static constexpr u64 SparseMapSize{0x400000000}; //!< 16GiB pool size for sparse mappings returned by TranslateRange, this number is arbritary and should be large enough to fit the largest sparse mapping in the AS u8 *sparseMap; //!< Pointer to a zero filled memory region that is returned by TranslateRange for sparse mappings + /** + * @brief Version of `Block` that is trivial so it can be stored in a segment table for rapid lookups, also holds an additional extent member + */ + struct SegmentTableEntry { + VaType virt; + u8 *phys; + VaType extent; + MemoryManagerBlockInfo extraInfo; + }; + + static constexpr size_t AddressSpaceSize{1ULL << AddressSpaceBits}; + SegmentTable blockSegmentTable; //!< A page table of all buffer mappings for O(1) lookups on full matches + + TranslatedAddressRange TranslateRangeImpl(VaType virt, VaType size, std::function)> cpuAccessCallback = {}); + + std::pair, size_t> LookupBlockLocked(VaType virt, std::function)> cpuAccessCallback = {}) { + const auto &blockEntry{this->blockSegmentTable[virt]}; + VaType segmentOffset{virt - blockEntry.virt}; + span blockSpan{blockEntry.phys, blockEntry.extent}; + if (cpuAccessCallback) + cpuAccessCallback(blockSpan); + + return {blockSpan, segmentOffset}; + } + public: FlatMemoryManager(); @@ -117,9 +133,31 @@ namespace skyline { } /** - * @return A vector of all physical ranges inside of the given virtual range + * @brief Looks up the mapped region that contains the given VA + * @return A span of the mapped region and the offset of the input VA in the region */ - TranslatedAddressRange TranslateRange(VaType virt, VaType size, std::function)> cpuAccessCallback = {}); + __attribute__((always_inline)) std::pair, VaType> LookupBlock(VaType virt, std::function)> cpuAccessCallback = {}) { + std::scoped_lock lock{this->blockMutex}; + return LookupBlockLocked(virt, cpuAccessCallback); + } + + /** + * @brief Translates a region in the VA space to a corresponding set of regions in the PA space + */ + TranslatedAddressRange TranslateRange(VaType virt, VaType size, std::function)> cpuAccessCallback = {}) { + std::scoped_lock lock{this->blockMutex}; + + // Fast path for when the range is mapped in a single block + auto [blockSpan, rangeOffset]{LookupBlockLocked(virt, cpuAccessCallback)}; + if (blockSpan.size() - rangeOffset >= size) { + TranslatedAddressRange ranges; + ranges.push_back(blockSpan.subspan(rangeOffset, size)); + return ranges; + } + + return TranslateRangeImpl(virt, size, cpuAccessCallback); + } + void Read(u8 *destination, VaType virt, VaType size, std::function)> cpuAccessCallback = {}); @@ -203,6 +241,18 @@ namespace skyline { } void Copy(VaType dst, VaType src, VaType size, std::function)> cpuAccessCallback = {}); + + void Map(VaType virt, u8 *phys, VaType size, MemoryManagerBlockInfo extraInfo = {}) { + std::scoped_lock lock(this->blockMutex); + blockSegmentTable.Set(virt, virt + size, {virt, phys, size, extraInfo}); + this->MapLocked(virt, phys, size, extraInfo); + } + + void Unmap(VaType virt, VaType size) { + std::scoped_lock lock(this->blockMutex); + blockSegmentTable.Set(virt, virt + size, {}); + this->UnmapLocked(virt, size); + } }; /** diff --git a/app/src/main/cpp/skyline/common/address_space.inc b/app/src/main/cpp/skyline/common/address_space.inc index 00607a04..56179554 100644 --- a/app/src/main/cpp/skyline/common/address_space.inc +++ b/app/src/main/cpp/skyline/common/address_space.inc @@ -7,7 +7,7 @@ #define MAP_MEMBER(returnType) template requires AddressSpaceValid returnType FlatAddressSpaceMap -#define MM_MEMBER(returnType) template requires AddressSpaceValid returnType FlatMemoryManager +#define MM_MEMBER(returnType) template requires AddressSpaceValid returnType FlatMemoryManager #define ALLOC_MEMBER(returnType) template requires AddressSpaceValid returnType FlatAllocator @@ -223,20 +223,11 @@ namespace skyline { unmapCallback(virt, size); } - MM_MEMBER()::FlatMemoryManager() { - sparseMap = static_cast(mmap(0, SparseMapSize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); - if (!sparseMap) - throw exception("Failed to mmap sparse map!"); - } - MM_MEMBER()::~FlatMemoryManager() { - munmap(sparseMap, SparseMapSize); - } - - MM_MEMBER(TranslatedAddressRange)::TranslateRange(VaType virt, VaType size, std::function)> cpuAccessCallback) { + MM_MEMBER(TranslatedAddressRange)::TranslateRangeImpl(VaType virt, VaType size, std::function)> cpuAccessCallback) { TRACE_EVENT("containers", "FlatMemoryManager::TranslateRange"); - std::scoped_lock lock(this->blockMutex); + TranslatedAddressRange ranges; auto successor{std::upper_bound(this->blocks.begin(), this->blocks.end(), virt, [] (auto virt, const auto &block) { return virt < block.virt; @@ -247,7 +238,6 @@ namespace skyline { u8 *blockPhys{predecessor->phys + (virt - predecessor->virt)}; VaType blockSize{std::min(successor->virt - virt, size)}; - TranslatedAddressRange ranges; while (size) { // Return a zeroed out map to emulate sparse mappings @@ -276,6 +266,16 @@ namespace skyline { return ranges; } + MM_MEMBER()::FlatMemoryManager() { + sparseMap = static_cast(mmap(0, SparseMapSize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); + if (!sparseMap) + throw exception("Failed to mmap sparse map!"); + } + + MM_MEMBER()::~FlatMemoryManager() { + munmap(sparseMap, SparseMapSize); + } + MM_MEMBER(void)::Read(u8 *destination, VaType virt, VaType size, std::function)> cpuAccessCallback) { TRACE_EVENT("containers", "FlatMemoryManager::Read"); @@ -491,10 +491,12 @@ namespace skyline { } ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) { - this->Map(virt, true, size); + std::scoped_lock lock(this->blockMutex); + this->MapLocked(virt, true, size, {}); } ALLOC_MEMBER(void)::Free(VaType virt, VaType size) { - this->Unmap(virt, size); + std::scoped_lock lock(this->blockMutex); + this->UnmapLocked(virt, size); } } diff --git a/app/src/main/cpp/skyline/soc/gm20b/gmmu.cpp b/app/src/main/cpp/skyline/soc/gm20b/gmmu.cpp index 778ef733..9ad97f13 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/gmmu.cpp +++ b/app/src/main/cpp/skyline/soc/gm20b/gmmu.cpp @@ -6,5 +6,5 @@ namespace skyline { template class FlatAddressSpaceMap; - template class FlatMemoryManager; + template class FlatMemoryManager; } diff --git a/app/src/main/cpp/skyline/soc/gm20b/gmmu.h b/app/src/main/cpp/skyline/soc/gm20b/gmmu.h index 8e874398..c0ab8d20 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/gmmu.h +++ b/app/src/main/cpp/skyline/soc/gm20b/gmmu.h @@ -3,17 +3,23 @@ #pragma once +#include #include namespace skyline::soc::gm20b { static constexpr u8 GmmuAddressSpaceBits{40}; //!< The size of the GMMU AS in bits + static constexpr size_t GmmuSmallPageSize{0x1000}; // 4KiB + static constexpr size_t GmmuSmallPageSizeBits{std::countr_zero(GmmuSmallPageSize)}; + static constexpr size_t GmmuMinBigPageSize{0x20000}; // 128KiB + static constexpr size_t GmmuMinBigPageSizeBits{std::countr_zero(GmmuMinBigPageSize)}; + /** * @brief The GMMU (Graphics Memory Management Unit) class handles mapping between a Maxwell GPU virtual address space and an application's address space and is meant to roughly emulate the GMMU on the X1 * @note This is not accurate to the X1 as it would have an SMMU between the GMMU and physical memory but we don't need to emulate this abstraction * @note The GMMU is implemented entirely as a template specialization over FlatMemoryManager */ - using GMMU = FlatMemoryManager; + using GMMU = FlatMemoryManager; struct AddressSpaceContext { GMMU gmmu; diff --git a/app/src/main/cpp/skyline/soc/smmu.cpp b/app/src/main/cpp/skyline/soc/smmu.cpp index 80015136..da83759c 100644 --- a/app/src/main/cpp/skyline/soc/smmu.cpp +++ b/app/src/main/cpp/skyline/soc/smmu.cpp @@ -6,5 +6,5 @@ namespace skyline { template class FlatAddressSpaceMap; - template class FlatMemoryManager; + template class FlatMemoryManager; } diff --git a/app/src/main/cpp/skyline/soc/smmu.h b/app/src/main/cpp/skyline/soc/smmu.h index e0ae4e9b..61a52e79 100644 --- a/app/src/main/cpp/skyline/soc/smmu.h +++ b/app/src/main/cpp/skyline/soc/smmu.h @@ -7,10 +7,16 @@ namespace skyline::soc { static constexpr u8 SmmuAddressSpaceBits{32}; //!< The size of the SMMU AS in bits + constexpr size_t SmmuPageSize{0x1000}; // 4KiB + constexpr size_t SmmuPageSizeBits{std::countr_zero(SmmuPageSize)}; + constexpr size_t SmmuL2PageSize{0x20000}; // 128KiB - not actually a thing in HW but needed for segment table + constexpr size_t SmmuL2PageSizeBits{std::countr_zero(SmmuL2PageSize)}; + + /** * @brief The SMMU (System Memory Management Unit) class handles mapping between the host1x peripheral virtual address space and an application's address space * @note The SMMU is implemented entirely as a template specialization over FlatMemoryManager */ - using SMMU = FlatMemoryManager; + using SMMU = FlatMemoryManager; }