mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-04 22:05:10 +01:00
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:
parent
970dde8c27
commit
de6d8d8f48
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
534
app/src/main/cpp/skyline/kernel/svc.cpp___jb_old___
Normal file
534
app/src/main/cpp/skyline/kernel/svc.cpp___jb_old___
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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 ®ion : regionInfoVec)
|
for (const auto ®ion : 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 &) {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,45 +56,38 @@ 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);
|
|
||||||
u32 cmdId = ctx->commandId;
|
|
||||||
Registers registers = ctx->registers;
|
|
||||||
while(ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel);
|
|
||||||
ctx->registers = funcRegs;
|
|
||||||
ctx->commandId = static_cast<u32>(call);
|
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;
|
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) {
|
||||||
auto ctx = reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address);
|
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) {
|
void NCE::StartThread(u64 entryArg, u32 handle, std::shared_ptr<kernel::type::KThread> &thread) {
|
||||||
auto ctx = reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address);
|
auto ctx = reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address);
|
||||||
while(ctx->state != ThreadState::WaitInit);
|
while (ctx->state != ThreadState::WaitInit);
|
||||||
ctx->tpidrroEl0 = thread->tls;
|
ctx->tpidrroEl0 = thread->tls;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,10 +96,10 @@ namespace skyline {
|
|||||||
std::string trace;
|
std::string trace;
|
||||||
std::string regStr;
|
std::string regStr;
|
||||||
ctx = ctx ? ctx : state.ctx;
|
ctx = ctx ? ctx : state.ctx;
|
||||||
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);
|
||||||
@ -103,16 +111,17 @@ namespace skyline {
|
|||||||
offset += sizeof(u32);
|
offset += sizeof(u32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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) {
|
||||||
@ -179,30 +188,29 @@ namespace skyline {
|
|||||||
|
|
||||||
std::vector<u32> patch((guest::saveCtxSize + guest::loadCtxSize + guest::svcHandlerSize) / sizeof(u32));
|
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;
|
offset += guest::saveCtxSize;
|
||||||
|
|
||||||
std::memcpy(reinterpret_cast<u8*>(patch.data()) + guest::saveCtxSize,
|
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::saveCtxSize,
|
||||||
reinterpret_cast<void*>(&guest::loadCtx), guest::loadCtxSize);
|
reinterpret_cast<void *>(&guest::loadCtx), guest::loadCtxSize);
|
||||||
offset += guest::loadCtxSize;
|
offset += guest::loadCtxSize;
|
||||||
|
|
||||||
std::memcpy(reinterpret_cast<u8*>(patch.data()) + guest::saveCtxSize + guest::loadCtxSize,
|
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::saveCtxSize + guest::loadCtxSize,
|
||||||
reinterpret_cast<void*>(&guest::svcHandler), guest::svcHandlerSize);
|
reinterpret_cast<void *>(&guest::svcHandler), guest::svcHandlerSize);
|
||||||
offset += 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 instrSvc = reinterpret_cast<instr::Svc *>(address);
|
||||||
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);
|
||||||
@ -219,7 +227,7 @@ namespace skyline {
|
|||||||
*address = bjunc.raw;
|
*address = bjunc.raw;
|
||||||
patch.push_back(strLr);
|
patch.push_back(strLr);
|
||||||
patch.push_back(bSvCtx.raw);
|
patch.push_back(bSvCtx.raw);
|
||||||
for(auto& instr : movPc)
|
for (auto &instr : movPc)
|
||||||
patch.push_back(instr);
|
patch.push_back(instr);
|
||||||
patch.push_back(movCmd.raw);
|
patch.push_back(movCmd.raw);
|
||||||
patch.push_back(bSvcHandler.raw);
|
patch.push_back(bSvcHandler.raw);
|
||||||
@ -230,7 +238,7 @@ namespace skyline {
|
|||||||
if (instrMrs->srcReg == constant::TpidrroEl0) {
|
if (instrMrs->srcReg == constant::TpidrroEl0) {
|
||||||
instr::B bjunc(offset);
|
instr::B bjunc(offset);
|
||||||
u32 strX0{};
|
u32 strX0{};
|
||||||
if(instrMrs->destReg != regs::X0) {
|
if (instrMrs->destReg != regs::X0) {
|
||||||
strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]!
|
strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]!
|
||||||
offset += sizeof(strX0);
|
offset += sizeof(strX0);
|
||||||
}
|
}
|
||||||
@ -240,7 +248,7 @@ namespace skyline {
|
|||||||
offset += sizeof(ldrTls);
|
offset += sizeof(ldrTls);
|
||||||
u32 movXn{};
|
u32 movXn{};
|
||||||
u32 ldrX0{};
|
u32 ldrX0{};
|
||||||
if(instrMrs->destReg != regs::X0) {
|
if (instrMrs->destReg != regs::X0) {
|
||||||
movXn = instr::Mov(regs::X(instrMrs->destReg), regs::X0).raw;
|
movXn = instr::Mov(regs::X(instrMrs->destReg), regs::X0).raw;
|
||||||
offset += sizeof(movXn);
|
offset += sizeof(movXn);
|
||||||
ldrX0 = 0xF84107E0; // LDR X0, [SP], #16
|
ldrX0 = 0xF84107E0; // LDR X0, [SP], #16
|
||||||
@ -250,15 +258,21 @@ 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);
|
||||||
else if (instrMrs->destReg == 1)
|
else if (instrMrs->destReg == 1)
|
||||||
offset += cntpctEl0X1.size() * sizeof(u32);
|
offset += cntpctEl0X1.size() * sizeof(u32);
|
||||||
@ -268,16 +282,19 @@ namespace skyline {
|
|||||||
offset += sizeof(bret);
|
offset += sizeof(bret);
|
||||||
|
|
||||||
*address = bjunc.raw;
|
*address = bjunc.raw;
|
||||||
if(instrMrs->destReg == 0)
|
if (instrMrs->destReg == 0)
|
||||||
for(auto& instr : cntpctEl0X0)
|
for (auto &instr : cntpctEl0X0)
|
||||||
patch.push_back(instr);
|
patch.push_back(instr);
|
||||||
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 {
|
||||||
for(auto& instr : cntpctEl0Xn)
|
cntpctEl0Xn[13] = instr::Fcvtzu(regs::X(instrMrs->destReg), 0).raw;
|
||||||
|
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);
|
||||||
@ -286,7 +303,7 @@ namespace skyline {
|
|||||||
offset += sizeof(bret);
|
offset += sizeof(bret);
|
||||||
|
|
||||||
*address = bjunc.raw;
|
*address = bjunc.raw;
|
||||||
for(auto& instr : movFreq)
|
for (auto &instr : movFreq)
|
||||||
patch.push_back(instr);
|
patch.push_back(instr);
|
||||||
patch.push_back(bret.raw);
|
patch.push_back(bret.raw);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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)
|
||||||
@ -88,30 +91,17 @@ namespace skyline::guest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void svcHandler(u64 pc, u32 svc) {
|
void svcHandler(u64 pc, u32 svc) {
|
||||||
volatile ThreadContext* ctx;
|
volatile ThreadContext *ctx;
|
||||||
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) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
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);
|
|
||||||
if (ctx->state == ThreadState::WaitRun)
|
if (ctx->state == ThreadState::WaitRun)
|
||||||
break;
|
return;
|
||||||
else if(ctx->state == ThreadState::WaitFunc) {
|
else if (ctx->state == ThreadState::WaitFunc) {
|
||||||
if(ctx->commandId == static_cast<u32>(ThreadCall::Syscall)) {
|
if (ctx->commandId == static_cast<u32>(ThreadCall::Syscall)) {
|
||||||
saveCtxStack();
|
saveCtxStack();
|
||||||
loadCtxTls();
|
loadCtxTls();
|
||||||
asm("STR LR, [SP, #-16]!\n\t"
|
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;
|
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"
|
||||||
@ -156,7 +187,7 @@ namespace skyline::guest {
|
|||||||
"MOV X27, XZR\n\t"
|
"MOV X27, XZR\n\t"
|
||||||
"MOV X28, XZR\n\t"
|
"MOV X28, XZR\n\t"
|
||||||
"MOV X29, 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();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"));
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user