Implement SvcMap/UnmapPhysicalMemory + Fix W-Register Writing + Improve Accuracy of SvcGetInfo

This commit is contained in:
◱ PixelyIon 2020-10-20 14:52:15 +05:30 committed by ◱ PixelyIon
parent 657beea070
commit 324381908b
10 changed files with 551 additions and 439 deletions

View File

@ -17,8 +17,8 @@ namespace skyline::kernel {
case memory::AddressSpaceType::AddressSpace36Bit: { case memory::AddressSpaceType::AddressSpace36Bit: {
addressSpace.address = 0; addressSpace.address = 0;
addressSpace.size = 1UL << 36; addressSpace.size = 1UL << 36;
base.size = 0x78000000 + 0x180000000 + 0x180000000 + 0x180000000; base.size = 0x78000000 + 0x180000000 + 0x78000000 + 0x180000000;
break; throw exception("36-bit address spaces are not supported"); // Due to VMM base being forced at 0x800000 and it being used by ART
} }
case memory::AddressSpaceType::AddressSpace39Bit: { case memory::AddressSpaceType::AddressSpace39Bit: {
@ -67,18 +67,17 @@ namespace skyline::kernel {
switch (addressSpace.size) { switch (addressSpace.size) {
case 1UL << 36: { case 1UL << 36: {
code.address = base.address; code.address = 0x800000;
code.size = 0x78000000; code.size = 0x78000000;
if (code.address > address || (code.size - (address - code.address)) < size) if (code.address > address || (code.size - (address - code.address)) < size)
throw exception("Code mapping larger than 36-bit code region"); throw exception("Code mapping larger than 36-bit code region");
alias.address = code.address + code.size; alias.address = code.address + code.size;
alias.size = 0x180000000; alias.size = 0x180000000;
stack.address = alias.address; stack.address = alias.address + alias.size;
stack.size = 0x180000000; stack.size = 0x78000000;
heap.address = alias.address + alias.size; tlsIo = stack; //!< TLS/IO is shared with Stack on 36-bit
heap.address = stack.address + stack.size;
heap.size = 0x180000000; heap.size = 0x180000000;
tlsIo.address = code.address;
tlsIo.size = 0;
break; break;
} }
@ -100,7 +99,7 @@ namespace skyline::kernel {
throw exception("Regions initialized without VMM initialization"); throw exception("Regions initialized without VMM initialization");
} }
auto newSize{code.size + alias.size + stack.size + heap.size + tlsIo.size}; auto newSize{code.size + alias.size + stack.size + heap.size + ((addressSpace.size == 1UL << 39) ? tlsIo.size : 0)};
if (newSize > base.size) if (newSize > base.size)
throw exception("Region size has exceeded pre-allocated area: 0x{:X}/0x{:X}", newSize, base.size); throw exception("Region size has exceeded pre-allocated area: 0x{:X}/0x{:X}", newSize, base.size);
if (newSize != base.size) if (newSize != base.size)
@ -177,10 +176,17 @@ namespace skyline::kernel {
} }
size_t MemoryManager::GetMemoryUsage() { size_t MemoryManager::GetMemoryUsage() {
std::shared_lock lock(mutex);
size_t size{}; size_t size{};
for (const auto &chunk : chunks) for (const auto &chunk : chunks)
if (chunk.state != memory::states::Unmapped) if (chunk.state != memory::states::Unmapped)
size += chunk.size; size += chunk.size;
return size; return size;
} }
size_t MemoryManager::GetKMemoryBlockSize() {
std::shared_lock lock(mutex);
constexpr size_t KMemoryBlockSize{0x40};
return util::AlignUp(chunks.size() * KMemoryBlockSize, PAGE_SIZE);
}
} }

View File

@ -240,6 +240,11 @@ namespace skyline {
* @return The cumulative size of all memory mappings in bytes * @return The cumulative size of all memory mappings in bytes
*/ */
size_t GetMemoryUsage(); size_t GetMemoryUsage();
/**
* @return The total page-aligned size used to store memory block metadata, if they were KMemoryBlocks rather than ChunkDescriptor
*/
size_t GetKMemoryBlockSize();
}; };
} }
} }

View File

