Work on making the kernel thread-safe #1 + add asynchronous ROM scanning

This commit makes the kernel thread safe in some instances (but not fully) and showcases the significantly improved performance over the ptrace method used prior. In addition, scanning for ROMs is now done asynchronously.
This commit is contained in:
◱ PixelyIon 2020-01-09 07:07:54 +05:30 committed by ◱ PixelyIon
parent 970dde8c27
commit de6d8d8f48
21 changed files with 804 additions and 157 deletions

View File

@ -5,6 +5,7 @@
#include <vector> #include <vector>
#include <fstream> #include <fstream>
#include <syslog.h> #include <syslog.h>
#include <mutex>
#import <thread> #import <thread>
#include <string> #include <string>
#include <sstream> #include <sstream>
@ -52,6 +53,7 @@ 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

View File

@ -42,7 +42,6 @@ namespace skyline::gpu {
resolution = buffer->resolution; resolution = buffer->resolution;
format = buffer->gbpBuffer.format; format = buffer->gbpBuffer.format;
} }
buffer->UpdateBuffer();
u8 *inBuffer = buffer->dataBuffer.data(); u8 *inBuffer = buffer->dataBuffer.data();
madvise(inBuffer, buffer->gbpBuffer.size, MADV_SEQUENTIAL); madvise(inBuffer, buffer->gbpBuffer.size, MADV_SEQUENTIAL);
ANativeWindow_Buffer windowBuffer; ANativeWindow_Buffer windowBuffer;

View File

