skyline/app/src/main/cpp/skyline/kernel/memory.h

338 lines
15 KiB
C
Raw Normal View History

// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
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<BlockDescriptor> 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<ChunkDescriptor> 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<DescriptorPack> 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();
};
}
}