// Copyright (c) 2012- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #pragma once #include #include #include "Common/CommonTypes.h" #include "Common/Log.h" #include "Common/Swap.h" class PointerWrap; // If you add to this, make sure to check KernelObjectPool::CreateByIDType(). enum TMIDType : int { SCE_KERNEL_TMID_Thread = 1, SCE_KERNEL_TMID_Semaphore = 2, SCE_KERNEL_TMID_EventFlag = 3, SCE_KERNEL_TMID_Mbox = 4, SCE_KERNEL_TMID_Vpl = 5, SCE_KERNEL_TMID_Fpl = 6, SCE_KERNEL_TMID_Mpipe = 7, SCE_KERNEL_TMID_Callback = 8, SCE_KERNEL_TMID_ThreadEventHandler = 9, SCE_KERNEL_TMID_Alarm = 10, SCE_KERNEL_TMID_VTimer = 11, SCE_KERNEL_TMID_Mutex = 12, SCE_KERNEL_TMID_LwMutex = 13, SCE_KERNEL_TMID_Tlspl = 14, SCE_KERNEL_TMID_SleepThread = 64, SCE_KERNEL_TMID_DelayThread = 65, SCE_KERNEL_TMID_SuspendThread = 66, SCE_KERNEL_TMID_DormantThread = 67, // This is kept for old savestates. Not the real value. SCE_KERNEL_TMID_Tlspl_v0 = 0x1001, // Not official, but need ids for save states. PPSSPP_KERNEL_TMID_Module = 0x100001, PPSSPP_KERNEL_TMID_PMB = 0x100002, PPSSPP_KERNEL_TMID_File = 0x100003, PPSSPP_KERNEL_TMID_DirList = 0x100004, PPSSPP_KERNEL_TMID_Heap = 0x100005, }; typedef int SceUID; typedef unsigned int SceSize; typedef int SceSSize; typedef unsigned char SceUChar; typedef unsigned int SceUInt; typedef int SceMode; typedef s64 SceOff; typedef u64 SceIores; typedef s32_le SceUID_le; typedef u32_le SceSize_le; typedef s32_le SceSSize_le; typedef u32_le SceUInt_le; typedef s32_le SceMode_le; typedef s64_le SceOff_le; typedef s64_le SceIores_le; struct SceKernelLoadExecParam { SceSize_le size; // Size of the structure SceSize_le args; // Size of the arg string u32_le argp; // Pointer to the arg string u32_le keyp; // Encryption key? Not yet used }; void __KernelInit(); void __KernelShutdown(); void __KernelDoState(PointerWrap &p); bool __KernelIsRunning(); bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param); // For crash reporting. std::string __KernelStateSummary(); int sceKernelLoadExec(const char *filename, u32 paramPtr); void sceKernelExitGame(); void sceKernelExitGameWithStatus(); u32 sceKernelDevkitVersion(); u32 sceKernelRegisterKprintfHandler(); int sceKernelRegisterDefaultExceptionHandler(); u32 sceKernelFindModuleByName(const char *name); void sceKernelSetGPO(u32 ledAddr); u32 sceKernelGetGPI(); int sceKernelDcacheInvalidateRange(u32 addr, int size); int sceKernelDcacheWritebackAll(); int sceKernelDcacheWritebackRange(u32 addr, int size); int sceKernelDcacheWritebackInvalidateRange(u32 addr, int size); int sceKernelDcacheWritebackInvalidateAll(); int sceKernelGetThreadStackFreeSize(SceUID threadID); u32 sceKernelIcacheInvalidateAll(); u32 sceKernelIcacheClearAll(); int sceKernelIcacheInvalidateRange(u32 addr, int size); #define KERNELOBJECT_MAX_NAME_LENGTH 31 class KernelObjectPool; class KernelObject { friend class KernelObjectPool; u32 uid; public: virtual ~KernelObject() {} SceUID 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); virtual void GetLongInfo(char *ptr, int bufSize) const; // Implement the following in all subclasses: // static u32 GetMissingErrorCode() // static int GetStaticIDType() virtual void DoState(PointerWrap &p) { _dbg_assert_msg_(false, "Unable to save state: bad kernel object."); } }; // TODO: Delete the "occupied" array, rely on non-zero pool entries? class KernelObjectPool { public: KernelObjectPool(); ~KernelObjectPool() {} // Allocates a UID within the range and inserts the object into the map. SceUID Create(KernelObject *obj, int rangeBottom = initialNextID, int rangeTop = 0x7fffffff); void DoState(PointerWrap &p); static KernelObject *CreateByIDType(int type); template u32 Destroy(SceUID handle) { u32 error; if (Get(handle, error)) { int index = handle - handleOffset; occupied[index] = false; delete pool[index]; pool[index] = nullptr; } return error; }; bool IsValid(SceUID handle) const { int index = handle - handleOffset; if (index < 0 || index >= maxCount) return false; else return occupied[index]; } template bool Is(SceUID handle) const { int index = handle - handleOffset; if (index < 0 || index >= maxCount) return false; else return occupied[index] && pool[handle - handleOffset]->GetIDType() == T::GetStaticIDType(); } template T* Get(SceUID handle, u32 &outError) { if (handle < handleOffset || handle >= handleOffset+maxCount || !occupied[handle-handleOffset]) { outError = T::GetMissingErrorCode(); return nullptr; } else { // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally, // it just acted as a static cast 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(pool[handle - handleOffset]); if (t == nullptr || t->GetIDType() != T::GetStaticIDType()) { WARN_LOG(Log::sceKernel, "Kernel: Wrong object type for %d (%08x), was %s, should have been %s", handle, handle, t ? t->GetTypeName() : "null", T::GetStaticTypeName()); outError = T::GetMissingErrorCode(); return nullptr; } outError = 0; // SCE_KERNEL_ERROR_OK but don't want to include the header here. return t; } } // ONLY use this when you KNOW the handle is valid. template T *GetFast(SceUID handle) { const SceUID realHandle = handle - handleOffset; _dbg_assert_(realHandle >= 0 && realHandle < maxCount && occupied[realHandle]); return static_cast(pool[realHandle]); } template void Iterate(F func) { int type = T::GetStaticIDType(); for (int i = 0; i < maxCount; i++) { if (!occupied[i]) continue; T *t = static_cast(pool[i]); if (t->GetIDType() == type) { if (!func(i + handleOffset, t)) break; } } } int ListIDType(int type, SceUID_le *uids, int count) const { int total = 0; for (int i = 0; i < maxCount; i++) { if (!occupied[i]) { continue; } if (pool[i]->GetIDType() == type) { if (total < count) { *uids++ = pool[i]->GetUID(); } ++total; } } return total; } bool GetIDType(SceUID handle, int *type) const { if (handle < handleOffset || handle >= handleOffset+maxCount || !occupied[handle-handleOffset]) { ERROR_LOG(Log::sceKernel, "Kernel: Bad object handle %i (%08x)", handle, handle); return false; } KernelObject *t = pool[handle - handleOffset]; *type = t->GetIDType(); return true; } void List(); void Clear(); int GetCount() const; enum { maxCount = 4096, handleOffset = 0x100, initialNextID = 0x10 }; private: KernelObject *pool[maxCount]; bool occupied[maxCount]; int nextID; }; extern KernelObjectPool kernelObjects; typedef std::pair KernelStatsSyscall; struct KernelStats { void Reset() { ResetFrame(); } void ResetFrame() { msInSyscalls = 0; slowestSyscallTime = 0; slowestSyscallName = 0; summedMsInSyscalls.clear(); summedSlowestSyscallTime = 0; summedSlowestSyscallName = 0; } double msInSyscalls; double slowestSyscallTime; const char *slowestSyscallName; std::map summedMsInSyscalls; double summedSlowestSyscallTime; const char *summedSlowestSyscallName; }; extern KernelStats kernelStats; extern u32 registeredExitCbId; extern u32 g_GPOBits; extern u32 g_GPIBits; void Register_ThreadManForUser(); void Register_ThreadManForKernel(); void Register_LoadExecForUser(); void Register_LoadExecForKernel(); void Register_UtilsForKernel(); // returns nullptr if not found. const char *KernelErrorToString(u32 err);