// SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once #include namespace skyline { namespace memory { /** * @brief The Permission struct holds the permission of a particular chunk of memory */ struct Permission { /** * @brief This constructor initializes all permissions to false */ constexpr Permission() : r(), w(), x() {} /** * @param read If memory has read permission * @param write If memory has write permission * @param execute If memory has execute permission */ constexpr Permission(bool read, bool write, bool execute) : r(read), w(write), x(execute) {} /** * @brief Equality operator between two Permission objects */ inline bool operator==(const Permission &rhs) const { return (this->r == rhs.r && this->w == rhs.w && this->x == rhs.x); } /** * @brief Inequality operator between two Permission objects */ inline bool operator!=(const Permission &rhs) const { return !operator==(rhs); } /** * @return The value of the permission struct in Linux format */ constexpr int Get() const { int perm{}; if (r) perm |= PROT_READ; if (w) perm |= PROT_WRITE; if (x) perm |= PROT_EXEC; return perm; } bool r; //!< The permission to read bool w; //!< The permission to write bool x; //!< The permission to execute }; /** * @brief This holds certain attributes of a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryAttribute */ union MemoryAttribute { struct { bool isBorrowed : 1; //!< This is required for async IPC user buffers bool isIpcLocked : 1; //!< True when IpcRefCount > 0 bool isDeviceShared : 1; //!< True when DeviceRefCount > 0 bool isUncached : 1; //!< This is used to disable memory caching to share memory with the GPU }; u32 value; }; /** * @brief This contains information about a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryInfo */ struct MemoryInfo { u64 address; //!< The base address of the mapping u64 size; //!< The size of the mapping u32 type; //!< The MemoryType of the mapping u32 attributes; //!< The attributes of the mapping u32 permissions; //!< The permissions of the mapping u32 ipcRefCount; //!< The IPC reference count (This is always 0) u32 deviceRefCount; //!< The device reference count (This is always 0) u32 _pad0_; }; static_assert(sizeof(MemoryInfo) == 0x28); /** * @brief These are specific markers for the type of a memory region (https://switchbrew.org/wiki/SVC#MemoryType) */ enum class MemoryType : u8 { Unmapped = 0x0, Io = 0x1, Normal = 0x2, CodeStatic = 0x3, CodeMutable = 0x4, Heap = 0x5, SharedMemory = 0x6, Alias = 0x7, ModuleCodeStatic = 0x8, ModuleCodeMutable = 0x9, Ipc = 0xA, Stack = 0xB, ThreadLocal = 0xC, TransferMemoryIsolated = 0xD, TransferMemory = 0xE, ProcessMemory = 0xF, Reserved = 0x10, NonSecureIpc = 0x11, NonDeviceIpc = 0x12, KernelStack = 0x13, CodeReadOnly = 0x14, CodeWritable = 0x15 }; /** * @brief This structure is used to hold the state of a certain block of memory (https://switchbrew.org/wiki/SVC#MemoryState) */ union MemoryState { constexpr MemoryState(const u32 value) : value(value) {} constexpr MemoryState() : value(0) {} struct { MemoryType type; //!< The MemoryType of this memory block bool permissionChangeAllowed : 1; //!< If the application can use svcSetMemoryPermission on this block bool forceReadWritableByDebugSyscalls : 1; //!< If the application can use svcWriteDebugProcessMemory on this block bool ipcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=0 bool nonDeviceIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=3 bool nonSecureIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=1 bool _pad0_ : 1; bool processPermissionChangeAllowed : 1; //!< If the application can use svcSetProcessMemoryPermission on this block bool mapAllowed : 1; //!< If the application can use svcMapMemory on this block bool unmapProcessCodeMemoryAllowed : 1; //!< If the application can use svcUnmapProcessCodeMemory on this block bool transferMemoryAllowed : 1; //!< If the application can use svcCreateTransferMemory on this block bool queryPhysicalAddressAllowed : 1; //!< If the application can use svcQueryPhysicalAddress on this block bool mapDeviceAllowed : 1; //!< If the application can use svcMapDeviceAddressSpace or svcMapDeviceAddressSpaceByForce on this block bool mapDeviceAlignedAllowed : 1; //!< If the application can use svcMapDeviceAddressSpaceAligned on this block bool ipcBufferAllowed : 1; //!< If the application can use this block with svcSendSyncRequestWithUserBuffer bool isReferenceCounted : 1; //!< If the physical memory blocks backing this region are reference counted bool mapProcessAllowed : 1; //!< If the application can use svcMapProcessMemory on this block bool attributeChangeAllowed : 1; //!< If the application can use svcSetMemoryAttribute on this block bool codeMemoryAllowed : 1; //!< If the application can use svcCreateCodeMemory on this block }; u32 value; }; static_assert(sizeof(MemoryState) == sizeof(u32)); /** * @brief The preset states that different regions are set to (https://switchbrew.org/wiki/SVC#MemoryType) */ namespace states { constexpr MemoryState Unmapped{0x00000000}; constexpr MemoryState Io{0x00002001}; constexpr MemoryState CodeStatic{0x00DC7E03}; constexpr MemoryState CodeMutable{0x03FEBD04}; constexpr MemoryState Heap{0x037EBD05}; constexpr MemoryState SharedMemory{0x00402006}; constexpr MemoryState Alias{0x00482907}; constexpr MemoryState AliasCode{0x00DD7E08}; constexpr MemoryState AliasCodeData{0x03FFBD09}; constexpr MemoryState Ipc{0x005C3C0A}; constexpr MemoryState Stack{0x005C3C0B}; constexpr MemoryState ThreadLocal{0x0040200C}; constexpr MemoryState TransferMemoryIsolated{0x015C3C0D}; constexpr MemoryState TransferMemory{0x005C380E}; constexpr MemoryState SharedCode{0x0040380F}; constexpr MemoryState Reserved{0x00000010}; constexpr MemoryState NonSecureIpc{0x005C3811}; constexpr MemoryState NonDeviceIpc{0x004C2812}; constexpr MemoryState KernelStack{0x00002013}; constexpr MemoryState CodeReadOnly{0x00402214}; constexpr MemoryState CodeWritable{0x00402015}; }; /** * @brief This struct is used to hold the location and size of a memory region */ struct Region { u64 address; //!< The base address of the region u64 size; //!< The size of the region in bytes /** * @brief Checks if the specified address is within the region * @param address The address to check * @return If the address is inside the region */ inline bool IsInside(u64 address) { return (this->address <= address) && ((this->address + this->size) > address); } }; /** * @brief The type of the address space used by an application */ enum class AddressSpaceType { AddressSpace32Bit, //!< 32-bit address space used by 32-bit applications AddressSpace36Bit, //!< 36-bit address space used by 64-bit applications before 2.0.0 AddressSpace39Bit, //!< 39-bit address space used by 64-bit applications after 2.0.0 }; } namespace loader { class NroLoader; class NsoLoader; class NcaLoader; } namespace kernel { namespace type { class KPrivateMemory; class KSharedMemory; class KTransferMemory; } namespace svc { void SetMemoryAttribute(DeviceState &state); void MapMemory(DeviceState &state); } /** * @brief This describes a single block of memory and all of it's individual attributes */ struct BlockDescriptor { u64 address; //!< The address of the current block u64 size; //!< The size of the current block in bytes memory::Permission permission; //!< The permissions applied to the current block memory::MemoryAttribute attributes; //!< The MemoryAttribute for the current block }; /** * @brief This describes a single chunk of memory, this is owned by a memory backing */ struct ChunkDescriptor { u64 address; //!< The address of the current chunk u64 size; //!< The size of the current chunk in bytes u64 host; //!< The address of the chunk in the host memory::MemoryState state; //!< The MemoryState for the current block std::vector blockList; //!< This vector holds the block descriptors for all the children blocks of this Chunk }; /** * @brief This contains both of the descriptors for a specific address */ struct DescriptorPack { const BlockDescriptor block; //!< The block descriptor at the address const ChunkDescriptor chunk; //!< The chunk descriptor at the address }; /** * @brief The MemoryManager class handles the memory map and the memory regions of the process */ class MemoryManager { private: const DeviceState &state; //!< The state of the device std::vector chunkList; //!< This vector holds all the chunk descriptors /** * @param address The address to find a chunk at * @return A pointer to the ChunkDescriptor or nullptr in case chunk was not found */ ChunkDescriptor *GetChunk(u64 address); /** * @param address The address to find a block at * @return A pointer to the BlockDescriptor or nullptr in case chunk was not found */ BlockDescriptor *GetBlock(u64 address, ChunkDescriptor *chunk = nullptr); /** * @brief Inserts a chunk into the memory map * @param chunk The chunk to insert */ void InsertChunk(const ChunkDescriptor &chunk); /** * @brief Deletes a chunk located at the address from the memory map * @param address The address of the chunk to delete */ void DeleteChunk(u64 address); /** * @brief Resize the specified chunk to the specified size * @param chunk The chunk to resize * @param size The new size of the chunk */ static void ResizeChunk(ChunkDescriptor *chunk, size_t size); /** * @brief Insert a block into a chunk * @param chunk The chunk to insert the block into * @param block The block to insert into the chunk */ static void InsertBlock(ChunkDescriptor *chunk, BlockDescriptor block); /** * @brief This initializes all of the regions in the address space * @param address The starting address of the code region * @param size The size of the code region * @param type The type of the address space */ void InitializeRegions(u64 address, u64 size, memory::AddressSpaceType type); public: friend class type::KPrivateMemory; friend class type::KSharedMemory; friend class type::KTransferMemory; friend class type::KProcess; friend class loader::NroLoader; friend class loader::NsoLoader; friend class loader::NcaLoader; friend void svc::SetMemoryAttribute(DeviceState &state); friend void svc::MapMemory(skyline::DeviceState &state); memory::Region addressSpace{}; //!< The Region object for the entire address space memory::Region base{}; //!< The Region object for the entire address space accessible to the application memory::Region code{}; //!< The Region object for the code memory region memory::Region alias{}; //!< The Region object for the alias memory region memory::Region heap{}; //!< The Region object for the heap memory region memory::Region stack{}; //!< The Region object for the stack memory region memory::Region tlsIo{}; //!< The Region object for the TLS/IO memory region MemoryManager(const DeviceState &state); /** * @param address The address to query in the memory map * @param requireMapped This specifies if only mapped regions should be returned otherwise unmapped but valid regions will also be returned * @return A DescriptorPack retrieved from the memory map */ std::optional Get(u64 address, bool requireMapped = true); /** * @brief The total amount of space in bytes occupied by all memory mappings * @return The cumulative size of all memory mappings in bytes */ size_t GetProgramSize(); }; } }