mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 06:05:40 +12:00
[Kernel] Add idle thread
This commit is contained in:
parent
94ea97a419
commit
0b46b92bb7
6 changed files with 88 additions and 3 deletions
|
@ -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
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
// 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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -19,7 +20,16 @@ class Kernel {
|
|||
|
||||
// The handle number for the next kernel object to be created
|
||||
u32 handleCounter;
|
||||
std::array<Thread, appResourceLimits.maxThreads> 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<Thread, appResourceLimits.maxThreads + 1> 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<KernelObject> objects;
|
||||
std::vector<Handle> 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);
|
||||
|
||||
|
|
|
@ -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<FCRAM_PAGE_COUNT> usedFCRAMPages;
|
||||
std::optional<u32> findPaddr(u32 size);
|
||||
u64 timeSince3DSEpoch();
|
||||
|
|
69
src/core/kernel/idle_thread.cpp
Normal file
69
src/core/kernel/idle_thread.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include <cstring>
|
||||
#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();
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue