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 <fstream>
#include <syslog.h>
#include <mutex>
#import <thread>
#include <string>
#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<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 CheckInterval = 10000000; //!< The amount of cycles to wait between checking if the guest thread is dead
// IPC
constexpr size_t TlsIpcSize = 0x100; //!< The size of the IPC command buffer in a TLS slot
constexpr u8 PortSize = 0x8; //!< The size of a port name string

View File

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

View File

@ -49,25 +49,22 @@ namespace skyline::gpu {
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);
i64 slot{-1};
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) {
slot = buffer.first;
buffer.second->status = BufferStatus::Dequeued;
while(slot == -1) {
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) {
slot = buffer.first;
buffer.second->status = BufferStatus::Dequeued;
break;
}
}
}
if (slot == -1) {
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;
sched_yield();
}
DequeueOut output(static_cast<u32>(slot));
out.WriteData(output);
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) {
@ -85,6 +82,7 @@ namespace skyline::gpu {
} *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength);
auto buffer = queue.at(data->slot);
buffer->status = BufferStatus::Queued;
buffer->UpdateBuffer();
displayQueue.emplace(buffer);
state.gpu->bufferEvent->Signal();
struct {

View File

@ -106,6 +106,7 @@ namespace skyline::gpu {
GbpBuffer gbpBuffer; //!< The information about the underlying buffer
BufferStatus status{BufferStatus::Free}; //!< The status of this buffer
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
/**
@ -187,10 +188,8 @@ namespace skyline::gpu {
/**
* @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

View File

@ -73,7 +73,7 @@ namespace skyline::kernel::svc {
void QueryMemory(DeviceState &state) {
memory::MemoryInfo memInfo{};
u64 addr = state.ctx->registers.x2;
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)) {
@ -111,7 +111,7 @@ namespace skyline::kernel::svc {
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.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.type = static_cast<u32>(type);
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.isIpcLocked = (info.ipcRefCount > 0);
info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0);
info.r = permission.r;
@ -78,11 +79,13 @@ namespace skyline::kernel::type {
KPrivateMemory::~KPrivateMemory() {
try {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x8 = __NR_munmap;
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread);
if(state.process) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x8 = __NR_munmap;
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
}
} catch (const std::exception &) {}
}
};

View File

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

View File

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

View File

@ -17,7 +17,7 @@ namespace skyline::loader {
* @param size The amount to read in bytes
*/
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);
}

View File

@ -1,5 +1,4 @@
#include <sched.h>
#include <mutex>
#include "os.h"
#include "jvm.h"
#include "nce/guest.h"
@ -11,27 +10,43 @@ extern skyline::Mutex jniMtx;
namespace skyline {
void NCE::KernelThread(pid_t thread) {
state.thread = state.process->threadMap.at(thread);
state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->guest.address);
while (!Halt) {
if (state.ctx->state == ThreadState::WaitKernel) {
const u16 svc = static_cast<const u16>(state.ctx->commandId);
try {
if (kernel::svc::SvcTable[svc]) {
state.logger->Debug("SVC called 0x{:X}", svc);
(*kernel::svc::SvcTable[svc])(state);
} else
throw exception("Unimplemented SVC 0x{:X}", svc);
} catch (const exception &e) {
throw exception("{} (SVC: 0x{:X})", e.what(), svc);
try {
state.thread = state.process->threadMap.at(thread);
state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->guest.address);
while (!Halt) {
if (state.ctx->state == ThreadState::WaitKernel) {
const u16 svc = static_cast<const u16>(state.ctx->commandId);
try {
if (kernel::svc::SvcTable[svc]) {
state.logger->Debug("SVC called 0x{:X}", svc);
(*kernel::svc::SvcTable[svc])(state);
} else
throw exception("Unimplemented SVC 0x{:X}", svc);
} catch (const std::exception &e) {
throw exception("{} (SVC: 0x{:X})", e.what(), svc);
}
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;
}
state.ctx->state = ThreadState::WaitRun;
}
} 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() {
for (auto &thread : threadMap)
thread.second->join();
}
void NCE::Execute() {
while (!Halt && state.os->process) {
std::lock_guard jniGd(jniMtx);
@ -41,45 +56,38 @@ namespace skyline {
Halt = false;
}
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr<kernel::type::KThread>& thread) {
auto ctx = reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address);
u32 cmdId = ctx->commandId;
Registers registers = ctx->registers;
while(ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel);
ctx->registers = funcRegs;
void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) {
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);
ctx->commandId = cmdId;
while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel);
funcRegs = ctx->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) {
auto ctx = 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;
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(state.process->threadMap.at(pid)->ctxMemory->kernel.address));
}
void NCE::WaitThreadInit(std::shared_ptr<kernel::type::KThread>& thread) {
void NCE::WaitThreadInit(std::shared_ptr<kernel::type::KThread> &thread) {
auto ctx = reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address);
while(ctx->state == ThreadState::NotReady);
while (ctx->state == ThreadState::NotReady);
}
void NCE::StartThread(u64 entryArg, u32 handle, std::shared_ptr<kernel::type::KThread> &thread) {
auto ctx = reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address);
while(ctx->state != ThreadState::WaitInit);
while (ctx->state != ThreadState::WaitInit);
ctx->tpidrroEl0 = thread->tls;
ctx->registers.x0 = entryArg;
ctx->registers.x1 = handle;
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);
}
@ -88,10 +96,10 @@ namespace skyline {
std::string trace;
std::string regStr;
ctx = ctx ? ctx : state.ctx;
if(numHist) {
if (numHist) {
std::vector<u32> instrs(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);
for (auto &instr : instrs) {
instr = __builtin_bswap32(instr);
@ -103,16 +111,17 @@ namespace skyline {
offset += sizeof(u32);
}
}
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]);
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]);
}
if(numHist)
state.logger->Debug("Process Trace:{}\nRaw Instructions: 0x{}\nCPU Context:{}", trace, raw, regStr);
else
if (numHist) {
state.logger->Debug("Process Trace:{}", trace);
state.logger->Debug("Raw Instructions: 0x{}\nCPU Context:{}", raw, regStr);
} else
state.logger->Warn("CPU Context:{}", regStr);
}
const std::array<u32, 18> cntpctEl0X0 = {
const std::array<u32, 17> cntpctEl0X0 = {
0xA9BF0BE1, // STP X1, X2, [SP, #-16]!
0x3C9F0FE0, // STR Q0, [SP, #-16]!
0x3C9F0FE1, // STR Q1, [SP, #-16]!
@ -129,10 +138,10 @@ namespace skyline {
0x9E790000, // FCVTZU X0, D0
0x3CC107E2, // LDR Q2, [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]!
0x3C9F0FE0, // STR Q0, [SP, #-16]!
0x3C9F0FE1, // STR Q1, [SP, #-16]!
@ -149,10 +158,10 @@ namespace skyline {
0x9E790001, // FCVTZU X0, D0
0x3CC107E2, // LDR Q2, [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]!
0x3C9F0FE0, // STR Q0, [SP, #-16]!
0x3C9F0FE1, // STR Q1, [SP, #-16]!
@ -166,10 +175,10 @@ namespace skyline {
0x9E630021, // UCVTF D1, X1
0x1E621800, // FDIV D0, D0, D2
0x1E610800, // FMUL D0, D0, D1
0x9E790000, // FCVTZU Xn, D0
0x00000000, // FCVTZU Xn, D0 (Set at runtime)
0x3CC107E2, // LDR Q2, [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) {
@ -179,30 +188,29 @@ namespace skyline {
std::vector<u32> patch((guest::saveCtxSize + guest::loadCtxSize + guest::svcHandlerSize) / sizeof(u32));
std::memcpy(patch.data(), reinterpret_cast<void*>(&guest::saveCtx), guest::saveCtxSize);
std::memcpy(patch.data(), reinterpret_cast<void *>(&guest::saveCtx), guest::saveCtxSize);
offset += guest::saveCtxSize;
std::memcpy(reinterpret_cast<u8*>(patch.data()) + guest::saveCtxSize,
reinterpret_cast<void*>(&guest::loadCtx), guest::loadCtxSize);
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::saveCtxSize,
reinterpret_cast<void *>(&guest::loadCtx), guest::loadCtxSize);
offset += guest::loadCtxSize;
std::memcpy(reinterpret_cast<u8*>(patch.data()) + guest::saveCtxSize + guest::loadCtxSize,
reinterpret_cast<void*>(&guest::svcHandler), guest::svcHandlerSize);
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::saveCtxSize + guest::loadCtxSize,
reinterpret_cast<void *>(&guest::svcHandler), guest::svcHandlerSize);
offset += guest::svcHandlerSize;
for (u32* address = start;address < end;address++) {
for (u32 *address = start; address < end; address++) {
auto instrSvc = reinterpret_cast<instr::Svc *>(address);
auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
if (instrSvc->Verify()) {
u64 pc = baseAddress + (address - start);
instr::B bjunc(offset);
constexpr u32 strLr = 0xF81F0FFE; // STR LR, [SP, #-16]!
offset += sizeof(strLr);
instr::BL bSvCtx(patchOffset - offset);
offset += sizeof(bSvCtx);
auto movPc = instr::MoveU64Reg(regs::X0, pc);
auto movPc = instr::MoveU64Reg(regs::X0, baseAddress + (address - start));
offset += sizeof(u32) * movPc.size();
instr::Movz movCmd(regs::W1, static_cast<u16>(instrSvc->value));
offset += sizeof(movCmd);
@ -219,18 +227,18 @@ namespace skyline {
*address = bjunc.raw;
patch.push_back(strLr);
patch.push_back(bSvCtx.raw);
for(auto& instr : movPc)
for (auto &instr : movPc)
patch.push_back(instr);
patch.push_back(movCmd.raw);
patch.push_back(bSvcHandler.raw);
patch.push_back(bLdCtx.raw);
patch.push_back(ldrLr);
patch.push_back(bret.raw);
} else if (instrMrs->Verify()) {
} else if (instrMrs->Verify()) {
if (instrMrs->srcReg == constant::TpidrroEl0) {
instr::B bjunc(offset);
u32 strX0{};
if(instrMrs->destReg != regs::X0) {
if (instrMrs->destReg != regs::X0) {
strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]!
offset += sizeof(strX0);
}
@ -240,7 +248,7 @@ namespace skyline {
offset += sizeof(ldrTls);
u32 movXn{};
u32 ldrX0{};
if(instrMrs->destReg != regs::X0) {
if (instrMrs->destReg != regs::X0) {
movXn = instr::Mov(regs::X(instrMrs->destReg), regs::X0).raw;
offset += sizeof(movXn);
ldrX0 = 0xF84107E0; // LDR X0, [SP], #16
@ -250,15 +258,21 @@ namespace skyline {
offset += sizeof(bret);
*address = bjunc.raw;
if(strX0) patch.push_back(strX0);
if (strX0)
patch.push_back(strX0);
patch.push_back(mrsX0);
patch.push_back(ldrTls);
if(movXn) patch.push_back(movXn);
if(ldrX0) patch.push_back(ldrX0);
if (movXn)
patch.push_back(movXn);
if (ldrX0)
patch.push_back(ldrX0);
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);
if(instrMrs->destReg == 0)
if (instrMrs->destReg == 0)
offset += cntpctEl0X0.size() * sizeof(u32);
else if (instrMrs->destReg == 1)
offset += cntpctEl0X1.size() * sizeof(u32);
@ -268,17 +282,20 @@ namespace skyline {
offset += sizeof(bret);
*address = bjunc.raw;
if(instrMrs->destReg == 0)
for(auto& instr : cntpctEl0X0)
if (instrMrs->destReg == 0)
for (auto &instr : cntpctEl0X0)
patch.push_back(instr);
else if (instrMrs->destReg == 1)
for(auto& instr : cntpctEl0X1)
for (auto &instr : cntpctEl0X1)
patch.push_back(instr);
else
for(auto& instr : cntpctEl0Xn)
else {
cntpctEl0Xn[13] = instr::Fcvtzu(regs::X(instrMrs->destReg), 0).raw;
for (auto &instr : cntpctEl0Xn)
patch.push_back(instr);
}
patch.push_back(bret.raw);
} else if (instrMrs->srcReg == constant::CntfrqEl0) {
*/
} else if (instrMrs->srcReg == constant::CntfrqEl0) {
instr::B bjunc(offset);
auto movFreq = instr::MoveU32Reg(static_cast<regs::X>(instrMrs->destReg), constant::TegraX1Freq);
offset += sizeof(u32) * movFreq.size();
@ -286,7 +303,7 @@ namespace skyline {
offset += sizeof(bret);
*address = bjunc.raw;
for(auto& instr : movFreq)
for (auto &instr : movFreq)
patch.push_back(instr);
patch.push_back(bret.raw);
}

View File

@ -26,6 +26,11 @@ namespace skyline {
public:
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
*/

View File

@ -1,3 +1,6 @@
#include <asm/siginfo.h>
#include <signal.h>
#include <common.h>
#include "guest_common.h"
#define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage)
@ -88,30 +91,17 @@ namespace skyline::guest {
}
void svcHandler(u64 pc, u32 svc) {
volatile ThreadContext* ctx;
volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
ctx->pc = pc;
ctx->commandId = svc;
ctx->state = ThreadState::WaitKernel;
while(ctx->state == ThreadState::WaitKernel);
if(ctx->state == ThreadState::WaitRun) {
return;
} else {
while (true) {
ctx->state = ThreadState::WaitKernel;
while(ctx->state == ThreadState::WaitKernel);
}
}
void entry(u64 address) {
volatile ThreadContext* ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
while(true) {
ctx->state = ThreadState::WaitInit;
while (ctx->state == ThreadState::WaitInit);
while (ctx->state == ThreadState::WaitKernel);
if (ctx->state == ThreadState::WaitRun)
break;
else if(ctx->state == ThreadState::WaitFunc) {
if(ctx->commandId == static_cast<u32>(ThreadCall::Syscall)) {
return;
else if (ctx->state == ThreadState::WaitFunc) {
if (ctx->commandId == static_cast<u32>(ThreadCall::Syscall)) {
saveCtxStack();
loadCtxTls();
asm("STR LR, [SP, #-16]!\n\t"
@ -124,6 +114,47 @@ namespace skyline::guest {
}
}
}
}
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) {
volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
while (true) {
ctx->state = ThreadState::WaitInit;
while (ctx->state == ThreadState::WaitInit);
if (ctx->state == ThreadState::WaitRun)
break;
else if (ctx->state == ThreadState::WaitFunc) {
if (ctx->commandId == static_cast<u32>(ThreadCall::Syscall)) {
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();
}
}
}
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;
asm("MOV LR, %0\n\t"
"MOV X0, %1\n\t"
@ -156,7 +187,7 @@ namespace skyline::guest {
"MOV X27, XZR\n\t"
"MOV X28, XZR\n\t"
"MOV X29, XZR\n\t"
"RET" :: "r"(address), "r"(ctx->registers.x0), "r"(ctx->registers.x1) : "x0","x1","lr");
"RET"::"r"(address), "r"(ctx->registers.x0), "r"(ctx->registers.x1) : "x0", "x1", "lr");
__builtin_unreachable();
}
}

View File

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

View File

@ -396,5 +396,46 @@ namespace skyline {
};
};
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, {
{0x0, SFUNC(ICommonStateGetter::GetEventHandle)},
{0x1, SFUNC(ICommonStateGetter::ReceiveMessage)},
{0x9, SFUNC(ICommonStateGetter::GetCurrentFocusState)},
{0x5, SFUNC(ICommonStateGetter::GetOperationMode)},
{0x6, SFUNC(ICommonStateGetter::GetPerformanceMode)},
{0x9, SFUNC(ICommonStateGetter::GetCurrentFocusState)},
{0x3C, SFUNC(ICommonStateGetter::GetDefaultDisplayResolution)}
}) {
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) {
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);
state.logger->Debug("HID Shared Memory Handle: {}", handle);
state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle);
}

View File

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

View File

@ -120,10 +120,12 @@ namespace skyline::service {
}
handle_t ServiceManager::NewSession(const Service serviceType) {
std::lock_guard serviceGuard(mutex);
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::lock_guard serviceGuard(mutex);
auto serviceObject = GetService(ServiceString.at(serviceName));
handle_t handle{};
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)
std::lock_guard serviceGuard(mutex);
handle_t handle{};
if (response.isDomain) {
session.domainTable[session.handleIndex] = serviceObject;
@ -152,6 +155,7 @@ namespace skyline::service {
}
void ServiceManager::CloseSession(const handle_t handle) {
std::lock_guard serviceGuard(mutex);
auto session = state.process->GetHandle<type::KSession>(handle);
if (session->serviceStatus == type::KSession::ServiceStatus::Open) {
if (session->isDomain) {
@ -164,6 +168,7 @@ namespace skyline::service {
};
void ServiceManager::Loop() {
std::lock_guard serviceGuard(mutex);
for (auto& [type, service] : serviceMap)
if (service->hasLoop)
service->Loop();

View File

@ -12,6 +12,7 @@ namespace skyline::service {
private:
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
skyline::Mutex mutex; //!< This mutex is used to ensure concurrent access to services doesn't cause crashes
/**
* @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.loader.BaseLoader
import emu.skyline.loader.NroLoader
import emu.skyline.loader.TitleEntry
import emu.skyline.utility.GameDialog
import emu.skyline.utility.RandomAccessDocument
import kotlinx.android.synthetic.main.main_activity.*
import java.io.File
import java.io.IOException
import java.util.*
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var sharedPreferences: SharedPreferences
private var adapter = GameAdapter(this)
fun notifyUser(text: String) {
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
for (file in directory.listFiles()) {
if (file.isDirectory) {
@ -46,8 +46,16 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
if (file.name != null) {
if (ext.equals(file.name?.substring((file.name!!.lastIndexOf(".")) + 1), ignoreCase = true)) {
val document = RandomAccessDocument(this, file)
if (loader.verifyFile(document))
mEntries.add(loader.getTitleEntry(document, file.uri))
if (loader.verifyFile(document)) {
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()
}
}
@ -68,29 +76,32 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
Log.w("refreshFiles", "Ran into exception while loading: " + e.message)
}
}
try {
adapter.clear()
val entries: List<TitleEntry> = findFile("nro", NroLoader(this), DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!, ArrayList())
if (entries.isNotEmpty()) {
adapter.addHeader(getString(R.string.nro))
for (entry in entries)
adapter.addItem(GameItem(entry))
} else {
adapter.addHeader(getString(R.string.no_rom))
}
thread(start = true) {
try {
adapter.save(File(applicationInfo.dataDir + "/roms.bin"))
} catch (e: IOException) {
Log.w("refreshFiles", "Ran into exception while saving: " + e.message)
runOnUiThread{adapter.clear()}
val entries = findFile("nro", NroLoader(this), DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!)
runOnUiThread {
if (entries == 0)
adapter.addHeader(getString(R.string.no_rom))
try {
adapter.save(File(applicationInfo.dataDir + "/roms.bin"))
} catch (e: IOException) {
Log.w("refreshFiles", "Ran into exception while saving: " + e.message)
}
}
sharedPreferences.edit().putBoolean("refresh_required", false).apply()
} catch (e: IllegalArgumentException) {
runOnUiThread {
sharedPreferences.edit().remove("search_location").apply()
val intent = intent
finish()
startActivity(intent)
}
} catch (e: Exception) {
runOnUiThread {
notifyUser(e.message!!)
}
}
sharedPreferences.edit().putBoolean("refresh_required", false).apply()
} catch (e: IllegalArgumentException) {
sharedPreferences.edit().remove("search_location").apply()
val intent = intent
finish()
startActivity(intent)
} catch (e: Exception) {
notifyUser(e.message!!)
}
}