diff --git a/app/src/main/cpp/skyline/common.cpp b/app/src/main/cpp/skyline/common.cpp index 3bf214a2..c4ed0a0b 100644 --- a/app/src/main/cpp/skyline/common.cpp +++ b/app/src/main/cpp/skyline/common.cpp @@ -1,25 +1,37 @@ #include "common.h" #include "nce.h" #include "gpu.h" +#include #include namespace skyline { void Mutex::lock() { - while (flag.test_and_set(std::memory_order_acquire)); - } - - void Mutex::unlock() { - flag.clear(std::memory_order_release); - } - - bool Mutex::try_lock() { - return !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"); + } + sched_yield(); + } } void GroupMutex::lock(Group group) { auto none = Group::None; - while (!flag.compare_exchange_weak(none, group) && flag != group); - num++; + 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()) diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index ad85f401..edc88caa 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -6,7 +6,7 @@ #include #include #include -#import +#include #include #include #include @@ -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); + } }; /** diff --git a/app/src/main/cpp/skyline/kernel/ipc.h b/app/src/main/cpp/skyline/kernel/ipc.h index 6f6125b8..aa4805db 100644 --- a/app/src/main/cpp/skyline/kernel/ipc.h +++ b/app/src/main/cpp/skyline/kernel/ipc.h @@ -121,7 +121,6 @@ namespace skyline::kernel::ipc { return static_cast(counter0_5) | static_cast(counter9_11) << 9; } }; - static_assert(sizeof(BufferDescriptorX) == 8); /** @@ -154,7 +153,6 @@ namespace skyline::kernel::ipc { return static_cast(size0_31) | static_cast(size32_35) << 32; } }; - static_assert(sizeof(BufferDescriptorABW) == 12); /** diff --git a/app/src/main/cpp/skyline/kernel/memory.cpp b/app/src/main/cpp/skyline/kernel/memory.cpp index 8e379923..22653b0e 100644 --- a/app/src/main/cpp/skyline/kernel/memory.cpp +++ b/app/src/main/cpp/skyline/kernel/memory.cpp @@ -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: diff --git a/app/src/main/cpp/skyline/kernel/memory.h b/app/src/main/cpp/skyline/kernel/memory.h index 9f6c54cb..bb51cc3e 100644 --- a/app/src/main/cpp/skyline/kernel/memory.h +++ b/app/src/main/cpp/skyline/kernel/memory.h @@ -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 { - u32 _pad0_; - struct { - bool r : 1, w : 1, x : 1; - }; - }; - u32 ipcRefCount; - u32 deviceRefCount; - u32 : 32; + 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_; }; - 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 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 diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index 64e41fc8..8f3162aa 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -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(destination, size, memory::Permission{true, true, true}, memory::MemoryStates::Stack); + state.process->NewHandle(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(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(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(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(memory::MemoryType::Unmapped) + .address = region.address, + .size = ~baseEnd + 1, + .type = static_cast(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(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(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(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); - state.ctx->registers.w0 = constant::status::Success; + 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; + 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; - try { - auto &cvar = state.process->condVars.at(condAddress); - if ((mtxAddress & ((1UL << WORD_BIT) - 1U))) { - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: 0x{:X}", mtxAddress); - return; - } - 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); - 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 / 1000000000U); - spec.tv_nsec = static_cast(time % 1000000000U); - if (pthread_cond_timedwait(&cvar, &mutex, &spec) == ETIMEDOUT) - 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; + auto handle = state.ctx->registers.w2; + if (handle != state.thread->handle) + 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); + 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; + 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.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count); + 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; diff --git a/app/src/main/cpp/skyline/kernel/svc.h b/app/src/main/cpp/skyline/kernel/svc.h index 90c4282f..c5b1d9e0 100644 --- a/app/src/main/cpp/skyline/kernel/svc.h +++ b/app/src/main/cpp/skyline/kernel/svc.h @@ -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 diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp index 7ed451f0..394a5edd 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp @@ -4,7 +4,7 @@ #include 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; diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h index 84b97238..9d5224c0 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h @@ -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 diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index 9dcbc4e1..0c92f215 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace skyline::kernel::type { KProcess::TlsPage::TlsPage(u64 address) : address(address) {} @@ -30,10 +32,11 @@ 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 - address = (*(tlsPages.end()-1))->address + PAGE_SIZE; + 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(address, PAGE_SIZE, memory::Permission(true, true, false), memory::MemoryStates::ThreadLocal).item; tlsPages.push_back(std::make_shared(tlsMem->address)); auto &tlsPage = tlsPages.back(); @@ -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(stackTop), CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, nullptr); - return static_cast(pid); - } - std::shared_ptr 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(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(fregs.regs[0]); - if (pid == -1) + fregs.x5 = reinterpret_cast(&guest::entry); + fregs.x6 = entryPoint; + state.nce->ExecuteFunction(ThreadCall::Clone, fregs); + if (static_cast(fregs.x0) < 0) throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop); - auto process = NewHandle(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this).item; + auto pid = static_cast(fregs.x0); + auto process = NewHandle(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,46 +125,107 @@ namespace skyline::kernel::type { } } - std::shared_ptr KProcess::GetMemoryObject(u64 address) { - for(auto& [handle, object] : state.process->handles) { - switch(object->objectType) { + std::optional> KProcess::GetMemoryObject(u64 address) { + for (auto&[handle, object] : state.process->handles) { + switch (object->objectType) { case type::KType::KPrivateMemory: case type::KType::KSharedMemory: case type::KType::KTransferMemory: { auto mem = std::static_pointer_cast(object); if (mem->IsInside(address)) - return mem; + return std::optional>({mem, handle}); } default: break; } } - return nullptr; + return std::nullopt; } - void KProcess::MutexLock(u64 address) { - try { - auto mtx = mutexes.at(address); - pthread_mutex_lock(&mtx); - u32 mtxVal = ReadMemory(address); - mtxVal = (mtxVal & ~constant::MtxOwnerMask) | state.thread->handle; - WriteMemory(mtxVal, address); - } catch (const std::out_of_range &) { - mutexes[address] = PTHREAD_MUTEX_INITIALIZER; + void KProcess::MutexLock(u64 address, handle_t owner, bool alwaysLock) { + std::unique_lock lock(mutexLock); + u32 mtxVal = ReadMemory(address); + if(alwaysLock) { + if(!mtxVal) { + state.logger->Warn("Mutex Value was 0"); + mtxVal = (constant::MtxOwnerMask & state.thread->handle); + WriteMemory(mtxVal, address); + return; + // TODO: Replace with atomic CAS + } + } else { + if (mtxVal != (owner | ~constant::MtxOwnerMask)) + return; } + auto &mtxWaiters = mutexes[address]; + std::shared_ptr status; + for (auto it = mtxWaiters.begin();;++it) { + if (it != mtxWaiters.end() && (*it)->priority >= state.thread->priority) + continue; + status = std::make_shared(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(mtxVal, address); + lock.unlock(); } - void KProcess::MutexUnlock(u64 address) { - try { - auto mtx = mutexes.at(address); - u32 mtxVal = ReadMemory(address); - if ((mtxVal & constant::MtxOwnerMask) != state.thread->handle) - throw exception("A non-owner thread tried to release a mutex"); + bool KProcess::MutexUnlock(u64 address) { + std::lock_guard lock(mutexLock); + u32 mtxVal = ReadMemory(address); + if ((mtxVal & constant::MtxOwnerMask) != state.thread->handle) + 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(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 status; + for (auto it = condWaiters.begin();;++it) { + if (it != condWaiters.end() && (*it)->priority >= state.thread->priority) + continue; + status = std::make_shared(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; } } diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index c05fb54d..3b5f135b 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -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> handles; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object std::unordered_map> threads; //!< A mapping from a PID to it's corresponding KThread object - std::unordered_map mutexes; //!< A map from a mutex's address to a vector of threads waiting on it - std::unordered_map condVars; //!< A map from a conditional variable's address to a vector of threads waiting on it + std::unordered_map>> mutexes; //!< A map from a mutex's address to a vector of Mutex objects for threads waiting on it + std::unordered_map>> conditionals; //!< A map from a conditional variable's address to a vector of threads waiting on it std::vector> tlsPages; //!< A vector of all allocated TLS pages std::shared_ptr 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 + 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 GetMemoryObject(u64 address); + std::optional> 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 diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.h b/app/src/main/cpp/skyline/kernel/types/KThread.h index 00401a3d..49d15352 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.h +++ b/app/src/main/cpp/skyline/kernel/types/KThread.h @@ -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 cancelSync; //!< This is to flag to a thread to cancel a synchronization call it currently is in std::shared_ptr 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. diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index 63405470..f77310bc 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -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(&NCE::KernelThread, this, thread->pid); } diff --git a/app/src/main/cpp/skyline/nce/guest.cpp b/app/src/main/cpp/skyline/nce/guest.cpp index e492c8fb..63226960 100644 --- a/app/src/main/cpp/skyline/nce/guest.cpp +++ b/app/src/main/cpp/skyline/nce/guest.cpp @@ -112,7 +112,7 @@ namespace skyline::guest { "MOV LR, SP\n\t" "SVC #0\n\t" "MOV SP, LR\n\t" - "LDR LR, [SP], #16" :: : "x0", "x1", "x2", "x3", "x4", "x5", "x8"); + "LDR LR, [SP], #16" ::: "x0", "x1", "x2", "x3", "x4", "x5", "x8"); break; } default: { @@ -179,6 +179,50 @@ namespace skyline::guest { auto end = src + size; while (src < end) *(src++) = *(dest++); + } else if (ctx->commandId == static_cast(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(); } } } @@ -268,7 +312,7 @@ namespace skyline::guest { "MOV X27, XZR\n\t" "MOV X28, XZR\n\t" "MOV X29, XZR\n\t" - "RET"::"r"(address), "r"(ctx->registers.x0), "r"(ctx->registers.x1) : "x0", "x1", "lr"); + "RET" :: "r"(address), "r"(ctx->registers.x0), "r"(ctx->registers.x1) : "x0", "x1", "lr"); __builtin_unreachable(); } } diff --git a/app/src/main/cpp/skyline/nce/guest.h b/app/src/main/cpp/skyline/nce/guest.h index aee5e85d..f94e663b 100644 --- a/app/src/main/cpp/skyline/nce/guest.h +++ b/app/src/main/cpp/skyline/nce/guest.h @@ -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); diff --git a/app/src/main/cpp/skyline/nce/guest_common.h b/app/src/main/cpp/skyline/nce/guest_common.h index caad126d..3dc880c6 100644 --- a/app/src/main/cpp/skyline/nce/guest_common.h +++ b/app/src/main/cpp/skyline/nce/guest_common.h @@ -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 }; /** diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp index 3af86e0c..955bbc5e 100644 --- a/app/src/main/cpp/skyline/os.cpp +++ b/app/src/main/cpp/skyline/os.cpp @@ -26,14 +26,13 @@ namespace skyline::kernel { munmap(stack, stackSize); throw exception("Failed to create guard pages"); } - auto tlsMem = std::make_shared(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, false), memory::MemoryStates::Reserved); + auto tlsMem = std::make_shared(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(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast(entry), nullptr, reinterpret_cast(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(state, pid, argument, reinterpret_cast(stack), stackSize, tlsMem); - state.logger->Debug("Successfully created process with PID: {}", pid); return process; } diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index c10605d8..3b7adf48 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -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()} } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3dd30565..62970b9c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,4 +34,5 @@ The system will emulate being in handheld mode The system will emulate being in docked mode Theme + Searching for ROMs