@ -8,7 +8,7 @@
namespace skyline::kernel::svc { namespace skyline::kernel::svc {
void SetHeapSize(const DeviceState &state) { void SetHeapSize(const DeviceState &state) {
auto size{state.ctx->gpr.w1}; u32 size{state.ctx->gpr.w1};
if (!util::IsAligned(size, 0x200000)) { if (!util::IsAligned(size, 0x200000)) {
state.ctx->gpr.w0 = result::InvalidSize; state.ctx->gpr.w0 = result::InvalidSize;
@ -35,7 +35,7 @@ namespace skyline::kernel::svc {
return; return;
} }
auto size{state.ctx->gpr.x1}; size_t size{state.ctx->gpr.x1};
if (!util::PageAligned(size)) { if (!util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize; state.ctx->gpr.w0 = result::InvalidSize;
state.logger->Warn("svcSetMemoryAttribute: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); state.logger->Warn("svcSetMemoryAttribute: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
@ -76,9 +76,9 @@ namespace skyline::kernel::svc {
} }
void MapMemory(const DeviceState &state) { void MapMemory(const DeviceState &state) {
auto destination{reinterpret_cast<u8*>(state.ctx->gpr.x0)}; auto destination{reinterpret_cast<u8 *>(state.ctx->gpr.x0)};
auto source{reinterpret_cast<u8*>(state.ctx->gpr.x1)}; auto source{reinterpret_cast<u8 *>(state.ctx->gpr.x1)};
auto size{state.ctx->gpr.x2}; size_t size{state.ctx->gpr.x2};
if (!util::PageAligned(destination) || !util::PageAligned(source)) { if (!util::PageAligned(destination) || !util::PageAligned(source)) {
state.ctx->gpr.w0 = result::InvalidAddress; state.ctx->gpr.w0 = result::InvalidAddress;
@ -124,9 +124,9 @@ namespace skyline::kernel::svc {
} }
void UnmapMemory(const DeviceState &state) { void UnmapMemory(const DeviceState &state) {
auto source{reinterpret_cast<u8*>(state.ctx->gpr.x0)}; auto source{reinterpret_cast<u8 *>(state.ctx->gpr.x0)};
auto destination{reinterpret_cast<u8*>(state.ctx->gpr.x1)}; auto destination{reinterpret_cast<u8 *>(state.ctx->gpr.x1)};
auto size{state.ctx->gpr.x2}; size_t size{state.ctx->gpr.x2};
if (!util::PageAligned(destination) || !util::PageAligned(source)) { if (!util::PageAligned(destination) || !util::PageAligned(source)) {
state.ctx->gpr.w0 = result::InvalidAddress; state.ctx->gpr.w0 = result::InvalidAddress;
@ -182,7 +182,7 @@ namespace skyline::kernel::svc {
void QueryMemory(const DeviceState &state) { void QueryMemory(const DeviceState &state) {
memory::MemoryInfo memInfo{}; memory::MemoryInfo memInfo{};
auto pointer{reinterpret_cast<u8*>(state.ctx->gpr.x2)}; auto pointer{reinterpret_cast<u8 *>(state.ctx->gpr.x2)};
auto chunk{state.process->memory.Get(pointer)}; auto chunk{state.process->memory.Get(pointer)};
if (chunk) { if (chunk) {
@ -196,7 +196,7 @@ namespace skyline::kernel::svc {
.ipcRefCount = 0, .ipcRefCount = 0,
}; };
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>(chunk->attributes.isUncached), chunk->permission.r ? 'R' : '-', chunk->permission.w ? 'W' : '-', chunk->permission.x ? 'X' : '-'); state.logger->Debug("svcQueryMemory: Pointer: 0x{:X}, Region Start: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", pointer, memInfo.address, memInfo.size, memInfo.type, static_cast<bool>(chunk->attributes.isUncached), chunk->permission.r ? 'R' : '-', chunk->permission.w ? 'W' : '-', chunk->permission.x ? 'X' : '-');
} else { } else {
auto addressSpaceEnd{reinterpret_cast<u64>(state.process->memory.addressSpace.address + state.process->memory.addressSpace.size)}; auto addressSpaceEnd{reinterpret_cast<u64>(state.process->memory.addressSpace.address + state.process->memory.addressSpace.size)};
@ -209,7 +209,7 @@ namespace skyline::kernel::svc {
state.logger->Debug("svcQueryMemory: Trying to query memory outside of the application's address space: 0x{:X}", pointer); state.logger->Debug("svcQueryMemory: Trying to query memory outside of the application's address space: 0x{:X}", pointer);
} }
*reinterpret_cast<memory::MemoryInfo*>(state.ctx->gpr.x0) = memInfo; *reinterpret_cast<memory::MemoryInfo *>(state.ctx->gpr.x0) = memInfo;
state.ctx->gpr.w0 = Result{}; state.ctx->gpr.w0 = Result{};
} }
@ -220,10 +220,10 @@ namespace skyline::kernel::svc {
} }
void CreateThread(const DeviceState &state) { void CreateThread(const DeviceState &state) {
auto entry{reinterpret_cast<void*>(state.ctx->gpr.x1)}; auto entry{reinterpret_cast<void *>(state.ctx->gpr.x1)};
auto entryArgument{state.ctx->gpr.x2}; auto entryArgument{state.ctx->gpr.x2};
auto stackTop{reinterpret_cast<u8*>(state.ctx->gpr.x3)}; auto stackTop{reinterpret_cast<u8 *>(state.ctx->gpr.x3)};
auto priority{static_cast<i8>(state.ctx->gpr.w4)}; auto priority{static_cast<i8>(static_cast<u32>(state.ctx->gpr.w4))};
if (!constant::HosPriority.Valid(priority)) { if (!constant::HosPriority.Valid(priority)) {
state.ctx->gpr.w0 = result::InvalidAddress; state.ctx->gpr.w0 = result::InvalidAddress;
@ -243,7 +243,7 @@ namespace skyline::kernel::svc {
} }
void StartThread(const DeviceState &state) { void StartThread(const DeviceState &state) {
auto handle{state.ctx->gpr.w0}; KHandle handle{state.ctx->gpr.w0};
try { try {
auto thread{state.process->GetHandle<type::KThread>(handle)}; auto thread{state.process->GetHandle<type::KThread>(handle)};
state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->id); state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->id);
@ -262,7 +262,7 @@ namespace skyline::kernel::svc {
} }
void SleepThread(const DeviceState &state) { void SleepThread(const DeviceState &state) {
auto in{state.ctx->gpr.x0}; u64 in{state.ctx->gpr.x0};
switch (in) { switch (in) {
case 0: case 0:
@ -281,7 +281,7 @@ namespace skyline::kernel::svc {
} }
void GetThreadPriority(const DeviceState &state) { void GetThreadPriority(const DeviceState &state) {
auto handle{state.ctx->gpr.w1}; KHandle handle{state.ctx->gpr.w1};
try { try {
auto priority{state.process->GetHandle<type::KThread>(handle)->priority}; auto priority{state.process->GetHandle<type::KThread>(handle)->priority};
state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority); state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority);
@ -295,8 +295,8 @@ namespace skyline::kernel::svc {
} }
void SetThreadPriority(const DeviceState &state) { void SetThreadPriority(const DeviceState &state) {
auto handle{state.ctx->gpr.w0}; KHandle handle{state.ctx->gpr.w0};
auto priority{state.ctx->gpr.w1}; u32 priority{state.ctx->gpr.w1};
try { try {
state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority); state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority);
@ -325,7 +325,7 @@ namespace skyline::kernel::svc {
return; return;
} }
auto size{state.ctx->gpr.x2}; size_t size{state.ctx->gpr.x2};
if (!util::PageAligned(size)) { if (!util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize; state.ctx->gpr.w0 = result::InvalidSize;
state.logger->Warn("svcMapSharedMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); state.logger->Warn("svcMapSharedMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
@ -345,7 +345,7 @@ namespace skyline::kernel::svc {
state.ctx->gpr.w0 = Result{}; state.ctx->gpr.w0 = Result{};
} catch (const std::exception &) { } catch (const std::exception &) {
state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->gpr.w0); state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", static_cast<u32>(state.ctx->gpr.w0));
state.ctx->gpr.w0 = result::InvalidHandle; state.ctx->gpr.w0 = result::InvalidHandle;
} }
} }
@ -358,7 +358,7 @@ namespace skyline::kernel::svc {
return; return;
} }
auto size{state.ctx->gpr.x2}; size_t size{state.ctx->gpr.x2};
if (!util::PageAligned(size)) { if (!util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize; state.ctx->gpr.w0 = result::InvalidSize;
state.logger->Warn("svcCreateTransferMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); state.logger->Warn("svcCreateTransferMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
@ -380,7 +380,7 @@ namespace skyline::kernel::svc {
} }
void CloseHandle(const DeviceState &state) { void CloseHandle(const DeviceState &state) {
auto handle{static_cast<KHandle>(state.ctx->gpr.w0)}; KHandle handle{static_cast<KHandle>(state.ctx->gpr.w0)};
try { try {
state.process->CloseHandle(handle); state.process->CloseHandle(handle);
state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle); state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle);
@ -392,7 +392,7 @@ namespace skyline::kernel::svc {
} }
void ResetSignal(const DeviceState &state) { void ResetSignal(const DeviceState &state) {
auto handle{state.ctx->gpr.w0}; KHandle handle{state.ctx->gpr.w0};
try { try {
auto object{state.process->GetHandle(handle)}; auto object{state.process->GetHandle(handle)};
switch (object->objectType) { switch (object->objectType) {
@ -423,7 +423,7 @@ namespace skyline::kernel::svc {
void WaitSynchronization(const DeviceState &state) { void WaitSynchronization(const DeviceState &state) {
constexpr u8 maxSyncHandles{0x40}; // The total amount of handles that can be passed to WaitSynchronization constexpr u8 maxSyncHandles{0x40}; // The total amount of handles that can be passed to WaitSynchronization
auto numHandles{state.ctx->gpr.w2}; u32 numHandles{state.ctx->gpr.w2};
if (numHandles > maxSyncHandles) { if (numHandles > maxSyncHandles) {
state.ctx->gpr.w0 = result::OutOfHandles; state.ctx->gpr.w0 = result::OutOfHandles;
return; return;
@ -431,7 +431,7 @@ namespace skyline::kernel::svc {
std::string handleStr; std::string handleStr;
std::vector<std::shared_ptr<type::KSyncObject>> objectTable; std::vector<std::shared_ptr<type::KSyncObject>> objectTable;
span waitHandles(reinterpret_cast<KHandle*>(state.ctx->gpr.x1), numHandles); span waitHandles(reinterpret_cast<KHandle *>(state.ctx->gpr.x1), numHandles);
for (const auto &handle : waitHandles) { for (const auto &handle : waitHandles) {
handleStr += fmt::format("* 0x{:X}\n", handle); handleStr += fmt::format("* 0x{:X}\n", handle);
@ -453,7 +453,7 @@ namespace skyline::kernel::svc {
objectTable.push_back(std::static_pointer_cast<type::KSyncObject>(object)); objectTable.push_back(std::static_pointer_cast<type::KSyncObject>(object));
} }
auto timeout{state.ctx->gpr.x3}; u64 timeout{state.ctx->gpr.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{util::GetTimeNs()}; auto start{util::GetTimeNs()};
@ -487,21 +487,21 @@ namespace skyline::kernel::svc {
try { try {
state.process->GetHandle<type::KThread>(state.ctx->gpr.w0)->cancelSync = true; state.process->GetHandle<type::KThread>(state.ctx->gpr.w0)->cancelSync = true;
} catch (const std::exception &) { } catch (const std::exception &) {
state.logger->Warn("svcCancelSynchronization: 'handle' invalid: 0x{:X}", state.ctx->gpr.w0); state.logger->Warn("svcCancelSynchronization: 'handle' invalid: 0x{:X}", static_cast<u32>(state.ctx->gpr.w0));
state.ctx->gpr.w0 = result::InvalidHandle; state.ctx->gpr.w0 = result::InvalidHandle;
} }
} }
void ArbitrateLock(const DeviceState &state) { void ArbitrateLock(const DeviceState &state) {
auto pointer{reinterpret_cast<u32*>(state.ctx->gpr.x1)}; auto pointer{reinterpret_cast<u32 *>(state.ctx->gpr.x1)};
if (!util::WordAligned(pointer)) { if (!util::WordAligned(pointer)) {
state.logger->Warn("svcArbitrateLock: 'pointer' not word aligned: 0x{:X}", pointer); state.logger->Warn("svcArbitrateLock: 'pointer' not word aligned: 0x{:X}", pointer);
state.ctx->gpr.w0 = result::InvalidAddress; state.ctx->gpr.w0 = result::InvalidAddress;
return; return;
} }
auto ownerHandle{state.ctx->gpr.w0}; KHandle ownerHandle{state.ctx->gpr.w0};
auto requesterHandle{state.ctx->gpr.w2}; KHandle requesterHandle{state.ctx->gpr.w2};
if (requesterHandle != state.thread->handle) if (requesterHandle != state.thread->handle)
throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", requesterHandle, state.thread->handle); throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", requesterHandle, state.thread->handle);
@ -516,7 +516,7 @@ namespace skyline::kernel::svc {
} }
void ArbitrateUnlock(const DeviceState &state) { void ArbitrateUnlock(const DeviceState &state) {
auto mutex{reinterpret_cast<u32*>(state.ctx->gpr.x0)}; auto mutex{reinterpret_cast<u32 *>(state.ctx->gpr.x0)};
if (!util::WordAligned(mutex)) { if (!util::WordAligned(mutex)) {
state.logger->Warn("svcArbitrateUnlock: 'mutex' not word aligned: 0x{:X}", mutex); state.logger->Warn("svcArbitrateUnlock: 'mutex' not word aligned: 0x{:X}", mutex);
state.ctx->gpr.w0 = result::InvalidAddress; state.ctx->gpr.w0 = result::InvalidAddress;
@ -535,15 +535,15 @@ namespace skyline::kernel::svc {
} }
void WaitProcessWideKeyAtomic(const DeviceState &state) { void WaitProcessWideKeyAtomic(const DeviceState &state) {
auto mutex{reinterpret_cast<u32*>(state.ctx->gpr.x0)}; auto mutex{reinterpret_cast<u32 *>(state.ctx->gpr.x0)};
if (!util::WordAligned(mutex)) { if (!util::WordAligned(mutex)) {
state.logger->Warn("svcWaitProcessWideKeyAtomic: 'mutex' not word aligned: 0x{:X}", mutex); state.logger->Warn("svcWaitProcessWideKeyAtomic: 'mutex' not word aligned: 0x{:X}", mutex);
state.ctx->gpr.w0 = result::InvalidAddress; state.ctx->gpr.w0 = result::InvalidAddress;
return; return;
} }
auto conditional{reinterpret_cast<void*>(state.ctx->gpr.x1)}; auto conditional{reinterpret_cast<void *>(state.ctx->gpr.x1)};
auto handle{state.ctx->gpr.w2}; KHandle handle{state.ctx->gpr.w2};
if (handle != state.thread->handle) if (handle != state.thread->handle)
throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", handle, state.thread->handle); throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", handle, state.thread->handle);
@ -553,7 +553,7 @@ namespace skyline::kernel::svc {
return; return;
} }
auto timeout{state.ctx->gpr.x3}; u64 timeout{state.ctx->gpr.x3};
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x{:X}, Timeout: {} ns", mutex, conditional, timeout); state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x{:X}, Timeout: {} ns", mutex, conditional, timeout);
if (state.process->ConditionalVariableWait(conditional, mutex, timeout)) { if (state.process->ConditionalVariableWait(conditional, mutex, timeout)) {
@ -566,8 +566,8 @@ namespace skyline::kernel::svc {
} }
void SignalProcessWideKey(const DeviceState &state) { void SignalProcessWideKey(const DeviceState &state) {
auto conditional{reinterpret_cast<void*>(state.ctx->gpr.x0)}; auto conditional{reinterpret_cast<void *>(state.ctx->gpr.x0)};
auto count{state.ctx->gpr.w1}; KHandle count{state.ctx->gpr.w1};
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", conditional, count); state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", conditional, count);
state.process->ConditionalVariableSignal(conditional, count); state.process->ConditionalVariableSignal(conditional, count);
@ -589,7 +589,7 @@ namespace skyline::kernel::svc {
void ConnectToNamedPort(const DeviceState &state) { void ConnectToNamedPort(const DeviceState &state) {
constexpr u8 portSize = 0x8; //!< The size of a port name string constexpr u8 portSize = 0x8; //!< The size of a port name string
std::string_view port(span(reinterpret_cast<char*>(state.ctx->gpr.x1), portSize).as_string(true)); std::string_view port(span(reinterpret_cast<char *>(state.ctx->gpr.x1), portSize).as_string(true));
KHandle handle{}; KHandle handle{};
if (port.compare("sm:") >= 0) { if (port.compare("sm:") >= 0) {
@ -613,7 +613,7 @@ namespace skyline::kernel::svc {
void GetThreadId(const DeviceState &state) { void GetThreadId(const DeviceState &state) {
constexpr KHandle threadSelf{0xFFFF8000}; // The handle used by threads to refer to themselves constexpr KHandle threadSelf{0xFFFF8000}; // The handle used by threads to refer to themselves
auto handle{state.ctx->gpr.w1}; KHandle handle{state.ctx->gpr.w1};
pid_t pid{}; pid_t pid{};
if (handle != threadSelf) if (handle != threadSelf)
@ -628,7 +628,7 @@ namespace skyline::kernel::svc {
} }
void OutputDebugString(const DeviceState &state) { void OutputDebugString(const DeviceState &state) {
auto debug{span(reinterpret_cast<u8*>(state.ctx->gpr.x0), state.ctx->gpr.x1).as_string()}; auto debug{span(reinterpret_cast<u8 *>(state.ctx->gpr.x0), state.ctx->gpr.x1).as_string()};
if (debug.back() == '\n') if (debug.back() == '\n')
debug.remove_suffix(1); debug.remove_suffix(1);
@ -638,91 +638,196 @@ namespace skyline::kernel::svc {
} }
void GetInfo(const DeviceState &state) { void GetInfo(const DeviceState &state) {
auto id0{state.ctx->gpr.w1}; enum class InfoState : u32 {
auto handle{state.ctx->gpr.w2}; // 1.0.0+
auto id1{state.ctx->gpr.x3}; AllowedCpuIdBitmask = 0x0,
AllowedThreadPriorityMask = 0x1,
AliasRegionBaseAddr = 0x2,
AliasRegionSize = 0x3,
HeapRegionBaseAddr = 0x4,
HeapRegionSize = 0x5,
TotalMemoryAvailable = 0x6,
TotalMemoryUsage = 0x7,
IsCurrentProcessBeingDebugged = 0x8,
ResourceLimit = 0x9,
IdleTickCount = 0xA,
RandomEntropy = 0xB,
// 2.0.0+
AddressSpaceBaseAddr = 0xC,
AddressSpaceSize = 0xD,
StackRegionBaseAddr = 0xE,
StackRegionSize = 0xF,
// 3.0.0+
TotalSystemResourceAvailable = 0x10,
TotalSystemResourceUsage = 0x11,
TitleId = 0x12,
// 4.0.0+
PrivilegedProcessId = 0x13,
// 5.0.0+
UserExceptionContextAddr = 0x14,
// 6.0.0+
TotalMemoryAvailableWithoutSystemResource = 0x15,
TotalMemoryUsageWithoutSystemResource = 0x16,
};
u64 out{}; InfoState info{static_cast<u32>(state.ctx->gpr.w1)};
KHandle handle{state.ctx->gpr.w2};
u64 id1{state.ctx->gpr.x3};
constexpr u64 totalPhysicalMemory{0xF8000000}; // ~4 GB of RAM constexpr u64 totalPhysicalMemory{0xF8000000}; // ~4 GB of RAM
switch (id0) { u64 out{};
case constant::infoState::AllowedCpuIdBitmask: switch (info) {
case constant::infoState::AllowedThreadPriorityMask: case InfoState::AllowedCpuIdBitmask:
case constant::infoState::IsCurrentProcessBeingDebugged: case InfoState::AllowedThreadPriorityMask:
case constant::infoState::TitleId: case InfoState::IsCurrentProcessBeingDebugged:
case constant::infoState::PrivilegedProcessId: case InfoState::TitleId:
case InfoState::PrivilegedProcessId:
break; break;
case constant::infoState::AliasRegionBaseAddr: case InfoState::AliasRegionBaseAddr:
out = state.process->memory.alias.address; out = state.process->memory.alias.address;
break; break;
case constant::infoState::AliasRegionSize: case InfoState::AliasRegionSize:
out = state.process->memory.alias.size; out = state.process->memory.alias.size;
break; break;
case constant::infoState::HeapRegionBaseAddr: case InfoState::HeapRegionBaseAddr:
out = state.process->memory.heap.address; out = state.process->memory.heap.address;
break; break;
case constant::infoState::HeapRegionSize: case InfoState::HeapRegionSize:
out = state.process->memory.heap.size; out = state.process->memory.heap.size;
break; break;
case constant::infoState::TotalMemoryAvailable: case InfoState::TotalMemoryAvailable:
out = totalPhysicalMemory; out = totalPhysicalMemory;
break; break;
case constant::infoState::TotalMemoryUsage: case InfoState::TotalMemoryUsage:
out = state.process->memory.GetMemoryUsage(); out = state.process->memory.GetMemoryUsage();
break; break;
case constant::infoState::AddressSpaceBaseAddr: case InfoState::AddressSpaceBaseAddr:
out = state.process->memory.base.address; out = state.process->memory.base.address;
break; break;
case constant::infoState::AddressSpaceSize: case InfoState::AddressSpaceSize:
out = state.process->memory.base.size; out = state.process->memory.base.size;
break; break;
case constant::infoState::StackRegionBaseAddr: case InfoState::StackRegionBaseAddr:
out = state.process->memory.stack.address; out = state.process->memory.stack.address;
break; break;
case constant::infoState::StackRegionSize: case InfoState::StackRegionSize:
out = state.process->memory.stack.size; out = state.process->memory.stack.size;
break; break;
case constant::infoState::PersonalMmHeapSize: case InfoState::TotalSystemResourceAvailable:
out = totalPhysicalMemory; out = totalPhysicalMemory; // TODO: NPDM specifies this in it's PersonalMmHeapSize field
break; break;
case constant::infoState::PersonalMmHeapUsage: case InfoState::TotalSystemResourceUsage:
out = state.process->heap->size + state.process->mainThreadStack->size; // A very rough approximation of what this should be on the Switch, the amount of memory allocated for storing the memory blocks (https://switchbrew.org/wiki/Kernel_objects#KMemoryBlockManager)
out = state.process->memory.GetKMemoryBlockSize();
break; break;
case constant::infoState::TotalMemoryAvailableWithoutMmHeap: case InfoState::TotalMemoryAvailableWithoutSystemResource:
out = totalPhysicalMemory; // TODO: NPDM specifies SystemResourceSize, subtract that from this out = totalPhysicalMemory; // TODO: Subtract TotalSystemResourceAvailable from this
break; break;
case constant::infoState::TotalMemoryUsedWithoutMmHeap: case InfoState::TotalMemoryUsageWithoutSystemResource:
out = state.process->heap->size + state.process->mainThreadStack->size; // TODO: Same as above out = state.process->memory.GetMemoryUsage();
break; break;
case constant::infoState::UserExceptionContextAddr: case InfoState::UserExceptionContextAddr:
out = reinterpret_cast<u64>(state.process->tlsPages[0]->Get(0)); out = reinterpret_cast<u64>(state.process->tlsPages[0]->Get(0));
break; break;
default: default:
state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1); state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", static_cast<u32>(info), id1);
state.ctx->gpr.w0 = result::InvalidEnumValue; state.ctx->gpr.w0 = result::InvalidEnumValue;
return; return;
} }
state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out); state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", static_cast<u32>(info), id1, out);
state.ctx->gpr.x1 = out; state.ctx->gpr.x1 = out;
state.ctx->gpr.w0 = Result{}; state.ctx->gpr.w0 = Result{};
} }
void MapPhysicalMemory(const DeviceState &state) {
auto pointer{reinterpret_cast<u8 *>(state.ctx->gpr.x0)};
size_t size{state.ctx->gpr.x1};
if (!util::PageAligned(pointer)) {
state.ctx->gpr.w0 = result::InvalidAddress;
return;
}
if (!size || !util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize;
return;
}
if (!state.process->memory.alias.IsInside(pointer) || !state.process->memory.alias.IsInside(pointer + size)) {
state.ctx->gpr.w0 = result::InvalidMemoryRegion;
return;
}
state.process->NewHandle<type::KPrivateMemory>(pointer, size, memory::Permission{true, true, false}, memory::states::Heap);
state.ctx->gpr.w0 = Result{};
}
void UnmapPhysicalMemory(const DeviceState &state) {
auto pointer{reinterpret_cast<u8 *>(state.ctx->gpr.x0)};
size_t size{state.ctx->gpr.x1};
if (!util::PageAligned(pointer)) {
state.ctx->gpr.w0 = result::InvalidAddress;
return;
}
if (!size || !util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize;
return;
}
if (!state.process->memory.alias.IsInside(pointer) || !state.process->memory.alias.IsInside(pointer + size)) {
state.ctx->gpr.w0 = result::InvalidMemoryRegion;
return;
}
auto end{pointer + size};
while (pointer < end) {
auto memory{state.process->GetMemoryObject(pointer)};
if (memory) {
auto item{static_pointer_cast<type::KPrivateMemory>(memory->item)};
auto initialSize{item->size};
if (item->memState == memory::states::Heap) {
if (item->ptr >= pointer) {
if (item->size <= size) {
item->Resize(0);
state.process->CloseHandle(memory->handle);
} else {
item->Remap(pointer + size, item->size - (size + (item->ptr - pointer)));
}
} else if (item->ptr < pointer) {
item->Resize(pointer - item->ptr);
}
}
pointer += initialSize;
size -= initialSize;
} else {
auto block{*state.process->memory.Get(pointer)};
pointer += block.size;
size -= block.size;
}
}
state.ctx->gpr.w0 = Result{};
}
} }

View File

@ -5,346 +5,324 @@
#include <common.h> #include <common.h>
namespace skyline { namespace skyline::kernel::svc {
namespace constant::infoState { /**
// 1.0.0+ * @brief Sets the process heap to a given size, it can both extend and shrink the heap
constexpr u8 AllowedCpuIdBitmask{0x0}; * @url https://switchbrew.org/wiki/SVC#SetHeapSize
constexpr u8 AllowedThreadPriorityMask{0x1}; */
constexpr u8 AliasRegionBaseAddr{0x2}; void SetHeapSize(const DeviceState &state);
constexpr u8 AliasRegionSize{0x3};
constexpr u8 HeapRegionBaseAddr{0x4};
constexpr u8 HeapRegionSize{0x5};
constexpr u8 TotalMemoryAvailable{0x6};
constexpr u8 TotalMemoryUsage{0x7};
constexpr u8 IsCurrentProcessBeingDebugged{0x8};
constexpr u8 ResourceLimit{0x9};
constexpr u8 IdleTickCount{0xA};
constexpr u8 RandomEntropy{0xB};
// 2.0.0+
constexpr u8 AddressSpaceBaseAddr{0xC};
constexpr u8 AddressSpaceSize{0xD};
constexpr u8 StackRegionBaseAddr{0xE};
constexpr u8 StackRegionSize{0xF};
// 3.0.0+
constexpr u8 PersonalMmHeapSize{0x10};
constexpr u8 PersonalMmHeapUsage{0x11};
constexpr u8 TitleId{0x12};
// 4.0.0+
constexpr u8 PrivilegedProcessId{0x13};
// 5.0.0+
constexpr u8 UserExceptionContextAddr{0x14};
// 6.0.0+
constexpr u8 TotalMemoryAvailableWithoutMmHeap{0x15};
constexpr u8 TotalMemoryUsedWithoutMmHeap{0x16};
}
namespace kernel::svc { /**
/** * @brief Change attribute of page-aligned memory region, this is used to turn on/off caching for a given memory area
* @brief Sets the process heap to a given size, it can both extend and shrink the heap * @url https://switchbrew.org/wiki/SVC#SetMemoryAttribute
* @url https://switchbrew.org/wiki/SVC#SetHeapSize */
*/ void SetMemoryAttribute(const DeviceState &state);
void SetHeapSize(const DeviceState &state);
/** /**
* @brief Change attribute of page-aligned memory region, this is used to turn on/off caching for a given memory area * @brief Maps a memory range into a different range, mainly used for adding guard pages around stack
* @url https://switchbrew.org/wiki/SVC#SetMemoryAttribute * @url https://switchbrew.org/wiki/SVC#SetMemoryAttribute
*/ */
void SetMemoryAttribute(const DeviceState &state); void MapMemory(const DeviceState &state);
/** /**
* @brief Maps a memory range into a different range, mainly used for adding guard pages around stack * @brief Unmaps a region that was previously mapped with #MapMemory
* @url https://switchbrew.org/wiki/SVC#SetMemoryAttribute * @url https://switchbrew.org/wiki/SVC#UnmapMemory
*/ */
void MapMemory(const DeviceState &state); void UnmapMemory(const DeviceState &state);
/** /**
* @brief Unmaps a region that was previously mapped with #MapMemory * @brief Query information about an address
* @url https://switchbrew.org/wiki/SVC#UnmapMemory * @url https://switchbrew.org/wiki/SVC#QueryMemory
*/ */
void UnmapMemory(const DeviceState &state); void QueryMemory(const DeviceState &state);
/** /**
* @brief Query information about an address * @brief Exits the current process
* @url https://switchbrew.org/wiki/SVC#QueryMemory * @url https://switchbrew.org/wiki/SVC#ExitProcess
*/ */
void QueryMemory(const DeviceState &state); void ExitProcess(const DeviceState &state);
/** /**
* @brief Exits the current process * @brief Create a thread in the current process
* @url https://switchbrew.org/wiki/SVC#ExitProcess * @url https://switchbrew.org/wiki/SVC#CreateThread
*/ */
void ExitProcess(const DeviceState &state); void CreateThread(const DeviceState &state);
/** /**
* @brief Create a thread in the current process * @brief Starts the thread for the provided handle
* @url https://switchbrew.org/wiki/SVC#CreateThread * @url https://switchbrew.org/wiki/SVC#StartThread
*/ */
void CreateThread(const DeviceState &state); void StartThread(const DeviceState &state);
/** /**
* @brief Starts the thread for the provided handle * @brief Exits the current thread
* @url https://switchbrew.org/wiki/SVC#StartThread * @url https://switchbrew.org/wiki/SVC#ExitThread
*/ */
void StartThread(const DeviceState &state); void ExitThread(const DeviceState &state);
/** /**
* @brief Exits the current thread * @brief Sleep for a specified amount of time, or yield thread
* @url https://switchbrew.org/wiki/SVC#ExitThread * @url https://switchbrew.org/wiki/SVC#SleepThread
*/ */
void ExitThread(const DeviceState &state); void SleepThread(const DeviceState &state);
/** /**
* @brief Sleep for a specified amount of time, or yield thread * @brief Get priority of provided thread handle
* @url https://switchbrew.org/wiki/SVC#SleepThread * @url https://switchbrew.org/wiki/SVC#GetThreadPriority
*/ */
void SleepThread(const DeviceState &state); void GetThreadPriority(const DeviceState &state);
/** /**
* @brief Get priority of provided thread handle * @brief Set priority of provided thread handle
* @url https://switchbrew.org/wiki/SVC#GetThreadPriority * @url https://switchbrew.org/wiki/SVC#SetThreadPriority
*/ */
void GetThreadPriority(const DeviceState &state); void SetThreadPriority(const DeviceState &state);
/** /**
* @brief Set priority of provided thread handle * @brief Clears a KEvent of it's signal
* @url https://switchbrew.org/wiki/SVC#SetThreadPriority * @url https://switchbrew.org/wiki/SVC#ClearEvent
*/ */
void SetThreadPriority(const DeviceState &state); void ClearEvent(const DeviceState &state);
/** /**
* @brief Clears a KEvent of it's signal * @brief Maps the block supplied by the handle
* @url https://switchbrew.org/wiki/SVC#ClearEvent * @url https://switchbrew.org/wiki/SVC#MapSharedMemory
*/ */
void ClearEvent(const DeviceState &state); void MapSharedMemory(const DeviceState &state);
/** /**
* @brief Maps the block supplied by the handle * @brief Returns a handle to a KSharedMemory object
* @url https://switchbrew.org/wiki/SVC#MapSharedMemory * @url https://switchbrew.org/wiki/SVC#CreateTransferMemory
*/ */
void MapSharedMemory(const DeviceState &state); void CreateTransferMemory(const DeviceState &state);
/** /**
* @brief Returns a handle to a KSharedMemory object * @brief Closes the specified handle
* @url https://switchbrew.org/wiki/SVC#CreateTransferMemory * @url https://switchbrew.org/wiki/SVC#CloseHandle
*/ */
void CreateTransferMemory(const DeviceState &state); void CloseHandle(const DeviceState &state);
/** /**
* @brief Closes the specified handle * @brief Resets a particular KEvent or KProcess which is signalled
* @url https://switchbrew.org/wiki/SVC#CloseHandle * @url https://switchbrew.org/wiki/SVC#ResetSignal
*/ */
void CloseHandle(const DeviceState &state); void ResetSignal(const DeviceState &state);
/** /**
* @brief Resets a particular KEvent or KProcess which is signalled * @brief Stalls a thread till a KSyncObject signals or the timeout has ended
* @url https://switchbrew.org/wiki/SVC#ResetSignal * @url https://switchbrew.org/wiki/SVC#WaitSynchronization
*/ */
void ResetSignal(const DeviceState &state); void WaitSynchronization(const DeviceState &state);
/** /**
* @brief Stalls a thread till a KSyncObject signals or the timeout has ended * @brief If the referenced thread is currently in a synchronization call, that call will be interrupted
* @url https://switchbrew.org/wiki/SVC#WaitSynchronization * @url https://switchbrew.org/wiki/SVC#CancelSynchronization
*/ */
void WaitSynchronization(const DeviceState &state); void CancelSynchronization(const DeviceState &state);
/** /**
* @brief If the referenced thread is currently in a synchronization call, that call will be interrupted * @brief Locks a specified mutex
* @url https://switchbrew.org/wiki/SVC#CancelSynchronization * @url https://switchbrew.org/wiki/SVC#ArbitrateLock
*/ */
void CancelSynchronization(const DeviceState &state); void ArbitrateLock(const DeviceState &state);
/** /**
* @brief Locks a specified mutex * @brief Unlocks a specified mutex
* @url https://switchbrew.org/wiki/SVC#ArbitrateLock * @url https://switchbrew.org/wiki/SVC#ArbitrateUnlock
*/ */
void ArbitrateLock(const DeviceState &state); void ArbitrateUnlock(const DeviceState &state);
/** /**
* @brief Unlocks a specified mutex * @brief Waits on a process-wide key (Conditional-Variable)
* @url https://switchbrew.org/wiki/SVC#ArbitrateUnlock * @url https://switchbrew.org/wiki/SVC#WaitProcessWideKeyAtomic
*/ */
void ArbitrateUnlock(const DeviceState &state); void WaitProcessWideKeyAtomic(const DeviceState &state);
/** /**
* @brief Waits on a process-wide key (Conditional-Variable) * @brief Signals a process-wide key (Conditional-Variable)
* @url https://switchbrew.org/wiki/SVC#WaitProcessWideKeyAtomic * @url https://switchbrew.org/wiki/SVC#SignalProcessWideKey
*/ */
void WaitProcessWideKeyAtomic(const DeviceState &state); void SignalProcessWideKey(const DeviceState &state);
/** /**
* @brief Signals a process-wide key (Conditional-Variable) * @brief Returns the value of CNTPCT_EL0 on the Switch
* @url https://switchbrew.org/wiki/SVC#SignalProcessWideKey * @url https://switchbrew.org/wiki/SVC#GetSystemTick
*/ */
void SignalProcessWideKey(const DeviceState &state); void GetSystemTick(const DeviceState &state);
/** /**
* @brief Returns the value of CNTPCT_EL0 on the Switch * @brief Connects to a named IPC port
* @url https://switchbrew.org/wiki/SVC#GetSystemTick * @url https://switchbrew.org/wiki/SVC#ConnectToNamedPort
*/ */
void GetSystemTick(const DeviceState &state); void ConnectToNamedPort(const DeviceState &state);
/** /**
* @brief Connects to a named IPC port * @brief Send a synchronous IPC request to a service
* @url https://switchbrew.org/wiki/SVC#ConnectToNamedPort * @url https://switchbrew.org/wiki/SVC#SendSyncRequest
*/ */
void ConnectToNamedPort(const DeviceState &state); void SendSyncRequest(const DeviceState &state);
/** /**
* @brief Send a synchronous IPC request to a service * @brief Retrieves the PID of a specific thread
* @url https://switchbrew.org/wiki/SVC#SendSyncRequest * @url https://switchbrew.org/wiki/SVC#GetThreadId
*/ */
void SendSyncRequest(const DeviceState &state); void GetThreadId(const DeviceState &state);
/** /**
* @brief Retrieves the PID of a specific thread * @brief Outputs a debug string
* @url https://switchbrew.org/wiki/SVC#GetThreadId * @url https://switchbrew.org/wiki/SVC#OutputDebugString
*/ */
void GetThreadId(const DeviceState &state); void OutputDebugString(const DeviceState &state);
/** /**
* @brief Outputs a debug string * @brief Retrieves a piece of information
* @url https://switchbrew.org/wiki/SVC#OutputDebugString * @url https://switchbrew.org/wiki/SVC#GetInfo
*/ */
void OutputDebugString(const DeviceState &state); void GetInfo(const DeviceState &state);
/** /**
* @brief Retrieves a piece of information * @brief Maps physical memory to a part of virtual memory
* @url https://switchbrew.org/wiki/SVC#GetInfo * @url https://switchbrew.org/wiki/SVC#MapPhysicalMemory
*/ */
void GetInfo(const DeviceState &state); void MapPhysicalMemory(const DeviceState &state);
/** /**
* @brief The SVC Table maps all SVCs to their corresponding functions * @brief Unmaps previously mapped physical memory
*/ * @url https://switchbrew.org/wiki/SVC#UnmapPhysicalMemory
static std::array<void (*)(const DeviceState &), 0x80> SvcTable{ */
nullptr, // 0x00 (Does not exist) void UnmapPhysicalMemory(const DeviceState &state);
SetHeapSize, // 0x01
nullptr, // 0x02 /**
SetMemoryAttribute, // 0x03 * @brief The SVC Table maps all SVCs to their corresponding functions
MapMemory, // 0x04 */
UnmapMemory, // 0x05 static std::array<void (*)(const DeviceState &), 0x80> SvcTable{
QueryMemory, // 0x06 nullptr, // 0x00 (Does not exist)
ExitProcess, // 0x07 SetHeapSize, // 0x01
CreateThread, // 0x08 nullptr, // 0x02
StartThread, // 0x09 SetMemoryAttribute, // 0x03
ExitThread, // 0x0A MapMemory, // 0x04
SleepThread, // 0x0B UnmapMemory, // 0x05
GetThreadPriority, // 0x0C QueryMemory, // 0x06
SetThreadPriority, // 0x0D ExitProcess, // 0x07
nullptr, // 0x0E CreateThread, // 0x08
nullptr, // 0x0F StartThread, // 0x09
nullptr, // 0x10 ExitThread, // 0x0A
nullptr, // 0x11 SleepThread, // 0x0B
ClearEvent, // 0x12 GetThreadPriority, // 0x0C
MapSharedMemory, // 0x13 SetThreadPriority, // 0x0D
nullptr, // 0x14 nullptr, // 0x0E
CreateTransferMemory, // 0x15 nullptr, // 0x0F
CloseHandle, // 0x16 nullptr, // 0x10
ResetSignal, // 0x17 nullptr, // 0x11
WaitSynchronization, // 0x18 ClearEvent, // 0x12
CancelSynchronization, // 0x19 MapSharedMemory, // 0x13
ArbitrateLock, // 0x1A nullptr, // 0x14
ArbitrateUnlock, // 0x1B CreateTransferMemory, // 0x15
WaitProcessWideKeyAtomic, // 0x1C CloseHandle, // 0x16
SignalProcessWideKey, // 0x1D ResetSignal, // 0x17
GetSystemTick, // 0x1E WaitSynchronization, // 0x18
ConnectToNamedPort, // 0x1F CancelSynchronization, // 0x19
nullptr, // 0x20 ArbitrateLock, // 0x1A
SendSyncRequest, // 0x21 ArbitrateUnlock, // 0x1B
nullptr, // 0x22 WaitProcessWideKeyAtomic, // 0x1C
nullptr, // 0x23 SignalProcessWideKey, // 0x1D
nullptr, // 0x24 GetSystemTick, // 0x1E
GetThreadId, // 0x25 ConnectToNamedPort, // 0x1F
nullptr, // 0x26 nullptr, // 0x20
OutputDebugString, // 0x27 SendSyncRequest, // 0x21
nullptr, // 0x28 nullptr, // 0x22
GetInfo, // 0x29 nullptr, // 0x23
nullptr, // 0x2A nullptr, // 0x24
nullptr, // 0x2B GetThreadId, // 0x25
nullptr, // 0x2C nullptr, // 0x26
nullptr, // 0x2D OutputDebugString, // 0x27
nullptr, // 0x2E nullptr, // 0x28
nullptr, // 0x2F GetInfo, // 0x29
nullptr, // 0x30 nullptr, // 0x2A
nullptr, // 0x31 nullptr, // 0x2B
nullptr, // 0x32 MapPhysicalMemory, // 0x2C
nullptr, // 0x33 UnmapPhysicalMemory, // 0x2D
nullptr, // 0x34 nullptr, // 0x2E
nullptr, // 0x35 nullptr, // 0x2F
nullptr, // 0x36 nullptr, // 0x30
nullptr, // 0x37 nullptr, // 0x31
nullptr, // 0x38 nullptr, // 0x32
nullptr, // 0x39 nullptr, // 0x33
nullptr, // 0x3A nullptr, // 0x34
nullptr, // 0x3B nullptr, // 0x35
nullptr, // 0x3C nullptr, // 0x36
nullptr, // 0x3D nullptr, // 0x37
nullptr, // 0x3E nullptr, // 0x38
nullptr, // 0x3F nullptr, // 0x39
nullptr, // 0x40 nullptr, // 0x3A
nullptr, // 0x41 nullptr, // 0x3B
nullptr, // 0x42 nullptr, // 0x3C
nullptr, // 0x43 nullptr, // 0x3D
nullptr, // 0x44 nullptr, // 0x3E
nullptr, // 0x45 nullptr, // 0x3F
nullptr, // 0x46 nullptr, // 0x40
nullptr, // 0x47 nullptr, // 0x41
nullptr, // 0x48 nullptr, // 0x42
nullptr, // 0x49 nullptr, // 0x43
nullptr, // 0x4A nullptr, // 0x44
nullptr, // 0x4B nullptr, // 0x45
nullptr, // 0x4C nullptr, // 0x46
nullptr, // 0x4D nullptr, // 0x47
nullptr, // 0x4E nullptr, // 0x48
nullptr, // 0x4F nullptr, // 0x49
nullptr, // 0x50 nullptr, // 0x4A
nullptr, // 0x51 nullptr, // 0x4B
nullptr, // 0x52 nullptr, // 0x4C
nullptr, // 0x53 nullptr, // 0x4D
nullptr, // 0x54 nullptr, // 0x4E
nullptr, // 0x55 nullptr, // 0x4F
nullptr, // 0x56 nullptr, // 0x50
nullptr, // 0x57 nullptr, // 0x51
nullptr, // 0x58 nullptr, // 0x52
nullptr, // 0x59 nullptr, // 0x53
nullptr, // 0x5A nullptr, // 0x54
nullptr, // 0x5B nullptr, // 0x55
nullptr, // 0x5C nullptr, // 0x56
nullptr, // 0x5D nullptr, // 0x57
nullptr, // 0x5E nullptr, // 0x58
nullptr, // 0x5F nullptr, // 0x59
nullptr, // 0x60 nullptr, // 0x5A
nullptr, // 0x61 nullptr, // 0x5B
nullptr, // 0x62 nullptr, // 0x5C
nullptr, // 0x63 nullptr, // 0x5D
nullptr, // 0x64 nullptr, // 0x5E
nullptr, // 0x65 nullptr, // 0x5F
nullptr, // 0x66 nullptr, // 0x60
nullptr, // 0x67 nullptr, // 0x61
nullptr, // 0x68 nullptr, // 0x62
nullptr, // 0x69 nullptr, // 0x63
nullptr, // 0x6A nullptr, // 0x64
nullptr, // 0x6B nullptr, // 0x65
nullptr, // 0x6C nullptr, // 0x66
nullptr, // 0x6D nullptr, // 0x67
nullptr, // 0x6E nullptr, // 0x68
nullptr, // 0x6F nullptr, // 0x69
nullptr, // 0x70 nullptr, // 0x6A
nullptr, // 0x71 nullptr, // 0x6B
nullptr, // 0x72 nullptr, // 0x6C
nullptr, // 0x73 nullptr, // 0x6D
nullptr, // 0x74 nullptr, // 0x6E
nullptr, // 0x75 nullptr, // 0x6F
nullptr, // 0x76 nullptr, // 0x70
nullptr, // 0x77 nullptr, // 0x71
nullptr, // 0x78 nullptr, // 0x72
nullptr, // 0x79 nullptr, // 0x73
nullptr, // 0x7A nullptr, // 0x74
nullptr, // 0x7B nullptr, // 0x75
nullptr, // 0x7C nullptr, // 0x76
nullptr, // 0x7D nullptr, // 0x77
nullptr, // 0x7E nullptr, // 0x78
nullptr // 0x7F nullptr, // 0x79
}; nullptr, // 0x7A
} nullptr, // 0x7B
nullptr, // 0x7C
nullptr, // 0x7D
nullptr, // 0x7E
nullptr // 0x7F
};
} }

View File

@ -8,7 +8,7 @@
#include "KProcess.h" #include "KProcess.h"
namespace skyline::kernel::type { namespace skyline::kernel::type {
KPrivateMemory::KPrivateMemory(const DeviceState &state, u8* ptr, size_t size, memory::Permission permission, memory::MemoryState memState) : ptr(ptr), size(size), permission(permission), memState(memState), KMemory(state, KType::KPrivateMemory) { KPrivateMemory::KPrivateMemory(const DeviceState &state, u8 *ptr, size_t size, memory::Permission permission, memory::MemoryState memState) : ptr(ptr), size(size), permission(permission), memState(memState), KMemory(state, KType::KPrivateMemory) {
if (!state.process->memory.base.IsInside(ptr) || !state.process->memory.base.IsInside(ptr + size)) if (!state.process->memory.base.IsInside(ptr) || !state.process->memory.base.IsInside(ptr + size))
throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", ptr, ptr + size); throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", ptr, ptr + size);
if (!util::PageAligned(ptr) || !util::PageAligned(size)) if (!util::PageAligned(ptr) || !util::PageAligned(size))
@ -47,7 +47,20 @@ namespace skyline::kernel::type {
size = nSize; size = nSize;
} }
void KPrivateMemory::UpdatePermission(u8* ptr, size_t size, memory::Permission permission) { void KPrivateMemory::Remap(u8 *nPtr, size_t nSize) {
if (!state.process->memory.base.IsInside(nPtr) || !state.process->memory.base.IsInside(nPtr + nSize))
throw exception("KPrivateMemory remapping isn't inside guest address space: 0x{:X} - 0x{:X}", nPtr, nPtr + nSize);
if (!util::PageAligned(nPtr) || !util::PageAligned(nSize))
throw exception("KPrivateMemory remapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", nPtr, nPtr + nSize, nSize);
if (mprotect(ptr, size, PROT_NONE) < 0)
throw exception("An occurred while remapping private memory: {}", strerror(errno));
if (mprotect(nPtr, nSize, PROT_NONE) < 0)
throw exception("An occurred while remapping private memory: {}", strerror(errno));
}
void KPrivateMemory::UpdatePermission(u8 *ptr, size_t size, memory::Permission permission) {
ptr = std::clamp(ptr, this->ptr, this->ptr + this->size); ptr = std::clamp(ptr, this->ptr, this->ptr + this->size);
size = std::min(size, static_cast<size_t>((this->ptr + this->size) - ptr)); size = std::min(size, static_cast<size_t>((this->ptr + this->size) - ptr));

View File

@ -24,6 +24,11 @@ namespace skyline::kernel::type {
void Resize(size_t size); void Resize(size_t size);
/**
* @note Only contents of any overlapping regions will be retained
*/
void Remap(u8* ptr, size_t size);
inline span<u8> Get() override { inline span<u8> Get() override {
return span(ptr, size); return span(ptr, size);
} }

View File

@ -23,6 +23,9 @@ namespace skyline {
* @brief KProcess manages process-global state such as memory, kernel handles allocated to the process and synchronization primitives * @brief KProcess manages process-global state such as memory, kernel handles allocated to the process and synchronization primitives
*/ */
class KProcess : public KSyncObject { class KProcess : public KSyncObject {
public:
MemoryManager memory; // This is here to ensure it is present during the destruction of dependent objects
private: private:
std::vector<std::shared_ptr<KObject>> handles; std::vector<std::shared_ptr<KObject>> handles;
std::shared_mutex handleMutex; std::shared_mutex handleMutex;
@ -63,7 +66,6 @@ namespace skyline {
}; };
public: public:
MemoryManager memory;
std::shared_ptr<KPrivateMemory> mainThreadStack; std::shared_ptr<KPrivateMemory> mainThreadStack;
std::shared_ptr<KPrivateMemory> heap; std::shared_ptr<KPrivateMemory> heap;
std::vector<std::shared_ptr<KThread>> threads; std::vector<std::shared_ptr<KThread>> threads;

View File

@ -1,7 +1,6 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <nce.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include "nso.h" #include "nso.h"
#include "nca.h" #include "nca.h"

View File

@ -35,7 +35,9 @@ namespace skyline::nce {
void NCE::SignalHandler(int signal, siginfo *, void *context) { void NCE::SignalHandler(int signal, siginfo *, void *context) {
ThreadContext *threadCtx; ThreadContext *threadCtx;
asm("MRS %0, TPIDR_EL0":"=r"(threadCtx)); asm volatile("MRS %x0, TPIDR_EL0":"=r"(threadCtx));
asm volatile("MSR TPIDR_EL0, %x0"::"r"(threadCtx->hostTpidrEl0));
const auto &state{*threadCtx->state}; const auto &state{*threadCtx->state};
state.logger->Warn("Thread #{} has crashed due to signal: {}", state.thread->id, strsignal(signal)); state.logger->Warn("Thread #{} has crashed due to signal: {}", state.thread->id, strsignal(signal));
@ -68,9 +70,11 @@ namespace skyline::nce {
for (u8 index{}; index < ((sizeof(mcontext_t::regs) / sizeof(u64)) - 2); index += 2) for (u8 index{}; index < ((sizeof(mcontext_t::regs) / sizeof(u64)) - 2); index += 2)
cpuContext += fmt::format("\n{}X{}: 0x{:<16X} {}{}: 0x{:X}", index < 10 ? ' ' : '\0', index, ctx.regs[index], index < 10 ? ' ' : '\0', index + 1, ctx.regs[index]); cpuContext += fmt::format("\n{}X{}: 0x{:<16X} {}{}: 0x{:X}", index < 10 ? ' ' : '\0', index, ctx.regs[index], index < 10 ? ' ' : '\0', index + 1, ctx.regs[index]);
state.logger->Debug("Process Trace:{}", trace); state.logger->Warn("Process Trace:{}", trace);
state.logger->Debug("Raw Instructions: 0x{}", raw); state.logger->Warn("Raw Instructions: 0x{}", raw);
state.logger->Debug("CPU Context:{}", cpuContext); state.logger->Warn("CPU Context:{}", cpuContext);
asm volatile("MSR TPIDR_EL0, %x0"::"r"(threadCtx));
} }
NCE::NCE(DeviceState &state) : state(state) {} NCE::NCE(DeviceState &state) : state(state) {}

View File

@ -8,6 +8,20 @@
namespace skyline { namespace skyline {
struct DeviceState; struct DeviceState;
namespace nce { namespace nce {
struct WRegister {
u32 lower;
u32 upper;
constexpr operator u32() {
return lower;
}
void operator=(u32 value) {
lower = value;
upper = 0;
}
};
/** /**
* @brief The state of callee-saved general purpose registers in the guest * @brief The state of callee-saved general purpose registers in the guest
* @note Read about ARMv8 registers here: https://developer.arm.com/architectures/learn-the-architecture/armv8-a-instruction-set-architecture/registers-in-aarch64-general-purpose-registers * @note Read about ARMv8 registers here: https://developer.arm.com/architectures/learn-the-architecture/armv8-a-instruction-set-architecture/registers-in-aarch64-general-purpose-registers
@ -37,44 +51,25 @@ namespace skyline {
u64 x18; u64 x18;
}; };
struct { struct {
u32 w0; WRegister w0;
u32 __w0__; WRegister w1;
u32 w1; WRegister w2;
u32 __w1__; WRegister w3;
u32 w2; WRegister w4;
u32 __w2__; WRegister w5;
u32 w3; WRegister w6;
u32 __w3__; WRegister w7;
u32 w4; WRegister w8;
u32 __w4__; WRegister w9;
u32 w5; WRegister w10;
u32 __w5__; WRegister w11;
u32 w6; WRegister w12;
u32 __w6__; WRegister w13;
u32 w7; WRegister w14;
u32 __w7__; WRegister w15;
u32 w8; WRegister w16;
u32 __w8__; WRegister w17;
u32 w9; WRegister w18;
u32 __w9__;
u32 w10;
u32 __w10__;
u32 w11;
u32 __w11__;
u32 w12;
u32 __w12__;
u32 w13;
u32 __w13__;
u32 w14;
u32 __w14__;
u32 w15;
u32 __w15__;
u32 w16;
u32 __w16__;
u32 w17;
u32 __w17__;
u32 w18;
u32 __w18__;
}; };
}; };