diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 7e6f4752..a4a819fd 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -13,12 +13,9 @@
-
+
-
-
-
@@ -79,8 +76,12 @@
+
+
+
+
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index de973ce5..7e2111e9 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -36,6 +36,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/gpu/devices/nvhost_as_gpu.cpp
${source_DIR}/skyline/os.cpp
${source_DIR}/skyline/loader/nro.cpp
+ ${source_DIR}/skyline/kernel/memory.cpp
${source_DIR}/skyline/kernel/ipc.cpp
${source_DIR}/skyline/kernel/svc.cpp
${source_DIR}/skyline/kernel/types/KProcess.cpp
diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h
index 976e45a3..ad85f401 100644
--- a/app/src/main/cpp/skyline/common.h
+++ b/app/src/main/cpp/skyline/common.h
@@ -23,11 +23,8 @@ namespace skyline {
namespace constant {
// Memory
- constexpr u64 BaseAddr = 0x8000000; //!< The address space base
- constexpr u64 MapAddr = BaseAddr + 0x80000000; //!< The address of the map region
- constexpr u64 HeapAddr = MapAddr + 0x1000000000; //!< The address of the heap region
+ constexpr u64 BaseAddress = 0x8000000; //!< The address space base
constexpr u64 BaseEnd = 0x7FFFFFFFFF; //!< The end of the address space
- constexpr u64 MapSize = 0x1000000000; //!< The size of the map region
constexpr u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM
constexpr size_t DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB
constexpr size_t HeapSizeDiv = 0x200000; //!< The amount heap size has to be divisible by
@@ -38,8 +35,6 @@ namespace skyline {
constexpr u32 NroMagic = 0x304F524E; //!< "NRO0" in reverse, this is written at the start of every NRO file
// NCE
constexpr u8 NumRegs = 30; //!< The amount of registers that ARMv8 has
- constexpr u16 SvcLast = 0x7F; //!< The index of the last SVC
- constexpr u16 BrkRdy = 0xFF; //!< This is reserved for our kernel's to know when a process/thread is ready
constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS
constexpr u32 CntfrqEl0 = 0x5F00; //!< ID of CNTFRQ_EL0 in MRS
constexpr u32 TegraX1Freq = 0x124F800; //!< The clock frequency of the Tegra X1 (19.2 MHz)
@@ -53,7 +48,6 @@ namespace skyline {
constexpr std::pair PriorityAn = {19, -8}; //!< The range of priority for Android, taken from https://medium.com/mindorks/exploring-android-thread-priority-5d0542eebbd1
constexpr std::pair PriorityNin = {0, 63}; //!< The range of priority for the Nintendo Switch
constexpr u32 MtxOwnerMask = 0xBFFFFFFF; //!< The mask of values which contain the owner of a mutex
- constexpr u32 CheckInterval = 10000000; //!< The amount of cycles to wait between checking if the guest thread is dead
// IPC
constexpr size_t TlsIpcSize = 0x100; //!< The size of the IPC command buffer in a TLS slot
constexpr u8 PortSize = 0x8; //!< The size of a port name string
@@ -78,11 +72,14 @@ namespace skyline {
constexpr u32 ServiceNotReg = 0xE15; //!< "Service not registered"
constexpr u32 InvSize = 0xCA01; //!< "Invalid size"
constexpr u32 InvAddress = 0xCC01; //!< "Invalid address"
- constexpr u32 InvPermission = 0xE001; //!< "Invalid Permission"
+ constexpr u32 InvState = 0xD401; //!< "Invalid MemoryState"
+ constexpr u32 InvPermission = 0xD801; //!< "Invalid Permission"
+ constexpr u32 InvMemRange = 0xD801; //!< "Invalid Memory Range"
+ constexpr u32 InvPriority = 0xE001; //!< "Invalid Priority"
constexpr u32 InvHandle = 0xE401; //!< "Invalid handle"
constexpr u32 InvCombination = 0xE801; //!< "Invalid combination"
- constexpr u32 MaxHandles = 0xEE01; //!< "Too many handles"
constexpr u32 Timeout = 0xEA01; //!< "Timeout"
+ constexpr u32 MaxHandles = 0xEE01; //!< "Too many handles"
constexpr u32 NotFound = 0xF201; //!< "Not found"
constexpr u32 Unimpl = 0x177202; //!< "Unimplemented behaviour"
}
@@ -98,6 +95,51 @@ namespace skyline {
NSP, //!< The NSP format from "nspwn" exploit: https://switchbrew.org/wiki/Switch_System_Flaws
};
+ namespace utils {
+ /**
+ * @brief Returns the current time in nanoseconds
+ * @return The current time in nanoseconds
+ */
+ inline u64 GetCurrTimeNs() {
+ return static_cast(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
+ }
+
+ /**
+ * @brief Aligns up a value to a multiple of two
+ * @tparam Type The type of the values
+ * @param value The value to round up
+ * @param multiple The multiple to round up to (Should be a multiple of 2)
+ * @tparam TypeVal The type of the value
+ * @tparam TypeMul The type of the multiple
+ * @return The aligned value
+ */
+ template
+ inline TypeVal AlignUp(TypeVal value, TypeMul multiple) {
+ static_assert(std::is_integral() && std::is_integral());
+ multiple--;
+ return (value + multiple) & ~multiple;
+ }
+
+ /**
+ * @brief Aligns down a value to a multiple of two
+ * @param value The value to round down
+ * @param multiple The multiple to round down to (Should be a multiple of 2)
+ * @tparam TypeVal The type of the value
+ * @tparam TypeMul The type of the multiple
+ * @return The aligned value
+ */
+ template
+ inline TypeVal AlignDown(TypeVal value, TypeMul multiple) {
+ static_assert(std::is_integral() && std::is_integral());
+ multiple--;
+ return value & ~multiple;
+ }
+
+ inline bool PageAligned(u64 address) {
+ return !(address & (PAGE_SIZE - 1U));
+ }
+ }
+
/**
* @brief The Mutex class is a wrapper around an atomic bool used for synchronization
*/
@@ -293,14 +335,6 @@ namespace skyline {
inline exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, args...)) {}
};
- /**
- * @brief Returns the current time in nanoseconds
- * @return The current time in nanoseconds
- */
- inline u64 GetCurrTimeNs() {
- return static_cast(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
- }
-
class NCE;
class JvmManager;
namespace gpu {
diff --git a/app/src/main/cpp/skyline/kernel/ipc.cpp b/app/src/main/cpp/skyline/kernel/ipc.cpp
index 4012c058..000e3851 100644
--- a/app/src/main/cpp/skyline/kernel/ipc.cpp
+++ b/app/src/main/cpp/skyline/kernel/ipc.cpp
@@ -32,7 +32,7 @@ namespace skyline::kernel::ipc {
}
}
- for (uint index = 0; header->Xno > index; index++) {
+ for (uint index = 0; header->xNo > index; index++) {
auto bufX = reinterpret_cast(currPtr);
if (bufX->Address()) {
inputBuf.emplace_back(bufX);
@@ -41,7 +41,7 @@ namespace skyline::kernel::ipc {
currPtr += sizeof(BufferDescriptorX);
}
- for (uint index = 0; header->Ano > index; index++) {
+ for (uint index = 0; header->aNo > index; index++) {
auto bufA = reinterpret_cast(currPtr);
if (bufA->Address()) {
inputBuf.emplace_back(bufA);
@@ -50,7 +50,7 @@ namespace skyline::kernel::ipc {
currPtr += sizeof(BufferDescriptorABW);
}
- for (uint index = 0; header->Bno > index; index++) {
+ for (uint index = 0; header->bNo > index; index++) {
auto bufB = reinterpret_cast(currPtr);
if (bufB->Address()) {
outputBuf.emplace_back(bufB);
@@ -59,7 +59,7 @@ namespace skyline::kernel::ipc {
currPtr += sizeof(BufferDescriptorABW);
}
- for (uint index = 0; header->Wno > index; index++) {
+ for (uint index = 0; header->wNo > index; index++) {
auto bufW = reinterpret_cast(currPtr);
if (bufW->Address()) {
inputBuf.emplace_back(bufW, IpcBufferType::W);
diff --git a/app/src/main/cpp/skyline/kernel/ipc.h b/app/src/main/cpp/skyline/kernel/ipc.h
index 3cee270b..6f6125b8 100644
--- a/app/src/main/cpp/skyline/kernel/ipc.h
+++ b/app/src/main/cpp/skyline/kernel/ipc.h
@@ -23,10 +23,10 @@ namespace skyline::kernel::ipc {
*/
struct CommandHeader {
CommandType type : 16;
- u8 Xno : 4;
- u8 Ano : 4;
- u8 Bno : 4;
- u8 Wno : 4;
+ u8 xNo : 4;
+ u8 aNo : 4;
+ u8 bNo : 4;
+ u8 wNo : 4;
u32 rawSize : 10;
BufferCFlag cFlag : 4;
u32 : 17;
diff --git a/app/src/main/cpp/skyline/kernel/memory.cpp b/app/src/main/cpp/skyline/kernel/memory.cpp
new file mode 100644
index 00000000..8e379923
--- /dev/null
+++ b/app/src/main/cpp/skyline/kernel/memory.cpp
@@ -0,0 +1,158 @@
+#include "memory.h"
+#include "types/KProcess.h"
+
+namespace skyline::kernel {
+ ChunkDescriptor *MemoryManager::GetChunk(u64 address) {
+ for (auto &chunk : chunkList)
+ if (chunk.address <= address && (chunk.address + chunk.size) >= address)
+ return &chunk;
+ return nullptr;
+ }
+
+ BlockDescriptor *MemoryManager::GetBlock(u64 address) {
+ auto chunk = GetChunk(address);
+ if (chunk)
+ for (auto &block : chunk->blockList)
+ if (block.address <= address && (block.address + block.size) >= address)
+ return █
+ return nullptr;
+ }
+
+ void MemoryManager::InsertChunk(const ChunkDescriptor &chunk) {
+ auto it = chunkList.begin();
+ if (chunkList.empty() || it->address > chunk.address)
+ chunkList.push_front(chunk);
+ else {
+ auto prevIt = it;
+ while (true) {
+ if (it == chunkList.end() || (prevIt->address < chunk.address && it->address > chunk.address)) {
+ if (prevIt->address + prevIt->size > chunk.address)
+ throw exception("InsertChunk: Descriptors are colliding: 0x{:X} and 0x{:X}", prevIt->address, chunk.address);
+ chunkList.insert_after(prevIt, chunk);
+ break;
+ }
+ prevIt = it++;
+ }
+ }
+ }
+
+ void MemoryManager::DeleteChunk(u64 address) {
+ chunkList.remove_if([address](const ChunkDescriptor &chunk) {
+ return chunk.address <= address && (chunk.address + chunk.size) >= address;
+ });
+ }
+
+ void MemoryManager::ResizeChunk(ChunkDescriptor *chunk, size_t size) {
+ if (std::next(chunk->blockList.begin()) == chunk->blockList.end())
+ chunk->blockList.begin()->size = size;
+ else if (size > chunk->size) {
+ auto end = chunk->blockList.begin();
+ for (; std::next(end) != chunk->blockList.end(); end++);
+ auto baseBlock = (*chunk->blockList.begin());
+ BlockDescriptor block{
+ .address = (end->address + end->size),
+ .size = (chunk->address + size) - (end->address + end->size),
+ .permission = baseBlock.permission,
+ .attributes = baseBlock.attributes,
+ };
+ chunk->blockList.insert_after(end, block);
+ } else if (chunk->size < size) {
+ auto endAddress = chunk->address + size;
+ chunk->blockList.remove_if([endAddress](const BlockDescriptor &block) {
+ return block.address > endAddress;
+ });
+ auto end = chunk->blockList.begin();
+ for (; std::next(end) != chunk->blockList.end(); end++);
+ end->size = endAddress - end->address;
+ }
+ chunk->size = size;
+ }
+
+ void MemoryManager::InsertBlock(ChunkDescriptor *chunk, BlockDescriptor block) {
+ for (auto iter = chunk->blockList.begin(); iter != chunk->blockList.end(); iter++) {
+ if (iter->address <= block.address && (iter->address + iter->size) >= block.address) {
+ if (iter->address == block.address && iter->size == block.size) {
+ iter->attributes = block.attributes;
+ iter->permission = block.permission;
+ } else {
+ auto endBlock = *iter;
+ endBlock.address = (block.address + block.size);
+ endBlock.size = (iter->address + iter->size) - endBlock.address;
+ iter->size = (iter->address - block.address);
+ chunk->blockList.insert_after(iter, {block, endBlock});
+ }
+ break;
+ }
+ }
+ }
+
+ void MemoryManager::InitializeRegions(u64 address, u64 size, const memory::AddressSpaceType type) {
+ switch(type) {
+ case memory::AddressSpaceType::AddressSpace32Bit:
+ throw exception("32-bit address spaces are not supported");
+ case memory::AddressSpaceType::AddressSpace36Bit: {
+ code.address = 0x8000000;
+ code.size = 0x78000000;
+ if(code.address > address || (code.size - (address - code.address)) < size)
+ throw exception("Code mapping larger than 36-bit code region");
+ alias.address = code.address + code.size;
+ alias.size = 0x180000000;
+ stack.address = alias.address;
+ stack.size = alias.size;
+ heap.address = alias.address + alias.size;
+ heap.size = 0x180000000;
+ tlsIo.address = heap.address + heap.size;
+ tlsIo.size = 0x1000000000;
+ break;
+ }
+ case memory::AddressSpaceType::AddressSpace39Bit: {
+ code.address = utils::AlignDown(address, 0x200000);
+ code.size = utils::AlignUp(address + size, 0x200000) - code.address;
+ alias.address = code.address + code.size;
+ alias.size = 0x1000000000;
+ heap.address = alias.address + alias.size;
+ heap.size = 0x180000000;
+ stack.address = heap.address + heap.size;
+ stack.size = 0x80000000;
+ tlsIo.address = stack.address + stack.size;
+ tlsIo.size = 0x1000000000;
+ break;
+ }
+ }
+ state.logger->Debug("Region Map:\nCode Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nAlias Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nHeap Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nStack Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nTLS/IO Region: 0x{:X} - 0x{:X} (Size: 0x{:X})", code.address, code.address + code.size, code.size, alias.address, alias.address + alias.size, alias.size, heap.address, heap
+ .address + heap.size, heap.size, stack.address, stack.address + stack.size, stack.size, tlsIo.address, tlsIo.address + tlsIo.size, tlsIo.size);
+ }
+
+ MemoryManager::MemoryManager(const DeviceState &state) : state(state) {}
+
+ std::optional MemoryManager::Get(u64 address) {
+ auto chunk = GetChunk(address);
+ if (chunk)
+ for (auto &block : chunk->blockList)
+ if (block.address <= address && (block.address + block.size) >= address)
+ return DescriptorPack{block, *chunk};
+ return std::nullopt;
+ }
+
+ memory::Region MemoryManager::GetRegion(memory::Regions region) {
+ switch(region) {
+ case memory::Regions::Code:
+ return code;
+ case memory::Regions::Alias:
+ return alias;
+ case memory::Regions::Heap:
+ return heap;
+ case memory::Regions::Stack:
+ return stack;
+ case memory::Regions::TlsIo:
+ return tlsIo;
+ }
+ }
+
+ size_t MemoryManager::GetProgramSize() {
+ size_t size = 0;
+ for (const auto &chunk : chunkList)
+ size += chunk.size;
+ return size;
+ }
+}
diff --git a/app/src/main/cpp/skyline/kernel/memory.h b/app/src/main/cpp/skyline/kernel/memory.h
new file mode 100644
index 00000000..9f6c54cb
--- /dev/null
+++ b/app/src/main/cpp/skyline/kernel/memory.h
@@ -0,0 +1,357 @@
+#pragma once
+
+#include
+#include "types/KObject.h"
+#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
+ */
+ Permission() {
+ r = 0;
+ w = 0;
+ x = 0;
+ };
+
+ /**
+ * @param read If memory has read permission
+ * @param write If memory has write permission
+ * @param execute If memory has execute permission
+ */
+ 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
+ */
+ int Get() const {
+ int perm = 0;
+ 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;
+ };
+
+ static_assert(sizeof(MemoryAttribute) == sizeof(u32));
+
+ /**
+ * @brief This contains information about a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryInfo
+ */
+ struct MemoryInfo {
+ u64 address;
+ u64 size;
+ u32 type;
+ MemoryAttribute attributes;
+ union {
+ u32 _pad0_;
+ struct {
+ bool r : 1, w : 1, x : 1;
+ };
+ };
+ u32 ipcRefCount;
+ u32 deviceRefCount;
+ u32 : 32;
+ };
+
+ 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 MemoryStates {
+ 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 enumerates all of the memory regions in the process address space
+ */
+ enum class Regions {
+ Code, //!< The code region contains all of the loaded in code
+ Alias, //!< The alias region is reserved for allocating thread stack before 2.0.0
+ Heap, //!< The heap region is reserved for heap allocations
+ Stack, //!< The stack region is reserved for allocating thread stack after 2.0.0
+ TlsIo, //!< The TLS/IO region is reserved for allocating TLS and Device MMIO
+ };
+
+ /**
+ * @brief This struct is used to hold the location and size of a memory region
+ */
+ struct Region {
+ Regions id; //!< The ID of the 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;
+ }
+
+ 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
+ memory::MemoryState state; //!< The MemoryState for the current block
+ std::forward_list blockList; //!< This linked list 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::forward_list chunkList; //!< This linked list holds all the chunk descriptors
+ memory::Region code{memory::Regions::Code}; //!< The Region object for the code memory region
+ memory::Region alias{memory::Regions::Alias}; //!< The Region object for the alias memory region
+ memory::Region heap{memory::Regions::Heap}; //!< The Region object for the heap memory region
+ memory::Region stack{memory::Regions::Stack}; //!< The Region object for the stack memory region
+ memory::Region tlsIo{memory::Regions::TlsIo}; //!< The Region object for the TLS/IO memory region
+
+ /**
+ * @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);
+
+ /**
+ * @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
+ */
+ 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, const 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 void svc::SetMemoryAttribute(DeviceState &state);
+ friend void svc::MapMemory(skyline::DeviceState &state);
+
+ MemoryManager(const DeviceState &state);
+
+ /**
+ * @param address The address to query in the memory map
+ * @return A DescriptorPack retrieved from the memory map
+ */
+ std::optional Get(u64 address);
+
+ /**
+ * @param region The region to retrieve
+ * @return A Region object for the specified region
+ */
+ memory::Region GetRegion(memory::Regions region);
+
+ /**
+ * @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();
+ };
+ }
+}
diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp
index a3afbe5e..64e41fc8 100644
--- a/app/src/main/cpp/skyline/kernel/svc.cpp
+++ b/app/src/main/cpp/skyline/kernel/svc.cpp
@@ -10,109 +10,119 @@ namespace skyline::kernel::svc {
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
return;
}
- std::shared_ptr heap;
- try {
- heap = state.process->memoryRegionMap.at(memory::Region::Heap);
- heap->Resize(size, true);
- } catch (const exception &) {
- state.logger->Warn("svcSetHeapSize: Falling back to recreating memory");
- state.process->UnmapPrivateRegion(memory::Region::Heap);
- heap = state.process->MapPrivateRegion(constant::HeapAddr, size, {true, true, false}, memory::Type::Heap, memory::Region::Heap).item;
- }
+ auto& heap = state.process->heap;
+ heap->Resize(size);
state.ctx->registers.w0 = constant::status::Success;
state.ctx->registers.x1 = heap->address;
state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size);
}
void SetMemoryAttribute(DeviceState &state) {
- const u64 addr = state.ctx->registers.x0;
- if ((addr & (PAGE_SIZE - 1U))) {
+ const u64 address = state.ctx->registers.x0;
+ if (!utils::PageAligned(address)) {
state.ctx->registers.w0 = constant::status::InvAddress;
- state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: {}", addr);
+ state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: 0x{:X}", address);
return;
}
const u64 size = state.ctx->registers.x1;
- if ((size & (PAGE_SIZE - 1U)) || !size) {
+ if (!utils::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize;
- state.logger->Warn("svcSetMemoryAttribute: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
+ state.logger->Warn("svcSetMemoryAttribute: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return;
}
- u32 mask = state.ctx->registers.w2;
- u32 value = state.ctx->registers.w3;
- u32 maskedValue = mask | value;
- if (maskedValue != mask) {
+ memory::MemoryAttribute mask{.value = state.ctx->registers.w2};
+ memory::MemoryAttribute value{.value = state.ctx->registers.w3};
+ u32 maskedValue = mask.value | value.value;
+ if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) {
state.ctx->registers.w0 = constant::status::InvCombination;
- state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask, value);
+ state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask.value, value.value);
return;
}
- memory::MemoryAttribute attribute = *reinterpret_cast(&maskedValue);
- bool found = false;
- for (const auto&[address, region] : state.process->memoryMap) {
- if (addr >= address && addr < (address + region->size)) {
- bool subFound = false;
- for (auto &subregion : region->regionInfoVec) {
- if ((address >= subregion.address) && (address < (subregion.address + subregion.size)))
- subregion.isUncached = attribute.isUncached;
- subFound = true;
- break;
- }
- if (!subFound)
- region->regionInfoVec.emplace_back(addr, size, static_cast(attribute.isUncached));
- found = true;
- break;
- }
- }
- if (!found) {
+ auto chunk = state.os->memory.GetChunk(address);
+ auto block = state.os->memory.GetBlock(address);
+ if (!chunk || !block) {
state.ctx->registers.w0 = constant::status::InvAddress;
- state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", addr);
+ state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", address);
return;
}
- state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", !attribute.isUncached, addr, size);
+ if(!chunk->state.AttributeChangeAllowed) {
+ state.ctx->registers.w0 = constant::status::InvState;
+ state.logger->Warn("svcSetMemoryAttribute: Attribute change not allowed for chunk: 0x{:X}", address);
+ return;
+ }
+ block->attributes.isUncached = value.isUncached;
+ MemoryManager::InsertBlock(chunk, *block);
+ state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", !block->attributes.isUncached, address, size);
+ state.ctx->registers.w0 = constant::status::Success;
+ }
+
+ void MapMemory(DeviceState &state) {
+ const u64 destination = state.ctx->registers.x0;
+ const u64 source = state.ctx->registers.x1;
+ const u64 size = state.ctx->registers.x2;
+ if(!utils::PageAligned(destination) || !utils::PageAligned(source)) {
+ state.ctx->registers.w0 = constant::status::InvAddress;
+ state.logger->Warn("svcMapMemory: Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
+ return;
+ }
+ if(!utils::PageAligned(size)) {
+ state.ctx->registers.w0 = constant::status::InvSize;
+ state.logger->Warn("svcMapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
+ return;
+ }
+ auto stack = state.os->memory.GetRegion(memory::Regions::Stack);
+ if(!stack.IsInside(destination)) {
+ state.ctx->registers.w0 = constant::status::InvMemRange;
+ state.logger->Warn("svcMapMemory: Addresses not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
+ return;
+ }
+ auto descriptor = state.os->memory.Get(source);
+ if(!descriptor) {
+ state.ctx->registers.w0 = constant::status::InvAddress;
+ state.logger->Warn("svcMapMemory: Source has no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
+ return;
+ }
+ if(!descriptor->chunk.state.MapAllowed) {
+ state.ctx->registers.w0 = constant::status::InvState;
+ state.logger->Warn("svcMapMemory: Source doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, descriptor->chunk.state.value);
+ return;
+ }
+ state.process->NewHandle(destination, size, memory::Permission{true, true, true}, memory::MemoryStates::Stack);
+ state.process->CopyMemory(source, destination, size);
+ auto object = state.process->GetMemoryObject(source);
+ if(!object)
+ throw exception("svcMapMemory: Cannot find memory object in handle table for address 0x{:X}", source);
+ object->UpdatePermission(source, size, {false, false, false});
+ state.logger->Debug("svcMapMemory: Mapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size);
state.ctx->registers.w0 = constant::status::Success;
}
void QueryMemory(DeviceState &state) {
+ u64 address = state.ctx->registers.x2;
memory::MemoryInfo memInfo{};
- u64 addr = state.ctx->registers.x2 & ~(PAGE_SIZE - 1);
- bool found = false;
- for (const auto&[address, region] : state.process->memoryMap) {
- if (addr >= address && addr < (address + region->size)) {
- memInfo = region->GetInfo(addr);
- found = true;
- break;
- }
+ auto descriptor = state.os->memory.Get(address);
+ if (descriptor) {
+ memInfo = {
+ .address = descriptor->block.address,
+ .size = descriptor->block.size,
+ .type = static_cast(descriptor->chunk.state.type),
+ .attributes = descriptor->block.attributes,
+ .r = descriptor->block.permission.r,
+ .w = descriptor->block.permission.w,
+ .x = descriptor->block.permission.x,
+ .deviceRefCount = 0,
+ .ipcRefCount = 0,
+ };
+ } else {
+ memInfo = {
+ .address = constant::BaseEnd,
+ .size = ~(constant::BaseEnd - 1),
+ .type = static_cast(memory::MemoryType::Unmapped)
+ };
+ state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", address);
}
- if (!found) {
- for (const auto &object : state.process->handleTable) {
- if (object.second->objectType == type::KType::KSharedMemory) {
- const auto &mem = state.process->GetHandle(object.first);
- if (mem->guest.valid()) {
- if (addr >= mem->guest.address && addr < (mem->guest.address + mem->guest.size)) {
- memInfo = mem->GetInfo();
- found = true;
- break;
- }
- }
- } else if (object.second->objectType == type::KType::KTransferMemory) {
- const auto &mem = state.process->GetHandle(object.first);
- if (addr >= mem->cAddress && addr < (mem->cAddress + mem->cSize)) {
- memInfo = mem->GetInfo();
- found = true;
- break;
- }
- }
- }
- if (!found) {
- memInfo = {
- .baseAddress = constant::BaseAddr,
- .size = static_cast(constant::BaseEnd),
- .type = static_cast(memory::Type::Unmapped)
- };
- state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", addr);
- }
- }
- state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.baseAddress, memInfo.size, memInfo.type, static_cast(memInfo.memoryAttribute.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-");
- state.process->WriteMemory(memInfo, state.ctx->registers.x0);
+ state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.address, memInfo.size, memInfo.type, static_cast(memInfo.attributes.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-");
+ state.process->WriteMemory(memInfo, state.ctx->registers.x0);
state.ctx->registers.w0 = constant::status::Success;
}
@@ -122,17 +132,17 @@ namespace skyline::kernel::svc {
}
void CreateThread(DeviceState &state) {
- u64 entryAddr = state.ctx->registers.x1;
- u64 entryArg = state.ctx->registers.x2;
+ u64 entryAddress = state.ctx->registers.x1;
+ u64 entryArgument = state.ctx->registers.x2;
u64 stackTop = state.ctx->registers.x3;
u8 priority = static_cast(state.ctx->registers.w4);
- if ((priority < constant::PriorityNin.first) && (priority > constant::PriorityNin.second)) { // NOLINT(misc-redundant-expression)
+ if ((priority < constant::PriorityNin.first) || (priority > constant::PriorityNin.second)) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
return;
}
- auto thread = state.process->CreateThread(entryAddr, entryArg, stackTop, priority);
- state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, PID: {})", thread->handle, entryAddr, entryArg, stackTop, priority, thread->pid);
+ auto thread = state.process->CreateThread(entryAddress, entryArgument, stackTop, priority);
+ state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, PID: {})", thread->handle, entryAddress, entryArgument, stackTop, priority, thread->pid);
state.ctx->registers.w1 = thread->handle;
state.ctx->registers.w0 = constant::status::Success;
}
@@ -201,16 +211,16 @@ namespace skyline::kernel::svc {
void MapSharedMemory(DeviceState &state) {
try {
auto object = state.process->GetHandle(state.ctx->registers.w0);
- u64 addr = state.ctx->registers.x1;
- if ((addr & (PAGE_SIZE - 1U))) {
+ u64 address = state.ctx->registers.x1;
+ if (!utils::PageAligned(address)) {
state.ctx->registers.w0 = constant::status::InvAddress;
- state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", addr);
+ state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", address);
return;
}
const u64 size = state.ctx->registers.x2;
- if ((size & (PAGE_SIZE - 1U)) || !size) {
+ if (!utils::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize;
- state.logger->Warn("svcMapSharedMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
+ state.logger->Warn("svcMapSharedMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return;
}
u32 perm = state.ctx->registers.w3;
@@ -220,8 +230,8 @@ namespace skyline::kernel::svc {
state.ctx->registers.w0 = constant::status::InvPermission;
return;
}
- state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
- object->Map(addr, size, permission);
+ state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", address, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
+ object->Map(address, size, permission);
state.ctx->registers.w0 = constant::status::Success;
} catch (const std::exception &) {
state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->registers.w0);
@@ -230,16 +240,16 @@ namespace skyline::kernel::svc {
}
void CreateTransferMemory(DeviceState &state) {
- u64 addr = state.ctx->registers.x1;
- if ((addr & (PAGE_SIZE - 1U))) {
+ u64 address = state.ctx->registers.x1;
+ if (!utils::PageAligned(address)) {
state.ctx->registers.w0 = constant::status::InvAddress;
- state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: {}", addr);
+ state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: 0x{:X}", address);
return;
}
u64 size = state.ctx->registers.x2;
- if ((size & (PAGE_SIZE - 1U)) || !size) {
+ if (!utils::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize;
- state.logger->Warn("svcCreateTransferMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
+ state.logger->Warn("svcCreateTransferMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return;
}
u32 perm = state.ctx->registers.w3;
@@ -249,8 +259,8 @@ namespace skyline::kernel::svc {
state.ctx->registers.w0 = constant::status::InvPermission;
return;
}
- state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
- auto shmem = state.process->NewHandle(state.process->pid, addr, size, permission);
+ state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", address, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
+ auto shmem = state.process->NewHandle(state.process->pid, address, size, permission);
state.ctx->registers.w0 = constant::status::Success;
state.ctx->registers.w1 = shmem.handle;
}
@@ -258,7 +268,7 @@ namespace skyline::kernel::svc {
void CloseHandle(DeviceState &state) {
auto handle = static_cast(state.ctx->registers.w0);
try {
- state.process->handleTable.erase(handle);
+ state.process->handles.erase(handle);
state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::Success;
} catch (const std::exception &) {
@@ -270,7 +280,7 @@ namespace skyline::kernel::svc {
void ResetSignal(DeviceState &state) {
auto handle = state.ctx->registers.w0;
try {
- auto &object = state.process->handleTable.at(handle);
+ auto &object = state.process->handles.at(handle);
switch (object->objectType) {
case (type::KType::KEvent):
std::static_pointer_cast(object)->ResetSignal();
@@ -305,7 +315,7 @@ namespace skyline::kernel::svc {
std::string handleStr;
for (const auto &handle : waitHandles) {
handleStr += fmt::format("* 0x{:X}\n", handle);
- auto object = state.process->handleTable.at(handle);
+ auto object = state.process->handles.at(handle);
switch (object->objectType) {
case type::KType::KProcess:
case type::KType::KThread:
@@ -321,7 +331,7 @@ namespace skyline::kernel::svc {
}
auto timeout = state.ctx->registers.x3;
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
- auto start = GetCurrTimeNs();
+ auto start = utils::GetCurrTimeNs();
while (true) {
uint index{};
for (const auto &object : objectTable) {
@@ -333,7 +343,7 @@ namespace skyline::kernel::svc {
}
index++;
}
- if ((GetCurrTimeNs() - start) >= timeout) {
+ if ((utils::GetCurrTimeNs() - start) >= timeout) {
state.logger->Debug("svcWaitSynchronization: Wait has timed out");
state.ctx->registers.w0 = constant::status::Timeout;
return;
@@ -345,7 +355,7 @@ namespace skyline::kernel::svc {
auto addr = state.ctx->registers.x1;
if ((addr & ((1UL << WORD_BIT) - 1U))) {
state.ctx->registers.w0 = constant::status::InvAddress;
- state.logger->Warn("svcArbitrateLock: 'address' not word aligned: {}", addr);
+ state.logger->Warn("svcArbitrateLock: 'address' not word aligned: 0x{:X}", addr);
return;
}
auto handle = state.ctx->registers.w2;
@@ -357,34 +367,34 @@ namespace skyline::kernel::svc {
}
void ArbitrateUnlock(DeviceState &state) {
- auto addr = state.ctx->registers.x0;
- if ((addr & ((1UL << WORD_BIT) - 1U))) {
+ auto address = state.ctx->registers.x0;
+ if ((address & ((1UL << WORD_BIT) - 1U))) {
state.ctx->registers.w0 = constant::status::InvAddress;
- state.logger->Warn("svcArbitrateUnlock: 'address' not word aligned: {}", addr);
+ state.logger->Warn("svcArbitrateUnlock: 'address' not word aligned: 0x{:X}", address);
return;
}
- state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", addr);
- state.process->MutexUnlock(addr);
+ state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", address);
+ state.process->MutexUnlock(address);
state.ctx->registers.w0 = constant::status::Success;
}
void WaitProcessWideKeyAtomic(DeviceState &state) {
- auto mtxAddr = state.ctx->registers.x0;
- auto condAddr = state.ctx->registers.x1;
+ auto mtxAddress = state.ctx->registers.x0;
+ auto condAddress = state.ctx->registers.x1;
try {
- auto &cvar = state.process->condVarMap.at(condAddr);
- if ((mtxAddr & ((1UL << WORD_BIT) - 1U))) {
+ auto &cvar = state.process->condVars.at(condAddress);
+ if ((mtxAddress & ((1UL << WORD_BIT) - 1U))) {
state.ctx->registers.w0 = constant::status::InvAddress;
- state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr);
+ state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: 0x{:X}", mtxAddress);
return;
}
auto handle = state.ctx->registers.w2;
if (handle != state.thread->handle)
throw exception("svcWaitProcessWideKeyAtomic: Called from another thread");
- state.process->MutexLock(mtxAddr);
- auto &mutex = state.process->mutexMap.at(mtxAddr);
+ state.process->MutexLock(mtxAddress);
+ auto &mutex = state.process->mutexes.at(mtxAddress);
auto timeout = state.ctx->registers.x3;
- state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddr, condAddr, timeout);
+ state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddress, condAddress, timeout);
timespec spec{};
clock_gettime(CLOCK_REALTIME, &spec);
u128 time = u128(spec.tv_sec * 1000000000U + spec.tv_nsec) + timeout; // u128 to prevent overflow
@@ -394,10 +404,10 @@ namespace skyline::kernel::svc {
state.ctx->registers.w0 = constant::status::Timeout;
else
state.ctx->registers.w0 = constant::status::Success;
- state.process->MutexUnlock(mtxAddr);
+ state.process->MutexUnlock(mtxAddress);
} catch (const std::out_of_range &) {
- state.logger->Debug("svcWaitProcessWideKeyAtomic: No Conditional-Variable at 0x{:X}", condAddr);
- state.process->condVarMap[condAddr] = PTHREAD_COND_INITIALIZER;
+ state.logger->Debug("svcWaitProcessWideKeyAtomic: No Conditional-Variable at 0x{:X}", condAddress);
+ state.process->condVars[condAddress] = PTHREAD_COND_INITIALIZER;
state.ctx->registers.w0 = constant::status::Success;
}
}
@@ -407,15 +417,15 @@ namespace skyline::kernel::svc {
auto count = state.ctx->registers.w1;
try {
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count);
- auto &cvar = state.process->condVarMap.at(address);
- if(count == UINT32_MAX)
+ auto &cvar = state.process->condVars.at(address);
+ if (count == UINT32_MAX)
pthread_cond_broadcast(&cvar);
else
for (u32 iter = 0; iter < count; iter++)
pthread_cond_signal(&cvar);
} catch (const std::out_of_range &) {
state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address);
- state.process->condVarMap[address] = PTHREAD_COND_INITIALIZER;
+ state.process->condVars[address] = PTHREAD_COND_INITIALIZER;
}
state.ctx->registers.w0 = constant::status::Success;
}
@@ -488,46 +498,46 @@ namespace skyline::kernel::svc {
case constant::infoState::PrivilegedProcessId:
break;
case constant::infoState::AliasRegionBaseAddr:
- out = constant::MapAddr;
+ out = state.os->memory.GetRegion(memory::Regions::Alias).address;
break;
case constant::infoState::AliasRegionSize:
- out = constant::MapSize;
+ out = state.os->memory.GetRegion(memory::Regions::Alias).size;
break;
case constant::infoState::HeapRegionBaseAddr:
- out = state.process->memoryRegionMap.at(memory::Region::Heap)->address;
+ out = state.os->memory.GetRegion(memory::Regions::Heap).address;
break;
case constant::infoState::HeapRegionSize:
- out = state.process->memoryRegionMap.at(memory::Region::Heap)->size;
+ out = state.os->memory.GetRegion(memory::Regions::Heap).size;
break;
case constant::infoState::TotalMemoryAvailable:
out = constant::TotalPhyMem;
break;
case constant::infoState::TotalMemoryUsage:
- out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz + state.process->GetProgramSize();
+ out = state.process->heap->address + constant::DefStackSize + state.os->memory.GetProgramSize();
break;
case constant::infoState::AddressSpaceBaseAddr:
- out = constant::BaseAddr;
+ out = constant::BaseAddress;
break;
case constant::infoState::AddressSpaceSize:
out = constant::BaseEnd;
break;
case constant::infoState::StackRegionBaseAddr:
- out = state.thread->stackTop;
+ out = state.os->memory.GetRegion(memory::Regions::Stack).address;
break;
case constant::infoState::StackRegionSize:
- out = state.process->mainThreadStackSz;
+ out = state.os->memory.GetRegion(memory::Regions::Stack).size;
break;
case constant::infoState::PersonalMmHeapSize:
out = constant::TotalPhyMem;
break;
case constant::infoState::PersonalMmHeapUsage:
- out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz;
+ out = state.process->heap->address + constant::DefStackSize;
break;
case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
out = constant::TotalPhyMem; // TODO: NPDM specifies SystemResourceSize, subtract that from this
break;
case constant::infoState::TotalMemoryUsedWithoutMmHeap:
- out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz; // TODO: Same as above
+ out = state.process->heap->size + constant::DefStackSize; // TODO: Same as above
break;
case constant::infoState::UserExceptionContextAddr:
out = state.process->tlsPages[0]->Get(0);
diff --git a/app/src/main/cpp/skyline/kernel/svc.h b/app/src/main/cpp/skyline/kernel/svc.h
index 080e2aa3..90c4282f 100644
--- a/app/src/main/cpp/skyline/kernel/svc.h
+++ b/app/src/main/cpp/skyline/kernel/svc.h
@@ -37,7 +37,7 @@ namespace skyline {
};
namespace kernel::svc {
/**
- * @brief Set the process heap to a given size (https://switchbrew.org/wiki/SVC#svcSetHeapSize)
+ * @brief Sets the process heap to a given Size. It can both extend and shrink the heap. (https://switchbrew.org/wiki/SVC#svcSetHeapSize)
*/
void SetHeapSize(DeviceState &state);
@@ -46,6 +46,11 @@ namespace skyline {
*/
void SetMemoryAttribute(DeviceState &state);
+ /**
+ * @brief Maps a memory range into a different range. Mainly used for adding guard pages around stack. (https://switchbrew.org/wiki/SVC#svcSetMemoryAttribute)
+ */
+ void MapMemory(DeviceState &state);
+
/**
* @brief Query information about an address (https://switchbrew.org/wiki/SVC#svcQueryMemory)
*/
@@ -169,7 +174,7 @@ namespace skyline {
SetHeapSize, // 0x01
nullptr, // 0x02
SetMemoryAttribute, // 0x03
- nullptr, // 0x04
+ MapMemory, // 0x04
nullptr, // 0x05
QueryMemory, // 0x06
ExitProcess, // 0x07
diff --git a/app/src/main/cpp/skyline/kernel/types/KMemory.h b/app/src/main/cpp/skyline/kernel/types/KMemory.h
new file mode 100644
index 00000000..65e518ce
--- /dev/null
+++ b/app/src/main/cpp/skyline/kernel/types/KMemory.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+#include "KObject.h"
+
+namespace skyline::kernel::type {
+ class KMemory : public KObject {
+ public:
+ KMemory(const DeviceState &state, KType objectType) : KObject(state, objectType) {}
+
+ /**
+ * @brief Remap a chunk of memory as to change the size occupied by it
+ * @param size The new size of the memory
+ * @return The address the memory was remapped to
+ */
+ virtual void Resize(size_t size) = 0;
+
+ /**
+ * @brief Updates the permissions of a block of mapped memory
+ * @param address The starting address to change the permissions at
+ * @param size The size of the partition to change the permissions of
+ * @param permission The new permissions to be set for the memory
+ */
+ virtual void UpdatePermission(const u64 address, const u64 size, memory::Permission permission) = 0;
+
+ /**
+ * @brief Updates the permissions of a chunk of mapped memory
+ * @param permission The new permissions to be set for the memory
+ */
+ inline virtual void UpdatePermission(memory::Permission permission) = 0;
+
+ /**
+ * @brief Checks if the specified address is within the memory object
+ * @param address The address to check
+ * @return If the address is inside the memory object
+ */
+ inline virtual bool IsInside(u64 address) = 0;
+ };
+}
diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp
index cb62a99b..7ed451f0 100644
--- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp
+++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp
@@ -1,68 +1,66 @@
#include "KPrivateMemory.h"
#include "KProcess.h"
-#include
+#include
#include
namespace skyline::kernel::type {
- KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 dstAddress, size_t size, memory::Permission permission, const memory::Type type, std::shared_ptr thread) : state(state), address(dstAddress), size(size), permission(permission), type(type), KObject(state, KType::KPrivateMemory) {
- Registers fregs{};
- fregs.x0 = dstAddress;
- fregs.x1 = size;
- fregs.x2 = static_cast(permission.Get());
- fregs.x3 = static_cast(MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0));
- fregs.x4 = static_cast(-1);
- fregs.x8 = __NR_mmap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, thread);
- if (fregs.x0 < 0)
- throw exception("An error occurred while mapping private region in child process");
- if (!this->address)
- this->address = fregs.x0;
- }
-
- u64 KPrivateMemory::Resize(size_t newSize, bool canMove) {
+ KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : state(state), address(address), size(size), KMemory(state, KType::KPrivateMemory) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
- fregs.x2 = newSize;
- fregs.x3 = canMove ? MREMAP_MAYMOVE : 0;
- fregs.x8 = __NR_mremap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread);
+ fregs.x2 = static_cast(permission.Get());
+ fregs.x3 = static_cast(MAP_PRIVATE | MAP_ANONYMOUS | ((address) ? MAP_FIXED : 0));
+ fregs.x4 = static_cast(-1);
+ fregs.x8 = __NR_mmap;
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
- throw exception("An error occurred while remapping private region in child process");
- address = fregs.x0;
- size = newSize;
- return address;
+ throw exception("An error occurred while mapping private section in child process");
+ if (!this->address)
+ this->address = fregs.x0;
+ BlockDescriptor block{
+ .address = fregs.x0,
+ .size = size,
+ .permission = permission,
+ };
+ ChunkDescriptor chunk{
+ .address = fregs.x0,
+ .size = size,
+ .state = memState,
+ .blockList = {block},
+ };
+ state.os->memory.InsertChunk(chunk);
}
- void KPrivateMemory::UpdatePermission(memory::Permission permission) {
+ void KPrivateMemory::Resize(size_t nSize) {
+ Registers fregs{};
+ fregs.x0 = address;
+ fregs.x1 = size;
+ fregs.x2 = nSize;
+ fregs.x8 = __NR_mremap;
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
+ if (fregs.x0 < 0)
+ throw exception("An error occurred while remapping private section in child process");
+ size = nSize;
+ auto chunk = state.os->memory.GetChunk(address);
+ MemoryManager::ResizeChunk(chunk, size);
+ }
+
+ void KPrivateMemory::UpdatePermission(const u64 address, const u64 size, memory::Permission permission) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = static_cast(permission.Get());
fregs.x8 = __NR_mprotect;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread);
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
- throw exception("An error occurred while updating private region's permissions in child process");
- this->permission = permission;
- }
-
- memory::MemoryInfo KPrivateMemory::GetInfo(u64 address) {
- memory::MemoryInfo info{};
- info.baseAddress = address;
- info.size = size;
- info.type = static_cast(type);
- for (const auto ®ion : regionInfoVec)
- if ((address >= region.address) && (address < (region.address + region.size))) {
- info.memoryAttribute.isUncached = region.isUncached;
- }
- info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0);
- info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0);
- info.r = permission.r;
- info.w = permission.w;
- info.x = permission.x;
- info.ipcRefCount = ipcRefCount;
- info.deviceRefCount = deviceRefCount;
- return info;
+ throw exception("An error occurred while updating private section's permissions in child process");
+ auto chunk = state.os->memory.GetChunk(address);
+ BlockDescriptor block{
+ .address = address,
+ .size = size,
+ .permission = permission,
+ };
+ MemoryManager::InsertBlock(chunk, block);
}
KPrivateMemory::~KPrivateMemory() {
@@ -72,9 +70,10 @@ namespace skyline::kernel::type {
fregs.x0 = address;
fregs.x1 = size;
fregs.x8 = __NR_munmap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
}
} catch (const std::exception &) {
}
+ state.os->memory.DeleteChunk(address);
}
};
diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h
index 72146f52..84b97238 100644
--- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h
+++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h
@@ -1,55 +1,59 @@
#pragma once
-#include
-#include "KObject.h"
+#include "KMemory.h"
namespace skyline::kernel::type {
/**
- * KPrivateMemory is used to hold some amount of private memory
+ * @brief KPrivateMemory is used to map memory local to the guest process
*/
- class KPrivateMemory : public KObject {
+ class KPrivateMemory : public KMemory {
private:
const DeviceState &state; //!< The state of the device
public:
- u64 address; //!< The address of the allocated memory
- size_t size; //!< The size of the allocated memory
- u16 ipcRefCount{}; //!< The amount of reference to this memory for IPC
- u16 deviceRefCount{}; //!< The amount of reference to this memory for IPC
- memory::Permission permission; //!< The permissions for the allocated memory
- const memory::Type type; //!< The type of this memory allocation
- std::vector regionInfoVec; //!< This holds information about specific memory regions
+ u64 address{}; //!< The address of the allocated memory
+ size_t size{}; //!< The size of the allocated memory
/**
* @param state The state of the device
- * @param dstAddress The address to map to (If NULL then an arbitrary address is picked)
+ * @param address The address to map to (If NULL then an arbitrary address is picked)
* @param size The size of the allocation
* @param permission The permissions for the allocated memory
- * @param type The type of the memory
- * @param thread The thread to execute the calls on
+ * @param memState The MemoryState of the chunk of memory
*/
- KPrivateMemory(const DeviceState &state, u64 dstAddress, size_t size, memory::Permission permission, const memory::Type type, std::shared_ptr thread = 0);
+ KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState);
/**
* @brief Remap a chunk of memory as to change the size occupied by it
- * @param newSize The new size of the memory
- * @param canMove If the memory can move if there is not enough space at the current address
+ * @param size The new size of the memory
* @return The address the memory was remapped to
*/
- u64 Resize(size_t newSize, bool canMove);
+ virtual void Resize(size_t size);
+
+ /**
+ * @brief Updates the permissions of a block of mapped memory
+ * @param address The starting address to change the permissions at
+ * @param size The size of the partition to change the permissions of
+ * @param permission The new permissions to be set for the memory
+ */
+ virtual void UpdatePermission(const u64 address, const u64 size, memory::Permission permission);
/**
* @brief Updates the permissions of a chunk of mapped memory
* @param permission The new permissions to be set for the memory
*/
- void UpdatePermission(memory::Permission permission);
+ inline virtual void UpdatePermission(memory::Permission permission) {
+ UpdatePermission(address, size, permission);
+ }
/**
- * @brief Returns a MemoryInfo object
- * @param address The specific address being queried (Used to fill MemoryAttribute)
- * @return A Memory::MemoryInfo struct based on attributes of the memory
+ * @brief Checks if the specified address is within the memory object
+ * @param address The address to check
+ * @return If the address is inside the memory object
*/
- memory::MemoryInfo GetInfo(u64 address);
+ inline virtual bool IsInside(u64 address) {
+ return (this->address <= address) && ((this->address + this->size) > address);
+ }
/**
* @brief The destructor of private memory, it deallocates the memory
diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp
index c96c3e88..9dcbc4e1 100644
--- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp
+++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp
@@ -26,12 +26,15 @@ namespace skyline::kernel::type {
}
u64 KProcess::GetTlsSlot() {
- for (auto &tlsPage: tlsPages) {
+ for (auto &tlsPage: tlsPages)
if (!tlsPage->Full())
return tlsPage->ReserveSlot();
- }
- auto tlsMem = NewHandle(0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal, threadMap.at(pid)).item;
- memoryMap[tlsMem->address] = tlsMem;
+ u64 address;
+ if(tlsPages.empty())
+ address = state.os->memory.GetRegion(memory::Regions::TlsIo).address;
+ else
+ address = (*(tlsPages.end()-1))->address + PAGE_SIZE;
+ auto tlsMem = NewHandle(address, PAGE_SIZE, memory::Permission(true, true, false), memory::MemoryStates::ThreadLocal).item;
tlsPages.push_back(std::make_shared(tlsMem->address));
auto &tlsPage = tlsPages.back();
if (tlsPages.empty())
@@ -39,13 +42,15 @@ namespace skyline::kernel::type {
return tlsPage->ReserveSlot();
}
- KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize, std::shared_ptr &tlsMemory) : pid(pid), mainThreadStackSz(stackSize), KSyncObject(state, KType::KProcess) {
+ void KProcess::InitializeMemory() {
+ heap = NewHandle(state.os->memory.GetRegion(memory::Regions::Heap).address, constant::DefHeapSize, memory::Permission{true, true, false}, memory::MemoryStates::Heap).item;
+ threads[pid]->tls = GetTlsSlot();
+ }
+
+ KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize, std::shared_ptr &tlsMemory) : pid(pid), KSyncObject(state, KType::KProcess) {
auto thread = NewHandle(pid, entryPoint, 0x0, stackBase + stackSize, 0, constant::DefaultPriority, this, tlsMemory).item;
- // Remove GetTlsSlot from KThread ctor and cleanup ctor in general
- threadMap[pid] = thread;
+ threads[pid] = thread;
state.nce->WaitThreadInit(thread);
- thread->tls = GetTlsSlot();
- MapPrivateRegion(constant::HeapAddr, constant::DefHeapSize, {true, true, false}, memory::Type::Heap, memory::Region::Heap);
memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC);
if (memFd == -1)
throw exception("Cannot open file descriptor to /proc/{}/mem, \"{}\"", pid, strerror(errno));
@@ -78,24 +83,24 @@ namespace skyline::kernel::type {
fregs.regs[0] = entryPoint;
fregs.regs[1] = stackTop;
fregs.x8 = __NR_clone;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid);
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
auto pid = static_cast(fregs.regs[0]);
if (pid == -1)
throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop);
auto process = NewHandle(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this).item;
- threadMap[pid] = process;
+ threads[pid] = process;
return process;
*/
return nullptr;
}
void KProcess::ReadMemory(void *destination, u64 offset, size_t size) const {
- struct iovec local {
+ struct iovec local{
.iov_base = destination,
.iov_len = size
};
- struct iovec remote {
- .iov_base = reinterpret_cast(offset),
+ struct iovec remote{
+ .iov_base = reinterpret_cast(offset),
.iov_len = size
};
@@ -104,12 +109,12 @@ namespace skyline::kernel::type {
}
void KProcess::WriteMemory(void *source, u64 offset, size_t size) const {
- struct iovec local {
+ struct iovec local{
.iov_base = source,
.iov_len = size
};
- struct iovec remote {
- .iov_base = reinterpret_cast(offset),
+ struct iovec remote{
+ .iov_base = reinterpret_cast(offset),
.iov_len = size
};
@@ -117,43 +122,52 @@ namespace skyline::kernel::type {
pwrite64(memFd, source, size, offset);
}
- KProcess::HandleOut KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) {
- auto mem = NewHandle(address, size, perms, type, threadMap.at(pid));
- memoryMap[mem.item->address] = mem.item;
- memoryRegionMap[region] = mem.item;
- return mem;
+ void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const {
+ if (size <= PAGE_SIZE) {
+ std::vector buffer(size);
+ state.process->ReadMemory(buffer.data(), source, size);
+ state.process->WriteMemory(buffer.data(), destination, size);
+ } else {
+ Registers fregs{};
+ fregs.x0 = source;
+ fregs.x1 = destination;
+ fregs.x2 = size;
+ state.nce->ExecuteFunction(ThreadCall::Memcopy, fregs);
+ }
}
- bool KProcess::UnmapPrivateRegion(const skyline::memory::Region region) {
- if (!memoryRegionMap.count(region))
- return false;
- memoryMap.erase(memoryRegionMap.at(region)->address);
- memoryRegionMap.erase(region);
- return true;
- }
-
- size_t KProcess::GetProgramSize() {
- size_t sharedSize = 0;
- for (auto ®ion : memoryRegionMap)
- sharedSize += region.second->size;
- return sharedSize;
+ std::shared_ptr KProcess::GetMemoryObject(u64 address) {
+ for(auto& [handle, object] : state.process->handles) {
+ switch(object->objectType) {
+ case type::KType::KPrivateMemory:
+ case type::KType::KSharedMemory:
+ case type::KType::KTransferMemory: {
+ auto mem = std::static_pointer_cast(object);
+ if (mem->IsInside(address))
+ return mem;
+ }
+ default:
+ break;
+ }
+ }
+ return nullptr;
}
void KProcess::MutexLock(u64 address) {
try {
- auto mtx = mutexMap.at(address);
+ auto mtx = mutexes.at(address);
pthread_mutex_lock(&mtx);
u32 mtxVal = ReadMemory(address);
mtxVal = (mtxVal & ~constant::MtxOwnerMask) | state.thread->handle;
WriteMemory(mtxVal, address);
} catch (const std::out_of_range &) {
- mutexMap[address] = PTHREAD_MUTEX_INITIALIZER;
+ mutexes[address] = PTHREAD_MUTEX_INITIALIZER;
}
}
void KProcess::MutexUnlock(u64 address) {
try {
- auto mtx = mutexMap.at(address);
+ auto mtx = mutexes.at(address);
u32 mtxVal = ReadMemory(address);
if ((mtxVal & constant::MtxOwnerMask) != state.thread->handle)
throw exception("A non-owner thread tried to release a mutex");
@@ -161,7 +175,7 @@ namespace skyline::kernel::type {
WriteMemory(mtxVal, address);
pthread_mutex_unlock(&mtx);
} catch (const std::out_of_range &) {
- mutexMap[address] = PTHREAD_MUTEX_INITIALIZER;
+ mutexes[address] = PTHREAD_MUTEX_INITIALIZER;
}
}
}
diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h
index 9eff4fef..c05fb54d 100644
--- a/app/src/main/cpp/skyline/kernel/types/KProcess.h
+++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h
@@ -6,6 +6,7 @@
#include "KSharedMemory.h"
#include "KSession.h"
#include "KEvent.h"
+#include
#include
namespace skyline::kernel::type {
@@ -57,25 +58,16 @@ namespace skyline::kernel::type {
*/
u64 GetTlsSlot();
- public:
- enum class Status {
- Created, //!< The process was created but the main thread has not started yet
- Started, //!< The process has been started
- Exiting //!< The process is exiting
- } status = Status::Created; //!< The state of the process
- handle_t handleIndex = constant::BaseHandleIndex; //!< This is used to keep track of what to map as an handle
- pid_t pid; //!< The PID of the main thread
- size_t mainThreadStackSz; //!< The size of the main thread's stack (All other threads map stack themselves so we don't know the size per-se)
- int memFd; //!< The file descriptor to the memory of the process
- std::unordered_map> memoryMap; //!< A mapping from every address to a shared pointer of it's corresponding KPrivateMemory, used to keep track of KPrivateMemory instances
- std::unordered_map> memoryRegionMap; //!< A mapping from every MemoryRegion to a shared pointer of it's corresponding KPrivateMemory
- std::unordered_map> handleTable; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
- std::unordered_map> threadMap; //!< A mapping from a PID to it's corresponding KThread object
- std::unordered_map mutexMap; //!< A map from a mutex's address to a vector of threads waiting on it
- std::unordered_map condVarMap; //!< A map from a conditional variable's address to a vector of threads waiting on it
- std::vector> tlsPages; //!< A vector of all allocated TLS pages
/**
- * This is used as the output for functions that return created kernel objects
+ * @brief This initializes heap and the initial TLS page
+ */
+ void InitializeMemory();
+
+ public:
+ friend OS;
+
+ /**
+ * @brief This is used as the output for functions that return created kernel objects
* @tparam objectClass The class of the kernel object
*/
template
@@ -84,6 +76,25 @@ namespace skyline::kernel::type {
handle_t handle; //!< The handle of the object in the process
};
+ /**
+ * @brief This enum is used to describe the current status of the process
+ */
+ enum class Status {
+ Created, //!< The process was created but the main thread has not started yet
+ Started, //!< The process has been started
+ Exiting //!< The process is exiting
+ } status = Status::Created; //!< The state of the process
+
+ handle_t handleIndex = constant::BaseHandleIndex; //!< This is used to keep track of what to map as an handle
+ pid_t pid; //!< The PID of the main thread
+ int memFd; //!< The file descriptor to the memory of the process
+ std::unordered_map> handles; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
+ std::unordered_map> threads; //!< A mapping from a PID to it's corresponding KThread object
+ std::unordered_map mutexes; //!< A map from a mutex's address to a vector of threads waiting on it
+ std::unordered_map condVars; //!< A map from a conditional variable's address to a vector of threads waiting on it
+ std::vector> tlsPages; //!< A vector of all allocated TLS pages
+ std::shared_ptr heap; //!< The kernel memory object backing the allocated heap
+
/**
* @brief Creates a KThread object for the main thread and opens the process's memory file
* @param state The state of the device
@@ -151,27 +162,12 @@ namespace skyline::kernel::type {
void WriteMemory(void *source, u64 offset, size_t size) const;
/**
- * @brief Map a chunk of process local memory (private memory)
- * @param address The address to map to (Can be 0 if address doesn't matter)
- * @param size The size of the chunk of memory
- * @param perms The permissions of the memory
- * @param type The type of the memory
- * @param region The specific region this memory is mapped for
- * @return The HandleOut of the created KPrivateMemory
+ * @brief Copy one chunk to another in the process's memory
+ * @param source The address of where the data to read is present
+ * @param destination The address to write the read data to
+ * @param size The amount of memory to be copied
*/
- HandleOut MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region);
-
- /**
- * @brief Unmap a chunk of process local memory (private memory)
- * @param region The region of memory to unmap
- * @return If the region was mapped at all
- */
- bool UnmapPrivateRegion(const memory::Region region);
-
- /**
- * @brief Returns the total memory occupied by regions mapped for the process
- */
- size_t GetProgramSize();
+ void CopyMemory(u64 source, u64 destination, size_t size) const;
/**
* @brief Creates a new handle to a KObject and adds it to the process handle_table
@@ -186,18 +182,18 @@ namespace skyline::kernel::type {
item = std::make_shared(state, handleIndex, args...);
else
item = std::make_shared(state, args...);
- handleTable[handleIndex] = std::static_pointer_cast(item);
+ handles[handleIndex] = std::static_pointer_cast(item);
return {item, handleIndex++};
}
/**
- * @brief This inserts an item into the process handle table
+ * @brief Inserts an item into the process handle table
* @param item The item to insert
* @return The handle of the corresponding item in the handle table
*/
template
handle_t InsertItem(std::shared_ptr &item) {
- handleTable[handleIndex] = std::static_pointer_cast(item);
+ handles[handleIndex] = std::static_pointer_cast(item);
return handleIndex++;
}
@@ -227,7 +223,7 @@ namespace skyline::kernel::type {
else
throw exception("KProcess::GetHandle couldn't determine object type");
try {
- auto item = handleTable.at(handle);
+ auto item = handles.at(handle);
if (item->objectType == objectType)
return std::static_pointer_cast(item);
else
@@ -237,6 +233,21 @@ namespace skyline::kernel::type {
}
}
+ /**
+ * @brief Retrieves a kernel memory object that owns the specified address
+ * @param address The address to look for
+ * @return A shared pointer to the corresponding KMemory object
+ */
+ std::shared_ptr GetMemoryObject(u64 address);
+
+ /**
+ * @brief This deletes a certain handle from the handle table
+ * @param handle The handle to delete
+ */
+ inline void DeleteHandle(handle_t handle) {
+ handles.erase(handle);
+ }
+
/**
* @brief This locks the Mutex at the specified address
* @param address The address of the mutex
diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp
index e229fbc7..c46a17cb 100644
--- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp
+++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp
@@ -1,22 +1,22 @@
#include "KSharedMemory.h"
#include "KProcess.h"
-#include
+#include
#include
#include
#include
namespace skyline::kernel::type {
- KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::Type type) : type(type), KObject(state, KType::KSharedMemory) {
+ KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : initialState(memState), KMemory(state, KType::KSharedMemory) {
fd = ASharedMemory_create("", size);
if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd);
- address = reinterpret_cast(mmap(reinterpret_cast(address), size, permission.Get(), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast(fd), 0));
+ address = reinterpret_cast(mmap(reinterpret_cast(address), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | ((address) ? MAP_FIXED : 0), fd, 0));
if (address == reinterpret_cast(MAP_FAILED))
- throw exception("An occurred while mapping shared region: {}", strerror(errno));
- kernel = {address, size, permission};
+ throw exception("An occurred while mapping shared memory: {}", strerror(errno));
+ kernel = {.address = address, .size = size, .permission = permission};
}
- u64 KSharedMemory::Map(u64 address, u64 size, memory::Permission permission) {
+ u64 KSharedMemory::Map(const u64 address, const u64 size, memory::Permission permission) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
@@ -24,10 +24,22 @@ namespace skyline::kernel::type {
fregs.x3 = static_cast(MAP_SHARED | ((address) ? MAP_FIXED : 0));
fregs.x4 = static_cast(fd);
fregs.x8 = __NR_mmap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid);
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
- throw exception("An error occurred while mapping shared region in child process");
- guest = {fregs.x0, size, permission};
+ throw exception("An error occurred while mapping shared memory in guest");
+ guest = {.address = fregs.x0, .size = size, .permission = permission};
+ ChunkDescriptor chunk{
+ .address = fregs.x0,
+ .size = size,
+ .state = initialState,
+ };
+ BlockDescriptor block{
+ .address = fregs.x0,
+ .size = size,
+ .permission = permission,
+ };
+ chunk.blockList.push_front(block);
+ state.os->memory.InsertChunk(chunk);
return fregs.x0;
}
@@ -38,52 +50,45 @@ namespace skyline::kernel::type {
fregs.x1 = guest.size;
fregs.x2 = size;
fregs.x8 = __NR_mremap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid);
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
- throw exception("An error occurred while remapping shared region in child process");
+ throw exception("An error occurred while remapping shared memory in guest");
guest.size = size;
+ auto chunk = state.os->memory.GetChunk(guest.address);
+ MemoryManager::ResizeChunk(chunk, size);
}
if (kernel.valid()) {
if (mremap(reinterpret_cast(kernel.address), kernel.size, size, 0) == MAP_FAILED)
- throw exception("An occurred while remapping shared region: {}", strerror(errno));
+ throw exception("An error occurred while remapping shared region: {}", strerror(errno));
kernel.size = size;
}
}
- void KSharedMemory::UpdatePermission(memory::Permission permission, bool host) {
+ void KSharedMemory::UpdatePermission(u64 address, u64 size, memory::Permission permission, bool host) {
if (guest.valid() && !host) {
Registers fregs{};
- fregs.x0 = guest.address;
- fregs.x1 = guest.size;
- fregs.x2 = static_cast(guest.permission.Get());
+ fregs.x0 = address;
+ fregs.x1 = size;
+ fregs.x2 = static_cast(permission.Get());
fregs.x8 = __NR_mprotect;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid);
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
- throw exception("An error occurred while updating shared region's permissions in child process");
- guest.permission = permission;
+ throw exception("An error occurred while updating shared memory's permissions in guest");
+ auto chunk = state.os->memory.GetChunk(address);
+ BlockDescriptor block{
+ .address = address,
+ .size = size,
+ .permission = permission,
+ };
+ MemoryManager::InsertBlock(chunk, block);
}
if (kernel.valid() && host) {
if (mprotect(reinterpret_cast(kernel.address), kernel.size, permission.Get()) == reinterpret_cast(MAP_FAILED))
- throw exception("An occurred while remapping shared region: {}", strerror(errno));
+ throw exception("An error occurred while remapping shared memory: {}", strerror(errno));
kernel.permission = permission;
}
}
- memory::MemoryInfo KSharedMemory::GetInfo() {
- memory::MemoryInfo info{};
- info.baseAddress = guest.address;
- info.size = guest.size;
- info.type = static_cast(type);
- info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0);
- info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0);
- info.r = guest.permission.r;
- info.w = guest.permission.w;
- info.x = guest.permission.x;
- info.ipcRefCount = ipcRefCount;
- info.deviceRefCount = deviceRefCount;
- return info;
- }
-
KSharedMemory::~KSharedMemory() {
try {
if (guest.valid() && state.process) {
@@ -91,12 +96,13 @@ namespace skyline::kernel::type {
fregs.x0 = guest.address;
fregs.x1 = guest.size;
fregs.x8 = __NR_munmap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
}
if (kernel.valid())
munmap(reinterpret_cast(kernel.address), kernel.size);
} catch (const std::exception &) {
}
+ state.os->memory.DeleteChunk(guest.address);
close(fd);
}
};
diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h
index 92b8665b..cfb9e9be 100644
--- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h
+++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h
@@ -1,15 +1,15 @@
#pragma once
-#include
-#include "KObject.h"
+#include "KMemory.h"
namespace skyline::kernel::type {
/**
* @brief KSharedMemory is used to hold a particular amount of shared memory
*/
- class KSharedMemory : public KObject {
+ class KSharedMemory : public KMemory {
private:
int fd; //!< A file descriptor to the underlying shared memory
+ memory::MemoryState initialState; //!< This is to hold the initial state for the Map call
public:
/**
@@ -27,21 +27,17 @@ namespace skyline::kernel::type {
inline bool valid() { return address && size && permission.Get(); }
} kernel, guest;
- u16 ipcRefCount{}; //!< The amount of reference to this memory for IPC
- u16 deviceRefCount{}; //!< The amount of reference to this memory for IPC
- memory::Type type; //!< The type of this memory allocation
-
/**
* @param state The state of the device
- * @param address The address of the allocation on the kernel (Arbitrary is 0)
+ * @param address The address of the allocation on the kernel (If NULL then an arbitrary address is picked)
* @param size The size of the allocation on the kernel
* @param permission The permission of the kernel process
- * @param type The type of the memory
+ * @param memState The MemoryState of the chunk of memory
*/
- KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::Type type);
+ KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState = memory::MemoryStates::SharedMemory);
/**
- * @brief Maps the shared memory at an address in the guest
+ * @brief Maps the shared memory in the guest
* @param address The address to map to (If NULL an arbitrary address is picked)
* @param size The amount of shared memory to map
* @param permission The permission of the kernel process
@@ -53,20 +49,44 @@ namespace skyline::kernel::type {
* @brief Resize a chunk of memory as to change the size occupied by it
* @param size The new size of the memory
*/
- void Resize(size_t size);
+ virtual void Resize(size_t size);
+
+ /**
+ * @brief Updates the permissions of a block of mapped memory
+ * @param address The starting address to change the permissions at
+ * @param size The size of the partition to change the permissions of
+ * @param permission The new permissions to be set for the memory
+ * @param host Set the permissions for the kernel rather than the guest
+ */
+ void UpdatePermission(u64 address, u64 size, memory::Permission permission, bool host = false);
+
+ /**
+ * @brief Updates the permissions of a block of mapped memory
+ * @param address The starting address to change the permissions at
+ * @param size The size of the partition to change the permissions of
+ * @param permission The new permissions to be set for the memory
+ */
+ virtual void UpdatePermission(u64 address, u64 size, memory::Permission permission) {
+ UpdatePermission(address, size, permission, false);
+ }
+
/**
* @brief Updates the permissions of a chunk of mapped memory
* @param permission The new permissions to be set for the memory
- * @param kernel Set the permissions for the kernel rather than the guest
*/
- void UpdatePermission(memory::Permission permission, bool host = 0);
+ inline virtual void UpdatePermission(memory::Permission permission) {
+ UpdatePermission(guest.address, guest.size, permission, false);
+ }
/**
- * @brief Creates a MemoryInfo struct from the current instance
- * @return A Memory::MemoryInfo struct based on attributes of the memory
+ * @brief Checks if the specified address is within the guest memory object
+ * @param address The address to check
+ * @return If the address is inside the guest memory object
*/
- memory::MemoryInfo GetInfo();
+ inline virtual bool IsInside(u64 address) {
+ return (guest.address <= address) && ((guest.address + guest.size) > address);
+ }
/**
* @brief The destructor of shared memory, it deallocates the memory from all processes
diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.cpp b/app/src/main/cpp/skyline/kernel/types/KThread.cpp
index 467d32be..e4554945 100644
--- a/app/src/main/cpp/skyline/kernel/types/KThread.cpp
+++ b/app/src/main/cpp/skyline/kernel/types/KThread.cpp
@@ -5,7 +5,7 @@
namespace skyline::kernel::type {
KThread::KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr &tlsMemory) : handle(handle), pid(self_pid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state,
- KType::KThread) {
+ KType::KThread) {
UpdatePriority(priority);
}
@@ -18,7 +18,7 @@ namespace skyline::kernel::type {
if (pid == parent->pid)
parent->status = KProcess::Status::Started;
status = Status::Running;
- state.nce->StartThread(entryArg, handle, parent->threadMap.at(pid));
+ state.nce->StartThread(entryArg, handle, parent->threads.at(pid));
}
}
diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp
index fb19ae45..68597fd2 100644
--- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp
+++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp
@@ -4,8 +4,25 @@
#include
namespace skyline::kernel::type {
- KTransferMemory::KTransferMemory(const DeviceState &state, pid_t pid, u64 address, size_t size, const memory::Permission permission) : owner(pid), cSize(size), permission(permission), KObject(state, KType::KTransferMemory) {
- if (pid) {
+ KTransferMemory::KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : host(host), size(size), KMemory(state, KType::KTransferMemory) {
+ BlockDescriptor block{
+ .size = size,
+ .permission = permission,
+ };
+ ChunkDescriptor chunk{
+ .size = size,
+ .state = memState,
+ .blockList = {block},
+ };
+ if (host) {
+ address = reinterpret_cast(mmap(reinterpret_cast(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0));
+ if (reinterpret_cast(address) == MAP_FAILED)
+ throw exception("An error occurred while mapping transfer memory in host");
+ this->address = address;
+ chunk.address = address;
+ chunk.blockList.front().address = address;
+ hostChunk = chunk;
+ } else {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
@@ -13,89 +30,152 @@ namespace skyline::kernel::type {
fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0));
fregs.x4 = static_cast(-1);
fregs.x8 = __NR_mmap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, pid);
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
throw exception("An error occurred while mapping shared region in child process");
- cAddress = fregs.x0;
- } else {
- address = reinterpret_cast(mmap(reinterpret_cast(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0));
- if (reinterpret_cast(address) == MAP_FAILED)
- throw exception("An error occurred while mapping transfer memory in kernel");
- cAddress = address;
+ this->address = fregs.x0;
+ chunk.address = fregs.x0;
+ chunk.blockList.front().address = fregs.x0;
+ state.os->memory.InsertChunk(chunk);
}
}
- u64 KTransferMemory::Transfer(pid_t process, u64 address, u64 size) {
- if (process) {
- Registers fregs{};
- fregs.x0 = address;
- fregs.x1 = size;
- fregs.x2 = static_cast(permission.Get());
- fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0));
- fregs.x4 = static_cast(-1);
- fregs.x8 = __NR_mmap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, process);
- if (fregs.x0 < 0)
- throw exception("An error occurred while mapping transfer memory in child process");
- address = fregs.x0;
- } else {
- address = reinterpret_cast(mmap(reinterpret_cast(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0));
- if (reinterpret_cast(address) == MAP_FAILED)
- throw exception("An error occurred while mapping transfer memory in kernel");
+ u64 KTransferMemory::Transfer(bool mHost, u64 nAddress, u64 nSize) {
+ nSize = nSize ? nSize : size;
+ ChunkDescriptor chunk = host ? hostChunk : *state.os->memory.GetChunk(address);
+ chunk.address = nAddress;
+ chunk.size = nSize;
+ MemoryManager::ResizeChunk(&chunk, nSize);
+ for (auto &block : chunk.blockList) {
+ block.address = nAddress + (block.address - address);
+ if ((mHost && !host) || (!mHost && !host)) {
+ Registers fregs{};
+ fregs.x0 = block.address;
+ fregs.x1 = block.size;
+ fregs.x2 = (block.permission.w) ? static_cast(block.permission.Get()) : (PROT_READ | PROT_WRITE);
+ fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0));
+ fregs.x4 = static_cast(-1);
+ fregs.x8 = __NR_mmap;
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
+ if (fregs.x0 < 0)
+ throw exception("An error occurred while mapping transfer memory in child process");
+ nAddress = fregs.x0;
+ } else if ((!mHost && host) || (mHost && host)) {
+ nAddress = reinterpret_cast(mmap(reinterpret_cast(block.address), block.size, block.permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0), -1, 0));
+ if (reinterpret_cast(nAddress) == MAP_FAILED)
+ throw exception("An error occurred while mapping transfer memory in host");
+ }
+ if (block.permission.r) {
+ if (mHost && !host)
+ state.process->ReadMemory(reinterpret_cast(nAddress), address, block.size);
+ else if (!mHost && host)
+ state.process->WriteMemory(reinterpret_cast(address), nAddress, block.size);
+ else if (!mHost && !host)
+ state.process->CopyMemory(address, nAddress, block.size);
+ else if (mHost && host)
+ memcpy(reinterpret_cast(nAddress), reinterpret_cast(address), block.size);
+ }
+ if (!block.permission.w) {
+ if (mHost) {
+ if (mprotect(reinterpret_cast(block.address), block.size, block.permission.Get()) == reinterpret_cast(MAP_FAILED))
+ throw exception("An error occurred while remapping transfer memory: {}", strerror(errno));
+ } else {
+ Registers fregs{};
+ fregs.x0 = block.address;
+ fregs.x1 = block.size;
+ fregs.x2 = static_cast(block.permission.Get());
+ fregs.x8 = __NR_mprotect;
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
+ if (fregs.x0 < 0)
+ throw exception("An error occurred while updating transfer memory's permissions in guest");
+ }
+ }
}
- size_t copySz = std::min(size, cSize);
- if (process && !owner) {
- state.process->WriteMemory(reinterpret_cast(cAddress), address, copySz);
- } else if (!process && owner) {
- state.process->ReadMemory(reinterpret_cast(address), cAddress, copySz);
- } else
- throw exception("Transferring from kernel to kernel is not supported");
- if (owner) {
+ if (mHost && !host) {
+ state.os->memory.DeleteChunk(address);
+ hostChunk = chunk;
+ } else if (!mHost && host)
+ state.os->memory.InsertChunk(chunk);
+ else if (mHost && host)
+ hostChunk = chunk;
+ else if (!mHost && !host) {
+ state.os->memory.DeleteChunk(address);
+ state.os->memory.InsertChunk(chunk);
+ }
+ if ((mHost && !host) || (!mHost && !host)) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x8 = __NR_munmap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, owner);
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
throw exception("An error occurred while unmapping transfer memory in child process");
- } else {
+ } else if ((!mHost && host) || (mHost && host)) {
if (reinterpret_cast(munmap(reinterpret_cast(address), size)) == MAP_FAILED)
- throw exception("An error occurred while unmapping transfer memory in kernel");
+ throw exception("An error occurred while unmapping transfer memory in host: {}");
}
- owner = process;
- cAddress = address;
- cSize = size;
+ host = mHost;
+ address = nAddress;
+ size = nSize;
return address;
}
- memory::MemoryInfo KTransferMemory::GetInfo() {
- memory::MemoryInfo info{};
- info.baseAddress = cAddress;
- info.size = cSize;
- info.type = static_cast(memory::Type::TransferMemory);
- info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0);
- info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0);
- info.r = permission.r;
- info.w = permission.w;
- info.x = permission.x;
- info.ipcRefCount = ipcRefCount;
- info.deviceRefCount = deviceRefCount;
- return info;
+ void KTransferMemory::Resize(size_t nSize) {
+ if (host) {
+ if (mremap(reinterpret_cast(address), size, nSize, 0) == MAP_FAILED)
+ throw exception("An error occurred while remapping transfer memory in host: {}", strerror(errno));
+ } else {
+ Registers fregs{};
+ fregs.x0 = address;
+ fregs.x1 = size;
+ fregs.x2 = nSize;
+ fregs.x8 = __NR_mremap;
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
+ if (fregs.x0 < 0)
+ throw exception("An error occurred while remapping transfer memory in guest");
+ size = nSize;
+ auto chunk = state.os->memory.GetChunk(address);
+ MemoryManager::ResizeChunk(chunk, size);
+ }
+ }
+
+ void KTransferMemory::UpdatePermission(const u64 address, const u64 size, memory::Permission permission) {
+ BlockDescriptor block{
+ .address = address,
+ .size = size,
+ .permission = permission,
+ };
+ if (host) {
+ if (mprotect(reinterpret_cast(address), size, permission.Get()) == reinterpret_cast(MAP_FAILED))
+ throw exception("An occurred while remapping transfer memory: {}", strerror(errno));
+ MemoryManager::InsertBlock(&hostChunk, block);
+ } else {
+ Registers fregs{};
+ fregs.x0 = address;
+ fregs.x1 = size;
+ fregs.x2 = static_cast(permission.Get());
+ fregs.x8 = __NR_mprotect;
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
+ if (fregs.x0 < 0)
+ throw exception("An error occurred while updating transfer memory's permissions in guest");
+ auto chunk = state.os->memory.GetChunk(address);
+ MemoryManager::InsertBlock(chunk, block);
+ }
}
KTransferMemory::~KTransferMemory() {
- if (owner) {
+ if (host)
+ munmap(reinterpret_cast(address), size);
+ else if (state.process) {
try {
- if (state.process) {
- Registers fregs{};
- fregs.x0 = cAddress;
- fregs.x1 = cSize;
- fregs.x8 = __NR_munmap;
- state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
- }
+ Registers fregs{};
+ fregs.x0 = address;
+ fregs.x1 = size;
+ fregs.x8 = __NR_munmap;
+ state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
+ state.os->memory.DeleteChunk(address);
} catch (const std::exception &) {
}
- } else
- munmap(reinterpret_cast(cAddress), cSize);
+ }
}
};
diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h
index fd692f52..4f1ddb56 100644
--- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h
+++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h
@@ -1,45 +1,70 @@
#pragma once
-#include
-#include "KObject.h"
+#include "KMemory.h"
namespace skyline::kernel::type {
/**
* @brief KTransferMemory is used to hold a particular amount of transferable memory
*/
- class KTransferMemory : public KObject {
+ class KTransferMemory : public KMemory {
+ private:
+ ChunkDescriptor hostChunk{};
public:
- pid_t owner; //!< The PID of the process owning this memory
- u64 cAddress; //!< The current address of the allocated memory for the kernel
- size_t cSize; //!< The current size of the allocated memory
- u16 ipcRefCount{}; //!< The amount of reference to this memory for IPC
- u16 deviceRefCount{}; //!< The amount of reference to this memory for IPC
- memory::Permission permission; //!< The permissions of the memory
+ bool host; //!< If the memory is mapped on the host or the guest
+ u64 address; //!< The current address of the allocated memory for the kernel
+ size_t size; //!< The current size of the allocated memory
/**
* @param state The state of the device
- * @param pid The PID of the owner thread (Use 0 for kernel)
+ * @param host If to map the memory on host or guest
* @param address The address to map to (If NULL an arbitrary address is picked)
* @param size The size of the allocation
* @param permission The permissions of the memory
* @param type The type of the memory
+ * @param memState The MemoryState of the chunk of memory
*/
- KTransferMemory(const DeviceState &state, pid_t pid, u64 address, size_t size, const memory::Permission permission);
+ KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState = memory::MemoryStates::TransferMemory);
/**
* @brief Transfers this piece of memory to another process
- * @param process The PID of the process (Use 0 for kernel)
+ * @param host If to transfer memory to host or guest
* @param address The address to map to (If NULL an arbitrary address is picked)
* @param size The amount of shared memory to map
* @return The address of the allocation
*/
- u64 Transfer(pid_t process, u64 address, u64 size);
+ u64 Transfer(bool host, u64 address, u64 size = 0);
/**
- * @brief Returns a MemoryInfo struct filled with attributes of this region of memory
- * @return A memory::MemoryInfo struct based on attributes of the memory
+ * @brief Remap a chunk of memory as to change the size occupied by it
+ * @param size The new size of the memory
+ * @return The address the memory was remapped to
*/
- memory::MemoryInfo GetInfo();
+ virtual void Resize(size_t size);
+
+ /**
+ * @brief Updates the permissions of a block of mapped memory
+ * @param address The starting address to change the permissions at
+ * @param size The size of the partition to change the permissions of
+ * @param permission The new permissions to be set for the memory
+ */
+ virtual void UpdatePermission(const u64 address, const u64 size, memory::Permission permission);
+
+ /**
+ * @brief Updates the permissions of a chunk of mapped memory
+ * @param permission The new permissions to be set for the memory
+ */
+ inline virtual void UpdatePermission(memory::Permission permission) {
+ UpdatePermission(address, size, permission);
+ }
+
+ /**
+ * @brief Checks if the specified address is within the memory object
+ * @param address The address to check
+ * @return If the address is inside the memory object
+ */
+ inline virtual bool IsInside(u64 address) {
+ return (this->address <= address) && ((this->address + this->size) > address);
+ }
/**
* @brief The destructor of private memory, it deallocates the memory
diff --git a/app/src/main/cpp/skyline/loader/loader.h b/app/src/main/cpp/skyline/loader/loader.h
index d81deda1..b3832fd0 100644
--- a/app/src/main/cpp/skyline/loader/loader.h
+++ b/app/src/main/cpp/skyline/loader/loader.h
@@ -22,8 +22,6 @@ namespace skyline::loader {
}
public:
- u64 mainEntry{}; //!< The address of the actual entry point for the application
-
/**
* @param filePath The path to the ROM file
*/
diff --git a/app/src/main/cpp/skyline/loader/nro.cpp b/app/src/main/cpp/skyline/loader/nro.cpp
index 74010a56..8a3fa874 100644
--- a/app/src/main/cpp/skyline/loader/nro.cpp
+++ b/app/src/main/cpp/skyline/loader/nro.cpp
@@ -1,4 +1,5 @@
#include
+#include
#include "nro.h"
namespace skyline::loader {
@@ -6,7 +7,6 @@ namespace skyline::loader {
ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader));
if (header.magic != constant::NroMagic)
throw exception("Invalid NRO magic! 0x{0:X}", header.magic);
- mainEntry = constant::BaseAddr;
}
void NroLoader::LoadProcessData(const std::shared_ptr process, const DeviceState &state) {
@@ -18,31 +18,34 @@ namespace skyline::loader {
ReadOffset(rodata.data(), header.ro.offset, header.ro.size);
ReadOffset(data.data(), header.data.offset, header.data.size);
- std::vector patch = state.nce->PatchCode(text, constant::BaseAddr, header.text.size + header.ro.size + header.data.size + header.bssSize);
+ std::vector patch = state.nce->PatchCode(text, constant::BaseAddress, header.text.size + header.ro.size + header.data.size + header.bssSize);
u64 textSize = text.size();
u64 rodataSize = rodata.size();
u64 dataSize = data.size();
u64 patchSize = patch.size() * sizeof(u32);
+ u64 padding = utils::AlignUp(textSize + rodataSize + dataSize + header.bssSize + patchSize, PAGE_SIZE) - (textSize + rodataSize + dataSize + header.bssSize + patchSize);
- process->MapPrivateRegion(constant::BaseAddr, textSize, {true, true, true}, memory::Type::CodeStatic, memory::Region::Text); // R-X
- state.logger->Debug("Successfully mapped region .text @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr, textSize);
+ process->NewHandle(constant::BaseAddress, textSize, memory::Permission{true, true, true}, memory::MemoryStates::CodeStatic); // R-X
+ state.logger->Debug("Successfully mapped section .text @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress, textSize);
- process->MapPrivateRegion(constant::BaseAddr + textSize, rodataSize, {true, false, false}, memory::Type::CodeReadOnly, memory::Region::RoData); // R--
- state.logger->Debug("Successfully mapped region .ro @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize, rodataSize);
+ process->NewHandle(constant::BaseAddress + textSize, rodataSize, memory::Permission{true, false, false}, memory::MemoryStates::CodeReadOnly); // R--
+ state.logger->Debug("Successfully mapped section .ro @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize, rodataSize);
- process->MapPrivateRegion(constant::BaseAddr + textSize + rodataSize, dataSize, {true, true, false}, memory::Type::CodeStatic, memory::Region::Data); // RW-
- state.logger->Debug("Successfully mapped region .data @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize + rodataSize, dataSize);
+ process->NewHandle(constant::BaseAddress + textSize + rodataSize, dataSize, memory::Permission{true, true, false}, memory::MemoryStates::CodeStatic); // RW-
+ state.logger->Debug("Successfully mapped section .data @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize + rodataSize, dataSize);
- process->MapPrivateRegion(constant::BaseAddr + textSize + rodataSize + dataSize, header.bssSize, {true, true, true}, memory::Type::CodeMutable, memory::Region::Bss); // RWX
- state.logger->Debug("Successfully mapped region .bss @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize + rodataSize + dataSize, header.bssSize);
+ process->NewHandle(constant::BaseAddress + textSize + rodataSize + dataSize, header.bssSize, memory::Permission{true, true, true}, memory::MemoryStates::CodeMutable); // RWX
+ state.logger->Debug("Successfully mapped section .bss @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize + rodataSize + dataSize, header.bssSize);
- process->MapPrivateRegion(constant::BaseAddr + textSize + rodataSize + dataSize + header.bssSize, patchSize, {true, true, true}, memory::Type::CodeStatic, memory::Region::Text); // RWX
- state.logger->Debug("Successfully mapped region .patch @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize + rodataSize + dataSize + header.bssSize, patchSize);
+ process->NewHandle(constant::BaseAddress + textSize + rodataSize + dataSize + header.bssSize, patchSize + padding, memory::Permission{true, true, true}, memory::MemoryStates::CodeStatic); // RWX
+ state.logger->Debug("Successfully mapped section .patch @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize + rodataSize + dataSize + header.bssSize, patchSize);
- process->WriteMemory(text.data(), constant::BaseAddr, textSize);
- process->WriteMemory(rodata.data(), constant::BaseAddr + textSize, rodataSize);
- process->WriteMemory(data.data(), constant::BaseAddr + textSize + rodataSize, dataSize);
- process->WriteMemory(patch.data(), constant::BaseAddr + textSize + rodataSize + dataSize + header.bssSize, patchSize);
+ process->WriteMemory(text.data(), constant::BaseAddress, textSize);
+ process->WriteMemory(rodata.data(), constant::BaseAddress + textSize, rodataSize);
+ process->WriteMemory(data.data(), constant::BaseAddress + textSize + rodataSize, dataSize);
+ process->WriteMemory(patch.data(), constant::BaseAddress + textSize + rodataSize + dataSize + header.bssSize, patchSize);
+
+ state.os->memory.InitializeRegions(constant::BaseAddress, textSize + rodataSize + dataSize + header.bssSize + patchSize + padding, memory::AddressSpaceType::AddressSpace39Bit);
}
}
diff --git a/app/src/main/cpp/skyline/memory.h b/app/src/main/cpp/skyline/memory.h
deleted file mode 100644
index 7eed229f..00000000
--- a/app/src/main/cpp/skyline/memory.h
+++ /dev/null
@@ -1,138 +0,0 @@
-#pragma once
-
-#include "common.h"
-
-namespace skyline::memory {
- /**
- * @brief The Permission struct holds the permission of a particular chunk of memory
- */
- struct Permission {
- /**
- * @brief This constructor initializes all permissions to false
- */
- Permission() {
- r = 0;
- w = 0;
- x = 0;
- };
-
- /**
- * @param read If memory has read permission
- * @param write If memory has write permission
- * @param execute If memory has execute permission
- */
- 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 mmap(2) format
- */
- int Get() const {
- int perm = 0;
- if (r)
- perm |= PROT_READ;
- if (w)
- perm |= PROT_WRITE;
- if (x)
- perm |= PROT_EXEC;
- return perm;
- };
-
- bool r;
- bool w;
- bool x;
- };
-
- /**
- * @brief This holds certain attributes of a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryAttribute
- */
- union MemoryAttribute {
- struct {
- bool isBorrowed : 1;
- bool isIpcLocked : 1;
- bool isDeviceShared : 1;
- bool isUncached : 1;
- };
- u32 value;
- };
- static_assert(sizeof(MemoryAttribute) == sizeof(u32));
-
- /**
- * @brief This describes the properties of a region of the allocated memory
- */
- struct RegionInfo {
- u64 address; //!< The starting address of the chunk of memory
- u64 size; //!< The size of the chunk of memory
- bool isUncached; //!< If the following region is uncached
-
- RegionInfo(u64 address, u64 size, bool isUncached) : address(address), size(size), isUncached(isUncached) {}
- };
-
- /**
- * @brief This contains information about a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryInfo
- */
- struct MemoryInfo {
- u64 baseAddress;
- u64 size;
- u32 type;
- MemoryAttribute memoryAttribute;
- union {
- u32 _pad0_;
- struct {
- bool r : 1, w : 1, x : 1;
- };
- };
- u32 ipcRefCount;
- u32 deviceRefCount;
- u32 : 32;
- };
- static_assert(sizeof(MemoryInfo) == 0x28);
-
- /**
- * @brief These are specific markers for the type of a memory region
- */
- enum class Type : u32 {
- Unmapped = 0x00000000,
- Io = 0x00002001,
- Normal = 0x00042002,
- CodeStatic = 0x00DC7E03,
- CodeMutable = 0x03FEBD04,
- Heap = 0x037EBD05,
- SharedMemory = 0x00402006,
- Alias = 0x00482907,
- ModuleCodeStatic = 0x00DD7E08,
- ModuleCodeMutable = 0x03FFBD09,
- Ipc = 0x005C3C0A,
- Stack = 0x005C3C0B,
- ThreadLocal = 0x0040200C,
- TransferMemoryIsolated = 0x015C3C0D,
- TransferMemory = 0x005C380E,
- ProcessMemory = 0x0040380F,
- Reserved = 0x00000010,
- NonSecureIpc = 0x005C3811,
- NonDeviceIpc = 0x004C2812,
- KernelStack = 0x00002013,
- CodeReadOnly = 0x00402214,
- CodeWritable = 0x00402015
- };
-
- /**
- * @brief Memory Regions that are mapped by the kernel
- */
- enum class Region {
- Heap, Text, RoData, Data, Bss
- };
-}
diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp
index 793ffd74..63405470 100644
--- a/app/src/main/cpp/skyline/nce.cpp
+++ b/app/src/main/cpp/skyline/nce.cpp
@@ -5,6 +5,7 @@
#include "nce/guest.h"
#include "nce/instr.h"
#include "kernel/svc.h"
+#include "nce.h"
extern bool Halt;
extern skyline::GroupMutex jniMtx;
@@ -12,8 +13,8 @@ extern skyline::GroupMutex jniMtx;
namespace skyline {
void NCE::KernelThread(pid_t thread) {
try {
- state.thread = state.process->threadMap.at(thread);
- state.ctx = reinterpret_cast(state.thread->ctxMemory->guest.address);
+ state.thread = state.process->threads.at(thread);
+ state.ctx = reinterpret_cast(state.thread->ctxMemory->kernel.address);
while (true) {
std::lock_guard jniGd(jniMtx);
if (Halt)
@@ -91,11 +92,9 @@ namespace skyline {
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(thread->ctxMemory->kernel.address));
}
- void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, pid_t pid) {
- if (state.process->status != kernel::type::KProcess::Status::Exiting)
- ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(state.process->threadMap.at(pid)->ctxMemory->kernel.address));
- else
- throw std::out_of_range("The KProcess object is missing");
+ void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs) {
+ auto thread = state.thread ? state.thread : state.process->threads.at(state.process->pid);
+ ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(thread->ctxMemory->kernel.address));
}
void NCE::WaitThreadInit(std::shared_ptr &thread) __attribute__ ((optnone)) {
@@ -339,7 +338,6 @@ namespace skyline {
offset -= sizeof(u32);
patchOffset -= sizeof(u32);
}
- patch.resize(patch.size() + PAGE_SIZE - 1 & ~(PAGE_SIZE - 1), 0x0);
return patch;
}
}
diff --git a/app/src/main/cpp/skyline/nce.h b/app/src/main/cpp/skyline/nce.h
index cf5e4c7f..e5b51d51 100644
--- a/app/src/main/cpp/skyline/nce.h
+++ b/app/src/main/cpp/skyline/nce.h
@@ -37,7 +37,7 @@ namespace skyline {
void Execute();
/**
- * @brief Execute any arbitrary function on a particular child process
+ * @brief Execute any arbitrary function on a specific child thread
* @param call The specific call to execute
* @param funcRegs A set of registers to run the function
* @param thread The thread to execute the function on
@@ -45,12 +45,11 @@ namespace skyline {
void ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr &thread);
/**
- * @brief Execute any arbitrary function on a particular child process
+ * @brief Execute any arbitrary function on the child process
* @param call The specific call to execute
* @param funcRegs A set of registers to run the function
- * @param pid The PID of the process
*/
- void ExecuteFunction(ThreadCall call, Registers &funcRegs, pid_t pid);
+ void ExecuteFunction(ThreadCall call, Registers &funcRegs);
/**
* @brief Waits till a thread is ready to execute commands
diff --git a/app/src/main/cpp/skyline/nce/guest.cpp b/app/src/main/cpp/skyline/nce/guest.cpp
index 8f4c1126..e492c8fb 100644
--- a/app/src/main/cpp/skyline/nce/guest.cpp
+++ b/app/src/main/cpp/skyline/nce/guest.cpp
@@ -172,6 +172,13 @@ namespace skyline::guest {
"LDR LR, [SP], #16");
saveCtxTls();
loadCtxStack();
+ } else if (ctx->commandId == static_cast(ThreadCall::Memcopy)) {
+ auto src = reinterpret_cast(ctx->registers.x0);
+ auto dest = reinterpret_cast(ctx->registers.x1);
+ auto size = ctx->registers.x2;
+ auto end = src + size;
+ while (src < end)
+ *(src++) = *(dest++);
}
}
}
@@ -214,6 +221,13 @@ namespace skyline::guest {
saveCtxTls();
loadCtxStack();
}
+ } else if (ctx->commandId == static_cast(ThreadCall::Memcopy)) {
+ auto src = reinterpret_cast(ctx->registers.x0);
+ auto dest = reinterpret_cast(ctx->registers.x1);
+ auto size = ctx->registers.x2;
+ auto end = src + size;
+ while (src < end)
+ *(src++) = *(dest++);
}
}
struct sigaction sigact{
diff --git a/app/src/main/cpp/skyline/nce/guest.h b/app/src/main/cpp/skyline/nce/guest.h
index 0479fc6b..aee5e85d 100644
--- a/app/src/main/cpp/skyline/nce/guest.h
+++ b/app/src/main/cpp/skyline/nce/guest.h
@@ -5,9 +5,9 @@ namespace skyline {
constexpr size_t saveCtxSize = 20 * sizeof(u32);
constexpr size_t loadCtxSize = 20 * sizeof(u32);
#ifdef NDEBUG
- constexpr size_t svcHandlerSize = 150 * sizeof(u32);
+ constexpr size_t svcHandlerSize = 175 * sizeof(u32);
#else
- constexpr size_t svcHandlerSize = 250 * sizeof(u32);
+ constexpr size_t svcHandlerSize = 275 * sizeof(u32);
#endif
void entry(u64 address);
diff --git a/app/src/main/cpp/skyline/nce/guest_common.h b/app/src/main/cpp/skyline/nce/guest_common.h
index c9a9eaf0..caad126d 100644
--- a/app/src/main/cpp/skyline/nce/guest_common.h
+++ b/app/src/main/cpp/skyline/nce/guest_common.h
@@ -121,12 +121,12 @@ namespace skyline {
* @brief This enumeration is used to convey the state of a thread to the kernel
*/
enum class ThreadState : u32 {
- NotReady = 0, //!< The thread hasn't yet entered the entry handler
- Running = 1, //!< The thread is currently executing code
+ NotReady = 0, //!< The thread hasn't yet entered the entry handler
+ Running = 1, //!< The thread is currently executing code
WaitKernel = 2, //!< The thread is currently waiting on the kernel
- WaitRun = 3, //!< The thread should be ready to run
- WaitInit = 4, //!< The thread is waiting to be initialized
- WaitFunc = 5, //!< The kernel is waiting for the thread to run a function
+ WaitRun = 3, //!< The thread should be ready to run
+ WaitInit = 4, //!< The thread is waiting to be initialized
+ WaitFunc = 5, //!< The kernel is waiting for the thread to run a function
GuestCrash = 6, //!< This is a notification to the kernel that the guest has crashed
};
@@ -135,6 +135,7 @@ namespace skyline {
*/
enum class ThreadCall : u32 {
Syscall = 0x100, //!< A linux syscall needs to be called from the guest
+ Memcopy = 0x101, //!< To copy memory from one location to another
};
/**
diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp
index d3a1dd2b..3af86e0c 100644
--- a/app/src/main/cpp/skyline/os.cpp
+++ b/app/src/main/cpp/skyline/os.cpp
@@ -3,7 +3,7 @@
#include "nce/guest.h"
namespace skyline::kernel {
- OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings) : state(this, process, jvmManager, settings, logger), serviceManager(state) {}
+ OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings) : state(this, process, jvmManager, settings, logger), memory(state), serviceManager(state) {}
void OS::Execute(const int romFd, const TitleFormat romType) {
std::shared_ptr loader;
@@ -11,9 +11,10 @@ namespace skyline::kernel {
loader = std::make_shared(romFd);
} else
throw exception("Unsupported ROM extension.");
- auto process = CreateProcess(loader->mainEntry, 0, constant::DefStackSize);
+ auto process = CreateProcess(constant::BaseAddress, 0, constant::DefStackSize);
loader->LoadProcessData(process, state);
- process->threadMap.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread
+ process->InitializeMemory();
+ process->threads.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread
state.nce->Execute();
}
@@ -25,7 +26,7 @@ namespace skyline::kernel {
munmap(stack, stackSize);
throw exception("Failed to create guard pages");
}
- auto tlsMem = std::make_shared(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, false), memory::Type::Reserved);
+ auto tlsMem = std::make_shared(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, false), memory::MemoryStates::Reserved);
tlsMem->guest = tlsMem->kernel;
pid_t pid = clone(reinterpret_cast(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast(entry), nullptr, reinterpret_cast(tlsMem->guest.address));
if (pid == -1)
@@ -39,11 +40,11 @@ namespace skyline::kernel {
void OS::KillThread(pid_t pid) {
if (process->pid == pid) {
state.logger->Debug("Killing process with PID: {}", pid);
- for (auto &thread: process->threadMap)
+ for (auto &thread: process->threads)
thread.second->Kill();
} else {
state.logger->Debug("Killing thread with TID: {}", pid);
- process->threadMap.at(pid)->Kill();
+ process->threads.at(pid)->Kill();
}
}
}
diff --git a/app/src/main/cpp/skyline/os.h b/app/src/main/cpp/skyline/os.h
index b4c3a4c5..b7d92463 100644
--- a/app/src/main/cpp/skyline/os.h
+++ b/app/src/main/cpp/skyline/os.h
@@ -20,6 +20,7 @@ namespace skyline::kernel {
public:
std::shared_ptr process; //!< The KProcess object for the emulator, representing the guest process
service::ServiceManager serviceManager; //!< This manages all of the service functions
+ MemoryManager memory; //!< The MemoryManager object for this process
/**
* @param logger An instance of the Logger class
diff --git a/app/src/main/cpp/skyline/services/hid/hid.cpp b/app/src/main/cpp/skyline/services/hid/hid.cpp
index d8d51645..deff2f45 100644
--- a/app/src/main/cpp/skyline/services/hid/hid.cpp
+++ b/app/src/main/cpp/skyline/services/hid/hid.cpp
@@ -7,7 +7,7 @@ namespace skyline::service::hid {
}) {}
void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
- hidSharedMemory = std::make_shared(state, NULL, constant::hidSharedMemSize, memory::Permission(true, false, false), memory::Type::SharedMemory);
+ hidSharedMemory = std::make_shared(state, NULL, constant::hidSharedMemSize, memory::Permission{true, false, false});
auto handle = state.process->InsertItem(hidSharedMemory);
state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle);