Add support for threads and mutexes

This commit adds support for threading and mutexes. However, there is also a basis of conditional variables but these don't work due to the lack of a shared memory model between the guest and host. So, conditional variables will be deferred to after the shared memory model is in place.
This commit is contained in:
◱ PixelyIon 2020-02-01 21:21:32 +05:30 committed by ◱ PixelyIon
parent 2aebf04b4b
commit d02267c34f
19 changed files with 429 additions and 208 deletions

View File

@ -1,25 +1,37 @@
#include "common.h"
#include "nce.h"
#include "gpu.h"
#include <kernel/types/KThread.h>
#include <tinyxml2.h>
namespace skyline {
void Mutex::lock() {
while (flag.test_and_set(std::memory_order_acquire));
while (true) {
for (int i = 0; i < 1000; ++i) {
if (!flag.test_and_set(std::memory_order_acquire))
return;
asm volatile("yield");
}
void Mutex::unlock() {
flag.clear(std::memory_order_release);
sched_yield();
}
bool Mutex::try_lock() {
return !flag.test_and_set(std::memory_order_acquire);
}
void GroupMutex::lock(Group group) {
auto none = Group::None;
while (!flag.compare_exchange_weak(none, group) && flag != group);
if (flag == group) {
num++;
return;
}
while (true) {
for (int i = 0; i < 1000; ++i) {
if (flag.compare_exchange_weak(none, group)) {
num++;
return;
}
asm volatile("yield");
}
sched_yield();
}
}
void GroupMutex::unlock() {
@ -38,13 +50,16 @@ namespace skyline {
stringMap[elem->FindAttribute("name")->Value()] = elem->GetText();
break;
case 'b':
boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->BoolValue();
boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute(
"value")->BoolValue();
break;
case 'i':
intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->IntValue();
intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute(
"value")->IntValue();
break;
default:
syslog(LOG_ALERT, "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
syslog(LOG_ALERT, "Settings type is missing: %s for %s", elem->Value(),
elem->FindAttribute("name")->Value());
break;
};
if (elem->NextSibling())

View File

@ -6,7 +6,7 @@
#include <fstream>
#include <syslog.h>
#include <mutex>
#import <thread>
#include <thread>
#include <string>
#include <sstream>
#include <memory>
@ -24,7 +24,6 @@ namespace skyline {
namespace constant {
// Memory
constexpr u64 BaseAddress = 0x8000000; //!< The address space base
constexpr u64 BaseEnd = 0x7FFFFFFFFF; //!< The end of the address space
constexpr u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM
constexpr size_t DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB
constexpr size_t HeapSizeDiv = 0x200000; //!< The amount heap size has to be divisible by
@ -79,6 +78,7 @@ namespace skyline {
constexpr u32 InvHandle = 0xE401; //!< "Invalid handle"
constexpr u32 InvCombination = 0xE801; //!< "Invalid combination"
constexpr u32 Timeout = 0xEA01; //!< "Timeout"
constexpr u32 Interrupted = 0xEC01; //!< "Interrupted"
constexpr u32 MaxHandles = 0xEE01; //!< "Too many handles"
constexpr u32 NotFound = 0xF201; //!< "Not found"
constexpr u32 Unimpl = 0x177202; //!< "Unimplemented behaviour"
@ -135,9 +135,21 @@ namespace skyline {
return value & ~multiple;
}
/**
* @param address The address to check for alignment
* @return If the address is page aligned
*/
inline bool PageAligned(u64 address) {
return !(address & (PAGE_SIZE - 1U));
}
/**
* @param address The address to check for alignment
* @return If the address is word aligned
*/
inline bool WordAligned(u64 address) {
return !(address & 3U);
}
}
/**
@ -156,12 +168,16 @@ namespace skyline {
* @brief Try to lock the mutex if it is unlocked else return
* @return If the mutex was successfully locked or not
*/
bool try_lock();
inline bool try_lock() {
return !flag.test_and_set(std::memory_order_acquire);
}
/**
* @brief Unlock the mutex if it is held by this thread
*/
void unlock();
inline void unlock() {
flag.clear(std::memory_order_release);
}
};
/**

View File

@ -121,7 +121,6 @@ namespace skyline::kernel::ipc {
return static_cast<u16>(counter0_5) | static_cast<u16>(counter9_11) << 9;
}
};
static_assert(sizeof(BufferDescriptorX) == 8);
/**
@ -154,7 +153,6 @@ namespace skyline::kernel::ipc {
return static_cast<u64>(size0_31) | static_cast<u64>(size32_35) << 32;
}
};
static_assert(sizeof(BufferDescriptorABW) == 12);
/**

View File

@ -91,7 +91,9 @@ namespace skyline::kernel {
case memory::AddressSpaceType::AddressSpace32Bit:
throw exception("32-bit address spaces are not supported");
case memory::AddressSpaceType::AddressSpace36Bit: {
code.address = 0x8000000;
base.address = constant::BaseAddress;
base.size = 0xFF8000000;
code.address = base.address;
code.size = 0x78000000;
if(code.address > address || (code.size - (address - code.address)) < size)
throw exception("Code mapping larger than 36-bit code region");
@ -101,11 +103,13 @@ namespace skyline::kernel {
stack.size = alias.size;
heap.address = alias.address + alias.size;
heap.size = 0x180000000;
tlsIo.address = heap.address + heap.size;
tlsIo.size = 0x1000000000;
tlsIo.address = code.address;
tlsIo.size = 0;
break;
}
case memory::AddressSpaceType::AddressSpace39Bit: {
base.address = constant::BaseAddress;
base.size = 0x7FF8000000;
code.address = utils::AlignDown(address, 0x200000);
code.size = utils::AlignUp(address + size, 0x200000) - code.address;
alias.address = code.address + code.size;
@ -136,6 +140,8 @@ namespace skyline::kernel {
memory::Region MemoryManager::GetRegion(memory::Regions region) {
switch(region) {
case memory::Regions::Base:
return base;
case memory::Regions::Code:
return code;
case memory::Regions::Alias:

View File

@ -72,27 +72,19 @@ namespace skyline {
u32 value;
};
static_assert(sizeof(MemoryAttribute) == sizeof(u32));
/**
* @brief This contains information about a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryInfo
*/
struct MemoryInfo {
u64 address;
u64 size;
u32 type;
MemoryAttribute attributes;
union {
u64 address; //!< The base address of the mapping
u64 size; //!< The size of the mapping
u32 type; //!< The MemoryType of the mapping
u32 attributes; //!< The attributes of the mapping
u32 permissions; //!< The permissions of the mapping
u32 ipcRefCount; //!< The IPC reference count (This is always 0)
u32 deviceRefCount; //!< The device reference count (This is always 0)
u32 _pad0_;
struct {
bool r : 1, w : 1, x : 1;
};
};
u32 ipcRefCount;
u32 deviceRefCount;
u32 : 32;
};
static_assert(sizeof(MemoryInfo) == 0x28);
/**
@ -153,7 +145,6 @@ namespace skyline {
};
u32 value;
};
static_assert(sizeof(MemoryState) == sizeof(u32));
/**
@ -187,6 +178,7 @@ namespace skyline {
* @brief This enumerates all of the memory regions in the process address space
*/
enum class Regions {
Base, //!< The region representing the entire address space
Code, //!< The code region contains all of the loaded in code
Alias, //!< The alias region is reserved for allocating thread stack before 2.0.0
Heap, //!< The heap region is reserved for heap allocations
@ -272,6 +264,7 @@ namespace skyline {
private:
const DeviceState &state; //!< The state of the device
std::forward_list<ChunkDescriptor> chunkList; //!< This linked list holds all the chunk descriptors
memory::Region base{memory::Regions::Base}; //!< The Region object for the entire address space
memory::Region code{memory::Regions::Code}; //!< The Region object for the code memory region
memory::Region alias{memory::Regions::Alias}; //!< The Region object for the alias memory region
memory::Region heap{memory::Regions::Heap}; //!< The Region object for the heap memory region

View File

@ -73,7 +73,7 @@ namespace skyline::kernel::svc {
auto stack = state.os->memory.GetRegion(memory::Regions::Stack);
if(!stack.IsInside(destination)) {
state.ctx->registers.w0 = constant::status::InvMemRange;
state.logger->Warn("svcMapMemory: Addresses not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
state.logger->Warn("svcMapMemory: Destination not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
auto descriptor = state.os->memory.Get(source);
@ -87,16 +87,61 @@ namespace skyline::kernel::svc {
state.logger->Warn("svcMapMemory: Source doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, descriptor->chunk.state.value);
return;
}
state.process->NewHandle<type::KPrivateMemory>(destination, size, memory::Permission{true, true, true}, memory::MemoryStates::Stack);
state.process->NewHandle<type::KPrivateMemory>(destination, size, descriptor->block.permission, memory::MemoryStates::Stack);
state.process->CopyMemory(source, destination, size);
auto object = state.process->GetMemoryObject(source);
if(!object)
throw exception("svcMapMemory: Cannot find memory object in handle table for address 0x{:X}", source);
object->UpdatePermission(source, size, {false, false, false});
object->item->UpdatePermission(source, size, {false, false, false});
state.logger->Debug("svcMapMemory: Mapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size);
state.ctx->registers.w0 = constant::status::Success;
}
void UnmapMemory(DeviceState &state) {
const u64 source = state.ctx->registers.x0;
const u64 destination = state.ctx->registers.x1;
const u64 size = state.ctx->registers.x2;
if(!utils::PageAligned(destination) || !utils::PageAligned(source)) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcUnmapMemory: Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
if(!utils::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcUnmapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return;
}
auto stack = state.os->memory.GetRegion(memory::Regions::Stack);
if(!stack.IsInside(source)) {
state.ctx->registers.w0 = constant::status::InvMemRange;
state.logger->Warn("svcUnmapMemory: Source not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
auto sourceDesc = state.os->memory.Get(source);
auto destDesc = state.os->memory.Get(destination);
if(!sourceDesc || !destDesc) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcUnmapMemory: Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
if(!destDesc->chunk.state.MapAllowed) {
state.ctx->registers.w0 = constant::status::InvState;
state.logger->Warn("svcUnmapMemory: Destination doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, destDesc->chunk.state.value);
return;
}
auto destObject = state.process->GetMemoryObject(destination);
if(!destObject)
throw exception("svcUnmapMemory: Cannot find destination memory object in handle table for address 0x{:X}", destination);
destObject->item->UpdatePermission(destination, size, sourceDesc->block.permission);
state.process->CopyMemory(destination, source, size);
auto sourceObject = state.process->GetMemoryObject(destination);
if(!sourceObject)
throw exception("svcUnmapMemory: Cannot find source memory object in handle table for address 0x{:X}", source);
state.process->DeleteHandle(sourceObject->handle);
state.logger->Debug("svcUnmapMemory: Unmapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size);
state.ctx->registers.w0 = constant::status::Success;
}
void QueryMemory(DeviceState &state) {
u64 address = state.ctx->registers.x2;
memory::MemoryInfo memInfo{};
@ -106,22 +151,22 @@ namespace skyline::kernel::svc {
.address = descriptor->block.address,
.size = descriptor->block.size,
.type = static_cast<u32>(descriptor->chunk.state.type),
.attributes = descriptor->block.attributes,
.r = descriptor->block.permission.r,
.w = descriptor->block.permission.w,
.x = descriptor->block.permission.x,
.attributes = descriptor->block.attributes.value,
.permissions = static_cast<u32>(descriptor->block.permission.Get()),
.deviceRefCount = 0,
.ipcRefCount = 0,
};
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.address, memInfo.size, memInfo.type, static_cast<bool>(descriptor->block.attributes.isUncached), descriptor->block.permission.r ? "R" : "-", descriptor->block.permission.w ? "W" : "-", descriptor->block.permission.x ? "X" : "-");
} else {
auto region = state.os->memory.GetRegion(memory::Regions::Base);
auto baseEnd = region.address + region.size;
memInfo = {
.address = constant::BaseEnd,
.size = ~(constant::BaseEnd - 1),
.type = static_cast<u32>(memory::MemoryType::Unmapped)
.address = region.address,
.size = ~baseEnd + 1,
.type = static_cast<u32>(memory::MemoryType::Unmapped),
};
state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", address);
}
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.address, memInfo.size, memInfo.type, static_cast<bool>(memInfo.attributes.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-");
state.process->WriteMemory(memInfo, state.ctx->registers.x0);
state.ctx->registers.w0 = constant::status::Success;
}
@ -153,6 +198,7 @@ namespace skyline::kernel::svc {
auto thread = state.process->GetHandle<type::KThread>(handle);
state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->pid);
thread->Start();
state.ctx->registers.w0 = constant::status::Success;
} catch (const std::exception &) {
state.logger->Warn("svcStartThread: 'handle' invalid: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::InvHandle;
@ -160,7 +206,7 @@ namespace skyline::kernel::svc {
}
void ExitThread(DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting current thread: {}", state.thread->pid);
state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->pid);
state.os->KillThread(state.thread->pid);
}
@ -333,6 +379,11 @@ namespace skyline::kernel::svc {
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
auto start = utils::GetCurrTimeNs();
while (true) {
if(state.thread->cancelSync) {
state.thread->cancelSync = false;
state.ctx->registers.w0 = constant::status::Interrupted;
break;
}
uint index{};
for (const auto &object : objectTable) {
if (object->signalled) {
@ -351,82 +402,82 @@ namespace skyline::kernel::svc {
}
}
void CancelSynchronization(DeviceState &state) {
try {
state.process->GetHandle<type::KThread>(state.ctx->registers.w0)->cancelSync = true;
} catch (const std::exception &) {
state.logger->Warn("svcCancelSynchronization: 'handle' invalid: 0x{:X}", state.ctx->registers.w0);
state.ctx->registers.w0 = constant::status::InvHandle;
}
}
void ArbitrateLock(DeviceState &state) {
auto addr = state.ctx->registers.x1;
if ((addr & ((1UL << WORD_BIT) - 1U))) {
auto address = state.ctx->registers.x1;
if (!utils::WordAligned(address)) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcArbitrateLock: 'address' not word aligned: 0x{:X}", addr);
state.logger->Warn("svcArbitrateLock: 'address' not word aligned: 0x{:X}", address);
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);
auto ownerHandle = state.ctx->registers.w0;
auto requesterHandle = state.ctx->registers.w2;
if (requesterHandle != state.thread->handle)
throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", requesterHandle, state.thread->handle);
state.logger->Debug("svcArbitrateLock: Locking mutex at 0x{:X} for thread {}", address, state.thread->pid);
state.process->MutexLock(address, ownerHandle);
state.logger->Debug("svcArbitrateLock: Locked mutex at 0x{:X} for thread {}", address, state.thread->pid);
state.ctx->registers.w0 = constant::status::Success;
}
void ArbitrateUnlock(DeviceState &state) {
auto address = state.ctx->registers.x0;
if ((address & ((1UL << WORD_BIT) - 1U))) {
if (!utils::WordAligned(address)) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcArbitrateUnlock: 'address' not word aligned: 0x{:X}", address);
return;
}
state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", address);
state.process->MutexUnlock(address);
if(state.process->MutexUnlock(address)) {
state.ctx->registers.w0 = constant::status::Success;
state.logger->Debug("svcArbitrateUnlock: Unlocked mutex at 0x{:X}", address);
} else {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Debug("svcArbitrateUnlock: A non-owner thread tried to release a mutex at 0x{:X}", address);
}
}
void WaitProcessWideKeyAtomic(DeviceState &state) {
auto mtxAddress = state.ctx->registers.x0;
auto condAddress = state.ctx->registers.x1;
try {
auto &cvar = state.process->condVars.at(condAddress);
if ((mtxAddress & ((1UL << WORD_BIT) - 1U))) {
if (!utils::WordAligned(mtxAddress)) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: 0x{:X}", mtxAddress);
return;
}
auto condAddress = state.ctx->registers.x1;
auto handle = state.ctx->registers.w2;
if (handle != state.thread->handle)
throw exception("svcWaitProcessWideKeyAtomic: Called from another thread");
state.process->MutexLock(mtxAddress);
auto &mutex = state.process->mutexes.at(mtxAddress);
throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", handle, state.thread->handle);
if(!state.process->MutexUnlock(mtxAddress)) {
state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Debug("WaitProcessWideKeyAtomic: A non-owner thread tried to release a mutex at 0x{:X}", mtxAddress);
return;
}
auto timeout = state.ctx->registers.x3;
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddress, condAddress, timeout);
timespec spec{};
clock_gettime(CLOCK_REALTIME, &spec);
u128 time = u128(spec.tv_sec * 1000000000U + spec.tv_nsec) + timeout; // u128 to prevent overflow
spec.tv_sec = static_cast<time_t>(time / 1000000000U);
spec.tv_nsec = static_cast<long>(time % 1000000000U);
if (pthread_cond_timedwait(&cvar, &mutex, &spec) == ETIMEDOUT)
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x{:X}, Timeout: {} ns", mtxAddress, condAddress, timeout);
if (state.process->ConditionalVariableWait(condAddress, timeout)) {
state.ctx->registers.w0 = constant::status::Success;
state.process->MutexLock(mtxAddress, handle, true);
state.logger->Debug("svcWaitProcessWideKeyAtomic: Waited for conditional variable and relocked mutex");
} else {
state.ctx->registers.w0 = constant::status::Timeout;
else
state.ctx->registers.w0 = constant::status::Success;
state.process->MutexUnlock(mtxAddress);
} catch (const std::out_of_range &) {
state.logger->Debug("svcWaitProcessWideKeyAtomic: No Conditional-Variable at 0x{:X}", condAddress);
state.process->condVars[condAddress] = PTHREAD_COND_INITIALIZER;
state.ctx->registers.w0 = constant::status::Success;
state.logger->Debug("svcWaitProcessWideKeyAtomic: Wait has timed out");
}
}
void SignalProcessWideKey(DeviceState &state) {
auto address = state.ctx->registers.x0;
auto count = state.ctx->registers.w1;
try {
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count);
auto &cvar = state.process->condVars.at(address);
if (count == UINT32_MAX)
pthread_cond_broadcast(&cvar);
else
for (u32 iter = 0; iter < count; iter++)
pthread_cond_signal(&cvar);
} catch (const std::out_of_range &) {
state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address);
state.process->condVars[address] = PTHREAD_COND_INITIALIZER;
}
state.process->ConditionalVariableSignal(address, count);
state.ctx->registers.w0 = constant::status::Success;
}
@ -516,10 +567,10 @@ namespace skyline::kernel::svc {
out = state.process->heap->address + constant::DefStackSize + state.os->memory.GetProgramSize();
break;
case constant::infoState::AddressSpaceBaseAddr:
out = constant::BaseAddress;
out = state.os->memory.GetRegion(memory::Regions::Base).address;
break;
case constant::infoState::AddressSpaceSize:
out = constant::BaseEnd;
out = state.os->memory.GetRegion(memory::Regions::Base).size;
break;
case constant::infoState::StackRegionBaseAddr:
out = state.os->memory.GetRegion(memory::Regions::Stack).address;

View File

@ -37,132 +37,142 @@ namespace skyline {
};
namespace kernel::svc {
/**
* @brief Sets the process heap to a given Size. It can both extend and shrink the heap. (https://switchbrew.org/wiki/SVC#svcSetHeapSize)
* @brief Sets the process heap to a given Size. It can both extend and shrink the heap. (https://switchbrew.org/wiki/SVC#SetHeapSize)
*/
void SetHeapSize(DeviceState &state);
/**
* @brief Change attribute of page-aligned memory region. This is used to turn on/off caching for a given memory area. (https://switchbrew.org/wiki/SVC#svcSetMemoryAttribute)
* @brief Change attribute of page-aligned memory region. This is used to turn on/off caching for a given memory area. (https://switchbrew.org/wiki/SVC#SetMemoryAttribute)
*/
void SetMemoryAttribute(DeviceState &state);
/**
* @brief Maps a memory range into a different range. Mainly used for adding guard pages around stack. (https://switchbrew.org/wiki/SVC#svcSetMemoryAttribute)
* @brief Maps a memory range into a different range. Mainly used for adding guard pages around stack. (https://switchbrew.org/wiki/SVC#SetMemoryAttribute)
*/
void MapMemory(DeviceState &state);
/**
* @brief Query information about an address (https://switchbrew.org/wiki/SVC#svcQueryMemory)
* @brief Unmaps a region that was previously mapped with #MapMemory. (https://switchbrew.org/wiki/SVC#UnmapMemory)
*/
void UnmapMemory(DeviceState &state);
/**
* @brief Query information about an address (https://switchbrew.org/wiki/SVC#QueryMemory)
*/
void QueryMemory(DeviceState &state);
/**
* @brief Exits the current process (https://switchbrew.org/wiki/SVC#svcExitProcess)
* @brief Exits the current process (https://switchbrew.org/wiki/SVC#ExitProcess)
*/
void ExitProcess(DeviceState &state);
/**
* @brief Create a thread in the current process (https://switchbrew.org/wiki/SVC#svcCreateThread)
* @brief Create a thread in the current process (https://switchbrew.org/wiki/SVC#CreateThread)
*/
void CreateThread(DeviceState &state);
/**
* @brief Starts the thread for the provided handle (https://switchbrew.org/wiki/SVC#svcStartThread)
* @brief Starts the thread for the provided handle (https://switchbrew.org/wiki/SVC#StartThread)
*/
void StartThread(DeviceState &state);
/**
* @brief Exits the current thread (https://switchbrew.org/wiki/SVC#svcExitThread)
* @brief Exits the current thread (https://switchbrew.org/wiki/SVC#ExitThread)
*/
void ExitThread(DeviceState &state);
/**
* @brief Sleep for a specified amount of time, or yield thread (https://switchbrew.org/wiki/SVC#svcExitThread)
* @brief Sleep for a specified amount of time, or yield thread (https://switchbrew.org/wiki/SVC#SleepThread)
*/
void SleepThread(DeviceState &state);
/**
* @brief Get priority of provided thread handle (https://switchbrew.org/wiki/SVC#svcGetThreadPriority)
* @brief Get priority of provided thread handle (https://switchbrew.org/wiki/SVC#GetThreadPriority)
*/
void GetThreadPriority(DeviceState &state);
/**
* @brief Set priority of provided thread handle (https://switchbrew.org/wiki/SVC#svcSetThreadPriority)
* @brief Set priority of provided thread handle (https://switchbrew.org/wiki/SVC#SetThreadPriority)
*/
void SetThreadPriority(DeviceState &state);
/**
* @brief Maps the block supplied by the handle (https://switchbrew.org/wiki/SVC#svcMapSharedMemory)
* @brief Maps the block supplied by the handle (https://switchbrew.org/wiki/SVC#MapSharedMemory)
*/
void MapSharedMemory(DeviceState &state);
/**
* @brief Returns a handle to a KSharedMemory object (https://switchbrew.org/wiki/SVC#svcCreateTransferMemory)
* @brief Returns a handle to a KSharedMemory object (https://switchbrew.org/wiki/SVC#CreateTransferMemory)
*/
void CreateTransferMemory(DeviceState &state);
/**
* @brief Closes the specified handle
* @brief Closes the specified handle (https://switchbrew.org/wiki/SVC#CloseHandle)
*/
void CloseHandle(DeviceState &state);
/**
* @brief This resets a particular KEvent or KProcess which is signalled (https://switchbrew.org/wiki/SVC#svcResetSignal)
* @brief This resets a particular KEvent or KProcess which is signalled (https://switchbrew.org/wiki/SVC#ResetSignal)
*/
void ResetSignal(DeviceState &state);
/**
* @brief Stalls a thread till a KSyncObject signals or the timeout has ended (https://switchbrew.org/wiki/SVC#svcWaitSynchronization)
* @brief Stalls a thread till a KSyncObject signals or the timeout has ended (https://switchbrew.org/wiki/SVC#WaitSynchronization)
*/
void WaitSynchronization(DeviceState &state);
/**
* @brief Locks a specified mutex
* @brief If the referenced thread is currently in a synchronization call, that call will be interrupted (https://switchbrew.org/wiki/SVC#CancelSynchronization)
*/
void CancelSynchronization(DeviceState &state);
/**
* @brief Locks a specified mutex (https://switchbrew.org/wiki/SVC#ArbitrateLock)
*/
void ArbitrateLock(DeviceState &state);
/**
* @brief Unlocks a specified mutex
* @brief Unlocks a specified mutex (https://switchbrew.org/wiki/SVC#ArbitrateUnlock)
*/
void ArbitrateUnlock(DeviceState &state);
/**
* @brief Waits on a process-wide key (Conditional-Variable)
* @brief Waits on a process-wide key (Conditional-Variable) (https://switchbrew.org/wiki/SVC#WaitProcessWideKeyAtomic)
*/
void WaitProcessWideKeyAtomic(DeviceState &state);
/**
* @brief Signals a process-wide key (Conditional-Variable)
* @brief Signals a process-wide key (Conditional-Variable) (https://switchbrew.org/wiki/SVC#SignalProcessWideKey)
*/
void SignalProcessWideKey(DeviceState &state);
/**
* @brief This returns the value of CNTPCT_EL0 on the Switch (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
* @brief This returns the value of CNTPCT_EL0 on the Switch (https://switchbrew.org/wiki/SVC#GetSystemTick)
*/
void GetSystemTick(DeviceState &state);
/**
* @brief Connects to a named IPC port
* @brief Connects to a named IPC port (https://switchbrew.org/wiki/SVC#ConnectToNamedPort)
*/
void ConnectToNamedPort(DeviceState &state);
/**
* @brief Send a synchronous IPC request to a service
* @brief Send a synchronous IPC request to a service (https://switchbrew.org/wiki/SVC#SendSyncRequest)
*/
void SendSyncRequest(DeviceState &state);
/**
* @brief Retrieves the PID of a specific thread
* @brief Retrieves the PID of a specific thread (https://switchbrew.org/wiki/SVC#GetThreadId)
*/
void GetThreadId(DeviceState &state);
/**
* @brief Outputs a debug string
* @brief Outputs a debug string (https://switchbrew.org/wiki/SVC#OutputDebugString)
*/
void OutputDebugString(DeviceState &state);
/**
* @brief Retrieves a piece of information (https://switchbrew.org/wiki/SVC#svcGetInfo)
* @brief Retrieves a piece of information (https://switchbrew.org/wiki/SVC#GetInfo)
*/
void GetInfo(DeviceState &state);
@ -175,7 +185,7 @@ namespace skyline {
nullptr, // 0x02
SetMemoryAttribute, // 0x03
MapMemory, // 0x04
nullptr, // 0x05
UnmapMemory, // 0x05
QueryMemory, // 0x06
ExitProcess, // 0x07
CreateThread, // 0x08
@ -195,7 +205,7 @@ namespace skyline {
CloseHandle, // 0x16
ResetSignal, // 0x17
WaitSynchronization, // 0x18
nullptr, // 0x19
CancelSynchronization, // 0x19
ArbitrateLock, // 0x1a
ArbitrateUnlock, // 0x1b
WaitProcessWideKeyAtomic, // 0x1c

View File

@ -4,7 +4,7 @@
#include <asm/unistd.h>
namespace skyline::kernel::type {
KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : state(state), address(address), size(size), KMemory(state, KType::KPrivateMemory) {
KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : address(address), size(size), KMemory(state, KType::KPrivateMemory) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;

View File

@ -7,9 +7,6 @@ namespace skyline::kernel::type {
* @brief KPrivateMemory is used to map memory local to the guest process
*/
class KPrivateMemory : public KMemory {
private:
const DeviceState &state; //!< The state of the device
public:
u64 address{}; //!< The address of the allocated memory
size_t size{}; //!< The size of the allocated memory

View File

@ -4,6 +4,8 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#include <asm/unistd.h>
#include <nce/guest.h>
namespace skyline::kernel::type {
KProcess::TlsPage::TlsPage(u64 address) : address(address) {}
@ -30,9 +32,10 @@ namespace skyline::kernel::type {
if (!tlsPage->Full())
return tlsPage->ReserveSlot();
u64 address;
if(tlsPages.empty())
address = state.os->memory.GetRegion(memory::Regions::TlsIo).address;
else
if (tlsPages.empty()) {
auto region = state.os->memory.GetRegion(memory::Regions::TlsIo);
address = region.size ? region.address : 0;
} else
address = (*(tlsPages.end() - 1))->address + PAGE_SIZE;
auto tlsMem = NewHandle<KPrivateMemory>(address, PAGE_SIZE, memory::Permission(true, true, false), memory::MemoryStates::ThreadLocal).item;
tlsPages.push_back(std::make_shared<TlsPage>(tlsMem->address));
@ -61,37 +64,23 @@ namespace skyline::kernel::type {
status = Status::Exiting;
}
/**
* @brief Function executed by all child threads after cloning
*/
int ExecuteChild(void *) {
asm volatile("BRK #0xFF"); // BRK #constant::brkRdy (So we know when the thread/process is ready)
return 0;
}
u64 CreateThreadFunc(u64 stackTop) {
pid_t pid = clone(&ExecuteChild, reinterpret_cast<void *>(stackTop), CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, nullptr);
return static_cast<u64>(pid);
}
std::shared_ptr<KThread> KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority) {
/*
* Future Reference:
* https://android.googlesource.com/platform/bionic/+/master/libc/bionic/clone.cpp
* https://android.googlesource.com/platform/bionic/+/master/libc/arch-arm64/bionic/__bionic_clone.S
auto size = (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, size, memory::Permission{true, true, false}, memory::MemoryStates::Reserved);
Registers fregs{};
fregs.regs[0] = entryPoint;
fregs.regs[1] = stackTop;
fregs.x0 = CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO;
fregs.x1 = stackTop;
fregs.x3 = tlsMem->Map(0, size, memory::Permission{true, true, false});
fregs.x8 = __NR_clone;
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
auto pid = static_cast<pid_t>(fregs.regs[0]);
if (pid == -1)
fregs.x5 = reinterpret_cast<u64>(&guest::entry);
fregs.x6 = entryPoint;
state.nce->ExecuteFunction(ThreadCall::Clone, fregs);
if (static_cast<int>(fregs.x0) < 0)
throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop);
auto process = NewHandle<KThread>(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this).item;
auto pid = static_cast<pid_t>(fregs.x0);
auto process = NewHandle<KThread>(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this, tlsMem).item;
threads[pid] = process;
return process;
*/
return nullptr;
}
void KProcess::ReadMemory(void *destination, u64 offset, size_t size) const {
@ -136,7 +125,7 @@ namespace skyline::kernel::type {
}
}
std::shared_ptr<KMemory> KProcess::GetMemoryObject(u64 address) {
std::optional<KProcess::HandleOut<KMemory>> KProcess::GetMemoryObject(u64 address) {
for (auto&[handle, object] : state.process->handles) {
switch (object->objectType) {
case type::KType::KPrivateMemory:
@ -144,38 +133,99 @@ namespace skyline::kernel::type {
case type::KType::KTransferMemory: {
auto mem = std::static_pointer_cast<type::KMemory>(object);
if (mem->IsInside(address))
return mem;
return std::optional<KProcess::HandleOut<KMemory>>({mem, handle});
}
default:
break;
}
}
return nullptr;
return std::nullopt;
}
void KProcess::MutexLock(u64 address) {
try {
auto mtx = mutexes.at(address);
pthread_mutex_lock(&mtx);
void KProcess::MutexLock(u64 address, handle_t owner, bool alwaysLock) {
std::unique_lock lock(mutexLock);
u32 mtxVal = ReadMemory<u32>(address);
mtxVal = (mtxVal & ~constant::MtxOwnerMask) | state.thread->handle;
WriteMemory(mtxVal, address);
} catch (const std::out_of_range &) {
mutexes[address] = PTHREAD_MUTEX_INITIALIZER;
if(alwaysLock) {
if(!mtxVal) {
state.logger->Warn("Mutex Value was 0");
mtxVal = (constant::MtxOwnerMask & state.thread->handle);
WriteMemory<u32>(mtxVal, address);
return;
// TODO: Replace with atomic CAS
}
} else {
if (mtxVal != (owner | ~constant::MtxOwnerMask))
return;
}
auto &mtxWaiters = mutexes[address];
std::shared_ptr<WaitStatus> status;
for (auto it = mtxWaiters.begin();;++it) {
if (it != mtxWaiters.end() && (*it)->priority >= state.thread->priority)
continue;
status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->pid);
mtxWaiters.insert(it, status);
break;
}
lock.unlock();
while (!status->flag);
lock.lock();
for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it)
if((*it)->pid == state.thread->pid) {
mtxWaiters.erase(it);
break;
}
mtxVal = (constant::MtxOwnerMask & state.thread->handle) | (mtxWaiters.empty() ? 0 : ~constant::MtxOwnerMask);
WriteMemory<u32>(mtxVal, address);
lock.unlock();
}
void KProcess::MutexUnlock(u64 address) {
try {
auto mtx = mutexes.at(address);
bool KProcess::MutexUnlock(u64 address) {
std::lock_guard lock(mutexLock);
u32 mtxVal = ReadMemory<u32>(address);
if ((mtxVal & constant::MtxOwnerMask) != state.thread->handle)
throw exception("A non-owner thread tried to release a mutex");
return false;
auto &mtxWaiters = mutexes[address];
if (mtxWaiters.empty()) {
mtxVal = 0;
WriteMemory(mtxVal, address);
pthread_mutex_unlock(&mtx);
} catch (const std::out_of_range &) {
mutexes[address] = PTHREAD_MUTEX_INITIALIZER;
}
WriteMemory<u32>(mtxVal, address);
} else
(*mtxWaiters.begin())->flag = true;
return true;
}
bool KProcess::ConditionalVariableWait(u64 address, u64 timeout) {
std::unique_lock lock(conditionalLock);
auto &condWaiters = conditionals[address];
std::shared_ptr<WaitStatus> status;
for (auto it = condWaiters.begin();;++it) {
if (it != condWaiters.end() && (*it)->priority >= state.thread->priority)
continue;
status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->pid);
condWaiters.insert(it, status);
break;
}
lock.unlock();
bool timedOut{};
auto start = utils::GetCurrTimeNs();
while (!status->flag) {
if ((utils::GetCurrTimeNs() - start) >= timeout)
timedOut = true;
}
lock.lock();
for (auto it = condWaiters.begin(); it != condWaiters.end(); ++it)
if((*it)->pid == state.thread->pid) {
condWaiters.erase(it);
break;
}
lock.unlock();
return !timedOut;
}
void KProcess::ConditionalVariableSignal(u64 address, u64 amount) {
std::lock_guard lock(conditionalLock);
auto &condWaiters = conditionals[address];
amount = std::min(condWaiters.size(), amount);
for (size_t i = 0; i < amount; ++i)
condWaiters[i]->flag = true;
}
}

View File

@ -85,15 +85,28 @@ namespace skyline::kernel::type {
Exiting //!< The process is exiting
} status = Status::Created; //!< The state of the process
/**
* @brief This is used to hold information about a single waiting thread for mutexes and conditional variables
*/
struct WaitStatus {
std::atomic_bool flag{false}; //!< The underlying atomic flag of the thread
u8 priority; //!< The priority of the thread
pid_t pid; //!< The PID of the thread
WaitStatus(u8 priority, pid_t pid) : priority(priority), pid(pid) {}
};
handle_t handleIndex = constant::BaseHandleIndex; //!< This is used to keep track of what to map as an handle
pid_t pid; //!< The PID of the main thread
int memFd; //!< The file descriptor to the memory of the process
std::unordered_map<handle_t, std::shared_ptr<KObject>> handles; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
std::unordered_map<pid_t, std::shared_ptr<KThread>> threads; //!< A mapping from a PID to it's corresponding KThread object
std::unordered_map<u64, pthread_mutex_t> mutexes; //!< A map from a mutex's address to a vector of threads waiting on it
std::unordered_map<u64, pthread_cond_t> condVars; //!< A map from a conditional variable's address to a vector of threads waiting on it
std::unordered_map<u64, std::vector<std::shared_ptr<WaitStatus>>> mutexes; //!< A map from a mutex's address to a vector of Mutex objects for threads waiting on it
std::unordered_map<u64, std::vector<std::shared_ptr<WaitStatus>>> conditionals; //!< A map from a conditional variable's address to a vector of threads waiting on it
std::vector<std::shared_ptr<TlsPage>> tlsPages; //!< A vector of all allocated TLS pages
std::shared_ptr<KPrivateMemory> heap; //!< The kernel memory object backing the allocated heap
Mutex mutexLock; //!< This Mutex is to prevent concurrent mutex operations to happen at once
Mutex conditionalLock; //!< This Mutex is to prevent concurrent conditional variable operations to happen at once
/**
* @brief Creates a KThread object for the main thread and opens the process's memory file
@ -145,6 +158,17 @@ namespace skyline::kernel::type {
WriteMemory(&item, address, sizeof(Type));
}
/**
* @brief Writes an object to process memory
* @tparam Type The type of the object to be written
* @param item The object to write
* @param address The address of the object
*/
template<typename Type>
inline void WriteMemory(const Type &item, u64 address) const {
WriteMemory(&item, address, sizeof(Type));
}
/**
* @brief Read data from the process's memory
* @param destination The address to the location where the process memory is written
@ -238,7 +262,7 @@ namespace skyline::kernel::type {
* @param address The address to look for
* @return A shared pointer to the corresponding KMemory object
*/
std::shared_ptr<KMemory> GetMemoryObject(u64 address);
std::optional<HandleOut<KMemory>> GetMemoryObject(u64 address);
/**
* @brief This deletes a certain handle from the handle table
@ -251,14 +275,31 @@ namespace skyline::kernel::type {
/**
* @brief This locks the Mutex at the specified address
* @param address The address of the mutex
* @param owner The handle of the current mutex owner
* @param alwaysLock If to return rather than lock if owner tag is not matched
*/
void MutexLock(u64 address);
void MutexLock(u64 address, handle_t owner, bool alwaysLock = false);
/**
* @brief This unlocks the Mutex at the specified address
* @param address The address of the mutex
* @return If the mutex was successfully unlocked
*/
void MutexUnlock(u64 address);
bool MutexUnlock(u64 address);
/**
* @param address The address of the conditional variable
* @param timeout The amount of time to wait for the conditional variable
* @return If the conditional variable was successfully waited for or timed out
*/
bool ConditionalVariableWait(u64 address, u64 timeout);
/**
* @brief This signals a number of conditional variable waiters
* @param address The address of the conditional variable
* @param amount The amount of waiters to signal
*/
void ConditionalVariableSignal(u64 address, u64 amount);
/**
* @brief This resets the object to an unsignalled state

View File

@ -19,12 +19,13 @@ namespace skyline::kernel::type {
Running, //!< The thread is running currently
Dead //!< The thread is dead and not running
} status = Status::Created; //!< The state of the thread
std::atomic<bool> cancelSync; //!< This is to flag to a thread to cancel a synchronization call it currently is in
std::shared_ptr<type::KSharedMemory> ctxMemory; //!< The KSharedMemory of the shared memory allocated by the guest process TLS
handle_t handle; // The handle of the object in the handle table
pid_t pid; //!< The PID of the current thread (As in kernel PID and not PGID [In short, Linux implements threads as processes that share a lot of stuff at the kernel level])
u64 stackTop; //!< The top of the stack (Where it starts growing downwards from)
u64 tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread
u8 priority; //!< Hold the priority of a thread in Nintendo format
u8 priority; //!< The priority of a thread in Nintendo format
/**
* @param state The state of the device
@ -55,11 +56,6 @@ namespace skyline::kernel::type {
*/
void Kill();
/**
* @brief This wakes up the thread from it's sleep (no-op if thread is already awake)
*/
void WakeUp();
/**
* @brief Update the priority level for the process.
* @details Set the priority of the current thread to `priority` using setpriority [https://linux.die.net/man/3/setpriority]. We rescale the priority from Nintendo scale to that of Android.

View File

@ -38,7 +38,7 @@ namespace skyline {
break;
}
}
} catch (std::exception &e) {
} catch (const std::exception &e) {
state.logger->Error(e.what());
} catch (...) {
state.logger->Error("An unknown exception has occurred");
@ -109,7 +109,7 @@ namespace skyline {
ctx->registers.x0 = entryArg;
ctx->registers.x1 = handle;
ctx->state = ThreadState::WaitRun;
state.logger->Debug("Starting thread with PID: {}", thread->pid);
state.logger->Debug("Starting kernel thread for guest thread: {}", thread->pid);
threadMap[thread->pid] = std::make_shared<std::thread>(&NCE::KernelThread, this, thread->pid);
}

View File

@ -179,6 +179,50 @@ namespace skyline::guest {
auto end = src + size;
while (src < end)
*(src++) = *(dest++);
} else if (ctx->commandId == static_cast<u32>(ThreadCall::Clone)) {
saveCtxStack();
loadCtxTls();
asm("STR LR, [SP, #-16]!\n\t"
"MOV LR, SP\n\t"
"SVC #0\n\t"
"CBNZ X0, .parent\n\t"
"MSR TPIDR_EL0, X3\n\t"
"MOV LR, 0\n\t"
"MOV X0, X6\n\t"
"MOV X1, XZR\n\t"
"MOV X2, XZR\n\t"
"MOV X3, XZR\n\t"
"MOV X4, XZR\n\t"
"MOV X6, XZR\n\t"
"MOV X7, XZR\n\t"
"MOV X8, XZR\n\t"
"MOV X9, XZR\n\t"
"MOV X10, XZR\n\t"
"MOV X11, XZR\n\t"
"MOV X12, XZR\n\t"
"MOV X13, XZR\n\t"
"MOV X14, XZR\n\t"
"MOV X15, XZR\n\t"
"MOV X16, XZR\n\t"
"MOV X17, XZR\n\t"
"MOV X18, XZR\n\t"
"MOV X19, XZR\n\t"
"MOV X20, XZR\n\t"
"MOV X21, XZR\n\t"
"MOV X22, XZR\n\t"
"MOV X23, XZR\n\t"
"MOV X24, XZR\n\t"
"MOV X25, XZR\n\t"
"MOV X26, XZR\n\t"
"MOV X27, XZR\n\t"
"MOV X28, XZR\n\t"
"MOV X29, XZR\n\t"
"BR X5\n\t"
".parent:\n\t"
"MOV SP, LR\n\t"
"LDR LR, [SP], #16");
saveCtxTls();
loadCtxStack();
}
}
}

View File

@ -5,9 +5,9 @@ namespace skyline {
constexpr size_t saveCtxSize = 20 * sizeof(u32);
constexpr size_t loadCtxSize = 20 * sizeof(u32);
#ifdef NDEBUG
constexpr size_t svcHandlerSize = 175 * sizeof(u32);
constexpr size_t svcHandlerSize = 225 * sizeof(u32);
#else
constexpr size_t svcHandlerSize = 275 * sizeof(u32);
constexpr size_t svcHandlerSize = 400 * sizeof(u32);
#endif
void entry(u64 address);

View File

@ -136,6 +136,7 @@ namespace skyline {
enum class ThreadCall : u32 {
Syscall = 0x100, //!< A linux syscall needs to be called from the guest
Memcopy = 0x101, //!< To copy memory from one location to another
Clone = 0x102, //!< Use the clone syscall to create a new thread
};
/**

View File

@ -26,14 +26,13 @@ namespace skyline::kernel {
munmap(stack, stackSize);
throw exception("Failed to create guard pages");
}
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, false), memory::MemoryStates::Reserved);
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission{true, true, false}, memory::MemoryStates::Reserved);
tlsMem->guest = tlsMem->kernel;
pid_t pid = clone(reinterpret_cast<int (*)(void *)>(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void *>(tlsMem->guest.address));
if (pid == -1)
throw exception("Call to clone() has failed: {}", strerror(errno));
state.logger->Debug("Successfully created process with PID: {}", pid);
process = std::make_shared<kernel::type::KProcess>(state, pid, argument, reinterpret_cast<u64>(stack), stackSize, tlsMem);
state.logger->Debug("Successfully created process with PID: {}", pid);
return process;
}

View File

@ -77,6 +77,8 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
}
}
thread(start = true) {
val snackbar = Snackbar.make(findViewById(android.R.id.content), getString(R.string.searching_roms), Snackbar.LENGTH_INDEFINITE)
runOnUiThread {snackbar.show()}
try {
runOnUiThread{adapter.clear()}
val entries = findFile("nro", NroLoader(this), DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!)
@ -102,6 +104,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
notifyUser(e.message!!)
}
}
runOnUiThread {snackbar.dismiss()}
}
}

View File

@ -34,4 +34,5 @@
<string name="handheld_enabled">The system will emulate being in handheld mode</string>
<string name="docked_enabled">The system will emulate being in docked mode</string>
<string name="theme">Theme</string>
<string name="searching_roms">Searching for ROMs</string>
</resources>