mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-29 14:55:28 +03:00
Refactor the memory implementation and add Regions
This commit does a major refactor of the memory implementation, it forms a memory map which is far cleaner than trying to access it through a handle table lookup. In addition, it creates a common interface for all memory kernel objects: KMemory from which all other kernel memory objects inherit. This allows doing resizing, permission change, etc without casting to the base memory type.
This commit is contained in:
parent
b13002f0e1
commit
00cdc1fd6f
9
.idea/codeStyles/Project.xml
generated
9
.idea/codeStyles/Project.xml
generated
@ -13,12 +13,9 @@
|
|||||||
<option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" />
|
<option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" />
|
||||||
<option name="FUNCTION_NON_TOP_AFTER_RETURN_TYPE_WRAP" value="0" />
|
<option name="FUNCTION_NON_TOP_AFTER_RETURN_TYPE_WRAP" value="0" />
|
||||||
<option name="FUNCTION_TOP_AFTER_RETURN_TYPE_WRAP" value="0" />
|
<option name="FUNCTION_TOP_AFTER_RETURN_TYPE_WRAP" value="0" />
|
||||||
<option name="FUNCTION_PARAMETERS_WRAP" value="5" />
|
<option name="FUNCTION_PARAMETERS_WRAP" value="0" />
|
||||||
<option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="5" />
|
<option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="5" />
|
||||||
<option name="SHIFT_OPERATION_WRAP" value="0" />
|
<option name="SHIFT_OPERATION_WRAP" value="0" />
|
||||||
<option name="TEMPLATE_DECLARATION_STRUCT_WRAP" value="1" />
|
|
||||||
<option name="TEMPLATE_DECLARATION_FUNCTION_WRAP" value="1" />
|
|
||||||
<option name="TEMPLATE_CALL_ARGUMENTS_WRAP" value="5" />
|
|
||||||
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" />
|
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" />
|
||||||
<option name="CLASS_CONSTRUCTOR_INIT_LIST_WRAP" value="0" />
|
<option name="CLASS_CONSTRUCTOR_INIT_LIST_WRAP" value="0" />
|
||||||
<option name="SUPERCLASS_LIST_WRAP" value="0" />
|
<option name="SUPERCLASS_LIST_WRAP" value="0" />
|
||||||
@ -79,8 +76,12 @@
|
|||||||
<option name="BLANK_LINES_AROUND_CLASS" value="0" />
|
<option name="BLANK_LINES_AROUND_CLASS" value="0" />
|
||||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="false" />
|
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="false" />
|
||||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="TERNARY_OPERATION_WRAP" value="0" />
|
||||||
<option name="FOR_STATEMENT_WRAP" value="1" />
|
<option name="FOR_STATEMENT_WRAP" value="1" />
|
||||||
|
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
|
||||||
|
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
|
||||||
<option name="ASSIGNMENT_WRAP" value="1" />
|
<option name="ASSIGNMENT_WRAP" value="1" />
|
||||||
|
<option name="SOFT_MARGINS" value="140" />
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
<option name="LABEL_INDENT_SIZE" value="-2" />
|
<option name="LABEL_INDENT_SIZE" value="-2" />
|
||||||
|
@ -36,6 +36,7 @@ add_library(skyline SHARED
|
|||||||
${source_DIR}/skyline/gpu/devices/nvhost_as_gpu.cpp
|
${source_DIR}/skyline/gpu/devices/nvhost_as_gpu.cpp
|
||||||
${source_DIR}/skyline/os.cpp
|
${source_DIR}/skyline/os.cpp
|
||||||
${source_DIR}/skyline/loader/nro.cpp
|
${source_DIR}/skyline/loader/nro.cpp
|
||||||
|
${source_DIR}/skyline/kernel/memory.cpp
|
||||||
${source_DIR}/skyline/kernel/ipc.cpp
|
${source_DIR}/skyline/kernel/ipc.cpp
|
||||||
${source_DIR}/skyline/kernel/svc.cpp
|
${source_DIR}/skyline/kernel/svc.cpp
|
||||||
${source_DIR}/skyline/kernel/types/KProcess.cpp
|
${source_DIR}/skyline/kernel/types/KProcess.cpp
|
||||||
|
@ -23,11 +23,8 @@ namespace skyline {
|
|||||||
|
|
||||||
namespace constant {
|
namespace constant {
|
||||||
// Memory
|
// Memory
|
||||||
constexpr u64 BaseAddr = 0x8000000; //!< The address space base
|
constexpr u64 BaseAddress = 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 BaseEnd = 0x7FFFFFFFFF; //!< The end of the address space
|
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 u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM
|
||||||
constexpr size_t DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB
|
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
|
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
|
constexpr u32 NroMagic = 0x304F524E; //!< "NRO0" in reverse, this is written at the start of every NRO file
|
||||||
// NCE
|
// NCE
|
||||||
constexpr u8 NumRegs = 30; //!< The amount of registers that ARMv8 has
|
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 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS
|
||||||
constexpr u32 CntfrqEl0 = 0x5F00; //!< ID of CNTFRQ_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)
|
constexpr u32 TegraX1Freq = 0x124F800; //!< The clock frequency of the Tegra X1 (19.2 MHz)
|
||||||
@ -53,7 +48,6 @@ namespace skyline {
|
|||||||
constexpr std::pair<int8_t, int8_t> PriorityAn = {19, -8}; //!< The range of priority for Android, taken from https://medium.com/mindorks/exploring-android-thread-priority-5d0542eebbd1
|
constexpr std::pair<int8_t, int8_t> PriorityAn = {19, -8}; //!< The range of priority for Android, taken from https://medium.com/mindorks/exploring-android-thread-priority-5d0542eebbd1
|
||||||
constexpr std::pair<u8, u8> PriorityNin = {0, 63}; //!< The range of priority for the Nintendo Switch
|
constexpr std::pair<u8, u8> 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 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
|
// IPC
|
||||||
constexpr size_t TlsIpcSize = 0x100; //!< The size of the IPC command buffer in a TLS slot
|
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
|
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 ServiceNotReg = 0xE15; //!< "Service not registered"
|
||||||
constexpr u32 InvSize = 0xCA01; //!< "Invalid size"
|
constexpr u32 InvSize = 0xCA01; //!< "Invalid size"
|
||||||
constexpr u32 InvAddress = 0xCC01; //!< "Invalid address"
|
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 InvHandle = 0xE401; //!< "Invalid handle"
|
||||||
constexpr u32 InvCombination = 0xE801; //!< "Invalid combination"
|
constexpr u32 InvCombination = 0xE801; //!< "Invalid combination"
|
||||||
constexpr u32 MaxHandles = 0xEE01; //!< "Too many handles"
|
|
||||||
constexpr u32 Timeout = 0xEA01; //!< "Timeout"
|
constexpr u32 Timeout = 0xEA01; //!< "Timeout"
|
||||||
|
constexpr u32 MaxHandles = 0xEE01; //!< "Too many handles"
|
||||||
constexpr u32 NotFound = 0xF201; //!< "Not found"
|
constexpr u32 NotFound = 0xF201; //!< "Not found"
|
||||||
constexpr u32 Unimpl = 0x177202; //!< "Unimplemented behaviour"
|
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
|
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<u64>(std::chrono::duration_cast<std::chrono::nanoseconds>(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 <typename TypeVal, typename TypeMul>
|
||||||
|
inline TypeVal AlignUp(TypeVal value, TypeMul multiple) {
|
||||||
|
static_assert(std::is_integral<TypeVal>() && std::is_integral<TypeMul>());
|
||||||
|
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 <typename TypeVal, typename TypeMul>
|
||||||
|
inline TypeVal AlignDown(TypeVal value, TypeMul multiple) {
|
||||||
|
static_assert(std::is_integral<TypeVal>() && std::is_integral<TypeMul>());
|
||||||
|
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
|
* @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...)) {}
|
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<u64>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
|
|
||||||
}
|
|
||||||
|
|
||||||
class NCE;
|
class NCE;
|
||||||
class JvmManager;
|
class JvmManager;
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
@ -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<BufferDescriptorX *>(currPtr);
|
auto bufX = reinterpret_cast<BufferDescriptorX *>(currPtr);
|
||||||
if (bufX->Address()) {
|
if (bufX->Address()) {
|
||||||
inputBuf.emplace_back(bufX);
|
inputBuf.emplace_back(bufX);
|
||||||
@ -41,7 +41,7 @@ namespace skyline::kernel::ipc {
|
|||||||
currPtr += sizeof(BufferDescriptorX);
|
currPtr += sizeof(BufferDescriptorX);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint index = 0; header->Ano > index; index++) {
|
for (uint index = 0; header->aNo > index; index++) {
|
||||||
auto bufA = reinterpret_cast<BufferDescriptorABW *>(currPtr);
|
auto bufA = reinterpret_cast<BufferDescriptorABW *>(currPtr);
|
||||||
if (bufA->Address()) {
|
if (bufA->Address()) {
|
||||||
inputBuf.emplace_back(bufA);
|
inputBuf.emplace_back(bufA);
|
||||||
@ -50,7 +50,7 @@ namespace skyline::kernel::ipc {
|
|||||||
currPtr += sizeof(BufferDescriptorABW);
|
currPtr += sizeof(BufferDescriptorABW);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint index = 0; header->Bno > index; index++) {
|
for (uint index = 0; header->bNo > index; index++) {
|
||||||
auto bufB = reinterpret_cast<BufferDescriptorABW *>(currPtr);
|
auto bufB = reinterpret_cast<BufferDescriptorABW *>(currPtr);
|
||||||
if (bufB->Address()) {
|
if (bufB->Address()) {
|
||||||
outputBuf.emplace_back(bufB);
|
outputBuf.emplace_back(bufB);
|
||||||
@ -59,7 +59,7 @@ namespace skyline::kernel::ipc {
|
|||||||
currPtr += sizeof(BufferDescriptorABW);
|
currPtr += sizeof(BufferDescriptorABW);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint index = 0; header->Wno > index; index++) {
|
for (uint index = 0; header->wNo > index; index++) {
|
||||||
auto bufW = reinterpret_cast<BufferDescriptorABW *>(currPtr);
|
auto bufW = reinterpret_cast<BufferDescriptorABW *>(currPtr);
|
||||||
if (bufW->Address()) {
|
if (bufW->Address()) {
|
||||||
inputBuf.emplace_back(bufW, IpcBufferType::W);
|
inputBuf.emplace_back(bufW, IpcBufferType::W);
|
||||||
|
@ -23,10 +23,10 @@ namespace skyline::kernel::ipc {
|
|||||||
*/
|
*/
|
||||||
struct CommandHeader {
|
struct CommandHeader {
|
||||||
CommandType type : 16;
|
CommandType type : 16;
|
||||||
u8 Xno : 4;
|
u8 xNo : 4;
|
||||||
u8 Ano : 4;
|
u8 aNo : 4;
|
||||||
u8 Bno : 4;
|
u8 bNo : 4;
|
||||||
u8 Wno : 4;
|
u8 wNo : 4;
|
||||||
u32 rawSize : 10;
|
u32 rawSize : 10;
|
||||||
BufferCFlag cFlag : 4;
|
BufferCFlag cFlag : 4;
|
||||||
u32 : 17;
|
u32 : 17;
|
||||||
|
158
app/src/main/cpp/skyline/kernel/memory.cpp
Normal file
158
app/src/main/cpp/skyline/kernel/memory.cpp
Normal file
@ -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<DescriptorPack> 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;
|
||||||
|
}
|
||||||
|
}
|
357
app/src/main/cpp/skyline/kernel/memory.h
Normal file
357
app/src/main/cpp/skyline/kernel/memory.h
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
#include "types/KObject.h"
|
||||||
|
#include <forward_list>
|
||||||
|
|
||||||
|
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<BlockDescriptor> 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<ChunkDescriptor> 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<DescriptorPack> 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();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -10,109 +10,119 @@ namespace skyline::kernel::svc {
|
|||||||
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
|
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::shared_ptr<type::KPrivateMemory> heap;
|
auto& heap = state.process->heap;
|
||||||
try {
|
heap->Resize(size);
|
||||||
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;
|
|
||||||
}
|
|
||||||
state.ctx->registers.w0 = constant::status::Success;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
state.ctx->registers.x1 = heap->address;
|
state.ctx->registers.x1 = heap->address;
|
||||||
state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size);
|
state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMemoryAttribute(DeviceState &state) {
|
void SetMemoryAttribute(DeviceState &state) {
|
||||||
const u64 addr = state.ctx->registers.x0;
|
const u64 address = state.ctx->registers.x0;
|
||||||
if ((addr & (PAGE_SIZE - 1U))) {
|
if (!utils::PageAligned(address)) {
|
||||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const u64 size = state.ctx->registers.x1;
|
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.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;
|
return;
|
||||||
}
|
}
|
||||||
u32 mask = state.ctx->registers.w2;
|
memory::MemoryAttribute mask{.value = state.ctx->registers.w2};
|
||||||
u32 value = state.ctx->registers.w3;
|
memory::MemoryAttribute value{.value = state.ctx->registers.w3};
|
||||||
u32 maskedValue = mask | value;
|
u32 maskedValue = mask.value | value.value;
|
||||||
if (maskedValue != mask) {
|
if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) {
|
||||||
state.ctx->registers.w0 = constant::status::InvCombination;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
memory::MemoryAttribute attribute = *reinterpret_cast<memory::MemoryAttribute *>(&maskedValue);
|
auto chunk = state.os->memory.GetChunk(address);
|
||||||
bool found = false;
|
auto block = state.os->memory.GetBlock(address);
|
||||||
for (const auto&[address, region] : state.process->memoryMap) {
|
if (!chunk || !block) {
|
||||||
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<bool>(attribute.isUncached));
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
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;
|
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<type::KPrivateMemory>(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;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueryMemory(DeviceState &state) {
|
void QueryMemory(DeviceState &state) {
|
||||||
|
u64 address = state.ctx->registers.x2;
|
||||||
memory::MemoryInfo memInfo{};
|
memory::MemoryInfo memInfo{};
|
||||||
u64 addr = state.ctx->registers.x2 & ~(PAGE_SIZE - 1);
|
auto descriptor = state.os->memory.Get(address);
|
||||||
bool found = false;
|
if (descriptor) {
|
||||||
for (const auto&[address, region] : state.process->memoryMap) {
|
|
||||||
if (addr >= address && addr < (address + region->size)) {
|
|
||||||
memInfo = region->GetInfo(addr);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
for (const auto &object : state.process->handleTable) {
|
|
||||||
if (object.second->objectType == type::KType::KSharedMemory) {
|
|
||||||
const auto &mem = state.process->GetHandle<type::KSharedMemory>(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<type::KTransferMemory>(object.first);
|
|
||||||
if (addr >= mem->cAddress && addr < (mem->cAddress + mem->cSize)) {
|
|
||||||
memInfo = mem->GetInfo();
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
memInfo = {
|
memInfo = {
|
||||||
.baseAddress = constant::BaseAddr,
|
.address = descriptor->block.address,
|
||||||
.size = static_cast<u64>(constant::BaseEnd),
|
.size = descriptor->block.size,
|
||||||
.type = static_cast<u64>(memory::Type::Unmapped)
|
.type = static_cast<u32>(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,
|
||||||
};
|
};
|
||||||
state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", addr);
|
} else {
|
||||||
|
memInfo = {
|
||||||
|
.address = constant::BaseEnd,
|
||||||
|
.size = ~(constant::BaseEnd - 1),
|
||||||
|
.type = static_cast<u32>(memory::MemoryType::Unmapped)
|
||||||
|
};
|
||||||
|
state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", address);
|
||||||
}
|
}
|
||||||
}
|
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.address, memInfo.size, memInfo.type, static_cast<bool>(memInfo.attributes.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-");
|
||||||
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.baseAddress, memInfo.size, memInfo.type, static_cast<bool>(memInfo.memoryAttribute.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-");
|
state.process->WriteMemory(memInfo, state.ctx->registers.x0);
|
||||||
state.process->WriteMemory<memory::MemoryInfo>(memInfo, state.ctx->registers.x0);
|
|
||||||
state.ctx->registers.w0 = constant::status::Success;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,17 +132,17 @@ namespace skyline::kernel::svc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CreateThread(DeviceState &state) {
|
void CreateThread(DeviceState &state) {
|
||||||
u64 entryAddr = state.ctx->registers.x1;
|
u64 entryAddress = state.ctx->registers.x1;
|
||||||
u64 entryArg = state.ctx->registers.x2;
|
u64 entryArgument = state.ctx->registers.x2;
|
||||||
u64 stackTop = state.ctx->registers.x3;
|
u64 stackTop = state.ctx->registers.x3;
|
||||||
u8 priority = static_cast<u8>(state.ctx->registers.w4);
|
u8 priority = static_cast<u8>(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.ctx->registers.w0 = constant::status::InvAddress;
|
||||||
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
|
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto thread = state.process->CreateThread(entryAddr, entryArg, stackTop, priority);
|
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, entryAddr, entryArg, stackTop, priority, thread->pid);
|
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.w1 = thread->handle;
|
||||||
state.ctx->registers.w0 = constant::status::Success;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
}
|
}
|
||||||
@ -201,16 +211,16 @@ namespace skyline::kernel::svc {
|
|||||||
void MapSharedMemory(DeviceState &state) {
|
void MapSharedMemory(DeviceState &state) {
|
||||||
try {
|
try {
|
||||||
auto object = state.process->GetHandle<type::KSharedMemory>(state.ctx->registers.w0);
|
auto object = state.process->GetHandle<type::KSharedMemory>(state.ctx->registers.w0);
|
||||||
u64 addr = state.ctx->registers.x1;
|
u64 address = state.ctx->registers.x1;
|
||||||
if ((addr & (PAGE_SIZE - 1U))) {
|
if (!utils::PageAligned(address)) {
|
||||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const u64 size = state.ctx->registers.x2;
|
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.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;
|
return;
|
||||||
}
|
}
|
||||||
u32 perm = state.ctx->registers.w3;
|
u32 perm = state.ctx->registers.w3;
|
||||||
@ -220,8 +230,8 @@ namespace skyline::kernel::svc {
|
|||||||
state.ctx->registers.w0 = constant::status::InvPermission;
|
state.ctx->registers.w0 = constant::status::InvPermission;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
|
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(addr, size, permission);
|
object->Map(address, size, permission);
|
||||||
state.ctx->registers.w0 = constant::status::Success;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
} catch (const std::exception &) {
|
} catch (const std::exception &) {
|
||||||
state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->registers.w0);
|
state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->registers.w0);
|
||||||
@ -230,16 +240,16 @@ namespace skyline::kernel::svc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CreateTransferMemory(DeviceState &state) {
|
void CreateTransferMemory(DeviceState &state) {
|
||||||
u64 addr = state.ctx->registers.x1;
|
u64 address = state.ctx->registers.x1;
|
||||||
if ((addr & (PAGE_SIZE - 1U))) {
|
if (!utils::PageAligned(address)) {
|
||||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
u64 size = state.ctx->registers.x2;
|
u64 size = state.ctx->registers.x2;
|
||||||
if ((size & (PAGE_SIZE - 1U)) || !size) {
|
if (!utils::PageAligned(size)) {
|
||||||
state.ctx->registers.w0 = constant::status::InvSize;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
u32 perm = state.ctx->registers.w3;
|
u32 perm = state.ctx->registers.w3;
|
||||||
@ -249,8 +259,8 @@ namespace skyline::kernel::svc {
|
|||||||
state.ctx->registers.w0 = constant::status::InvPermission;
|
state.ctx->registers.w0 = constant::status::InvPermission;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
|
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<type::KTransferMemory>(state.process->pid, addr, size, permission);
|
auto shmem = state.process->NewHandle<type::KTransferMemory>(state.process->pid, address, size, permission);
|
||||||
state.ctx->registers.w0 = constant::status::Success;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
state.ctx->registers.w1 = shmem.handle;
|
state.ctx->registers.w1 = shmem.handle;
|
||||||
}
|
}
|
||||||
@ -258,7 +268,7 @@ namespace skyline::kernel::svc {
|
|||||||
void CloseHandle(DeviceState &state) {
|
void CloseHandle(DeviceState &state) {
|
||||||
auto handle = static_cast<handle_t>(state.ctx->registers.w0);
|
auto handle = static_cast<handle_t>(state.ctx->registers.w0);
|
||||||
try {
|
try {
|
||||||
state.process->handleTable.erase(handle);
|
state.process->handles.erase(handle);
|
||||||
state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle);
|
state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle);
|
||||||
state.ctx->registers.w0 = constant::status::Success;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
} catch (const std::exception &) {
|
} catch (const std::exception &) {
|
||||||
@ -270,7 +280,7 @@ namespace skyline::kernel::svc {
|
|||||||
void ResetSignal(DeviceState &state) {
|
void ResetSignal(DeviceState &state) {
|
||||||
auto handle = state.ctx->registers.w0;
|
auto handle = state.ctx->registers.w0;
|
||||||
try {
|
try {
|
||||||
auto &object = state.process->handleTable.at(handle);
|
auto &object = state.process->handles.at(handle);
|
||||||
switch (object->objectType) {
|
switch (object->objectType) {
|
||||||
case (type::KType::KEvent):
|
case (type::KType::KEvent):
|
||||||
std::static_pointer_cast<type::KEvent>(object)->ResetSignal();
|
std::static_pointer_cast<type::KEvent>(object)->ResetSignal();
|
||||||
@ -305,7 +315,7 @@ namespace skyline::kernel::svc {
|
|||||||
std::string handleStr;
|
std::string handleStr;
|
||||||
for (const auto &handle : waitHandles) {
|
for (const auto &handle : waitHandles) {
|
||||||
handleStr += fmt::format("* 0x{:X}\n", handle);
|
handleStr += fmt::format("* 0x{:X}\n", handle);
|
||||||
auto object = state.process->handleTable.at(handle);
|
auto object = state.process->handles.at(handle);
|
||||||
switch (object->objectType) {
|
switch (object->objectType) {
|
||||||
case type::KType::KProcess:
|
case type::KType::KProcess:
|
||||||
case type::KType::KThread:
|
case type::KType::KThread:
|
||||||
@ -321,7 +331,7 @@ namespace skyline::kernel::svc {
|
|||||||
}
|
}
|
||||||
auto timeout = state.ctx->registers.x3;
|
auto timeout = state.ctx->registers.x3;
|
||||||
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
|
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
|
||||||
auto start = GetCurrTimeNs();
|
auto start = utils::GetCurrTimeNs();
|
||||||
while (true) {
|
while (true) {
|
||||||
uint index{};
|
uint index{};
|
||||||
for (const auto &object : objectTable) {
|
for (const auto &object : objectTable) {
|
||||||
@ -333,7 +343,7 @@ namespace skyline::kernel::svc {
|
|||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
if ((GetCurrTimeNs() - start) >= timeout) {
|
if ((utils::GetCurrTimeNs() - start) >= timeout) {
|
||||||
state.logger->Debug("svcWaitSynchronization: Wait has timed out");
|
state.logger->Debug("svcWaitSynchronization: Wait has timed out");
|
||||||
state.ctx->registers.w0 = constant::status::Timeout;
|
state.ctx->registers.w0 = constant::status::Timeout;
|
||||||
return;
|
return;
|
||||||
@ -345,7 +355,7 @@ namespace skyline::kernel::svc {
|
|||||||
auto addr = state.ctx->registers.x1;
|
auto addr = state.ctx->registers.x1;
|
||||||
if ((addr & ((1UL << WORD_BIT) - 1U))) {
|
if ((addr & ((1UL << WORD_BIT) - 1U))) {
|
||||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
auto handle = state.ctx->registers.w2;
|
auto handle = state.ctx->registers.w2;
|
||||||
@ -357,34 +367,34 @@ namespace skyline::kernel::svc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArbitrateUnlock(DeviceState &state) {
|
void ArbitrateUnlock(DeviceState &state) {
|
||||||
auto addr = state.ctx->registers.x0;
|
auto address = state.ctx->registers.x0;
|
||||||
if ((addr & ((1UL << WORD_BIT) - 1U))) {
|
if ((address & ((1UL << WORD_BIT) - 1U))) {
|
||||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", addr);
|
state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", address);
|
||||||
state.process->MutexUnlock(addr);
|
state.process->MutexUnlock(address);
|
||||||
state.ctx->registers.w0 = constant::status::Success;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitProcessWideKeyAtomic(DeviceState &state) {
|
void WaitProcessWideKeyAtomic(DeviceState &state) {
|
||||||
auto mtxAddr = state.ctx->registers.x0;
|
auto mtxAddress = state.ctx->registers.x0;
|
||||||
auto condAddr = state.ctx->registers.x1;
|
auto condAddress = state.ctx->registers.x1;
|
||||||
try {
|
try {
|
||||||
auto &cvar = state.process->condVarMap.at(condAddr);
|
auto &cvar = state.process->condVars.at(condAddress);
|
||||||
if ((mtxAddr & ((1UL << WORD_BIT) - 1U))) {
|
if ((mtxAddress & ((1UL << WORD_BIT) - 1U))) {
|
||||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
auto handle = state.ctx->registers.w2;
|
auto handle = state.ctx->registers.w2;
|
||||||
if (handle != state.thread->handle)
|
if (handle != state.thread->handle)
|
||||||
throw exception("svcWaitProcessWideKeyAtomic: Called from another thread");
|
throw exception("svcWaitProcessWideKeyAtomic: Called from another thread");
|
||||||
state.process->MutexLock(mtxAddr);
|
state.process->MutexLock(mtxAddress);
|
||||||
auto &mutex = state.process->mutexMap.at(mtxAddr);
|
auto &mutex = state.process->mutexes.at(mtxAddress);
|
||||||
auto timeout = state.ctx->registers.x3;
|
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{};
|
timespec spec{};
|
||||||
clock_gettime(CLOCK_REALTIME, &spec);
|
clock_gettime(CLOCK_REALTIME, &spec);
|
||||||
u128 time = u128(spec.tv_sec * 1000000000U + spec.tv_nsec) + timeout; // u128 to prevent overflow
|
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;
|
state.ctx->registers.w0 = constant::status::Timeout;
|
||||||
else
|
else
|
||||||
state.ctx->registers.w0 = constant::status::Success;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
state.process->MutexUnlock(mtxAddr);
|
state.process->MutexUnlock(mtxAddress);
|
||||||
} catch (const std::out_of_range &) {
|
} catch (const std::out_of_range &) {
|
||||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: No Conditional-Variable at 0x{:X}", condAddr);
|
state.logger->Debug("svcWaitProcessWideKeyAtomic: No Conditional-Variable at 0x{:X}", condAddress);
|
||||||
state.process->condVarMap[condAddr] = PTHREAD_COND_INITIALIZER;
|
state.process->condVars[condAddress] = PTHREAD_COND_INITIALIZER;
|
||||||
state.ctx->registers.w0 = constant::status::Success;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -407,7 +417,7 @@ namespace skyline::kernel::svc {
|
|||||||
auto count = state.ctx->registers.w1;
|
auto count = state.ctx->registers.w1;
|
||||||
try {
|
try {
|
||||||
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count);
|
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count);
|
||||||
auto &cvar = state.process->condVarMap.at(address);
|
auto &cvar = state.process->condVars.at(address);
|
||||||
if (count == UINT32_MAX)
|
if (count == UINT32_MAX)
|
||||||
pthread_cond_broadcast(&cvar);
|
pthread_cond_broadcast(&cvar);
|
||||||
else
|
else
|
||||||
@ -415,7 +425,7 @@ namespace skyline::kernel::svc {
|
|||||||
pthread_cond_signal(&cvar);
|
pthread_cond_signal(&cvar);
|
||||||
} catch (const std::out_of_range &) {
|
} catch (const std::out_of_range &) {
|
||||||
state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address);
|
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;
|
state.ctx->registers.w0 = constant::status::Success;
|
||||||
}
|
}
|
||||||
@ -488,46 +498,46 @@ namespace skyline::kernel::svc {
|
|||||||
case constant::infoState::PrivilegedProcessId:
|
case constant::infoState::PrivilegedProcessId:
|
||||||
break;
|
break;
|
||||||
case constant::infoState::AliasRegionBaseAddr:
|
case constant::infoState::AliasRegionBaseAddr:
|
||||||
out = constant::MapAddr;
|
out = state.os->memory.GetRegion(memory::Regions::Alias).address;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::AliasRegionSize:
|
case constant::infoState::AliasRegionSize:
|
||||||
out = constant::MapSize;
|
out = state.os->memory.GetRegion(memory::Regions::Alias).size;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::HeapRegionBaseAddr:
|
case constant::infoState::HeapRegionBaseAddr:
|
||||||
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address;
|
out = state.os->memory.GetRegion(memory::Regions::Heap).address;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::HeapRegionSize:
|
case constant::infoState::HeapRegionSize:
|
||||||
out = state.process->memoryRegionMap.at(memory::Region::Heap)->size;
|
out = state.os->memory.GetRegion(memory::Regions::Heap).size;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::TotalMemoryAvailable:
|
case constant::infoState::TotalMemoryAvailable:
|
||||||
out = constant::TotalPhyMem;
|
out = constant::TotalPhyMem;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::TotalMemoryUsage:
|
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;
|
break;
|
||||||
case constant::infoState::AddressSpaceBaseAddr:
|
case constant::infoState::AddressSpaceBaseAddr:
|
||||||
out = constant::BaseAddr;
|
out = constant::BaseAddress;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::AddressSpaceSize:
|
case constant::infoState::AddressSpaceSize:
|
||||||
out = constant::BaseEnd;
|
out = constant::BaseEnd;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::StackRegionBaseAddr:
|
case constant::infoState::StackRegionBaseAddr:
|
||||||
out = state.thread->stackTop;
|
out = state.os->memory.GetRegion(memory::Regions::Stack).address;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::StackRegionSize:
|
case constant::infoState::StackRegionSize:
|
||||||
out = state.process->mainThreadStackSz;
|
out = state.os->memory.GetRegion(memory::Regions::Stack).size;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::PersonalMmHeapSize:
|
case constant::infoState::PersonalMmHeapSize:
|
||||||
out = constant::TotalPhyMem;
|
out = constant::TotalPhyMem;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::PersonalMmHeapUsage:
|
case constant::infoState::PersonalMmHeapUsage:
|
||||||
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz;
|
out = state.process->heap->address + constant::DefStackSize;
|
||||||
break;
|
break;
|
||||||
case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
|
case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
|
||||||
out = constant::TotalPhyMem; // TODO: NPDM specifies SystemResourceSize, subtract that from this
|
out = constant::TotalPhyMem; // TODO: NPDM specifies SystemResourceSize, subtract that from this
|
||||||
break;
|
break;
|
||||||
case constant::infoState::TotalMemoryUsedWithoutMmHeap:
|
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;
|
break;
|
||||||
case constant::infoState::UserExceptionContextAddr:
|
case constant::infoState::UserExceptionContextAddr:
|
||||||
out = state.process->tlsPages[0]->Get(0);
|
out = state.process->tlsPages[0]->Get(0);
|
||||||
|
@ -37,7 +37,7 @@ namespace skyline {
|
|||||||
};
|
};
|
||||||
namespace kernel::svc {
|
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);
|
void SetHeapSize(DeviceState &state);
|
||||||
|
|
||||||
@ -46,6 +46,11 @@ namespace skyline {
|
|||||||
*/
|
*/
|
||||||
void SetMemoryAttribute(DeviceState &state);
|
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)
|
* @brief Query information about an address (https://switchbrew.org/wiki/SVC#svcQueryMemory)
|
||||||
*/
|
*/
|
||||||
@ -169,7 +174,7 @@ namespace skyline {
|
|||||||
SetHeapSize, // 0x01
|
SetHeapSize, // 0x01
|
||||||
nullptr, // 0x02
|
nullptr, // 0x02
|
||||||
SetMemoryAttribute, // 0x03
|
SetMemoryAttribute, // 0x03
|
||||||
nullptr, // 0x04
|
MapMemory, // 0x04
|
||||||
nullptr, // 0x05
|
nullptr, // 0x05
|
||||||
QueryMemory, // 0x06
|
QueryMemory, // 0x06
|
||||||
ExitProcess, // 0x07
|
ExitProcess, // 0x07
|
||||||
|
39
app/src/main/cpp/skyline/kernel/types/KMemory.h
Normal file
39
app/src/main/cpp/skyline/kernel/types/KMemory.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kernel/memory.h>
|
||||||
|
#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;
|
||||||
|
};
|
||||||
|
}
|
@ -1,68 +1,66 @@
|
|||||||
#include "KPrivateMemory.h"
|
#include "KPrivateMemory.h"
|
||||||
#include "KProcess.h"
|
#include "KProcess.h"
|
||||||
#include <nce.h>
|
#include <os.h>
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
namespace skyline::kernel::type {
|
namespace skyline::kernel::type {
|
||||||
KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 dstAddress, size_t size, memory::Permission permission, const memory::Type type, std::shared_ptr<KThread> thread) : state(state), address(dstAddress), size(size), permission(permission), type(type), KObject(state, KType::KPrivateMemory) {
|
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 = dstAddress;
|
|
||||||
fregs.x1 = size;
|
|
||||||
fregs.x2 = static_cast<u64>(permission.Get());
|
|
||||||
fregs.x3 = static_cast<u64>(MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0));
|
|
||||||
fregs.x4 = static_cast<u64>(-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) {
|
|
||||||
Registers fregs{};
|
Registers fregs{};
|
||||||
fregs.x0 = address;
|
fregs.x0 = address;
|
||||||
fregs.x1 = size;
|
fregs.x1 = size;
|
||||||
fregs.x2 = newSize;
|
fregs.x2 = static_cast<u64>(permission.Get());
|
||||||
fregs.x3 = canMove ? MREMAP_MAYMOVE : 0;
|
fregs.x3 = static_cast<u64>(MAP_PRIVATE | MAP_ANONYMOUS | ((address) ? MAP_FIXED : 0));
|
||||||
fregs.x8 = __NR_mremap;
|
fregs.x4 = static_cast<u64>(-1);
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread);
|
fregs.x8 = __NR_mmap;
|
||||||
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
if (fregs.x0 < 0)
|
if (fregs.x0 < 0)
|
||||||
throw exception("An error occurred while remapping private region in child process");
|
throw exception("An error occurred while mapping private section in child process");
|
||||||
address = fregs.x0;
|
if (!this->address)
|
||||||
size = newSize;
|
this->address = fregs.x0;
|
||||||
return address;
|
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{};
|
Registers fregs{};
|
||||||
fregs.x0 = address;
|
fregs.x0 = address;
|
||||||
fregs.x1 = size;
|
fregs.x1 = size;
|
||||||
fregs.x2 = static_cast<u64>(permission.Get());
|
fregs.x2 = static_cast<u64>(permission.Get());
|
||||||
fregs.x8 = __NR_mprotect;
|
fregs.x8 = __NR_mprotect;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
if (fregs.x0 < 0)
|
if (fregs.x0 < 0)
|
||||||
throw exception("An error occurred while updating private region's permissions in child process");
|
throw exception("An error occurred while updating private section's permissions in child process");
|
||||||
this->permission = permission;
|
auto chunk = state.os->memory.GetChunk(address);
|
||||||
}
|
BlockDescriptor block{
|
||||||
|
.address = address,
|
||||||
memory::MemoryInfo KPrivateMemory::GetInfo(u64 address) {
|
.size = size,
|
||||||
memory::MemoryInfo info{};
|
.permission = permission,
|
||||||
info.baseAddress = address;
|
};
|
||||||
info.size = size;
|
MemoryManager::InsertBlock(chunk, block);
|
||||||
info.type = static_cast<u32>(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KPrivateMemory::~KPrivateMemory() {
|
KPrivateMemory::~KPrivateMemory() {
|
||||||
@ -72,9 +70,10 @@ namespace skyline::kernel::type {
|
|||||||
fregs.x0 = address;
|
fregs.x0 = address;
|
||||||
fregs.x1 = size;
|
fregs.x1 = size;
|
||||||
fregs.x8 = __NR_munmap;
|
fregs.x8 = __NR_munmap;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
}
|
}
|
||||||
} catch (const std::exception &) {
|
} catch (const std::exception &) {
|
||||||
}
|
}
|
||||||
|
state.os->memory.DeleteChunk(address);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,55 +1,59 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory.h>
|
#include "KMemory.h"
|
||||||
#include "KObject.h"
|
|
||||||
|
|
||||||
namespace skyline::kernel::type {
|
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:
|
private:
|
||||||
const DeviceState &state; //!< The state of the device
|
const DeviceState &state; //!< The state of the device
|
||||||
|
|
||||||
public:
|
public:
|
||||||
u64 address; //!< The address of the allocated memory
|
u64 address{}; //!< The address of the allocated memory
|
||||||
size_t size; //!< The size 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<memory::RegionInfo> regionInfoVec; //!< This holds information about specific memory regions
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param state The state of the device
|
* @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 size The size of the allocation
|
||||||
* @param permission The permissions for the allocated memory
|
* @param permission The permissions for the allocated memory
|
||||||
* @param type The type of the memory
|
* @param memState The MemoryState of the chunk of memory
|
||||||
* @param thread The thread to execute the calls on
|
|
||||||
*/
|
*/
|
||||||
KPrivateMemory(const DeviceState &state, u64 dstAddress, size_t size, memory::Permission permission, const memory::Type type, std::shared_ptr<KThread> 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
|
* @brief Remap a chunk of memory as to change the size occupied by it
|
||||||
* @param newSize The new size of the memory
|
* @param size The new size of the memory
|
||||||
* @param canMove If the memory can move if there is not enough space at the current address
|
|
||||||
* @return The address the memory was remapped to
|
* @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
|
* @brief Updates the permissions of a chunk of mapped memory
|
||||||
* @param permission The new permissions to be set for the 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
|
* @brief Checks if the specified address is within the memory object
|
||||||
* @param address The specific address being queried (Used to fill MemoryAttribute)
|
* @param address The address to check
|
||||||
* @return A Memory::MemoryInfo struct based on attributes of the memory
|
* @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
|
* @brief The destructor of private memory, it deallocates the memory
|
||||||
|
@ -26,12 +26,15 @@ namespace skyline::kernel::type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 KProcess::GetTlsSlot() {
|
u64 KProcess::GetTlsSlot() {
|
||||||
for (auto &tlsPage: tlsPages) {
|
for (auto &tlsPage: tlsPages)
|
||||||
if (!tlsPage->Full())
|
if (!tlsPage->Full())
|
||||||
return tlsPage->ReserveSlot();
|
return tlsPage->ReserveSlot();
|
||||||
}
|
u64 address;
|
||||||
auto tlsMem = NewHandle<KPrivateMemory>(0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal, threadMap.at(pid)).item;
|
if(tlsPages.empty())
|
||||||
memoryMap[tlsMem->address] = tlsMem;
|
address = state.os->memory.GetRegion(memory::Regions::TlsIo).address;
|
||||||
|
else
|
||||||
|
address = (*(tlsPages.end()-1))->address + PAGE_SIZE;
|
||||||
|
auto tlsMem = NewHandle<KPrivateMemory>(address, PAGE_SIZE, memory::Permission(true, true, false), memory::MemoryStates::ThreadLocal).item;
|
||||||
tlsPages.push_back(std::make_shared<TlsPage>(tlsMem->address));
|
tlsPages.push_back(std::make_shared<TlsPage>(tlsMem->address));
|
||||||
auto &tlsPage = tlsPages.back();
|
auto &tlsPage = tlsPages.back();
|
||||||
if (tlsPages.empty())
|
if (tlsPages.empty())
|
||||||
@ -39,13 +42,15 @@ namespace skyline::kernel::type {
|
|||||||
return tlsPage->ReserveSlot();
|
return tlsPage->ReserveSlot();
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize, std::shared_ptr<type::KSharedMemory> &tlsMemory) : pid(pid), mainThreadStackSz(stackSize), KSyncObject(state, KType::KProcess) {
|
void KProcess::InitializeMemory() {
|
||||||
|
heap = NewHandle<KPrivateMemory>(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<type::KSharedMemory> &tlsMemory) : pid(pid), KSyncObject(state, KType::KProcess) {
|
||||||
auto thread = NewHandle<KThread>(pid, entryPoint, 0x0, stackBase + stackSize, 0, constant::DefaultPriority, this, tlsMemory).item;
|
auto thread = NewHandle<KThread>(pid, entryPoint, 0x0, stackBase + stackSize, 0, constant::DefaultPriority, this, tlsMemory).item;
|
||||||
// Remove GetTlsSlot from KThread ctor and cleanup ctor in general
|
threads[pid] = thread;
|
||||||
threadMap[pid] = thread;
|
|
||||||
state.nce->WaitThreadInit(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);
|
memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC);
|
||||||
if (memFd == -1)
|
if (memFd == -1)
|
||||||
throw exception("Cannot open file descriptor to /proc/{}/mem, \"{}\"", pid, strerror(errno));
|
throw exception("Cannot open file descriptor to /proc/{}/mem, \"{}\"", pid, strerror(errno));
|
||||||
@ -78,12 +83,12 @@ namespace skyline::kernel::type {
|
|||||||
fregs.regs[0] = entryPoint;
|
fregs.regs[0] = entryPoint;
|
||||||
fregs.regs[1] = stackTop;
|
fregs.regs[1] = stackTop;
|
||||||
fregs.x8 = __NR_clone;
|
fregs.x8 = __NR_clone;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
auto pid = static_cast<pid_t>(fregs.regs[0]);
|
auto pid = static_cast<pid_t>(fregs.regs[0]);
|
||||||
if (pid == -1)
|
if (pid == -1)
|
||||||
throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop);
|
throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop);
|
||||||
auto process = NewHandle<KThread>(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this).item;
|
auto process = NewHandle<KThread>(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this).item;
|
||||||
threadMap[pid] = process;
|
threads[pid] = process;
|
||||||
return process;
|
return process;
|
||||||
*/
|
*/
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -117,43 +122,52 @@ namespace skyline::kernel::type {
|
|||||||
pwrite64(memFd, source, size, offset);
|
pwrite64(memFd, source, size, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcess::HandleOut<KPrivateMemory> KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) {
|
void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const {
|
||||||
auto mem = NewHandle<KPrivateMemory>(address, size, perms, type, threadMap.at(pid));
|
if (size <= PAGE_SIZE) {
|
||||||
memoryMap[mem.item->address] = mem.item;
|
std::vector<u8> buffer(size);
|
||||||
memoryRegionMap[region] = mem.item;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<KMemory> 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<type::KMemory>(object);
|
||||||
|
if (mem->IsInside(address))
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
bool KProcess::UnmapPrivateRegion(const skyline::memory::Region region) {
|
break;
|
||||||
if (!memoryRegionMap.count(region))
|
|
||||||
return false;
|
|
||||||
memoryMap.erase(memoryRegionMap.at(region)->address);
|
|
||||||
memoryRegionMap.erase(region);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
size_t KProcess::GetProgramSize() {
|
return nullptr;
|
||||||
size_t sharedSize = 0;
|
|
||||||
for (auto ®ion : memoryRegionMap)
|
|
||||||
sharedSize += region.second->size;
|
|
||||||
return sharedSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KProcess::MutexLock(u64 address) {
|
void KProcess::MutexLock(u64 address) {
|
||||||
try {
|
try {
|
||||||
auto mtx = mutexMap.at(address);
|
auto mtx = mutexes.at(address);
|
||||||
pthread_mutex_lock(&mtx);
|
pthread_mutex_lock(&mtx);
|
||||||
u32 mtxVal = ReadMemory<u32>(address);
|
u32 mtxVal = ReadMemory<u32>(address);
|
||||||
mtxVal = (mtxVal & ~constant::MtxOwnerMask) | state.thread->handle;
|
mtxVal = (mtxVal & ~constant::MtxOwnerMask) | state.thread->handle;
|
||||||
WriteMemory(mtxVal, address);
|
WriteMemory(mtxVal, address);
|
||||||
} catch (const std::out_of_range &) {
|
} catch (const std::out_of_range &) {
|
||||||
mutexMap[address] = PTHREAD_MUTEX_INITIALIZER;
|
mutexes[address] = PTHREAD_MUTEX_INITIALIZER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KProcess::MutexUnlock(u64 address) {
|
void KProcess::MutexUnlock(u64 address) {
|
||||||
try {
|
try {
|
||||||
auto mtx = mutexMap.at(address);
|
auto mtx = mutexes.at(address);
|
||||||
u32 mtxVal = ReadMemory<u32>(address);
|
u32 mtxVal = ReadMemory<u32>(address);
|
||||||
if ((mtxVal & constant::MtxOwnerMask) != state.thread->handle)
|
if ((mtxVal & constant::MtxOwnerMask) != state.thread->handle)
|
||||||
throw exception("A non-owner thread tried to release a mutex");
|
throw exception("A non-owner thread tried to release a mutex");
|
||||||
@ -161,7 +175,7 @@ namespace skyline::kernel::type {
|
|||||||
WriteMemory(mtxVal, address);
|
WriteMemory(mtxVal, address);
|
||||||
pthread_mutex_unlock(&mtx);
|
pthread_mutex_unlock(&mtx);
|
||||||
} catch (const std::out_of_range &) {
|
} catch (const std::out_of_range &) {
|
||||||
mutexMap[address] = PTHREAD_MUTEX_INITIALIZER;
|
mutexes[address] = PTHREAD_MUTEX_INITIALIZER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "KSharedMemory.h"
|
#include "KSharedMemory.h"
|
||||||
#include "KSession.h"
|
#include "KSession.h"
|
||||||
#include "KEvent.h"
|
#include "KEvent.h"
|
||||||
|
#include <kernel/memory.h>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
namespace skyline::kernel::type {
|
namespace skyline::kernel::type {
|
||||||
@ -57,25 +58,16 @@ namespace skyline::kernel::type {
|
|||||||
*/
|
*/
|
||||||
u64 GetTlsSlot();
|
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<u64, std::shared_ptr<KPrivateMemory>> 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<memory::Region, std::shared_ptr<KPrivateMemory>> memoryRegionMap; //!< A mapping from every MemoryRegion to a shared pointer of it's corresponding KPrivateMemory
|
|
||||||
std::unordered_map<handle_t, std::shared_ptr<KObject>> handleTable; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
|
|
||||||
std::unordered_map<pid_t, std::shared_ptr<KThread>> threadMap; //!< A mapping from a PID to it's corresponding KThread object
|
|
||||||
std::unordered_map<u64, pthread_mutex_t> mutexMap; //!< A map from a mutex's address to a vector of threads waiting on it
|
|
||||||
std::unordered_map<u64, pthread_cond_t> condVarMap; //!< A map from a conditional variable's address to a vector of threads waiting on it
|
|
||||||
std::vector<std::shared_ptr<TlsPage>> 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
|
* @tparam objectClass The class of the kernel object
|
||||||
*/
|
*/
|
||||||
template<typename objectClass>
|
template<typename objectClass>
|
||||||
@ -84,6 +76,25 @@ namespace skyline::kernel::type {
|
|||||||
handle_t handle; //!< The handle of the object in the process
|
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<handle_t, std::shared_ptr<KObject>> handles; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
|
||||||
|
std::unordered_map<pid_t, std::shared_ptr<KThread>> threads; //!< A mapping from a PID to it's corresponding KThread object
|
||||||
|
std::unordered_map<u64, pthread_mutex_t> mutexes; //!< A map from a mutex's address to a vector of threads waiting on it
|
||||||
|
std::unordered_map<u64, pthread_cond_t> condVars; //!< A map from a conditional variable's address to a vector of threads waiting on it
|
||||||
|
std::vector<std::shared_ptr<TlsPage>> tlsPages; //!< A vector of all allocated TLS pages
|
||||||
|
std::shared_ptr<KPrivateMemory> 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
|
* @brief Creates a KThread object for the main thread and opens the process's memory file
|
||||||
* @param state The state of the device
|
* @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;
|
void WriteMemory(void *source, u64 offset, size_t size) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Map a chunk of process local memory (private memory)
|
* @brief Copy one chunk to another in the process's memory
|
||||||
* @param address The address to map to (Can be 0 if address doesn't matter)
|
* @param source The address of where the data to read is present
|
||||||
* @param size The size of the chunk of memory
|
* @param destination The address to write the read data to
|
||||||
* @param perms The permissions of the memory
|
* @param size The amount of memory to be copied
|
||||||
* @param type The type of the memory
|
|
||||||
* @param region The specific region this memory is mapped for
|
|
||||||
* @return The HandleOut of the created KPrivateMemory
|
|
||||||
*/
|
*/
|
||||||
HandleOut<KPrivateMemory> MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region);
|
void CopyMemory(u64 source, u64 destination, size_t size) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @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();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a new handle to a KObject and adds it to the process handle_table
|
* @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<objectClass>(state, handleIndex, args...);
|
item = std::make_shared<objectClass>(state, handleIndex, args...);
|
||||||
else
|
else
|
||||||
item = std::make_shared<objectClass>(state, args...);
|
item = std::make_shared<objectClass>(state, args...);
|
||||||
handleTable[handleIndex] = std::static_pointer_cast<KObject>(item);
|
handles[handleIndex] = std::static_pointer_cast<KObject>(item);
|
||||||
return {item, handleIndex++};
|
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
|
* @param item The item to insert
|
||||||
* @return The handle of the corresponding item in the handle table
|
* @return The handle of the corresponding item in the handle table
|
||||||
*/
|
*/
|
||||||
template<typename objectClass>
|
template<typename objectClass>
|
||||||
handle_t InsertItem(std::shared_ptr<objectClass> &item) {
|
handle_t InsertItem(std::shared_ptr<objectClass> &item) {
|
||||||
handleTable[handleIndex] = std::static_pointer_cast<KObject>(item);
|
handles[handleIndex] = std::static_pointer_cast<KObject>(item);
|
||||||
return handleIndex++;
|
return handleIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +223,7 @@ namespace skyline::kernel::type {
|
|||||||
else
|
else
|
||||||
throw exception("KProcess::GetHandle couldn't determine object type");
|
throw exception("KProcess::GetHandle couldn't determine object type");
|
||||||
try {
|
try {
|
||||||
auto item = handleTable.at(handle);
|
auto item = handles.at(handle);
|
||||||
if (item->objectType == objectType)
|
if (item->objectType == objectType)
|
||||||
return std::static_pointer_cast<objectClass>(item);
|
return std::static_pointer_cast<objectClass>(item);
|
||||||
else
|
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<KMemory> 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
|
* @brief This locks the Mutex at the specified address
|
||||||
* @param address The address of the mutex
|
* @param address The address of the mutex
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
#include "KSharedMemory.h"
|
#include "KSharedMemory.h"
|
||||||
#include "KProcess.h"
|
#include "KProcess.h"
|
||||||
#include <nce.h>
|
#include <os.h>
|
||||||
#include <android/sharedmem.h>
|
#include <android/sharedmem.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
namespace skyline::kernel::type {
|
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);
|
fd = ASharedMemory_create("", size);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
throw exception("An error occurred while creating shared memory: {}", fd);
|
throw exception("An error occurred while creating shared memory: {}", fd);
|
||||||
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, permission.Get(), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast<int>(fd), 0));
|
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | ((address) ? MAP_FIXED : 0), fd, 0));
|
||||||
if (address == reinterpret_cast<u64>(MAP_FAILED))
|
if (address == reinterpret_cast<u64>(MAP_FAILED))
|
||||||
throw exception("An occurred while mapping shared region: {}", strerror(errno));
|
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
|
||||||
kernel = {address, size, permission};
|
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{};
|
Registers fregs{};
|
||||||
fregs.x0 = address;
|
fregs.x0 = address;
|
||||||
fregs.x1 = size;
|
fregs.x1 = size;
|
||||||
@ -24,10 +24,22 @@ namespace skyline::kernel::type {
|
|||||||
fregs.x3 = static_cast<u64>(MAP_SHARED | ((address) ? MAP_FIXED : 0));
|
fregs.x3 = static_cast<u64>(MAP_SHARED | ((address) ? MAP_FIXED : 0));
|
||||||
fregs.x4 = static_cast<u64>(fd);
|
fregs.x4 = static_cast<u64>(fd);
|
||||||
fregs.x8 = __NR_mmap;
|
fregs.x8 = __NR_mmap;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
if (fregs.x0 < 0)
|
if (fregs.x0 < 0)
|
||||||
throw exception("An error occurred while mapping shared region in child process");
|
throw exception("An error occurred while mapping shared memory in guest");
|
||||||
guest = {fregs.x0, size, permission};
|
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;
|
return fregs.x0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,52 +50,45 @@ namespace skyline::kernel::type {
|
|||||||
fregs.x1 = guest.size;
|
fregs.x1 = guest.size;
|
||||||
fregs.x2 = size;
|
fregs.x2 = size;
|
||||||
fregs.x8 = __NR_mremap;
|
fregs.x8 = __NR_mremap;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
if (fregs.x0 < 0)
|
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;
|
guest.size = size;
|
||||||
|
auto chunk = state.os->memory.GetChunk(guest.address);
|
||||||
|
MemoryManager::ResizeChunk(chunk, size);
|
||||||
}
|
}
|
||||||
if (kernel.valid()) {
|
if (kernel.valid()) {
|
||||||
if (mremap(reinterpret_cast<void *>(kernel.address), kernel.size, size, 0) == MAP_FAILED)
|
if (mremap(reinterpret_cast<void *>(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;
|
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) {
|
if (guest.valid() && !host) {
|
||||||
Registers fregs{};
|
Registers fregs{};
|
||||||
fregs.x0 = guest.address;
|
fregs.x0 = address;
|
||||||
fregs.x1 = guest.size;
|
fregs.x1 = size;
|
||||||
fregs.x2 = static_cast<u64>(guest.permission.Get());
|
fregs.x2 = static_cast<u64>(permission.Get());
|
||||||
fregs.x8 = __NR_mprotect;
|
fregs.x8 = __NR_mprotect;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
if (fregs.x0 < 0)
|
if (fregs.x0 < 0)
|
||||||
throw exception("An error occurred while updating shared region's permissions in child process");
|
throw exception("An error occurred while updating shared memory's permissions in guest");
|
||||||
guest.permission = permission;
|
auto chunk = state.os->memory.GetChunk(address);
|
||||||
|
BlockDescriptor block{
|
||||||
|
.address = address,
|
||||||
|
.size = size,
|
||||||
|
.permission = permission,
|
||||||
|
};
|
||||||
|
MemoryManager::InsertBlock(chunk, block);
|
||||||
}
|
}
|
||||||
if (kernel.valid() && host) {
|
if (kernel.valid() && host) {
|
||||||
if (mprotect(reinterpret_cast<void *>(kernel.address), kernel.size, permission.Get()) == reinterpret_cast<u64>(MAP_FAILED))
|
if (mprotect(reinterpret_cast<void *>(kernel.address), kernel.size, permission.Get()) == reinterpret_cast<u64>(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;
|
kernel.permission = permission;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memory::MemoryInfo KSharedMemory::GetInfo() {
|
|
||||||
memory::MemoryInfo info{};
|
|
||||||
info.baseAddress = guest.address;
|
|
||||||
info.size = guest.size;
|
|
||||||
info.type = static_cast<u32>(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() {
|
KSharedMemory::~KSharedMemory() {
|
||||||
try {
|
try {
|
||||||
if (guest.valid() && state.process) {
|
if (guest.valid() && state.process) {
|
||||||
@ -91,12 +96,13 @@ namespace skyline::kernel::type {
|
|||||||
fregs.x0 = guest.address;
|
fregs.x0 = guest.address;
|
||||||
fregs.x1 = guest.size;
|
fregs.x1 = guest.size;
|
||||||
fregs.x8 = __NR_munmap;
|
fregs.x8 = __NR_munmap;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
}
|
}
|
||||||
if (kernel.valid())
|
if (kernel.valid())
|
||||||
munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
|
munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
|
||||||
} catch (const std::exception &) {
|
} catch (const std::exception &) {
|
||||||
}
|
}
|
||||||
|
state.os->memory.DeleteChunk(guest.address);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory.h>
|
#include "KMemory.h"
|
||||||
#include "KObject.h"
|
|
||||||
|
|
||||||
namespace skyline::kernel::type {
|
namespace skyline::kernel::type {
|
||||||
/**
|
/**
|
||||||
* @brief KSharedMemory is used to hold a particular amount of shared memory
|
* @brief KSharedMemory is used to hold a particular amount of shared memory
|
||||||
*/
|
*/
|
||||||
class KSharedMemory : public KObject {
|
class KSharedMemory : public KMemory {
|
||||||
private:
|
private:
|
||||||
int fd; //!< A file descriptor to the underlying shared memory
|
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:
|
public:
|
||||||
/**
|
/**
|
||||||
@ -27,21 +27,17 @@ namespace skyline::kernel::type {
|
|||||||
inline bool valid() { return address && size && permission.Get(); }
|
inline bool valid() { return address && size && permission.Get(); }
|
||||||
} kernel, guest;
|
} 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 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 size The size of the allocation on the kernel
|
||||||
* @param permission The permission of the kernel process
|
* @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 address The address to map to (If NULL an arbitrary address is picked)
|
||||||
* @param size The amount of shared memory to map
|
* @param size The amount of shared memory to map
|
||||||
* @param permission The permission of the kernel process
|
* @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
|
* @brief Resize a chunk of memory as to change the size occupied by it
|
||||||
* @param size The new size of the memory
|
* @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
|
* @brief Updates the permissions of a chunk of mapped memory
|
||||||
* @param permission The new permissions to be set for the 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
|
* @brief Checks if the specified address is within the guest memory object
|
||||||
* @return A Memory::MemoryInfo struct based on attributes of the memory
|
* @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
|
* @brief The destructor of shared memory, it deallocates the memory from all processes
|
||||||
|
@ -18,7 +18,7 @@ namespace skyline::kernel::type {
|
|||||||
if (pid == parent->pid)
|
if (pid == parent->pid)
|
||||||
parent->status = KProcess::Status::Started;
|
parent->status = KProcess::Status::Started;
|
||||||
status = Status::Running;
|
status = Status::Running;
|
||||||
state.nce->StartThread(entryArg, handle, parent->threadMap.at(pid));
|
state.nce->StartThread(entryArg, handle, parent->threads.at(pid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,25 @@
|
|||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
namespace skyline::kernel::type {
|
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) {
|
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) {
|
||||||
if (pid) {
|
BlockDescriptor block{
|
||||||
|
.size = size,
|
||||||
|
.permission = permission,
|
||||||
|
};
|
||||||
|
ChunkDescriptor chunk{
|
||||||
|
.size = size,
|
||||||
|
.state = memState,
|
||||||
|
.blockList = {block},
|
||||||
|
};
|
||||||
|
if (host) {
|
||||||
|
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0));
|
||||||
|
if (reinterpret_cast<void *>(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{};
|
Registers fregs{};
|
||||||
fregs.x0 = address;
|
fregs.x0 = address;
|
||||||
fregs.x1 = size;
|
fregs.x1 = size;
|
||||||
@ -13,89 +30,152 @@ namespace skyline::kernel::type {
|
|||||||
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0));
|
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0));
|
||||||
fregs.x4 = static_cast<u64>(-1);
|
fregs.x4 = static_cast<u64>(-1);
|
||||||
fregs.x8 = __NR_mmap;
|
fregs.x8 = __NR_mmap;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, pid);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
if (fregs.x0 < 0)
|
if (fregs.x0 < 0)
|
||||||
throw exception("An error occurred while mapping shared region in child process");
|
throw exception("An error occurred while mapping shared region in child process");
|
||||||
cAddress = fregs.x0;
|
this->address = fregs.x0;
|
||||||
} else {
|
chunk.address = fregs.x0;
|
||||||
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0));
|
chunk.blockList.front().address = fregs.x0;
|
||||||
if (reinterpret_cast<void *>(address) == MAP_FAILED)
|
state.os->memory.InsertChunk(chunk);
|
||||||
throw exception("An error occurred while mapping transfer memory in kernel");
|
|
||||||
cAddress = address;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 KTransferMemory::Transfer(pid_t process, u64 address, u64 size) {
|
u64 KTransferMemory::Transfer(bool mHost, u64 nAddress, u64 nSize) {
|
||||||
if (process) {
|
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{};
|
Registers fregs{};
|
||||||
fregs.x0 = address;
|
fregs.x0 = block.address;
|
||||||
fregs.x1 = size;
|
fregs.x1 = block.size;
|
||||||
fregs.x2 = static_cast<u64 >(permission.Get());
|
fregs.x2 = (block.permission.w) ? static_cast<u64>(block.permission.Get()) : (PROT_READ | PROT_WRITE);
|
||||||
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0));
|
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0));
|
||||||
fregs.x4 = static_cast<u64>(-1);
|
fregs.x4 = static_cast<u64>(-1);
|
||||||
fregs.x8 = __NR_mmap;
|
fregs.x8 = __NR_mmap;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, process);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
if (fregs.x0 < 0)
|
if (fregs.x0 < 0)
|
||||||
throw exception("An error occurred while mapping transfer memory in child process");
|
throw exception("An error occurred while mapping transfer memory in child process");
|
||||||
address = fregs.x0;
|
nAddress = fregs.x0;
|
||||||
} else {
|
} else if ((!mHost && host) || (mHost && host)) {
|
||||||
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0));
|
nAddress = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(block.address), block.size, block.permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0), -1, 0));
|
||||||
if (reinterpret_cast<void *>(address) == MAP_FAILED)
|
if (reinterpret_cast<void *>(nAddress) == MAP_FAILED)
|
||||||
throw exception("An error occurred while mapping transfer memory in kernel");
|
throw exception("An error occurred while mapping transfer memory in host");
|
||||||
}
|
}
|
||||||
size_t copySz = std::min(size, cSize);
|
if (block.permission.r) {
|
||||||
if (process && !owner) {
|
if (mHost && !host)
|
||||||
state.process->WriteMemory(reinterpret_cast<void *>(cAddress), address, copySz);
|
state.process->ReadMemory(reinterpret_cast<void *>(nAddress), address, block.size);
|
||||||
} else if (!process && owner) {
|
else if (!mHost && host)
|
||||||
state.process->ReadMemory(reinterpret_cast<void *>(address), cAddress, copySz);
|
state.process->WriteMemory(reinterpret_cast<void *>(address), nAddress, block.size);
|
||||||
} else
|
else if (!mHost && !host)
|
||||||
throw exception("Transferring from kernel to kernel is not supported");
|
state.process->CopyMemory(address, nAddress, block.size);
|
||||||
if (owner) {
|
else if (mHost && host)
|
||||||
|
memcpy(reinterpret_cast<void *>(nAddress), reinterpret_cast<void *>(address), block.size);
|
||||||
|
}
|
||||||
|
if (!block.permission.w) {
|
||||||
|
if (mHost) {
|
||||||
|
if (mprotect(reinterpret_cast<void *>(block.address), block.size, block.permission.Get()) == reinterpret_cast<u64>(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<u64>(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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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{};
|
Registers fregs{};
|
||||||
fregs.x0 = address;
|
fregs.x0 = address;
|
||||||
fregs.x1 = size;
|
fregs.x1 = size;
|
||||||
fregs.x8 = __NR_munmap;
|
fregs.x8 = __NR_munmap;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, owner);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
if (fregs.x0 < 0)
|
if (fregs.x0 < 0)
|
||||||
throw exception("An error occurred while unmapping transfer memory in child process");
|
throw exception("An error occurred while unmapping transfer memory in child process");
|
||||||
} else {
|
} else if ((!mHost && host) || (mHost && host)) {
|
||||||
if (reinterpret_cast<void *>(munmap(reinterpret_cast<void *>(address), size)) == MAP_FAILED)
|
if (reinterpret_cast<void *>(munmap(reinterpret_cast<void *>(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;
|
host = mHost;
|
||||||
cAddress = address;
|
address = nAddress;
|
||||||
cSize = size;
|
size = nSize;
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory::MemoryInfo KTransferMemory::GetInfo() {
|
void KTransferMemory::Resize(size_t nSize) {
|
||||||
memory::MemoryInfo info{};
|
if (host) {
|
||||||
info.baseAddress = cAddress;
|
if (mremap(reinterpret_cast<void *>(address), size, nSize, 0) == MAP_FAILED)
|
||||||
info.size = cSize;
|
throw exception("An error occurred while remapping transfer memory in host: {}", strerror(errno));
|
||||||
info.type = static_cast<u64>(memory::Type::TransferMemory);
|
} else {
|
||||||
info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0);
|
Registers fregs{};
|
||||||
info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0);
|
fregs.x0 = address;
|
||||||
info.r = permission.r;
|
fregs.x1 = size;
|
||||||
info.w = permission.w;
|
fregs.x2 = nSize;
|
||||||
info.x = permission.x;
|
fregs.x8 = __NR_mremap;
|
||||||
info.ipcRefCount = ipcRefCount;
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
info.deviceRefCount = deviceRefCount;
|
if (fregs.x0 < 0)
|
||||||
return info;
|
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<void *>(address), size, permission.Get()) == reinterpret_cast<u64>(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<u64>(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() {
|
KTransferMemory::~KTransferMemory() {
|
||||||
if (owner) {
|
if (host)
|
||||||
|
munmap(reinterpret_cast<void *>(address), size);
|
||||||
|
else if (state.process) {
|
||||||
try {
|
try {
|
||||||
if (state.process) {
|
|
||||||
Registers fregs{};
|
Registers fregs{};
|
||||||
fregs.x0 = cAddress;
|
fregs.x0 = address;
|
||||||
fregs.x1 = cSize;
|
fregs.x1 = size;
|
||||||
fregs.x8 = __NR_munmap;
|
fregs.x8 = __NR_munmap;
|
||||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
|
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
|
||||||
}
|
state.os->memory.DeleteChunk(address);
|
||||||
} catch (const std::exception &) {
|
} catch (const std::exception &) {
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
munmap(reinterpret_cast<void *>(cAddress), cSize);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,45 +1,70 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory.h>
|
#include "KMemory.h"
|
||||||
#include "KObject.h"
|
|
||||||
|
|
||||||
namespace skyline::kernel::type {
|
namespace skyline::kernel::type {
|
||||||
/**
|
/**
|
||||||
* @brief KTransferMemory is used to hold a particular amount of transferable memory
|
* @brief KTransferMemory is used to hold a particular amount of transferable memory
|
||||||
*/
|
*/
|
||||||
class KTransferMemory : public KObject {
|
class KTransferMemory : public KMemory {
|
||||||
|
private:
|
||||||
|
ChunkDescriptor hostChunk{};
|
||||||
public:
|
public:
|
||||||
pid_t owner; //!< The PID of the process owning this memory
|
bool host; //!< If the memory is mapped on the host or the guest
|
||||||
u64 cAddress; //!< The current address of the allocated memory for the kernel
|
u64 address; //!< The current address of the allocated memory for the kernel
|
||||||
size_t cSize; //!< The current size of the allocated memory
|
size_t size; //!< 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
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param state The state of the device
|
* @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 address The address to map to (If NULL an arbitrary address is picked)
|
||||||
* @param size The size of the allocation
|
* @param size The size of the allocation
|
||||||
* @param permission The permissions of the memory
|
* @param permission The permissions of the memory
|
||||||
* @param type The type 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
|
* @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 address The address to map to (If NULL an arbitrary address is picked)
|
||||||
* @param size The amount of shared memory to map
|
* @param size The amount of shared memory to map
|
||||||
* @return The address of the allocation
|
* @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
|
* @brief Remap a chunk of memory as to change the size occupied by it
|
||||||
* @return A memory::MemoryInfo struct based on attributes of the memory
|
* @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
|
* @brief The destructor of private memory, it deallocates the memory
|
||||||
|
@ -22,8 +22,6 @@ namespace skyline::loader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
u64 mainEntry{}; //!< The address of the actual entry point for the application
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param filePath The path to the ROM file
|
* @param filePath The path to the ROM file
|
||||||
*/
|
*/
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <kernel/memory.h>
|
||||||
#include "nro.h"
|
#include "nro.h"
|
||||||
|
|
||||||
namespace skyline::loader {
|
namespace skyline::loader {
|
||||||
@ -6,7 +7,6 @@ namespace skyline::loader {
|
|||||||
ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader));
|
ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader));
|
||||||
if (header.magic != constant::NroMagic)
|
if (header.magic != constant::NroMagic)
|
||||||
throw exception("Invalid NRO magic! 0x{0:X}", header.magic);
|
throw exception("Invalid NRO magic! 0x{0:X}", header.magic);
|
||||||
mainEntry = constant::BaseAddr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NroLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) {
|
void NroLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) {
|
||||||
@ -18,31 +18,34 @@ namespace skyline::loader {
|
|||||||
ReadOffset(rodata.data(), header.ro.offset, header.ro.size);
|
ReadOffset(rodata.data(), header.ro.offset, header.ro.size);
|
||||||
ReadOffset(data.data(), header.data.offset, header.data.size);
|
ReadOffset(data.data(), header.data.offset, header.data.size);
|
||||||
|
|
||||||
std::vector<u32> patch = state.nce->PatchCode(text, constant::BaseAddr, header.text.size + header.ro.size + header.data.size + header.bssSize);
|
std::vector<u32> patch = state.nce->PatchCode(text, constant::BaseAddress, header.text.size + header.ro.size + header.data.size + header.bssSize);
|
||||||
|
|
||||||
u64 textSize = text.size();
|
u64 textSize = text.size();
|
||||||
u64 rodataSize = rodata.size();
|
u64 rodataSize = rodata.size();
|
||||||
u64 dataSize = data.size();
|
u64 dataSize = data.size();
|
||||||
u64 patchSize = patch.size() * sizeof(u32);
|
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
|
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress, textSize, memory::Permission{true, true, true}, memory::MemoryStates::CodeStatic); // R-X
|
||||||
state.logger->Debug("Successfully mapped region .text @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr, textSize);
|
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--
|
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize, rodataSize, memory::Permission{true, false, false}, memory::MemoryStates::CodeReadOnly); // R--
|
||||||
state.logger->Debug("Successfully mapped region .ro @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize, rodataSize);
|
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-
|
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize + rodataSize, dataSize, memory::Permission{true, true, false}, memory::MemoryStates::CodeStatic); // RW-
|
||||||
state.logger->Debug("Successfully mapped region .data @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize + rodataSize, dataSize);
|
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
|
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize + rodataSize + dataSize, header.bssSize, memory::Permission{true, true, true}, memory::MemoryStates::CodeMutable); // RWX
|
||||||
state.logger->Debug("Successfully mapped region .bss @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize + rodataSize + dataSize, header.bssSize);
|
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
|
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize + rodataSize + dataSize + header.bssSize, patchSize + padding, memory::Permission{true, true, true}, memory::MemoryStates::CodeStatic); // RWX
|
||||||
state.logger->Debug("Successfully mapped region .patch @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize + rodataSize + dataSize + header.bssSize, patchSize);
|
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(text.data(), constant::BaseAddress, textSize);
|
||||||
process->WriteMemory(rodata.data(), constant::BaseAddr + textSize, rodataSize);
|
process->WriteMemory(rodata.data(), constant::BaseAddress + textSize, rodataSize);
|
||||||
process->WriteMemory(data.data(), constant::BaseAddr + textSize + rodataSize, dataSize);
|
process->WriteMemory(data.data(), constant::BaseAddress + textSize + rodataSize, dataSize);
|
||||||
process->WriteMemory(patch.data(), constant::BaseAddr + textSize + rodataSize + dataSize + header.bssSize, patchSize);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
};
|
|
||||||
}
|
|
@ -5,6 +5,7 @@
|
|||||||
#include "nce/guest.h"
|
#include "nce/guest.h"
|
||||||
#include "nce/instr.h"
|
#include "nce/instr.h"
|
||||||
#include "kernel/svc.h"
|
#include "kernel/svc.h"
|
||||||
|
#include "nce.h"
|
||||||
|
|
||||||
extern bool Halt;
|
extern bool Halt;
|
||||||
extern skyline::GroupMutex jniMtx;
|
extern skyline::GroupMutex jniMtx;
|
||||||
@ -12,8 +13,8 @@ extern skyline::GroupMutex jniMtx;
|
|||||||
namespace skyline {
|
namespace skyline {
|
||||||
void NCE::KernelThread(pid_t thread) {
|
void NCE::KernelThread(pid_t thread) {
|
||||||
try {
|
try {
|
||||||
state.thread = state.process->threadMap.at(thread);
|
state.thread = state.process->threads.at(thread);
|
||||||
state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->guest.address);
|
state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->kernel.address);
|
||||||
while (true) {
|
while (true) {
|
||||||
std::lock_guard jniGd(jniMtx);
|
std::lock_guard jniGd(jniMtx);
|
||||||
if (Halt)
|
if (Halt)
|
||||||
@ -91,11 +92,9 @@ namespace skyline {
|
|||||||
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address));
|
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, pid_t pid) {
|
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs) {
|
||||||
if (state.process->status != kernel::type::KProcess::Status::Exiting)
|
auto thread = state.thread ? state.thread : state.process->threads.at(state.process->pid);
|
||||||
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(state.process->threadMap.at(pid)->ctxMemory->kernel.address));
|
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address));
|
||||||
else
|
|
||||||
throw std::out_of_range("The KProcess object is missing");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCE::WaitThreadInit(std::shared_ptr<kernel::type::KThread> &thread) __attribute__ ((optnone)) {
|
void NCE::WaitThreadInit(std::shared_ptr<kernel::type::KThread> &thread) __attribute__ ((optnone)) {
|
||||||
@ -339,7 +338,6 @@ namespace skyline {
|
|||||||
offset -= sizeof(u32);
|
offset -= sizeof(u32);
|
||||||
patchOffset -= sizeof(u32);
|
patchOffset -= sizeof(u32);
|
||||||
}
|
}
|
||||||
patch.resize(patch.size() + PAGE_SIZE - 1 & ~(PAGE_SIZE - 1), 0x0);
|
|
||||||
return patch;
|
return patch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ namespace skyline {
|
|||||||
void Execute();
|
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 call The specific call to execute
|
||||||
* @param funcRegs A set of registers to run the function
|
* @param funcRegs A set of registers to run the function
|
||||||
* @param thread The thread to execute the function on
|
* @param thread The thread to execute the function on
|
||||||
@ -45,12 +45,11 @@ namespace skyline {
|
|||||||
void ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr<kernel::type::KThread> &thread);
|
void ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr<kernel::type::KThread> &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 call The specific call to execute
|
||||||
* @param funcRegs A set of registers to run the function
|
* @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
|
* @brief Waits till a thread is ready to execute commands
|
||||||
|
@ -172,6 +172,13 @@ namespace skyline::guest {
|
|||||||
"LDR LR, [SP], #16");
|
"LDR LR, [SP], #16");
|
||||||
saveCtxTls();
|
saveCtxTls();
|
||||||
loadCtxStack();
|
loadCtxStack();
|
||||||
|
} else if (ctx->commandId == static_cast<u32>(ThreadCall::Memcopy)) {
|
||||||
|
auto src = reinterpret_cast<u8*>(ctx->registers.x0);
|
||||||
|
auto dest = reinterpret_cast<u8*>(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();
|
saveCtxTls();
|
||||||
loadCtxStack();
|
loadCtxStack();
|
||||||
}
|
}
|
||||||
|
} else if (ctx->commandId == static_cast<u32>(ThreadCall::Memcopy)) {
|
||||||
|
auto src = reinterpret_cast<u8*>(ctx->registers.x0);
|
||||||
|
auto dest = reinterpret_cast<u8*>(ctx->registers.x1);
|
||||||
|
auto size = ctx->registers.x2;
|
||||||
|
auto end = src + size;
|
||||||
|
while (src < end)
|
||||||
|
*(src++) = *(dest++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
struct sigaction sigact{
|
struct sigaction sigact{
|
||||||
|
@ -5,9 +5,9 @@ namespace skyline {
|
|||||||
constexpr size_t saveCtxSize = 20 * sizeof(u32);
|
constexpr size_t saveCtxSize = 20 * sizeof(u32);
|
||||||
constexpr size_t loadCtxSize = 20 * sizeof(u32);
|
constexpr size_t loadCtxSize = 20 * sizeof(u32);
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
constexpr size_t svcHandlerSize = 150 * sizeof(u32);
|
constexpr size_t svcHandlerSize = 175 * sizeof(u32);
|
||||||
#else
|
#else
|
||||||
constexpr size_t svcHandlerSize = 250 * sizeof(u32);
|
constexpr size_t svcHandlerSize = 275 * sizeof(u32);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void entry(u64 address);
|
void entry(u64 address);
|
||||||
|
@ -135,6 +135,7 @@ namespace skyline {
|
|||||||
*/
|
*/
|
||||||
enum class ThreadCall : u32 {
|
enum class ThreadCall : u32 {
|
||||||
Syscall = 0x100, //!< A linux syscall needs to be called from the guest
|
Syscall = 0x100, //!< A linux syscall needs to be called from the guest
|
||||||
|
Memcopy = 0x101, //!< To copy memory from one location to another
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "nce/guest.h"
|
#include "nce/guest.h"
|
||||||
|
|
||||||
namespace skyline::kernel {
|
namespace skyline::kernel {
|
||||||
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : state(this, process, jvmManager, settings, logger), serviceManager(state) {}
|
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : state(this, process, jvmManager, settings, logger), memory(state), serviceManager(state) {}
|
||||||
|
|
||||||
void OS::Execute(const int romFd, const TitleFormat romType) {
|
void OS::Execute(const int romFd, const TitleFormat romType) {
|
||||||
std::shared_ptr<loader::Loader> loader;
|
std::shared_ptr<loader::Loader> loader;
|
||||||
@ -11,9 +11,10 @@ namespace skyline::kernel {
|
|||||||
loader = std::make_shared<loader::NroLoader>(romFd);
|
loader = std::make_shared<loader::NroLoader>(romFd);
|
||||||
} else
|
} else
|
||||||
throw exception("Unsupported ROM extension.");
|
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);
|
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();
|
state.nce->Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ namespace skyline::kernel {
|
|||||||
munmap(stack, stackSize);
|
munmap(stack, stackSize);
|
||||||
throw exception("Failed to create guard pages");
|
throw exception("Failed to create guard pages");
|
||||||
}
|
}
|
||||||
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, false), memory::Type::Reserved);
|
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, false), memory::MemoryStates::Reserved);
|
||||||
tlsMem->guest = tlsMem->kernel;
|
tlsMem->guest = tlsMem->kernel;
|
||||||
pid_t pid = clone(reinterpret_cast<int (*)(void *)>(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void *>(tlsMem->guest.address));
|
pid_t pid = clone(reinterpret_cast<int (*)(void *)>(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void *>(tlsMem->guest.address));
|
||||||
if (pid == -1)
|
if (pid == -1)
|
||||||
@ -39,11 +40,11 @@ namespace skyline::kernel {
|
|||||||
void OS::KillThread(pid_t pid) {
|
void OS::KillThread(pid_t pid) {
|
||||||
if (process->pid == pid) {
|
if (process->pid == pid) {
|
||||||
state.logger->Debug("Killing process with PID: {}", pid);
|
state.logger->Debug("Killing process with PID: {}", pid);
|
||||||
for (auto &thread: process->threadMap)
|
for (auto &thread: process->threads)
|
||||||
thread.second->Kill();
|
thread.second->Kill();
|
||||||
} else {
|
} else {
|
||||||
state.logger->Debug("Killing thread with TID: {}", pid);
|
state.logger->Debug("Killing thread with TID: {}", pid);
|
||||||
process->threadMap.at(pid)->Kill();
|
process->threads.at(pid)->Kill();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ namespace skyline::kernel {
|
|||||||
public:
|
public:
|
||||||
std::shared_ptr<type::KProcess> process; //!< The KProcess object for the emulator, representing the guest process
|
std::shared_ptr<type::KProcess> process; //!< The KProcess object for the emulator, representing the guest process
|
||||||
service::ServiceManager serviceManager; //!< This manages all of the service functions
|
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
|
* @param logger An instance of the Logger class
|
||||||
|
@ -7,7 +7,7 @@ namespace skyline::service::hid {
|
|||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
hidSharedMemory = std::make_shared<kernel::type::KSharedMemory>(state, NULL, constant::hidSharedMemSize, memory::Permission(true, false, false), memory::Type::SharedMemory);
|
hidSharedMemory = std::make_shared<kernel::type::KSharedMemory>(state, NULL, constant::hidSharedMemSize, memory::Permission{true, false, false});
|
||||||
auto handle = state.process->InsertItem<type::KSharedMemory>(hidSharedMemory);
|
auto handle = state.process->InsertItem<type::KSharedMemory>(hidSharedMemory);
|
||||||
state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle);
|
state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle);
|
||||||
response.copyHandles.push_back(handle);
|
response.copyHandles.push_back(handle);
|
||||||
|
Loading…
Reference in New Issue
Block a user