| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651 |
- // Copyright 2014 Citra Emulator Project / PPSSPP Project
- // Licensed under GPLv2
- // Refer to the license.txt file included.
- #include <stdio.h>
- #include <list>
- #include <vector>
- #include <map>
- #include <string>
- #include "common/common.h"
- #include "core/core.h"
- #include "core/mem_map.h"
- #include "core/hle/kernel/kernel.h"
- #include "core/hle/kernel/thread.h"
- struct ThreadQueueList {
- // Number of queues (number of priority levels starting at 0.)
- static const int NUM_QUEUES = 128;
- // Initial number of threads a single queue can handle.
- static const int INITIAL_CAPACITY = 32;
- struct Queue {
- // Next ever-been-used queue (worse priority.)
- Queue *next;
- // First valid item in data.
- int first;
- // One after last valid item in data.
- int end;
- // A too-large array with room on the front and end.
- UID *data;
- // Size of data array.
- int capacity;
- };
- ThreadQueueList() {
- memset(queues, 0, sizeof(queues));
- first = invalid();
- }
- ~ThreadQueueList() {
- for (int i = 0; i < NUM_QUEUES; ++i) {
- if (queues[i].data != NULL) {
- free(queues[i].data);
- }
- }
- }
- // Only for debugging, returns priority level.
- int contains(const UID uid) {
- for (int i = 0; i < NUM_QUEUES; ++i) {
- if (queues[i].data == NULL) {
- continue;
- }
- Queue *cur = &queues[i];
- for (int j = cur->first; j < cur->end; ++j) {
- if (cur->data[j] == uid) {
- return i;
- }
- }
- }
- return -1;
- }
- inline UID pop_first() {
- Queue *cur = first;
- while (cur != invalid()) {
- if (cur->end - cur->first > 0) {
- return cur->data[cur->first++];
- }
- cur = cur->next;
- }
- _dbg_assert_msg_(KERNEL, false, "ThreadQueueList should not be empty.");
- return 0;
- }
- inline UID pop_first_better(u32 priority) {
- Queue *cur = first;
- Queue *stop = &queues[priority];
- while (cur < stop) {
- if (cur->end - cur->first > 0) {
- return cur->data[cur->first++];
- }
- cur = cur->next;
- }
- return 0;
- }
- inline void push_front(u32 priority, const UID thread_id) {
- Queue *cur = &queues[priority];
- cur->data[--cur->first] = thread_id;
- if (cur->first == 0) {
- rebalance(priority);
- }
- }
- inline void push_back(u32 priority, const UID thread_id)
- {
- Queue *cur = &queues[priority];
- cur->data[cur->end++] = thread_id;
- if (cur->end == cur->capacity) {
- rebalance(priority);
- }
- }
- inline void remove(u32 priority, const UID thread_id) {
- Queue *cur = &queues[priority];
- _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
- for (int i = cur->first; i < cur->end; ++i) {
- if (cur->data[i] == thread_id) {
- int remaining = --cur->end - i;
- if (remaining > 0) {
- memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(UID));
- }
- return;
- }
- }
- // Wasn't there.
- }
- inline void rotate(u32 priority) {
- Queue *cur = &queues[priority];
- _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
- if (cur->end - cur->first > 1) {
- cur->data[cur->end++] = cur->data[cur->first++];
- if (cur->end == cur->capacity) {
- rebalance(priority);
- }
- }
- }
- inline void clear() {
- for (int i = 0; i < NUM_QUEUES; ++i) {
- if (queues[i].data != NULL) {
- free(queues[i].data);
- }
- }
- memset(queues, 0, sizeof(queues));
- first = invalid();
- }
- inline bool empty(u32 priority) const {
- const Queue *cur = &queues[priority];
- return cur->first == cur->end;
- }
- inline void prepare(u32 priority) {
- Queue *cur = &queues[priority];
- if (cur->next == NULL) {
- link(priority, INITIAL_CAPACITY);
- }
- }
- private:
- Queue *invalid() const {
- return (Queue *)-1;
- }
- void link(u32 priority, int size) {
- _dbg_assert_msg_(KERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once.");
- if (size <= INITIAL_CAPACITY) {
- size = INITIAL_CAPACITY;
- } else {
- int goal = size;
- size = INITIAL_CAPACITY;
- while (size < goal)
- size *= 2;
- }
- Queue *cur = &queues[priority];
- cur->data = (UID*)malloc(sizeof(UID)* size);
- cur->capacity = size;
- cur->first = size / 2;
- cur->end = size / 2;
- for (int i = (int)priority - 1; i >= 0; --i) {
- if (queues[i].next != NULL) {
- cur->next = queues[i].next;
- queues[i].next = cur;
- return;
- }
- }
- cur->next = first;
- first = cur;
- }
- void rebalance(u32 priority) {
- Queue *cur = &queues[priority];
- int size = cur->end - cur->first;
- if (size >= cur->capacity - 2) {
- UID* new_data = (UID*)realloc(cur->data, cur->capacity * 2 * sizeof(UID));
- if (new_data != NULL) {
- cur->capacity *= 2;
- cur->data = new_data;
- }
- }
- int newFirst = (cur->capacity - size) / 2;
- if (newFirst != cur->first) {
- memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(UID));
- cur->first = newFirst;
- cur->end = newFirst + size;
- }
- }
- // The first queue that's ever been used.
- Queue* first;
- // The priority level queues of thread ids.
- Queue queues[NUM_QUEUES];
- };
- // Supposed to represent a real CTR struct... but not sure of the correct fields yet.
- struct NativeThread {
- //u32 Pointer to vtable
- //u32 Reference count
- //KProcess* Process the thread belongs to (virtual address)
- //u32 Thread id
- //u32* ptr = *(KThread+0x8C) - 0xB0
- //u32* End-address of the page for this thread allocated in the 0xFF4XX000 region. Thus,
- // if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000.
- //KThread* Previous ? (virtual address)
- //KThread* Next ? (virtual address)
- u32_le native_size;
- char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
- // Threading stuff
- u32_le status;
- u32_le entry_point;
- u32_le initial_stack;
- u32_le stack_top;
- u32_le stack_size;
-
- u32_le arg;
- u32_le processor_id;
- s32_le initial_priority;
- s32_le current_priority;
- };
- struct ThreadWaitInfo {
- u32 wait_value;
- u32 timeout_ptr;
- };
- class Thread : public KernelObject {
- public:
- /*const char *GetName() { return nt.name; }*/
- const char *GetTypeName() { return "Thread"; }
- //void GetQuickInfo(char *ptr, int size)
- //{
- // sprintf(ptr, "pc= %08x sp= %08x %s %s %s %s %s %s (wt=%i wid=%i wv= %08x )",
- // context.pc, context.r[13], // 13 is stack pointer
- // (nt.status & THREADSTATUS_RUNNING) ? "RUN" : "",
- // (nt.status & THREADSTATUS_READY) ? "READY" : "",
- // (nt.status & THREADSTATUS_WAIT) ? "WAIT" : "",
- // (nt.status & THREADSTATUS_SUSPEND) ? "SUSPEND" : "",
- // (nt.status & THREADSTATUS_DORMANT) ? "DORMANT" : "",
- // (nt.status & THREADSTATUS_DEAD) ? "DEAD" : "",
- // nt.waitType,
- // nt.waitID,
- // waitInfo.waitValue);
- //}
- //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; }
- static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; }
- KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; }
- bool SetupStack(u32 stack_top, int stack_size) {
- current_stack.start = stack_top;
- nt.initial_stack = current_stack.start;
- nt.stack_size = stack_size;
- return true;
- }
- //bool FillStack() {
- // // Fill the stack.
- // if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) {
- // Memory::Memset(current_stack.start, 0xFF, nt.stack_size);
- // }
- // context.r[MIPS_REG_SP] = current_stack.start + nt.stack_size;
- // current_stack.end = context.r[MIPS_REG_SP];
- // // The k0 section is 256 bytes at the top of the stack.
- // context.r[MIPS_REG_SP] -= 256;
- // context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP];
- // u32 k0 = context.r[MIPS_REG_K0];
- // Memory::Memset(k0, 0, 0x100);
- // Memory::Write_U32(GetUID(), k0 + 0xc0);
- // Memory::Write_U32(nt.initialStack, k0 + 0xc8);
- // Memory::Write_U32(0xffffffff, k0 + 0xf8);
- // Memory::Write_U32(0xffffffff, k0 + 0xfc);
- // // After k0 comes the arguments, which is done by sceKernelStartThread().
- // Memory::Write_U32(GetUID(), nt.initialStack);
- // return true;
- //}
- //void FreeStack() {
- // if (current_stack.start != 0) {
- // DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name);
- // if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) {
- // Memory::Memset(nt.initialStack, 0, nt.stack_size);
- // }
- // if (nt.attr & PSP_THREAD_ATTR_KERNEL) {
- // kernelMemory.Free(current_stack.start);
- // }
- // else {
- // userMemory.Free(current_stack.start);
- // }
- // current_stack.start = 0;
- // }
- //}
- //bool PushExtendedStack(u32 size) {
- // u32 stack = userMemory.Alloc(size, true, (std::string("extended/") + nt.name).c_str());
- // if (stack == (u32)-1)
- // return false;
- // pushed_stacks.push_back(current_stack);
- // current_stack.start = stack;
- // current_stack.end = stack + size;
- // nt.initialStack = current_stack.start;
- // nt.stack_size = current_stack.end - current_stack.start;
- // // We still drop the thread_id at the bottom and fill it, but there's no k0.
- // Memory::Memset(current_stack.start, 0xFF, nt.stack_size);
- // Memory::Write_U32(GetUID(), nt.initialStack);
- // return true;
- //}
- //bool PopExtendedStack() {
- // if (pushed_stacks.size() == 0) {
- // return false;
- // }
- // userMemory.Free(current_stack.start);
- // current_stack = pushed_stacks.back();
- // pushed_stacks.pop_back();
- // nt.initialStack = current_stack.start;
- // nt.stack_size = current_stack.end - current_stack.start;
- // return true;
- //}
- Thread() {
- current_stack.start = 0;
- }
- // Can't use a destructor since savestates will call that too.
- //void Cleanup() {
- // // Callbacks are automatically deleted when their owning thread is deleted.
- // for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it)
- // kernelObjects.Destroy<Callback>(*it);
- // if (pushed_stacks.size() != 0)
- // {
- // WARN_LOG(KERNEL, "Thread ended within an extended stack");
- // for (size_t i = 0; i < pushed_stacks.size(); ++i)
- // userMemory.Free(pushed_stacks[i].start);
- // }
- // FreeStack();
- //}
- void setReturnValue(u32 retval);
- void setReturnValue(u64 retval);
- void resumeFromWait();
- //bool isWaitingFor(WaitType type, int id);
- //int getWaitID(WaitType type);
- ThreadWaitInfo getWaitInfo();
- // Utils
- inline bool IsRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; }
- inline bool IsStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; }
- inline bool IsReady() const { return (nt.status & THREADSTATUS_READY) != 0; }
- inline bool IsWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; }
- inline bool IsSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; }
- NativeThread nt;
- ThreadWaitInfo waitInfo;
- UID moduleId;
- //bool isProcessingCallbacks;
- //u32 currentMipscallId;
- //UID currentCallbackId;
- ThreadContext context;
- std::vector<UID> callbacks;
- std::list<u32> pending_calls;
- struct StackInfo {
- u32 start;
- u32 end;
- };
- // This is a stack of... stacks, since sceKernelExtendThreadStack() can recurse.
- // These are stacks that aren't "active" right now, but will pop off once the func returns.
- std::vector<StackInfo> pushed_stacks;
- StackInfo current_stack;
- // For thread end.
- std::vector<UID> waiting_threads;
- // Key is the callback id it was for, or if no callback, the thread id.
- std::map<UID, u64> paused_waits;
- };
- void ThreadContext::reset() {
- for (int i = 0; i < 16; i++) {
- reg[i] = 0;
- }
- reg[13] = Memory::SCRATCHPAD_VADDR_END;
- cpsr = 0;
- }
- // Lists all thread ids that aren't deleted/etc.
- std::vector<UID> g_thread_queue;
- // Lists only ready thread ids
- ThreadQueueList g_thread_ready_queue;
- UID g_current_thread = 0;
- Thread* g_current_thread_ptr = NULL;
- const char* g_hle_current_thread_name = NULL;
- /// Creates a new thread
- Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority,
- u32 entrypoint, u32 arg, u32 stack_top, u32 processor_id, int stack_size) {
- Thread *t = new Thread;
- id = g_kernel_objects.Create(t);
- g_thread_queue.push_back(id);
- g_thread_ready_queue.prepare(priority);
- memset(&t->nt, 0xCD, sizeof(t->nt));
- t->nt.entry_point = entrypoint;
- t->nt.native_size = sizeof(t->nt);
- t->nt.initial_priority = t->nt.current_priority = priority;
- t->nt.status = THREADSTATUS_DORMANT;
- t->nt.initial_stack = t->nt.stack_top = stack_top;
- t->nt.stack_size = stack_size;
- t->nt.processor_id = processor_id;
- strncpy(t->nt.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
- t->nt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0';
- t->nt.stack_size = stack_size;
- t->SetupStack(stack_top, stack_size);
- return t;
- }
- /// Resets the specified thread back to initial calling state
- void __KernelResetThread(Thread *t, int lowest_priority) {
- t->context.reset();
- t->context.pc = t->nt.entry_point;
- // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd...
- if (t->nt.current_priority < lowest_priority) {
- t->nt.current_priority = t->nt.initial_priority;
- }
- memset(&t->waitInfo, 0, sizeof(t->waitInfo));
- }
- /// Returns the current executing thread
- inline Thread *__GetCurrentThread() {
- return g_current_thread_ptr;
- }
- /// Sets the current executing thread
- inline void __SetCurrentThread(Thread *thread, UID thread_id, const char *name) {
- g_current_thread = thread_id;
- g_current_thread_ptr = thread;
- g_hle_current_thread_name = name;
- }
- // TODO: Use __KernelChangeThreadState instead? It has other affects...
- void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready) {
- // Passing the id as a parameter is just an optimization, if it's wrong it will cause havoc.
- _dbg_assert_msg_(KERNEL, thread->GetUID() == thread_id, "Incorrect thread_id");
- int prio = thread->nt.current_priority;
- if (thread->IsReady()) {
- if (!ready)
- g_thread_ready_queue.remove(prio, thread_id);
- } else if (ready) {
- if (thread->IsRunning()) {
- g_thread_ready_queue.push_front(prio, thread_id);
- } else {
- g_thread_ready_queue.push_back(prio, thread_id);
- }
- thread->nt.status = THREADSTATUS_READY;
- }
- }
- void __KernelChangeReadyState(UID thread_id, bool ready) {
- u32 error;
- Thread *thread = g_kernel_objects.Get<Thread>(thread_id, error);
- if (thread) {
- __KernelChangeReadyState(thread, thread_id, ready);
- } else {
- WARN_LOG(KERNEL, "Trying to change the ready state of an unknown thread?");
- }
- }
- /// Returns NULL if the current thread is fine.
- Thread* __KernelNextThread() {
- UID best_thread;
- // If the current thread is running, it's a valid candidate.
- Thread *cur = __GetCurrentThread();
- if (cur && cur->IsRunning()) {
- best_thread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority);
- if (best_thread != 0) {
- __KernelChangeReadyState(cur, g_current_thread, true);
- }
- } else {
- best_thread = g_thread_ready_queue.pop_first();
- }
- // Assume g_thread_ready_queue has not become corrupt.
- if (best_thread != 0) {
- return g_kernel_objects.GetFast<Thread>(best_thread);
- } else {
- return NULL;
- }
- }
- /// Saves the current CPU context
- void __KernelSaveContext(ThreadContext *ctx) {
- ctx->reg[0] = Core::g_app_core->GetReg(0);
- ctx->reg[1] = Core::g_app_core->GetReg(1);
- ctx->reg[2] = Core::g_app_core->GetReg(2);
- ctx->reg[3] = Core::g_app_core->GetReg(3);
- ctx->reg[4] = Core::g_app_core->GetReg(4);
- ctx->reg[5] = Core::g_app_core->GetReg(5);
- ctx->reg[6] = Core::g_app_core->GetReg(6);
- ctx->reg[7] = Core::g_app_core->GetReg(7);
- ctx->reg[8] = Core::g_app_core->GetReg(8);
- ctx->reg[9] = Core::g_app_core->GetReg(9);
- ctx->reg[10] = Core::g_app_core->GetReg(10);
- ctx->reg[11] = Core::g_app_core->GetReg(11);
- ctx->reg[12] = Core::g_app_core->GetReg(12);
- ctx->reg[13] = Core::g_app_core->GetReg(13);
- ctx->reg[14] = Core::g_app_core->GetReg(14);
- ctx->reg[15] = Core::g_app_core->GetReg(15);
- ctx->pc = Core::g_app_core->GetPC();
- ctx->cpsr = Core::g_app_core->GetCPSR();
- }
- /// Loads a CPU context
- void __KernelLoadContext(ThreadContext *ctx) {
- Core::g_app_core->SetReg(0, ctx->reg[0]);
- Core::g_app_core->SetReg(1, ctx->reg[1]);
- Core::g_app_core->SetReg(2, ctx->reg[2]);
- Core::g_app_core->SetReg(3, ctx->reg[3]);
- Core::g_app_core->SetReg(4, ctx->reg[4]);
- Core::g_app_core->SetReg(5, ctx->reg[5]);
- Core::g_app_core->SetReg(6, ctx->reg[6]);
- Core::g_app_core->SetReg(7, ctx->reg[7]);
- Core::g_app_core->SetReg(8, ctx->reg[8]);
- Core::g_app_core->SetReg(9, ctx->reg[9]);
- Core::g_app_core->SetReg(10, ctx->reg[10]);
- Core::g_app_core->SetReg(11, ctx->reg[11]);
- Core::g_app_core->SetReg(12, ctx->reg[12]);
- Core::g_app_core->SetReg(13, ctx->reg[13]);
- Core::g_app_core->SetReg(14, ctx->reg[14]);
- Core::g_app_core->SetReg(15, ctx->reg[15]);
- Core::g_app_core->SetPC(ctx->pc);
- Core::g_app_core->SetCPSR(ctx->cpsr);
- }
- /// Switches thread context
- void __KernelSwitchContext(Thread *target, const char *reason) {
- u32 old_pc = 0;
- UID old_uid = 0;
- const char *old_name = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)";
- Thread *cur = __GetCurrentThread();
- if (cur) { // It might just have been deleted.
- __KernelSaveContext(&cur->context);
- old_pc = Core::g_app_core->GetPC();
- old_uid = cur->GetUID();
- // Normally this is taken care of in __KernelNextThread().
- if (cur->IsRunning())
- __KernelChangeReadyState(cur, old_uid, true);
- }
- if (target) {
- __SetCurrentThread(target, target->GetUID(), target->nt.name);
- __KernelChangeReadyState(target, g_current_thread, false);
- target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY;
- __KernelLoadContext(&target->context);
- } else {
- __SetCurrentThread(NULL, 0, NULL);
- }
- }
- /// Sets up the root (primary) thread of execution
- UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) {
- UID id;
- Thread *thread = __KernelCreateThread(id, module_id, "root", prio, Core::g_app_core->GetPC(),
- arg, Memory::SCRATCHPAD_VADDR_END, 0xFFFFFFFE, stack_size=stack_size);
- if (thread->current_stack.start == 0) {
- ERROR_LOG(KERNEL, "Unable to allocate stack for root thread.");
- }
- __KernelResetThread(thread, 0);
- Thread *prev_thread = __GetCurrentThread();
- if (prev_thread && prev_thread->IsRunning())
- __KernelChangeReadyState(g_current_thread, true);
- __SetCurrentThread(thread, id, "root");
- thread->nt.status = THREADSTATUS_RUNNING; // do not schedule
- strcpy(thread->nt.name, "root");
- __KernelLoadContext(&thread->context);
-
- // NOTE(bunnei): Not sure this is really correct, ignore args for now...
- //Core::g_app_core->SetReg(0, args);
- //Core::g_app_core->SetReg(13, (args + 0xf) & ~0xf); // Setup SP - probably not correct
- //u32 location = Core::g_app_core->GetReg(13); // SP
- //Core::g_app_core->SetReg(1, location);
-
- //if (argp)
- // Memory::Memcpy(location, argp, args);
- //// Let's assume same as starting a new thread, 64 bytes for safety/kernel.
- //Core::g_app_core->SetReg(13, Core::g_app_core->GetReg(13) - 64);
- return id;
- }
- void __KernelThreadingInit() {
- }
- void __KernelThreadingShutdown() {
- }
|