added initial kernel/thread modules

This commit is contained in:
bunnei 2014-05-09 22:11:18 -04:00
parent daf7a7880a
commit 6b264518a5
7 changed files with 548 additions and 0 deletions

View file

@ -22,6 +22,8 @@ set(SRCS core.cpp
hle/config_mem.cpp hle/config_mem.cpp
hle/coprocessor.cpp hle/coprocessor.cpp
hle/syscall.cpp hle/syscall.cpp
hle/kernel/kernel.cpp
hle/kernel/thread.cpp
hle/service/apt.cpp hle/service/apt.cpp
hle/service/gsp.cpp hle/service/gsp.cpp
hle/service/hid.cpp hle/service/hid.cpp

View file

@ -156,6 +156,8 @@
<ClCompile Include="hle\config_mem.cpp" /> <ClCompile Include="hle\config_mem.cpp" />
<ClCompile Include="hle\coprocessor.cpp" /> <ClCompile Include="hle\coprocessor.cpp" />
<ClCompile Include="hle\hle.cpp" /> <ClCompile Include="hle\hle.cpp" />
<ClCompile Include="hle\kernel\kernel.cpp" />
<ClCompile Include="hle\kernel\thread.cpp" />
<ClCompile Include="hle\service\apt.cpp" /> <ClCompile Include="hle\service\apt.cpp" />
<ClCompile Include="hle\service\gsp.cpp" /> <ClCompile Include="hle\service\gsp.cpp" />
<ClCompile Include="hle\service\hid.cpp" /> <ClCompile Include="hle\service\hid.cpp" />
@ -198,6 +200,8 @@
<ClInclude Include="hle\coprocessor.h" /> <ClInclude Include="hle\coprocessor.h" />
<ClInclude Include="hle\function_wrappers.h" /> <ClInclude Include="hle\function_wrappers.h" />
<ClInclude Include="hle\hle.h" /> <ClInclude Include="hle\hle.h" />
<ClInclude Include="hle\kernel\kernel.h" />
<ClInclude Include="hle\kernel\thread.h" />
<ClInclude Include="hle\service\apt.h" /> <ClInclude Include="hle\service\apt.h" />
<ClInclude Include="hle\service\gsp.h" /> <ClInclude Include="hle\service\gsp.h" />
<ClInclude Include="hle\service\hid.h" /> <ClInclude Include="hle\service\hid.h" />

View file

@ -28,6 +28,9 @@
<Filter Include="hle\service"> <Filter Include="hle\service">
<UniqueIdentifier>{812c5189-ca49-4704-b842-3ffad09092d3}</UniqueIdentifier> <UniqueIdentifier>{812c5189-ca49-4704-b842-3ffad09092d3}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="hle\kernel">
<UniqueIdentifier>{f2b132eb-caff-4b04-aaae-88d24393a711}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="arm\disassembler\arm_disasm.cpp"> <ClCompile Include="arm\disassembler\arm_disasm.cpp">
@ -114,6 +117,12 @@
<ClCompile Include="hle\config_mem.cpp"> <ClCompile Include="hle\config_mem.cpp">
<Filter>hle</Filter> <Filter>hle</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="hle\kernel\thread.cpp">
<Filter>hle\kernel</Filter>
</ClCompile>
<ClCompile Include="hle\kernel\kernel.cpp">
<Filter>hle\kernel</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="arm\disassembler\arm_disasm.h"> <ClInclude Include="arm\disassembler\arm_disasm.h">
@ -223,6 +232,12 @@
<ClInclude Include="hle\config_mem.h"> <ClInclude Include="hle\config_mem.h">
<Filter>hle</Filter> <Filter>hle</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="hle\kernel\thread.h">
<Filter>hle\kernel</Filter>
</ClInclude>
<ClInclude Include="hle\kernel\kernel.h">
<Filter>hle\kernel</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Text Include="CMakeLists.txt" /> <Text Include="CMakeLists.txt" />

View file

@ -0,0 +1,142 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include <string.h>
#include "common/common.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
KernelObjectPool g_kernel_objects;
void __KernelInit() {
__KernelThreadingInit();
}
void __KernelShutdown() {
__KernelThreadingShutdown();
}
KernelObjectPool::KernelObjectPool() {
memset(occupied, 0, sizeof(bool) * MAX_COUNT);
next_id = INITIAL_NEXT_ID;
}
UID KernelObjectPool::Create(KernelObject *obj, int range_bottom, int range_top) {
if (range_top > MAX_COUNT) {
range_top = MAX_COUNT;
}
if (next_id >= range_bottom && next_id < range_top) {
range_bottom = next_id++;
}
for (int i = range_bottom; i < range_top; i++) {
if (!occupied[i]) {
occupied[i] = true;
pool[i] = obj;
pool[i]->uid = i + HANDLE_OFFSET;
return i + HANDLE_OFFSET;
}
}
ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use.");
return 0;
}
bool KernelObjectPool::IsValid(UID handle)
{
int index = handle - HANDLE_OFFSET;
if (index < 0)
return false;
if (index >= MAX_COUNT)
return false;
return occupied[index];
}
void KernelObjectPool::Clear()
{
for (int i = 0; i < MAX_COUNT; i++)
{
//brutally clear everything, no validation
if (occupied[i])
delete pool[i];
occupied[i] = false;
}
memset(pool, 0, sizeof(KernelObject*)*MAX_COUNT);
next_id = INITIAL_NEXT_ID;
}
KernelObject *&KernelObjectPool::operator [](UID handle)
{
_dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
return pool[handle - HANDLE_OFFSET];
}
void KernelObjectPool::List() {
for (int i = 0; i < MAX_COUNT; i++) {
if (occupied[i]) {
if (pool[i]) {
INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName(),
pool[i]->GetName());
}
}
}
}
int KernelObjectPool::GetCount()
{
int count = 0;
for (int i = 0; i < MAX_COUNT; i++)
{
if (occupied[i])
count++;
}
return count;
}
KernelObject *KernelObjectPool::CreateByIDType(int type) {
// Used for save states. This is ugly, but what other way is there?
switch (type) {
//case SCE_KERNEL_TMID_Alarm:
// return __KernelAlarmObject();
//case SCE_KERNEL_TMID_EventFlag:
// return __KernelEventFlagObject();
//case SCE_KERNEL_TMID_Mbox:
// return __KernelMbxObject();
//case SCE_KERNEL_TMID_Fpl:
// return __KernelMemoryFPLObject();
//case SCE_KERNEL_TMID_Vpl:
// return __KernelMemoryVPLObject();
//case PPSSPP_KERNEL_TMID_PMB:
// return __KernelMemoryPMBObject();
//case PPSSPP_KERNEL_TMID_Module:
// return __KernelModuleObject();
//case SCE_KERNEL_TMID_Mpipe:
// return __KernelMsgPipeObject();
//case SCE_KERNEL_TMID_Mutex:
// return __KernelMutexObject();
//case SCE_KERNEL_TMID_LwMutex:
// return __KernelLwMutexObject();
//case SCE_KERNEL_TMID_Semaphore:
// return __KernelSemaphoreObject();
//case SCE_KERNEL_TMID_Callback:
// return __KernelCallbackObject();
//case SCE_KERNEL_TMID_Thread:
// return __KernelThreadObject();
//case SCE_KERNEL_TMID_VTimer:
// return __KernelVTimerObject();
//case SCE_KERNEL_TMID_Tlspl:
// return __KernelTlsplObject();
//case PPSSPP_KERNEL_TMID_File:
// return __KernelFileNodeObject();
//case PPSSPP_KERNEL_TMID_DirList:
// return __KernelDirListingObject();
default:
ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
return NULL;
}
}

