From 0b46b92bb75062dcdf2b16825c6898f8e6fa2722 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Tue, 18 Apr 2023 20:42:17 +0300 Subject: [PATCH] [Kernel] Add idle thread --- CMakeLists.txt | 1 + include/arm_defs.hpp | 5 ++- include/kernel/kernel.hpp | 13 ++++++- include/memory.hpp | 2 + src/core/kernel/idle_thread.cpp | 69 +++++++++++++++++++++++++++++++++ src/core/kernel/kernel.cpp | 1 + 6 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 src/core/kernel/idle_thread.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index eda9acaf..d3ffa15a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limi src/core/kernel/events.cpp src/core/kernel/threads.cpp src/core/kernel/address_arbiter.cpp src/core/kernel/error.cpp src/core/kernel/file_operations.cpp src/core/kernel/directory_operations.cpp + src/core/kernel/idle_thread.cpp ) set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services/apt.cpp src/core/services/hid.cpp src/core/services/fs.cpp src/core/services/gsp_gpu.cpp src/core/services/gsp_lcd.cpp diff --git a/include/arm_defs.hpp b/include/arm_defs.hpp index 1d7a9bde..2f167e2f 100644 --- a/include/arm_defs.hpp +++ b/include/arm_defs.hpp @@ -1,8 +1,9 @@ #pragma once +#include // Status register definitions namespace CPSR { - enum : u32 { + enum : std::uint32_t { // Privilege modes UserMode = 16, FIQMode = 17, @@ -26,7 +27,7 @@ namespace CPSR { namespace FPSCR { // FPSCR Flags - enum : u32 { + enum : std::uint32_t { Sign = 1U << 31U, // Negative condition flag Zero = 1 << 30, // Zero condition flag Carry = 1 << 29, // Carry condition flag diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 118e315d..a2e22803 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -19,7 +20,16 @@ class Kernel { // The handle number for the next kernel object to be created u32 handleCounter; - std::array threads; + // A list of our OS threads, the max number of which depends on the resource limit (hardcoded 32 per process on retail it seems). + // We have an extra thread for when no thread is capable of running. This thread is called the "idle thread" in our code + // This thread is set up in setupIdleThread and just yields in a loop to see if any other thread has woken up + std::array threads; + static constexpr int idleThreadIndex = appResourceLimits.maxThreads; + // Our waitlist system uses a bitfield of 64 bits to show which threads are waiting on an object. + // That means we can have a maximum of 63 threads + 1 idle thread. This assert should never trigger because the max thread # is 32 + // But we have it here for safety purposes + static_assert(appResourceLimits.maxThreads <= 63, "The waitlist system is built on the premise that <= 63 threads max can be active"); + std::vector objects; std::vector portHandles; @@ -71,6 +81,7 @@ private: s32 getCurrentResourceValue(const KernelObject* limit, u32 resourceName); u32 getMaxForResource(const KernelObject* limit, u32 resourceName); u32 getTLSPointer(); + void setupIdleThread(); bool isWaitable(const KernelObject* object); diff --git a/include/memory.hpp b/include/memory.hpp index b15cac62..0e75f36c 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -111,6 +111,7 @@ class Memory { SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle) // HID shared memory }; +public: static constexpr u32 pageShift = 12; static constexpr u32 pageSize = 1 << pageShift; static constexpr u32 pageMask = pageSize - 1; @@ -125,6 +126,7 @@ class Memory { static constexpr u32 DSP_CODE_MEMORY_OFFSET = 0_KB; static constexpr u32 DSP_DATA_MEMORY_OFFSET = 256_KB; +private: std::bitset usedFCRAMPages; std::optional findPaddr(u32 size); u64 timeSince3DSEpoch(); diff --git a/src/core/kernel/idle_thread.cpp b/src/core/kernel/idle_thread.cpp new file mode 100644 index 00000000..4b0a981a --- /dev/null +++ b/src/core/kernel/idle_thread.cpp @@ -0,0 +1,69 @@ +#include +#include "arm_defs.hpp" +#include "kernel.hpp" + +/* + This file sets up an idle thread that's meant to run when no other OS thread can run. + It simply idles and constantly yields to check if there's any other thread that can run + The code for our idle thread looks like this + +idle_thread_main: + mov r0, #4096 @ Loop counter + + .loop: + nop; nop; nop; nop @ NOP 4 times to waste some cycles + subs r0, #1 @ Decrement counter by 1, go back to looping if loop counter != 0 + bne .loop + + // Sleep for 0 seconds with the SleepThread SVC, which just yields execution + mov r0, #0 + mov r1, #0 + svc SleepThread + + b idle_thread_main +*/ + +static constexpr u8 idleThreadCode[] = { + 0x01, 0x0A, 0xA0, 0xE3, // mov r0, #4096 + 0x00, 0xF0, 0x20, 0xE3, 0x00, 0xF0, 0x20, 0xE3, 0x00, 0xF0, 0x20, 0xE3, 0x00, 0xF0, 0x20, 0xE3, // nop (4 times) + 0x01, 0x00, 0x50, 0xE2, // subs r0, #1 + 0xF9, 0xFF, 0xFF, 0x1A, // bne loop + 0x00, 0x00, 0xA0, 0xE3, // mov r0, #0 + 0x00, 0x10, 0xA0, 0xE3, // mov r1, #0 + 0x0A, 0x00, 0x00, 0xEF, // svc SleepThread + 0xF4, 0xFF, 0xFF, 0xEA // b idle_thread_main +}; + +// Set up an idle thread to run when no thread is able to run +void Kernel::setupIdleThread() { + Thread& t = threads[idleThreadIndex]; + constexpr u32 codeAddress = 0xBFC00000; + + // Reserve some memory for the idle thread's code. We map this memory to vaddr BFC00000 which is not userland-accessible + // We only allocate 4KB (1 page) because our idle code is pretty small + const u32 fcramIndex = mem.allocateSysMemory(Memory::pageSize); + auto vaddr = mem.allocateMemory(codeAddress, fcramIndex, Memory::pageSize, true, true, false, true, false, true); + if (!vaddr.has_value() || vaddr.value() != codeAddress) { + Helpers::panic("Failed to setup idle thread"); + } + + // Copy idle thread code to the allocated FCRAM + std::memcpy(&mem.getFCRAM()[fcramIndex], idleThreadCode, sizeof(idleThreadCode)); + + t.entrypoint = codeAddress; + t.gprs[13] = 0; // Set SP & LR to 0 just in case. The idle thread should never access memory, but let's be safe + t.gprs[14] = 0; + t.gprs[15] = codeAddress; + t.cpsr = CPSR::UserMode; + t.fpscr = FPSCR::ThreadDefault; + + // Our idle thread should have as low of a priority as possible, because, well, it's an idle thread. + // We handle this by giving it a priority of 0xff, which is lower than is actually allowed for user threads + // (High priority value = low priority) + t.priority = 0xff; + t.status = ThreadStatus::Ready; + + // Add idle thread to the list of thread indices + threadIndices.push_back(idleThreadIndex); + sortThreads(); +} \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index dd6a6e00..1a5184f3 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -131,6 +131,7 @@ void Kernel::reset() { // which is thankfully not used. Maybe we should prevent this mainThread = makeThread(0, VirtualAddrs::StackTop, 0x30, -2, 0, ThreadStatus::Running); currentThreadIndex = 0; + setupIdleThread(); // Create some of the OS ports srvHandle = makePort("srv:"); // Service manager port