@ -49,25 +49,22 @@ namespace skyline::gpu {
state.logger->Debug("RequestBuffer: Slot: {}", slot, sizeof(GbpBuffer)); state.logger->Debug("RequestBuffer: Slot: {}", slot, sizeof(GbpBuffer));
} }
bool BufferQueue::DequeueBuffer(Parcel &in, Parcel &out, kernel::ipc::OutputBuffer& buffer) { void BufferQueue::DequeueBuffer(Parcel &in, Parcel &out) {
auto *data = reinterpret_cast<DequeueIn *>(in.data.data() + constant::TokenLength); auto *data = reinterpret_cast<DequeueIn *>(in.data.data() + constant::TokenLength);
i64 slot{-1}; i64 slot{-1};
while(slot == -1) {
for (auto &buffer : queue) { for (auto &buffer : queue) {
if (buffer.second->status == BufferStatus::Free && buffer.second->resolution.width == data->width && buffer.second->resolution.height == data->height && buffer.second->gbpBuffer.usage == data->usage) { if (buffer.second->status == BufferStatus::Free && buffer.second->resolution.width == data->width && buffer.second->resolution.height == data->height && buffer.second->gbpBuffer.usage == data->usage) {
slot = buffer.first; slot = buffer.first;
buffer.second->status = BufferStatus::Dequeued; buffer.second->status = BufferStatus::Dequeued;
break;
} }
} }
if (slot == -1) { sched_yield();
state.thread->Sleep();
waitVec.emplace_back(state.thread, *data, buffer);
state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, No Free Buffers", data->width, data->height, data->format, data->usage, data->timestamps);
return true;
} }
DequeueOut output(static_cast<u32>(slot)); DequeueOut output(static_cast<u32>(slot));
out.WriteData(output); out.WriteData(output);
state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, Slot: {}", data->width, data->height, data->format, data->usage, data->timestamps, slot); state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, Slot: {}", data->width, data->height, data->format, data->usage, data->timestamps, slot);
return false;
} }
void BufferQueue::QueueBuffer(Parcel &in, Parcel &out) { void BufferQueue::QueueBuffer(Parcel &in, Parcel &out) {
@ -85,6 +82,7 @@ namespace skyline::gpu {
} *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength); } *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength);
auto buffer = queue.at(data->slot); auto buffer = queue.at(data->slot);
buffer->status = BufferStatus::Queued; buffer->status = BufferStatus::Queued;
buffer->UpdateBuffer();
displayQueue.emplace(buffer); displayQueue.emplace(buffer);
state.gpu->bufferEvent->Signal(); state.gpu->bufferEvent->Signal();
struct { struct {

View File

@ -106,6 +106,7 @@ namespace skyline::gpu {
GbpBuffer gbpBuffer; //!< The information about the underlying buffer GbpBuffer gbpBuffer; //!< The information about the underlying buffer
BufferStatus status{BufferStatus::Free}; //!< The status of this buffer BufferStatus status{BufferStatus::Free}; //!< The status of this buffer
std::vector<u8> dataBuffer; //!< The vector holding the actual pixel data std::vector<u8> dataBuffer; //!< The vector holding the actual pixel data
std::vector<u8> swizzBuffer; //!< The vector holding the swizzled pixel data
std::shared_ptr<device::NvMap::NvMapObject> nvBuffer{}; //!< A shared pointer to the buffer's nvmap object std::shared_ptr<device::NvMap::NvMapObject> nvBuffer{}; //!< A shared pointer to the buffer's nvmap object
/** /**
@ -187,10 +188,8 @@ namespace skyline::gpu {
/** /**
* @brief This returns the slot of a free buffer * @brief This returns the slot of a free buffer
* @param buffer The output parcel buffer
* @return If the process is waiting for a buffer or not
*/ */
bool DequeueBuffer(Parcel &in, Parcel &out, kernel::ipc::OutputBuffer& buffer); void DequeueBuffer(Parcel &in, Parcel &out);
/** /**
* @brief This queues a buffer to be displayed * @brief This queues a buffer to be displayed

View File

@ -73,7 +73,7 @@ namespace skyline::kernel::svc {
void QueryMemory(DeviceState &state) { void QueryMemory(DeviceState &state) {
memory::MemoryInfo memInfo{}; memory::MemoryInfo memInfo{};
u64 addr = state.ctx->registers.x2; u64 addr = (state.ctx->registers.x2 & ~(PAGE_SIZE - 1));
bool found = false; bool found = false;
for (const auto&[address, region] : state.process->memoryMap) { for (const auto&[address, region] : state.process->memoryMap) {
if (addr >= address && addr < (address + region->size)) { if (addr >= address && addr < (address + region->size)) {
@ -111,7 +111,7 @@ namespace skyline::kernel::svc {
state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", addr); state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", addr);
} }
} }
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: {}, 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.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<memory::MemoryInfo>(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;
} }

View File

@ -0,0 +1,534 @@
#include "svc.h"
#include <os.h>
namespace skyline::kernel::svc {
void SetHeapSize(DeviceState &state) {
const u32 size = state.ctx->registers.w1;
if(size%constant::HeapSizeDiv != 0) {
state.ctx->registers.x1 = 0;
state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
return;
}
std::shared_ptr<type::KPrivateMemory> heap;
try {
heap = state.process->memoryRegionMap.at(memory::Region::Heap);
heap->Resize(size, true);
} catch (const exception &) {
state.logger->Warn("svcSetHeapSize: Falling back to recreating memory");
state.process->UnmapPrivateRegion(memory::Region::Heap);
heap = state.process->MapPrivateRegion(constant::HeapAddr, size, {true, true, false}, memory::Type::Heap, memory::Region::Heap).item;
}
state.ctx->registers.w0 = constant::status::Success;
state.ctx->registers.x1 = heap->address;
state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size);
}
void SetMemoryAttribute(DeviceState &state) {
const u64 addr = state.ctx->registers.x0;
if((addr & (PAGE_SIZE - 1U))) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: {}", addr);
return;
}
const u64 size = state.ctx->registers.x1;
if((size & (PAGE_SIZE - 1U)) || !size) {
state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcSetMemoryAttribute: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
return;
}
u32 mask = state.ctx->registers.w2;
u32 value = state.ctx->registers.w3;
u32 maskedValue = mask | value;
if(maskedValue != mask) {
state.ctx->registers.w0 = constant::status::InvCombination;
state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask, value);
return;
}
memory::MemoryAttribute attribute = *reinterpret_cast<memory::MemoryAttribute*>(&maskedValue);
bool found = false;
for (const auto&[address, region] : state.process->memoryMap) {
if (addr >= address && addr < (address + region->size)) {
bool subFound = false;
for (auto &subregion : region->regionInfoVec) {
if ((address >= subregion.address) && (address < (subregion.address + subregion.size)))
subregion.isUncached = attribute.isUncached;
subFound = true;
break;
}
if (!subFound)
region->regionInfoVec.emplace_back(addr, size, static_cast<bool>(attribute.isUncached));
found = true;
break;
}
}
if(!found) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", addr);
return;
}
state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", !attribute.isUncached, addr, size);
state.ctx->registers.w0 = constant::status::Success;
}
void QueryMemory(DeviceState &state) {
memory::MemoryInfo memInfo{};
u64 addr = (state.ctx->registers.x2 & ~(PAGE_SIZE-1));
bool found = false;
for (const auto&[address, region] : state.process->memoryMap) {
if (addr >= address && addr < (address + region->size)) {
memInfo = region->GetInfo(addr);
found = true;
break;
}
}
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 = {
.baseAddress = constant::BaseAddr,
.size = static_cast<u64>(constant::BaseEnd),
.type = static_cast<u64>(memory::Type::Unmapped)
};
state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", addr);
}
}
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.baseAddress, memInfo.size, memInfo.type, static_cast<bool>(memInfo.memoryAttribute.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-");
state.process->WriteMemory<memory::MemoryInfo>(memInfo, state.ctx->registers.x0);
state.ctx->registers.w0 = constant::status::Success;
}
void ExitProcess(DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting current process: {}", state.process->pid);
state.os->KillThread(state.process->pid);
}
void CreateThread(DeviceState &state) {
u64 entryAddr = state.ctx->registers.x1;
u64 entryArg = state.ctx->registers.x2;
u64 stackTop = state.ctx->registers.x3;
u8 priority = static_cast<u8>(state.ctx->registers.w4);
if((priority < constant::PriorityNin.first) && (priority > constant::PriorityNin.second)) { // NOLINT(misc-redundant-expression)
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
return;
}
auto thread = state.process->CreateThread(entryAddr, entryArg, stackTop, priority);
state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, PID: {})", thread->handle, entryAddr, entryArg, stackTop, priority, thread->pid);
state.ctx->registers.w1 = thread->handle;
state.ctx->registers.w0 = constant::status::Success;
}
void StartThread(DeviceState &state) {
auto handle = state.ctx->registers.w0;
try {
auto thread = state.process->GetHandle<type::KThread>(handle);
state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->pid);
thread->Start();
} catch (const std::exception&) {
state.logger->Warn("svcStartThread: 'handle' invalid: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::InvHandle;
}
}
void ExitThread(DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting current thread: {}", state.thread->pid);
state.os->KillThread(state.thread->pid);
}
void SleepThread(DeviceState &state) {
auto in = state.ctx->registers.x0;
switch (in) {
case 0:
case 1:
case 2:
state.logger->Debug("svcSleepThread: Yielding thread: {}", in);
state.thread->status = type::KThread::Status::Runnable; // Will cause the application to awaken on the next iteration of the main loop
break;
default:
state.logger->Debug("svcSleepThread: Thread sleeping for {} ns", in);
state.thread->timeout = GetCurrTimeNs() + in;
state.thread->status = type::KThread::Status::Sleeping;
}
}
void GetThreadPriority(DeviceState &state) {
auto handle = state.ctx->registers.w0;
try {
auto priority = state.process->GetHandle<type::KThread>(handle)->priority;
state.ctx->registers.w1 = priority;
state.ctx->registers.w0 = constant::status::Success;
state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority);
} catch (const std::exception&) {
state.logger->Warn("svcGetThreadPriority: 'handle' invalid: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::InvHandle;
}
}
void SetThreadPriority(DeviceState &state) {
auto handle = state.ctx->registers.w0;
auto priority = state.ctx->registers.w1;
try {
state.process->GetHandle<type::KThread>(handle)->UpdatePriority(static_cast<u8>(priority));
state.ctx->registers.w0 = constant::status::Success;
state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority);
} catch (const std::exception&) {
state.logger->Warn("svcSetThreadPriority: 'handle' invalid: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::InvHandle;
}
}
void MapSharedMemory(DeviceState &state) {
try {
auto object = state.process->GetHandle<type::KSharedMemory>(state.ctx->registers.w0);
u64 addr = state.ctx->registers.x1;
if ((addr & (PAGE_SIZE - 1U))) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", addr);
return;
}
const u64 size = state.ctx->registers.x2;
if ((size & (PAGE_SIZE - 1U)) || !size) {
state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcMapSharedMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
return;
}
u32 perm = state.ctx->registers.w3;
memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
state.logger->Warn("svcMapSharedMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
state.ctx->registers.w0 = constant::status::InvPermission;
return;
}
state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
object->Map(addr, size, permission);
state.ctx->registers.w0 = constant::status::Success;
} catch (const std::exception &) {
state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->registers.w0);
state.ctx->registers.w0 = constant::status::InvHandle;
}
}
void CreateTransferMemory(DeviceState &state) {
u64 addr = state.ctx->registers.x1;
if ((addr & (PAGE_SIZE - 1U))) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: {}", addr);
return;
}
u64 size = state.ctx->registers.x2;
if ((size & (PAGE_SIZE - 1U)) || !size) {
state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcCreateTransferMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
return;
}
u32 perm = state.ctx->registers.w3;
memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
state.logger->Warn("svcCreateTransferMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
state.ctx->registers.w0 = constant::status::InvPermission;
return;
}
state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
auto shmem = state.process->NewHandle<type::KTransferMemory>(state.process->pid, addr, size, permission);
state.ctx->registers.w0 = constant::status::Success;
state.ctx->registers.w1 = shmem.handle;
}
void CloseHandle(DeviceState &state) {
auto handle = static_cast<handle_t>(state.ctx->registers.w0);
try {
state.process->handleTable.erase(handle);
state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::Success;
} catch(const std::exception&) {
state.logger->Warn("svcCloseHandle: 'handle' invalid: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::InvHandle;
}
}
void ResetSignal(DeviceState &state) {
auto handle = state.ctx->registers.w0;
try {
auto &object = state.process->handleTable.at(handle);
switch (object->objectType) {
case (type::KType::KEvent):
std::static_pointer_cast<type::KEvent>(object)->ResetSignal();
break;
case (type::KType::KProcess):
std::static_pointer_cast<type::KProcess>(object)->ResetSignal();
break;
default: {
state.logger->Warn("svcResetSignal: 'handle' type invalid: 0x{:X} ({})", handle, object->objectType);
state.ctx->registers.w0 = constant::status::InvHandle;
return;
}
}
state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::Success;
} catch(const std::out_of_range&) {
state.logger->Warn("svcResetSignal: 'handle' invalid: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::InvHandle;
return;
}
}
void WaitSynchronization(DeviceState &state) {
auto numHandles = state.ctx->registers.w2;
if (numHandles > constant::MaxSyncHandles) {
state.ctx->registers.w0 = constant::status::MaxHandles;
return;
}
std::vector<handle_t> waitHandles(numHandles);
state.process->ReadMemory(waitHandles.data(), state.ctx->registers.x1, numHandles * sizeof(handle_t));
std::string handleStr;
uint index{};
for (const auto &handle : waitHandles) {
handleStr += fmt::format("* 0x{:X}\n", handle);
auto object = state.process->handleTable.at(handle);
switch (object->objectType) {
case type::KType::KProcess:
case type::KType::KThread:
case type::KType::KEvent:
case type::KType::KSession:
break;
default: {
state.ctx->registers.w0 = constant::status::InvHandle;
state.thread->ClearWaitObjects();
return;
}
}
auto syncObject = std::static_pointer_cast<type::KSyncObject>(object);
if (syncObject->signalled) {
state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::Success;
state.ctx->registers.w1 = index;
state.thread->ClearWaitObjects();
return;
}
state.thread->waitObjects.push_back(syncObject);
syncObject->waitThreads.emplace_back(state.thread->pid, index);
}
auto timeout = state.ctx->registers.x3;
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
if (state.ctx->registers.x3 != std::numeric_limits<u64>::max())
state.thread->timeout = GetCurrTimeNs() + timeout;
else
state.thread->timeout = 0;
state.thread->status = type::KThread::Status::WaitSync;
}
void ArbitrateLock(DeviceState &state) {
auto addr = state.ctx->registers.x1;
if((addr & ((1UL << WORD_BIT) - 1U))) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcArbitrateLock: 'address' not word aligned: {}", addr);
return;
}
auto handle = state.ctx->registers.w2;
if (handle != state.thread->handle)
throw exception("svcArbitrateLock: Called from another thread");
state.logger->Debug("svcArbitrateLock: Locking mutex at 0x{:X} for thread 0x{:X}", addr, handle);
state.process->MutexLock(addr);
state.ctx->registers.w0 = constant::status::Success;
}
void ArbitrateUnlock(DeviceState &state) {
auto addr = state.ctx->registers.x0;
if((addr & ((1UL << WORD_BIT) - 1U))) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcArbitrateUnlock: 'address' not word aligned: {}", addr);
return;
}
state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", addr);
state.process->MutexUnlock(addr);
state.ctx->registers.w0 = constant::status::Success;
}
void WaitProcessWideKeyAtomic(DeviceState &state) {
auto mtxAddr = state.ctx->registers.x0;
if((mtxAddr & ((1UL << WORD_BIT) - 1U))) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr);
return;
}
auto handle = state.ctx->registers.w2;
if (handle != state.thread->handle)
throw exception("svcWaitProcessWideKeyAtomic: Called from another thread");
state.process->MutexUnlock(mtxAddr);
auto condAddr = state.ctx->registers.x1;
auto &cvarVec = state.process->condVarMap[condAddr];
for (auto thread = cvarVec.begin();; thread++) {
if ((*thread)->priority < state.thread->priority) {
cvarVec.insert(thread, state.thread);
break;
} else if (thread + 1 == cvarVec.end()) {
cvarVec.push_back(state.thread);
break;
}
}
auto timeout = state.ctx->registers.x3;
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddr, condAddr, timeout);
state.thread->status = type::KThread::Status::WaitCondVar;
state.thread->timeout = GetCurrTimeNs() + timeout;
state.ctx->registers.w0 = constant::status::Success;
}
void SignalProcessWideKey(DeviceState &state) {
auto address = state.ctx->registers.x0;
auto count = state.ctx->registers.w1;
state.ctx->registers.w0 = constant::status::Success;
if (!state.process->condVarMap.count(address)) {
state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address);
return;
}
auto &cvarVec = state.process->condVarMap.at(address);
count = std::min(count, static_cast<u32>(cvarVec.size()));
for (uint index = 0; index < count; index++)
cvarVec[index]->status = type::KThread::Status::Runnable;
cvarVec.erase(cvarVec.begin(), cvarVec.begin() + count);
if (cvarVec.empty())
state.process->condVarMap.erase(address);
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count);
}
void GetSystemTick(DeviceState &state) {
u64 tick;
asm("STR X1, [SP, #-16]!\n\t"
"MRS %0, CNTVCT_EL0\n\t"
"MOV X1, #0xF800\n\t"
"MOVK X1, #0x124, lsl #16\n\t"
"MUL %0, %0, X1\n\t"
"MRS X1, CNTFRQ_EL0\n\t"
"UDIV %0, %0, X1\n\t"
"LDR X1, [SP], #16" : "=r"(tick));
state.ctx->registers.x0 = tick;
}
void ConnectToNamedPort(DeviceState &state) {
char port[constant::PortSize + 1]{0};
state.process->ReadMemory(port, state.ctx->registers.x1, constant::PortSize);
handle_t handle{};
if (std::strcmp(port, "sm:") == 0)
handle = state.os->serviceManager.NewSession(service::Service::sm);
else {
state.logger->Warn("svcConnectToNamedPort: Connecting to invalid port: '{}'", port);
state.ctx->registers.w0 = constant::status::NotFound;
return;
}
state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle);
state.ctx->registers.w1 = handle;
state.ctx->registers.w0 = constant::status::Success;
}
void SendSyncRequest(DeviceState &state) {
state.os->serviceManager.SyncRequestHandler(static_cast<handle_t>(state.ctx->registers.x0));
state.ctx->registers.w0 = constant::status::Success;
}
void GetThreadId(DeviceState &state) {
pid_t pid{};
auto handle = state.ctx->registers.w1;
if (handle != constant::ThreadSelf) {
pid = state.process->GetHandle<type::KThread>(handle)->pid;
} else
pid = state.thread->pid;
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
state.ctx->registers.x1 = static_cast<u64>(pid);
state.ctx->registers.w0 = constant::status::Success;
}
void OutputDebugString(DeviceState &state) {
std::string debug(state.ctx->registers.x1, '\0');
state.process->ReadMemory(debug.data(), state.ctx->registers.x0, state.ctx->registers.x1);
if(debug.back() == '\n')
debug.pop_back();
state.logger->Info("Debug Output: {}", debug);
state.ctx->registers.w0 = constant::status::Success;
}
void GetInfo(DeviceState &state) {
auto id0 = state.ctx->registers.w1;
auto handle = state.ctx->registers.w2;
auto id1 = state.ctx->registers.x3;
u64 out{};
switch (id0) {
case constant::infoState::AllowedCpuIdBitmask:
case constant::infoState::AllowedThreadPriorityMask:
case constant::infoState::IsCurrentProcessBeingDebugged:
case constant::infoState::TitleId:
case constant::infoState::PrivilegedProcessId:
break;
case constant::infoState::AliasRegionBaseAddr:
out = constant::MapAddr;
break;
case constant::infoState::AliasRegionSize:
out = constant::MapSize;
break;
case constant::infoState::HeapRegionBaseAddr:
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address;
break;
case constant::infoState::HeapRegionSize:
out = state.process->memoryRegionMap.at(memory::Region::Heap)->size;
break;
case constant::infoState::TotalMemoryAvailable:
out = constant::TotalPhyMem;
break;
case constant::infoState::TotalMemoryUsage:
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz + state.process->GetProgramSize();
break;
case constant::infoState::AddressSpaceBaseAddr:
out = constant::BaseAddr;
break;
case constant::infoState::AddressSpaceSize:
out = constant::BaseEnd;
break;
case constant::infoState::StackRegionBaseAddr:
out = state.thread->stackTop;
break;
case constant::infoState::StackRegionSize:
out = state.process->mainThreadStackSz;
break;
case constant::infoState::PersonalMmHeapSize:
out = constant::TotalPhyMem;
break;
case constant::infoState::PersonalMmHeapUsage:
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz;
break;
case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
out = constant::TotalPhyMem; // TODO: NPDM specifies SystemResourceSize, subtract that from this
break;
case constant::infoState::TotalMemoryUsedWithoutMmHeap:
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz; // TODO: Same as above
break;
case constant::infoState::UserExceptionContextAddr:
out = state.process->tlsPages[0]->Get(0);
break;
default:
state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1);
state.ctx->registers.w0 = constant::status::Unimpl;
return;
}
state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out);
state.ctx->registers.x1 = out;
state.ctx->registers.w0 = constant::status::Success;
}
}

View File

@ -60,8 +60,9 @@ namespace skyline::kernel::type {
info.size = size; info.size = size;
info.type = static_cast<u32>(type); info.type = static_cast<u32>(type);
for (const auto &region : regionInfoVec) for (const auto &region : regionInfoVec)
if ((address >= region.address) && (address < (region.address + region.size))) if ((address >= region.address) && (address < (region.address + region.size))) {
info.memoryAttribute.isUncached = region.isUncached; info.memoryAttribute.isUncached = region.isUncached;
}
info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0); info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0);
info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0); info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0);
info.r = permission.r; info.r = permission.r;
@ -78,11 +79,13 @@ namespace skyline::kernel::type {
KPrivateMemory::~KPrivateMemory() { KPrivateMemory::~KPrivateMemory() {
try { try {
if(state.process) {
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, state.thread); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
}
} catch (const std::exception &) {} } catch (const std::exception &) {}
} }
}; };

View File

@ -42,12 +42,12 @@ namespace skyline::kernel::type {
KSharedMemory::~KSharedMemory() { KSharedMemory::~KSharedMemory() {
try { try {
if (guest.valid()) { if (guest.valid() && state.process) {
Registers fregs{}; Registers fregs{};
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.thread->pid); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
} }
if (kernel.valid()) if (kernel.valid())
UnmapSharedFunc(kernel.address, kernel.size); UnmapSharedFunc(kernel.address, kernel.size);

View File

@ -96,10 +96,12 @@ namespace skyline::kernel::type {
KTransferMemory::~KTransferMemory() { KTransferMemory::~KTransferMemory() {
if (owner) { if (owner) {
try { try {
if(state.process) {
Registers fregs{}; Registers fregs{};
fregs.x0 = cAddress; fregs.x0 = cAddress;
fregs.x1 = cSize; fregs.x1 = cSize;
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, owner); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
}
} catch (const std::exception &) {} } catch (const std::exception &) {}
} else } else
UnmapTransferFunc(cAddress, cSize); UnmapTransferFunc(cAddress, cSize);

View File

@ -17,7 +17,7 @@ namespace skyline::loader {
* @param size The amount to read in bytes * @param size The amount to read in bytes
*/ */
template<typename T> template<typename T>
void ReadOffset(T *output, u64 offset, size_t size) { inline void ReadOffset(T *output, u64 offset, size_t size) {
pread64(romFd, output, size, offset); pread64(romFd, output, size, offset);
} }

View File

@ -1,5 +1,4 @@
#include <sched.h> #include <sched.h>
#include <mutex>
#include "os.h" #include "os.h"
#include "jvm.h" #include "jvm.h"
#include "nce/guest.h" #include "nce/guest.h"
@ -11,6 +10,7 @@ extern skyline::Mutex jniMtx;
namespace skyline { namespace skyline {
void NCE::KernelThread(pid_t thread) { void NCE::KernelThread(pid_t thread) {
try {
state.thread = state.process->threadMap.at(thread); state.thread = state.process->threadMap.at(thread);
state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->guest.address); state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->guest.address);
while (!Halt) { while (!Halt) {
@ -22,16 +22,31 @@ namespace skyline {
(*kernel::svc::SvcTable[svc])(state); (*kernel::svc::SvcTable[svc])(state);
} else } else
throw exception("Unimplemented SVC 0x{:X}", svc); throw exception("Unimplemented SVC 0x{:X}", svc);
} catch (const exception &e) { } catch (const std::exception &e) {
throw exception("{} (SVC: 0x{:X})", e.what(), svc); throw exception("{} (SVC: 0x{:X})", e.what(), svc);
} }
state.ctx->state = ThreadState::WaitRun; state.ctx->state = ThreadState::WaitRun;
} else if (state.ctx->state == ThreadState::GuestCrash) {
state.logger->Warn("Thread with PID {} has crashed due to signal: {}", thread, strsignal(state.ctx->commandId));
ThreadTrace();
break;
} }
} }
} catch (std::exception &e) {
state.logger->Error(e.what());
} catch (...) {
state.logger->Error("An unknown exception has occurred");
}
state.os->KillThread(thread);
} }
NCE::NCE(DeviceState &state) : state(state) {} NCE::NCE(DeviceState &state) : state(state) {}
NCE::~NCE() {
for (auto &thread : threadMap)
thread.second->join();
}
void NCE::Execute() { void NCE::Execute() {
while (!Halt && state.os->process) { while (!Halt && state.os->process) {
std::lock_guard jniGd(jniMtx); std::lock_guard jniGd(jniMtx);
@ -41,30 +56,23 @@ namespace skyline {
Halt = false; Halt = false;
} }
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr<kernel::type::KThread>& thread) { void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) {
auto ctx = reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address); ctx->commandId = static_cast<u32>(call);
u32 cmdId = ctx->commandId;
Registers registers = ctx->registers; Registers registers = ctx->registers;
while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel);
ctx->registers = funcRegs; ctx->registers = funcRegs;
ctx->commandId = static_cast<u32>(call);
ctx->state = ThreadState::WaitFunc; ctx->state = ThreadState::WaitFunc;
while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel);
ctx->commandId = cmdId;
funcRegs = ctx->registers; funcRegs = ctx->registers;
ctx->registers = registers; ctx->registers = registers;
} }
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr<kernel::type::KThread> &thread) {
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, pid_t pid) {
auto ctx = reinterpret_cast<ThreadContext *>(state.process->threadMap.at(pid)->ctxMemory->kernel.address); ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(state.process->threadMap.at(pid)->ctxMemory->kernel.address));
ctx->commandId = static_cast<u32>(call);
Registers registers = ctx->registers;
while(ctx->state != ThreadState::WaitInit || ctx->state != ThreadState::WaitKernel);
ctx->registers = funcRegs;
ctx->state = ThreadState::WaitFunc;
while(ctx->state != ThreadState::WaitInit || ctx->state != ThreadState::WaitKernel);
funcRegs = ctx->registers;
ctx->registers = registers;
} }
void NCE::WaitThreadInit(std::shared_ptr<kernel::type::KThread> &thread) { void NCE::WaitThreadInit(std::shared_ptr<kernel::type::KThread> &thread) {
@ -79,7 +87,7 @@ namespace skyline {
ctx->registers.x0 = entryArg; ctx->registers.x0 = entryArg;
ctx->registers.x1 = handle; ctx->registers.x1 = handle;
ctx->state = ThreadState::WaitRun; ctx->state = ThreadState::WaitRun;
while(ctx->state != ThreadState::Running); state.logger->Debug("Starting thread with PID: {}", thread->pid);
threadMap[thread->pid] = std::make_shared<std::thread>(&NCE::KernelThread, this, thread->pid); threadMap[thread->pid] = std::make_shared<std::thread>(&NCE::KernelThread, this, thread->pid);
} }
@ -91,7 +99,7 @@ namespace skyline {
if (numHist) { if (numHist) {
std::vector<u32> instrs(numHist); std::vector<u32> instrs(numHist);
u64 size = (sizeof(u32) * numHist); u64 size = (sizeof(u32) * numHist);
u64 offset = ctx->pc - size; u64 offset = ctx->pc - size + (2 * sizeof(u32));
state.process->ReadMemory(instrs.data(), offset, size); state.process->ReadMemory(instrs.data(), offset, size);
for (auto &instr : instrs) { for (auto &instr : instrs) {
instr = __builtin_bswap32(instr); instr = __builtin_bswap32(instr);
@ -106,13 +114,14 @@ namespace skyline {
for (u16 index = 0; index < constant::NumRegs - 1; index += 2) { for (u16 index = 0; index < constant::NumRegs - 1; index += 2) {
regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, ctx->registers.regs[index], index + 1, ctx->registers.regs[index + 1]); regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, ctx->registers.regs[index], index + 1, ctx->registers.regs[index + 1]);
} }
if(numHist) if (numHist) {
state.logger->Debug("Process Trace:{}\nRaw Instructions: 0x{}\nCPU Context:{}", trace, raw, regStr); state.logger->Debug("Process Trace:{}", trace);
else state.logger->Debug("Raw Instructions: 0x{}\nCPU Context:{}", raw, regStr);
} else
state.logger->Warn("CPU Context:{}", regStr); state.logger->Warn("CPU Context:{}", regStr);
} }
const std::array<u32, 18> cntpctEl0X0 = { const std::array<u32, 17> cntpctEl0X0 = {
0xA9BF0BE1, // STP X1, X2, [SP, #-16]! 0xA9BF0BE1, // STP X1, X2, [SP, #-16]!
0x3C9F0FE0, // STR Q0, [SP, #-16]! 0x3C9F0FE0, // STR Q0, [SP, #-16]!
0x3C9F0FE1, // STR Q1, [SP, #-16]! 0x3C9F0FE1, // STR Q1, [SP, #-16]!
@ -129,10 +138,10 @@ namespace skyline {
0x9E790000, // FCVTZU X0, D0 0x9E790000, // FCVTZU X0, D0
0x3CC107E2, // LDR Q2, [SP], #16 0x3CC107E2, // LDR Q2, [SP], #16
0x3CC107E1, // LDR Q1, [SP], #16 0x3CC107E1, // LDR Q1, [SP], #16
0xA97F07C0, // LDP X1, X2, [LR, #-16] 0xA8C10BE1, // LDP X1, X2, [SP], #16
}; };
const std::array<u32, 18> cntpctEl0X1 = { const std::array<u32, 17> cntpctEl0X1 = {
0xA9BF0BE0, // STP X0, X2, [SP, #-16]! 0xA9BF0BE0, // STP X0, X2, [SP, #-16]!
0x3C9F0FE0, // STR Q0, [SP, #-16]! 0x3C9F0FE0, // STR Q0, [SP, #-16]!
0x3C9F0FE1, // STR Q1, [SP, #-16]! 0x3C9F0FE1, // STR Q1, [SP, #-16]!
@ -149,10 +158,10 @@ namespace skyline {
0x9E790001, // FCVTZU X0, D0 0x9E790001, // FCVTZU X0, D0
0x3CC107E2, // LDR Q2, [SP], #16 0x3CC107E2, // LDR Q2, [SP], #16
0x3CC107E1, // LDR Q1, [SP], #16 0x3CC107E1, // LDR Q1, [SP], #16
0xA97F0BC0, // LDP X0, X2, [LR, #-16] 0xA8C10BE0, // LDP X0, X2, [SP], #16
}; };
const std::array<u32, 18> cntpctEl0Xn = { std::array<u32, 17> cntpctEl0Xn = {
0xA9BF07E0, // STP X0, X1, [SP, #-16]! 0xA9BF07E0, // STP X0, X1, [SP, #-16]!
0x3C9F0FE0, // STR Q0, [SP, #-16]! 0x3C9F0FE0, // STR Q0, [SP, #-16]!
0x3C9F0FE1, // STR Q1, [SP, #-16]! 0x3C9F0FE1, // STR Q1, [SP, #-16]!
@ -166,10 +175,10 @@ namespace skyline {
0x9E630021, // UCVTF D1, X1 0x9E630021, // UCVTF D1, X1
0x1E621800, // FDIV D0, D0, D2 0x1E621800, // FDIV D0, D0, D2
0x1E610800, // FMUL D0, D0, D1 0x1E610800, // FMUL D0, D0, D1
0x9E790000, // FCVTZU Xn, D0 0x00000000, // FCVTZU Xn, D0 (Set at runtime)
0x3CC107E2, // LDR Q2, [SP], #16 0x3CC107E2, // LDR Q2, [SP], #16
0x3CC107E1, // LDR Q1, [SP], #16 0x3CC107E1, // LDR Q1, [SP], #16
0xA97F07C0, // LDP X0, X1, [LR, #-16] 0xA8C107E0, // LDP X0, X1, [SP], #16
}; };
std::vector<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) { std::vector<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) {
@ -195,14 +204,13 @@ namespace skyline {
auto instrMrs = reinterpret_cast<instr::Mrs *>(address); auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
if (instrSvc->Verify()) { if (instrSvc->Verify()) {
u64 pc = baseAddress + (address - start);
instr::B bjunc(offset); instr::B bjunc(offset);
constexpr u32 strLr = 0xF81F0FFE; // STR LR, [SP, #-16]! constexpr u32 strLr = 0xF81F0FFE; // STR LR, [SP, #-16]!
offset += sizeof(strLr); offset += sizeof(strLr);
instr::BL bSvCtx(patchOffset - offset); instr::BL bSvCtx(patchOffset - offset);
offset += sizeof(bSvCtx); offset += sizeof(bSvCtx);
auto movPc = instr::MoveU64Reg(regs::X0, pc); auto movPc = instr::MoveU64Reg(regs::X0, baseAddress + (address - start));
offset += sizeof(u32) * movPc.size(); offset += sizeof(u32) * movPc.size();
instr::Movz movCmd(regs::W1, static_cast<u16>(instrSvc->value)); instr::Movz movCmd(regs::W1, static_cast<u16>(instrSvc->value));
offset += sizeof(movCmd); offset += sizeof(movCmd);
@ -250,13 +258,19 @@ namespace skyline {
offset += sizeof(bret); offset += sizeof(bret);
*address = bjunc.raw; *address = bjunc.raw;
if(strX0) patch.push_back(strX0); if (strX0)
patch.push_back(strX0);
patch.push_back(mrsX0); patch.push_back(mrsX0);
patch.push_back(ldrTls); patch.push_back(ldrTls);
if(movXn) patch.push_back(movXn); if (movXn)
if(ldrX0) patch.push_back(ldrX0); patch.push_back(movXn);
if (ldrX0)
patch.push_back(ldrX0);
patch.push_back(bret.raw); patch.push_back(bret.raw);
} if (instrMrs->srcReg == constant::CntpctEl0) { } else if (instrMrs->srcReg == constant::CntpctEl0) {
instr::Mrs mrs(constant::CntvctEl0, regs::X(instrMrs->destReg));
*address = mrs.raw;
/*
instr::B bjunc(offset); instr::B bjunc(offset);
if (instrMrs->destReg == 0) if (instrMrs->destReg == 0)
offset += cntpctEl0X0.size() * sizeof(u32); offset += cntpctEl0X0.size() * sizeof(u32);
@ -274,10 +288,13 @@ namespace skyline {
else if (instrMrs->destReg == 1) else if (instrMrs->destReg == 1)
for (auto &instr : cntpctEl0X1) for (auto &instr : cntpctEl0X1)
patch.push_back(instr); patch.push_back(instr);
else else {
cntpctEl0Xn[13] = instr::Fcvtzu(regs::X(instrMrs->destReg), 0).raw;
for (auto &instr : cntpctEl0Xn) for (auto &instr : cntpctEl0Xn)
patch.push_back(instr); patch.push_back(instr);
}
patch.push_back(bret.raw); patch.push_back(bret.raw);
*/
} else if (instrMrs->srcReg == constant::CntfrqEl0) { } else if (instrMrs->srcReg == constant::CntfrqEl0) {
instr::B bjunc(offset); instr::B bjunc(offset);
auto movFreq = instr::MoveU32Reg(static_cast<regs::X>(instrMrs->destReg), constant::TegraX1Freq); auto movFreq = instr::MoveU32Reg(static_cast<regs::X>(instrMrs->destReg), constant::TegraX1Freq);

View File

@ -26,6 +26,11 @@ namespace skyline {
public: public:
NCE(DeviceState &state); NCE(DeviceState &state);
/**
* @brief The destructor for NCE, this calls join() on all the threads
*/
~NCE();
/** /**
* @brief This function is the main event loop of the program * @brief This function is the main event loop of the program
*/ */

View File

@ -1,3 +1,6 @@
#include <asm/siginfo.h>
#include <signal.h>
#include <common.h>
#include "guest_common.h" #include "guest_common.h"
#define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage) #define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage)
@ -92,15 +95,37 @@ namespace skyline::guest {
asm("MRS %0, TPIDR_EL0":"=r"(ctx)); asm("MRS %0, TPIDR_EL0":"=r"(ctx));
ctx->pc = pc; ctx->pc = pc;
ctx->commandId = svc; ctx->commandId = svc;
while (true) {
ctx->state = ThreadState::WaitKernel; ctx->state = ThreadState::WaitKernel;
while (ctx->state == ThreadState::WaitKernel); while (ctx->state == ThreadState::WaitKernel);
if(ctx->state == ThreadState::WaitRun) { if (ctx->state == ThreadState::WaitRun)
return; return;
} else { else if (ctx->state == ThreadState::WaitFunc) {
ctx->state = ThreadState::WaitKernel; if (ctx->commandId == static_cast<u32>(ThreadCall::Syscall)) {
while(ctx->state == ThreadState::WaitKernel); saveCtxStack();
loadCtxTls();
asm("STR LR, [SP, #-16]!\n\t"
"MOV LR, SP\n\t"
"SVC #0\n\t"
"MOV SP, LR\n\t"
"LDR LR, [SP], #16");
saveCtxTls();
loadCtxStack();
} }
} }
}
}
void signalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) {
volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
for (u8 index = 0; index < constant::NumRegs; index++)
ctx->registers.regs[index] = ucontext->uc_mcontext.regs[index];
ctx->pc = ucontext->uc_mcontext.pc;
ctx->commandId = static_cast<u32>(signal);
ctx->state = ThreadState::GuestCrash;
exit(0);
}
void entry(u64 address) { void entry(u64 address) {
volatile ThreadContext *ctx; volatile ThreadContext *ctx;
@ -124,6 +149,12 @@ namespace skyline::guest {
} }
} }
} }
struct sigaction sigact{
.sa_sigaction = reinterpret_cast<void (*)(int, struct siginfo *, void *)>(reinterpret_cast<void *>(signalHandler)),
.sa_flags = SA_SIGINFO,
};
for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
sigaction(signal, &sigact, nullptr);
ctx->state = ThreadState::Running; ctx->state = ThreadState::Running;
asm("MOV LR, %0\n\t" asm("MOV LR, %0\n\t"
"MOV X0, %1\n\t" "MOV X0, %1\n\t"

View File

@ -127,6 +127,7 @@ namespace skyline {
WaitRun = 3, //!< The thread should be ready to run WaitRun = 3, //!< The thread should be ready to run
WaitInit = 4, //!< The thread is waiting to be initialized WaitInit = 4, //!< The thread is waiting to be initialized
WaitFunc = 5, //!< The kernel is waiting for the thread to run a function WaitFunc = 5, //!< The kernel is waiting for the thread to run a function
GuestCrash = 6, //!< This is a notification to the kernel that the guest has crashed
}; };
/** /**

View File

@ -396,5 +396,46 @@ namespace skyline {
}; };
}; };
static_assert(sizeof(Mov) == sizeof(u32)); static_assert(sizeof(Mov) == sizeof(u32));
/**
* @brief A bit-field struct that encapsulates a FCVTZU (Scalar, Integer) instruction. See https://developer.arm.com/docs/ddi0602/d/simd-and-floating-point-instructions-alphabetic-order/fcvtzu-scalar-integer-floating-point-convert-to-unsigned-integer-rounding-toward-zero-scalar.
*/
struct Fcvtzu {
public:
/**
* @brief Creates a FCVTZU (Scalar, Integer) instruction
* @param destReg The destination Xn register to store the value in
* @param srcReg The source Dn register to retrieve the value from
*/
Fcvtzu(regs::X destReg, u8 srcReg) {
this->destReg = static_cast<u8>(destReg);
this->srcReg = static_cast<u8>(srcReg);
sig0 = 0xE40;
ftype = 1;
sig1 = 0x1E;
sf = 1;
}
/**
* @brief Returns if the opcode is valid or not
* @return If the opcode represents a valid FCVTZU instruction
*/
inline bool Verify() {
return (sig0 == 0xE40 && sig1 == 0x1E);
}
union {
struct __attribute__((packed)) {
u8 destReg : 5;
u8 srcReg : 5;
u32 sig0 : 12;
u8 ftype : 2;
u8 sig1 : 7;
u8 sf : 1;
};
u32 raw{};
};
};
static_assert(sizeof(Fcvtzu) == sizeof(u32));
} }
} }

View File

@ -9,9 +9,9 @@ namespace skyline::service::am {
ICommonStateGetter::ICommonStateGetter(const DeviceState &state, ServiceManager &manager) : messageEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager, false, Service::am_ICommonStateGetter, { ICommonStateGetter::ICommonStateGetter(const DeviceState &state, ServiceManager &manager) : messageEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager, false, Service::am_ICommonStateGetter, {
{0x0, SFUNC(ICommonStateGetter::GetEventHandle)}, {0x0, SFUNC(ICommonStateGetter::GetEventHandle)},
{0x1, SFUNC(ICommonStateGetter::ReceiveMessage)}, {0x1, SFUNC(ICommonStateGetter::ReceiveMessage)},
{0x9, SFUNC(ICommonStateGetter::GetCurrentFocusState)},
{0x5, SFUNC(ICommonStateGetter::GetOperationMode)}, {0x5, SFUNC(ICommonStateGetter::GetOperationMode)},
{0x6, SFUNC(ICommonStateGetter::GetPerformanceMode)}, {0x6, SFUNC(ICommonStateGetter::GetPerformanceMode)},
{0x9, SFUNC(ICommonStateGetter::GetCurrentFocusState)},
{0x3C, SFUNC(ICommonStateGetter::GetDefaultDisplayResolution)} {0x3C, SFUNC(ICommonStateGetter::GetDefaultDisplayResolution)}
}) { }) {
operationMode = static_cast<OperationMode>(state.settings->GetBool("operation_mode")); operationMode = static_cast<OperationMode>(state.settings->GetBool("operation_mode"));

View File

@ -9,7 +9,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), memory::Type::SharedMemory);
auto handle = state.process->InsertItem<type::KSharedMemory>(hidSharedMemory); auto handle = state.process->InsertItem<type::KSharedMemory>(hidSharedMemory);
state.logger->Debug("HID Shared Memory Handle: {}", handle); state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
} }