View file

@ -0,0 +1,121 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
typedef u32 UID;
class KernelObjectPool;
class KernelObject {
friend class KernelObjectPool;
u32 uid;
public:
virtual ~KernelObject() {}
UID GetUID() const { return uid; }
virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; }
virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; }
virtual int GetIDType() const = 0;
//virtual void GetQuickInfo(char *ptr, int size);
};
class KernelObjectPool {
public:
KernelObjectPool();
~KernelObjectPool() {}
// Allocates a UID within the range and inserts the object into the map.
UID Create(KernelObject *obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF);
static KernelObject *CreateByIDType(int type);
template <class T>
u32 Destroy(UID handle) {
u32 error;
if (Get<T>(handle, error)) {
occupied[handle - handleOffset] = false;
delete pool[handle - handleOffset];
}
return error;
};
bool IsValid(UID handle);
template <class T>
T* Get(UID handle, u32& outError) {
if (handle < handleOffset || handle >= handleOffset + maxCount || !occupied[handle - handleOffset]) {
// Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
if (handle != 0 && (u32)handle != 0x80020001) {
WARN_LOG(SCEKERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
}
outError = T::GetMissingErrorCode();
return 0;
} else {
// Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
// it just acted as a static case and everything worked. This means that we will never
// see the Wrong type object error below, but we'll just have to live with that danger.
T* t = static_cast<T*>(pool[handle - handleOffset]);
if (t == 0 || t->GetIDType() != T::GetStaticIDType()) {
WARN_LOG(SCEKERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
outError = T::GetMissingErrorCode();
return 0;
}
outError = SCE_KERNEL_ERROR_OK;
return t;
}
}
// ONLY use this when you know the handle is valid.
template <class T>
T *GetFast(UID handle) {
const UID realHandle = handle - handleOffset;
_dbg_assert_(SCEKERNEL, realHandle >= 0 && realHandle < maxCount && occupied[realHandle]);
return static_cast<T *>(pool[realHandle]);
}
template <class T, typename ArgT>
void Iterate(bool func(T *, ArgT), ArgT arg) {
int type = T::GetStaticIDType();
for (int i = 0; i < maxCount; i++)
{
if (!occupied[i])
continue;
T *t = static_cast<T *>(pool[i]);
if (t->GetIDType() == type) {
if (!func(t, arg))
break;
}
}
}
bool GetIDType(UID handle, int *type) const {
if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
!occupied[handle - HANDLE_OFFSET]) {
ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
return false;
}
KernelObject *t = pool[handle - HANDLE_OFFSET];
*type = t->GetIDType();
return true;
}
KernelObject *&operator [](UID handle);
void List();
void Clear();
int GetCount();
private:
enum {
MAX_COUNT = 0x1000,
HANDLE_OFFSET = 0x100,
INITIAL_NEXT_ID = 0x10,
};
KernelObject *pool[MAX_COUNT];
bool occupied[MAX_COUNT];
int next_id;
};
extern KernelObjectPool g_kernel_objects;

View file

@ -0,0 +1,228 @@
// 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/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
// Real CTR struct, don't change the fields.
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)
};
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 int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; }
//int GetIDType() const { return SCE_KERNEL_TMID_Thread; }
//bool AllocateStack(u32 &stack_size) {
// FreeStack();
// bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0;
// if (nt.attr & PSP_THREAD_ATTR_KERNEL)
// {
// // Allocate stacks for kernel threads (idle) in kernel RAM
// currentStack.start = kernelMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str());
// }
// else
// {
// currentStack.start = userMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str());
// }
// if (currentStack.start == (u32)-1)
// {
// currentStack.start = 0;
// nt.initialStack = 0;
// ERROR_LOG(KERNEL, "Failed to allocate stack for thread");
// return false;
// }
// nt.initialStack = currentStack.start;
// nt.stack_size = stack_size;
// return true;
//}
//bool FillStack() {
// // Fill the stack.
// if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) {
// Memory::Memset(currentStack.start, 0xFF, nt.stack_size);
// }
// context.r[MIPS_REG_SP] = currentStack.start + nt.stack_size;
// currentStack.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 (currentStack.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(currentStack.start);
// }
// else {
// userMemory.Free(currentStack.start);
// }
// currentStack.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(currentStack);
// currentStack.start = stack;
// currentStack.end = stack + size;
// nt.initialStack = currentStack.start;
// nt.stack_size = currentStack.end - currentStack.start;
// // We still drop the threadID at the bottom and fill it, but there's no k0.
// Memory::Memset(currentStack.start, 0xFF, nt.stack_size);
// Memory::Write_U32(GetUID(), nt.initialStack);
// return true;
//}
//bool PopExtendedStack() {
// if (pushed_stacks.size() == 0) {
// return false;
// }
// userMemory.Free(currentStack.start);
// currentStack = pushed_stacks.back();
// pushed_stacks.pop_back();
// nt.initialStack = currentStack.start;
// nt.stack_size = currentStack.end - currentStack.start;
// return true;
//}
Thread() {
currentStack.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 currentStack;
// 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 __KernelThreadingInit() {
}
void __KernelThreadingShutdown() {
}
//const char *__KernelGetThreadName(UID threadID);
//
//void __KernelSaveContext(ThreadContext *ctx);
//void __KernelLoadContext(ThreadContext *ctx);
//void __KernelSwitchContext(Thread *target, const char *reason);

View file

@ -0,0 +1,36 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
enum ThreadStatus {
THREADSTATUS_RUNNING = 1,
THREADSTATUS_READY = 2,
THREADSTATUS_WAIT = 4,
THREADSTATUS_SUSPEND = 8,
THREADSTATUS_DORMANT = 16,
THREADSTATUS_DEAD = 32,
THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND
};
struct ThreadContext {
void reset();
u32 reg[16];
u32 cpsr;
u32 pc;
};
void __KernelThreadingInit();
void __KernelThreadingShutdown();
//const char *__KernelGetThreadName(SceUID threadID);
//
//void __KernelSaveContext(ThreadContext *ctx);
//void __KernelLoadContext(ThreadContext *ctx);
//void __KernelSwitchContext(Thread *target, const char *reason);