View File

@ -20,11 +20,9 @@ namespace skyline::service::nvnflinger {
case TransactionCode::RequestBuffer: case TransactionCode::RequestBuffer:
state.gpu->bufferQueue.RequestBuffer(in, out); state.gpu->bufferQueue.RequestBuffer(in, out);
break; break;
case TransactionCode::DequeueBuffer: { case TransactionCode::DequeueBuffer:
if (state.gpu->bufferQueue.DequeueBuffer(in, out, request.outputBuf.at(0))) state.gpu->bufferQueue.DequeueBuffer(in, out);
return;
break; break;
}
case TransactionCode::QueueBuffer: case TransactionCode::QueueBuffer:
state.gpu->bufferQueue.QueueBuffer(in, out); state.gpu->bufferQueue.QueueBuffer(in, out);
break; break;

View File

@ -120,10 +120,12 @@ namespace skyline::service {
} }
handle_t ServiceManager::NewSession(const Service serviceType) { handle_t ServiceManager::NewSession(const Service serviceType) {
std::lock_guard serviceGuard(mutex);
return state.process->NewHandle<type::KSession>(GetService(serviceType)).handle; return state.process->NewHandle<type::KSession>(GetService(serviceType)).handle;
} }
std::shared_ptr<BaseService> ServiceManager::NewService(const std::string &serviceName, type::KSession &session, ipc::IpcResponse &response) { std::shared_ptr<BaseService> ServiceManager::NewService(const std::string &serviceName, type::KSession &session, ipc::IpcResponse &response) {
std::lock_guard serviceGuard(mutex);
auto serviceObject = GetService(ServiceString.at(serviceName)); auto serviceObject = GetService(ServiceString.at(serviceName));
handle_t handle{}; handle_t handle{};
if (response.isDomain) { if (response.isDomain) {
@ -139,6 +141,7 @@ namespace skyline::service {
} }
void ServiceManager::RegisterService(std::shared_ptr<BaseService> serviceObject, type::KSession &session, ipc::IpcResponse &response) { // NOLINT(performance-unnecessary-value-param) void ServiceManager::RegisterService(std::shared_ptr<BaseService> serviceObject, type::KSession &session, ipc::IpcResponse &response) { // NOLINT(performance-unnecessary-value-param)
std::lock_guard serviceGuard(mutex);
handle_t handle{}; handle_t handle{};
if (response.isDomain) { if (response.isDomain) {
session.domainTable[session.handleIndex] = serviceObject; session.domainTable[session.handleIndex] = serviceObject;
@ -152,6 +155,7 @@ namespace skyline::service {
} }
void ServiceManager::CloseSession(const handle_t handle) { void ServiceManager::CloseSession(const handle_t handle) {
std::lock_guard serviceGuard(mutex);
auto session = state.process->GetHandle<type::KSession>(handle); auto session = state.process->GetHandle<type::KSession>(handle);
if (session->serviceStatus == type::KSession::ServiceStatus::Open) { if (session->serviceStatus == type::KSession::ServiceStatus::Open) {
if (session->isDomain) { if (session->isDomain) {
@ -164,6 +168,7 @@ namespace skyline::service {
}; };
void ServiceManager::Loop() { void ServiceManager::Loop() {
std::lock_guard serviceGuard(mutex);
for (auto& [type, service] : serviceMap) for (auto& [type, service] : serviceMap)
if (service->hasLoop) if (service->hasLoop)
service->Loop(); service->Loop();

View File

@ -12,6 +12,7 @@ namespace skyline::service {
private: private:
const DeviceState &state; //!< The state of the device const DeviceState &state; //!< The state of the device
std::unordered_map<Service, std::shared_ptr<BaseService>> serviceMap; //!< A mapping from a Service to the underlying object std::unordered_map<Service, std::shared_ptr<BaseService>> serviceMap; //!< A mapping from a Service to the underlying object
skyline::Mutex mutex; //!< This mutex is used to ensure concurrent access to services doesn't cause crashes
/** /**
* @param serviceType The type of service requested * @param serviceType The type of service requested

View File

@ -20,23 +20,23 @@ import emu.skyline.adapter.GameAdapter
import emu.skyline.adapter.GameItem import emu.skyline.adapter.GameItem
import emu.skyline.loader.BaseLoader import emu.skyline.loader.BaseLoader
import emu.skyline.loader.NroLoader import emu.skyline.loader.NroLoader
import emu.skyline.loader.TitleEntry
import emu.skyline.utility.GameDialog import emu.skyline.utility.GameDialog
import emu.skyline.utility.RandomAccessDocument import emu.skyline.utility.RandomAccessDocument
import kotlinx.android.synthetic.main.main_activity.* import kotlinx.android.synthetic.main.main_activity.*
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.* import kotlin.concurrent.thread
class MainActivity : AppCompatActivity(), View.OnClickListener { class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private var adapter = GameAdapter(this) private var adapter = GameAdapter(this)
fun notifyUser(text: String) { fun notifyUser(text: String) {
Snackbar.make(findViewById(android.R.id.content), text, Snackbar.LENGTH_SHORT).show() Snackbar.make(findViewById(android.R.id.content), text, Snackbar.LENGTH_SHORT).show()
} }
private fun findFile(ext: String, loader: BaseLoader, directory: DocumentFile, entries: MutableList<TitleEntry>): MutableList<TitleEntry> { private fun findFile(ext: String, loader: BaseLoader, directory: DocumentFile, entries: Int = 0): Int {
var mEntries = entries var mEntries = entries
for (file in directory.listFiles()) { for (file in directory.listFiles()) {
if (file.isDirectory) { if (file.isDirectory) {
@ -46,8 +46,16 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
if (file.name != null) { if (file.name != null) {
if (ext.equals(file.name?.substring((file.name!!.lastIndexOf(".")) + 1), ignoreCase = true)) { if (ext.equals(file.name?.substring((file.name!!.lastIndexOf(".")) + 1), ignoreCase = true)) {
val document = RandomAccessDocument(this, file) val document = RandomAccessDocument(this, file)
if (loader.verifyFile(document)) if (loader.verifyFile(document)) {
mEntries.add(loader.getTitleEntry(document, file.uri)) val entry = loader.getTitleEntry(document, file.uri)
val header = (mEntries == 0)
runOnUiThread {
if(header)
adapter.addHeader(getString(R.string.nro))
adapter.addItem(GameItem(entry))
}
mEntries++
}
document.close() document.close()
} }
} }
@ -68,31 +76,34 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
Log.w("refreshFiles", "Ran into exception while loading: " + e.message) Log.w("refreshFiles", "Ran into exception while loading: " + e.message)
} }
} }
thread(start = true) {
try { try {
adapter.clear() runOnUiThread{adapter.clear()}
val entries: List<TitleEntry> = findFile("nro", NroLoader(this), DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!, ArrayList()) val entries = findFile("nro", NroLoader(this), DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!)
if (entries.isNotEmpty()) { runOnUiThread {
adapter.addHeader(getString(R.string.nro)) if (entries == 0)
for (entry in entries)
adapter.addItem(GameItem(entry))
} else {
adapter.addHeader(getString(R.string.no_rom)) adapter.addHeader(getString(R.string.no_rom))
}
try { try {
adapter.save(File(applicationInfo.dataDir + "/roms.bin")) adapter.save(File(applicationInfo.dataDir + "/roms.bin"))
} catch (e: IOException) { } catch (e: IOException) {
Log.w("refreshFiles", "Ran into exception while saving: " + e.message) Log.w("refreshFiles", "Ran into exception while saving: " + e.message)
} }
}
sharedPreferences.edit().putBoolean("refresh_required", false).apply() sharedPreferences.edit().putBoolean("refresh_required", false).apply()
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
runOnUiThread {
sharedPreferences.edit().remove("search_location").apply() sharedPreferences.edit().remove("search_location").apply()
val intent = intent val intent = intent
finish() finish()
startActivity(intent) startActivity(intent)
}
} catch (e: Exception) { } catch (e: Exception) {
runOnUiThread {
notifyUser(e.message!!) notifyUser(e.message!!)
} }
} }
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)