Add all the files

This commit is contained in:
Exzap 2022-08-22 22:21:23 +02:00
parent e3db07a16a
commit d60742f52b
1445 changed files with 430238 additions and 0 deletions

129
src/Cafe/OS/RPL/elf.cpp Normal file
View file

@ -0,0 +1,129 @@
#include <zlib.h>
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "util/VirtualHeap/VirtualHeap.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
typedef struct
{
/* +0x00 */ uint32be magic;
/* +0x04 */ uint8 eiClass;
/* +0x05 */ uint8 eiData;
/* +0x06 */ uint8 eiVersion;
/* +0x07 */ uint8 eiOSABI;
/* +0x08 */ uint8 eiOSABIVersion;
/* +0x09 */ uint8 eiPadding[7];
/* +0x10 */ uint16be eType;
/* +0x12 */ uint16be eMachine;
/* +0x14 */ uint32be eVersion;
/* +0x18 */ uint32be entrypoint;
/* +0x1C */ uint32be phOffset;
/* +0x20 */ uint32be shOffset;
/* +0x24 */ uint32be eFlags;
/* +0x28 */ uint16be eHeaderSize;
/* +0x2A */ uint16be ePHEntrySize;
/* +0x2C */ uint16be ePHNum;
/* +0x2E */ uint16be eSHEntrySize;
/* +0x30 */ uint16be eSHNum;
/* +0x32 */ uint16be eShStrIndex;
}elfHeader_t;
static_assert(sizeof(elfHeader_t) == 0x34, "");
typedef struct
{
/* +0x00 */ uint32be nameOffset;
/* +0x04 */ uint32be shType;
/* +0x08 */ uint32be shFlags;
/* +0x0C */ uint32be shAddr;
/* +0x10 */ uint32be shOffset;
/* +0x14 */ uint32be shSize;
/* +0x18 */ uint32be shLink;
/* +0x1C */ uint32be shInfo;
/* +0x20 */ uint32be shAddrAlign;
/* +0x24 */ uint32be shEntSize;
}elfSectionEntry_t;
static_assert(sizeof(elfSectionEntry_t) == 0x28, "");
// Map elf into memory
uint32 ELF_LoadFromMemory(uint8* elfData, sint32 size, const char* name)
{
elfHeader_t* header = (elfHeader_t*)elfData;
uint32 sectionCount = header->eSHNum;
uint32 sectionTableOffset = header->shOffset;
uint32 sectionTableEntrySize = header->eSHEntrySize;
elfSectionEntry_t* sectionTable = (elfSectionEntry_t*)(elfData + (uint32)header->shOffset);
memory_enableHBLELFCodeArea();
for (uint32 i = 0; i < sectionCount; i++)
{
debug_printf("%02d Addr %08x Size %08x Offs %08x Flags %08x Type %08x EntSize %08x\n", i, (uint32)sectionTable[i].shAddr, (uint32)sectionTable[i].shSize, (uint32)sectionTable[i].shOffset, (uint32)sectionTable[i].shFlags, (uint32)sectionTable[i].shType, (uint32)sectionTable[i].shEntSize);
uint32 shAddr = (uint32)sectionTable[i].shAddr;
uint32 shSize = (uint32)sectionTable[i].shSize;
uint32 shOffset = (uint32)sectionTable[i].shOffset;
uint32 shType = (uint32)sectionTable[i].shType;
if (shOffset > (uint32)size)
{
forceLog_printf("ELF section %d out of bounds", i);
continue;
}
uint32 copySize = std::min(shSize, size - shOffset);
// 0x00802000
if (shAddr != 0)
{
if (shType == SHT_NOBITS)
{
memset(memory_getPointerFromVirtualOffset(shAddr), 0, shSize);
}
else
{
memcpy(memory_getPointerFromVirtualOffset(shAddr), elfData + shOffset, copySize);
}
// SHT_NOBITS
}
}
return header->entrypoint;
}
// From Homebrew Launcher:
//#define MEM_BASE (0x00800000)
//#define ELF_DATA_ADDR (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x00))
//#define ELF_DATA_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x04))
//#define HBL_CHANNEL (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x08))
//#define RPX_MAX_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x0C))
//#define RPX_MAX_CODE_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x10))
//#define MAIN_ENTRY_ADDR (*(volatile unsigned int*)(MEM_BASE + 0x1400 + 0x00))
//#define OS_FIRMWARE (*(volatile unsigned int*)(MEM_BASE + 0x1400 + 0x04))
//
//#define OS_SPECIFICS ((OsSpecifics*)(MEM_BASE + 0x1500))
//
//#define MEM_AREA_TABLE ((s_mem_area*)(MEM_BASE + 0x1600))
//typedef struct _OsSpecifics
//{
// unsigned int addr_OSDynLoad_Acquire;
// unsigned int addr_OSDynLoad_FindExport;
// unsigned int addr_OSTitle_main_entry;
//
// unsigned int addr_KernSyscallTbl1;
// unsigned int addr_KernSyscallTbl2;
// unsigned int addr_KernSyscallTbl3;
// unsigned int addr_KernSyscallTbl4;
// unsigned int addr_KernSyscallTbl5;
//
// int(*LiWaitIopComplete)(int, int *);
// int(*LiWaitIopCompleteWithInterrupts)(int, int *);
// unsigned int addr_LiWaitOneChunk;
// unsigned int addr_PrepareTitle_hook;
// unsigned int addr_sgIsLoadingBuffer;
// unsigned int addr_gDynloadInitialized;
// unsigned int orig_LiWaitOneChunkInstr;
//} OsSpecifics;

2389
src/Cafe/OS/RPL/rpl.cpp Normal file

File diff suppressed because it is too large Load diff

55
src/Cafe/OS/RPL/rpl.h Normal file
View file

@ -0,0 +1,55 @@
#pragma once
struct RPLModule;
#define RPL_INVALID_HANDLE (0xFFFFFFFF)
void RPLLoader_InitState();
void RPLLoader_ResetState();
uint8* RPLLoader_AllocateTrampolineCodeSpace(sint32 size);
MPTR RPLLoader_AllocateCodeSpace(uint32 size, uint32 alignment);
uint32 RPLLoader_GetMaxCodeOffset();
uint32 RPLLoader_GetDataAllocatorAddr();
__declspec(dllexport) RPLModule* rpl_loadFromMem(uint8* rplData, sint32 size, char* name);
uint32 rpl_mapHLEImport(RPLModule* rplLoaderContext, const char* rplName, const char* funcName, bool functionMustExist);
void RPLLoader_Link();
MPTR RPLLoader_FindRPLExport(RPLModule* rplLoaderContext, const char* symbolName, bool isData);
uint32 RPLLoader_GetModuleEntrypoint(RPLModule* rplLoaderContext);
void RPLLoader_SetMainModule(RPLModule* rplLoaderContext);
uint32 RPLLoader_GetMainModuleHandle();
void RPLLoader_CallEntrypoints();
void RPLLoader_NotifyControlPassedToApplication();
void RPLLoader_AddDependency(const char* name);
void RPLLoader_RemoveDependency(uint32 handle);
void RPLLoader_UpdateDependencies();
uint32 RPLLoader_GetHandleByModuleName(const char* name);
uint32 RPLLoader_GetMaxTLSModuleIndex();
bool RPLLoader_GetTLSDataByTLSIndex(sint16 tlsModuleIndex, uint8** tlsData, sint32* tlsSize);
uint32 RPLLoader_FindModuleOrHLEExport(uint32 moduleHandle, bool isData, const char* exportName);
uint32 RPLLoader_GetSDA1Base();
uint32 RPLLoader_GetSDA2Base();
sint32 RPLLoader_GetModuleCount();
RPLModule** RPLLoader_GetModuleList();
MEMPTR<void> RPLLoader_AllocateCodeCaveMem(uint32 alignment, uint32 size);
void RPLLoader_ReleaseCodeCaveMem(MEMPTR<void> addr);
// exports
uint32 RPLLoader_MakePPCCallable(void(*ppcCallableExport)(struct PPCInterpreter_t* hCPU));
// elf loader
uint32 ELF_LoadFromMemory(uint8* elfData, sint32 size, const char* name);

View file

@ -0,0 +1,21 @@
#include "Cafe/OS/RPL/rpl_debug_symbols.h"
std::map<MPTR, rplDebugSymbolBase*> map_DebugSymbols;
void rplDebugSymbol_createComment(MPTR address, const wchar_t* comment)
{
auto new_comment = new rplDebugSymbolComment();
new_comment->type = RplDebugSymbolComment;
new_comment->comment = comment;
map_DebugSymbols[address] = new_comment;
}
rplDebugSymbolBase* rplDebugSymbol_getForAddress(MPTR address)
{
return map_DebugSymbols[address];
}
const std::map<MPTR, rplDebugSymbolBase*>& rplDebugSymbol_getSymbols()
{
return map_DebugSymbols;
}

View file

@ -0,0 +1,23 @@
#pragma once
#include <map>
enum RplDebugSymbolType : uint8
{
RplDebugSymbolComment = 0,
};
struct rplDebugSymbolBase
{
RplDebugSymbolType type;
rplDebugSymbolBase* next;
};
struct rplDebugSymbolComment : rplDebugSymbolBase
{
std::wstring comment;
};
void rplDebugSymbol_createComment(MPTR address, const wchar_t* comment);
rplDebugSymbolBase* rplDebugSymbol_getForAddress(MPTR address);
const std::map<MPTR, rplDebugSymbolBase*>& rplDebugSymbol_getSymbols();

View file

@ -0,0 +1,258 @@
#pragma once
#include "util/ChunkedHeap/ChunkedHeap.h"
#define RPL_MODULE_NAME_LENGTH 64
#define RPL_MODULE_PATH_LENGTH 256
// types
#define SHT_RPL_EXPORTS (0x80000001)
#define SHT_RPL_IMPORTS (0x80000002)
#define SHT_RPL_CRCS (0x80000003)
#define SHT_RPL_FILEINFO (0x80000004)
#define SHT_PROGBITS (0x00000001)
#define SHT_SYMTAB (0x00000002)
#define SHT_STRTAB (0x00000003)
#define SHT_RELA (0x00000004)
#define SHT_HASH (0x00000005)
#define SHT_DYNAMIC (0x00000006)
#define SHT_NOTE (0x00000007)
#define SHT_NOBITS (0x00000008) // this section contains no data
#define SHT_REL (0x00000009)
#define SHT_SHLIB (0x0000000A)
#define SHT_DYNSYM (0x0000000B)
// flags
#define SHF_EXECUTE 0x00000004
#define SHF_RPL_COMPRESSED 0x08000000
#define SHF_TLS 0x04000000
// relocs
#define RPL_RELOC_ADDR32 1
#define RPL_RELOC_LO16 4
#define RPL_RELOC_HI16 5
#define RPL_RELOC_HA16 6
#define RPL_RELOC_REL24 10
#define RPL_RELOC_REL14 11
#define R_PPC_DTPMOD32 68
#define R_PPC_DTPREL32 78
#define R_PPC_REL16_HA 251
#define R_PPC_REL16_HI 252
#define R_PPC_REL16_LO 253
#define HLE_MODULE_PTR ((RPLModule*)-1)
typedef struct
{
/* +0x00 */ uint32be relocOffset;
/* +0x04 */ uint32be symbolIndexAndType;
/* +0x08 */ uint32be relocAddend;
}rplRelocNew_t;
typedef struct
{
/* +0x00 */ uint32be nameOffset;
/* +0x04 */ uint32be type;
/* +0x08 */ uint32be flags;
/* +0x0C */ uint32be virtualAddress;
/* +0x10 */ uint32be fileOffset;
/* +0x14 */ uint32be sectionSize;
/* +0x18 */ uint32be symtabSectionIndex;
/* +0x1C */ uint32be relocTargetSectionIndex;
/* +0x20 */ uint32be alignment;
/* +0x24 */ uint32be ukn24; // for symtab: Size of each symbol entry
}rplSectionEntryNew_t;
typedef struct
{
/* +0x00 */ uint32be magic1;
/* +0x04 */ uint8 version04; // probably version?
/* +0x05 */ uint8 ukn05; // probably version?
/* +0x06 */ uint8 ukn06; // probably version?
/* +0x07 */ uint8 magic2_0; // part of second magic
/* +0x08 */ uint8 magic2_1; // part of second magic
/* +0x09 */ uint8 ukn09;
/* +0x0A */ uint8 ukn0A;
/* +0x0B */ uint8 ukn0B;
/* +0x0C */ uint32be dataRegionSize;
/* +0x10 */ uint8 ukn10;
/* +0x11 */ uint8 ukn11;
/* +0x12 */ uint16be ukn12;
/* +0x14 */ uint32be ukn14;
/* +0x18 */ uint32be entrypoint;
/* +0x1C */ uint32be ukn1C;
/* +0x20 */ uint32be sectionTableOffset;
/* +0x24 */ uint32be ukn24;
/* +0x28 */ uint16be ukn28;
/* +0x2A */ uint16be programHeaderTableEntrySize;
/* +0x2C */ uint16be programHeaderTableEntryCount;
/* +0x2E */ uint16be sectionTableEntrySize;
/* +0x30 */ uint16be sectionTableEntryCount;
/* +0x32 */ uint16be nameSectionIndex;
}rplHeaderNew_t;
static_assert(offsetof(rplHeaderNew_t, dataRegionSize) == 0xC);
static_assert(offsetof(rplHeaderNew_t, programHeaderTableEntrySize) == 0x2A);
typedef struct
{
/* +0x00 */ uint32be fileInfoMagic; // always 0xCAFE0402
/* +0x04 */ uint32be textRegionSize; // text region size
/* +0x08 */ uint32be ukn08; // base align text
/* +0x0C */ uint32be dataRegionSize; // size of data sections
/* +0x10 */ uint32be baseAlign; // base align data?
/* +0x14 */ uint32be ukn14;
/* +0x18 */ uint32be ukn18;
/* +0x1C */ uint32be ukn1C;
/* +0x20 */ uint32be trampolineAdjustment;
/* +0x24 */ uint32be sdataBase1;
/* +0x28 */ uint32be sdataBase2;
/* +0x2C */ uint32be ukn2C;
/* +0x30 */ uint32be ukn30;
/* +0x34 */ uint32be ukn34;
/* +0x38 */ uint32be ukn38;
/* +0x3C */ uint32be ukn3C;
/* +0x40 */ uint32be toolkitVersion;
/* +0x44 */ uint32be ukn44;
/* +0x48 */ uint32be ukn48;
/* +0x4C */ uint32be ukn4C;
/* +0x50 */ uint32be ukn50;
/* +0x54 */ uint32be ukn54;
/* +0x58 */ sint16be tlsModuleIndex;
}RPLFileInfoData;
static_assert(offsetof(RPLFileInfoData, tlsModuleIndex) == 0x58);
typedef struct
{
//uint32 address;
void* ptr;
}rplSectionAddressEntry_t;
typedef struct
{
uint32be virtualOffset;
uint32be nameOffset;
}rplExportTableEntry_t;
struct RPLModule
{
uint32 ukn00; // pointer to shared memory region? (0xEFE01000)
uint32 ukn04; // related to text region size?
char* moduleNamePtr__depr; // converted to lower case
uint32 moduleNameLength__depr; // length of module name
uint32 moduleNameSize; // aligned alloc size, not the same as actual length
uint32 padding14;
uint32 padding18;
rplHeaderNew_t rplHeader;
rplSectionEntryNew_t* sectionTablePtr; // copy of section table
RPLFileInfoData* fileInfoPtr__depr{}; // copy of fileinfo section
uint32 fileInfoSize__depr{}; // size of fileInfo section
uint32 fileInfoAllocSize__depr{}; // aligned alloc size
uint32be* crcTablePtr_depr{}; // copy of CRC section
uint32 crcTableAllocSize_depr{};
uint32 entrypoint;
uint8* rplData_depr; // Cemuhook might still read this
MPTR textRegionTemp; // temporary memory for text section?
MEMPTR<void> regionMappingBase_text; // base destination address for text region
MPTR regionMappingBase_data; // base destination address for data region
MPTR regionMappingBase_loaderInfo; // base destination address for loaderInfo region
uint8* tempRegionPtr;
uint32 tempRegionAllocSize;
rplSectionAddressEntry_t* sectionAddressTable__depr;
uint32 sectionAddressTableSize__depr;
uint32 exportDCount;
rplExportTableEntry_t* exportDDataPtr;
uint32 exportFCount;
rplExportTableEntry_t* exportFDataPtr;
/* above are hardcoded in Cemuhook */
std::string moduleName2;
std::vector<rplSectionAddressEntry_t> sectionAddressTable2;
uint32 tlsStartAddress;
uint32 tlsEndAddress;
uint32 regionSize_text;
uint32 regionSize_data;
uint32 regionSize_loaderInfo;
uint32 patchCRC; // Cemuhook style module crc for patches.txt
// trampoline management
ChunkedFlatAllocator<16 * 1024> heapTrampolineArea;
std::unordered_map<MPTR, MPTR> trampolineMap;
// section data
std::vector<uint8> sectionData_fileInfo;
std::vector<uint8> sectionData_crc;
// parsed FILEINFO
struct
{
uint32 textRegionSize; // size of region containing all text sections
//uint32 ukn08; // base align text?
uint32 dataRegionSize; // size of region containing all data sections
uint32 baseAlign;
uint32 ukn14;
uint32 trampolineAdjustment;
uint32 ukn4C;
sint16 tlsModuleIndex;
uint32 sdataBase1;
uint32 sdataBase2;
}fileInfo;
// parsed CRC
std::vector<uint32> crcTable;
uint32 GetSectionCRC(size_t sectionIndex) const
{
if (sectionIndex >= crcTable.size())
return 0;
return crcTable[sectionIndex];
}
// state
bool isLinked; // set to true if _linkModule was called on this module
bool entrypointCalled; // set if entrypoint was called
// allocator
betype<MPTR> funcAlloc;
betype<MPTR> funcFree;
// replaces rplData ptr
std::span<uint8> RPLRawData;
bool debugSectionLoadMask[128] = { false };
bool hasError{ false };
};
typedef struct
{
char modulename[RPL_MODULE_NAME_LENGTH];
char filepath[RPL_MODULE_PATH_LENGTH];
bool loadAttempted;
//bool isHLEModule; // determined to be a HLE module
RPLModule* rplLoaderContext; // context of loaded module
sint32 referenceCount;
uint32 coreinitHandle; // fake handle for coreinit
sint16 tlsModuleIndex; // tls module index assigned to this dependency
}rplDependency_t;
RPLModule* RPLLoader_FindModuleByCodeAddr(uint32 addr);
RPLModule* RPLLoader_FindModuleByDataAddr(uint32 addr);
RPLModule* RPLLoader_FindModuleByName(std::string module);

View file

@ -0,0 +1,150 @@
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
struct rplSymbolLib_t
{
char* libName;
rplSymbolLib_t* next;
};
struct
{
rplSymbolLib_t* libs;
std::mutex m_symbolStorageMutex;
std::unordered_map<uint32, RPLStoredSymbol*> map_symbolByAddress;
// allocator for strings
char* strAllocatorBlock;
sint32 strAllocatorOffset;
std::vector<void*> list_strAllocatedBlocks;
}rplSymbolStorage = { 0 };
#define STR_ALLOC_BLOCK_SIZE (128*1024) // allocate 128KB blocks at once
char* rplSymbolStorage_allocDupString(const char* str)
{
sint32 len = (sint32)strlen(str);
if (rplSymbolStorage.strAllocatorBlock == nullptr || (rplSymbolStorage.strAllocatorOffset + len + 1) >= STR_ALLOC_BLOCK_SIZE)
{
// allocate new block
rplSymbolStorage.strAllocatorBlock = (char*)malloc(STR_ALLOC_BLOCK_SIZE);
rplSymbolStorage.strAllocatorOffset = 0;
rplSymbolStorage.list_strAllocatedBlocks.emplace_back(rplSymbolStorage.strAllocatorBlock);
}
cemu_assert_debug((rplSymbolStorage.strAllocatorOffset + len + 1) <= STR_ALLOC_BLOCK_SIZE);
char* allocatedStr = rplSymbolStorage.strAllocatorBlock + rplSymbolStorage.strAllocatorOffset;
rplSymbolStorage.strAllocatorOffset += len + 1;
strcpy(allocatedStr, str);
return allocatedStr;
}
char* rplSymbolStorage_storeLibname(const char* libName)
{
if (rplSymbolStorage.libs == NULL)
{
rplSymbolLib_t* libEntry = new rplSymbolLib_t();
libEntry->libName = rplSymbolStorage_allocDupString(libName);
libEntry->next = NULL;
rplSymbolStorage.libs = libEntry;
return libEntry->libName;
}
rplSymbolLib_t* libItr = rplSymbolStorage.libs;
while (libItr)
{
if (boost::iequals(libItr->libName, libName))
return libItr->libName;
// next
libItr = libItr->next;
}
// create new entry
rplSymbolLib_t* libEntry = new rplSymbolLib_t();
libEntry->libName = rplSymbolStorage_allocDupString(libName);
libEntry->next = rplSymbolStorage.libs;
rplSymbolStorage.libs = libEntry;
return libEntry->libName;
}
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address)
{
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
char* libNameStorage = rplSymbolStorage_storeLibname(libName);
char* symbolNameStorage = rplSymbolStorage_allocDupString(symbolName);
RPLStoredSymbol* storedSymbol = new RPLStoredSymbol();
storedSymbol->address = address;
storedSymbol->libName = libNameStorage;
storedSymbol->symbolName = symbolNameStorage;
storedSymbol->flags = 0;
rplSymbolStorage.map_symbolByAddress[address] = storedSymbol;
return storedSymbol;
}
RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address)
{
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
return rplSymbolStorage.map_symbolByAddress[address];
}
void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol)
{
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
if (rplSymbolStorage.map_symbolByAddress[storedSymbol->address] == storedSymbol)
rplSymbolStorage.map_symbolByAddress[storedSymbol->address] = nullptr;
delete storedSymbol;
}
void rplSymbolStorage_removeRange(MPTR address, sint32 length)
{
while (length > 0)
{
RPLStoredSymbol* symbol = rplSymbolStorage_getByAddress(address);
if (symbol)
rplSymbolStorage_remove(symbol);
address += 4;
length -= 4;
}
}
void rplSymbolStorage_createJumpProxySymbol(MPTR jumpAddress, MPTR destAddress)
{
RPLStoredSymbol* destSymbol = rplSymbolStorage_getByAddress(destAddress);
if (destSymbol)
rplSymbolStorage_store((char*)destSymbol->libName, (char*)destSymbol->symbolName, jumpAddress);
}
std::unordered_map<uint32, RPLStoredSymbol*>& rplSymbolStorage_lockSymbolMap()
{
rplSymbolStorage.m_symbolStorageMutex.lock();
return rplSymbolStorage.map_symbolByAddress;
}
void rplSymbolStorage_unlockSymbolMap()
{
rplSymbolStorage.m_symbolStorageMutex.unlock();
}
void rplSymbolStorage_init()
{
cemu_assert_debug(rplSymbolStorage.map_symbolByAddress.empty());
cemu_assert_debug(rplSymbolStorage.strAllocatorBlock == nullptr);
}
void rplSymbolStorage_unloadAll()
{
// free symbols
for (auto& it : rplSymbolStorage.map_symbolByAddress)
delete it.second;
rplSymbolStorage.map_symbolByAddress.clear();
// free libs
rplSymbolLib_t* lib = rplSymbolStorage.libs;
while (lib)
{
rplSymbolLib_t* next = lib->next;
delete lib;
lib = next;
}
rplSymbolStorage.libs = nullptr;
// free strings
for (auto it : rplSymbolStorage.list_strAllocatedBlocks)
free(it);
rplSymbolStorage.strAllocatorBlock = nullptr;
rplSymbolStorage.strAllocatorOffset = 0;
}

View file

@ -0,0 +1,18 @@
struct RPLStoredSymbol
{
MPTR address;
void* libName;
void* symbolName;
uint32 flags;
};
void rplSymbolStorage_init();
void rplSymbolStorage_unloadAll();
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address);
void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol);
void rplSymbolStorage_removeRange(MPTR address, sint32 length);
RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address);
void rplSymbolStorage_createJumpProxySymbol(MPTR jumpAddress, MPTR destAddress);
std::unordered_map<uint32, RPLStoredSymbol*>& rplSymbolStorage_lockSymbolMap();
void rplSymbolStorage_unlockSymbolMap();

View file

@ -0,0 +1,220 @@
#include "Cafe/HW/Espresso/PPCState.h"
#include "Cafe/OS/libs/proc_ui/proc_ui.h"
#include "Cafe/OS/libs/nsysnet/nsysnet.h"
#include "Cafe/OS/libs/nlibnss/nlibnss.h"
#include "Cafe/OS/libs/nlibcurl/nlibcurl.h"
#include "Cafe/OS/libs/nn_nfp/nn_nfp.h"
#include "Cafe/OS/libs/nn_act/nn_act.h"
#include "Cafe/OS/libs/nn_acp/nn_acp.h"
#include "Cafe/OS/libs/nn_ac/nn_ac.h"
#include "Cafe/OS/libs/nn_uds/nn_uds.h"
#include "Cafe/OS/libs/nn_nim/nn_nim.h"
#include "Cafe/OS/libs/nn_ndm/nn_ndm.h"
#include "Cafe/OS/libs/nn_ec/nn_ec.h"
#include "Cafe/OS/libs/nn_boss/nn_boss.h"
#include "Cafe/OS/libs/nn_fp/nn_fp.h"
#include "Cafe/OS/libs/nn_olv/nn_olv.h"
#include "Cafe/OS/libs/nn_idbe/nn_idbe.h"
#include "Cafe/OS/libs/nn_save/nn_save.h"
#include "Cafe/OS/libs/erreula/erreula.h"
#include "Cafe/OS/libs/sysapp/sysapp.h"
#include "Cafe/OS/libs/dmae/dmae.h"
#include "Cafe/OS/libs/snd_core/ax.h"
#include "Cafe/OS/libs/gx2/GX2.h"
#include "Cafe/OS/libs/vpad/vpad.h"
#include "Cafe/OS/libs/nsyskbd/nsyskbd.h"
#include "Cafe/OS/libs/nsyshid/nsyshid.h"
#include "Cafe/OS/libs/snd_user/snd_user.h"
#include "Cafe/OS/libs/zlib125/zlib125.h"
#include "Cafe/OS/libs/padscore/padscore.h"
#include "Cafe/OS/libs/camera/camera.h"
#include "../libs/swkbd/swkbd.h"
struct osFunctionEntry_t
{
uint32 libHashA;
uint32 libHashB;
uint32 funcHashA;
uint32 funcHashB;
std::string name;
HLEIDX hleFunc;
osFunctionEntry_t(uint32 libHashA, uint32 libHashB, uint32 funcHashA, uint32 funcHashB, std::string_view name, HLEIDX hleFunc) :
libHashA(libHashA), libHashB(libHashB), funcHashA(funcHashA), funcHashB(funcHashB), name(name), hleFunc(hleFunc) {};
};
typedef struct
{
uint32 libHashA;
uint32 libHashB;
uint32 funcHashA;
uint32 funcHashB;
uint32 vPtr;
}osPointerEntry_t;
std::vector<osFunctionEntry_t>* s_osFunctionTable;
std::vector<osPointerEntry_t> osDataTable;
void osLib_generateHashFromName(const char* name, uint32* hashA, uint32* hashB)
{
uint32 h1 = 0x688BA2BA;
uint32 h2 = 0xF64A71D5;
while( *name )
{
uint32 c = (uint32)*name;
h1 += c;
h1 = (h1<<3)|((h1>>29));
h2 ^= c;
h2 = (h2<<7)|((h2>>25));
h1 += h2;
h2 += c;
h2 = (h2<<3)|((h2>>29));
name++;
}
*hashA = h1;
*hashB = h2;
}
void osLib_addFunctionInternal(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t* hCPU))
{
if (!s_osFunctionTable)
s_osFunctionTable = new std::vector<osFunctionEntry_t>(); // replace with static allocation + constinit once we have C++20 available
// calculate hash
uint32 libHashA, libHashB;
uint32 funcHashA, funcHashB;
osLib_generateHashFromName(libraryName, &libHashA, &libHashB);
osLib_generateHashFromName(functionName, &funcHashA, &funcHashB);
// if entry already exists, update it
for (auto& it : *s_osFunctionTable)
{
if (it.libHashA == libHashA &&
it.libHashB == libHashB &&
it.funcHashA == funcHashA &&
it.funcHashB == funcHashB)
{
it.hleFunc = PPCInterpreter_registerHLECall(osFunction);
return;
}
}
s_osFunctionTable->emplace_back(libHashA, libHashB, funcHashA, funcHashB, fmt::format("{}.{}", libraryName, functionName), PPCInterpreter_registerHLECall(osFunction));
}
__declspec(dllexport) void osLib_registerHLEFunction(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t* hCPU))
{
osLib_addFunctionInternal(libraryName, functionName, osFunction);
}
sint32 osLib_getFunctionIndex(const char* libraryName, const char* functionName)
{
uint32 libHashA, libHashB;
uint32 funcHashA, funcHashB;
osLib_generateHashFromName(libraryName, &libHashA, &libHashB);
osLib_generateHashFromName(functionName, &funcHashA, &funcHashB);
for (auto& it : *s_osFunctionTable)
{
if (it.libHashA == libHashA &&
it.libHashB == libHashB &&
it.funcHashA == funcHashA &&
it.funcHashB == funcHashB)
{
return it.hleFunc;
}
}
return -1;
}
void osLib_addVirtualPointer(const char* libraryName, const char* functionName, uint32 vPtr)
{
// calculate hash
uint32 libHashA, libHashB;
uint32 funcHashA, funcHashB;
osLib_generateHashFromName(libraryName, &libHashA, &libHashB);
osLib_generateHashFromName(functionName, &funcHashA, &funcHashB);
// if entry already exists, update it
for (auto& it : osDataTable)
{
if (it.libHashA == libHashA &&
it.libHashB == libHashB &&
it.funcHashA == funcHashA &&
it.funcHashB == funcHashB)
{
it.vPtr = vPtr;
return;
}
}
// add entry
auto writeIndex = osDataTable.size();
osDataTable.resize(osDataTable.size() + 1);
osDataTable[writeIndex].libHashA = libHashA;
osDataTable[writeIndex].libHashB = libHashB;
osDataTable[writeIndex].funcHashA = funcHashA;
osDataTable[writeIndex].funcHashB = funcHashB;
osDataTable[writeIndex].vPtr = vPtr;
}
uint32 osLib_getPointer(const char* libraryName, const char* functionName)
{
uint32 libHashA, libHashB;
uint32 funcHashA, funcHashB;
osLib_generateHashFromName(libraryName, &libHashA, &libHashB);
osLib_generateHashFromName(functionName, &funcHashA, &funcHashB);
for (auto& it : osDataTable)
{
if (it.libHashA == libHashA &&
it.libHashB == libHashB &&
it.funcHashA == funcHashA &&
it.funcHashB == funcHashB)
{
return it.vPtr;
}
}
return 0xFFFFFFFF;
}
void osLib_returnFromFunction(PPCInterpreter_t* hCPU, uint32 returnValue)
{
hCPU->gpr[3] = returnValue;
hCPU->instructionPointer = hCPU->spr.LR;
}
void osLib_returnFromFunction64(PPCInterpreter_t* hCPU, uint64 returnValue64)
{
hCPU->gpr[3] = (returnValue64>>32)&0xFFFFFFFF;
hCPU->gpr[4] = (returnValue64>>0)&0xFFFFFFFF;
hCPU->instructionPointer = hCPU->spr.LR;
}
void osLib_load()
{
// load HLE modules
coreinit_load();
zlib::load();
gx2_load();
dmae_load();
padscore::load();
vpad::load();
snd_core::loadExports();
nn::erreula::load();
nnAct_load();
nn::acp::load();
nnAc_load();
nnEc_load();
nnBoss_load();
nn::nfp::load();
nnUds_load();
nn::nim::load();
nn::ndm::load();
nn::save::load();
nsysnet_load();
nn::fp::load();
nn::olv::load();
nn::idbe::load();
nlibnss::load();
nlibcurl::load();
sysapp_load();
nsyshid::load();
nsyskbd::nsyskbd_load();
swkbd::load();
camera::load();
procui_load();
}

View file

@ -0,0 +1,25 @@
#pragma once
struct PPCInterpreter_t;
#define OSLIB_FUNCTIONTABLE_TYPE_FUNCTION (1)
#define OSLIB_FUNCTIONTABLE_TYPE_POINTER (2)
void osLib_load();
void osLib_generateHashFromName(const char* name, uint32* hashA, uint32* hashB);
sint32 osLib_getFunctionIndex(const char* libraryName, const char* functionName);
uint32 osLib_getPointer(const char* libraryName, const char* functionName);
void osLib_addFunctionInternal(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t* hCPU));
#define osLib_addFunction(__p1, __p2, __p3) osLib_addFunctionInternal((const char*)__p1, __p2, __p3)
void osLib_addVirtualPointer(const char* libraryName, const char* functionName, uint32 vPtr);
void osLib_returnFromFunction(PPCInterpreter_t* hCPU, uint32 returnValue);
void osLib_returnFromFunction64(PPCInterpreter_t* hCPU, uint64 returnValue64);
// libs
#include "Cafe/OS/libs/coreinit/coreinit.h"
// utility functions
#include "Cafe/OS/common/OSUtil.h"

233
src/Cafe/OS/common/OSUtil.h Normal file
View file

@ -0,0 +1,233 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Cafe/HW/Espresso/PPCState.h"
#include "Cafe/HW/MMU/MMU.h"
#include <fmt/ostream.h>
#include <fmt/compile.h>
#include <fmt/ranges.h>
class cafeExportParamWrapper
{
public:
template <typename T>
static void getParamWrapper(PPCInterpreter_t* hCPU, int& gprIndex, int& fprIndex, T& v)
{
if constexpr (std::is_pointer_v<T>)
{
uint32be addr;
if (gprIndex >= 8)
addr = memory_readU32(hCPU->gpr[1] + 8 + (gprIndex - 8) * 4);
else
addr = hCPU->gpr[3 + gprIndex];
using TPtr = std::remove_pointer_t<T>;
v = MEMPTR<TPtr>(addr).GetPtr();
gprIndex++;
}
else if constexpr (std::is_base_of_v<MEMPTRBase, T>)
{
uint32be addr;
if (gprIndex >= 8)
addr = memory_readU32(hCPU->gpr[1] + 8 + (gprIndex - 8) * 4);
else
addr = hCPU->gpr[3 + gprIndex];
v = addr.value();
gprIndex++;
}
else if constexpr (std::is_enum_v<T>)
{
using TEnum = std::underlying_type_t<T>;
getParamWrapper<TEnum>(hCPU, gprIndex, fprIndex, (TEnum&)v);
}
else if constexpr (std::is_integral_v<T>)
{
if constexpr (sizeof(T) == sizeof(uint64))
{
gprIndex = (gprIndex + 1)&~1;
if (gprIndex >= 8)
v = (T)memory_readU64(hCPU->gpr[1] + 8 + (gprIndex - 8) * 4);
else
v = (T)(((uint64)hCPU->gpr[3 + gprIndex]) << 32) | ((uint64)hCPU->gpr[3 + gprIndex + 1]);
gprIndex += 2;
}
else
{
if (gprIndex >= 8)
v = (T)memory_readU32(hCPU->gpr[1] + 8 + (gprIndex - 8) * 4);
else
v = (T)hCPU->gpr[3 + gprIndex];
gprIndex++;
}
}
else if constexpr (std::is_floating_point_v<T>)
{
v = (T)ppcInterpreterCurrentInstance->fpr[1 + fprIndex].fpr;
fprIndex++;
}
else
{
assert_dbg();
}
}
template<typename T>
static void setReturnResult(PPCInterpreter_t* hCPU, T r)
{
if constexpr (std::is_pointer_v<T>)
{
hCPU->gpr[3] = MEMPTR(r).GetMPTR();
}
else if constexpr (std::is_reference_v<T>)
{
hCPU->gpr[3] = MEMPTR(&r).GetMPTR();
}
else if constexpr (std::is_enum_v<T>)
{
using TEnum = std::underlying_type_t<T>;
setReturnResult<TEnum>(hCPU, (TEnum)r);
}
else if constexpr (std::is_integral_v<T>)
{
if constexpr(sizeof(T) == 8)
{
const auto t = static_cast<uint64>(r);
hCPU->gpr[3] = (uint32)(t >> 32); // high
hCPU->gpr[4] = (uint32)(t); // low
}
else
{
hCPU->gpr[3] = (uint32)r;
}
}
else
{
cemu_assert_unimplemented();
//static_assert(false);
}
}
template<typename T>
static auto getFormatResult(T r)
{
if constexpr (std::is_pointer_v<T>)
return MEMPTR(r).GetMPTR();
else if constexpr (std::is_enum_v<T>)
return static_cast<std::underlying_type_t<T>>(r);
else if constexpr(!std::is_fundamental_v<T>)
return MEMPTR(&r).GetMPTR();
else
return r;
}
};
template<typename T>
T cafeExportGetParamWrapper(PPCInterpreter_t* hCPU, int& gprIndex, int& fprIndex)
{
T v;
cafeExportParamWrapper::getParamWrapper(hCPU, gprIndex, fprIndex, v);
return v;
}
template <typename R, typename ... Args>
static std::tuple<Args...> cafeExportBuildArgTuple(PPCInterpreter_t* hCPU, R(fn)(Args...))
{
int gprIndex = 0;
int fprIndex = 0;
return std::tuple<Args...>{ cafeExportGetParamWrapper<Args>(hCPU, gprIndex, fprIndex)... };
}
template<typename T>
using _CAFE_FORMAT_ARG = std::conditional_t<std::is_pointer_v<T>,
std::conditional_t<std::is_same_v<T, char*> || std::is_same_v<T, const char*>, T, MEMPTR<T>>, T>;
template <typename R, typename... Args>
static auto cafeExportBuildFormatTuple(PPCInterpreter_t* hCPU, R(fn)(Args...))
{
int gprIndex = 0;
int fprIndex = 0;
return std::tuple<_CAFE_FORMAT_ARG<Args>...>{
cafeExportGetParamWrapper<_CAFE_FORMAT_ARG<Args>>(hCPU, gprIndex, fprIndex)...
};
}
template<auto fn, typename TNames, LogType TLogType>
void cafeExportCallWrapper(PPCInterpreter_t* hCPU)
{
auto tup = cafeExportBuildArgTuple(hCPU, fn);
bool shouldLog = false;
if (cemuLog_isLoggingEnabled(TLogType))
{
const auto format_tup = cafeExportBuildFormatTuple(hCPU, fn);
if(cemuLog_advancedPPCLoggingEnabled())
{
MPTR threadMPTR = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread());
if constexpr (std::tuple_size<decltype(format_tup)>::value > 0)
shouldLog = cemuLog_log(TLogType, "{}.{}{} # LR: {:#x} | Thread: {:#x}", TNames::GetLib(), TNames::GetFunc(), format_tup, hCPU->spr.LR, threadMPTR);
else
shouldLog = cemuLog_log(TLogType, "{}.{}() # LR: {:#x} | Thread: {:#x}", TNames::GetLib(), TNames::GetFunc(), hCPU->spr.LR, threadMPTR);
}
else
{
if constexpr (std::tuple_size<decltype(format_tup)>::value > 0)
{
shouldLog = cemuLog_log(TLogType, "{}.{}{}", TNames::GetLib(), TNames::GetFunc(), format_tup);
}
else
shouldLog = cemuLog_log(TLogType, "{}.{}()", TNames::GetLib(), TNames::GetFunc());
}
}
if constexpr (!std::is_void<decltype(std::apply(fn, tup))>::value)
{
// has non-void return type
decltype(auto) result = std::apply(fn, tup);
cafeExportParamWrapper::setReturnResult<decltype(std::apply(fn, tup))>(hCPU, result);
if(shouldLog)
cemuLog_log(TLogType, "\t\t{}.{} -> {}", TNames::GetLib(), TNames::GetFunc(), cafeExportParamWrapper::getFormatResult(result));
}
else
{
// return type is void
std::apply(fn, tup);
}
// return from func
hCPU->instructionPointer = hCPU->spr.LR;
}
void osLib_addFunctionInternal(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t* hCPU));
template<auto fn, typename TNames, LogType TLogType>
void cafeExportMakeWrapper(const char* libname, const char* funcname)
{
osLib_addFunctionInternal(libname, funcname, &cafeExportCallWrapper<fn, TNames, TLogType>);
}
#define cafeExportRegister(__libname, __func, __logtype) \
{ \
struct StringWrapper { \
static const char* GetLib() { return __libname; }; \
static const char* GetFunc() { return #__func; }; \
}; \
cafeExportMakeWrapper<__func, StringWrapper, __logtype>(__libname, # __func);\
}
#define cafeExportRegisterFunc(__func, __libname, __funcname, __logtype) \
{\
struct StringWrapper { \
static const char* GetLib() { return __libname; }; \
static const char* GetFunc() { return __funcname; }; \
}; \
cafeExportMakeWrapper<__func, StringWrapper, __logtype>(__libname, __funcname);\
}
template<auto fn>
MPTR makeCallableExport()
{
return PPCInterpreter_makeCallableExportDepr(&cafeExportCallWrapper<fn, "CALLABLE_EXPORT">);
}
void osLib_addVirtualPointer(const char* libraryName, const char* functionName, uint32 vPtr);

View file

@ -0,0 +1,68 @@
#pragma once
#include <mutex>
#include <condition_variable>
#include <queue>
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
template <typename T>
class PPCConcurrentQueue
{
public:
PPCConcurrentQueue() {}
PPCConcurrentQueue(const PPCConcurrentQueue&) = delete;
PPCConcurrentQueue& operator=(const PPCConcurrentQueue&) = delete;
void push(const T& item, OSThread_t* thread)
{
//if(thread == nullptr)
// thread = coreinitThread_getCurrentThread(ppcInterpreterCurrentInstance);
//OSThread_t* currentThread = coreinit::OSGetCurrentThread();
//cemu_assert_debug(thread == nullptr || currentThread == thread);
// forceLogDebug_printf("push suspend count: %d", _swapEndianU32(thread->suspend) - m_suspendCount);
//__OSLockScheduler();
__OSLockScheduler();
m_queue.push(item);
coreinit::__OSResumeThreadInternal(thread, 1);
__OSUnlockScheduler();
//__OSUnlockScheduler();
//m_prevSuspendCount = _swapEndianU32(thread->suspend) - m_suspendCount;
//coreinit_resumeThread(thread, _swapEndianU32(thread->suspend));
}
T pop(OSThread_t* thread = nullptr)
{
//if (thread == nullptr)
// thread = coreinitThread_getCurrentThread(ppcInterpreterCurrentInstance);
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
cemu_assert_debug(thread == nullptr || currentThread == thread);
//thread = coreinitThread_getCurrentThread(ppcInterpreterCurrentInstance);
// forceLogDebug_printf("pop suspend count: %d", _swapEndianU32(thread->suspend) + m_suspendCount);
__OSLockScheduler();
if (m_queue.empty())
coreinit::__OSSuspendThreadInternal(thread);
auto val = m_queue.front();
m_queue.pop();
__OSUnlockScheduler();
//coreinit_suspendThread(thread, m_suspendCount + m_prevSuspendCount);
//m_prevSuspendCount = 0;
//PPCCore_switchToScheduler();
return val;
}
private:
//const int m_suspendCount = 8000;
std::queue<T> m_queue;
//std::atomic<uint32> m_prevSuspendCount;
};

View file

@ -0,0 +1,28 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/TCL/TCL.h"
namespace TCL
{
enum class TCL_SUBMISSION_FLAG : uint32
{
SURFACE_SYNC = 0x400000, // submit surface sync packet before cmd
TRIGGER_INTERRUPT = 0x200000, // probably
UKN_20000000 = 0x20000000,
};
int TCLSubmitToRing(uint32be* cmd, uint32 cmdLen, uint32be* controlFlags, uint64* submissionTimestamp)
{
// todo - figure out all the bits of *controlFlags
// if submissionTimestamp != nullptr then set it to the timestamp of the submission. Note: We should make sure that uint64's are written atomically by the GPU command processor
cemu_assert_debug(false);
return 0;
}
void Initialize()
{
cafeExportRegister("TCL", TCLSubmitToRing, LogType::Placeholder);
}
}

View file

@ -0,0 +1,4 @@
namespace TCL
{
void Initialize();
}

View file

@ -0,0 +1,40 @@
#include "Cafe/OS/common/OSCommon.h"
#include "avm.h"
namespace avm
{
bool AVMIsHDCPAvailable()
{
return true;
}
bool AVMIsHDCPOn()
{
return true;
}
bool AVMGetAnalogContentsProtectionEnable(uint32be* isEnable)
{
*isEnable = 1;
return false;
}
bool AVMIsAnalogContentsProtectionOn()
{
return true;
}
bool AVMSetAnalogContentsProtectionEnable(sint32 newState)
{
return true; // returns 1 (true) if new state was applied successfully?
}
void Initialize()
{
cafeExportRegister("avm", AVMIsHDCPAvailable, LogType::Placeholder);
cafeExportRegister("avm", AVMIsHDCPOn, LogType::Placeholder);
cafeExportRegister("avm", AVMGetAnalogContentsProtectionEnable, LogType::Placeholder);
cafeExportRegister("avm", AVMIsAnalogContentsProtectionOn, LogType::Placeholder);
cafeExportRegister("avm", AVMSetAnalogContentsProtectionEnable, LogType::Placeholder);
}
}

View file

@ -0,0 +1,5 @@
namespace avm
{
void Initialize();
}

View file

@ -0,0 +1,257 @@
#include "Common/precompiled.h"
#include "Cafe/OS/common/OSCommon.h"
#include "camera.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
namespace camera
{
struct CAMInitInfo_t
{
/* +0x00 */ uint32be ukn00;
/* +0x04 */ uint32be width;
/* +0x08 */ uint32be height;
/* +0x0C */ uint32be workMemorySize;
/* +0x10 */ MEMPTR<void> workMemory;
/* +0x14 */ uint32be handlerFuncPtr;
/* +0x18 */ uint32be ukn18;
/* +0x1C */ uint32be fps;
/* +0x20 */ uint32be ukn20;
};
struct CAMTargetSurface
{
/* +0x00 */ uint32be surfaceSize;
/* +0x04 */ MEMPTR<void> surfacePtr;
/* +0x08 */ uint32be ukn08;
/* +0x0C */ uint32be ukn0C;
/* +0x10 */ uint32be ukn10;
/* +0x14 */ uint32be ukn14;
/* +0x18 */ uint32be ukn18;
/* +0x1C */ uint32be ukn1C;
};
struct CAMCallbackParam
{
// type 0 - frame decoded | field1 - imagePtr, field2 - imageSize, field3 - ukn (0)
// type 1 - ???
/* +0x0 */ uint32be type; // 0 -> Frame decoded
/* +0x4 */ uint32be field1;
/* +0x8 */ uint32be field2;
/* +0xC */ uint32be field3;
};
#define CAM_ERROR_SUCCESS 0
#define CAM_ERROR_INVALID_HANDLE -8
std::vector<struct CameraInstance*> g_table_cameraHandles;
std::vector<struct CameraInstance*> g_activeCameraInstances;
std::recursive_mutex g_mutex_camera;
std::atomic_int g_cameraCounter{ 0 };
SysAllocator<coreinit::OSAlarm_t, 1> g_alarm_camera;
SysAllocator<CAMCallbackParam, 1> g_cameraHandlerParam;
CameraInstance* GetCameraInstanceByHandle(sint32 camHandle)
{
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
if (camHandle <= 0)
return nullptr;
camHandle -= 1;
if (camHandle >= g_table_cameraHandles.size())
return nullptr;
return g_table_cameraHandles[camHandle];
}
struct CameraInstance
{
CameraInstance(uint32 frameWidth, uint32 frameHeight, MPTR handlerFunc) : width(frameWidth), height(frameHeight), handlerFunc(handlerFunc) { AcquireHandle(); };
~CameraInstance() { if (isOpen) { CloseCam(); } ReleaseHandle(); };
sint32 handle{ 0 };
uint32 width;
uint32 height;
bool isOpen{false};
std::queue<CAMTargetSurface> queue_targetSurfaces;
MPTR handlerFunc;
bool OpenCam()
{
if (isOpen)
return false;
isOpen = true;
g_activeCameraInstances.push_back(this);
return true;
}
bool CloseCam()
{
if (!isOpen)
return false;
isOpen = false;
vectorRemoveByValue(g_activeCameraInstances, this);
return true;
}
void QueueTargetSurface(CAMTargetSurface* targetSurface)
{
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
cemu_assert_debug(queue_targetSurfaces.size() < 100); // check for sane queue length
queue_targetSurfaces.push(*targetSurface);
}
private:
void AcquireHandle()
{
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
for (uint32 i = 0; i < g_table_cameraHandles.size(); i++)
{
if (g_table_cameraHandles[i] == nullptr)
{
g_table_cameraHandles[i] = this;
this->handle = i + 1;
return;
}
}
this->handle = (sint32)(g_table_cameraHandles.size() + 1);
g_table_cameraHandles.push_back(this);
}
void ReleaseHandle()
{
for (uint32 i = 0; i < g_table_cameraHandles.size(); i++)
{
if (g_table_cameraHandles[i] == this)
{
g_table_cameraHandles[i] = nullptr;
return;
}
}
cemu_assert_debug(false);
}
};
sint32 CAMGetMemReq(void* ukn)
{
return 1 * 1024; // always return 1KB
}
sint32 CAMCheckMemSegmentation(void* base, uint32 size)
{
return CAM_ERROR_SUCCESS; // always return success
}
void ppcCAMUpdate60(PPCInterpreter_t* hCPU)
{
// update all open camera instances
size_t numCamInstances = g_activeCameraInstances.size();
//for (auto& itr : g_activeCameraInstances)
for(size_t i=0; i<numCamInstances; i++)
{
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
if (i >= g_activeCameraInstances.size())
break;
CameraInstance* camInstance = g_activeCameraInstances[i];
// todo - handle 30 / 60 FPS
if (camInstance->queue_targetSurfaces.empty())
continue;
auto& targetSurface = camInstance->queue_targetSurfaces.front();
g_cameraHandlerParam->type = 0;
g_cameraHandlerParam->field1 = targetSurface.surfacePtr.GetMPTR();
g_cameraHandlerParam->field2 = targetSurface.surfaceSize;
g_cameraHandlerParam->field3 = 0;
cemu_assert_debug(camInstance->handlerFunc != MPTR_NULL);
camInstance->queue_targetSurfaces.pop();
_lock.unlock();
PPCCoreCallback(camInstance->handlerFunc, g_cameraHandlerParam.GetPtr());
}
osLib_returnFromFunction(hCPU, 0);
}
sint32 CAMInit(uint32 cameraId, CAMInitInfo_t* camInitInfo, uint32be* error)
{
CameraInstance* camInstance = new CameraInstance(camInitInfo->width, camInitInfo->height, camInitInfo->handlerFuncPtr);
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
if (g_cameraCounter == 0)
{
coreinit::OSCreateAlarm(g_alarm_camera.GetPtr());
coreinit::OSSetPeriodicAlarm(g_alarm_camera.GetPtr(), coreinit::coreinit_getOSTime(), (uint64)ESPRESSO_TIMER_CLOCK / 60ull, RPLLoader_MakePPCCallable(ppcCAMUpdate60));
}
g_cameraCounter++;
return camInstance->handle;
}
sint32 CAMExit(sint32 camHandle)
{
CameraInstance* camInstance = GetCameraInstanceByHandle(camHandle);
if (!camInstance)
return CAM_ERROR_INVALID_HANDLE;
CAMClose(camHandle);
delete camInstance;
std::unique_lock<std::recursive_mutex> _lock(g_mutex_camera);
g_cameraCounter--;
if (g_cameraCounter == 0)
coreinit::OSCancelAlarm(g_alarm_camera.GetPtr());
return CAM_ERROR_SUCCESS;
}
sint32 CAMOpen(sint32 camHandle)
{
CameraInstance* camInstance = GetCameraInstanceByHandle(camHandle);
if (!camInstance)
return CAM_ERROR_INVALID_HANDLE;
camInstance->OpenCam();
return CAM_ERROR_SUCCESS;
}
sint32 CAMClose(sint32 camHandle)
{
CameraInstance* camInstance = GetCameraInstanceByHandle(camHandle);
if (!camInstance)
return CAM_ERROR_INVALID_HANDLE;
camInstance->CloseCam();
return CAM_ERROR_SUCCESS;
}
sint32 CAMSubmitTargetSurface(sint32 camHandle, CAMTargetSurface* targetSurface)
{
CameraInstance* camInstance = GetCameraInstanceByHandle(camHandle);
if (!camInstance)
return CAM_ERROR_INVALID_HANDLE;
camInstance->QueueTargetSurface(targetSurface);
return CAM_ERROR_SUCCESS;
}
void reset()
{
g_cameraCounter = 0;
}
void load()
{
reset();
cafeExportRegister("camera", CAMGetMemReq, LogType::Placeholder);
cafeExportRegister("camera", CAMCheckMemSegmentation, LogType::Placeholder);
cafeExportRegister("camera", CAMInit, LogType::Placeholder);
cafeExportRegister("camera", CAMExit, LogType::Placeholder);
cafeExportRegister("camera", CAMOpen, LogType::Placeholder);
cafeExportRegister("camera", CAMClose, LogType::Placeholder);
cafeExportRegister("camera", CAMSubmitTargetSurface, LogType::Placeholder);
}
}

View file

@ -0,0 +1,10 @@
#pragma once
namespace camera
{
sint32 CAMOpen(sint32 camHandle);
sint32 CAMClose(sint32 camHandle);
void load();
};

View file

@ -0,0 +1,378 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Common/SysAllocator.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
// includes for Initialize coreinit submodules
#include "Cafe/OS/libs/coreinit/coreinit_BSP.h"
#include "Cafe/OS/libs/coreinit/coreinit_Scheduler.h"
#include "Cafe/OS/libs/coreinit/coreinit_Atomic.h"
#include "Cafe/OS/libs/coreinit/coreinit_OverlayArena.h"
#include "Cafe/OS/libs/coreinit/coreinit_DynLoad.h"
#include "Cafe/OS/libs/coreinit/coreinit_GHS.h"
#include "Cafe/OS/libs/coreinit/coreinit_HWInterface.h"
#include "Cafe/OS/libs/coreinit/coreinit_Memory.h"
#include "Cafe/OS/libs/coreinit/coreinit_IM.h"
#include "Cafe/OS/libs/coreinit/coreinit_LockedCache.h"
#include "Cafe/OS/libs/coreinit/coreinit_MemoryMapping.h"
#include "Cafe/OS/libs/coreinit/coreinit_IPC.h"
#include "Cafe/OS/libs/coreinit/coreinit_IPCBuf.h"
#include "Cafe/OS/libs/coreinit/coreinit_Coroutine.h"
#include "Cafe/OS/libs/coreinit/coreinit_OSScreen.h"
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
#include "Cafe/OS/libs/coreinit/coreinit_SystemInfo.h"
#include "Cafe/OS/libs/coreinit/coreinit_SysHeap.h"
#include "Cafe/OS/libs/coreinit/coreinit_MCP.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
#include "Cafe/OS/libs/coreinit/coreinit_CodeGen.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Cafe/OS/libs/coreinit/coreinit_MPQueue.h"
#include "Cafe/OS/libs/coreinit/coreinit_FS.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_UnitHeap.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h"
coreinitData_t* gCoreinitData = NULL;
sint32 ScoreStackTrace(OSThread_t* thread, MPTR sp)
{
uint32 stackMinAddr = _swapEndianU32(thread->stackEnd);
uint32 stackMaxAddr = _swapEndianU32(thread->stackBase);
sint32 score = 0;
uint32 currentStackPtr = sp;
for (sint32 i = 0; i < 50; i++)
{
uint32 nextStackPtr = memory_readU32(currentStackPtr);
if (nextStackPtr < currentStackPtr)
break;
if (nextStackPtr < stackMinAddr || nextStackPtr > stackMaxAddr)
break;
if ((nextStackPtr & 3) != 0)
break;
score += 10;
uint32 returnAddress = 0;
returnAddress = memory_readU32(nextStackPtr + 4);
//cemuLog_log(LogType::Force, fmt::format("SP {0:08x} ReturnAddress {1:08x}", nextStackPtr, returnAddress));
if (returnAddress > 0 && returnAddress < 0x10000000 && (returnAddress&3) == 0)
score += 5; // within code region
else
score -= 5;
currentStackPtr = nextStackPtr;
}
return score;
}
void DebugLogStackTrace(OSThread_t* thread, MPTR sp)
{
// sp might not point to a valid stackframe
// scan stack and evaluate which sp is most likely the beginning of the stackframe
// scan 0x400 bytes
sint32 highestScore = -1;
uint32 highestScoreSP = sp;
for (sint32 i = 0; i < 0x100; i++)
{
uint32 sampleSP = sp + i * 4;
sint32 score = ScoreStackTrace(thread, sampleSP);
if (score > highestScore)
{
highestScore = score;
highestScoreSP = sampleSP;
}
}
if (highestScoreSP != sp)
cemuLog_log(LogType::Force, fmt::format("Trace starting at SP {0:08x} r1 = {1:08x}", highestScoreSP, sp));
else
cemuLog_log(LogType::Force, fmt::format("Trace starting at SP/r1 {0:08x}", highestScoreSP));
// print stack trace
uint32 currentStackPtr = highestScoreSP;
uint32 stackMinAddr = _swapEndianU32(thread->stackEnd);
uint32 stackMaxAddr = _swapEndianU32(thread->stackBase);
for (sint32 i = 0; i < 20; i++)
{
uint32 nextStackPtr = memory_readU32(currentStackPtr);
if (nextStackPtr < currentStackPtr)
break;
if (nextStackPtr < stackMinAddr || nextStackPtr > stackMaxAddr)
break;
uint32 returnAddress = 0;
returnAddress = memory_readU32(nextStackPtr + 4);
cemuLog_log(LogType::Force, fmt::format("SP {0:08x} ReturnAddr {1:08x}", nextStackPtr, returnAddress));
currentStackPtr = nextStackPtr;
}
}
void coreinitExport_OSPanic(PPCInterpreter_t* hCPU)
{
debug_printf("OSPanic!\n");
debug_printf("File: %s:%d\n", memory_getPointerFromVirtualOffset(hCPU->gpr[3]), hCPU->gpr[4]);
debug_printf("Msg: %s\n", memory_getPointerFromVirtualOffset(hCPU->gpr[5]));
DebugLogStackTrace(coreinit::OSGetCurrentThread(), coreinit::OSGetStackPointer());
#ifndef PUBLIC_RELEASE
assert_dbg();
while (true) std::this_thread::sleep_for(std::chrono::milliseconds(100));
#endif
osLib_returnFromFunction(hCPU, 0);
}
typedef struct
{
/* +0x00 */ uint32be name;
/* +0x04 */ uint32be fileType; // 2 = font
/* +0x08 */ uint32be kernelFilenamePtr;
/* +0x0C */ MEMPTR<void> data;
/* +0x10 */ uint32be size;
/* +0x14 */ uint32be ukn14;
/* +0x18 */ uint32be ukn18;
}coreinitShareddataEntry_t;
static_assert(sizeof(coreinitShareddataEntry_t) == 0x1C, "");
uint8* extractCafeDefaultFont(sint32* size);
MPTR placeholderFont = MPTR_NULL;
sint32 placeholderFontSize = 0;
void coreinitExport_OSGetSharedData(PPCInterpreter_t* hCPU)
{
// parameters:
// r3 sharedAreaId
// r4 flags
// r5 areaPtrPtr
// r6 areaSizePtr
// on real Wii U hw/sw there is a list of shared area entries starting at offset +0xF8000000
// properly formated (each entry is 0x1C bytes) it looks like this:
// FF CA FE 01 00 00 00 02 FF E8 47 AC F8 00 00 70 00 C8 0D 4C 00 00 00 00 FF FF FF FC
// FF CA FE 02 00 00 00 02 FF E8 47 B7 F8 C8 0D C0 00 22 7E B4 00 00 00 00 00 00 11 D5
// FF CA FE 03 00 00 00 02 FF E8 47 A0 F8 EA 8C 80 00 25 44 E0 00 00 00 00 FF A0 00 00
// FF CA FE 04 00 00 00 02 FF E8 47 C2 F9 0F D1 60 00 7D 93 5C 00 00 00 00 FF FF FF FC
uint32 sharedAreaId = hCPU->gpr[3];
coreinitShareddataEntry_t* shareddataTable = (coreinitShareddataEntry_t*)memory_getPointerFromVirtualOffset(MEMORY_SHAREDDATA_AREA_ADDR);
uint32 name = 0xFFCAFE01 + sharedAreaId;
for (sint32 i = 0; i < 4; i++)
{
if ((uint32)shareddataTable[i].name == name)
{
memory_writeU32(hCPU->gpr[5], shareddataTable[i].data.GetMPTR());
memory_writeU32(hCPU->gpr[6], (uint32)shareddataTable[i].size);
osLib_returnFromFunction(hCPU, 1);
return;
}
}
// some games require a valid result or they will crash, return a pointer to our placeholder font
forceLog_printf("OSGetSharedData() called by game but no shareddata fonts loaded. Use placeholder font");
if (placeholderFont == MPTR_NULL)
{
// load and then return placeholder font
uint8* placeholderFontPtr = extractCafeDefaultFont(&placeholderFontSize);
placeholderFont = coreinit_allocFromSysArea(placeholderFontSize, 256);
if (placeholderFont == MPTR_NULL)
forceLog_printf("Failed to alloc placeholder font sys memory");
memcpy(memory_getPointerFromVirtualOffset(placeholderFont), placeholderFontPtr, placeholderFontSize);
free(placeholderFontPtr);
}
// return placeholder font
memory_writeU32(hCPU->gpr[5], placeholderFont);
memory_writeU32(hCPU->gpr[6], placeholderFontSize);
osLib_returnFromFunction(hCPU, 1);
}
typedef struct
{
MPTR getDriverName;
MPTR ukn04;
MPTR onAcquiredForeground;
MPTR onReleaseForeground;
MPTR ukn10;
}OSDriverCallbacks_t;
void coreinitExport_OSDriver_Register(PPCInterpreter_t* hCPU)
{
#ifndef PUBLIC_RELEASE
forceLog_printf("OSDriver_Register(0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7], hCPU->gpr[8]);
#endif
OSDriverCallbacks_t* driverCallbacks = (OSDriverCallbacks_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[5]);
// todo
osLib_returnFromFunction(hCPU, 0);
}
namespace coreinit
{
sint32 OSGetCoreId()
{
return PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
}
uint32 OSGetCoreCount()
{
return Espresso::CORE_COUNT;
}
uint32 OSIsDebuggerInitialized()
{
return 0;
}
uint32 OSGetConsoleType()
{
return 0x03000050;
}
uint32 OSGetMainCoreId()
{
return 1;
}
bool OSIsMainCore()
{
return OSGetCoreId() == OSGetMainCoreId();
}
uint32 OSGetStackPointer()
{
return ppcInterpreterCurrentInstance->gpr[1];
}
void coreinitExport_ENVGetEnvironmentVariable(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("ENVGetEnvironmentVariable(\"%s\",0x08x,0x%x)\n", (char*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]), hCPU->gpr[4], hCPU->gpr[5]);
char* envKeyStr = (char*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
char* outputString = (char*)memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
sint32 outputStringMaxLen = (sint32)hCPU->gpr[5];
// also return the string "" just in case
if (outputStringMaxLen > 0)
{
outputString[0] = '\0';
}
osLib_returnFromFunction(hCPU, 1);
}
void coreinit_exit(uint32 r)
{
forceLog_printf("coreinit.exit(%d)", r);
cemu_assert_debug(false);
// never return
while (true) std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
bool OSIsOffBoot()
{
return true;
}
uint32 OSGetBootPMFlags()
{
forceLogDebug_printf("OSGetBootPMFlags() - placeholder");
return 0;
}
uint32 OSGetSystemMode()
{
forceLogDebug_printf("OSGetSystemMode() - placeholder");
// if this returns 2, barista softlocks shortly after boot
return 0;
}
void InitializeCore()
{
cafeExportRegister("coreinit", OSGetCoreId, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSGetCoreCount, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSIsDebuggerInitialized, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSGetConsoleType, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSGetMainCoreId, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSIsMainCore, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSGetStackPointer, LogType::CoreinitThread);
osLib_addFunction("coreinit", "ENVGetEnvironmentVariable", coreinitExport_ENVGetEnvironmentVariable);
cafeExportRegisterFunc(coreinit_exit, "coreinit", "exit", LogType::CoreinitThread);
cafeExportRegister("coreinit", OSIsOffBoot, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSGetBootPMFlags, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSGetSystemMode, LogType::CoreinitThread);
}
};
void coreinit_load()
{
coreinit::InitializeCore();
coreinit::InitializeSchedulerLock();
coreinit::InitializeSysHeap();
// allocate coreinit global data
gCoreinitData = (coreinitData_t*)memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sizeof(coreinitData_t), 32));
memset(gCoreinitData, 0x00, sizeof(coreinitData_t));
// coreinit weak links
osLib_addVirtualPointer("coreinit", "MEMAllocFromDefaultHeap", memory_getVirtualOffsetFromPointer(&gCoreinitData->MEMAllocFromDefaultHeap));
osLib_addVirtualPointer("coreinit", "MEMAllocFromDefaultHeapEx", memory_getVirtualOffsetFromPointer(&gCoreinitData->MEMAllocFromDefaultHeapEx));
osLib_addVirtualPointer("coreinit", "MEMFreeToDefaultHeap", memory_getVirtualOffsetFromPointer(&gCoreinitData->MEMFreeToDefaultHeap));
osLib_addVirtualPointer("coreinit", "__atexit_cleanup", memory_getVirtualOffsetFromPointer(&gCoreinitData->__atexit_cleanup));
osLib_addVirtualPointer("coreinit", "__stdio_cleanup", memory_getVirtualOffsetFromPointer(&gCoreinitData->__stdio_cleanup));
osLib_addVirtualPointer("coreinit", "__cpp_exception_cleanup_ptr", memory_getVirtualOffsetFromPointer(&gCoreinitData->__cpp_exception_cleanup_ptr));
osLib_addVirtualPointer("coreinit", "__cpp_exception_init_ptr", memory_getVirtualOffsetFromPointer(&gCoreinitData->__cpp_exception_init_ptr));
// init GHS and threads
coreinit::PrepareGHSRuntime();
coreinit::InitializeThread();
// reset threads
activeThreadCount = 0;
// init submodules
coreinit::InitializeMEM();
coreinit::InitializeMEMFrmHeap();
coreinit::InitializeMEMUnitHeap();
coreinit::InitializeMEMBlockHeap();
coreinit::InitializeFG();
coreinit::InitializeBSP();
coreinit::InitializeMCP();
coreinit::InitializeOverlayArena();
coreinit::InitializeDynLoad();
coreinit::InitializeGHS();
coreinit::InitializeHWInterface();
coreinit::InitializeAtomic();
coreinit::InitializeMemory();
coreinit::InitializeIM();
coreinit::InitializeLC();
coreinit::InitializeMP();
coreinit::InitializeTimeAndCalendar();
coreinit::InitializeAlarm();
coreinit::InitializeFS();
coreinit::InitializeSystemInfo();
coreinit::InitializeConcurrency();
coreinit::InitializeSpinlock();
coreinit::InitializeMessageQueue();
coreinit::InitializeIPC();
coreinit::InitializeIPCBuf();
coreinit::InitializeCodeGen();
coreinit::InitializeCoroutine();
coreinit::InitializeOSScreen();
// legacy mem stuff
coreinit::expheap_load();
// misc exports
coreinit::miscInit();
osLib_addFunction("coreinit", "OSGetSharedData", coreinitExport_OSGetSharedData);
osLib_addFunction("coreinit", "UCReadSysConfig", coreinitExport_UCReadSysConfig);
osLib_addFunction("coreinit", "OSDriver_Register", coreinitExport_OSDriver_Register);
// async callbacks
InitializeAsyncCallback();
}

View file

@ -0,0 +1,48 @@
#pragma once
#include "Cafe/HW/Espresso/Const.h"
#define PPC_CORE_COUNT (Espresso::CORE_COUNT)
#include "Cafe/OS/libs/coreinit/coreinit_MessageQueue.h"
// async callback helper
void InitializeAsyncCallback();
void coreinitAsyncCallback_add(MPTR functionMPTR, uint32 numParameters, uint32 r3 = 0, uint32 r4 = 0, uint32 r5 = 0, uint32 r6 = 0, uint32 r7 = 0, uint32 r8 = 0, uint32 r9 = 0, uint32 r10 = 0);
void coreinitAsyncCallback_addWithLock(MPTR functionMPTR, uint32 numParameters, uint32 r3 = 0, uint32 r4 = 0, uint32 r5 = 0, uint32 r6 = 0, uint32 r7 = 0, uint32 r8 = 0, uint32 r9 = 0, uint32 r10 = 0);
// misc
void coreinit_load();
// coreinit shared memory
typedef struct
{
MEMPTR<void> MEMAllocFromDefaultHeap;
MEMPTR<void> MEMAllocFromDefaultHeapEx;
MEMPTR<void> MEMFreeToDefaultHeap;
MPTR __atexit_cleanup;
MPTR __cpp_exception_init_ptr;
MPTR __cpp_exception_cleanup_ptr;
MPTR __stdio_cleanup;
}coreinitData_t;
extern coreinitData_t* gCoreinitData;
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
// coreinit init
void coreinit_start(PPCInterpreter_t* hCPU);
MPTR OSAllocFromSystem(uint32 size, uint32 alignment);
void OSFreeToSystem(MPTR mem);
// above is all the legacy stuff. New code uses namespaces
namespace coreinit
{
sint32 OSGetCoreId();
uint32 OSGetCoreCount();
uint32 OSGetStackPointer();
};

View file

@ -0,0 +1,359 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#include "Cafe/OS/RPL/rpl.h"
// #define ALARM_LOGGING
namespace coreinit
{
SysAllocator<OSEvent> g_alarmEvent;
SysAllocator<OSThread_t> g_alarmThread;
SysAllocator<uint8, 1024 * 128> _g_alarmThreadStack;
SysAllocator<char, 32> _g_alarmThreadName;
class OSHostAlarm
{
public:
OSHostAlarm(uint64 nextFire, uint64 period, void(*callbackFunc)(uint64 currentTick, void* context), void* context) : m_nextFire(nextFire), m_period(period), m_callbackFunc(callbackFunc), m_context(context)
{
cemu_assert_debug(__OSHasSchedulerLock()); // must hold lock
auto r = g_activeAlarmList.emplace(this);
cemu_assert_debug(r.second); // check if insertion was successful
m_isActive = true;
updateEarliestAlarmAtomic();
}
~OSHostAlarm()
{
cemu_assert_debug(__OSHasSchedulerLock()); // must hold lock
if (m_isActive)
{
g_activeAlarmList.erase(g_activeAlarmList.find(this));
updateEarliestAlarmAtomic();
}
}
uint64 getFireTick() const
{
return m_nextFire;
}
void triggerAlarm(uint64 currentTick)
{
m_callbackFunc(currentTick, m_context);
}
static void updateEarliestAlarmAtomic()
{
cemu_assert_debug(__OSHasSchedulerLock());
if (!g_activeAlarmList.empty())
{
auto firstAlarm = g_activeAlarmList.begin();
g_soonestAlarm = (*firstAlarm)->m_nextFire;
}
else
{
g_soonestAlarm = std::numeric_limits<uint64>::max();
}
}
static void updateAlarms(uint64 currentTick)
{
cemu_assert_debug(__OSHasSchedulerLock());
if (g_activeAlarmList.empty())
return;
// debug begin
#ifndef PUBLIC_RELEASE
uint64 prevTick = 0;
auto itr = g_activeAlarmList.begin();
while (itr != g_activeAlarmList.end())
{
uint64 t = (*itr)->m_nextFire;
if (t < prevTick)
cemu_assert_suspicious();
prevTick = t;
++itr;
}
#endif
// debug end
while (true)
{
auto firstAlarm = g_activeAlarmList.begin();
if (currentTick >= (*firstAlarm)->m_nextFire)
{
OSHostAlarm* alarm = *firstAlarm;
g_activeAlarmList.erase(firstAlarm);
alarm->triggerAlarm(currentTick);
// if periodic alarm then requeue
if (alarm->m_period > 0)
{
alarm->m_nextFire += alarm->m_period;
g_activeAlarmList.emplace(alarm);
}
else
alarm->m_isActive = false;
updateEarliestAlarmAtomic();
}
else
break;
}
}
uint64 getNextFire() const
{
return m_nextFire;
}
static bool quickCheckForAlarm(uint64 currentTick)
{
// fast way to check if any alarm was triggered without requiring scheduler lock
return currentTick >= g_soonestAlarm;
}
public:
struct ComparatorFireTime
{
bool operator ()(OSHostAlarm* const & p1, OSHostAlarm* const & p2) const
{
auto p1Fire = p1->getNextFire();
auto p2Fire = p2->getNextFire();
if (p1Fire == p2Fire)
return (uintptr_t)p1 < (uintptr_t)p2; // if time is equal, differ by pointer (to allow storing multiple alarms with same firing time)
return p1Fire < p2Fire;
}
};
private:
uint64 m_nextFire;
uint64 m_period; // if zero then repeat is disabled
bool m_isActive{ false };
void (*m_callbackFunc)(uint64 currentTick, void* context);
void* m_context;
static std::set<class OSHostAlarm*, ComparatorFireTime> g_activeAlarmList;
static std::atomic_uint64_t g_soonestAlarm;
};
std::set<class OSHostAlarm*, OSHostAlarm::ComparatorFireTime> OSHostAlarm::g_activeAlarmList;
std::atomic_uint64_t OSHostAlarm::g_soonestAlarm{};
OSHostAlarm* OSHostAlarmCreate(uint64 nextFire, uint64 period, void(*callbackFunc)(uint64 currentTick, void* context), void* context)
{
OSHostAlarm* hostAlarm = new OSHostAlarm(nextFire, period, callbackFunc, context);
return hostAlarm;
}
void OSHostAlarmDestroy(OSHostAlarm* hostAlarm)
{
delete hostAlarm;
}
void alarm_update()
{
cemu_assert_debug(!__OSHasSchedulerLock());
uint64 currentTick = coreinit::coreinit_getOSTime();
if (!OSHostAlarm::quickCheckForAlarm(currentTick))
return;
__OSLockScheduler();
OSHostAlarm::updateAlarms(currentTick);
__OSUnlockScheduler();
}
/* alarm API */
void OSCreateAlarm(OSAlarm_t* alarm)
{
memset(alarm, 0, sizeof(OSAlarm_t));
alarm->setMagic();
}
void coreinitExport_OSCreateAlarmEx(PPCInterpreter_t* hCPU)
{
OSAlarm_t* OSAlarm = (OSAlarm_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
OSCreateAlarm(OSAlarm);
OSAlarm->name = _swapEndianU32(hCPU->gpr[4]);
osLib_returnFromFunction(hCPU, 0);
}
std::unordered_map<OSAlarm_t*, OSHostAlarm*> g_activeAlarms;
bool OSCancelAlarm(OSAlarm_t* alarm)
{
__OSLockScheduler();
bool alarmWasActive = false;
auto itr = g_activeAlarms.find(alarm);
if (itr != g_activeAlarms.end())
{
OSHostAlarmDestroy(itr->second);
g_activeAlarms.erase(itr);
alarmWasActive = true;
}
__OSUnlockScheduler();
return alarmWasActive;
}
void __OSHostAlarmTriggered(uint64 currentTick, void* context)
{
#ifdef ALARM_LOGGING
cemuLog_log(LogType::Force, "[Alarm] Alarm ready and alarm thread signalled. Current tick: {}", currentTick);
#endif
OSSignalEventInternal(g_alarmEvent.GetPtr());
}
void __OSInitiateAlarm(OSAlarm_t* alarm, uint64 startTime, uint64 period, MPTR handlerFunc, bool isPeriodic)
{
cemu_assert_debug(__OSHasSchedulerLock());
uint64 nextTime = startTime;
#ifdef ALARM_LOGGING
double periodInMS = (double)period * 1000.0 / (double)EspressoTime::GetTimerClock();
cemuLog_log(LogType::Force, "[Alarm] Start alarm 0x{:08x}. Func 0x{:08x}. Period: {}ms", MEMPTR(alarm).GetMPTR(), handlerFunc, periodInMS);
#endif
if (isPeriodic)
{
cemu_assert_debug(period != 0);
if (period == 0)
return;
uint64 currentTime = coreinit_getOSTime();
uint64 ticksSinceStart = currentTime - startTime;
uint64 numPeriods = ticksSinceStart / period;
nextTime = startTime + (numPeriods + 1ull) * period;
alarm->startTime = _swapEndianU64(startTime);
alarm->nextTime = _swapEndianU64(nextTime);
alarm->period = _swapEndianU64(period);
alarm->handler = _swapEndianU32(handlerFunc);
}
else
{
alarm->nextTime = _swapEndianU64(startTime);
alarm->period = 0;
alarm->handler = _swapEndianU32(handlerFunc);
}
auto existingAlarmItr = g_activeAlarms.find(alarm);
if (existingAlarmItr != g_activeAlarms.end())
{
// delete existing alarm
forceLogDebug_printf("__OSInitiateAlarm() called on alarm which was already active");
OSHostAlarmDestroy(existingAlarmItr->second);
g_activeAlarms.erase(existingAlarmItr);
}
g_activeAlarms[alarm] = OSHostAlarmCreate(nextTime, period, __OSHostAlarmTriggered, nullptr);
}
void OSSetAlarm(OSAlarm_t* alarm, uint64 delayInTicks, MPTR handlerFunc)
{
__OSLockScheduler();
__OSInitiateAlarm(alarm, coreinit_getOSTime() + delayInTicks, 0, handlerFunc, false);
__OSUnlockScheduler();
}
void OSSetPeriodicAlarm(OSAlarm_t* alarm, uint64 nextFire, uint64 period, MPTR handlerFunc)
{
__OSLockScheduler();
__OSInitiateAlarm(alarm, nextFire, period, handlerFunc, true);
__OSUnlockScheduler();
}
void OSSetAlarmUserData(OSAlarm_t* alarm, uint32 userData)
{
alarm->userData = _swapEndianU32(userData);
}
void coreinitExport_OSGetAlarmUserData(PPCInterpreter_t* hCPU)
{
OSAlarm_t* OSAlarmBE = (OSAlarm_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
MPTR userData = _swapEndianU32(OSAlarmBE->userData);
osLib_returnFromFunction(hCPU, userData);
}
void OSAlarm_resetAll()
{
cemu_assert_debug(g_activeAlarms.empty());
cemu_assert_debug(false);
}
void _OSAlarmThread(PPCInterpreter_t* hCPU)
{
while( true )
{
OSWaitEvent(g_alarmEvent.GetPtr());
uint64 currentTick = coreinit_getOSTime();
while (true)
{
// get alarm to fire
OSAlarm_t* alarm = nullptr;
__OSLockScheduler();
auto itr = g_activeAlarms.begin();
while(itr != g_activeAlarms.end())
{
if (currentTick >= _swapEndianU64(itr->first->nextTime))
{
alarm = itr->first;
if (alarm->period == 0)
{
// end alarm
g_activeAlarms.erase(itr);
break;
}
else
{
alarm->nextTime = _swapEndianU64(_swapEndianU64(alarm->nextTime) + _swapEndianU64(alarm->period));
}
break;
}
++itr;
}
__OSUnlockScheduler();
if (!alarm)
break;
// do callback for alarm
#ifdef ALARM_LOGGING
double periodInMS = (double)_swapEndianU64(alarm->period) * 1000.0 / (double)EspressoTime::GetTimerClock();
cemuLog_log(LogType::Force, "[Alarm] Callback 0x{:08x} for alarm 0x{:08x}. Current tick: {}. Period: {}ms", _swapEndianU32(alarm->handler), MEMPTR(alarm).GetMPTR(), currentTick, periodInMS);
#endif
PPCCoreCallback(_swapEndianU32(alarm->handler), alarm, &(g_alarmThread.GetPtr()->context));
}
}
}
void InitializeAlarm()
{
cafeExportRegister("coreinit", OSCreateAlarm, LogType::Placeholder);
cafeExportRegister("coreinit", OSCancelAlarm, LogType::Placeholder);
cafeExportRegister("coreinit", OSSetAlarm, LogType::Placeholder);
cafeExportRegister("coreinit", OSSetPeriodicAlarm, LogType::Placeholder);
cafeExportRegister("coreinit", OSSetAlarmUserData, LogType::Placeholder);
osLib_addFunction("coreinit", "OSCreateAlarmEx", coreinitExport_OSCreateAlarmEx);
osLib_addFunction("coreinit", "OSGetAlarmUserData", coreinitExport_OSGetAlarmUserData);
// init event
OSInitEvent(g_alarmEvent.GetPtr(), OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_AUTO);
// create alarm callback handler thread
coreinit::OSCreateThreadType(g_alarmThread.GetPtr(), RPLLoader_MakePPCCallable(_OSAlarmThread), 0, nullptr, _g_alarmThreadStack.GetPtr() + _g_alarmThreadStack.GetByteSize(), (sint32)_g_alarmThreadStack.GetByteSize(), 0, 0x7, OSThread_t::THREAD_TYPE::TYPE_IO);
OSResumeThread(g_alarmThread.GetPtr());
strcpy(_g_alarmThreadName.GetPtr(), "Alarm Thread");
coreinit::OSSetThreadName(g_alarmThread.GetPtr(), _g_alarmThreadName.GetPtr());
}
}

View file

@ -0,0 +1,54 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
namespace coreinit
{
class OSHostAlarm;
OSHostAlarm* OSHostAlarmCreate(uint64 nextFire, uint64 period, void(*callbackFunc)(uint64 currentTick, void* context), void* context);
void OSHostAlarmDestroy(OSHostAlarm* hostAlarm);
struct OSAlarm_t
{
/* +0x00 */ betype<uint32> magic;
/* +0x04 */ MPTR_UINT8 name;
/* +0x08 */ uint32 ukn08;
/* +0x0C */ MPTR handler;
/* +0x10 */ uint32 ukn10;
/* +0x14 */ uint32 padding14;
/* +0x18 */ uint64 nextTime; // next fire time
/* +0x20 */ MPTR prev; // pointer to OSAlarm
/* +0x24 */ MPTR next; // pointer to OSAlarm
/* +0x28 */ uint64 period; // period (zero for non-periodic timer)
/* +0x30 */ uint64 startTime; // period start
/* +0x38 */ MPTR userData;
/* +0x3C */ uint32 ukn3C;
/* +0x40 */ OSThreadQueue uknThreadQueue;
/* +0x50 */ MPTR alarmQueue;
/* +0x54 */ MPTR ukn54;
void setMagic()
{
magic = (uint32)'aLrM';
}
bool checkMagic()
{
return magic == (uint32)'aLrM';
}
};
static_assert(sizeof(OSAlarm_t) == 0x58);
void OSCreateAlarm(OSAlarm_t* alarm);
bool OSCancelAlarm(OSAlarm_t* alarm);
void OSSetAlarm(OSAlarm_t* alarm, uint64 time, MPTR handlerFunc);
void OSSetAlarmUserData(OSAlarm_t* alarm, uint32 userData);
void OSSetPeriodicAlarm(OSAlarm_t* OSAlarm, uint64 startTick, uint64 periodTick, MPTR OSAlarmHandler);
void OSAlarm_resetAll();
void alarm_update();
void InitializeAlarm();
}

View file

@ -0,0 +1,139 @@
#include "Cafe/OS/common/OSCommon.h"
#include <atomic>
#include "coreinit_Atomic.h"
namespace coreinit
{
/* 32bit atomic operations */
uint32 OSSwapAtomic(std::atomic<uint32be>* mem, uint32 newValue)
{
uint32be _newValue = newValue;
uint32be previousValue = mem->exchange(_newValue);
return previousValue;
}
bool OSCompareAndSwapAtomic(std::atomic<uint32be>* mem, uint32 compareValue, uint32 swapValue)
{
// seen in GTA3 homebrew port
uint32be _compareValue = compareValue;
uint32be _swapValue = swapValue;
return mem->compare_exchange_strong(_compareValue, _swapValue);
}
bool OSCompareAndSwapAtomicEx(std::atomic<uint32be>* mem, uint32 compareValue, uint32 swapValue, uint32be* previousValue)
{
// seen in GTA3 homebrew port
uint32be _compareValue = compareValue;
uint32be _swapValue = swapValue;
bool r = mem->compare_exchange_strong(_compareValue, _swapValue);
*previousValue = _compareValue;
return r;
}
uint32 OSAddAtomic(std::atomic<uint32be>* mem, uint32 adder)
{
uint32be knownValue;
while (true)
{
uint32be knownValue = mem->load();
uint32be newValue = knownValue + adder;
if (mem->compare_exchange_strong(knownValue, newValue))
break;
}
return knownValue;
}
/* 64bit atomic operations */
uint64 OSSwapAtomic64(std::atomic<uint64be>* mem, uint64 newValue)
{
uint64be _newValue = newValue;
uint64be previousValue = mem->exchange(_newValue);
return previousValue;
}
uint64 OSSetAtomic64(std::atomic<uint64be>* mem, uint64 newValue)
{
return OSSwapAtomic64(mem, newValue);
}
uint64 OSGetAtomic64(std::atomic<uint64be>* mem)
{
return mem->load();
}
uint64 OSAddAtomic64(std::atomic<uint64be>* mem, uint64 adder)
{
uint64be knownValue;
while (true)
{
uint64be knownValue = mem->load();
uint64be newValue = knownValue + adder;
if (mem->compare_exchange_strong(knownValue, newValue))
break;
}
return knownValue;
}
uint64 OSAndAtomic64(std::atomic<uint64be>* mem, uint64 val)
{
uint64be knownValue;
while (true)
{
uint64be knownValue = mem->load();
uint64be newValue = knownValue & val;
if (mem->compare_exchange_strong(knownValue, newValue))
break;
}
return knownValue;
}
uint64 OSOrAtomic64(std::atomic<uint64be>* mem, uint64 val)
{
uint64be knownValue;
while (true)
{
uint64be knownValue = mem->load();
uint64be newValue = knownValue | val;
if (mem->compare_exchange_strong(knownValue, newValue))
break;
}
return knownValue;
}
bool OSCompareAndSwapAtomic64(std::atomic<uint64be>* mem, uint64 compareValue, uint64 swapValue)
{
uint64be _compareValue = compareValue;
uint64be _swapValue = swapValue;
return mem->compare_exchange_strong(_compareValue, _swapValue);
}
bool OSCompareAndSwapAtomicEx64(std::atomic<uint64be>* mem, uint64 compareValue, uint64 swapValue, uint64be* previousValue)
{
uint64be _compareValue = compareValue;
uint64be _swapValue = swapValue;
bool r = mem->compare_exchange_strong(_compareValue, _swapValue);
*previousValue = _compareValue;
return r;
}
void InitializeAtomic()
{
// 32bit atomic operations
cafeExportRegister("coreinit", OSSwapAtomic, LogType::Placeholder);
cafeExportRegister("coreinit", OSCompareAndSwapAtomic, LogType::Placeholder);
cafeExportRegister("coreinit", OSCompareAndSwapAtomicEx, LogType::Placeholder);
cafeExportRegister("coreinit", OSAddAtomic, LogType::Placeholder);
// 64bit atomic operations
cafeExportRegister("coreinit", OSSetAtomic64, LogType::Placeholder);
cafeExportRegister("coreinit", OSGetAtomic64, LogType::Placeholder);
cafeExportRegister("coreinit", OSSwapAtomic64, LogType::Placeholder);
cafeExportRegister("coreinit", OSAddAtomic64, LogType::Placeholder);
cafeExportRegister("coreinit", OSAndAtomic64, LogType::Placeholder);
cafeExportRegister("coreinit", OSOrAtomic64, LogType::Placeholder);
cafeExportRegister("coreinit", OSCompareAndSwapAtomic64, LogType::Placeholder);
cafeExportRegister("coreinit", OSCompareAndSwapAtomicEx64, LogType::Placeholder);
}
}

View file

@ -0,0 +1,21 @@
#pragma once
#include <atomic>
namespace coreinit
{
uint32 OSSwapAtomic(std::atomic<uint32be>* mem, uint32 newValue);
bool OSCompareAndSwapAtomic(std::atomic<uint32be>* mem, uint32 compareValue, uint32 swapValue);
bool OSCompareAndSwapAtomicEx(std::atomic<uint32be>* mem, uint32 compareValue, uint32 swapValue, uint32be* previousValue);
uint32 OSAddAtomic(std::atomic<uint32be>* mem, uint32 adder);
uint64 OSSwapAtomic64(std::atomic<uint64be>* mem, uint64 newValue);
uint64 OSSetAtomic64(std::atomic<uint64be>* mem, uint64 newValue);
uint64 OSGetAtomic64(std::atomic<uint64be>* mem);
uint64 OSAddAtomic64(std::atomic<uint64be>* mem, uint64 adder);
uint64 OSAndAtomic64(std::atomic<uint64be>* mem, uint64 val);
uint64 OSOrAtomic64(std::atomic<uint64be>* mem, uint64 val);
bool OSCompareAndSwapAtomic64(std::atomic<uint64be>* mem, uint64 compareValue, uint64 swapValue);
bool OSCompareAndSwapAtomicEx64(std::atomic<uint64be>* mem, uint64 compareValue, uint64 swapValue, uint64be* previousValue);
void InitializeAtomic();
}

View file

@ -0,0 +1,19 @@
#include "Cafe/OS/common/OSCommon.h"
#include "coreinit_BSP.h"
namespace coreinit
{
bool bspGetHardwareVersion(uint32be* version)
{
uint8 highVersion = 0x11; // anything below 0x11 will be considered as Hollywood
// todo: Check version returned on console
uint32 tempVers = highVersion << 24;
*version = tempVers;
return true;
}
void InitializeBSP()
{
cafeExportRegister("coreinit", bspGetHardwareVersion, LogType::Placeholder);
}
}

View file

@ -0,0 +1,4 @@
namespace coreinit
{
void InitializeBSP();
};

View file

@ -0,0 +1,118 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "util/helpers/fspinlock.h"
struct CoreinitAsyncCallback
{
CoreinitAsyncCallback(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10) :
m_functionMPTR(functionMPTR), m_numParameters(numParameters), m_gprParam{ r3, r4, r5, r6, r7, r8, r9, r10 } {};
static void queue(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10)
{
s_asyncCallbackSpinlock.acquire();
s_asyncCallbackQueue.emplace_back(allocateAndInitFromPool(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10));
s_asyncCallbackSpinlock.release();
}
static void callNextFromQueue()
{
s_asyncCallbackSpinlock.acquire();
if (s_asyncCallbackQueue.empty())
{
cemuLog_log(LogType::Force, "AsyncCallbackQueue is empty. Unexpected behavior");
s_asyncCallbackSpinlock.release();
return;
}
CoreinitAsyncCallback* cb = s_asyncCallbackQueue[0];
s_asyncCallbackQueue.erase(s_asyncCallbackQueue.begin());
s_asyncCallbackSpinlock.release();
cb->doCall();
s_asyncCallbackSpinlock.acquire();
releaseToPool(cb);
s_asyncCallbackSpinlock.release();
}
private:
void doCall()
{
PPCCoreCallback(m_functionMPTR, m_gprParam[0], m_gprParam[1], m_gprParam[2], m_gprParam[3], m_gprParam[4], m_gprParam[5], m_gprParam[6], m_gprParam[7]);
}
static CoreinitAsyncCallback* allocateAndInitFromPool(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10)
{
cemu_assert_debug(s_asyncCallbackSpinlock.isHolding());
if (s_asyncCallbackPool.empty())
{
CoreinitAsyncCallback* cb = new CoreinitAsyncCallback(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10);
return cb;
}
CoreinitAsyncCallback* cb = s_asyncCallbackPool[0];
s_asyncCallbackPool.erase(s_asyncCallbackPool.begin());
*cb = CoreinitAsyncCallback(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10);
return cb;
}
static void releaseToPool(CoreinitAsyncCallback* cb)
{
cemu_assert_debug(s_asyncCallbackSpinlock.isHolding());
s_asyncCallbackPool.emplace_back(cb);
}
static std::vector<struct CoreinitAsyncCallback*> s_asyncCallbackPool;
static std::vector<struct CoreinitAsyncCallback*> s_asyncCallbackQueue;
static FSpinlock s_asyncCallbackSpinlock;
sint32 m_numParameters;
uint32 m_gprParam[9];
MPTR m_functionMPTR;
};
std::vector<struct CoreinitAsyncCallback*> CoreinitAsyncCallback::s_asyncCallbackPool;
std::vector<struct CoreinitAsyncCallback*> CoreinitAsyncCallback::s_asyncCallbackQueue;
FSpinlock CoreinitAsyncCallback::s_asyncCallbackSpinlock;
SysAllocator<OSThread_t> g_coreinitCallbackThread;
SysAllocator<uint8, 1024*64> _g_coreinitCallbackThreadStack;
SysAllocator<coreinit::OSSemaphore> g_asyncCallbackAsync;
SysAllocator<char, 32> _g_coreinitCBThreadName;
void _coreinitCallbackThread(PPCInterpreter_t* hCPU)
{
while (coreinit::OSWaitSemaphore(g_asyncCallbackAsync.GetPtr()))
{
CoreinitAsyncCallback::callNextFromQueue();
}
}
void coreinitAsyncCallback_addWithLock(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10)
{
cemu_assert_debug(numParameters <= 8);
if (functionMPTR >= 0x10000000)
{
cemuLog_log(LogType::Force, fmt::format("Suspicious callback address {0:08x} params: {1:08x} {2:08x} {3:08x} {4:08x}", functionMPTR, r3, r4, r5, r6));
cemuLog_waitForFlush();
}
CoreinitAsyncCallback::queue(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10);
__OSLockScheduler();
coreinit::OSSignalSemaphoreInternal(g_asyncCallbackAsync.GetPtr(), false);
__OSUnlockScheduler();
}
void coreinitAsyncCallback_add(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10)
{
cemu_assert_debug(__OSHasSchedulerLock() == false); // do not call when holding scheduler lock
coreinitAsyncCallback_addWithLock(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10);
}
void InitializeAsyncCallback()
{
coreinit::OSInitSemaphore(g_asyncCallbackAsync.GetPtr(), 0);
coreinit::OSCreateThreadType(g_coreinitCallbackThread.GetPtr(), PPCInterpreter_makeCallableExportDepr(_coreinitCallbackThread), 0, nullptr, _g_coreinitCallbackThreadStack.GetPtr() + _g_coreinitCallbackThreadStack.GetByteSize(), (sint32)_g_coreinitCallbackThreadStack.GetByteSize(), 0, 7, OSThread_t::THREAD_TYPE::TYPE_IO);
coreinit::OSResumeThread(g_coreinitCallbackThread.GetPtr());
strcpy(_g_coreinitCBThreadName.GetPtr(), "Callback Thread");
coreinit::OSSetThreadName(g_coreinitCallbackThread.GetPtr(), _g_coreinitCBThreadName.GetPtr());
}

View file

@ -0,0 +1,148 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/libs/coreinit/coreinit_CodeGen.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#include "Common/ExceptionHandler/ExceptionHandler.h"
namespace coreinit
{
struct
{
bool rangeIsAllocated;
MPTR rangeStart;
uint32 rangeSize;
uint8* cacheStateCopy; // holds a copy of the entire range, simulates icache state (updated via ICBI)
}coreinitCodeGen = {0};
void codeGenArea_memoryWriteCallback(void* pageStart, size_t size)
{
uint32 ea = memory_getVirtualOffsetFromPointer(pageStart);
uint32 eaEnd = ea + (uint32)size;
while (ea <= eaEnd)
{
codeGenHandleICBI(ea);
ea += 0x20;
}
}
void OSGetCodegenVirtAddrRange(betype<uint32>* rangeStart, betype<uint32>* rangeSize)
{
uint32 codegenSize = 0x01000000; // todo: Read from cos.xml
//debug_printf("OSGetCodegenVirtAddrRange(0x%08x,0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4]);
// on first call, allocate range
if( coreinitCodeGen.rangeIsAllocated == false )
{
coreinitCodeGen.rangeStart = RPLLoader_AllocateCodeSpace(codegenSize, 0x1000);
coreinitCodeGen.rangeSize = codegenSize;
coreinitCodeGen.cacheStateCopy = new uint8[codegenSize];
memset(coreinitCodeGen.cacheStateCopy, 0, codegenSize);
coreinitCodeGen.rangeIsAllocated = true;
}
*rangeStart = coreinitCodeGen.rangeStart;
*rangeSize = coreinitCodeGen.rangeSize;
}
void OSGetCodegenVirtAddrRangeInternal(uint32& rangeStart, uint32& rangeSize)
{
if (coreinitCodeGen.rangeIsAllocated == 0)
{
rangeStart = 0;
rangeSize = 0;
return;
}
rangeStart = coreinitCodeGen.rangeStart;
rangeSize = coreinitCodeGen.rangeSize;
}
void ICInvalidateRange(uint32 startAddress, uint32 size)
{
uint32 ea = startAddress & ~0x1F;
uint32 eaEnd = (startAddress + size + 0x1F)&~0x1F;
while (ea <= eaEnd)
{
codeGenHandleICBI(ea);
ea += 0x20;
}
}
void coreinitExport_OSCodegenCopy(PPCInterpreter_t* hCPU)
{
if( coreinitCodeGen.rangeIsAllocated == false )
assert_dbg();
uint32 codeAddrDest = hCPU->gpr[3];
uint32 memAddrSrc = hCPU->gpr[4];
uint32 size = hCPU->gpr[5];
if( codeAddrDest < coreinitCodeGen.rangeStart || codeAddrDest >= (coreinitCodeGen.rangeStart+coreinitCodeGen.rangeSize) )
assert_dbg();
if( (codeAddrDest+size) < coreinitCodeGen.rangeStart || (codeAddrDest+size) > (coreinitCodeGen.rangeStart+coreinitCodeGen.rangeSize) )
assert_dbg();
memcpy(memory_getPointerFromVirtualOffset(codeAddrDest), memory_getPointerFromVirtualOffset(memAddrSrc), size);
// invalidate recompiler range
uint32 ea = codeAddrDest & ~0x1F;
uint32 eaEnd = (codeAddrDest + size + 0x1F)&~0x1F;
while (ea <= eaEnd)
{
codeGenHandleICBI(ea);
ea += 0x20;
}
osLib_returnFromFunction(hCPU, 0);
}
void codeGenHandleICBI(uint32 ea)
{
cemu_assert_debug((ea & 0x1F) == 0);
if (coreinitCodeGen.rangeIsAllocated == false)
return;
cemu_assert_debug((coreinitCodeGen.rangeStart & 0x1F) == 0);
cemu_assert_debug((coreinitCodeGen.rangeSize & 0x1F) == 0);
if (ea >= coreinitCodeGen.rangeStart && ea < (coreinitCodeGen.rangeStart + coreinitCodeGen.rangeSize))
{
uint8* cacheCopy = coreinitCodeGen.cacheStateCopy + (ea - coreinitCodeGen.rangeStart);
uint8* currentState = memory_getPointerFromVirtualOffset(ea);
if (memcmp(currentState, cacheCopy, 32) != 0)
{
// instructions changed
// flush cache
PPCRecompiler_invalidateRange(ea, ea+0x20);
// update icache copy
memcpy(cacheCopy, currentState, 32);
}
}
}
bool _avoidCodeGenJIT = false;
// currently we dont handle code invalidation well for direct write access
// therefore if we detect attempts to write we will disable JITing the area
bool codeGenShouldAvoid()
{
return _avoidCodeGenJIT;
}
bool OSSwitchSecCodeGenMode(bool isRXOnly)
{
if (!_avoidCodeGenJIT)
{
forceLog_printf("Disable JIT on dynamic code area");
}
_avoidCodeGenJIT = true; // this function getting called is usually a sign that
// does this have a return value?
return true;
}
void InitializeCodeGen()
{
cafeExportRegister("coreinit", OSGetCodegenVirtAddrRange, LogType::Placeholder);
cafeExportRegister("coreinit", ICInvalidateRange, LogType::Placeholder);
osLib_addFunction("coreinit", "OSCodegenCopy", coreinitExport_OSCodegenCopy);
cafeExportRegister("coreinit", OSSwitchSecCodeGenMode, LogType::Placeholder);
}
}

View file

@ -0,0 +1,10 @@
#pragma once
namespace coreinit
{
void OSGetCodegenVirtAddrRangeInternal(uint32& rangeStart, uint32& rangeSize);
void codeGenHandleICBI(uint32 ea);
bool codeGenShouldAvoid();
void InitializeCodeGen();
}

View file

@ -0,0 +1,100 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_Coroutine.h"
#include "Cafe/HW/Espresso/PPCState.h"
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h"
#include "Cafe/HW/MMU/MMU.h"
namespace coreinit
{
void coreinitExport_OSInitCoroutine(PPCInterpreter_t* hCPU)
{
OSCoroutine* coroutine = (OSCoroutine*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
coroutine->lr = _swapEndianU32(hCPU->gpr[4]);
coroutine->r1 = _swapEndianU32(hCPU->gpr[5]);
osLib_returnFromFunction(hCPU, 0);
}
void coreinitCoroutine_OSSaveCoroutine(OSCoroutine* coroutine, PPCInterpreter_t* hCPU)
{
coroutine->lr = _swapEndianU32(hCPU->spr.LR);
coroutine->cr = _swapEndianU32(ppc_getCR(hCPU));
coroutine->gqr1 = _swapEndianU32(hCPU->spr.UGQR[1]);
coroutine->r1 = _swapEndianU32(hCPU->gpr[1]);
coroutine->r2 = _swapEndianU32(hCPU->gpr[2]);
coroutine->r13 = _swapEndianU32(hCPU->gpr[13]);
for (sint32 i = 14; i < 32; i++)
coroutine->gpr[i - 14] = _swapEndianU32(hCPU->gpr[i]);
for (sint32 i = 14; i < 32; i++)
{
coroutine->fpr[i - 14] = _swapEndianU64(hCPU->fpr[i].fp0int);
}
for (sint32 i = 14; i < 32; i++)
{
coroutine->psr[i - 14] = _swapEndianU64(hCPU->fpr[i].fp1int);
}
}
void coreinitCoroutine_OSLoadCoroutine(OSCoroutine* coroutine, PPCInterpreter_t* hCPU)
{
hCPU->spr.LR = _swapEndianU32(coroutine->lr);
ppc_setCR(hCPU, _swapEndianU32(coroutine->cr));
hCPU->spr.UGQR[1] = _swapEndianU32(coroutine->gqr1);
hCPU->gpr[1] = _swapEndianU32(coroutine->r1);
hCPU->gpr[2] = _swapEndianU32(coroutine->r2);
hCPU->gpr[13] = _swapEndianU32(coroutine->r13);
for (sint32 i = 14; i < 32; i++)
hCPU->gpr[i] = _swapEndianU32(coroutine->gpr[i - 14]);
for (sint32 i = 14; i < 32; i++)
{
hCPU->fpr[i].fp0int = _swapEndianU64(coroutine->fpr[i - 14]);
}
for (sint32 i = 14; i < 32; i++)
{
hCPU->fpr[i].fp1int = _swapEndianU64(coroutine->psr[i - 14]);
}
}
void coreinitExport_OSSwitchCoroutine(PPCInterpreter_t* hCPU)
{
OSCoroutine* coroutineCurrent = (OSCoroutine*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
OSCoroutine* coroutineNext = (OSCoroutine*)memory_getPointerFromVirtualOffsetAllowNull(hCPU->gpr[4]);
coreinitCoroutine_OSSaveCoroutine(coroutineCurrent, hCPU);
if (coroutineNext != NULL)
{
coreinitCoroutine_OSLoadCoroutine(coroutineNext, hCPU);
}
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_OSSwitchFiberEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(param0, 0);
ppcDefineParamU32(param1, 1);
ppcDefineParamU32(param2, 2);
ppcDefineParamU32(param3, 3);
ppcDefineParamMPTR(newInstructionPointer, 4);
ppcDefineParamMPTR(newStackPointer, 5);
MPTR prevStackpointer = hCPU->gpr[1];
hCPU->gpr[1] = newStackPointer;
hCPU->gpr[3] = param0;
hCPU->gpr[4] = param1;
hCPU->gpr[5] = param2;
hCPU->gpr[6] = param3;
PPCCore_executeCallbackInternal(newInstructionPointer);
uint32 returnValue = hCPU->gpr[3];
hCPU->gpr[1] = prevStackpointer;
osLib_returnFromFunction(hCPU, returnValue);
}
void InitializeCoroutine()
{
osLib_addFunction("coreinit", "OSInitCoroutine", coreinitExport_OSInitCoroutine);
osLib_addFunction("coreinit", "OSSwitchCoroutine", coreinitExport_OSSwitchCoroutine);
osLib_addFunction("coreinit", "OSSwitchFiberEx", coreinitExport_OSSwitchFiberEx);
}
}

View file

@ -0,0 +1,21 @@
#pragma once
namespace coreinit
{
struct OSCoroutine
{
/* +0x00 */ uint32 lr;
/* +0x04 */ uint32 cr;
/* +0x08 */ uint32 gqr1;
/* +0x0C */ uint32 r1; // stack pointer
/* +0x10 */ uint32 r2;
/* +0x14 */ uint32 r13;
/* +0x18 */ uint32 gpr[18];
uint64 fpr[18];
uint64 psr[18];
};
static_assert(sizeof(OSCoroutine) == 0x180);
void InitializeCoroutine();
}

View file

@ -0,0 +1,147 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/libs/coreinit/coreinit_DynLoad.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
namespace coreinit
{
MPTR _osDynLoadFuncAlloc = MPTR_NULL;
MPTR _osDynLoadFuncFree = MPTR_NULL;
MPTR _osDynLoadTLSFuncAlloc = MPTR_NULL;
MPTR _osDynLoadTLSFuncFree = MPTR_NULL;
uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc)
{
_osDynLoadFuncAlloc = allocFunc;
_osDynLoadFuncFree = freeFunc;
return 0;
}
void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc)
{
_osDynLoadTLSFuncAlloc = allocFunc;
_osDynLoadTLSFuncFree = freeFunc;
}
uint32 OSDynLoad_GetAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree)
{
*funcAlloc = _osDynLoadFuncAlloc;
*funcFree = _osDynLoadFuncFree;
return 0;
}
void OSDynLoad_GetTLSAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree)
{
*funcAlloc = _osDynLoadTLSFuncAlloc;
*funcFree = _osDynLoadTLSFuncFree;
}
void* OSDynLoad_AllocatorAlloc(sint32 size, sint32 alignment)
{
if (_osDynLoadFuncAlloc == MPTR_NULL)
return MPTR_NULL;
StackAllocator<MEMPTR<void>> _ptrStorage;
int r = PPCCoreCallback(_osDynLoadFuncAlloc, size, alignment, _ptrStorage.GetMPTR());
if (r != 0)
{
cemu_assert_debug(false);
return MPTR_NULL;
}
return _ptrStorage->GetPtr();
}
void OSDynLoad_AllocatorFree(void* mem)
{
if (_osDynLoadFuncFree == MPTR_NULL)
return;
MEMPTR<void> _mem = mem;
PPCCoreCallback(_osDynLoadFuncFree, _mem);
}
uint32 OSDynLoad_Acquire(const char* libName, uint32be* moduleHandleOut)
{
// truncate path
sint32 fileNameStartIndex = 0;
sint32 tempLen = (sint32)strlen(libName);
for (sint32 i = tempLen - 1; i >= 0; i--)
{
if (libName[i] == '/')
{
fileNameStartIndex = i + 1;
break;
}
}
// truncate file extension
char tempLibName[512];
strcpy(tempLibName, libName + fileNameStartIndex);
tempLen = (sint32)strlen(tempLibName);
for (sint32 i = tempLen - 1; i >= 0; i--)
{
if (tempLibName[i] == '.')
{
tempLibName[i] = '\0';
break;
}
}
// search for loaded modules with matching name
uint32 rplHandle = RPLLoader_GetHandleByModuleName(libName);
if (rplHandle == RPL_INVALID_HANDLE)
{
RPLLoader_AddDependency(libName);
RPLLoader_UpdateDependencies();
RPLLoader_Link();
RPLLoader_CallEntrypoints();
rplHandle = RPLLoader_GetHandleByModuleName(libName);
if (rplHandle == RPL_INVALID_HANDLE)
rplHandle = 0;
}
*moduleHandleOut = rplHandle;
// return module not found error code
if (rplHandle == RPL_INVALID_HANDLE)
return 0xFFFCFFE9;
return 0;
}
uint32 OSDynLoad_Release(uint32 moduleHandle)
{
if (moduleHandle == RPL_INVALID_HANDLE)
return 0;
RPLLoader_RemoveDependency(moduleHandle);
RPLLoader_UpdateDependencies();
// this function isn't supposed to return anything, but early versions of Cemu did and Cemuhook (up to 0.5.7.6) now relies on it. We still keep the return value around for compatibility
return 0;
}
uint32 OSDynLoad_FindExport(uint32 moduleHandle, uint32 isData, const char* exportName, betype<MPTR>* addrOut)
{
if (moduleHandle == 0xFFFFFFFF)
{
// main module
// Assassins Creed 4 has this handle hardcoded
moduleHandle = RPLLoader_GetMainModuleHandle();
}
MPTR exportResult = RPLLoader_FindModuleOrHLEExport(moduleHandle, isData, exportName);
*addrOut = exportResult;
if (exportResult == MPTR_NULL)
return 0xFFFFFFFF;
return 0;
}
void InitializeDynLoad()
{
cafeExportRegister("coreinit", OSDynLoad_SetAllocator, LogType::Placeholder);
cafeExportRegister("coreinit", OSDynLoad_SetTLSAllocator, LogType::Placeholder);
cafeExportRegister("coreinit", OSDynLoad_GetAllocator, LogType::Placeholder);
cafeExportRegister("coreinit", OSDynLoad_GetTLSAllocator, LogType::Placeholder);
cafeExportRegister("coreinit", OSDynLoad_Acquire, LogType::Placeholder);
cafeExportRegister("coreinit", OSDynLoad_Release, LogType::Placeholder);
cafeExportRegister("coreinit", OSDynLoad_FindExport, LogType::Placeholder);
}
}

View file

@ -0,0 +1,18 @@
#pragma once
namespace coreinit
{
uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc);
void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc);
uint32 OSDynLoad_GetAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree);
void OSDynLoad_GetTLSAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree);
void* OSDynLoad_AllocatorAlloc(sint32 size, sint32 alignment);
void OSDynLoad_AllocatorFree(void* mem);
uint32 OSDynLoad_Acquire(const char* libName, uint32be* moduleHandleOut);
uint32 OSDynLoad_Release(uint32 moduleHandle);
uint32 OSDynLoad_FindExport(uint32 moduleHandle, uint32 isData, const char* exportName, betype<MPTR>* addrOut);
void InitializeDynLoad();
}

View file

@ -0,0 +1,200 @@
#include "Cafe/OS/common/OSCommon.h"
#include <memory>
#define FG_BUCKET_AREA_FREE 0 // free area available game
#define FG_BUCKET_AREA_AUDIO_TRANSITION 1 // transition audio buffer
#define FG_BUCKET_AREA2 2 // frame storage? TV?
#define FG_BUCKET_AREA3 3 // frame storage? DRC?
#define FG_BUCKET_AREA4 4 // frame storage? TV?
#define FG_BUCKET_AREA5 5 // frame storage? DRC?
#define FG_BUCKET_AREA_SAVE 6
#define FG_BUCKET_AREA_COPY 7 // for OS copy data (clipboard and title switch parameters)
#define FG_BUCKET_AREA_FREE_SIZE 0x2800000
#define FG_BUCKET_AREA_SAVE_SIZE 0x1000
#define FG_BUCKET_AREA_COPY_SIZE 0x400000
#define FG_BUCKET_AREA_COUNT 8
namespace coreinit
{
MEMPTR<void> fgAddr = nullptr; // NULL if not in foreground
MEMPTR<uint8> fgSaveAreaAddr = nullptr;
struct
{
uint32 id;
uint32 startOffset;
uint32 size;
}fgAreaEntries[FG_BUCKET_AREA_COUNT] =
{
{ 0, 0, 0x2800000 },
{ 7, 0x2800000, 0x400000 },
{ 1, 0x2C00000, 0x900000 },
{ 2, 0x3500000, 0x3C0000 },
{ 3, 0x38C0000, 0x1C0000 },
{ 4, 0x3A80000, 0x3C0000 },
{ 5, 0x3E40000, 0x1BF000 },
{ 6, 0x3FFF000, 0x1000 }
};
MEMPTR<uint8> GetFGMemByArea(uint32 areaId)
{
if (fgAddr == nullptr)
return nullptr;
for (sint32 i = 0; i < FG_BUCKET_AREA_COUNT; i++)
{
if (fgAreaEntries[i].id == areaId)
return MEMPTR<uint8>(fgAddr.GetPtr<uint8>() + fgAreaEntries[i].startOffset);
}
return nullptr;
}
bool OSGetForegroundBucket(MEMPTR<void>* offset, uint32be* size)
{
// return full size of foreground bucket area
if (offset)
*offset = MEMPTR<void>{ (uint32)MEMORY_FGBUCKET_AREA_ADDR };
if (size)
*size = MEMORY_FGBUCKET_AREA_SIZE;
// return true if in foreground
return true;
}
bool OSGetForegroundBucketFreeArea(MPTR* offset, MPTR* size)
{
uint8* freeAreaAddr = GetFGMemByArea(FG_BUCKET_AREA_FREE).GetPtr();
*offset = _swapEndianU32(memory_getVirtualOffsetFromPointer(freeAreaAddr));
*size = _swapEndianU32(FG_BUCKET_AREA_FREE_SIZE);
// return true if in foreground
return (fgAddr != nullptr);
}
void coreinitExport_OSGetForegroundBucket(PPCInterpreter_t* hCPU)
{
//debug_printf("OSGetForegroundBucket(0x%x,0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
// returns the whole FG bucket area (if the current process is in the foreground)
ppcDefineParamMPTR(areaOutput, 0);
ppcDefineParamMPTR(areaSize, 1);
bool r = OSGetForegroundBucket((MEMPTR<void>*)memory_getPointerFromVirtualOffsetAllowNull(areaOutput), (uint32be*)memory_getPointerFromVirtualOffsetAllowNull(areaSize));
osLib_returnFromFunction(hCPU, r ? 1 : 0);
}
void coreinitExport_OSGetForegroundBucketFreeArea(PPCInterpreter_t* hCPU)
{
debug_printf("OSGetForegroundBucketFreeArea(0x%x,0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
ppcDefineParamMPTR(areaOutput, 0);
ppcDefineParamMPTR(areaSize, 1);
bool r = OSGetForegroundBucketFreeArea((MPTR*)memory_getPointerFromVirtualOffsetAllowNull(areaOutput), (MPTR*)memory_getPointerFromVirtualOffsetAllowNull(areaSize));
osLib_returnFromFunction(hCPU, r ? 1 : 0);
}
void InitForegroundBucket()
{
uint32be fgSize;
OSGetForegroundBucket(&fgAddr, &fgSize);
uint8* freeAreaPtr = GetFGMemByArea(FG_BUCKET_AREA_FREE).GetPtr();
memset(freeAreaPtr, 0, FG_BUCKET_AREA_FREE_SIZE);
uint8* saveAreaPtr = GetFGMemByArea(FG_BUCKET_AREA_SAVE).GetPtr();
fgSaveAreaAddr = saveAreaPtr;
if (*(uint32be*)saveAreaPtr != (uint32be)'Save')
{
// clear save area
memset(saveAreaPtr, 0, FG_BUCKET_AREA_SAVE_SIZE);
// clear copy area
memset(GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr(), 0, FG_BUCKET_AREA_COPY_SIZE);
// init save area
*(uint32be*)(saveAreaPtr + 0x00) = 'Save';
*(uint32be*)(saveAreaPtr + 0x08) |= 0x300;
}
}
void __OSClearCopyData()
{
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
if (fgCopyArea)
{
*(uint32be*)(fgCopyArea + 0x00) = 0;
}
}
uint32 __OSGetCopyDataSize()
{
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
if (fgCopyArea)
{
return *(uint32be*)(fgCopyArea + 0x00);
}
return 0;
}
uint8* __OSGetCopyDataPtr()
{
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
if (fgCopyArea)
return fgCopyArea + 4;
return nullptr;
}
bool __OSAppendCopyData(uint8* data, sint32 length)
{
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
if (fgCopyArea == nullptr)
return false;
uint32 currentOffset = *(uint32be*)(fgCopyArea + 0x00);
if ((currentOffset + length) > FG_BUCKET_AREA_COPY_SIZE - 4)
{
return false;
}
memcpy(fgCopyArea + currentOffset + 4, data, length);
*(uint32be*)(fgCopyArea + 0x00) += length;
return true;
}
bool __OSResizeCopyData(sint32 length)
{
uint8* fgCopyArea = GetFGMemByArea(FG_BUCKET_AREA_COPY).GetPtr();
if (fgCopyArea == nullptr)
return false;
sint32 currentOffset = (sint32) * (uint32be*)(fgCopyArea + 0x00);
if (length < currentOffset)
{
// can only make copy data smaller
*(uint32be*)(fgCopyArea + 0x00) = length;
return true;
}
return false;
}
bool OSCopyFromClipboard(void* data, uint32be* size)
{
uint32 cSize = *size;
if (cSize == 0 && data == nullptr)
return false;
if (OSGetForegroundBucket(nullptr, nullptr) == false)
return false;
// todo
*size = 0;
return true;
}
void coreinitExport_OSCopyFromClipboard(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("OSCopyFromClipboard(0x%x,0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
ppcDefineParamMEMPTR(buffer, void, 0);
ppcDefineParamMEMPTR(size, uint32be, 1);
bool r = OSCopyFromClipboard(buffer.GetPtr(), size.GetPtr());
osLib_returnFromFunction(hCPU, r ? 1 : 0);
}
void InitializeFG()
{
osLib_addFunction("coreinit", "OSGetForegroundBucket", coreinitExport_OSGetForegroundBucket);
osLib_addFunction("coreinit", "OSGetForegroundBucketFreeArea", coreinitExport_OSGetForegroundBucketFreeArea);
osLib_addFunction("coreinit", "OSCopyFromClipboard", coreinitExport_OSCopyFromClipboard);
}
}

View file

@ -0,0 +1,17 @@
#pragma once
namespace coreinit
{
void __OSClearCopyData();
uint32 __OSGetCopyDataSize();
uint8* __OSGetCopyDataPtr();
bool __OSAppendCopyData(uint8* data, sint32 length);
bool __OSResizeCopyData(sint32 length);
bool OSGetForegroundBucket(MEMPTR<void>* offset, uint32be* size);
bool OSGetForegroundBucketFreeArea(MPTR* offset, MPTR* size);
void InitForegroundBucket();
void InitializeFG();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,325 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Cafe/IOSU/iosu_ipc_common.h"
#include "Cafe/IOSU/fsa/fsa_types.h"
#include "Cafe/IOSU/fsa/iosu_fsa.h"
#include "coreinit_MessageQueue.h"
typedef struct
{
uint32be fileHandle;
}FSFileHandleDepr_t;
typedef MEMPTR<betype<FSDirHandle2>> FSDirHandlePtr;
typedef struct
{
MEMPTR<void> userCallback;
MEMPTR<void> userContext;
MEMPTR<coreinit::OSMessageQueue> ioMsgQueue;
}FSAsyncParamsNew_t;
static_assert(sizeof(FSAsyncParamsNew_t) == 0xC);
typedef struct
{
MPTR userCallback; // 0x96C
MPTR userContext;
MPTR ioMsgQueue;
}FSAsyncParams_t; // legacy struct. Replace with FSAsyncParamsNew_t
namespace coreinit
{
struct FSCmdQueue
{
enum class QUEUE_FLAG : uint32
{
IS_FULL = (1 << 0), // waiting for Ioctl(v) result
CANCEL_ALL = (1 << 4),
};
/* +0x00 */ MPTR firstMPTR;
/* +0x04 */ MPTR lastMPTR;
/* +0x08 */ OSMutex mutex;
/* +0x34 */ MPTR dequeueHandlerFuncMPTR;
/* +0x38 */ uint32be numCommandsInFlight;
/* +0x3C */ uint32 numMaxCommandsInFlight;
/* +0x40 */ betype<QUEUE_FLAG> queueFlags;
};
DEFINE_ENUM_FLAG_OPERATORS(FSCmdQueue::QUEUE_FLAG);
#define FS_CLIENT_BUFFER_SIZE (5888)
#define FS_CMD_BLOCK_SIZE (2688)
struct FSClient_t
{
uint8 buffer[FS_CLIENT_BUFFER_SIZE];
};
struct FSCmdBlock_t
{
union
{
uint8 buffer[FS_CMD_BLOCK_SIZE];
struct
{
uint32 mount_it;
}data;
};
};
static_assert(sizeof(FSCmdBlock_t) == FS_CMD_BLOCK_SIZE);
struct FSClientBody_t
{
uint8 ukn0000[0x100];
uint8 ukn0100[0x100];
uint8 ukn0200[0x100];
uint8 ukn0300[0x100];
uint8 ukn0400[0x100];
uint8 ukn0500[0x100];
uint8 ukn0600[0x100];
uint8 ukn0700[0x100];
uint8 ukn0800[0x100];
uint8 ukn0900[0x100];
uint8 ukn0A00[0x100];
uint8 ukn0B00[0x100];
uint8 ukn0C00[0x100];
uint8 ukn0D00[0x100];
uint8 ukn0E00[0x100];
uint8 ukn0F00[0x100];
uint8 ukn1000[0x100];
uint8 ukn1100[0x100];
uint8 ukn1200[0x100];
uint8 ukn1300[0x100];
uint8 ukn1400[0x10];
uint8 ukn1410[0x10];
uint8 ukn1420[0x10];
uint8 ukn1430[0x10];
uint32 ukn1440;
betype<IOSDevHandle> iosuFSAHandle;
uint32 ukn1448;
uint32 ukn144C;
uint8 ukn1450[0x10];
uint8 ukn1460[0x10];
uint8 ukn1470[0x10];
FSCmdQueue fsCmdQueue;
/* +0x14C4 */ MEMPTR<struct FSCmdBlockBody_t> currentCmdBlockBody; // set to currently active cmd
uint32 ukn14C8;
uint32 ukn14CC;
uint8 ukn14D0[0x10];
uint8 ukn14E0[0x10];
uint8 ukn14F0[0x10];
uint8 ukn1500[0x100];
uint32 ukn1600;
uint32 ukn1604;
uint32 ukn1608;
uint32 ukn160C;
uint32 ukn1610;
MEMPTR<FSClientBody_t> fsClientBodyNext; // next FSClientBody_t* in list of registered clients (list is circular, the last element points to the first element)
uint32 ukn1618;
/* +0x161C */ MEMPTR<FSClient_t> selfClient; // pointer to FSClient struct which holds this FSClientBody
uint32 ukn1620;
};
struct FSAsyncResult
{
/* +0x00 */ FSAsyncParamsNew_t fsAsyncParamsNew;
// fs message storage
struct FSMessage
{
/* +0x0C / 0x0978 */ MEMPTR<FSAsyncResult> fsAsyncResult;
/* +0x10 */ MPTR fsClientMPTR2; // 0x097C
/* +0x14 */ MPTR fsCmdBlockMPTR; // 0x0980
/* +0x18 */ MPTR commandType; // 0x0984
};
union
{
OSMessage osMsg;
FSMessage fsMsg;
}msgUnion;
/* +0x1C */ MEMPTR<FSClient_t> fsClient; // 0x0988
/* +0x20 */ MEMPTR<FSCmdBlock_t> fsCmdBlock; // 0x98C
/* +0x24 */ uint32be fsStatusNew; // 0x990
};
static_assert(sizeof(FSAsyncResult) == 0x28);
struct FSCmdBlockBody_t
{
iosu::fsa::FSAIpcCommand ipcData;
uint8 ukn0820[0x10];
uint8 ukn0830[0x10];
uint8 ukn0840[0x10];
uint8 ukn0850[0x10];
uint8 ukn0860[0x10];
uint8 ukn0870[0x10];
MPTR fsCmdBlockBodyMPTR;
uint32 ukn0884;
uint32 ukn0888;
uint32 destBuffer88CMPTR;
uint32 ukn0890;
uint32 ukn0894;
uint32 ukn0898;
uint32 ukn089C;
uint32 ukn08A0;
uint32 ukn08A4;
uint32 ukn08A8;
uint32 ukn08AC;
uint8 ukn08B0[0x10];
uint8 ukn08C0[0x10];
uint8 ukn08D0[0x10];
uint8 ukn08E0[0x10];
uint8 ukn08F0[0x10];
/* +0x0900 */ uint32be operationType;
betype<IOSDevHandle> fsaDevHandle;
/* +0x0908 */ uint16be ipcReqType; // 0 -> IoctlAsync, 1 -> IoctlvAsync
uint8 ukn090A;
uint8 ukn090B;
uint32 ukn090C;
uint32 ukn0910;
uint32 ukn0914;
uint32 ukn0918;
uint32 ukn091C;
uint32 ukn0920;
uint32 ukn0924;
uint32 ukn0928;
uint32 ukn092C;
uint32 ukn0930;
uint32 ukn0934;
/* +0x0938 */ MEMPTR<FSClientBody_t> fsClientBody;
/* +0x093C */ uint32 statusCode; // not a status code but rather the state? Uses weird values for some reason
/* +0x0940 */ uint32be cancelState; // bitmask. Bit 0 -> If set command has been canceled
// return values
/* +0x0944 */ uint32 returnValueMPTR; // returnedFilePos (used to store pointer to variable that holds return value?), also used by QUERYINFO to store pointer for result. Also used for GetCwd() to hold the pointer for the returned dir path. Also used by OPENFILE to hold returned fileHandle
/* +0x0948 */ uint32 transferSize; // number of bytes to transfer
// transfer control?
uint32 uknVal094C;
uint32 transferElemSize; // number of bytes of a single transferred element (count of elements can be calculated via count = transferSize/transferElemSize)
uint32 uknVal0954; // this is set to max(0x10, transferSize) for reads and to min(0x40000, transferSize) for writes?
// link for cmd queue
MPTR nextMPTR; // points towards FSCmdQueue->first
MPTR previousMPTR; // points towards FSCmdQueue->last
/* +0x960 */ betype<FSA_RESULT> lastFSAStatus;
uint32 ukn0964;
/* +0x0968 */ uint8 errHandling; // return error flag mask
/* +0x096C */ FSAsyncResult asyncResult;
/* +0x0994 */ MEMPTR<void> userData;
/* +0x0998 */ OSMessageQueue syncTaskMsgQueue; // this message queue is used when mapping asynchronous tasks to synchronous API
/* +0x09D4 */ OSMessage _syncTaskMsg[1];
/* +0x09E4 */ MPTR cmdFinishFuncMPTR;
/* +0x09E8 */ uint8 priority;
uint8 uknStatusGuessed09E9;
uint8 ukn09EA;
uint8 ukn09EB;
uint32 ukn09EC;
uint32 ukn9F0;
uint32be ukn9F4_lastErrorRelated;
/* +0x9F8 */ MEMPTR<FSCmdBlock_t> selfCmdBlock;
uint32 ukn9FC;
};
static_assert(sizeof(FSAsyncParams_t) == 0xC);
static_assert(sizeof(FSCmdBlock_t) == 0xA80);
#define FSA_CMD_FLAG_SET_POS (1<<0)
#define FSA_CMD_OPERATION_TYPE_CHANGEDIR (0x5)
#define FSA_CMD_OPERATION_TYPE_GETCWD (0x6)
#define FSA_CMD_OPERATION_TYPE_MAKEDIR (0x7)
#define FSA_CMD_OPERATION_TYPE_REMOVE (0x8)
#define FSA_CMD_OPERATION_TYPE_RENAME (0x9)
#define FSA_CMD_OPERATION_TYPE_OPENDIR (0xA)
#define FSA_CMD_OPERATION_TYPE_READDIR (0xB)
#define FSA_CMD_OPERATION_TYPE_CLOSEDIR (0xD)
#define FSA_CMD_OPERATION_TYPE_OPENFILE (0xE)
#define FSA_CMD_OPERATION_TYPE_READ (0xF)
#define FSA_CMD_OPERATION_TYPE_WRITE (0x10)
#define FSA_CMD_OPERATION_TYPE_GETPOS (0x11)
#define FSA_CMD_OPERATION_TYPE_SETPOS (0x12)
#define FSA_CMD_OPERATION_TYPE_ISEOF (0x13)
#define FSA_CMD_OPERATION_TYPE_GETSTATFILE (0x14)
#define FSA_CMD_OPERATION_TYPE_CLOSEFILE (0x15)
#define FSA_CMD_OPERATION_TYPE_QUERYINFO (0x18)
#define FSA_CMD_OPERATION_TYPE_APPENDFILE (0x19)
#define FSA_CMD_OPERATION_TYPE_TRUNCATEFILE (0x1A)
#define FSA_CMD_OPERATION_TYPE_FLUSHQUOTA (0x1E)
#define FSA_CMD_STATUS_CODE_D900A21 0xD900A21 // cmd block is initialized
#define FSA_CMD_STATUS_CODE_D900A22 0xD900A22 // cmd block is queued
#define FSA_CMD_STATUS_CODE_D900A24 0xD900A24 // cmd block was processed and is available again
#define FSA_CMD_STATUS_CODE_D900A26 0xD900A26 // cmd block result is being processed
enum FS_VOLSTATE : sint32
{
FS_VOLSTATE_READY = 1,
};
// internal interface
sint32 __FSQueryInfoAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint8* queryString, uint32 queryType, void* queryResult, uint32 errHandling, FSAsyncParamsNew_t* fsAsyncParams);
// coreinit exports
FS_RESULT FSAddClientEx(FSClient_t* fsClient, uint32 uknR4, uint32 errHandling);
FS_RESULT FSAddClient(FSClient_t* fsClient, uint32 errHandling);
FS_RESULT FSDelClient(FSClient_t* fsClient, uint32 errHandling);
void FSInitCmdBlock(FSCmdBlock_t* fsCmdBlock);
sint32 FSOpenFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, char* mode, FSFileHandleDepr_t* fileHandle, uint32 errHandling, FSAsyncParamsNew_t* asyncParams);
sint32 FSOpenFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, char* mode, FSFileHandleDepr_t* fileHandle, uint32 errHandling);
sint32 FSReadFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSReadFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask);
sint32 FSReadFileWithPosAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSReadFileWithPos(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask);
sint32 FSWriteFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSWriteFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask);
sint32 FSWriteFileWithPosAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSWriteFileWithPos(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask);
sint32 FSSetPosFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 filePos, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSSetPosFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 filePos, uint32 errorMask);
sint32 FSGetPosFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32be* returnedFilePos, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSGetPosFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32be* returnedFilePos, uint32 errorMask);
sint32 FSAppendFileAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 size, uint32 count, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSAppendFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 size, uint32 count, uint32 errorMask);
sint32 FSIsEofAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSIsEof(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errorMask);
sint32 FSRenameAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* srcPath, char* dstPath, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSRename(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* srcPath, char* dstPath, uint32 errorMask);
sint32 FSRemoveAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint8* filePath, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSRemove(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint8* filePath, uint32 errorMask);
sint32 FSMakeDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const uint8* dirPath, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSMakeDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const uint8* path, uint32 errorMask);
sint32 FSChangeDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSChangeDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask);
sint32 FSGetCwdAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* dirPathOut, sint32 dirPathMaxLen, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSGetCwd(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* dirPathOut, sint32 dirPathMaxLen, uint32 errorMask);
sint32 FSGetFreeSpaceSizeAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, FSLargeSize* returnedFreeSize, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSGetFreeSpaceSize(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, FSLargeSize* returnedFreeSize, uint32 errorMask);
sint32 FSOpenDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, FSDirHandlePtr dirHandleOut, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSOpenDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, FSDirHandlePtr dirHandleOut, uint32 errorMask);
sint32 FSReadDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, FSDirEntry_t* dirEntryOut, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSReadDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, FSDirEntry_t* dirEntryOut, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSCloseDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSCloseDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask);
sint32 FSFlushQuotaAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams);
sint32 FSFlushQuota(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask);
FS_VOLSTATE FSGetVolumeState(FSClient_t* fsClient);
void InitializeFS();
};

View file

@ -0,0 +1,285 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_GHS.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Cafe/OS/RPL/rpl.h"
namespace coreinit
{
struct iobbuf
{
uint32be ukn00; // lock index?
uint32be ukn04; // ?
uint32be ukn08; // ?
uint32be flags; // permissions and channel
};
#define GHS_FOPEN_MAX 100
struct GHSAccessibleData
{
iobbuf _iob[GHS_FOPEN_MAX];
MPTR _iob_lock[GHS_FOPEN_MAX];
uint16be __gh_FOPEN_MAX;
MEMPTR<void> ghs_environ;
uint32 ghs_Errno; // exposed by __gh_errno_ptr() or via 'errno' data export
};
SysAllocator<GHSAccessibleData> g_ghs_data;
struct ghs_flock
{
uint32be mutexIndex;
};
void __ghs_flock_create(ghs_flock* flock);
ghs_flock* __ghs_flock_ptr(iobbuf* iob);
std::recursive_mutex g_ghsLock;
std::recursive_mutex g_ghsLockFlock;
SysAllocator<coreinit::OSMutex, GHS_FOPEN_MAX> _flockMutexArray;
bool _flockMutexMask[GHS_FOPEN_MAX]; // if set, mutex in _flockMutexArray is reserved
#define IOB_FLAG_IN (0x1)
#define IOB_FLAG_OUT (0x2)
#define IOB_FLAG_CHANNEL(__x) ((__x)<<18)
void PrepareGHSRuntime()
{
g_ghs_data->ghs_environ = nullptr;
g_ghs_data->__gh_FOPEN_MAX = GHS_FOPEN_MAX;
g_ghs_data->ghs_Errno = 0;
for (sint32 i = 0; i < GHS_FOPEN_MAX; i++)
_flockMutexMask[i] = false;
// init stdin/stdout/stderr
g_ghs_data->_iob[0].flags = IOB_FLAG_IN;
g_ghs_data->_iob[1].flags = IOB_FLAG_OUT;
g_ghs_data->_iob[1].flags = IOB_FLAG_OUT;
g_ghs_data->_iob[0].flags |= IOB_FLAG_CHANNEL(0);
g_ghs_data->_iob[1].flags |= IOB_FLAG_CHANNEL(1);
g_ghs_data->_iob[2].flags |= IOB_FLAG_CHANNEL(2);
__ghs_flock_create(__ghs_flock_ptr(g_ghs_data->_iob + 0));
__ghs_flock_create(__ghs_flock_ptr(g_ghs_data->_iob + 1));
__ghs_flock_create(__ghs_flock_ptr(g_ghs_data->_iob + 2));
osLib_addVirtualPointer("coreinit", "__gh_FOPEN_MAX", memory_getVirtualOffsetFromPointer(&g_ghs_data->__gh_FOPEN_MAX));
osLib_addVirtualPointer("coreinit", "_iob", memory_getVirtualOffsetFromPointer(g_ghs_data->_iob));
osLib_addVirtualPointer("coreinit", "environ", memory_getVirtualOffsetFromPointer(&g_ghs_data->ghs_environ));
osLib_addVirtualPointer("coreinit", "errno", memory_getVirtualOffsetFromPointer(&g_ghs_data->ghs_Errno));
}
void __ghs_flock_create(ghs_flock* flock)
{
g_ghsLockFlock.lock();
// find available mutex
sint32 mutexIndex = -1;
for (sint32 i = 0; i < GHS_FOPEN_MAX; i++)
{
if (!_flockMutexMask[i])
{
mutexIndex = i;
break;
}
}
if (mutexIndex == -1)
{
forceLog_printf("__ghs_flock_create(): No flock available");
cemu_assert(false); // no available mutex
}
// mark mutex as reserved
_flockMutexMask[mutexIndex] = true;
// init mutex
coreinit::OSInitMutexEx(_flockMutexArray.GetPtr() + mutexIndex, NULL);
// update flock to point to the reserved mutex
flock->mutexIndex = mutexIndex;
g_ghsLockFlock.unlock();
}
void __ghs_flock_destroy(uint32 index)
{
g_ghsLockFlock.lock();
cemu_assert_debug(index > 2); // stdin/stdout/stderr should never be released?
cemu_assert(index < GHS_FOPEN_MAX);
cemu_assert_debug(_flockMutexMask[index]);
_flockMutexMask[index] = false;
g_ghsLockFlock.unlock();
}
ghs_flock* __ghs_flock_ptr(iobbuf* iob)
{
size_t streamIndex = iob - g_ghs_data->_iob;
return (ghs_flock*)&(g_ghs_data->_iob_lock[streamIndex]);
}
void __ghs_flock_file(uint32 index)
{
cemu_assert(index < GHS_FOPEN_MAX);
OSLockMutex(_flockMutexArray.GetPtr() + index);
}
void __ghs_funlock_file(uint32 index)
{
cemu_assert(index < GHS_FOPEN_MAX);
OSUnlockMutex(_flockMutexArray.GetPtr() + index);
}
void __ghsLock()
{
while (!g_ghsLock.try_lock())
{
PPCCore_switchToScheduler();
}
}
void __ghsUnlock()
{
g_ghsLock.unlock();
}
void* __get_eh_init_block()
{
return nullptr;
}
void* __get_eh_globals()
{
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
return currentThread->crt.eh_globals.GetPtr();
}
void* __get_eh_mem_manage()
{
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
return &currentThread->crt.eh_mem_manage;
}
void* __gh_errno_ptr()
{
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
return &currentThread->context.error;
}
void* __get_eh_store_globals()
{
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
return &currentThread->crt.eh_store_globals;
}
void* __get_eh_store_globals_tdeh()
{
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
return &currentThread->crt.eh_store_globals_tdeh;
}
struct ghs_mtx_t
{
MEMPTR<coreinit::OSMutex> mutexPtr;
};
void __ghs_mtx_init(ghs_mtx_t* mtx)
{
mtx->mutexPtr = (coreinit::OSMutex*)coreinit::_weak_MEMAllocFromDefaultHeapEx(ppcsizeof<coreinit::OSMutex>(), 8);
coreinit::OSInitMutex(mtx->mutexPtr.GetPtr());
}
void __ghs_mtx_dst(ghs_mtx_t* mtx)
{
coreinit::_weak_MEMFreeToDefaultHeap(mtx->mutexPtr.GetPtr());
mtx->mutexPtr = nullptr;
}
void __ghs_mtx_lock(ghs_mtx_t* mtx)
{
coreinit::OSLockMutex(mtx->mutexPtr.GetPtr());
}
void __ghs_mtx_unlock(ghs_mtx_t* mtx)
{
coreinit::OSUnlockMutex(mtx->mutexPtr.GetPtr());
}
struct OSTLSBlock
{
MPTR addr;
uint32 ukn04;
};
static_assert(sizeof(OSTLSBlock) == 8);
struct TLS_Index
{
uint16 ukn00;
uint16 tlsModuleIndex;
MPTR ukn04;
};
void* __tls_get_addr(TLS_Index* tlsIndex)
{
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
if (_swapEndianU16(tlsIndex->tlsModuleIndex) == 0)
assert_dbg();
// check if we need to allocate additional TLS blocks for this thread
if (_swapEndianU16(tlsIndex->tlsModuleIndex) >= _swapEndianU32(currentThread->numAllocatedTLSBlocks))
{
uint32 allocSize = (RPLLoader_GetMaxTLSModuleIndex() + 1) * sizeof(OSTLSBlock); // __OSDynLoad_gTLSHeader.ukn00 * 8;
MPTR allocMem = coreinit_allocFromSysArea(allocSize, 4);
memset(memory_getPointerFromVirtualOffset(allocMem), 0, allocSize);
if (_swapEndianU32(currentThread->numAllocatedTLSBlocks) != 0)
{
// keep previously allocated blocks
memcpy(memory_getPointerFromVirtualOffset(allocMem), memory_getPointerFromVirtualOffset(_swapEndianU32(currentThread->tlsBlocksMPTR)), _swapEndianU32(currentThread->numAllocatedTLSBlocks) * 8);
}
currentThread->tlsBlocksMPTR = _swapEndianU32(allocMem);
currentThread->numAllocatedTLSBlocks = _swapEndianU16(RPLLoader_GetMaxTLSModuleIndex() + 1);
}
// look up TLS address based on moduleIndex
OSTLSBlock* tlsBlock = (OSTLSBlock*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(currentThread->tlsBlocksMPTR) + sizeof(OSTLSBlock) * (uint32)_swapEndianU16(tlsIndex->tlsModuleIndex));
if (tlsBlock->addr != _swapEndianU32(MPTR_NULL))
{
//osLib_returnFromFunction(hCPU, _swapEndianU32(tlsBlock->addr)+_swapEndianU32(tlsIndex->ukn04));
return memory_getPointerFromVirtualOffset(_swapEndianU32(tlsBlock->addr) + _swapEndianU32(tlsIndex->ukn04));
}
// alloc data for TLS block
uint8* tlsSectionData = nullptr;
sint32 tlsSize = 0;
bool r = RPLLoader_GetTLSDataByTLSIndex((sint16)_swapEndianU16(tlsIndex->tlsModuleIndex), &tlsSectionData, &tlsSize);
cemu_assert(r);
cemu_assert(tlsSize != 0);
MPTR tlsData = coreinit_allocFromSysArea(tlsSize, 32);
memcpy(memory_getPointerFromVirtualOffset(tlsData), tlsSectionData, tlsSize);
tlsBlock->addr = _swapEndianU32(tlsData);
return memory_getPointerFromVirtualOffset(_swapEndianU32(tlsBlock->addr) + _swapEndianU32(tlsIndex->ukn04));
}
void InitializeGHS()
{
cafeExportRegister("coreinit", __ghs_flock_create, LogType::Placeholder);
cafeExportRegister("coreinit", __ghs_flock_destroy, LogType::Placeholder);
cafeExportRegister("coreinit", __ghs_flock_ptr, LogType::Placeholder);
cafeExportRegister("coreinit", __ghs_flock_file, LogType::Placeholder);
cafeExportRegister("coreinit", __ghs_funlock_file, LogType::Placeholder);
cafeExportRegister("coreinit", __ghsLock, LogType::Placeholder);
cafeExportRegister("coreinit", __ghsUnlock, LogType::Placeholder);
cafeExportRegister("coreinit", __get_eh_init_block, LogType::Placeholder);
cafeExportRegister("coreinit", __get_eh_globals, LogType::Placeholder);
cafeExportRegister("coreinit", __get_eh_mem_manage, LogType::Placeholder);
cafeExportRegister("coreinit", __gh_errno_ptr, LogType::Placeholder);
cafeExportRegister("coreinit", __get_eh_store_globals, LogType::Placeholder);
cafeExportRegister("coreinit", __get_eh_store_globals_tdeh, LogType::Placeholder);
cafeExportRegister("coreinit", __ghs_mtx_init, LogType::Placeholder);
cafeExportRegister("coreinit", __ghs_mtx_dst, LogType::Placeholder);
cafeExportRegister("coreinit", __ghs_mtx_lock, LogType::Placeholder);
cafeExportRegister("coreinit", __ghs_mtx_unlock, LogType::Placeholder);
cafeExportRegister("coreinit", __tls_get_addr, LogType::Placeholder);
}
};

View file

@ -0,0 +1,8 @@
#pragma once
namespace coreinit
{
void PrepareGHSRuntime();
void InitializeGHS();
};

View file

@ -0,0 +1,138 @@
#include "Cafe/OS/common/OSCommon.h"
#include "coreinit_HWInterface.h"
namespace coreinit
{
enum class RegisterInterfaceId : uint32 // for __OSRead/__OSWrite API (register access in userspace)
{
INTERFACE_VI_UKN = 0, // 0x0C1E0000
INTERFACE_VI2_UKN = 3, // might also be some other interface?
};
enum class SysRegisterInterfaceId : uint32 // for __OSRead/__OSWriteRegister (register access via kernel systemcall)
{
INTERFACE_UKN = 0,
INTERFACE_3_ACR_VI = 3, // 0x0D00021C
INTERFACE_6_SI = 6, // 0x0D006400
INTERFACE_7_AI_PROBABLY = 7, // 0x0D046C00 // AI or some secondary AI interface?
};
PAddr _GetRegisterPhysicalAddress(RegisterInterfaceId interfaceId, uint32 offset)
{
PAddr base = 0;
switch (interfaceId)
{
case RegisterInterfaceId::INTERFACE_VI_UKN:
base = 0x0C1E0000;
break;
default:
cemu_assert_debug(false); // todo
return 0;
}
return base + offset;
}
PAddr _GetSysRegisterPhysicalAddress(SysRegisterInterfaceId interfaceId, uint32 offset)
{
PAddr base = 0;
switch (interfaceId)
{
case SysRegisterInterfaceId::INTERFACE_3_ACR_VI:
base = 0x0D00021C;
break;
case SysRegisterInterfaceId::INTERFACE_6_SI:
base = 0x0D006400;
break;
default:
cemu_assert_debug(false); // todo
return 0;
}
return base + offset;
}
/* Userspace register interface */
uint32 OSReadRegister32(RegisterInterfaceId interfaceId, uint32 offset)
{
PAddr regAddr = _GetRegisterPhysicalAddress(interfaceId, offset);
cemu_assert_debug(regAddr);
return MMU::ReadMMIO_32(regAddr);
}
uint16 OSReadRegister16(RegisterInterfaceId interfaceId, uint32 offset)
{
PAddr regAddr = _GetRegisterPhysicalAddress(interfaceId, offset);
cemu_assert_debug(regAddr);
return MMU::ReadMMIO_16(regAddr);
}
void OSWriteRegister16(uint16 newValue, RegisterInterfaceId interfaceId, uint32 offset)
{
static bool s_dbg = false;
if (!s_dbg)
{
cemu_assert_debug(false);
s_dbg = true;
}
}
void OSWriteRegister32(uint16 newValue, RegisterInterfaceId interfaceId, uint32 offset)
{
static bool s_dbg = false;
if (!s_dbg)
{
cemu_assert_debug(false);
s_dbg = true;
}
}
void OSModifyRegister16(RegisterInterfaceId interfaceId, uint32 uknR4, uint32 uknR5, uint32 uknR6)
{
static bool s_dbg = false;
if (!s_dbg)
{
cemu_assert_debug(false);
s_dbg = true;
}
}
/* Kernel register interface */
uint32 __OSReadRegister32Ex(SysRegisterInterfaceId interfaceId, uint32 registerId)
{
uint32 offset = registerId * 4;
cemu_assert_debug(offset < 0x40);
PAddr regAddr = _GetSysRegisterPhysicalAddress(interfaceId, offset);
cemu_assert_debug(regAddr);
return MMU::ReadMMIO_32(regAddr);
}
void __OSWriteRegister32Ex(SysRegisterInterfaceId interfaceId, uint32 registerId, uint32 newValue)
{
uint32 offset = registerId * 4;
cemu_assert_debug(offset < 0x40);
PAddr regAddr = _GetSysRegisterPhysicalAddress(interfaceId, offset);
cemu_assert_debug(regAddr);
MMU::WriteMMIO_32(regAddr, newValue);
}
void InitializeHWInterface()
{
cafeExportRegister("coreinit", OSReadRegister32, LogType::Placeholder);
cafeExportRegister("coreinit", OSReadRegister16, LogType::Placeholder);
cafeExportRegister("coreinit", OSWriteRegister16, LogType::Placeholder);
cafeExportRegister("coreinit", OSWriteRegister32, LogType::Placeholder);
cafeExportRegister("coreinit", OSModifyRegister16, LogType::Placeholder);
cafeExportRegister("coreinit", __OSReadRegister32Ex, LogType::Placeholder);
cafeExportRegister("coreinit", __OSWriteRegister32Ex, LogType::Placeholder);
};
};

View file

@ -0,0 +1,4 @@
namespace coreinit
{
void InitializeHWInterface();
};

View file

@ -0,0 +1,137 @@
#include "Cafe/OS/common/OSCommon.h"
// APD = Automatic Power Down
namespace coreinit
{
#define IM_ERROR_NONE 0
void coreinitExport_IMIsAPDEnabledBySysSettings(PPCInterpreter_t* hCPU)
{
debug_printf("IMIsAPDEnabledBySysSettings(0x%08x)\n", hCPU->gpr[3]);
ppcDefineParamTypePtr(isAPDEnabled, uint32be, 0);
*isAPDEnabled = 0;
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
}
void coreinitExport_IMGetTimeBeforeAPD(PPCInterpreter_t* hCPU)
{
// parameters:
// r3 uint32* returns the remaining number of seconds until auto-shutdown
memory_writeU32(hCPU->gpr[3], 60 * 30); // 30 minutes
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
}
void coreinitExport_IMGetTimeBeforeDimming(PPCInterpreter_t* hCPU)
{
// parameters:
// r3 uint32* returns the remaining number of seconds until dimming
memory_writeU32(hCPU->gpr[3], 60 * 30); // 30 minutes
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
}
bool imDimIsEnabled = true;
void coreinitExport_IMEnableDim(PPCInterpreter_t* hCPU)
{
imDimIsEnabled = true;
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
}
void coreinitExport_IMIsDimEnabled(PPCInterpreter_t* hCPU)
{
// parameters:
// r3 uint32* returns the remaining number of seconds until auto-shutdown
memory_writeU32(hCPU->gpr[3], imDimIsEnabled ? 1 : 0); // enabled
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
}
void coreinitExport_IMGetAPDPeriod(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("IMGetAPDPeriod(0x%08x)\n", hCPU->gpr[3]);
// parameters:
// r3 uint32* returns the number of seconds until auto-shutdown occurs
memory_writeU32(hCPU->gpr[3], 600);
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
}
void coreinitExport_IM_GetParameter(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("IM_GetParameter()");
ppcDefineParamS32(imHandle, 0); // handle from IM_Open()
ppcDefineParamS32(uknR4, 1);
ppcDefineParamS32(parameterId, 2);
ppcDefineParamStructPtr(output, void, 3);
ppcDefineParamS32(uknR7, 4);
ppcDefineParamS32(uknR8, 5);
if (parameterId == 0)
{
// inactive seconds
*(uint32be*)output = 600;
}
else
{
cemu_assert_unimplemented();
}
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
}
void coreinitExport_IM_GetRuntimeParameter(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("IM_GetRuntimeParameter()");
ppcDefineParamS32(parameterId, 0);
ppcDefineParamStructPtr(output, void, 1);
if (parameterId == 8)
{
// indicates if last session was ended due to auto-power-down
*(uint32be*)output = 0;
}
else
{
cemu_assert_unimplemented();
}
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
}
void coreinitExport_IM_GetHomeButtonParams(PPCInterpreter_t* hCPU)
{
debug_printf("IM_GetHomeButtonParams(...)\n");
ppcDefineParamS32(imObj, 0);
ppcDefineParamMPTR(ipcBuf, 1);
ppcDefineParamMPTR(paramOut, 2);
ppcDefineParamS32(uknR6, 3);
ppcDefineParamS32(uknR7, 4);
// todo
// note: No idea what these values mean. But they were chosen so that the Browser (surf.rpx) does not OSPanic()
memory_writeU32(paramOut + 0x0, 0);
memory_writeU32(paramOut + 0x4, 0);
// for scope.rpx (Download Manager)
//memory_writeU32(paramOut + 0x0, 1);
//memory_writeU32(paramOut + 0x4, 2); // some sort of index (starting at 1?)
osLib_returnFromFunction(hCPU, IM_ERROR_NONE);
}
void InitializeIM()
{
osLib_addFunction("coreinit", "IMIsAPDEnabledBySysSettings", coreinitExport_IMIsAPDEnabledBySysSettings);
osLib_addFunction("coreinit", "IMGetTimeBeforeAPD", coreinitExport_IMGetTimeBeforeAPD);
osLib_addFunction("coreinit", "IMGetTimeBeforeDimming", coreinitExport_IMGetTimeBeforeDimming);
osLib_addFunction("coreinit", "IMEnableDim", coreinitExport_IMEnableDim);
osLib_addFunction("coreinit", "IMIsDimEnabled", coreinitExport_IMIsDimEnabled);
osLib_addFunction("coreinit", "IMGetAPDPeriod", coreinitExport_IMGetAPDPeriod);
osLib_addFunction("coreinit", "IM_GetHomeButtonParams", coreinitExport_IM_GetHomeButtonParams);
osLib_addFunction("coreinit", "IM_GetParameter", coreinitExport_IM_GetParameter);
osLib_addFunction("coreinit", "IM_GetRuntimeParameter", coreinitExport_IM_GetRuntimeParameter);
}
};

View file

@ -0,0 +1,6 @@
#pragma once
namespace coreinit
{
void InitializeIM();
};

View file

@ -0,0 +1,91 @@
#include "Cafe/OS/libs/coreinit/coreinit_IOS.h"
#include "Cafe/IOSU/legacy/iosu_ioctl.h"
// superseded by coreinit_IPC.cpp/h
sint32 __depr__IOS_Open(char* path, uint32 mode)
{
sint32 iosDevice = 0;
if (path == nullptr)
{
iosDevice = 0;
}
else
{
if (strcmp(path, IOS_PATH_ODM) == 0)
iosDevice = IOS_DEVICE_ODM;
else if (strcmp(path, IOS_PATH_SOCKET) == 0)
iosDevice = IOS_DEVICE_SOCKET;
else if (strcmp(path, IOS_PATH_ACT) == 0)
iosDevice = IOS_DEVICE_ACT;
else if (strcmp(path, IOS_PATH_FPD) == 0)
iosDevice = IOS_DEVICE_FPD;
else if (strcmp(path, IOS_PATH_ACP_MAIN) == 0)
iosDevice = IOS_DEVICE_ACP_MAIN;
else if (strcmp(path, IOS_PATH_MCP) == 0)
iosDevice = IOS_DEVICE_MCP;
else if (strcmp(path, IOS_PATH_BOSS) == 0)
iosDevice = IOS_DEVICE_BOSS;
else if (strcmp(path, IOS_PATH_NIM) == 0)
iosDevice = IOS_DEVICE_NIM;
else if (strcmp(path, IOS_PATH_IOSUHAX) == 0)
return -1;
else
iosDevice = IOS_DEVICE_UKN;
}
return iosDevice;
}
sint32 __depr__IOS_Ioctl(uint32 fd, uint32 request, void* inBuffer, uint32 inSize, void* outBuffer, uint32 outSize)
{
switch (fd)
{
case IOS_DEVICE_ODM:
{
// Home Menu uses ioctl cmd 5 on startup and then repeats cmd 4 every frame
if (request == 4)
{
// check drive state
debug_printf("checkDriveState()\n");
*(uint32be*)outBuffer = 0xA;
}
else
{
debug_printf("odm unsupported ioctl %d\n", request);
}
break;
}
default:
{
// todo
forceLogDebug_printf("Unsupported Ioctl command");
}
}
return 0;
}
sint32 __depr__IOS_Ioctlv(uint32 fd, uint32 request, uint32 countIn, uint32 countOut, ioBufferVector_t* ioBufferVectors)
{
StackAllocator<ioQueueEntry_t> _queueEntryBuf;
ioQueueEntry_t* queueEntry = _queueEntryBuf.GetPointer();
queueEntry->isIoctlv = true;
queueEntry->isAsync = false;
queueEntry->request = request;
queueEntry->countIn = countIn;
queueEntry->countOut = countOut;
queueEntry->bufferVectors = ioBufferVectors;
queueEntry->ppcThread = nullptr;
queueEntry->returnValue = 0;
queueEntry->isCompleted = false;
sint32 r = iosuIoctl_pushAndWait(fd, queueEntry);
return r;
}
sint32 __depr__IOS_Close(uint32 fd)
{
return 0;
}

View file

@ -0,0 +1,9 @@
// IOS
typedef struct _ioBufferVector_t ioBufferVector_t;
sint32 __depr__IOS_Open(char* path, uint32 mode);
sint32 __depr__IOS_Ioctl(uint32 fd, uint32 request, void* inBuffer, uint32 inSize, void* outBuffer, uint32 outSize);
sint32 __depr__IOS_Ioctlv(uint32 fd, uint32 request, uint32 countIn, uint32 countOut, ioBufferVector_t* ioBufferVectors);
sint32 __depr__IOS_Close(uint32 fd);
// superseded by coreinit_IPC.h

View file

@ -0,0 +1,482 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/IOSU/kernel/iosu_kernel.h"
#include "Cafe/HW/Espresso/Const.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
#include "coreinit_MessageQueue.h"
#include "coreinit_IPC.h"
namespace coreinit
{
static constexpr inline size_t IPC_NUM_RESOURCE_BUFFERS = 0x30;
struct IPCResourceBuffer
{
IPCCommandBody commandBody;
uint8 bufferData[0x80 - 0x48];
};
static_assert(sizeof(IPCCommandBody) == 0x48);
static_assert(sizeof(IPCResourceBuffer) == 0x80);
struct IPCResourceBufferDescriptor
{
/* +0x00 */ uint32be IsAllocated;
/* +0x04 */ MEMPTR<OSMessageQueue> asyncMsgQueue; // optional, if set a message will be sent to this queue...
/* +0x08 */ MEMPTR<void> asyncResultFunc; // ...otherwise this is checked and delegated to the IPC threads. If false, only eventSynchronousIPC will be signaled. If true, a message will be sent to the per-core ipc queue
/* +0x0C */ MEMPTR<void> asyncResultUserParam;
/* +0x10 */ uint32 ukn10;
/* +0x14 */ MEMPTR<IPCResourceBuffer> resourcePtr;
/* +0x18 */ OSEvent eventSynchronousIPC;
};
static_assert(sizeof(IPCResourceBufferDescriptor) == 0x3C);
struct IPCBufferFIFO
{
sint32be writeIndex;
sint32be readIndex;
sint32be numQueuedEntries;
sint32be mostQueuedEntries;
MEMPTR<IPCResourceBufferDescriptor> ringbufferArray[IPC_NUM_RESOURCE_BUFFERS];
void Init()
{
writeIndex = 0;
readIndex = -1;
numQueuedEntries = 0;
mostQueuedEntries = 0;
for (size_t i = 0; i < IPC_NUM_RESOURCE_BUFFERS; i++)
ringbufferArray[i] = nullptr;
}
void Push(IPCResourceBufferDescriptor* descriptor)
{
cemu_assert(readIndex != writeIndex); // if equal, fifo is full (should not happen as there not more buffers than ringbuffer entries)
ringbufferArray[writeIndex] = descriptor;
if (readIndex < 0)
readIndex = writeIndex;
writeIndex = (writeIndex + 1) % IPC_NUM_RESOURCE_BUFFERS;
++numQueuedEntries;
if (numQueuedEntries > mostQueuedEntries)
mostQueuedEntries = numQueuedEntries;
}
IPCResourceBufferDescriptor* Pop()
{
if (numQueuedEntries == 0)
return nullptr;
IPCResourceBufferDescriptor* r = ringbufferArray[readIndex].GetPtr();
--numQueuedEntries;
if (numQueuedEntries == 0)
readIndex = -1;
else
readIndex = (readIndex + 1) % IPC_NUM_RESOURCE_BUFFERS;
return r;
}
};
struct IPCDriverCOSKernelCommunicationArea
{
uint32be numAvailableResponses;
MEMPTR<IPCCommandBody> responseArray[11];
};
static_assert(sizeof(IPCDriverCOSKernelCommunicationArea) == 0x30);
struct alignas(64) IPCDriver
{
betype<IPCDriverState> state;
uint32 ukn04;
uint32 coreIndex;
uint32 writeIndexCmd020;
MEMPTR<IPCResourceBuffer> resourceBuffers;
/* 0x16C */ IPCBufferFIFO fifoFreeBuffers;
/* 0x23C */ IPCBufferFIFO fifoBuffersInFlight;
/* 0x334 */ uint32 resourceBuffersInitialized;
/* 0x338 */ IPCDriverCOSKernelCommunicationArea kernelSharedArea; // this is passed to system call 0x1E00 (IPCOpen)
/* 0x3FC */ IPCResourceBufferDescriptor resBufferDescriptor[IPC_NUM_RESOURCE_BUFFERS];
};
//static_assert(sizeof(IPCDriverInstance) == 0x1740);
SysAllocator<IPCResourceBuffer, IPC_NUM_RESOURCE_BUFFERS * Espresso::CORE_COUNT, 0x40> s_ipcResourceBuffers;
SysAllocator<IPCDriver, Espresso::CORE_COUNT, 0x40> s_ipcDriver;
IPCDriver& IPCDriver_GetByCore(uint32 coreIndex)
{
cemu_assert_debug(coreIndex >= 0 && coreIndex < (uint32)Espresso::CORE_COUNT);
return s_ipcDriver[coreIndex];
}
void IPCDriver_InitForCore(uint32 coreIndex)
{
IPCDriver& ipcDriver = IPCDriver_GetByCore(coreIndex);
ipcDriver.coreIndex = coreIndex;
ipcDriver.state = IPCDriverState::INITIALIZED;
ipcDriver.resourceBuffers = s_ipcResourceBuffers.GetPtr() + IPC_NUM_RESOURCE_BUFFERS * coreIndex;
ipcDriver.resourceBuffersInitialized = 0;
// setup resource descriptors
for (size_t i = 0; i < IPC_NUM_RESOURCE_BUFFERS; i++)
{
ipcDriver.resBufferDescriptor[i].resourcePtr = ipcDriver.resourceBuffers.GetPtr() + i;
ipcDriver.resBufferDescriptor[i].asyncResultFunc = nullptr;
ipcDriver.resBufferDescriptor[i].asyncResultUserParam = nullptr;
}
ipcDriver.resourceBuffersInitialized = 1;
// setup resource buffer FIFOs
ipcDriver.fifoFreeBuffers.Init();
ipcDriver.fifoBuffersInFlight.Init();
for (size_t i = 0; i < IPC_NUM_RESOURCE_BUFFERS; i++)
ipcDriver.fifoFreeBuffers.Push(ipcDriver.resBufferDescriptor + i);
}
IPCResourceBufferDescriptor* IPCDriver_AllocateResource(IPCDriver* ipcDriver, IOSDevHandle devHandle, IPCCommandId cmdId, OSMessageQueue* asyncMessageQueue, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam)
{
cemu_assert_debug(ipcDriver->coreIndex == OSGetCoreId());
IPCResourceBufferDescriptor* descriptor = nullptr;
while (true)
{
descriptor = ipcDriver->fifoFreeBuffers.Pop();
if (!descriptor)
{
cemuLog_log(LogType::Force, "IPCDriver: Exceeded free resources");
OSYieldThread();
cemu_assert_unimplemented(); // we should wait for an event instead of busylooping
}
else
break;
}
cemu_assert_debug(descriptor >= ipcDriver->resBufferDescriptor && descriptor < ipcDriver->resBufferDescriptor + IPC_NUM_RESOURCE_BUFFERS);
cemu_assert_debug(descriptor->resourcePtr.GetPtr() >= ipcDriver->resourceBuffers.GetPtr() && descriptor->resourcePtr.GetPtr() < (ipcDriver->resourceBuffers.GetPtr() + IPC_NUM_RESOURCE_BUFFERS));
IPCResourceBuffer* res = descriptor->resourcePtr;
IPCCommandBody& cmdBody = res->commandBody;
descriptor->IsAllocated = 1;
descriptor->asyncMsgQueue = asyncMessageQueue;
descriptor->asyncResultFunc = asyncResultFunc;
descriptor->asyncResultUserParam = asyncResultUserParam;
cmdBody.cmdId = cmdId;
cmdBody.ukn0C = 0;
cmdBody.ukn14 = 0;
cmdBody.result = 0;
cmdBody.devHandle = devHandle;
return descriptor;
}
void IPCDriver_ReleaseResource(IPCDriver* ipcDriver, IPCResourceBufferDescriptor* requestDescriptor)
{
requestDescriptor->IsAllocated = 0;
ipcDriver->fifoFreeBuffers.Push(requestDescriptor);
}
/* IPC threads */
SysAllocator<OSThread_t, Espresso::CORE_COUNT> gIPCThread;
SysAllocator<uint8, 0x4000 * Espresso::CORE_COUNT> _gIPCThreadStack;
SysAllocator<uint8, 0x18 * Espresso::CORE_COUNT> _gIPCThreadNameStorage;
SysAllocator<OSMessageQueue, Espresso::CORE_COUNT> gIPCThreadMsgQueue;
SysAllocator<OSMessage, Espresso::CORE_COUNT * IPC_NUM_RESOURCE_BUFFERS> _gIPCThreadSemaphoreStorage;
// handler thread for asynchronous callbacks for IPC responses
void __IPCDriverThreadFunc(PPCInterpreter_t* hCPU)
{
uint32 coreIndex = OSGetCoreId();
while (true)
{
OSMessage msg;
OSReceiveMessage(gIPCThreadMsgQueue.GetPtr() + coreIndex, &msg, OS_MESSAGE_BLOCK);
cemu_assert(msg.data2 == 1); // type must be callback
MEMPTR<void> cbFunc{ msg.message };
cemu_assert(cbFunc != nullptr);
PPCCoreCallback(cbFunc.GetPtr(), (uint32)msg.data0, (uint32)msg.data1);
}
osLib_returnFromFunction(hCPU, 0);
}
void IPCDriver_InitIPCThread(uint32 coreIndex)
{
// create a thread with 0x4000 stack space
// and a message queue large enough to hold the maximum number of commands (IPC_NUM_RESOURCE_BUFFERS)
OSInitMessageQueue(gIPCThreadMsgQueue.GetPtr() + coreIndex, _gIPCThreadSemaphoreStorage.GetPtr() + coreIndex * IPC_NUM_RESOURCE_BUFFERS, IPC_NUM_RESOURCE_BUFFERS);
OSThread_t* ipcThread = gIPCThread.GetPtr() + coreIndex;
OSCreateThreadType(ipcThread, PPCInterpreter_makeCallableExportDepr(__IPCDriverThreadFunc), 0, nullptr, _gIPCThreadStack.GetPtr() + 0x4000 * coreIndex + 0x4000, 0x4000, 15, (1 << coreIndex), OSThread_t::THREAD_TYPE::TYPE_DRIVER);
sprintf((char*)_gIPCThreadNameStorage.GetPtr()+coreIndex*0x18, "{SYS IPC Core %d}", coreIndex);
OSSetThreadName(ipcThread, (char*)_gIPCThreadNameStorage.GetPtr() + coreIndex * 0x18);
OSResumeThread(ipcThread);
}
/* coreinit IOS_* API */
void _IPCDriver_SubmitCmdAllQueued(IPCDriver& ipcDriver)
{
// on COS, submitted commands first go to the COS kernel via syscall 0x2000, where they are processed, copied and queued again
// we skip all of this and just pass our IPC commands directly to the IOSU kernel handler HLE function
// important: IOSU needs to know which PPC core sent the command, so that it can also notify the same core about the result
ipcDriver.state = IPCDriverState::SUBMITTING;
while (true)
{
IPCResourceBufferDescriptor* res = ipcDriver.fifoBuffersInFlight.Pop();
if (!res)
break;
// resolve pointers
switch (res->resourcePtr->commandBody.cmdId)
{
case IPCCommandId::IOS_OPEN:
res->resourcePtr->commandBody.args[0] = res->resourcePtr->commandBody.ppcVirt0.GetMPTR();
break;
case IPCCommandId::IOS_CLOSE:
break;
case IPCCommandId::IOS_IOCTL:
res->resourcePtr->commandBody.args[1] = res->resourcePtr->commandBody.ppcVirt0.GetMPTR();
res->resourcePtr->commandBody.args[3] = res->resourcePtr->commandBody.ppcVirt1.GetMPTR();
break;
case IPCCommandId::IOS_IOCTLV:
res->resourcePtr->commandBody.args[3] = res->resourcePtr->commandBody.ppcVirt0.GetMPTR();
break;
default:
cemu_assert_unimplemented();
break;
}
iosu::kernel::IPCSubmitFromCOS(ipcDriver.coreIndex, &res->resourcePtr->commandBody);
}
ipcDriver.state = IPCDriverState::READY;
}
void _IPCDriver_SubmitCmd(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor)
{
if (requestDescriptor->asyncResultFunc == nullptr)
OSInitEvent(&requestDescriptor->eventSynchronousIPC, OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_AUTO);
ipcDriver.fifoBuffersInFlight.Push(requestDescriptor);
cemu_assert_debug(ipcDriver.state == IPCDriverState::READY || ipcDriver.state == IPCDriverState::INITIALIZED);
_IPCDriver_SubmitCmdAllQueued(ipcDriver);
}
uint32 _IPCDriver_WaitForResultAndRelease(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor)
{
OSWaitEvent(&requestDescriptor->eventSynchronousIPC);
uint32 r = requestDescriptor->resourcePtr->commandBody.result;
IPCDriver_ReleaseResource(&ipcDriver, requestDescriptor);
return r;
}
void IPCDriver_HandleResponse(IPCDriver& ipcDriver, IPCCommandBody* res, uint32 ppcCoreIndex)
{
size_t index = (IPCResourceBuffer*)res - ipcDriver.resourceBuffers.GetPtr();
cemu_assert(index < IPC_NUM_RESOURCE_BUFFERS);
IPCResourceBufferDescriptor* descriptor = ipcDriver.resBufferDescriptor + index;
cemu_assert(descriptor->IsAllocated != 0);
if (descriptor->asyncMsgQueue != nullptr)
{
OSMessage msg;
msg.message = 0;
msg.data0 = res->result;
msg.data1 = descriptor->asyncResultUserParam.GetMPTR();
msg.data2 = 0;
sint32 r = OSSendMessage(descriptor->asyncMsgQueue.GetPtr(), &msg, 0);
cemu_assert(r != 0);
IPCDriver_ReleaseResource(&ipcDriver, descriptor);
return;
}
if (descriptor->asyncResultFunc != nullptr)
{
OSMessage msg;
msg.message = descriptor->asyncResultFunc.GetMPTR();
msg.data0 = res->result;
msg.data1 = descriptor->asyncResultUserParam.GetMPTR();
msg.data2 = 1;
sint32 r = OSSendMessage(gIPCThreadMsgQueue.GetPtr() + ppcCoreIndex, &msg, 0);
cemu_assert(r != 0);
IPCDriver_ReleaseResource(&ipcDriver, descriptor);
return;
}
// signal event for synchronous IPC
OSSignalEvent(&descriptor->eventSynchronousIPC);
}
// handles responses queued in shared region
void IPCDriver_KernelCallback(IPCDriver& ipcDriver)
{
cemu_assert_debug(ipcDriver.kernelSharedArea.numAvailableResponses != 0);
for (uint32 i = 0; i < ipcDriver.kernelSharedArea.numAvailableResponses; i++)
IPCDriver_HandleResponse(ipcDriver, ipcDriver.kernelSharedArea.responseArray[i], ipcDriver.coreIndex);
ipcDriver.kernelSharedArea.numAvailableResponses = 0;
}
// called by our HLE'd IOSU directly
void IPCDriver_NotifyResponses(uint32 ppcCoreIndex, IPCCommandBody** responseArray, uint32 numResponses)
{
cemu_assert(numResponses < 11);
IPCDriver& ipcDriver = IPCDriver_GetByCore(ppcCoreIndex);
ipcDriver.kernelSharedArea.numAvailableResponses = numResponses;
for (uint32 i = 0; i < numResponses; i++)
ipcDriver.kernelSharedArea.responseArray[i] = responseArray[i];
IPCDriver_KernelCallback(ipcDriver);
}
void _IPCDriver_SetupCmd_IOSOpen(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor, const char* devicePath, uint32 flags)
{
// store the path in the buffer after the command body
IPCResourceBuffer* resBuffer = requestDescriptor->resourcePtr;
IPCCommandBody& cmdBody = resBuffer->commandBody;
uint8* buffer = resBuffer->bufferData;
size_t pathLen = strlen(devicePath);
if (pathLen > 31)
{
cemuLog_log(LogType::Force, "IOS_Open(): Device path must not exceed 31 characters");
cemu_assert_error();
}
memcpy(buffer, devicePath, pathLen + 1);
cmdBody.ppcVirt0 = MEMPTR<void>(buffer).GetMPTR();
cmdBody.args[0] = 0;
cmdBody.args[1] = (uint32)(pathLen + 1);
cmdBody.args[2] = flags;
}
IOS_ERROR _IPCDriver_SetupCmd_IOSIoctl(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut)
{
IPCCommandBody& cmdBody = requestDescriptor->resourcePtr->commandBody;
cmdBody.args[0] = requestId;
cmdBody.args[1] = MPTR_NULL; // set to ppcVirt0 later
cmdBody.args[2] = sizeIn;
cmdBody.args[3] = MPTR_NULL; // set to ppcVirt1 later
cmdBody.args[4] = sizeOut;
cmdBody.ppcVirt0 = MEMPTR<void>(ptrIn).GetMPTR();
cmdBody.ppcVirt1 = MEMPTR<void>(ptrOut).GetMPTR();
return IOS_ERROR_OK;
}
IOS_ERROR _IPCDriver_SetupCmd_IOSIoctlv(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec)
{
IPCCommandBody& cmdBody = requestDescriptor->resourcePtr->commandBody;
// verify input and output vectors
IPCIoctlVector* vecIn = vec;
IPCIoctlVector* vecOut = vec + numIn;
for (uint32 i = 0; i < numIn; i++)
{
if (vecIn[i].baseVirt == nullptr && vecIn[i].size != 0)
return IOS_ERROR_INVALID_ARG;
vecIn[i].basePhys = vecIn[i].baseVirt;
vecIn[i].baseVirt = nullptr;
}
for (uint32 i = 0; i < numOut; i++)
{
if (vecOut[i].baseVirt == nullptr && vecOut[i].size != 0)
return IOS_ERROR_INVALID_ARG;
vecOut[i].basePhys = vecOut[i].baseVirt;
vecOut[i].baseVirt = nullptr;
}
// set args
cmdBody.ppcVirt0 = MEMPTR<void>(vec).GetMPTR();
cmdBody.args[0] = requestId;
cmdBody.args[1] = numIn;
cmdBody.args[2] = numOut;
cmdBody.args[3] = 0; // set to ppcVirt0 later
return IOS_ERROR_OK;
}
IOSDevHandle IOS_Open(const char* devicePath, uint32 flags)
{
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, 0, IPCCommandId::IOS_OPEN, nullptr, nullptr, nullptr);
_IPCDriver_SetupCmd_IOSOpen(ipcDriver, ipcDescriptor, devicePath, flags);
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
uint32 r = _IPCDriver_WaitForResultAndRelease(ipcDriver, ipcDescriptor);
return r;
}
IOS_ERROR IOS_Close(IOSDevHandle devHandle)
{
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_CLOSE, nullptr, nullptr, nullptr);
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
IOS_ERROR r = (IOS_ERROR)_IPCDriver_WaitForResultAndRelease(ipcDriver, ipcDescriptor);
return r;
}
IOS_ERROR IOS_Ioctl(IOSDevHandle devHandle, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut)
{
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_IOCTL, nullptr, nullptr, nullptr);
IOS_ERROR r = _IPCDriver_SetupCmd_IOSIoctl(ipcDriver, ipcDescriptor, requestId, ptrIn, sizeIn, ptrOut, sizeOut);
if (r != IOS_ERROR_OK)
{
cemuLog_log(LogType::Force, "IOS_Ioctl failed due to bad parameters");
IPCDriver_ReleaseResource(&ipcDriver, ipcDescriptor);
return r;
}
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
r = (IOS_ERROR)_IPCDriver_WaitForResultAndRelease(ipcDriver, ipcDescriptor);
return r;
}
IOS_ERROR IOS_IoctlAsync(IOSDevHandle devHandle, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam)
{
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_IOCTL, nullptr, asyncResultFunc, asyncResultUserParam);
IOS_ERROR r = _IPCDriver_SetupCmd_IOSIoctl(ipcDriver, ipcDescriptor, requestId, ptrIn, sizeIn, ptrOut, sizeOut);
if (r != IOS_ERROR_OK)
{
cemuLog_log(LogType::Force, "IOS_Ioctl failed due to bad parameters");
IPCDriver_ReleaseResource(&ipcDriver, ipcDescriptor);
return r;
}
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
return r;
}
IOS_ERROR IOS_Ioctlv(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec)
{
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_IOCTLV, nullptr, nullptr, nullptr);
IOS_ERROR r = _IPCDriver_SetupCmd_IOSIoctlv(ipcDriver, ipcDescriptor, requestId, numIn, numOut, vec);
if (r != IOS_ERROR_OK)
{
cemuLog_log(LogType::Force, "IOS_Ioctlv failed due to bad parameters");
IPCDriver_ReleaseResource(&ipcDriver, ipcDescriptor);
return r;
}
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
r = (IOS_ERROR)_IPCDriver_WaitForResultAndRelease(ipcDriver, ipcDescriptor);
return r;
}
IOS_ERROR IOS_IoctlvAsync(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam)
{
IPCDriver& ipcDriver = IPCDriver_GetByCore(OSGetCoreId());
IPCResourceBufferDescriptor* ipcDescriptor = IPCDriver_AllocateResource(&ipcDriver, devHandle, IPCCommandId::IOS_IOCTLV, nullptr, asyncResultFunc, asyncResultUserParam);
IOS_ERROR r = _IPCDriver_SetupCmd_IOSIoctlv(ipcDriver, ipcDescriptor, requestId, numIn, numOut, vec);
if (r != IOS_ERROR_OK)
{
cemuLog_log(LogType::Force, "IOS_Ioctlv failed due to bad parameters");
IPCDriver_ReleaseResource(&ipcDriver, ipcDescriptor);
return r;
}
_IPCDriver_SubmitCmd(ipcDriver, ipcDescriptor);
return r;
}
void InitializeIPC()
{
for (uint32 i = 0; i < Espresso::CORE_COUNT; i++)
{
IPCDriver_InitForCore(i);
IPCDriver_InitIPCThread(i);
}
// register API
cafeExportRegister("coreinit", IOS_Open, LogType::PPC_IPC);
cafeExportRegister("coreinit", IOS_Close, LogType::PPC_IPC);
cafeExportRegister("coreinit", IOS_Ioctl, LogType::PPC_IPC);
cafeExportRegister("coreinit", IOS_IoctlAsync, LogType::PPC_IPC);
cafeExportRegister("coreinit", IOS_Ioctlv, LogType::PPC_IPC);
cafeExportRegister("coreinit", IOS_IoctlvAsync, LogType::PPC_IPC);
}
};

View file

@ -0,0 +1,16 @@
#include "Cafe/IOSU/iosu_ipc_common.h"
#include "Cafe/IOSU/iosu_types_common.h"
namespace coreinit
{
void IPCDriver_NotifyResponses(uint32 ppcCoreIndex, IPCCommandBody** responseArray, uint32 numResponses);
IOSDevHandle IOS_Open(const char* devicePath, uint32 flags);
IOS_ERROR IOS_Close(IOSDevHandle devHandle);
IOS_ERROR IOS_Ioctl(IOSDevHandle devHandle, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut);
IOS_ERROR IOS_IoctlAsync(IOSDevHandle devHandle, uint32 requestId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam);
IOS_ERROR IOS_Ioctlv(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec);
IOS_ERROR IOS_IoctlvAsync(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam);
void InitializeIPC();
};

View file

@ -0,0 +1,233 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
namespace coreinit
{
struct FIFOEntry_t
{
MEMPTR<uint8> p;
};
struct IPCFifo_t
{
uint32be writeIndex;
uint32be readIndex;
uint32be availableEntries; // number of available entries
uint32be entryCount;
MEMPTR<FIFOEntry_t> entryArray;
};
struct IPCBufPool_t
{
/* +0x00 */ uint32be magic;
/* +0x04 */ MEMPTR<void> fullBufferPtr;
/* +0x08 */ uint32be fullBufferSize;
/* +0x0C */ uint32be uknFromParamR7; // boolean?
/* +0x10 */ uint32be ukn10; // set to zero on init
/* +0x14 */ uint32be entrySize1;
/* +0x18 */ uint32be entrySize2; // set to same value as entrySize1
/* +0x1C */ uint32be entryCount; // actual number of used entries
/* +0x20 */ MEMPTR<uint8> entryStartPtr;
/* +0x24 */ uint32be entryCountMul4;
/* +0x28 */ IPCFifo_t fifo;
/* +0x3C */ coreinit::OSMutex mutex;
// full size is 0x68
};
void FIFOInit(IPCFifo_t* fifo, uint32 entryCount, void* entryArray)
{
fifo->entryCount = entryCount;
fifo->entryArray = (FIFOEntry_t*)entryArray;
fifo->writeIndex = 0;
fifo->readIndex = -1;
fifo->availableEntries = 0;
}
sint32 FIFOPush(IPCFifo_t* fifo, void* entry)
{
if (fifo->readIndex == fifo->writeIndex)
{
assert_dbg();
return -8;
}
fifo->entryArray[(uint32)fifo->writeIndex].p = (uint8*)entry;
if ((sint32)fifo->readIndex < 0)
{
// set readIndex to valid value when fifo was empty
fifo->readIndex = fifo->writeIndex;
}
fifo->availableEntries = (uint32)fifo->availableEntries + 1;
fifo->writeIndex = ((uint32)fifo->writeIndex + 1) % (uint32)fifo->entryCount;
return 0;
}
sint32 FIFOPop(IPCFifo_t* fifo, uint8** entry)
{
*entry = nullptr;
if ((sint32)fifo->readIndex < 0)
return -7;
fifo->availableEntries = (uint32)fifo->availableEntries - 1;
*entry = fifo->entryArray[(uint32)fifo->readIndex].p.GetPtr();
fifo->readIndex = ((uint32)fifo->readIndex + 1) % (uint32)fifo->entryCount;
if (fifo->availableEntries == (uint32be)0)
{
fifo->readIndex = -1;
}
return 0;
}
void* getUpwardsAlignedAddr(void* addr, uint32 alignment)
{
uint64 v = ((uint64)addr + alignment - 1) & ~(uint64)(alignment - 1);
return (uint8*)v;
}
void* getDownwardsAlignedAddr(void* addr, uint32 alignment)
{
uint64 v = ((uint64)addr) & ~(uint64)(alignment - 1);
return (uint8*)v;
}
static_assert(sizeof(IPCBufPool_t) == 0x68);
IPCBufPool_t* IPCBufPoolCreate(uint8* bufferArea, uint32 bufferSize, uint32 entrySize, uint32be* entryCountOutput, uint32 uknR7)
{
memset(bufferArea, 0, bufferSize);
IPCBufPool_t* ipcBufPool = (IPCBufPool_t*)getUpwardsAlignedAddr(bufferArea, 4);
uint8* alignedEnd = (uint8*)getDownwardsAlignedAddr(bufferArea + bufferSize, 4);
uint8* dataStart = (uint8*)getUpwardsAlignedAddr(ipcBufPool + 1, 0x4);
*entryCountOutput = 0;
// todo: Validate parameters
OSInitMutexEx(&ipcBufPool->mutex, NULL);
ipcBufPool->fullBufferPtr = bufferArea;
ipcBufPool->fullBufferSize = bufferSize;
ipcBufPool->uknFromParamR7 = uknR7;
uint32 paddedEntrySize = (entrySize + 0x3F) & ~0x3F;
ipcBufPool->ukn10 = 0;
ipcBufPool->magic = 0xBADF00D;
ipcBufPool->entrySize1 = paddedEntrySize;
ipcBufPool->entrySize2 = paddedEntrySize;
uint32 remainingSize = (uint32)((bufferArea + bufferSize) - dataStart);
uint32 entryCount = remainingSize / paddedEntrySize;
if (entryCount <= 1)
assert_dbg();
ipcBufPool->entryCountMul4 = entryCount * 4;
if ((uint32)ipcBufPool->entryCountMul4 >= paddedEntrySize)
{
// special handling required (need to adjust entry count?)
assert_dbg();
}
else
{
entryCount--; // remove one entry to make room for entry pointer array
ipcBufPool->entryCount = entryCount;
*entryCountOutput = entryCount;
ipcBufPool->entryStartPtr = (dataStart + (uint32)ipcBufPool->entryCountMul4);
FIFOInit(&ipcBufPool->fifo, (uint32)ipcBufPool->entryCount, dataStart);
}
// add all entries to the fifo
for (sint32 i = 0; i < (sint32)ipcBufPool->entryCount; i++)
{
uint8* entry = ipcBufPool->entryStartPtr.GetPtr() + i * (uint32)ipcBufPool->entrySize2;
if (FIFOPush(&ipcBufPool->fifo, entry) != 0)
return nullptr;
}
return ipcBufPool;
}
uint8* IPCBufPoolAllocate(IPCBufPool_t* ipcBufPool, uint32 size)
{
uint8* entry = nullptr;
OSLockMutex(&ipcBufPool->mutex);
if (ipcBufPool->magic == (uint32be)0xBADF00D && size <= (uint32)ipcBufPool->entrySize1)
{
FIFOPop(&ipcBufPool->fifo, &entry);
}
else
{
assert_dbg();
}
OSUnlockMutex(&ipcBufPool->mutex);
return entry;
}
sint32 IPCBufPoolFree(IPCBufPool_t* ipcBufPool, uint8* entry)
{
OSLockMutex(&ipcBufPool->mutex);
sint32 res = 0;
if (ipcBufPool->magic == (uint32be)0xBADF00D)
{
// check if entry is actually part of the pool
uint32 offset = (uint32)(entry - (uint8*)ipcBufPool->entryStartPtr.GetPtr());
if ((offset % (uint32)ipcBufPool->entrySize2) != 0)
assert_dbg();
uint32 index = offset / (uint32)ipcBufPool->entrySize2;
if ((index >= (uint32)ipcBufPool->entryCount))
assert_dbg();
FIFOPush(&ipcBufPool->fifo, entry);
}
else
{
assert_dbg();
res = -4;
}
OSUnlockMutex(&ipcBufPool->mutex);
return res;
}
void coreinitExport_IPCBufPoolCreate(PPCInterpreter_t* hCPU)
{
ppcDefineParamTypePtr(bufferArea, uint8, 0);
ppcDefineParamU32(bufferSize, 1);
ppcDefineParamU32(entrySize, 2);
ppcDefineParamU32BEPtr(entryCountOutput, 3);
ppcDefineParamU32(uknR7, 4);
IPCBufPool_t* ipcBufPool = IPCBufPoolCreate(bufferArea, bufferSize, entrySize, entryCountOutput, uknR7);
osLib_returnFromFunction(hCPU, memory_getVirtualOffsetFromPointer(ipcBufPool));
return;
// example dump of IPC buffer (at 0x1011FF40):
// 0000 0B AD F0 0D 10 11 FF 40 00 00 53 01 00 00 00 01 00 00 00 00 00 00 02 C0 00 00 02 C0 00 00 00 1D
// 0020 10 12 00 40 00 00 00 78 00 00 00 00 00 00 00 00 00 00 00 1D 00 00 00 1D 10 11 FF A8 6D 55 74 58
// 0040 10 00 87 2C 00 00 00 00 00 00 00 00 00 00 00 00 10 11 FF 7C 00 00 00 00 00 00 00 00 00 00 00 00
// 0060 00 00 00 00 00 00 00 00 10 12 00 40 10 12 03 00 10 12 05 C0 10 12 08 80 10 12 0B 40 10 12 0E 00
// 0080 10 12 10 C0 10 12 13 80 10 12 16 40 10 12 19 00 10 12 1B C0 10 12 1E 80 10 12 21 40 10 12 24 00
// 00A0 10 12 26 C0 10 12 29 80 10 12 2C 40 10 12 2F 00 10 12 31 C0 10 12 34 80 10 12 37 40 10 12 3A 00
// 00C0 10 12 3C C0 10 12 3F 80 10 12 42 40 10 12 45 00 10 12 47 C0 10 12 4A 80 10 12 4D 40 00 00 00 00
// 00E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
}
void coreinitExport_IPCBufPoolAllocate(PPCInterpreter_t* hCPU)
{
debug_printf("IPCBufPoolAllocate(0x%08x,0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
ppcDefineParamStructPtr(ipcBufPool, IPCBufPool_t, 0);
ppcDefineParamU32(size, 1);
uint8* entry = IPCBufPoolAllocate(ipcBufPool, size);
osLib_returnFromFunction(hCPU, memory_getVirtualOffsetFromPointer(entry));
}
void coreinitExport_IPCBufPoolFree(PPCInterpreter_t* hCPU)
{
debug_printf("IPCBufPoolFree(0x%08x,0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4]);
ppcDefineParamStructPtr(ipcBufPool, IPCBufPool_t, 0);
ppcDefineParamTypePtr(entry, uint8, 1);
sint32 res = IPCBufPoolFree(ipcBufPool, entry);
osLib_returnFromFunction(hCPU, res);
}
void InitializeIPCBuf()
{
osLib_addFunction("coreinit", "IPCBufPoolCreate", coreinitExport_IPCBufPoolCreate);
osLib_addFunction("coreinit", "IPCBufPoolAllocate", coreinitExport_IPCBufPoolAllocate);
osLib_addFunction("coreinit", "IPCBufPoolFree", coreinitExport_IPCBufPoolFree);
}
}

View file

@ -0,0 +1,6 @@
#pragma once
namespace coreinit
{
void InitializeIPCBuf();
}

View file

@ -0,0 +1,198 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
#include "Cafe/OS/libs/coreinit/coreinit.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/libs/padscore/padscore.h"
#include "Cafe/OS/libs/coreinit/coreinit_SysHeap.h"
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
#include "Cafe/OS/libs/vpad/vpad.h"
#include "Cafe/OS/libs/coreinit/coreinit_GHS.h"
#include "Cafe/OS/libs/coreinit/coreinit_DynLoad.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
#include "Cafe/CafeSystem.h"
extern MPTR _entryPoint;
extern RPLModule* applicationRPX;
typedef struct
{
MPTR argv[32];
uint32be argc;
char argStorage[0x1000];
}coreinitInit_t;
coreinitInit_t* _coreinitInfo = nullptr;
MPTR OSAllocFromSystem(uint32 size, uint32 alignment)
{
return coreinit_allocFromSysArea(size, alignment);
}
void OSFreeToSystem(MPTR mem)
{
coreinit_freeToSysArea(mem);
}
extern std::string _pathToExecutable;
sint32 argStorageIndex;
void _AddArg(const char* arg, sint32 len)
{
uint32 argc = _coreinitInfo->argc;
char* argStorageStr = _coreinitInfo->argStorage + argStorageIndex;
memcpy(argStorageStr, arg, len);
argStorageStr[len] = '\0';
argStorageIndex += (sint32)strlen(arg) + 1;
_coreinitInfo->argv[argc] = _swapEndianU32(memory_getVirtualOffsetFromPointer(argStorageStr));
_coreinitInfo->argc = argc+1;
}
sint32 _GetArgLength(const char* arg)
{
sint32 c = 0;
while (*arg)
{
if (*arg == ' ')
break; // end at whitespace
cemu_assert_debug(*arg != '\"' && *arg != '\''); // todo
arg++;
c++;
}
return c;
}
void CafeInit()
{
// extract executable filename
sint32 rpxPathStart = (sint32)_pathToExecutable.size() - 1;
if (rpxPathStart > 0)
{
while (rpxPathStart > 0 && _pathToExecutable[rpxPathStart-1] != '/')
rpxPathStart--;
}
else
{
rpxPathStart = 0;
}
std::string_view rpxFileName = std::basic_string_view<char>(_pathToExecutable.data() + rpxPathStart, _pathToExecutable.data() + _pathToExecutable.size());
argStorageIndex = 0;
_coreinitInfo->argc = 0;
_AddArg(rpxFileName.data(), rpxFileName.size());
strcpy((char*)_coreinitInfo->argStorage, std::string(rpxFileName).c_str());
std::string _argStr = CafeSystem::GetForegroundTitleArgStr();
const char* argString = _argStr.c_str();
// attach parameters from arg string
if (argString && argString[0] != '\0')
{
const char* t = strstr(argString, ".rpx");
if (t)
{
t += 4;
while (*t)
{
// skip all whitespace
while (*t)
{
if (*t == ' ')
{
t++;
}
else
break;
}
// get length of arg
sint32 argLength = _GetArgLength(t);
if (argLength > 0)
{
// add arg
_AddArg(t, argLength);
// next
t += argLength;
}
}
}
else
{
forceLogDebug_printf("Unable to find end of rpx file name in arg string");
}
}
// setup UGQR
ppcInterpreterCurrentInstance->spr.UGQR[0 + 2] = 0x00040004;
ppcInterpreterCurrentInstance->spr.UGQR[0 + 3] = 0x00050005;
ppcInterpreterCurrentInstance->spr.UGQR[0 + 4] = 0x00060006;
ppcInterpreterCurrentInstance->spr.UGQR[0 + 5] = 0x00070007;
coreinit::InitForegroundBucket();
coreinit::InitSysHeap();
}
struct PreinitUserHeapStruct
{
MEMPTR<coreinit::MEMHeapBase> heapTempMEM1;
MEMPTR<coreinit::MEMHeapBase> heapTempFG;
MEMPTR<coreinit::MEMHeapBase> heapTempMEM2;
};
SysAllocator<PreinitUserHeapStruct> g_preinitUserParam;
void InitCafeHeaps()
{
// init default heaps
g_preinitUserParam->heapTempMEM1 = nullptr;
g_preinitUserParam->heapTempFG = nullptr;
g_preinitUserParam->heapTempMEM2 = nullptr;
coreinit::InitDefaultHeaps(g_preinitUserParam->heapTempMEM1, g_preinitUserParam->heapTempFG, g_preinitUserParam->heapTempMEM2);
// if __preinit_user export exists in main executable, run it and pass our heaps
MPTR exportAddr = applicationRPX ? RPLLoader_FindRPLExport(applicationRPX, "__preinit_user", false) : MPTR_NULL;
if (exportAddr != MPTR_NULL)
{
PPCCoreCallback(exportAddr, &g_preinitUserParam->heapTempMEM1, &g_preinitUserParam->heapTempFG, &g_preinitUserParam->heapTempMEM2);
}
// setup heaps
if (g_preinitUserParam->heapTempMEM1 != nullptr)
coreinit::MEMSetBaseHeapHandle(0, g_preinitUserParam->heapTempMEM1);
if (g_preinitUserParam->heapTempFG != nullptr)
coreinit::MEMSetBaseHeapHandle(8, g_preinitUserParam->heapTempFG);
if (g_preinitUserParam->heapTempMEM2 != nullptr)
coreinit::MEMSetBaseHeapHandle(1, g_preinitUserParam->heapTempMEM2);
}
MPTR CoreInitEntry(sint32 argc, MPTR argv)
{
const char* rpxPath = (char*)memory_getPointerFromVirtualOffset(memory_readU32(argv + 0));
InitCafeHeaps();
// do a dummy allocation via the OSDynLoad allocator
// Watch Dogs relies on this to correctly set up its malloc() allocator
// must be larger than 0x190 to trigger creation of a new memory segment. But also must not be close to page alignment (0x1000) or else the bug will trigger
void* dummyAlloc = coreinit::OSDynLoad_AllocatorAlloc(0x500, 0x4);
if (dummyAlloc)
coreinit::OSDynLoad_AllocatorFree(dummyAlloc);
return _entryPoint;
}
sint32 _coreinitTitleEntryPoint;
void coreinit_start(PPCInterpreter_t* hCPU)
{
_coreinitInfo = (coreinitInit_t*)memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sizeof(coreinitInit_t), 32));
memset(_coreinitInfo, 0, sizeof(coreinitInit_t));
CafeInit();
_coreinitTitleEntryPoint = CoreInitEntry(_coreinitInfo->argc, memory_getVirtualOffsetFromPointer(_coreinitInfo->argv));
RPLLoader_CallEntrypoints();
// init vpadbase (todo - simulate entrypoints for HLE modules)
padscore::start();
vpad::start();
// continue at main executable entrypoint
hCPU->gpr[4] = memory_getVirtualOffsetFromPointer(_coreinitInfo->argv);
hCPU->gpr[3] = _coreinitInfo->argc;
hCPU->instructionPointer = _coreinitTitleEntryPoint;
}

View file

@ -0,0 +1,299 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit.h"
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
/*
Locked cache is mapped to the following memory regions:
offset size
core0: 0xFFC00000 0x4000
core1: 0xFFC40000 0x4000
core2: 0xFFC80000 0x4000
*/
#define LC_LOCKED_CACHE_GRANULARITY (0x200) // 512B
#define LC_LOCKED_CACHE_SIZE (0x4000) // 16KB
#define LC_MASK_FREE (0)
#define LC_MASK_RESERVED (1)
#define LC_MASK_RESERVED_END (2) // indicates end of reserved block
namespace coreinit
{
uint8 lcCacheMask[PPC_CORE_COUNT][(LC_LOCKED_CACHE_SIZE + LC_LOCKED_CACHE_GRANULARITY - 1) / LC_LOCKED_CACHE_GRANULARITY] = { 0 };
uint32 lcAllocatedBlocks[PPC_CORE_COUNT] = { 0 };
MPTR lcAddr[] =
{
0xFFC00000, // core 0
0xFFC40000, // core 1
0xFFC80000 // core 2
};
void coreinitExport_LCGetMaxSize(PPCInterpreter_t* hCPU)
{
osLib_returnFromFunction(hCPU, LC_LOCKED_CACHE_SIZE);
}
void coreinitExport_LCAlloc(PPCInterpreter_t* hCPU)
{
//debug_printf("LCAlloc(0x%04x) Thread %08x Core %d", hCPU->gpr[3], coreinitThread_getCurrentThread(hCPU), PPCInterpreter_getCoreIndex(hCPU));
uint32 size = hCPU->gpr[3];
if (size == 0 || size > LC_LOCKED_CACHE_SIZE)
{
debug_printf("LCAlloc: Invalid alloc size %d\n", size);
osLib_returnFromFunction(hCPU, MPTR_NULL);
return;
}
if ((size % LC_LOCKED_CACHE_GRANULARITY) != 0)
{
debug_printf("LCAlloc: Unaligned alloc size 0x%04x\n", size);
size += (LC_LOCKED_CACHE_GRANULARITY - 1);
size &= ~(LC_LOCKED_CACHE_GRANULARITY - 1);
}
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
uint8* cacheMask = lcCacheMask[coreIndex];
uint32 entryMaskLength = size / LC_LOCKED_CACHE_GRANULARITY;
for (uint32 i = 0; i <= (LC_LOCKED_CACHE_SIZE / LC_LOCKED_CACHE_GRANULARITY) - entryMaskLength; i++)
{
// check if range starting at i is free
bool rangeIsFree = true;
for (uint32 f = 0; f < entryMaskLength; f++)
{
if (cacheMask[i + f] != LC_MASK_FREE)
{
rangeIsFree = false;
break;
}
}
if (rangeIsFree)
{
// found space for allocation
MPTR allocAddr = lcAddr[coreIndex] + i * LC_LOCKED_CACHE_GRANULARITY;
// mark range as allocated
for (uint32 f = 0; f < entryMaskLength - 1; f++)
{
cacheMask[i + f] = LC_MASK_RESERVED;
}
cacheMask[i + entryMaskLength - 1] = LC_MASK_RESERVED_END;
// update allocation counter
lcAllocatedBlocks[coreIndex] += entryMaskLength;
// return allocAddr
//debug_printf("LCAlloc result %08x Thread %08x Core %d", (uint32)allocAddr, coreinitThread_getCurrentThread(hCPU), PPCInterpreter_getCoreIndex(hCPU));
osLib_returnFromFunction(hCPU, allocAddr);
return;
}
}
// not enough space left
//debug_printf("LCAlloc failed Thread %08x Core %d", coreinitThread_getCurrentThread(hCPU), PPCInterpreter_getCoreIndex(hCPU));
osLib_returnFromFunction(hCPU, MPTR_NULL);
}
void coreinitExport_LCDealloc(PPCInterpreter_t* hCPU)
{
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
uint8* cacheMask = lcCacheMask[coreIndex];
//printf("LCDealloc(0x%08x)\n", hCPU->gpr[3]);
MPTR deallocAddr = hCPU->gpr[3];
if (deallocAddr < lcAddr[coreIndex] || deallocAddr >= (lcAddr[coreIndex] + LC_LOCKED_CACHE_SIZE))
{
// out of bounds
#ifndef PUBLIC_RELEASE
forceLog_printf("LCDealloc(): Out of bounds");
#endif
osLib_returnFromFunction(hCPU, 0);
return;
}
uint32 relativeOffset = (uint32)deallocAddr - lcAddr[coreIndex];
if ((relativeOffset % LC_LOCKED_CACHE_GRANULARITY) != 0)
{
debug_printf("LCDealloc: Offset alignment is invalid (0x%08x / 0x%08x)\n", deallocAddr, relativeOffset);
osLib_returnFromFunction(hCPU, 0);
return;
}
uint32 startIndex = relativeOffset / LC_LOCKED_CACHE_GRANULARITY;
// check this is really the beginning of an allocated block
if (startIndex > 0 && (cacheMask[startIndex - 1] != LC_MASK_RESERVED_END && cacheMask[startIndex - 1] != LC_MASK_FREE))
{
debug_printf("LCDealloc: Offset is invalid (0x%08x / 0x%08x)\n", deallocAddr, relativeOffset);
osLib_returnFromFunction(hCPU, 0);
return;
}
// free range by reseting mask in allocated region
for (uint32 i = startIndex; i < (LC_LOCKED_CACHE_SIZE / LC_LOCKED_CACHE_GRANULARITY); i++)
{
if (cacheMask[i] == LC_MASK_RESERVED_END)
{
// end of allocated block reached
cacheMask[i] = LC_MASK_FREE;
// update allocation counter
lcAllocatedBlocks[coreIndex]--;
break;
}
else if (cacheMask[i] == LC_MASK_FREE)
{
debug_printf("LCDealloc: Allocation mask error detected\n");
break;
}
cacheMask[i] = LC_MASK_FREE;
// update allocation counter
lcAllocatedBlocks[coreIndex]--;
}
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_LCGetUnallocated(PPCInterpreter_t* hCPU)
{
uint32 unallocatedBytes = 0;
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
unallocatedBytes = LC_LOCKED_CACHE_SIZE - (lcAllocatedBlocks[coreIndex] * LC_LOCKED_CACHE_GRANULARITY);
//debug_printf("LCGetUnallocated() Result: 0x%x\n", unallocatedBytes);
osLib_returnFromFunction(hCPU, unallocatedBytes);
}
void coreinitExport_LCGetAllocatableSize(PPCInterpreter_t* hCPU)
{
debug_printf("LCGetAllocatableSize()\n");
// no parameters, returns largest allocatable block size
// get core LC parameters
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
uint8* cacheMask = lcCacheMask[coreIndex];
// scan for largest range of available blocks
uint32 largestFreeRange = 0;
uint32 currentRangeSize = 0;
for (uint32 i = 0; i < LC_LOCKED_CACHE_SIZE / LC_LOCKED_CACHE_GRANULARITY; i++)
{
if (cacheMask[i] == LC_MASK_FREE)
{
currentRangeSize++;
}
else
{
largestFreeRange = std::max(largestFreeRange, currentRangeSize);
currentRangeSize = 0;
}
}
largestFreeRange = std::max(largestFreeRange, currentRangeSize);
osLib_returnFromFunction(hCPU, largestFreeRange * LC_LOCKED_CACHE_GRANULARITY);
}
uint32 LCIsEnabled[PPC_CORE_COUNT] = { 0 };
void coreinitExport_LCEnableDMA(PPCInterpreter_t* hCPU)
{
//debug_printf("LCEnableDMA()\n");
LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)]++;
osLib_returnFromFunction(hCPU, 1);
}
sint32 _lcDisableErrorCounter = 0;
void coreinitExport_LCDisableDMA(PPCInterpreter_t* hCPU)
{
//debug_printf("LCDisableDMA()\n");
#ifndef PUBLIC_RELASE
if (LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)] == 0)
assert_dbg();
#endif
LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)]--;
#ifndef PUBLIC_RELEASE
if (LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)] == 0)
{
uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU);
uint8* cacheMask = lcCacheMask[coreIndex];
for (uint32 i = 0; i <= (LC_LOCKED_CACHE_SIZE / LC_LOCKED_CACHE_GRANULARITY); i++)
{
if (cacheMask[i] != LC_MASK_FREE)
{
if (_lcDisableErrorCounter < 15)
{
forceLog_printf("LC disabled but there is still memory allocated");
_lcDisableErrorCounter++;
}
}
}
}
#endif
osLib_returnFromFunction(hCPU, 1);
}
void coreinitExport_LCIsDMAEnabled(PPCInterpreter_t* hCPU)
{
osLib_returnFromFunction(hCPU, LCIsEnabled[PPCInterpreter_getCoreIndex(hCPU)] ? 1 : 0);
}
void coreinitExport_LCHardwareIsAvailable(PPCInterpreter_t* hCPU)
{
// on the real HW, LC is shared between processes and not all processes can access it. In the emulator we don't care and always return true.
osLib_returnFromFunction(hCPU, 1);
}
void coreinitExport_LCLoadDMABlocks(PPCInterpreter_t* hCPU)
{
//printf("LCLoadDMABlocks(0x%08x, 0x%08x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
uint32 numBlocks = hCPU->gpr[5];
if (numBlocks == 0)
numBlocks = 128;
uint32 transferSize = numBlocks * 32;
uint8* destPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint8* srcPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
// copy right away, we don't emulate the DMAQueue currently
memcpy(destPtr, srcPtr, transferSize);
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_LCStoreDMABlocks(PPCInterpreter_t* hCPU)
{
//printf("LCStoreDMABlocks(0x%08x, 0x%08x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
uint32 numBlocks = hCPU->gpr[5];
if (numBlocks == 0)
numBlocks = 128;
//uint32 transferSize = numBlocks*32;
uint8* destPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint8* srcPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
// copy right away, we don't emulate the DMAQueue currently
memcpy_qwords(destPtr, srcPtr, numBlocks * (32 / sizeof(uint64)));
LatteBufferCache_notifyDCFlush(hCPU->gpr[3], numBlocks * 32);
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_LCWaitDMAQueue(PPCInterpreter_t* hCPU)
{
//printf("LCWaitDMAQueue(%d)\n", hCPU->gpr[3]);
uint32 len = hCPU->gpr[3];
// todo: Implement (be aware DMA queue is per core)
osLib_returnFromFunction(hCPU, 0);
}
void InitializeLC()
{
for (sint32 f = 0; f < PPC_CORE_COUNT; f++)
{
memset(lcCacheMask[f], LC_MASK_FREE, (LC_LOCKED_CACHE_SIZE + LC_LOCKED_CACHE_GRANULARITY - 1) / LC_LOCKED_CACHE_GRANULARITY);
}
osLib_addFunction("coreinit", "LCGetMaxSize", coreinitExport_LCGetMaxSize);
osLib_addFunction("coreinit", "LCAlloc", coreinitExport_LCAlloc);
osLib_addFunction("coreinit", "LCDealloc", coreinitExport_LCDealloc);
osLib_addFunction("coreinit", "LCGetUnallocated", coreinitExport_LCGetUnallocated);
osLib_addFunction("coreinit", "LCGetAllocatableSize", coreinitExport_LCGetAllocatableSize);
osLib_addFunction("coreinit", "LCEnableDMA", coreinitExport_LCEnableDMA);
osLib_addFunction("coreinit", "LCDisableDMA", coreinitExport_LCDisableDMA);
osLib_addFunction("coreinit", "LCIsDMAEnabled", coreinitExport_LCIsDMAEnabled);
osLib_addFunction("coreinit", "LCHardwareIsAvailable", coreinitExport_LCHardwareIsAvailable);
osLib_addFunction("coreinit", "LCLoadDMABlocks", coreinitExport_LCLoadDMABlocks);
osLib_addFunction("coreinit", "LCStoreDMABlocks", coreinitExport_LCStoreDMABlocks);
osLib_addFunction("coreinit", "LCWaitDMAQueue", coreinitExport_LCWaitDMAQueue);
}
}

View file

@ -0,0 +1,6 @@
#pragma once
namespace coreinit
{
void InitializeLC();
}

View file

@ -0,0 +1,561 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/IOSU/legacy/iosu_ioctl.h"
#include "Cafe/IOSU/legacy/iosu_mcp.h"
#include "Cafe/OS/libs/coreinit/coreinit_IOS.h"
#include "Cafe/OS/libs/coreinit/coreinit_MCP.h"
#include "Cafe/OS/libs/nn_common.h"
#include "Cafe/OS/libs/nn_act/nn_act.h"
#include "config/CemuConfig.h"
#include "Cafe/CafeSystem.h"
#define mcpPrepareRequest() \
StackAllocator<iosuMcpCemuRequest_t> _buf_mcpRequest; \
StackAllocator<ioBufferVector_t> _buf_bufferVector; \
iosuMcpCemuRequest_t* mcpRequest = _buf_mcpRequest.GetPointer(); \
ioBufferVector_t* mcpBufferVector = _buf_bufferVector.GetPointer(); \
memset(mcpRequest, 0, sizeof(iosuMcpCemuRequest_t)); \
memset(mcpBufferVector, 0, sizeof(ioBufferVector_t)); \
mcpBufferVector->buffer = (uint8*)mcpRequest;
#define MCP_FAKE_HANDLE (0x00000010)
typedef struct
{
uint32be n0;
uint32be n1;
uint32be n2;
}mcpSystemVersion_t;
static_assert(sizeof(MCPTitleInfo) == 0x61, "title entry size is invalid");
static_assert(offsetof(MCPTitleInfo, appPath) == 0xC, "title entry appPath has invalid offset");
static_assert(offsetof(MCPTitleInfo, titleVersion) == 0x48, "title entry titleVersion has invalid offset");
static_assert(offsetof(MCPTitleInfo, osVersion) == 0x4A, "title entry osVersion has invalid offset");
MCPHANDLE MCP_Open()
{
// placeholder
return MCP_FAKE_HANDLE;
}
void MCP_Close(MCPHANDLE mcpHandle)
{
// placeholder
}
void coreinitExport_MCP_Open(PPCInterpreter_t* hCPU)
{
// placeholder
osLib_returnFromFunction(hCPU, MCP_Open());
}
void coreinitExport_MCP_Close(PPCInterpreter_t* hCPU)
{
// placeholder
osLib_returnFromFunction(hCPU, 0);
}
sint32 MCP_GetSysProdSettings(MCPHANDLE mcpHandle, SysProdSettings* sysProdSettings)
{
memset(sysProdSettings, 0x00, sizeof(SysProdSettings));
// todo: Other fields are currently unknown
sysProdSettings->gameRegion = (uint8)CafeSystem::GetForegroundTitleRegion();
sysProdSettings->platformRegion = (uint8)CafeSystem::GetPlatformRegion();
// contains Wii U serial parts at +0x1A and +0x22?
return 0;
}
void coreinitExport_MCP_GetSysProdSettings(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("MCP_GetSysProdSettings(0x%08x,0x%08x)", hCPU->gpr[3], hCPU->gpr[4]);
sint32 result = MCP_GetSysProdSettings(hCPU->gpr[3], (SysProdSettings*)memory_getPointerFromVirtualOffset(hCPU->gpr[4]));
osLib_returnFromFunction(hCPU, result);
}
void coreinitExport_MCP_TitleListByAppType(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("MCP_TitleListByAppType(0x%08x,0x%08x,0x%08x,0x%08x,0x%08x)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]);
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamU32(appType, 1);
ppcDefineParamU32BEPtr(countOutput, 2);
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 3);
ppcDefineParamU32(titleListSize, 4);
sint32 maxCount = titleListSize / sizeof(MCPTitleInfo);
mcpPrepareRequest();
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_LIST_BY_APP_TYPE;
mcpRequest->titleListRequest.titleCount = maxCount;
mcpRequest->titleListRequest.titleList = titleList;
mcpRequest->titleListRequest.titleListBufferSize = sizeof(MCPTitleInfo) * 1;
mcpRequest->titleListRequest.appType = appType;
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
*countOutput = mcpRequest->titleListRequest.titleCount;
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_MCP_TitleList(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("MCP_TitleList(...) unimplemented");
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamU32BEPtr(countOutput, 1);
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 2);
ppcDefineParamU32(titleListBufferSize, 3);
// todo -> Other parameters
mcpPrepareRequest();
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_LIST;
mcpRequest->titleListRequest.titleCount = *countOutput;
mcpRequest->titleListRequest.titleList = titleList;
mcpRequest->titleListRequest.titleListBufferSize = titleListBufferSize;
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
*countOutput = mcpRequest->titleListRequest.titleCount;
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
}
void coreinitExport_MCP_TitleCount(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("MCP_TitleCount(): Untested");
ppcDefineParamU32(mcpHandle, 0);
mcpPrepareRequest();
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_COUNT;
mcpRequest->titleListRequest.titleCount = 0;
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
osLib_returnFromFunction(hCPU, mcpRequest->titleListRequest.titleCount);
}
void coreinitExport_MCP_GetTitleInfo(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamU64(titleId, 2);
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 4);
forceLogDebug_printf("MCP_GetTitleInfo() called");
mcpPrepareRequest();
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_INFO;
mcpRequest->titleListRequest.titleCount = 1;
mcpRequest->titleListRequest.titleList = titleList;
mcpRequest->titleListRequest.titleListBufferSize = sizeof(MCPTitleInfo);
mcpRequest->titleListRequest.titleId = titleId;
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
if (mcpRequest->titleListRequest.titleCount == 0)
{
forceLog_printf("MCP_GetTitleInfo() failed to get title info");
}
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
}
void coreinitExport_MCP_GetTitleInfoByTitleAndDevice(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamU64(titleId, 2);
ppcDefineParamStr(device, 4); // e.g. "odd"
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 5);
forceLogDebug_printf("MCP_GetTitleInfoByTitleAndDevice() called (todo - device type support)");
mcpPrepareRequest();
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_INFO;
mcpRequest->titleListRequest.titleCount = 1;
mcpRequest->titleListRequest.titleList = titleList;
mcpRequest->titleListRequest.titleListBufferSize = sizeof(MCPTitleInfo);
mcpRequest->titleListRequest.titleId = titleId;
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
if (mcpRequest->titleListRequest.titleCount == 0)
{
forceLogDebug_printf("MCP_GetTitleInfoByTitleAndDevice() no title found");
osLib_returnFromFunction(hCPU, BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_MCP, 0)); // E-Shop/nn_vctl.rpl expects error to be returned when no title is found
return;
}
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
}
namespace coreinit
{
void export_MCP_GetSystemVersion(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("MCP_GetSystemVersion(%d,0x%08x)", hCPU->gpr[3], hCPU->gpr[4]);
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamStructPtr(systemVersion, mcpSystemVersion_t, 1);
systemVersion->n0 = 0x5;
systemVersion->n1 = 0x5;
systemVersion->n2 = 0x2;
// todo: Load this from \sys\title\00050010\10041200\content\version.bin
osLib_returnFromFunction(hCPU, 0);
}
void export_MCP_Get4SecondOffStatus(PPCInterpreter_t* hCPU)
{
// r3 = mcpHandle
forceLogDebug_printf("MCP_Get4SecondOffStatus(...) placeholder");
// if this returns 1 then Barista will display the warning about cold-shutdown ('Holding the POWER button for at least four seconds...')
osLib_returnFromFunction(hCPU, 0);
}
void export_MCP_TitleListByDevice(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamStr(deviceName, 1);
ppcDefineParamU32BEPtr(titleCount, 2);
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 3); // type guessed
// purpose of r7 parameter is unknown
cemu_assert_unimplemented();
titleList[0].titleIdHigh = 0x00050000;
titleList[0].titleIdLow = 0x11223344;
strcpy(titleList[0].appPath, "titlePathTest");
*titleCount = 1;
forceLogDebug_printf("MCP_TitleListByDevice() - placeholder");
osLib_returnFromFunction(hCPU, 0);
}
#pragma pack(1)
typedef struct
{
/* +0x000 */ char storageName[0x90]; // the name in the storage path
/* +0x090 */ char storagePath[0x280 - 1]; // /vol/storage_%s%02x
/* +0x30F */ uint32be storageSubindexOrMask; // the id in the storage path, but this might also be a MASK of indices (e.g. 1 -> Only device 1, 7 -> Device 1,2,3) men.rpx expects 0xF (or 0x7?) to be set for MLC, SLC and USB for MLC_FullDeviceList
uint8 ukn313[4];
uint8 ukn317[4];
}MCPDevice_t;
#pragma pack()
static_assert(sizeof(MCPDevice_t) == 0x31B, "MCPDevice_t has invalid size");
static_assert(offsetof(MCPDevice_t, storagePath) == 0x090, "MCPDevice_t.storagePath has invalid offset");
static_assert(offsetof(MCPDevice_t, storageSubindexOrMask) == 0x30F, "MCPDevice_t.storageSubindex has invalid offset");
static_assert(offsetof(MCPDevice_t, ukn313) == 0x313, "MCPDevice_t.ukn313 has invalid offset");
static_assert(offsetof(MCPDevice_t, ukn317) == 0x317, "MCPDevice_t.ukn317 has invalid offset");
void MCP_DeviceListEx(uint32 mcpHandle, uint32be* deviceCount, MCPDevice_t* deviceList, uint32 deviceListSize, bool returnFullList)
{
sint32 maxDeviceCount = deviceListSize / sizeof(MCPDevice_t);
if (maxDeviceCount < 3*3)
assert_dbg();
// if this doesnt return both MLC and SLC friendlist (frd.rpx) will softlock during boot
memset(deviceList, 0, sizeof(MCPDevice_t) * 1);
sint32 index = 0;
for (sint32 f = 0; f < 1; f++)
{
// 0
strcpy(deviceList[index].storageName, "mlc");
deviceList[index].storageSubindexOrMask = 0xF; // bitmask?
sprintf(deviceList[index].storagePath, "/vol/storage_%s%02x", deviceList[index].storageName, (sint32)deviceList[index].storageSubindexOrMask);
index++;
// 1
strcpy(deviceList[index].storageName, "slc");
deviceList[index].storageSubindexOrMask = 0xF; // bitmask?
sprintf(deviceList[index].storagePath, "/vol/storage_%s%02x", deviceList[index].storageName, (sint32)deviceList[index].storageSubindexOrMask);
index++;
// 2
strcpy(deviceList[index].storageName, "usb");
deviceList[index].storageSubindexOrMask = 0xF;
sprintf(deviceList[index].storagePath, "/vol/storage_%s%02x", deviceList[index].storageName, (sint32)deviceList[index].storageSubindexOrMask);
index++;
}
*deviceCount = index;
}
void export_MCP_DeviceList(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamU32BEPtr(deviceCount, 1);
ppcDefineParamStructPtr(deviceList, MCPDevice_t, 2);
ppcDefineParamU32(deviceListSize, 3);
forceLogDebug_printf("MCP_DeviceList() - placeholder LR %08x", hCPU->spr.LR);
sint32 maxDeviceCount = deviceListSize / sizeof(MCPDevice_t);
if (maxDeviceCount < 2)
assert_dbg();
// if this doesnt return both MLC and SLC friendlist (frd.rpx) will softlock during boot
memset(deviceList, 0, sizeof(MCPDevice_t) * 1);
// 0
strcpy(deviceList[0].storageName, "mlc");
deviceList[0].storageSubindexOrMask = (0x01); // bitmask?
sprintf(deviceList[0].storagePath, "/vol/storage_%s%02x", deviceList[0].storageName, (sint32)deviceList[0].storageSubindexOrMask);
// 1
strcpy(deviceList[1].storageName, "slc");
deviceList[1].storageSubindexOrMask = (0x01); // bitmask?
sprintf(deviceList[1].storagePath, "/vol/storage_%s%02x", deviceList[1].storageName, (sint32)deviceList[1].storageSubindexOrMask);
// 2
//strcpy(deviceList[2].storageName, "usb");
//deviceList[2].storageSubindex = 1;
//sprintf(deviceList[2].storagePath, "/vol/storage_%s%02x", deviceList[2].storageName, (sint32)deviceList[2].storageSubindex);
*deviceCount = 2;
osLib_returnFromFunction(hCPU, 0);
}
void export_MCP_FullDeviceList(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamU32BEPtr(deviceCount, 1);
ppcDefineParamStructPtr(deviceList, MCPDevice_t, 2);
ppcDefineParamU32(deviceListSize, 3);
forceLogDebug_printf("MCP_FullDeviceList() - placeholder LR %08x", hCPU->spr.LR);
MCP_DeviceListEx(mcpHandle, deviceCount, deviceList, deviceListSize, true);
osLib_returnFromFunction(hCPU, 0);
}
void export_MCP_UpdateCheckContext(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamU32BEPtr(unknownParam, 1);
forceLogDebug_printf("MCP_UpdateCheckContext() - placeholder (might be wrong)");
*unknownParam = 1;
osLib_returnFromFunction(hCPU, 0);
}
void export_MCP_TitleListUpdateGetNext(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamMPTR(callbackMPTR, 1);
forceLogDebug_printf("MCP_TitleListUpdateGetNext() - placeholder/unimplemented");
// this callback is to let the app know when the title list changed?
osLib_returnFromFunction(hCPU, 0);
}
void export_MCP_GetOverlayAppInfo(PPCInterpreter_t* hCPU)
{
forceLogDebug_printf("MCP_GetOverlayAppInfo(...) placeholder");
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamU32(appType, 1);
ppcDefineParamU32(uknR5, 2);
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 3);
// hacky
mcpPrepareRequest();
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_LIST_BY_APP_TYPE;
mcpRequest->titleListRequest.titleCount = 1;
mcpRequest->titleListRequest.titleList = titleList;
mcpRequest->titleListRequest.titleListBufferSize = sizeof(MCPTitleInfo)*1;
mcpRequest->titleListRequest.appType = appType;
__depr__IOS_Ioctlv(IOS_DEVICE_MCP, IOSU_MCP_REQUEST_CEMU, 1, 1, mcpBufferVector);
if (mcpRequest->titleListRequest.titleCount != 1)
assert_dbg();
osLib_returnFromFunction(hCPU, 0);
}
void InitializeMCP()
{
osLib_addFunction("coreinit", "MCP_Open", coreinitExport_MCP_Open);
osLib_addFunction("coreinit", "MCP_Close", coreinitExport_MCP_Close);
osLib_addFunction("coreinit", "MCP_GetSysProdSettings", coreinitExport_MCP_GetSysProdSettings);
osLib_addFunction("coreinit", "MCP_TitleListByAppType", coreinitExport_MCP_TitleListByAppType);
osLib_addFunction("coreinit", "MCP_TitleList", coreinitExport_MCP_TitleList);
osLib_addFunction("coreinit", "MCP_TitleCount", coreinitExport_MCP_TitleCount);
osLib_addFunction("coreinit", "MCP_GetTitleInfo", coreinitExport_MCP_GetTitleInfo);
osLib_addFunction("coreinit", "MCP_GetTitleInfoByTitleAndDevice", coreinitExport_MCP_GetTitleInfoByTitleAndDevice);
osLib_addFunction("coreinit", "MCP_TitleListByDevice", export_MCP_TitleListByDevice);
osLib_addFunction("coreinit", "MCP_GetSystemVersion", export_MCP_GetSystemVersion);
osLib_addFunction("coreinit", "MCP_Get4SecondOffStatus", export_MCP_Get4SecondOffStatus);
osLib_addFunction("coreinit", "MCP_DeviceList", export_MCP_DeviceList);
osLib_addFunction("coreinit", "MCP_FullDeviceList", export_MCP_FullDeviceList);
osLib_addFunction("coreinit", "MCP_UpdateCheckContext", export_MCP_UpdateCheckContext);
osLib_addFunction("coreinit", "MCP_TitleListUpdateGetNext", export_MCP_TitleListUpdateGetNext);
osLib_addFunction("coreinit", "MCP_GetOverlayAppInfo", export_MCP_GetOverlayAppInfo);
}
}
typedef struct
{
char settingName[0x40];
uint32 ukn1;
uint32 ukn2;
uint32 ukn3;
uint32 ukn4_size; // size guessed
MPTR resultPtr; // pointer to output value
}UCParamStruct_t;
static_assert(sizeof(UCParamStruct_t) == 0x54); // unsure
#if BOOST_OS_LINUX
#define _strcmpi strcasecmp
#endif
void coreinitExport_UCReadSysConfig(PPCInterpreter_t* hCPU)
{
// parameters:
// r3 = UC handle?
// r4 = Number of values to read (count of UCParamStruct_t entries)
// r5 = UCParamStruct_t*
UCParamStruct_t* ucParamBase = (UCParamStruct_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[5]);
uint32 ucParamCount = hCPU->gpr[4];
for(uint32 i=0; i<ucParamCount; i++)
{
UCParamStruct_t* ucParam = ucParamBase+i;
if(_strcmpi(ucParam->settingName, "cafe.cntry_reg") == 0)
{
// country code
// get country from simple address
uint32be simpleAddress = 0;
nn::act::GetSimpleAddressIdEx(&simpleAddress, nn::act::ACT_SLOT_CURRENT);
uint32 countryCode = nn::act::getCountryCodeFromSimpleAddress(simpleAddress);
if( ucParam->resultPtr != _swapEndianU32(MPTR_NULL) )
memory_writeU32(_swapEndianU32(ucParam->resultPtr), countryCode);
}
else if (_strcmpi(ucParam->settingName, "cafe.language") == 0)
{
// language
// 0 -> Japanese
// 1 -> English
// ...
uint32 languageId = (uint32)GetConfig().console_language.GetValue();
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU32(_swapEndianU32(ucParam->resultPtr), languageId);
}
else if (_strcmpi(ucParam->settingName, "cafe.initial_launch") == 0)
{
// initial launch
// possible ids:
// 0xFF Set by SCIFormatSystem (default?)
// 0x01 Inited but no user created yet (setting this will make the home menu go into user creation dialog)
// 0x02 Inited, with user
// other values are unknown
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 2);
}
else if (_strcmpi(ucParam->settingName, "cafe.eula_version") == 0)
{
// todo - investigate values
memory_writeU32(_swapEndianU32(ucParam->resultPtr), 0);
}
else if (_strcmpi(ucParam->settingName, "cafe.eula_agree") == 0)
{
// todo - investigate values
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
}
else if (_strcmpi(ucParam->settingName, "cafe.version") == 0)
{
// todo - investigate values
memory_writeU16(_swapEndianU32(ucParam->resultPtr), 0);
}
else if (_strcmpi(ucParam->settingName, "cafe.eco") == 0)
{
// todo - investigate values
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
}
else if (_strcmpi(ucParam->settingName, "cafe.fast_boot") == 0)
{
// todo - investigate values
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
}
else if (_strcmpi(ucParam->settingName, "parent.enable") == 0)
{
// parental feature enabled
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU32(_swapEndianU32(ucParam->resultPtr), 0);
}
else if (_strcmpi(ucParam->settingName, "nn.act.account_repaired") == 0)
{
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
}
else if (_strcmpi(ucParam->settingName, "p_acct1.net_communication_on_game") == 0)
{
// get parental online control for online features
// note: This option is account-bound, the p_acct1 prefix indicates that the account in slot 1 is used
// account in slot 1
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0); // data type is guessed
}
else if (_strcmpi(ucParam->settingName, "p_acct1.int_movie") == 0)
{
// get parental online control for movies?
// used by Pikmin HD movies
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0); // 0 -> allowed
}
else if (_strcmpi(ucParam->settingName, "p_acct1.network_launcher") == 0)
{
// miiverse restrictions
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0); // data type is guessed (0 -> no restrictions, 1 -> read only?, 2 -> no access?)
}
else if (_strcmpi(ucParam->settingName, "s_acct01.uuid") == 0)
{
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL) && ucParam->ukn4_size >= 37)
{
StackAllocator<uint8, 16> _uuid;
uint8* uuid = _uuid.GetPointer();
nn::act::GetUuidEx(uuid, 1, 0);
char tempStr[64];
sprintf(tempStr, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
strcpy((char*)memory_getPointerFromVirtualOffset(_swapEndianU32(ucParam->resultPtr)), tempStr);
}
}
else if (_strcmpi(ucParam->settingName, "s_acct01.nn.ec.eshop_initialized") == 0)
{
// E-Shop welcome screen
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 1); // data type is guessed
}
else if (_strcmpi(ucParam->settingName, "p_acct1.int_browser") == 0)
{
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
}
else
{
forceLogDebug_printf("Unsupported SCI value: %s Size %08x\n", ucParam->settingName, ucParam->ukn4_size);
}
}
osLib_returnFromFunction(hCPU, 0); // 0 -> success
}

View file

@ -0,0 +1,54 @@
#pragma once
struct SysProdSettings
{
uint8 ukn00; // 0x00
uint8 ukn01; // 0x01
uint8 ukn02; // 0x02
uint8 platformRegion; // 0x03
uint8 ukn04;
uint8 ukn05;
uint8 ukn06;
uint8 ukn07;
uint8 prevBlock;
uint8 ukn09;
uint8 ukn0A;
uint8 gameRegion; // game region?
uint8 nextBlock;
uint8 ukn0D;
uint8 ukn0E;
uint8 ukn0F;
uint8 ukn10;
uint8 ukn11;
uint8 ukn12;
uint8 ukn13;
uint8 ukn14[4];
uint8 ukn18[4];
uint8 ukn1C[4];
uint8 ukn20[4];
uint8 ukn24[4];
uint8 ukn28[4];
uint8 ukn2C[4];
uint8 ukn30[4];
uint8 ukn34[4];
uint8 ukn38[4];
uint8 ukn3C[4];
uint8 ukn40[4];
uint8 ukn44;
uint8 ukn45;
};
static_assert(sizeof(SysProdSettings) == 0x46);
typedef uint32 MCPHANDLE;
MCPHANDLE MCP_Open();
void MCP_Close(MCPHANDLE mcpHandle);
sint32 MCP_GetSysProdSettings(MCPHANDLE mcpHandle, SysProdSettings* sysProdSettings);
void coreinitExport_UCReadSysConfig(PPCInterpreter_t* hCPU);
namespace coreinit
{
void InitializeMCP();
}

View file

@ -0,0 +1,651 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h"
#include "Cafe/OS/libs/coreinit/coreinit_Memory.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.h"
#include "coreinit_DynLoad.h"
// the system area is a block of memory that exists only in the emulator. It is used to simplify dynamic memory allocation for system data
// this partially overlaps with the system heap from coreinit_SysHeap.cpp -> Use SysHeap for everything
MPTR sysAreaAllocatorOffset = 0;
MPTR coreinit_allocFromSysArea(uint32 size, uint32 alignment)
{
cemu_assert_debug(mmuRange_CEMU_AREA.isMapped());
// deprecated, use OSAllocFromSystem instead (for everything but SysAllocator which probably should use its own allocator?)
static std::mutex s_allocator_mutex;
s_allocator_mutex.lock();
sysAreaAllocatorOffset = (sysAreaAllocatorOffset+alignment-1);
sysAreaAllocatorOffset -= (sysAreaAllocatorOffset%alignment);
uint32 newMemOffset = mmuRange_CEMU_AREA.getBase() + sysAreaAllocatorOffset;
sysAreaAllocatorOffset += (size+3)&~3;
if( sysAreaAllocatorOffset >= mmuRange_CEMU_AREA.getSize() )
{
forceLog_printf("Ran out of system memory");
cemu_assert(false); // out of bounds
}
s_allocator_mutex.unlock();
return newMemOffset;
}
void coreinit_freeToSysArea(MPTR mem)
{
// todo
}
namespace coreinit
{
#define MEM_MAX_HEAP_TABLE (0x20)
sint32 g_heapTableCount = 0;
MEMHeapBase* g_heapTable[MEM_MAX_HEAP_TABLE] = {};
bool g_slockInitialized = false;
bool g_listsInitialized = false;
MEMList g_list1;
MEMList g_list2;
MEMList g_list3;
std::array<uint32, 3> gHeapFillValues{ 0xC3C3C3C3, 0xF3F3F3F3, 0xD3D3D3D3 };
OSSpinLock gHeapGlobalLock;
MEMHeapBase* gDefaultHeap;
bool MEMHeapTable_Add(MEMHeapBase* heap)
{
if (g_heapTableCount >= MEM_MAX_HEAP_TABLE)
return false;
g_heapTable[g_heapTableCount] = heap;
g_heapTableCount++;
return true;
}
bool MEMHeapTable_Remove(MEMHeapBase* heap)
{
if (g_heapTableCount == 0)
return false;
if (g_heapTableCount > MEM_MAX_HEAP_TABLE)
return false;
for (sint32 i = 0; i < g_heapTableCount; ++i)
{
if (g_heapTable[i] == heap)
{
g_heapTable[i] = nullptr;
g_heapTableCount--;
if (g_heapTableCount == 0)
return true;
if (i >= g_heapTableCount)
return true;
cemu_assert_debug(i < MEM_MAX_HEAP_TABLE);
for (; i < g_heapTableCount; ++i)
{
g_heapTable[i] = g_heapTable[i + 1];
}
cemu_assert_debug(i < MEM_MAX_HEAP_TABLE);
g_heapTable[i] = nullptr;
return true;
}
}
return false;
}
MEMHeapBase* _MEMList_FindContainingHeap(MEMList* list, MEMHeapBase* heap)
{
for (MEMHeapBase* obj = (MEMHeapBase*)MEMGetFirstListObject(list); obj; obj = (MEMHeapBase*)MEMGetNextListObject(list, obj))
{
if (obj->heapStart.GetPtr() <= heap && heap < obj->heapEnd.GetPtr())
{
const MEMHeapHandle containHeap = _MEMList_FindContainingHeap(&obj->childList, heap);
return containHeap ? containHeap : obj;
}
}
return nullptr;
}
bool MEMList_ContainsHeap(MEMList* list, MEMHeapBase* heap)
{
for (MEMHeapBase* obj = (MEMHeapBase*)MEMGetFirstListObject(list); obj; obj = (MEMHeapBase*)MEMGetNextListObject(list, obj))
{
if (obj == heap)
return true;
}
return false;
}
MEMList* MEMList_FindContainingHeap(MEMHeapBase* head)
{
MEMPTR<void> memBound;
uint32be memBoundSize;
OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize);
MEMPTR<void> bucket;
uint32be bucketSize;
coreinit::OSGetForegroundBucket(&bucket, &bucketSize);
if ((uintptr_t)memBound.GetPtr() > (uintptr_t)head || (uintptr_t)head >= (uintptr_t)memBound.GetPtr() + (uint32)memBoundSize)
{
if ((uintptr_t)bucket.GetPtr() > (uintptr_t)head || (uintptr_t)head >= (uintptr_t)bucket.GetPtr() + (uint32)bucketSize)
{
MEMHeapBase* containHeap = _MEMList_FindContainingHeap(&g_list1, head);
if (containHeap)
return &containHeap->childList;
return &g_list1;
}
MEMHeapBase* containHeap = _MEMList_FindContainingHeap(&g_list3, head);
if (containHeap)
return &containHeap->childList;
return &g_list3;
}
MEMHeapBase* containHeap = _MEMList_FindContainingHeap(&g_list2, head);
if (containHeap)
return &containHeap->childList;
return &g_list2;
}
void MEMInitHeapBase(MEMHeapBase* heap, MEMHeapMagic magic, void* heapStart, void* heapEnd, uint32 createFlags)
{
memset(heap, 0, sizeof(MEMHeapBase));
heap->magic = magic;
heap->heapStart = heapStart;
heap->heapEnd = heapEnd;
heap->flags = (uint8)createFlags;
MEMInitList(&heap->childList, 4);
if (!g_slockInitialized)
{
OSInitSpinLock(&gHeapGlobalLock);
g_slockInitialized = true;
}
if (!g_listsInitialized)
{
MEMInitList(&g_list1, offsetof(MEMHeapBase, link));
MEMInitList(&g_list2, offsetof(MEMHeapBase, link));
MEMInitList(&g_list3, offsetof(MEMHeapBase, link));
g_listsInitialized = true;
}
OSInitSpinLock(&heap->spinlock);
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
MEMList* list = MEMList_FindContainingHeap(heap);
MEMAppendListObject(list, heap);
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
}
void MEMBaseDestroyHeap(MEMHeapBase* heap)
{
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
if (HAS_FLAG(heap->flags, MEM_HEAP_OPTION_THREADSAFE))
OSUninterruptibleSpinLock_Acquire(&heap->spinlock);
MEMList* containHeap = MEMList_FindContainingHeap(heap);
cemu_assert_debug(MEMList_ContainsHeap(containHeap, heap));
MEMRemoveListObject(containHeap, heap);
if (HAS_FLAG(heap->flags, MEM_HEAP_OPTION_THREADSAFE))
OSUninterruptibleSpinLock_Release(&heap->spinlock);
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
}
std::array<MEMHeapBase*, 9> sHeapBaseHandle{};
MEMHeapBase* MEMGetBaseHeapHandle(uint32 index)
{
if (index >= sHeapBaseHandle.size())
return nullptr;
return sHeapBaseHandle[index];
}
MEMHeapBase* MEMSetBaseHeapHandle(uint32 index, MEMHeapBase* heapBase)
{
if (index >= sHeapBaseHandle.size())
return nullptr;
if (sHeapBaseHandle[index] != nullptr)
{
cemuLog_log(LogType::Force, "MEMSetBaseHeapHandle(): Trying to assign heap to non-empty slot");
return sHeapBaseHandle[index];
}
sHeapBaseHandle[index] = heapBase;
return nullptr;
}
uint32 MEMSetFillValForHeap(HEAP_FILL_TYPE type, uint32 value)
{
uint32 idx = (uint32)type;
cemu_assert(idx < gHeapFillValues.size());
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
const uint32 oldValue = gHeapFillValues[idx];
gHeapFillValues[idx] = value;
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
return oldValue;
}
uint32 MEMGetFillValForHeap(HEAP_FILL_TYPE type)
{
uint32 idx = (uint32)type;
cemu_assert(idx < gHeapFillValues.size());
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
const uint32 value = gHeapFillValues[idx];
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
return value;
}
MEMHeapBase* MEMFindContainHeap(const void* memBlock)
{
MEMPTR<void> memBound;
uint32be memBoundSize;
OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize);
MEMPTR<void> bucket;
uint32be bucketSize;
coreinit::OSGetForegroundBucket(&bucket, &bucketSize);
OSUninterruptibleSpinLock_Acquire(&gHeapGlobalLock);
MEMHeapBase* result;
if ((uintptr_t)memBound.GetPtr() > (uintptr_t)memBlock || (uintptr_t)memBlock >= (uintptr_t)memBound.GetPtr() + (uint32)memBoundSize)
{
if ((uintptr_t)bucket.GetPtr() > (uintptr_t)memBlock || (uintptr_t)memBlock >= (uintptr_t)bucket.GetPtr() + (uint32)bucketSize)
{
result = _MEMList_FindContainingHeap(&g_list1, (MEMHeapBase*)memBlock);
}
else
{
if (coreinit::OSGetForegroundBucket(nullptr, nullptr) == 0)
{
cemu_assert_unimplemented(); // foreground required
result = nullptr;
}
else
{
result = _MEMList_FindContainingHeap(&g_list3, (MEMHeapBase*)memBlock);
}
}
}
else
{
if (coreinit::OSGetForegroundBucket(nullptr, nullptr) == 0)
{
cemu_assert_unimplemented(); // foreground required
result = nullptr;
}
else
{
result = _MEMList_FindContainingHeap(&g_list2, (MEMHeapBase*)memBlock);
}
}
OSUninterruptibleSpinLock_Release(&gHeapGlobalLock);
return result;
}
void* MEMCreateUserHeapHandle(void* heapAddress, uint32 heapSize)
{
MEMInitHeapBase((MEMHeapBase*)heapAddress, MEMHeapMagic::USER_HEAP, (uint8*)heapAddress + 0x40, (uint8*)heapAddress + heapSize, 0);
return heapAddress;
}
/* MEM Heap list */
#define GET_MEM_LINK(__obj__,__offset__) ((MEMLink*)((uintptr_t)__obj__ + (uint16)__offset__))
void* MEMGetFirstListObject(MEMList* list)
{
return MEMGetNextListObject(list, nullptr);
}
void MEMList_SetSingleObject(MEMList* list, void* object)
{
cemu_assert_debug(list != nullptr);
cemu_assert_debug(object != nullptr);
MEMLink* link = GET_MEM_LINK(object, list->offset);
link->prev = nullptr;
link->next = nullptr;
list->numObjects = list->numObjects + 1;
list->head = object;
list->tail = object;
}
void MEMInitList(MEMList* list, uint32 offset)
{
cemu_assert_debug(list != nullptr);
cemu_assert(offset <= 0xFFFF);
memset(list, 0x00, sizeof(MEMLink));
list->offset = (uint16)offset;
}
void MEMAppendListObject(MEMList* list, void* object)
{
cemu_assert_debug(list != nullptr);
cemu_assert_debug(object != nullptr);
if (list->head)
{
list->numObjects = list->numObjects + 1;
MEMLink* link = GET_MEM_LINK(object, list->offset);
link->prev = list->tail;
link->next = nullptr;
MEMLink* tailLink = GET_MEM_LINK(list->tail.GetPtr(), list->offset);
tailLink->next = object;
list->tail = object;
}
else
MEMList_SetSingleObject(list, object);
}
void MEMPrependListObject(MEMList* list, void* object)
{
cemu_assert_debug(list != nullptr);
cemu_assert_debug(object != nullptr);
MEMPTR<void> headObject = list->head;
if (headObject)
{
list->numObjects = list->numObjects + 1;
MEMLink* link = GET_MEM_LINK(object, list->offset);
link->prev = nullptr;
link->next = headObject;
MEMLink* headLink = GET_MEM_LINK(headObject.GetPtr(), list->offset);
headLink->prev = object;
list->head = object;
}
else
MEMList_SetSingleObject(list, object);
}
void MEMRemoveListObject(MEMList* list, void* object)
{
cemu_assert_debug(list != nullptr);
cemu_assert_debug(object != nullptr);
MEMLink* link = GET_MEM_LINK(object, list->offset);
MEMPTR<void> prevObject = link->prev;
if (prevObject)
{
MEMLink* prevLink = GET_MEM_LINK(prevObject.GetPtr(), list->offset);
prevLink->next = link->next;
}
else
list->head = link->next;
MEMPTR<void> nextObject = link->next;
if (nextObject)
{
MEMLink* nextLink = GET_MEM_LINK(nextObject.GetPtr(), list->offset);
nextLink->prev = prevObject;
}
else
list->tail = prevObject;
link->prev = nullptr;
link->next = nullptr;
list->numObjects = list->numObjects - 1;
}
void* MEMGetNextListObject(MEMList* list, void* object)
{
cemu_assert_debug(list != nullptr);
if (!object)
return list->head.GetPtr();
MEMLink* result = GET_MEM_LINK(object, list->offset);
return result->next.GetPtr();
}
void* MEMGetPrevListObject(MEMList* list, void* object)
{
cemu_assert_debug(list != nullptr);
if (!object)
return list->tail.GetPtr();
MEMLink* result = GET_MEM_LINK(object, list->offset);
return result->prev.GetPtr();
}
void* MEMGetNthListObject(MEMList* list, uint32 index)
{
cemu_assert_debug(list != nullptr);
void* result = MEMGetNextListObject(list, nullptr);
for (uint32 i = 0; i != index; ++i)
{
result = MEMGetNextListObject(list, result);
}
return result;
}
/* Default allocators */
MEMHeapBase* MEMDefaultHeap_Init(void* mem, uint32 size)
{
MEMHeapBase* heap = MEMCreateExpHeapEx(mem, size, 4);
gDefaultHeap = heap;
cemu_assert_debug(heap);
return heap;
}
void* default_MEMAllocFromDefaultHeap(uint32 size)
{
void* mem = MEMAllocFromExpHeapEx(gDefaultHeap, size, 0x40);
coreinitMemLog_printf("MEMAllocFromDefaultHeap(0x%08x) Result: 0x%08x", size, memory_getVirtualOffsetFromPointer(mem));
return mem;
}
void export_default_MEMAllocFromDefaultHeap(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(size, 0);
MEMPTR<void> mem = default_MEMAllocFromDefaultHeap(size);
osLib_returnFromFunction(hCPU, mem.GetMPTR());
}
void* default_MEMAllocFromDefaultHeapEx(uint32 size, sint32 alignment)
{
void* mem = MEMAllocFromExpHeapEx(gDefaultHeap, size, alignment);
coreinitMemLog_printf("MEMAllocFromDefaultHeap(0x%08x,%d) Result: 0x%08x", size, alignment, memory_getVirtualOffsetFromPointer(mem));
return mem;
}
void export_default_MEMAllocFromDefaultHeapEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(size, 0);
ppcDefineParamS32(alignment, 1);
MEMPTR<void> mem = default_MEMAllocFromDefaultHeapEx(size, alignment);
osLib_returnFromFunction(hCPU, mem.GetMPTR());
}
void default_MEMFreeToDefaultHeap(void* mem)
{
MEMFreeToExpHeap(gDefaultHeap, mem);
}
void export_default_MEMFreeToDefaultHeap(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(mem, void, 0);
default_MEMFreeToDefaultHeap(mem.GetPtr());
osLib_returnFromFunction(hCPU, 0);
}
void default_DynLoadAlloc(PPCInterpreter_t* hCPU)
{
ppcDefineParamS32(size, 0);
ppcDefineParamS32(alignment, 1);
ppcDefineParamMEMPTR(memResultPtr, uint32be, 2);
MPTR mem = PPCCoreCallback(gCoreinitData->MEMAllocFromDefaultHeapEx.GetMPTR(), size, alignment);
*memResultPtr = mem;
osLib_returnFromFunction(hCPU, 0);
}
void default_DynLoadFree(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(mem, void, 0);
PPCCoreCallback(gCoreinitData->MEMFreeToDefaultHeap.GetMPTR(), mem);
osLib_returnFromFunction(hCPU, 0);
}
void* _weak_MEMAllocFromDefaultHeapEx(uint32 size, sint32 alignment)
{
MEMPTR<void> r{ PPCCoreCallback(gCoreinitData->MEMAllocFromDefaultHeapEx.GetMPTR(), size, alignment) };
return r.GetPtr();
}
void* _weak_MEMAllocFromDefaultHeap(uint32 size)
{
MEMPTR<void> r{ PPCCoreCallback(gCoreinitData->MEMAllocFromDefaultHeap.GetMPTR(), size) };
return r.GetPtr();
}
void _weak_MEMFreeToDefaultHeap(void* ptr)
{
PPCCoreCallback(gCoreinitData->MEMFreeToDefaultHeap.GetMPTR(), ptr);
}
void coreinitExport_MEMAllocFromAllocator(PPCInterpreter_t* hCPU)
{
debug_printf("MEMAllocFromAllocator(0x%x, 0x%x)\n", hCPU->gpr[3], hCPU->gpr[4]);
MEMAllocator* memAllocator = (MEMAllocator*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
// redirect execution to allocator alloc callback
hCPU->instructionPointer = memAllocator->func->funcAlloc.GetMPTR();
}
void coreinitExport_MEMFreeToAllocator(PPCInterpreter_t* hCPU)
{
debug_printf("MEMFreeToAllocator(0x%x, 0x%08x)\n", hCPU->gpr[3], hCPU->gpr[4]);
MEMAllocator* memAllocator = (MEMAllocator*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
// redirect execution to allocator free callback
hCPU->instructionPointer = memAllocator->func->funcFree.GetMPTR();
}
void _DefaultHeapAllocator_Alloc(PPCInterpreter_t* hCPU)
{
hCPU->gpr[3] = hCPU->gpr[4];
hCPU->instructionPointer = gCoreinitData->MEMAllocFromDefaultHeap.GetMPTR();
}
void _DefaultHeapAllocator_Free(PPCInterpreter_t* hCPU)
{
hCPU->gpr[3] = hCPU->gpr[4];
hCPU->instructionPointer = gCoreinitData->MEMFreeToDefaultHeap.GetMPTR();
}
SysAllocator<MEMAllocatorFunc> gDefaultHeapAllocator;
void coreinitExport_MEMInitAllocatorForDefaultHeap(PPCInterpreter_t* hCPU)
{
debug_printf("MEMInitAllocatorForDefaultHeap(0x%08x)", hCPU->gpr[3]);
ppcDefineParamStructPtr(memAllocator, MEMAllocator, 0);
gDefaultHeapAllocator->funcAlloc = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(_DefaultHeapAllocator_Alloc));
gDefaultHeapAllocator->funcFree = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(_DefaultHeapAllocator_Free));
memAllocator->func = gDefaultHeapAllocator.GetPtr();
memAllocator->heap = MEMPTR<void>(MEMGetBaseHeapHandle(1));
memAllocator->param1 = 0;
memAllocator->param2 = 0;
osLib_returnFromFunction(hCPU, 0);
}
void InitDefaultHeaps(MEMPTR<MEMHeapBase>& mem1Heap, MEMPTR<MEMHeapBase>& memFGHeap, MEMPTR<MEMHeapBase>& mem2Heap)
{
mem1Heap = nullptr;
memFGHeap = nullptr;
mem2Heap = nullptr;
gCoreinitData->MEMAllocFromDefaultHeap = RPLLoader_MakePPCCallable(export_default_MEMAllocFromDefaultHeap);
gCoreinitData->MEMAllocFromDefaultHeapEx = RPLLoader_MakePPCCallable(export_default_MEMAllocFromDefaultHeapEx);
gCoreinitData->MEMFreeToDefaultHeap = RPLLoader_MakePPCCallable(export_default_MEMFreeToDefaultHeap);
if (OSGetForegroundBucket(nullptr, nullptr))
{
MEMPTR<void> memBound;
uint32be memBoundSize;
OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize);
mem1Heap = MEMCreateFrmHeapEx(memBound.GetPtr(), (uint32)memBoundSize, 0);
OSGetForegroundBucketFreeArea((MPTR*)memBound.GetBEPtr(), (MPTR*)&memBoundSize);
memFGHeap = MEMCreateFrmHeapEx(memBound.GetPtr(), (uint32)memBoundSize, 0);
}
MEMPTR<void> memBound;
uint32be memBoundSize;
OSGetMemBound(2, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize);
mem2Heap = MEMDefaultHeap_Init(memBound.GetPtr(), (uint32)memBoundSize);
// set DynLoad allocators
OSDynLoad_SetAllocator(RPLLoader_MakePPCCallable(default_DynLoadAlloc), RPLLoader_MakePPCCallable(default_DynLoadFree));
OSDynLoad_SetTLSAllocator(RPLLoader_MakePPCCallable(default_DynLoadAlloc), RPLLoader_MakePPCCallable(default_DynLoadFree));
}
void CoreInitDefaultHeap(/* ukn */)
{
cemu_assert_unimplemented();
}
void InitializeMEM()
{
for (auto& it : sHeapBaseHandle)
it = nullptr;
cafeExportRegister("coreinit", CoreInitDefaultHeap, LogType::CoreinitMem);
osLib_addFunction("coreinit", "MEMInitAllocatorForDefaultHeap", coreinitExport_MEMInitAllocatorForDefaultHeap);
osLib_addFunction("coreinit", "MEMAllocFromAllocator", coreinitExport_MEMAllocFromAllocator);
osLib_addFunction("coreinit", "MEMFreeToAllocator", coreinitExport_MEMFreeToAllocator);
cafeExportRegister("coreinit", MEMGetBaseHeapHandle, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMSetBaseHeapHandle, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMFindContainHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMGetFillValForHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMSetFillValForHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMCreateUserHeapHandle, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMInitList, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMPrependListObject, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMAppendListObject, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMRemoveListObject, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMGetNextListObject, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMGetNthListObject, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMGetPrevListObject, LogType::CoreinitMem);
}
}

View file

@ -0,0 +1,183 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
struct MEMLink_t
{
MPTR prevObject;
MPTR nextObject;
};
static_assert(sizeof(MEMLink_t) == 8);
struct MEMList_t
{
MPTR headObject;
MPTR tailObject;
uint16 numObjects;
uint16 offset;
};
static_assert(sizeof(MEMList_t) == 0xC);
struct MEMAllocatorFunc
{
MEMPTR<void> funcAlloc;
MEMPTR<void> funcFree;
};
static_assert(sizeof(MEMAllocatorFunc) == 8);
struct MEMAllocator
{
/* +0x000 */ MEMPTR<MEMAllocatorFunc> func;
/* +0x004 */ MEMPTR<void> heap;
/* +0x00C */ uint32be param1;
/* +0x010 */ uint32be param2;
};
static_assert(sizeof(MEMAllocator) == 0x10);
MPTR coreinit_allocFromSysArea(uint32 size, uint32 alignment);
void coreinit_freeToSysArea(MPTR mem);
// mem exports
void coreinitExport_MEMInitAllocatorForDefaultHeap(PPCInterpreter_t* hCPU);
void coreinitExport_MEMGetBaseHeapHandle(PPCInterpreter_t* hCPU);
/* legacy stuff above */
namespace coreinit
{
#define MEM_HEAP_INVALID_HANDLE (nullptr)
#define MEM_HEAP_DEFAULT_ALIGNMENT 4
#define MIN_ALIGNMENT 4
#define MIN_ALIGNMENT_MINUS_ONE (MIN_ALIGNMENT-1)
#define MEM_HEAP_OPTION_NONE (0)
#define MEM_HEAP_OPTION_CLEAR (1 << 0)
#define MEM_HEAP_OPTION_FILL (1 << 1)
#define MEM_HEAP_OPTION_THREADSAFE (1 << 2)
enum class MEMHeapMagic : uint32
{
UNIT_HEAP = 'UNTH',
BLOCK_HEAP = 'BLKH',
FRAME_HEAP = 'FRMH',
EXP_HEAP = 'EXPH',
USER_HEAP = 'USRH',
};
struct MEMLink
{
MEMPTR<void> prev;
MEMPTR<void> next;
};
static_assert(sizeof(MEMLink) == 0x8);
struct MEMList
{
/* 0x00 */ MEMPTR<void> head;
/* 0x04 */ MEMPTR<void> tail;
/* 0x08 */ uint16be numObjects;
/* 0x0A */ uint16be offset;
};
static_assert(sizeof(MEMList) == 0xC);
void MEMInitList(MEMList* list, uint32 offset);
void MEMAppendListObject(MEMList* list, void* object);
void MEMRemoveListObject(MEMList* list, void* object);
void* MEMGetFirstListObject(MEMList* list);
void* MEMGetNextListObject(MEMList* list, void* object);
struct MEMHeapBase
{
/* +0x00 */ betype<MEMHeapMagic> magic;
/* +0x04 */ MEMLink link;
/* +0x0C */ MEMList childList;
/* +0x18 */ MEMPTR<void> heapStart;
/* +0x1C */ MEMPTR<void> heapEnd; // heap end + 1
/* +0x20 */ OSSpinLock spinlock;
/* +0x30 */ uint8 _ukn[3];
/* +0x33 */ uint8 flags;
void AcquireLock()
{
if (flags & MEM_HEAP_OPTION_THREADSAFE)
OSUninterruptibleSpinLock_Acquire(&spinlock);
}
void ReleaseLock()
{
if (flags & MEM_HEAP_OPTION_THREADSAFE)
OSUninterruptibleSpinLock_Release(&spinlock);
}
// if set, memset allocations to zero
bool HasOptionClear() const
{
return (flags & MEM_HEAP_OPTION_CLEAR) != 0;
}
// if set, memset allocations/releases to specific fill values
bool HasOptionFill() const
{
return (flags & MEM_HEAP_OPTION_FILL) != 0;
}
};
static_assert(offsetof(MEMHeapBase, childList) == 0xC);
static_assert(offsetof(MEMHeapBase, spinlock) == 0x20);
static_assert(offsetof(MEMHeapBase, flags) == 0x33);
static_assert(sizeof(MEMHeapBase) == 0x34); // heap base is actually 0x40 but bytes 0x34 to 0x40 are padding?
typedef MEMHeapBase* MEMHeapHandle;
/* Heap base */
void MEMInitHeapBase(MEMHeapBase* heap, MEMHeapMagic magic, void* heapStart, void* heapEnd, uint32 createFlags);
void MEMBaseDestroyHeap(MEMHeapBase* heap);
MEMHeapBase* MEMGetBaseHeapHandle(uint32 index);
MEMHeapBase* MEMSetBaseHeapHandle(uint32 index, MEMHeapBase* heapBase);
/* Heap list */
bool MEMHeapTable_Add(MEMHeapBase* heap);
bool MEMHeapTable_Remove(MEMHeapBase* heap);
MEMHeapBase* _MEMList_FindContainingHeap(MEMList* list, MEMHeapBase* heap);
bool MEMList_ContainsHeap(MEMList* list, MEMHeapBase* heap);
MEMList* MEMList_FindContainingHeap(MEMHeapBase* head);
/* Heap settings */
enum class HEAP_FILL_TYPE : uint32
{
ON_HEAP_CREATE = 0,
ON_ALLOC = 1,
ON_FREE = 2,
};
uint32 MEMGetFillValForHeap(HEAP_FILL_TYPE type);
uint32 MEMSetFillValForHeap(HEAP_FILL_TYPE type, uint32 value);
MEMHeapHandle MEMFindContainHeap(const void* memBlock);
/* Heap default allocators */
void InitDefaultHeaps(MEMPTR<MEMHeapBase>& mem1Heap, MEMPTR<MEMHeapBase>& memFGHeap, MEMPTR<MEMHeapBase>& mem2Heap);
void* default_MEMAllocFromDefaultHeap(uint32 size);
void* default_MEMAllocFromDefaultHeapEx(uint32 size, sint32 alignment);
void default_MEMFreeToDefaultHeap(void* mem);
void* _weak_MEMAllocFromDefaultHeapEx(uint32 size, sint32 alignment);
void* _weak_MEMAllocFromDefaultHeap(uint32 size);
void _weak_MEMFreeToDefaultHeap(void* ptr);
/* Unit heap */
void InitializeMEMUnitHeap();
void InitializeMEM();
}

View file

@ -0,0 +1,596 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.h"
namespace coreinit
{
MEMHeapHandle MEMInitBlockHeap(MEMBlockHeap2_t* memStart, void* startAddr, void* endAddr, void* initTrackMem, uint32 initTrackMemSize, uint32 createFlags)
{
if (memStart == nullptr || startAddr == nullptr || endAddr == nullptr || (uintptr_t)startAddr >= (uintptr_t)endAddr)
return nullptr;
if (initTrackMemSize == 0)
initTrackMem = nullptr;
else if (initTrackMem == nullptr)
initTrackMemSize = 0;
MEMInitHeapBase(memStart, MEMHeapMagic::BLOCK_HEAP, startAddr, endAddr, createFlags);
memStart->track.addrStart = startAddr;
memStart->track.addrEnd = (void*)((uintptr_t)endAddr - 1);
memStart->track.isFree = 1;
memStart->track.previousBlock = nullptr;
memStart->track.nextBlock = nullptr;
memStart->headBlock = &memStart->track;
memStart->tailBlock = &memStart->track;
memStart->nextFreeBlock = nullptr;
memStart->freeBlocksLeft = 0;
uint8 flags = memStart->flags;
if(HAS_FLAG(flags, MEM_HEAP_OPTION_FILL))
{
cemu_assert_unimplemented();
}
if(initTrackMemSize != 0)
{
sint32 result = MEMAddBlockHeapTracking(MEMPTR<void>(memStart).GetMPTR(), MEMPTR<void>(initTrackMem).GetMPTR(), initTrackMemSize);
if(result != 0)
{
MEMBaseDestroyHeap(memStart);
return nullptr;
}
}
MEMHeapTable_Add(memStart);
return memStart;
}
void* MEMDestroyBlockHeap(MEMHeapHandle hHeap)
{
if (!hHeap)
return nullptr;
cemu_assert_debug(hHeap->magic == MEMHeapMagic::BLOCK_HEAP);
MEMBaseDestroyHeap(hHeap);
MEMHeapTable_Remove(hHeap);
memset(hHeap, 0x00, sizeof(MEMBlockHeap2_t));
return hHeap;
}
sint32 MEMGetAllocatableSizeForBlockHeapEx(MEMBlockHeap2_t* blockHeap, sint32 alignment)
{
if (!blockHeap || blockHeap->magic != MEMHeapMagic::BLOCK_HEAP)
{
cemuLog_log(LogType::Force, "MEMGetAllocatableSizeForBlockHeapEx(): Not a valid block heap");
return 0;
}
if (alignment < 0)
alignment = -alignment;
else if (alignment == 0)
alignment = 4;
if (HAS_FLAG(blockHeap->flags, MEM_HEAP_OPTION_THREADSAFE))
OSUninterruptibleSpinLock_Acquire(&blockHeap->spinlock);
MEMBlockHeapTrack2_t* track = (MEMBlockHeapTrack2_t*)blockHeap->headBlock.GetPtr();
uint32 allocatableSize = 0;
while (track)
{
if (track->isFree != 0)
{
uint32 addrStart = track->addrStart.GetMPTR();
uint32 addrEnd = track->addrEnd.GetMPTR();
uint32 alignmentMinusOne = alignment - 1;
uint32 alignedAddrStart = ((addrStart + alignmentMinusOne) / alignment) * alignment;
if (alignedAddrStart <= addrEnd)
{
uint32 size = (addrEnd + 1) - alignedAddrStart;
allocatableSize = std::max(allocatableSize, size);
}
}
// next
track = (MEMBlockHeapTrack2_t*)track->nextBlock.GetPtr();
}
if (HAS_FLAG(blockHeap->flags, MEM_HEAP_OPTION_THREADSAFE))
OSUninterruptibleSpinLock_Release(&blockHeap->spinlock);
return allocatableSize;
}
void MEMDumpBlockHeap(MPTR heap);
typedef struct
{
/* +0x00 */ uint32 addrStart;
/* +0x04 */ uint32 addrEnd;
/* +0x08 */ uint32 isFree; // if 0 -> block is used
/* +0x0C */ MPTR previousBlock;
/* +0x10 */ MPTR nextBlock;
}MEMBlockHeapTrackDEPR;
typedef struct
{
/* +0x00 */ uint32 ukn00;
/* +0x04 */ uint32 ukn04;
/* +0x08 */ uint32 trackArray;
/* +0x0C */ uint32 trackCount;
}MEMBlockHeapGroupDEPR;
typedef struct
{
/* +0x000 */ betype<MEMHeapMagic> magic;
/* +0x004 */ MEMLink_t link;
/* +0x00C */ MEMList_t childList;
/* +0x018 */ MPTR heapStart;
/* +0x01C */ MPTR heapEnd;
/* +0x020 */ coreinit::OSSpinLock spinlock;
/* +0x030 */
union
{
uint32 val;
uint32 flags;
}
attribute;
// start of block heap header
/* +0x034 */ uint8 ukn034[0x50 - 0x34]; // ?
/* +0x050 */ MEMBlockHeapTrackDEPR nullBlockTrack;
/* +0x064 */ MPTR headBlock;
/* +0x068 */ MPTR tailBlock;
/* +0x06C */ MPTR nextFreeBlock;
/* +0x070 */ uint32 freeBlocksLeft;
}MEMBlockHeapDEPR;
void _blockHeapDebugVerifyIfBlockIsLinked(MEMBlockHeapDEPR* blockHeapHead, MEMBlockHeapTrackDEPR* track)
{
//MEMBlockHeapTrack_t* trackItr = (MEMBlockHeapTrack_t*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->firstBlock));
//MEMBlockHeapTrack_t* prevHistory[4];
//while( trackItr )
//{
// if( trackItr == track )
// {
// debug_printf("PrevBlock3 %08x\n", memory_getVirtualOffsetFromPointer(prevHistory[0]));
// debug_printf("PrevBlock2 %08x\n", memory_getVirtualOffsetFromPointer(prevHistory[1]));
// debug_printf("PrevBlock1 %08x\n", memory_getVirtualOffsetFromPointer(prevHistory[2]));
// debug_printf("PrevBlock0 %08x\n", memory_getVirtualOffsetFromPointer(prevHistory[3]));
// assert_dbg();
// }
// prevHistory[3] = prevHistory[2];
// prevHistory[2] = prevHistory[1];
// prevHistory[1] = prevHistory[0];
// prevHistory[0] = trackItr;
// // next
// trackItr = (MEMBlockHeapTrack_t*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(trackItr->nextBlock));
//}
}
void _blockHeapDebugVerifyLinkOrder(MEMBlockHeapDEPR* blockHeapHead)
{
//MEMBlockHeapTrack_t* trackItr = (MEMBlockHeapTrack_t*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->firstBlock));
//MEMBlockHeapTrack_t* prev = NULL;
//while( trackItr )
//{
// MPTR prevMPTR = memory_getVirtualOffsetFromPointer(prev);
// if( _swapEndianU32(trackItr->previousBlock) != prevMPTR )
// assert_dbg();
// // next
// prev = trackItr;
// trackItr = (MEMBlockHeapTrack_t*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(trackItr->nextBlock));
//}
}
sint32 MEMAddBlockHeapTracking(MPTR heap, MPTR trackMem, uint32 trackMemSize)
{
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
{
return 0;
}
if (trackMem == MPTR_NULL || trackMemSize <= (sizeof(MEMBlockHeapGroupDEPR) + sizeof(MEMBlockHeapTrackDEPR)))
{
return -4;
}
uint32 trackCount = (trackMemSize - sizeof(MEMBlockHeapGroupDEPR)) / sizeof(MEMBlockHeapTrackDEPR);
MEMBlockHeapGroupDEPR* group = (MEMBlockHeapGroupDEPR*)memory_getPointerFromVirtualOffset(trackMem);
group->ukn00 = _swapEndianU32(0);
group->ukn04 = _swapEndianU32(0);
group->trackArray = _swapEndianU32(trackMem + sizeof(MEMBlockHeapGroupDEPR));
group->trackCount = _swapEndianU32(trackCount);
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)(group + 1);
track[0].previousBlock = _swapEndianU32(0);
if (trackCount > 1)
{
for (uint32 i = 0; i < trackCount - 1; i++)
{
track[i].nextBlock = _swapEndianU32(memory_getVirtualOffsetFromPointer(track + i + 1));
track[i + 1].previousBlock = _swapEndianU32(0);
}
}
// todo: Use spinlock from heap (and only use it if threadsafe heap flag is set)
__OSLockScheduler();
track[trackCount - 1].nextBlock = blockHeapHead->nextFreeBlock;
blockHeapHead->nextFreeBlock = _swapEndianU32(memory_getVirtualOffsetFromPointer(track));
blockHeapHead->freeBlocksLeft = _swapEndianU32(_swapEndianU32(blockHeapHead->freeBlocksLeft) + trackCount);
__OSUnlockScheduler();
return 0;
}
uint32 MEMGetTrackingLeftInBlockHeap(MPTR heap)
{
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
{
return 0;
}
return _swapEndianU32(blockHeapHead->freeBlocksLeft);
}
MPTR _MEMBlockHeap_GetFreeBlockTrack(MEMBlockHeapDEPR* blockHeapHead)
{
MPTR trackMPTR = _swapEndianU32(blockHeapHead->nextFreeBlock);
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(trackMPTR);
MPTR nextFreeBlockMPTR = _swapEndianU32(track->nextBlock); // for unreserved tracks, nextBlock holds the next unreserved block
track->nextBlock = _swapEndianU32(MPTR_NULL);
blockHeapHead->nextFreeBlock = _swapEndianU32(nextFreeBlockMPTR);
if (_swapEndianU32(blockHeapHead->freeBlocksLeft) == 0)
{
forceLog_printf("BlockHeap: No free blocks left\n");
assert_dbg();
}
blockHeapHead->freeBlocksLeft = _swapEndianU32(_swapEndianU32(blockHeapHead->freeBlocksLeft) - 1);
return trackMPTR;
}
/*
* Release MEMBlockHeapTrack_t struct for block heap
*/
void _MEMBlockHeap_ReleaseBlockTrack(MEMBlockHeapDEPR* blockHeapHead, MPTR trackMPTR)
{
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(trackMPTR);
track->nextBlock = blockHeapHead->nextFreeBlock;
blockHeapHead->nextFreeBlock = _swapEndianU32(trackMPTR);
blockHeapHead->freeBlocksLeft = _swapEndianU32(_swapEndianU32(blockHeapHead->freeBlocksLeft) + 1);
}
sint32 _MEMBlockHeap_AllocAtBlock(MEMBlockHeapDEPR* blockHeapHead, MEMBlockHeapTrackDEPR* track, MPTR allocationAddress, uint32 size)
{
MPTR trackMPTR = memory_getVirtualOffsetFromPointer(track);
uint32 trackEndAddr = _swapEndianU32(track->addrEnd);
uint32 prefixBlockSize = allocationAddress - _swapEndianU32(track->addrStart);
uint32 suffixBlockSize = (_swapEndianU32(track->addrEnd) + 1) - (allocationAddress + size);
if (prefixBlockSize > 0 && suffixBlockSize > 0)
{
// requires two free blocks
if (_swapEndianU32(blockHeapHead->freeBlocksLeft) < 2)
return -1;
}
else if (prefixBlockSize > 0 || suffixBlockSize > 0)
{
// requires one free block
if (_swapEndianU32(blockHeapHead->freeBlocksLeft) < 1)
return -2;
}
MPTR currentPreviousTrack = _swapEndianU32(track->previousBlock);
// remove used block from chain of free blocks (debug)
if (track->isFree != _swapEndianU32(0))
{
// check if block is in list of free blocks (it shouldnt be)
MEMBlockHeapTrackDEPR* trackItr = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->nextFreeBlock));
while (trackItr)
{
if (trackItr == track)
assert_dbg();
// next
trackItr = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(trackItr->nextBlock));
}
}
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
// create prefix block
if (prefixBlockSize > 0)
{
uint32 blockRangeStart = _swapEndianU32(track->addrStart);
uint32 blockRangeEnd = allocationAddress - 1;
MPTR prefixTrackMPTR = _MEMBlockHeap_GetFreeBlockTrack(blockHeapHead);
MEMBlockHeapTrackDEPR* prefixTrack = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(prefixTrackMPTR);
prefixTrack->isFree = _swapEndianU32(1);
prefixTrack->addrStart = _swapEndianU32(blockRangeStart);
prefixTrack->addrEnd = _swapEndianU32(blockRangeEnd);
// register new firstBlock
if (blockHeapHead->headBlock == _swapEndianU32(trackMPTR))
blockHeapHead->headBlock = _swapEndianU32(prefixTrackMPTR);
// update link in original previous block
if (_swapEndianU32(track->previousBlock) != MPTR_NULL)
{
MEMBlockHeapTrackDEPR* tempTrack = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(_swapEndianU32(track->previousBlock));
tempTrack->nextBlock = _swapEndianU32(prefixTrackMPTR);
}
// update previous/next track link
prefixTrack->nextBlock = _swapEndianU32(trackMPTR);
prefixTrack->previousBlock = _swapEndianU32(currentPreviousTrack);
// set prefix track as current previous track
currentPreviousTrack = prefixTrackMPTR;
}
// update used block
track->isFree = _swapEndianU32(0);
track->addrStart = _swapEndianU32(allocationAddress);
track->addrEnd = _swapEndianU32(allocationAddress + size - 1);
track->previousBlock = _swapEndianU32(currentPreviousTrack);
currentPreviousTrack = trackMPTR;
// create suffix block
if (suffixBlockSize > 0)
{
uint32 blockRangeStart = allocationAddress + size;
uint32 blockRangeEnd = trackEndAddr;
// get suffix track
MPTR suffixTrackMPTR = _MEMBlockHeap_GetFreeBlockTrack(blockHeapHead);
MEMBlockHeapTrackDEPR* suffixTrack = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(suffixTrackMPTR);
suffixTrack->isFree = _swapEndianU32(1);
suffixTrack->addrStart = _swapEndianU32(blockRangeStart);
suffixTrack->addrEnd = _swapEndianU32(blockRangeEnd);
// update previous/next track link
suffixTrack->previousBlock = _swapEndianU32(currentPreviousTrack);
suffixTrack->nextBlock = track->nextBlock;
// update last block of heap
if (_swapEndianU32(blockHeapHead->tailBlock) == trackMPTR)
blockHeapHead->tailBlock = _swapEndianU32(suffixTrackMPTR);
// update link in original next block
if (_swapEndianU32(track->nextBlock) != MPTR_NULL)
{
MEMBlockHeapTrackDEPR* tempTrack = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(_swapEndianU32(track->nextBlock));
tempTrack->previousBlock = _swapEndianU32(suffixTrackMPTR);
}
// update next block
track->nextBlock = _swapEndianU32(suffixTrackMPTR);
}
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
// todo: Get fill value via MEMGetFillValForHeap
memset(memory_getPointerFromVirtualOffset(allocationAddress), 0x00, size);
return 0;
}
MEMBlockHeapTrackDEPR* _MEMBlockHeap_FindBlockContaining(MEMBlockHeapDEPR* blockHeapHead, MPTR memAddr)
{
MPTR heapStart = _swapEndianU32(blockHeapHead->heapStart);
MPTR heapEnd = _swapEndianU32(blockHeapHead->heapEnd);
if (memAddr < heapStart)
return NULL;
if (memAddr > heapEnd)
return NULL;
uint32 distanceToStart = memAddr - heapStart;
uint32 distanceToEnd = heapEnd - memAddr;
// todo: If distanceToStart < distanceToEnd -> Iterate starting from firstBlock, else iterate starting from lastBlock (and continue via track->previousBlock)
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->headBlock));
if (track == NULL)
return NULL;
while (track)
{
if (_swapEndianU32(track->addrStart) == memAddr)
return track;
// next
// todo: Should this be ->previousBlock ?
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(track->nextBlock));
}
return NULL;
}
MPTR MEMAllocFromBlockHeapEx(MPTR heap, uint32 size, sint32 alignment)
{
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
{
return MPTR_NULL;
}
// find free block which can hold the data (including alignment)
__OSLockScheduler(); // todo: replace with spinlock from heap
if (alignment == 0)
alignment = 4;
bool allocateAtEndOfBlock = false;
if (alignment < 0)
{
allocateAtEndOfBlock = true;
alignment = -alignment;
}
MEMBlockHeapTrackDEPR* track;
if (allocateAtEndOfBlock)
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->tailBlock));
else
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->headBlock));
cemu_assert_debug(__popcnt(alignment) == 1); // not a supported alignment value
while (track)
{
if (track->isFree != _swapEndianU32(0))
{
uint32 blockRangeStart = _swapEndianU32(track->addrStart);
uint32 blockRangeEnd = _swapEndianU32(track->addrEnd) + 1;
if (allocateAtEndOfBlock == false)
{
// calculate remaining size with proper alignment
uint32 alignedBlockRangeStart = (blockRangeStart + alignment - 1) & ~(alignment - 1);
if (alignedBlockRangeStart < blockRangeEnd)
{
uint32 allocRange = blockRangeEnd - alignedBlockRangeStart;
if (allocRange >= size)
{
sint32 allocError = _MEMBlockHeap_AllocAtBlock(blockHeapHead, track, alignedBlockRangeStart, size);
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
__OSUnlockScheduler();
if (allocError == 0)
return alignedBlockRangeStart;
return MPTR_NULL;
}
}
}
else
{
uint32 alignedBlockRangeStart = (blockRangeEnd + 1 - size) & ~(alignment - 1);
if (alignedBlockRangeStart >= blockRangeStart)
{
sint32 allocError = _MEMBlockHeap_AllocAtBlock(blockHeapHead, track, alignedBlockRangeStart, size);
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
__OSUnlockScheduler();
if (allocError == 0)
return alignedBlockRangeStart;
return MPTR_NULL;
}
}
}
// next
if (allocateAtEndOfBlock)
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(track->previousBlock));
else
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(track->nextBlock));
if (track == nullptr)
break;
}
__OSUnlockScheduler();
return MPTR_NULL;
}
void MEMFreeToBlockHeap(MPTR heap, MPTR memAddr)
{
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
{
return;
}
__OSLockScheduler(); // todo: replace with spinlock from heap (if heap threadsafe flag is set)
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
MEMBlockHeapTrackDEPR* block = _MEMBlockHeap_FindBlockContaining(blockHeapHead, memAddr);
if (block != NULL)
{
MPTR blockMPTR = memory_getVirtualOffsetFromPointer(block);
#ifndef PUBLIC_RELEASE
if (block->isFree != 0)
assert_dbg();
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
#endif
// mark current block as free
block->isFree = _swapEndianU32(1);
// attempt to merge with previous block
if (_swapEndianU32(block->previousBlock) != NULL)
{
MPTR previousBlockMPTR = _swapEndianU32(block->previousBlock);
MEMBlockHeapTrackDEPR* previousBlock = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(previousBlockMPTR);
if (_swapEndianU32(previousBlock->isFree) != 0)
{
// merge
previousBlock->addrEnd = block->addrEnd;
previousBlock->nextBlock = block->nextBlock;
if (_swapEndianU32(block->nextBlock) != MPTR_NULL)
{
MEMBlockHeapTrackDEPR* tempNextBlock = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(_swapEndianU32(block->nextBlock));
tempNextBlock->previousBlock = _swapEndianU32(previousBlockMPTR);
}
// release removed block
_MEMBlockHeap_ReleaseBlockTrack(blockHeapHead, blockMPTR);
// debug
_blockHeapDebugVerifyIfBlockIsLinked(blockHeapHead, (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(blockMPTR));
// merged block becomes the new current block
blockMPTR = previousBlockMPTR;
block = previousBlock;
}
}
// attempt to merge with next block
if (_swapEndianU32(block->nextBlock) != NULL)
{
MPTR nextBlockMPTR = _swapEndianU32(block->nextBlock);
MEMBlockHeapTrackDEPR* nextBlock = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(nextBlockMPTR);
if (_swapEndianU32(nextBlock->isFree) != 0)
{
// merge
block->addrEnd = nextBlock->addrEnd;
block->nextBlock = nextBlock->nextBlock;
if (_swapEndianU32(nextBlock->nextBlock) != MPTR_NULL)
{
MEMBlockHeapTrackDEPR* tempNextBlock = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(_swapEndianU32(nextBlock->nextBlock));
tempNextBlock->previousBlock = _swapEndianU32(blockMPTR);
}
//// merged block becomes the new current block
//blockMPTR = previousBlockMPTR;
//block = previousBlock;
// update last block
if (_swapEndianU32(blockHeapHead->tailBlock) == nextBlockMPTR)
{
blockHeapHead->tailBlock = _swapEndianU32(blockMPTR);
}
// release removed block
_MEMBlockHeap_ReleaseBlockTrack(blockHeapHead, nextBlockMPTR);
// debug
_blockHeapDebugVerifyIfBlockIsLinked(blockHeapHead, (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(nextBlockMPTR));
}
}
}
_blockHeapDebugVerifyLinkOrder(blockHeapHead);
__OSUnlockScheduler();
}
uint32 MEMGetTotalFreeSizeForBlockHeap(MEMBlockHeapDEPR* blockHeap)
{
if (!blockHeap || blockHeap->magic != MEMHeapMagic::BLOCK_HEAP)
{
cemu_assert_suspicious();
return 0;
}
__OSLockScheduler(); // todo: replace with spinlock from heap (if heap threadsafe flag is set)
uint32 totalSize = 0;
// sum up all free blocks
MPTR blockMPTR = _swapEndianU32(blockHeap->headBlock);
while (blockMPTR)
{
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffset(blockMPTR);
if (track->isFree != _swapEndianU32(0))
{
// get block size
uint32 blockSize = _swapEndianU32(track->addrEnd) - _swapEndianU32(track->addrStart) + 1;
// add to totalSize
totalSize += blockSize;
}
// next
blockMPTR = _swapEndianU32(track->nextBlock);
}
__OSUnlockScheduler(); // todo: replace with spinlock from heap (if heap threadsafe flag is set)
return totalSize;
}
void MEMDumpBlockHeap(MPTR heap)
{
MEMBlockHeapDEPR* blockHeapHead = (MEMBlockHeapDEPR*)memory_getPointerFromVirtualOffset(heap);
if (blockHeapHead->magic != MEMHeapMagic::BLOCK_HEAP)
{
cemu_assert_suspicious();
return;
}
// iterate reserved/sized blocks
debug_printf("### MEMDumpBlockHeap ###\n");
__OSLockScheduler(); // todo: replace with spinlock from heap
MEMBlockHeapTrackDEPR* track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(blockHeapHead->headBlock));
while (track)
{
uint32 blockRangeStart = _swapEndianU32(track->addrStart);
uint32 blockRangeEnd = _swapEndianU32(track->addrEnd) + 1;
debug_printf(" %08x %08x - %08x prev/next %08x %08x isFree: %d\n", memory_getVirtualOffsetFromPointer(track), blockRangeStart, blockRangeEnd, _swapEndianU32(track->previousBlock), _swapEndianU32(track->nextBlock), _swapEndianU32(track->isFree));
// next
track = (MEMBlockHeapTrackDEPR*)memory_getPointerFromVirtualOffsetAllowNull(_swapEndianU32(track->nextBlock));
}
debug_printf("\n");
__OSUnlockScheduler();
}
void InitializeMEMBlockHeap()
{
cafeExportRegister("coreinit", MEMInitBlockHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMDestroyBlockHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMGetAllocatableSizeForBlockHeapEx, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMAddBlockHeapTracking, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMGetTrackingLeftInBlockHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMAllocFromBlockHeapEx, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMFreeToBlockHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMGetTotalFreeSizeForBlockHeap, LogType::CoreinitMem);
}
}

View file

@ -0,0 +1,34 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
namespace coreinit
{
struct MEMBlockHeapTrack2_t
{
/* +0x00 */ MEMPTR<void> addrStart;
/* +0x04 */ MEMPTR<void> addrEnd;
/* +0x08 */ uint32be isFree; // if 0 -> block is used
/* +0x0C */ MEMPTR<void> previousBlock;
/* +0x10 */ MEMPTR<void> nextBlock;
};
static_assert(sizeof(MEMBlockHeapTrack2_t) == 0x14);
struct MEMBlockHeap2_t : MEMHeapBase
{
/* +0x34 */ uint8 ukn[0x50 - 0x34];
/* +0x50 */ MEMBlockHeapTrack2_t track;
/* +0x64 */ MEMPTR<void> headBlock;
/* +0x68 */ MEMPTR<void> tailBlock;
/* +0x6C */ MEMPTR<void> nextFreeBlock;
/* +0x70 */ uint32be freeBlocksLeft;
/* +0x74 */ uint8 padding[0x80 - 0x74];
};
static_assert(sizeof(MEMBlockHeap2_t) == 0x80);
MEMHeapHandle MEMInitBlockHeap(MEMBlockHeap2_t* memStart, void* startAddr, void* endAddr, void* initTrackMem, uint32 initTrackMemSize, uint32 createFlags);
void* MEMDestroyBlockHeap(MEMHeapHandle hHeap);
sint32 MEMAddBlockHeapTracking(MPTR heap, MPTR trackMem, uint32 trackMemSize);
void InitializeMEMBlockHeap();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
namespace coreinit
{
void expheap_load();
#define MEM_EXPHEAP_ALLOC_MODE_FIRST (0)
#define MEM_EXPHEAP_ALLOC_MODE_NEAR (1)
#define MEM_EXPHEAP_USE_ALIGN_MARGIN (2)
enum class MEMExpHeapAllocDirection : uint32
{
HEAD = 0,
TAIL = 1
};
struct MBlockChain2_t
{
MEMPTR<struct MBlock2_t> headMBlock; // 0x00
MEMPTR<struct MBlock2_t> tailMBlock; // 0x04
};
static_assert(sizeof(MBlockChain2_t) == 8);
struct MEMExpHeapHead40_t
{
/* +0x00 */ MBlockChain2_t chainFreeBlocks; // 0x00
/* +0x08 */ MBlockChain2_t chainUsedBlocks; // 0x08
/* +0x10 */ uint16 groupID;
/* +0x12 */ uint16 fields; // Bit 0 -> Alloc mode, Bit 1 -> Allocate within alignment (create free blocks inside alignment padding)
};
static_assert(sizeof(MEMExpHeapHead40_t) == 0x14);
struct MEMExpHeapHead2 : MEMHeapBase
{
// Base
/* +0x34 */ uint32be ukn34;
/* +0x38 */ uint32be ukn38;
/* +0x3C */ uint32be ukn3C;
/* +0x40 */ MEMExpHeapHead40_t expHeapHead;
};
static_assert(sizeof(MEMExpHeapHead2) == 0x54);
MEMHeapHandle MEMCreateExpHeapEx(void* startAddress, uint32 size, uint32 createFlags);
void* MEMAllocFromExpHeapEx(MEMHeapHandle heap, uint32 size, sint32 alignment);
void MEMFreeToExpHeap(MEMHeapHandle heap, void* mem);
uint32 MEMGetAllocatableSizeForExpHeapEx(MEMHeapHandle heap, sint32 alignment);
}

View file

@ -0,0 +1,246 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.h"
namespace coreinit
{
bool __FrmHeapDebug_IsValid(MEMFrmHeap* frmHeap, const char* funcName)
{
if (!frmHeap)
{
cemuLog_log(LogType::APIErrors, "{}: Heap is nullptr", funcName);
return false;
}
if (frmHeap->magic != MEMHeapMagic::FRAME_HEAP)
{
cemuLog_log(LogType::APIErrors, "{}: Heap has bad magic. Not initialized?", funcName);
return false;
}
return true;
}
MEMFrmHeap* MEMCreateFrmHeapEx(void* memStart, uint32 size, uint32 createFlags)
{
cemu_assert_debug(memStart);
uintptr_t startAddr = (uintptr_t)memStart;
uintptr_t endAddr = startAddr + size;
// align and pad address
startAddr = (startAddr + 3) & ~3;
endAddr &= ~3;
if (startAddr == 0)
return nullptr;
if (startAddr > endAddr || (endAddr - startAddr) < sizeof(MEMFrmHeap))
return nullptr;
MEMFrmHeap* frmHeap = (MEMFrmHeap*)startAddr;
MEMInitHeapBase(frmHeap, MEMHeapMagic::FRAME_HEAP, (void*)((uintptr_t)startAddr + sizeof(MEMFrmHeap)), (void*)endAddr, createFlags);
frmHeap->allocationHead = frmHeap->heapStart;
frmHeap->allocationTail = frmHeap->heapEnd;
frmHeap->recordedStates = nullptr;
MEMHeapTable_Add(frmHeap);
return frmHeap;
}
void* MEMDestroyFrmHeap(MEMFrmHeap* frmHeap)
{
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMDestroyFrmHeap"))
return nullptr;
MEMBaseDestroyHeap(frmHeap);
MEMHeapTable_Remove(frmHeap);
return frmHeap;
}
uint32 MEMGetAllocatableSizeForFrmHeapEx(MEMFrmHeap* frmHeap, sint32 alignment)
{
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMGetAllocatableSizeForFrmHeapEx"))
return 0;
frmHeap->AcquireLock();
uint32 allocatableSize = 0;
bool negativeAlignment = alignment < 0;
if (negativeAlignment)
alignment = -alignment;
if (!std::has_single_bit<uint32>((uint32)alignment))
{
cemuLog_log(LogType::APIErrors, "MEMGetAllocatableSizeForFrmHeapEx(): Invalid alignment");
return 0;
}
if (negativeAlignment)
{
cemu_assert_unimplemented();
}
else
{
uint32 headAllocator = frmHeap->allocationHead.GetMPTR();
uint32 tailAllocator = frmHeap->allocationTail.GetMPTR();
uint32 allocStart = (headAllocator + alignment - 1) & ~(alignment - 1);
if (allocStart <= tailAllocator)
{
allocatableSize = tailAllocator - allocStart;
}
}
frmHeap->ReleaseLock();
return allocatableSize;
}
void* MEMiGetFreeStartForFrmHeap(MEMFrmHeap* heap)
{
if (!__FrmHeapDebug_IsValid(heap, "MEMiGetFreeStartForFrmHeap"))
return nullptr;
return heap->allocationHead;
}
void* MEMiGetFreeEndForFrmHeap(MEMFrmHeap* heap)
{
if (!__FrmHeapDebug_IsValid(heap, "MEMiGetFreeEndForFrmHeap"))
return nullptr;
return heap->allocationTail;
}
void* __FrmHeap_AllocFromHead(MEMFrmHeap* frmHeap, uint32 size, sint32 alignment)
{
uint32 head = frmHeap->allocationHead.GetMPTR();
uint32 tail = frmHeap->allocationTail.GetMPTR();
uint32 allocStart = (head + alignment - 1) & ~(alignment - 1);
if ((allocStart + size) <= tail)
{
auto prevHead = frmHeap->allocationHead;
frmHeap->allocationHead = allocStart + size;
if (frmHeap->HasOptionClear())
memset(prevHead.GetPtr(), 0, frmHeap->allocationHead.GetMPTR() - prevHead.GetMPTR());
return MEMPTR<void>(allocStart).GetPtr();
}
return nullptr;
}
void* __FrmHeap_AllocFromTail(MEMFrmHeap* frmHeap, uint32 size, sint32 alignment)
{
uint32 head = frmHeap->allocationHead.GetMPTR();
uint32 tail = frmHeap->allocationTail.GetMPTR();
uint32 allocStart = (tail - size) & ~(alignment - 1);
if (allocStart >= head)
{
auto prevTail = frmHeap->allocationTail;
frmHeap->allocationTail = allocStart;
if (frmHeap->HasOptionClear())
memset(frmHeap->allocationTail.GetPtr(), 0, prevTail.GetMPTR() - frmHeap->allocationTail.GetMPTR());
return MEMPTR<void>(allocStart).GetPtr();
}
return nullptr;
}
void* MEMAllocFromFrmHeapEx(MEMFrmHeap* frmHeap, uint32 size, sint32 alignment)
{
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMAllocFromFrmHeapEx"))
return nullptr;
if (size == 0)
size = 4;
size = (size + 3) & ~3; // pad to 4 byte alignment
frmHeap->AcquireLock();
void* mem;
if (alignment >= 0)
mem = __FrmHeap_AllocFromHead(frmHeap, size, alignment);
else
mem = __FrmHeap_AllocFromTail(frmHeap, size, -alignment);
frmHeap->ReleaseLock();
return mem;
}
void __FrmHeap_FreeFromHead(MEMFrmHeap* frmHeap)
{
cemu_assert_debug(frmHeap->recordedStates.IsNull());
frmHeap->recordedStates = nullptr;
frmHeap->allocationHead = frmHeap->heapStart;
}
void __FrmHeap_FreeFromTail(MEMFrmHeap* frmHeap)
{
cemu_assert_debug(frmHeap->recordedStates.IsNull());
frmHeap->recordedStates = nullptr;
void* heapEnd = frmHeap->heapEnd.GetPtr();
frmHeap->allocationTail = heapEnd;
}
void MEMFreeToFrmHeap(MEMFrmHeap* frmHeap, FrmHeapMode mode)
{
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMFreeToFrmHeap"))
return;
frmHeap->AcquireLock();
if ((mode & FrmHeapMode::Head) != 0)
__FrmHeap_FreeFromHead(frmHeap);
if ((mode & FrmHeapMode::Tail) != 0)
__FrmHeap_FreeFromTail(frmHeap);
frmHeap->ReleaseLock();
}
bool MEMRecordStateForFrmHeap(MEMFrmHeap* frmHeap, uint32 id)
{
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMRecordStateForFrmHeap"))
return false;
frmHeap->AcquireLock();
auto allocationHead = frmHeap->allocationHead;
auto allocationTail = frmHeap->allocationTail;
MEMFrmHeapRecordedState* rState = (MEMFrmHeapRecordedState*)__FrmHeap_AllocFromHead(frmHeap, sizeof(MEMFrmHeapRecordedState), 4); // modifies memHeap->allocationHead
cemu_assert_debug(rState);
if (!rState)
{
frmHeap->ReleaseLock();
return false;
}
rState->id = id;
rState->allocationHead = allocationHead;
rState->allocationTail = allocationTail;
rState->prevRecordedState = frmHeap->recordedStates;
frmHeap->recordedStates = rState;
frmHeap->ReleaseLock();
return true;
}
bool MEMFreeByStateToFrmHeap(MEMFrmHeap* frmHeap, uint32 id)
{
if (!__FrmHeapDebug_IsValid(frmHeap, "MEMFreeByStateToFrmHeap"))
return false;
frmHeap->AcquireLock();
// find matching state
MEMFrmHeapRecordedState* rState = frmHeap->recordedStates.GetPtr();
while (rState)
{
if (id == 0)
break;
if (rState->id == id)
break;
rState = rState->prevRecordedState.GetPtr();
}
if (!rState)
{
frmHeap->ReleaseLock();
return false;
}
// apply state
frmHeap->allocationHead = rState->allocationHead;
frmHeap->allocationTail = rState->allocationTail;
frmHeap->recordedStates = rState->prevRecordedState;
frmHeap->ReleaseLock();
return true;
}
void InitializeMEMFrmHeap()
{
cafeExportRegister("coreinit", MEMCreateFrmHeapEx, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMDestroyFrmHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMGetAllocatableSizeForFrmHeapEx, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMiGetFreeStartForFrmHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMiGetFreeEndForFrmHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMAllocFromFrmHeapEx, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMFreeToFrmHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMFreeToFrmHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMRecordStateForFrmHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMFreeByStateToFrmHeap, LogType::CoreinitMem);
}
}

View file

@ -0,0 +1,41 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
namespace coreinit
{
struct MEMFrmHeapRecordedState
{
uint32be id;
MEMPTR<void> allocationHead;
MEMPTR<void> allocationTail;
MEMPTR<MEMFrmHeapRecordedState> prevRecordedState;
};
static_assert(sizeof(MEMFrmHeapRecordedState) == 0x10);
struct MEMFrmHeap : MEMHeapBase
{
/* +0x34 */ uint32be ukn34;
/* +0x38 */ uint32be ukn38;
/* +0x3C */ uint32be ukn3C;
/* +0x40 */ MEMPTR<void> allocationHead;
/* +0x44 */ MEMPTR<void> allocationTail;
/* +0x48 */ MEMPTR<MEMFrmHeapRecordedState> recordedStates;
};
static_assert(sizeof(MEMFrmHeap) == 0x4C);
enum class FrmHeapMode : uint32
{
Head = (1 << 0),
Tail = (1 << 1),
All = (Head | Tail),
};
MEMFrmHeap* MEMCreateFrmHeapEx(void* memStart, uint32 size, uint32 createFlags);
void* MEMDestroyFrmHeap(MEMFrmHeap* frmHeap);
void* MEMAllocFromFrmHeapEx(MEMFrmHeap* frmHeap, uint32 size, sint32 alignment);
void MEMFreeToFrmHeap(MEMFrmHeap* frmHeap, FrmHeapMode mode);
void InitializeMEMFrmHeap();
}
ENABLE_BITMASK_OPERATORS(coreinit::FrmHeapMode);

View file

@ -0,0 +1,146 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_UnitHeap.h"
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
namespace coreinit
{
void _MEMUnitHeap_IsValidHeap(MEMHeapHandle heap)
{
cemu_assert(heap != MEM_HEAP_INVALID_HANDLE);
cemu_assert(heap->magic == MEMHeapMagic::UNIT_HEAP);
}
MEMHeapBase* MEMCreateUnitHeapEx(void* memStart, uint32 heapSize, uint32 blockSize, uint32 alignment, uint32 createFlags)
{
cemu_assert_debug(memStart != nullptr);
cemu_assert_debug(alignment % MIN_ALIGNMENT == 0);
cemu_assert_debug(MIN_ALIGNMENT <= alignment && alignment <= 32);
uintptr_t startAddr = (uintptr_t)memStart;
uintptr_t endAddr = startAddr + heapSize;
startAddr = (startAddr + MIN_ALIGNMENT_MINUS_ONE) & (~MIN_ALIGNMENT_MINUS_ONE);
endAddr &= (~MIN_ALIGNMENT_MINUS_ONE);
if (startAddr > endAddr)
return nullptr;
const uint32 alignmentMinusOne = alignment - 1;
MEMUnitHeap* unitHeap = (MEMUnitHeap*)startAddr;
uintptr_t alignedStart = startAddr + sizeof(MEMUnitHeap) + alignmentMinusOne & ~((uintptr_t)alignmentMinusOne);
if (alignedStart > endAddr)
return nullptr;
blockSize = blockSize + alignmentMinusOne & ~alignmentMinusOne;
uint32 totalBlockSize = (uint32)(endAddr - alignedStart);
uint32 numBlocks = totalBlockSize / blockSize;
if (numBlocks == 0)
return nullptr;
MEMInitHeapBase(unitHeap, MEMHeapMagic::UNIT_HEAP, (void*)alignedStart, (void*)(alignedStart + (numBlocks * blockSize)), createFlags);
unitHeap->firstFreeBlock = (MEMUnitHeapBlock*)alignedStart;
unitHeap->blockSize = blockSize;
MEMUnitHeapBlock* currentBlock = (MEMUnitHeapBlock*)alignedStart;
for (uint32 i = 0; i < numBlocks - 1; ++i)
{
MEMUnitHeapBlock* nextBlock = (MEMUnitHeapBlock*)((uintptr_t)currentBlock + blockSize);
currentBlock->nextBlock = nextBlock;
currentBlock = nextBlock;
}
currentBlock->nextBlock = nullptr;
if ((MEMHeapHandle*)startAddr != nullptr)
{
MEMHeapTable_Add((MEMHeapHandle)startAddr);
}
return (MEMHeapBase*)startAddr;
}
void* MEMDestroyUnitHeap(MEMHeapHandle heap)
{
_MEMUnitHeap_IsValidHeap(heap);
MEMBaseDestroyHeap(heap);
MEMHeapTable_Remove(heap);
return heap;
}
uint32 MEMCalcHeapSizeForUnitHeap(uint32 blockSize, uint32 blockCount, uint32 alignment)
{
uint32 alignedBlockSize = (blockSize + (alignment - 1)) & ~(alignment - 1);
uint32 blockTotalSize = blockCount * alignedBlockSize;
uint32 heapSize = blockTotalSize + (alignment - 4) + sizeof(MEMUnitHeap);
return heapSize;
}
uint32 MEMCountFreeBlockForUnitHeap(coreinit::MEMUnitHeap* heap)
{
_MEMUnitHeap_IsValidHeap(heap);
heap->AcquireLock();
MEMUnitHeapBlock* currentBlock = heap->firstFreeBlock;
uint32 blockCount = 0;
while (currentBlock)
{
blockCount++;
currentBlock = currentBlock->nextBlock;
}
heap->ReleaseLock();
return blockCount;
}
void* MEMAllocFromUnitHeap(MEMUnitHeap* heap)
{
_MEMUnitHeap_IsValidHeap(heap);
heap->AcquireLock();
MEMUnitHeapBlock* currentBlock = heap->firstFreeBlock;
if (!currentBlock)
{
heap->ReleaseLock();
return nullptr;
}
// remove from list of free blocks
heap->firstFreeBlock = currentBlock->nextBlock;
// fill block
if (heap->HasOptionClear())
{
memset(currentBlock, 0, heap->blockSize);
}
else if (heap->HasOptionFill())
{
memset(currentBlock, coreinit::MEMGetFillValForHeap(coreinit::HEAP_FILL_TYPE::ON_ALLOC), heap->blockSize);
}
heap->ReleaseLock();
return currentBlock;
}
void MEMFreeToUnitHeap(MEMUnitHeap* heap, void* mem)
{
_MEMUnitHeap_IsValidHeap(heap);
if (!mem)
return;
heap->AcquireLock();
cemu_assert_debug(mem >= heap->heapStart.GetPtr() && mem < heap->heapEnd.GetPtr());
// add to list of free blocks
MEMUnitHeapBlock* releasedBlock = (MEMUnitHeapBlock*)mem;
releasedBlock->nextBlock = heap->firstFreeBlock;
heap->firstFreeBlock = releasedBlock;
if (heap->HasOptionFill())
memset(releasedBlock, coreinit::MEMGetFillValForHeap(coreinit::HEAP_FILL_TYPE::ON_FREE), heap->blockSize);
heap->ReleaseLock();
}
void InitializeMEMUnitHeap()
{
cafeExportRegister("coreinit", MEMCreateUnitHeapEx, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMDestroyUnitHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMCalcHeapSizeForUnitHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMCountFreeBlockForUnitHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMAllocFromUnitHeap, LogType::CoreinitMem);
cafeExportRegister("coreinit", MEMFreeToUnitHeap, LogType::CoreinitMem);
}
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
namespace coreinit
{
struct MEMUnitHeapBlock
{
MEMPTR<MEMUnitHeapBlock> nextBlock;
};
static_assert(sizeof(MEMUnitHeapBlock) == 4);
struct MEMUnitHeap : public MEMHeapBase
{
/* +0x34 */ uint32 padding034;
/* +0x38 */ uint32 padding038;
/* +0x3C */ uint32 padding03C;
/* +0x40 */ MEMPTR<MEMUnitHeapBlock> firstFreeBlock;
/* +0x44 */ uint32be blockSize;
};
static_assert(sizeof(MEMUnitHeap) == 0x48);
MEMHeapBase* MEMCreateUnitHeapEx(void* memStart, uint32 heapSize, uint32 memBlockSize, uint32 alignment, uint32 createFlags);
void* MEMDestroyUnitHeap(MEMHeapHandle hHeap);
}

View file

@ -0,0 +1,516 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_MPQueue.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
#include "util/helpers/fspinlock.h"
// titles that utilize MP task queue: Yoshi's Woolly World, Fast Racing Neo, Tokyo Mirage Sessions, Mii Maker
#define AcquireMPQLock() s_workaroundSpinlock.acquire()
#define ReleaseMPQLock() s_workaroundSpinlock.release()
namespace coreinit
{
FSpinlock s_workaroundSpinlock; // workaround for a race condition
/*
Race condition pseudo-code:
WorkerThreads:
while( task = MPDequeTask() ) MPRunTask(task);
MainThread:
QueueTasks();
// wait and reset
MPWaitForTaskQWithTimeout(DONE)
MPTermTaskQ()
MPInitTaskQ()
The race condition then happens when a worker thread calls MPDequeTask()/MPRunTask while MPInitTaskQ() is being executed on the main thread.
Since MPInitTaskQ() (re)initializes the internal spinlock it's not thread-safe, leading to a corruption of the spinlock state for other threads
We work around this by using a global spinlock instead of the taskQ specific one
*/
void MPInitTask(MPTask* task, void* func, void* data, uint32 size)
{
s_workaroundSpinlock.acquire();
task->thisptr = task;
task->coreIndex = PPC_CORE_COUNT;
task->taskFunc.func = func;
task->taskFunc.data = data;
task->taskFunc.size = size;
task->taskState = MP_TASK_STATE_INIT;
task->userdata = nullptr;
task->runtime = 0;
s_workaroundSpinlock.release();
}
bool MPTermTask(MPTask* task)
{
return true;
}
bool MPRunTask(MPTask* task)
{
if (task->taskState != MP_TASK_STATE_READY)
return false;
auto* taskQ = task->taskQ.GetPtr();
if(taskQ->state != MP_TASKQ_STATE_STOPPING && taskQ->state != MP_TASKQ_STATE_STOP)
{
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskQ->spinlock);
if (taskQ->state == MP_TASKQ_STATE_STOPPING || taskQ->state == MP_TASKQ_STATE_STOP)
{
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskQ->spinlock);
return false;
}
taskQ->taskReadyCount = taskQ->taskReadyCount - 1;
taskQ->taskRunCount = taskQ->taskRunCount + 1;
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskQ->spinlock);
const auto startTime = OSGetSystemTime();
task->coreIndex = OSGetCoreId();
task->taskState = MP_TASK_STATE_RUN;
task->taskFunc.result = PPCCoreCallback(task->taskFunc.func, task->taskFunc.data, task->taskFunc.size);
task->taskState = MP_TASK_STATE_DONE;
const auto endTime = OSGetSystemTime();
task->runtime = endTime - startTime;
cemu_assert_debug(taskQ->state != MP_TASKQ_STATE_DONE);
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskQ->spinlock);
taskQ->taskRunCount = taskQ->taskRunCount - 1;
taskQ->taskDoneCount[1] = taskQ->taskDoneCount[1] + 1;
if (taskQ->state == MP_TASKQ_STATE_STOPPING && taskQ->taskRunCount == 0)
taskQ->state = MP_TASKQ_STATE_STOP;
if (taskQ->taskCount == taskQ->taskDoneCount[1])
taskQ->state = MP_TASKQ_STATE_DONE;
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskQ->spinlock);
return true;
}
return false;
}
bool MPGetTaskInfo(MPTask* task, MPTaskInfo* info)
{
info->state = task->taskState;
info->coreIndex = task->coreIndex;
info->runtime = task->runtime;
// not setting function result?
return true;
}
void* MPGetTaskUserData(MPTask* task)
{
return task->userdata.GetPtr();
}
void MPSetTaskUserData(MPTask* task, void* userdata)
{
task->userdata = userdata;
}
void MPInitTaskQ(MPTaskQ* taskQ, MPTask** tasks, uint32 taskCount)
{
AcquireMPQLock();
taskQ->thisptr = taskQ;
OSInitSpinLock(&taskQ->spinlock);
taskQ->state = MP_TASKQ_STATE_INIT;
taskQ->taskReadyCount = 0;
taskQ->taskCount = 0;
taskQ->taskRunCount = 0;
for(uint32 i = 0; i < OSGetCoreCount(); ++i)
{
taskQ->taskDoneCount[i] = 0;
taskQ->nextIndex[i] = 0;
taskQ->endIndex[i] = 0;
}
taskQ->taskQueue = (MEMPTR<MPTask>*)tasks;
taskQ->taskQueueSize = taskCount;
ReleaseMPQLock();
}
bool MPEnqueTask(MPTaskQ* taskq, MPTask* task)
{
bool result = false;
if(task->taskState == MP_TASK_STATE_INIT)
{
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
const uint32 taskQState = taskq->state;
if((uint32)taskq->endIndex[1] < taskq->taskQueueSize
&& (taskQState == MP_TASKQ_STATE_INIT || taskQState == MP_TASKQ_STATE_RUN || taskQState == MP_TASKQ_STATE_STOPPING || taskQState == MP_TASKQ_STATE_STOP || taskQState == MP_TASKQ_STATE_DONE))
{
task->taskQ = taskq;
task->taskState = MP_TASK_STATE_READY;
taskq->thisptr = taskq;
const uint32 endIndex = taskq->endIndex[1];
taskq->endIndex[1] = endIndex + 1;
taskq->taskCount = taskq->taskCount + 1;
taskq->taskReadyCount = taskq->taskReadyCount + 1;
taskq->taskQueue[endIndex] = task;
if (taskQState == MP_TASKQ_STATE_DONE)
taskq->state = MP_TASKQ_STATE_RUN;
result = true;
}
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
}
return result;
}
bool MPTermTaskQ(MPTaskQ* taskq)
{
// workaround code for TMS
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
//if ((uint32)taskq->taskReadyCount > 0 && taskq->state == MP_TASKQ_STATE_RUN)
if (taskq->state == MP_TASKQ_STATE_RUN)
{
taskq->state = MP_TASKQ_STATE_STOP;
}
while (taskq->taskRunCount != 0)
{
// wait for tasks to finish
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
OSYieldThread();
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
}
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
return true;
}
bool MPGetTaskQInfo(MPTaskQ* taskq, MPTaskQInfo* info)
{
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
info->state = taskq->state;
info->taskCount = taskq->taskCount;
info->taskReadyCount = taskq->taskReadyCount;
info->taskRunCount = taskq->taskRunCount;
info->taskDoneCount = taskq->taskDoneCount[1];
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
return true;
}
bool MPStartTaskQ(MPTaskQ* taskq)
{
bool result = false;
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
if (taskq->state == MP_TASKQ_STATE_INIT || taskq->state == MP_TASKQ_STATE_STOP)
{
taskq->state = MP_TASKQ_STATE_RUN;
result = true;
}
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
return result;
}
bool MPRunTasksFromTaskQ(MPTaskQ* taskq, int granularity)
{
uint32 result = 0;
while (true)
{
if (taskq->state != MP_TASKQ_STATE_RUN)
return (taskq->state & MP_TASKQ_STATE_DONE) != 0;
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
const auto nextIndex = taskq->nextIndex[1];
const auto endIndex = taskq->endIndex[1];
if (nextIndex == endIndex)
break;
auto newNextIndex = nextIndex + granularity;
if (endIndex < nextIndex + granularity)
newNextIndex = endIndex;
const auto workCount = (newNextIndex - nextIndex);
taskq->nextIndex[1] = newNextIndex;
taskq->taskReadyCount = taskq->taskReadyCount - workCount;
taskq->taskRunCount = taskq->taskRunCount + workCount;
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
// since we are having a granularity parameter, we might want to give the scheduler the chance for other stuff when having multiple tasks
if(result != 0)
PPCCore_switchToScheduler();
for (int i = nextIndex; i < newNextIndex; ++i)
{
const auto startTime = OSGetSystemTime();
const auto& task = taskq->taskQueue[i];
result = task->thisptr.GetMPTR();
task->taskState = MP_TASK_STATE_RUN;
task->coreIndex = OSGetCoreId();
task->taskFunc.result = PPCCoreCallback(task->taskFunc.func, task->taskFunc.data, task->taskFunc.size);
task->taskState = MP_TASK_STATE_DONE;
const auto endTime = OSGetSystemTime();
task->runtime = endTime - startTime;
}
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
const auto runRemaining = taskq->taskRunCount - workCount;
taskq->taskRunCount = runRemaining;
const auto doneCount = taskq->taskDoneCount[1] + workCount;
taskq->taskDoneCount[1] = doneCount;
if (taskq->state == 4 && runRemaining == 0)
taskq->state = MP_TASKQ_STATE_STOP;
if (taskq->taskCount == doneCount)
taskq->state = MP_TASKQ_STATE_DONE;
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
}
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
return result != 0;
}
bool MPStopTaskQ(MPTaskQ* taskq)
{
bool result = false;
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
if (taskq->state == MP_TASKQ_STATE_RUN)
{
taskq->state = MP_TASKQ_STATE_STOPPING;
if (taskq->taskRunCount == 0)
taskq->state = MP_TASKQ_STATE_STOP;
result = true;
}
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
return result;
}
bool MPWaitTaskQ(MPTaskQ* taskQ, uint32 waitState)
{
bool waitRun = (waitState & MP_TASKQ_STATE_RUN) != 0;
bool waitStop = (waitState & MP_TASKQ_STATE_STOP) != 0;
bool waitDone = (waitState & MP_TASKQ_STATE_DONE) != 0;
size_t loopCounter = 0;
while (waitStop || waitDone || waitRun)
{
const uint32 state = taskQ->state;
if (waitRun && HAS_FLAG(state, MP_TASKQ_STATE_RUN))
{
waitRun = false;
waitDone = false;
continue;
}
if (waitStop && HAS_FLAG(state, MP_TASKQ_STATE_STOP))
{
waitStop = false;
waitDone = false;
continue;
}
if (waitDone && HAS_FLAG(state, MP_TASKQ_STATE_DONE))
{
waitDone = false;
waitRun = false;
waitStop = false;
continue;
}
if (loopCounter > 0)
coreinit::OSSleepTicks(EspressoTime::ConvertNsToTimerTicks(50000)); // sleep thread for 0.05ms to give other threads a chance to run (avoids softlocks in YWW)
else
PPCCore_switchToScheduler();
loopCounter++;
}
return true;
}
bool MPWaitTaskQWithTimeout(MPTaskQ* taskQ, uint32 waitState, sint64 timeout)
{
bool waitRun = (waitState & MP_TASKQ_STATE_RUN) != 0;
bool waitStop = (waitState & MP_TASKQ_STATE_STOP) != 0;
bool waitDone = (waitState & MP_TASKQ_STATE_DONE) != 0;
const auto startTime = OSGetSystemTime();
const auto timerTicks = EspressoTime::ConvertNsToTimerTicks(timeout);
const auto endTime = startTime + timerTicks;
while (waitStop || waitDone || waitRun)
{
const uint32 state = taskQ->state;
if (waitRun && HAS_FLAG(state, MP_TASKQ_STATE_RUN))
{
waitRun = false;
waitDone = false;
continue;
}
if (waitStop && HAS_FLAG(state, MP_TASKQ_STATE_STOP))
{
waitStop = false;
waitDone = false;
continue;
}
if (waitDone && HAS_FLAG(state, MP_TASKQ_STATE_DONE))
{
waitDone = false;
waitRun = false;
waitStop = false;
continue;
}
if (OSGetSystemTime() >= endTime)
{
if (waitState == MP_TASKQ_STATE_DONE)
cemuLog_log(LogType::Force, "MPWaitTaskQWithTimeout(): Timeout occurred while waiting for done-only state");
return false;
}
PPCCore_switchToScheduler();
}
return true;
}
MPTask* MPDequeTask(MPTaskQ* taskq)
{
MPTask* result = nullptr;
if (taskq->state == MP_TASKQ_STATE_RUN)
{
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
if (taskq->state == MP_TASKQ_STATE_RUN && taskq->nextIndex[1] != taskq->endIndex[1])
{
result = taskq->taskQueue[taskq->nextIndex[1]].GetPtr();
taskq->nextIndex[1] = taskq->nextIndex[1] + 1;
}
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
}
return result;
}
uint32 MPDequeTasks(MPTaskQ* taskq, MPTask** tasks, sint32 maxTasks)
{
uint32 dequeCount = 0;
if (taskq->state == MP_TASKQ_STATE_RUN)
{
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
if (taskq->state == MP_TASKQ_STATE_RUN)
{
auto nextIndex = (sint32)taskq->nextIndex[1];
auto newEndIndex = nextIndex + maxTasks;
if (taskq->endIndex[1] < nextIndex + maxTasks)
newEndIndex = taskq->endIndex[1];
dequeCount = newEndIndex - nextIndex;
taskq->nextIndex[1] = newEndIndex;
for(int i = 0; nextIndex < newEndIndex; ++nextIndex, ++i)
{
tasks[i] = taskq->taskQueue[nextIndex].GetPtr();
}
auto idx = 0;
while (nextIndex < newEndIndex)
{
tasks[idx] = taskq->taskQueue[nextIndex].GetPtr();
nextIndex = nextIndex + 1;
idx = idx + 1;
}
}
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
}
return dequeCount;
}
bool MPResetTaskQ(MPTaskQ* taskq)
{
debug_printf("MPResetTaskQ called\n");
bool result = false;
AcquireMPQLock(); // OSUninterruptibleSpinLock_Acquire(&taskq->spinlock);
if (taskq->state == MP_TASKQ_STATE_DONE || taskq->state == MP_TASKQ_STATE_STOP)
{
taskq->state = MP_TASKQ_STATE_INIT;
taskq->taskRunCount = 0;
taskq->taskCount = taskq->endIndex[1];
taskq->taskReadyCount = taskq->endIndex[1];
for(uint32 i = 0; i < OSGetCoreCount(); ++i)
{
taskq->taskDoneCount[i] = 0;
taskq->nextIndex[i] = 0;
}
for(uint32 i = 0; i < taskq->taskCount; ++i)
{
const auto& task = taskq->taskQueue[i];
task->taskFunc.result = 0;
task->coreIndex = PPC_CORE_COUNT;
task->runtime = 0;
task->taskState = MP_TASK_STATE_READY;
}
result = true;
}
ReleaseMPQLock(); // OSUninterruptibleSpinLock_Release(&taskq->spinlock);
return result;
}
void InitializeMP()
{
// task
cafeExportRegister("coreinit", MPInitTask, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPTermTask, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPRunTask, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPGetTaskInfo, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPGetTaskUserData, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPSetTaskUserData, LogType::CoreinitMP);
// taskq
cafeExportRegister("coreinit", MPInitTaskQ, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPResetTaskQ, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPEnqueTask, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPDequeTask, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPDequeTasks, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPRunTasksFromTaskQ, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPStartTaskQ, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPWaitTaskQ, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPWaitTaskQWithTimeout, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPStopTaskQ, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPTermTaskQ, LogType::CoreinitMP);
cafeExportRegister("coreinit", MPGetTaskQInfo, LogType::CoreinitMP);
}
}

View file

@ -0,0 +1,106 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit.h"
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
namespace coreinit
{
enum MPTaskState
{
MP_TASK_STATE_INIT = (1 << 0),
MP_TASK_STATE_READY = (1 << 1),
MP_TASK_STATE_RUN = (1 << 2),
MP_TASK_STATE_DONE = (1 << 3)
};
enum MPTaskQState
{
MP_TASKQ_STATE_INIT = (1 << 0),
MP_TASKQ_STATE_RUN = (1 << 1),
MP_TASKQ_STATE_STOPPING = (1 << 2),
MP_TASKQ_STATE_STOP = (1 << 3),
MP_TASKQ_STATE_DONE = (1 << 4)
};
struct MPTaskFunction
{
/* +0x00 */ MEMPTR<void> func;
/* +0x04 */ MEMPTR<void> data;
/* +0x08 */ uint32be size;
/* +0x0C */ uint32be result;
};
static_assert(sizeof(MPTaskFunction) == 0x10);
#pragma pack(1)
struct MPTask
{
/* +0x00 */ MEMPTR<void> thisptr;
/* +0x04 */ MEMPTR<struct MPTaskQ> taskQ;
/* +0x08 */ uint32be taskState;
/* +0x0C */ MPTaskFunction taskFunc;
/* +0x1C */ uint32be coreIndex;
/* +0x20 */ sint64be runtime;
/* +0x28 */ MEMPTR<void> userdata;
};
static_assert(sizeof(MPTask) == 0x2C);
#pragma pack()
struct MPTaskQ
{
/* +0x00 */ MEMPTR<void> thisptr;
/* +0x04 */ uint32be state;
/* +0x08 */ uint32be taskCount;
/* +0x0C */ uint32be taskReadyCount;
/* +0x10 */ uint32be taskRunCount;
/* +0x14 */ uint32be taskDoneCount[PPC_CORE_COUNT];
/* +0x20 */ sint32be nextIndex[PPC_CORE_COUNT];
/* +0x2C */ sint32be endIndex[PPC_CORE_COUNT];
/* +0x38 */ MEMPTR<MEMPTR<MPTask>> taskQueue;
/* +0x3C */ uint32be taskQueueSize;
/* +0x40 */ OSSpinLock spinlock;
};
static_assert(sizeof(MPTaskQ) == 0x50);
struct MPTaskQInfo
{
/* +0x00 */ uint32be state;
/* +0x04 */ uint32be taskCount;
/* +0x08 */ uint32be taskReadyCount;
/* +0x0C */ uint32be taskRunCount;
/* +0x10 */ uint32be taskDoneCount;
};
static_assert(sizeof(MPTaskQInfo) == 0x14);
struct MPTaskInfo
{
/* +0x00 */ uint32be state;
/* +0x04 */ uint32be funcResult;
/* +0x08 */ uint32be coreIndex;
/* +0x0C */ sint64be runtime;
};
static_assert(sizeof(MPTaskQInfo) == 0x14);
void MPInitTask(MPTask* task, void* func, void* data, uint32 size);
bool MPTermTask(MPTask* task);
bool MPRunTask(MPTask* task);
bool MPGetTaskInfo(MPTask* task, MPTaskInfo* info);
void* MPGetTaskUserData(MPTask* task);
void MPSetTaskUserData(MPTask* task, void* userdata);
void MPInitTaskQ(MPTaskQ* taskq, MPTask** tasks, uint32 taskCount);
bool MPEnqueTask(MPTaskQ* taskq, MPTask* task);
bool MPTermTaskQ(MPTaskQ* taskq);
bool MPGetTaskQInfo(MPTaskQ* taskq, MPTaskQInfo* info);
bool MPStartTaskQ(MPTaskQ* taskq);
bool MPRunTasksFromTaskQ(MPTaskQ* taskq, int granularity);
bool MPStopTaskQ(MPTaskQ* taskq);
bool MPWaitTaskQ(MPTaskQ* taskq, uint32 waitState);
bool MPWaitTaskQWithTimeout(MPTaskQ* taskq, uint32 waitState, sint64 timeout);
MPTask* MPDequeTask(MPTaskQ* taskq);
uint32 MPDequeTasks(MPTaskQ* taskq, MPTask** tasks, sint32 maxTasks);
bool MPResetTaskQ(MPTaskQ* taskq);
void InitializeMP();
}

View file

@ -0,0 +1,225 @@
#include "Cafe/OS/common/OSCommon.h"
#include "coreinit_Memory.h"
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/GraphicPack/GraphicPack2.h"
#include "Cafe/CafeSystem.h"
namespace coreinit
{
void DCInvalidateRange(MPTR addr, uint32 size)
{
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
addr &= ~0x1F;
//LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
}
void DCFlushRange(MPTR addr, uint32 size)
{
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
addr &= ~0x1F;
LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
}
void DCFlushRangeNoSync(MPTR addr, uint32 size)
{
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
addr &= ~0x1F;
LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
}
void DCStoreRange(MPTR addr, uint32 size)
{
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
addr &= ~0x1F;
//LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
}
void DCStoreRangeNoSync(MPTR addr, uint32 size)
{
MPTR addrEnd = (addr + size + 0x1F) & ~0x1F;
addr &= ~0x1F;
LatteBufferCache_notifyDCFlush(addr, addrEnd - addr);
}
void DCZeroRange(MPTR addr, uint32 size)
{
MPTR alignedAddr = addr & ~31;
uint32 cachlineOffset = addr & 31;
uint32 blocks = (cachlineOffset + size + 31) / 32;
if (blocks > 0)
{
memset(memory_getPointerFromVirtualOffset(alignedAddr), 0x00, blocks * 32);
LatteBufferCache_notifyDCFlush(alignedAddr, blocks * 32);
}
}
bool OSIsAddressRangeDCValid(uint32 startOffset, uint32 range)
{
uint32 endOffset = startOffset + range - 1;
uint32 boundaryLow = 0xE8000000;
uint32 boundaryHigh = 0xEC000000;
if (startOffset < boundaryLow || startOffset >= boundaryHigh)
return false;
if (endOffset < boundaryLow || endOffset >= boundaryHigh)
return false;
return true;
}
void* coreinit_memset(void* dst, uint32 value, uint32 size)
{
memset(dst, value, size);
return dst;
}
void* coreinit_memcpy(MEMPTR<void> dst, MEMPTR<void> src, uint32 size)
{
if (dst.GetMPTR() == 0xFFFFFFFF)
{
// games this was seen in: The Swapper
// this may be a bug in the game. The last few bytes of the address space are writable, but wrap-around behavior of COS memcpy is unknown
cemu_assert_debug(false);
}
if (size > 0)
{
memcpy(dst.GetPtr(), src.GetPtr(), size);
// always flushes the cache!
LatteBufferCache_notifyDCFlush(dst.GetMPTR(), size);
}
return dst.GetPtr();
}
void* coreinit_memmove(MEMPTR<void> dst, void* src, uint32 size)
{
if (size > 0)
{
memmove(dst.GetPtr(), src, size);
// always flushes the cache!
LatteBufferCache_notifyDCFlush(dst.GetMPTR(), size);
}
return dst.GetPtr();
}
void* OSBlockMove(MEMPTR<void> dst, MEMPTR<void> src, uint32 size, bool flushDC)
{
if (size > 0)
{
memmove(dst.GetPtr(), src.GetPtr(), size);
if (flushDC)
LatteBufferCache_notifyDCFlush(dst.GetMPTR(), size);
}
return dst.GetPtr();
}
void* OSBlockSet(MEMPTR<void> dst, uint32 value, uint32 size)
{
memset(dst.GetPtr(), value&0xFF, size);
return dst.GetPtr();
}
MPTR OSEffectiveToPhysical(MPTR effectiveAddr)
{
MPTR physicalAddr = memory_virtualToPhysical(effectiveAddr);
return physicalAddr;
}
void OSMemoryBarrier(PPCInterpreter_t* hCPU)
{
// no-op
}
void OSGetMemBound(sint32 memType, MPTR* offsetOutput, uint32* sizeOutput)
{
MPTR memAddr = MPTR_NULL;
uint32 memSize = 0;
/*
Data taken from browser dump:
type start size
MEM1 0xF4000000 0x02000000
MEM2 0x106DE000 0x170C2000
*/
if (memType == 1)
{
// MEM1
memAddr = mmuRange_MEM1.getBase();
memSize = mmuRange_MEM1.getSize();
}
else if (memType == 2)
{
// MEM2
uint32 currentRPLAllocatorOffset = RPLLoader_GetDataAllocatorAddr();
// due to differences in our library implementations we currently allocate less memory for the OS/RPLs than on the actual hardware,
// as a result more memory is available to games
// however, some games crash due to internal overflows if there is too much memory available
// here we artificially reduce the available memory for the affected games
uint64 titleId = CafeSystem::GetForegroundTitleId();
if (
titleId == 0x0005000010132400ULL || // Lego Marvel Super Heroes (EU)
titleId == 0x0005000010132B00ULL || // Lego Marvel Super Heroes (US)
titleId == 0x0005000010194200ull || // Lego Dimensions (US)
titleId == 0x0005000010195D00ull || // Lego Dimensions (EU)
titleId == 0x00050000101A6200ull || // Lego Jurassic World (US)
titleId == 0x00050000101A5C00 || // Lego Jurassic World (EU)
titleId == 0x000500001014DE00 || // The Lego Movie Videogame (US)
titleId == 0x000500001014E000 || // The Lego Movie Videogame (EU)
titleId == 0x0005000010168D00 || // Lego The Hobbit (EU)
titleId == 0x000500001016A700 || // Lego The Hobbit (JP)
// The Hobbit US title id?
titleId == 0x00050000101DAB00 || // Lego Star Wars: The Force Awakens (US)
titleId == 0x00050000101DAA00 || // Lego Star Wars: The Force Awakens (EU)
// LEGO Batman 3: BEYOND GOTHAM
titleId == 0x000500001016A400 || // EU
titleId == 0x000500001016AD00 || // US
// Lego Marvel Avengers
titleId == 0x00050000101BE900 || // EU
titleId == 0x00050000101BEF00 || // US
// LEGO BATMAN 2: DC Super Heroes
titleId == 0x0005000010135500 || // EU
titleId == 0x0005000010135E00 // US
)
{
forceLogDebug_printf("Hack: Reduce available memory to simulate loaded RPLs");
currentRPLAllocatorOffset += (48 * 1024 * 1024); // 48MB
}
memAddr = currentRPLAllocatorOffset;
memSize = mmuRange_MEM2.getEnd() - currentRPLAllocatorOffset;
}
else
{
cemu_assert_debug(false);
}
if (offsetOutput)
*offsetOutput = _swapEndianU32(memAddr);
if (sizeOutput)
*sizeOutput = _swapEndianU32(memSize);
}
void InitializeMemory()
{
cafeExportRegister("coreinit", DCInvalidateRange, LogType::Placeholder);
cafeExportRegister("coreinit", DCFlushRange, LogType::Placeholder);
cafeExportRegister("coreinit", DCFlushRangeNoSync, LogType::Placeholder);
cafeExportRegister("coreinit", DCStoreRange, LogType::Placeholder);
cafeExportRegister("coreinit", DCStoreRangeNoSync, LogType::Placeholder);
cafeExportRegister("coreinit", DCZeroRange, LogType::Placeholder);
cafeExportRegister("coreinit", OSIsAddressRangeDCValid, LogType::Placeholder);
cafeExportRegisterFunc(coreinit_memcpy, "coreinit", "memcpy", LogType::Placeholder);
cafeExportRegisterFunc(coreinit_memset, "coreinit", "memset", LogType::Placeholder);
cafeExportRegisterFunc(coreinit_memmove, "coreinit", "memmove", LogType::Placeholder);
cafeExportRegister("coreinit", OSBlockMove, LogType::Placeholder);
cafeExportRegister("coreinit", OSBlockSet, LogType::Placeholder);
cafeExportRegister("coreinit", OSEffectiveToPhysical, LogType::Placeholder);
cafeExportRegister("coreinit", OSMemoryBarrier, LogType::Placeholder);
cafeExportRegister("coreinit", OSGetMemBound, LogType::Placeholder);
}
}

View file

@ -0,0 +1,8 @@
#pragma once
namespace coreinit
{
void InitializeMemory();
void OSGetMemBound(sint32 memType, MPTR* offsetOutput, uint32* sizeOutput);
}

View file

@ -0,0 +1,168 @@
#include "Cafe/OS/common/OSCommon.h"
#include "util/MemMapper/MemMapper.h"
#define OS_MAP_READ_ONLY (1)
#define OS_MAP_READ_WRITE (2)
namespace coreinit
{
struct OSVirtMemory
{
MPTR virtualAddress;
uint32 size;
uint32 alignment;
OSVirtMemory* next;
};
OSVirtMemory* virtualMemoryList = nullptr;
MPTR _VirtualMemoryAlloc(uint32 size, uint32 alignment)
{
uint32 currentAddress = MEMORY_MAPABLE_VIRT_AREA_OFFSET;
uint32 endAddress = MEMORY_MAPABLE_VIRT_AREA_OFFSET + MEMORY_MAPABLE_VIRT_AREA_SIZE;
uint32 pageSize = (uint32)MemMapper::GetPageSize();
while (true)
{
// calculated aligned start and end address for current region
currentAddress = (currentAddress + alignment - 1) & ~(alignment - 1);
currentAddress = (currentAddress + pageSize - 1) & ~(pageSize - 1);
uint32 currentEndAddress = currentAddress + size;
currentEndAddress = (currentEndAddress + pageSize - 1) & ~(pageSize - 1);
// check if out of available space
if (currentEndAddress >= endAddress)
{
debug_printf("coreinitVirtualMemory_alloc(): Unable to allocate memory\n");
debugBreakpoint();
return NULL;
}
// check for overlapping regions
OSVirtMemory* virtMemItr = virtualMemoryList;
bool emptySpaceFound = true;
while (virtMemItr)
{
// check for range collision
if (currentAddress < (virtMemItr->virtualAddress + virtMemItr->size) && currentEndAddress > virtMemItr->virtualAddress)
{
// regions overlap
// adjust current address and try again
currentAddress = virtMemItr->virtualAddress + virtMemItr->size;
emptySpaceFound = false;
break;
}
// next
virtMemItr = virtMemItr->next;
}
if (emptySpaceFound)
{
// add entry
OSVirtMemory* virtMemory = (OSVirtMemory*)malloc(sizeof(OSVirtMemory));
memset(virtMemory, 0x00, sizeof(OSVirtMemory));
virtMemory->virtualAddress = currentAddress;
virtMemory->size = currentEndAddress - currentAddress;
virtMemory->alignment = alignment;
virtMemory->next = virtualMemoryList;
virtualMemoryList = virtMemory;
return currentAddress;
}
}
return NULL;
}
void coreinitExport_OSGetAvailPhysAddrRange(PPCInterpreter_t* hCPU)
{
// parameters:
// r3 MPTR* areaStart
// r4 uint32 areaSize
memory_writeU32(hCPU->gpr[3], MEMORY_MAPABLE_PHYS_AREA_OFFSET);
memory_writeU32(hCPU->gpr[4], MEMORY_MAPABLE_PHYS_AREA_SIZE);
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_OSAllocVirtAddr(PPCInterpreter_t* hCPU)
{
// parameters:
// r3 MPTR address
// r4 uint32 size
// r5 uint32 align
uint32 address = hCPU->gpr[3];
uint32 size = hCPU->gpr[4];
uint32 align = hCPU->gpr[5];
if (address != MPTR_NULL)
{
debug_printf("coreinitExport_OSAllocVirtAddr(): Unsupported address != NULL\n");
debugBreakpoint();
}
if (align == 0)
align = 1;
if (align != 0 && align != 1)
assert_dbg();
address = _VirtualMemoryAlloc(size, align);
debug_printf("coreinitExport_OSAllocVirtAddr(): Allocated virtual memory at 0x%08x\n", address);
osLib_returnFromFunction(hCPU, address);
}
void coreinitExport_OSMapMemory(PPCInterpreter_t* hCPU)
{
// parameters:
// r3 MPTR virtualAddress
// r4 MPTR physicalAddress
// r5 uint32 size
// r6 uint32 mode
MPTR virtualAddress = hCPU->gpr[3];
MPTR physicalAddress = hCPU->gpr[4];
uint32 size = hCPU->gpr[5];
uint32 mode = hCPU->gpr[6];
if (virtualAddress < MEMORY_MAPABLE_VIRT_AREA_OFFSET || virtualAddress >= (MEMORY_MAPABLE_VIRT_AREA_OFFSET + MEMORY_MAPABLE_VIRT_AREA_SIZE))
cemu_assert_suspicious();
uint8* virtualPtr = memory_getPointerFromVirtualOffset(virtualAddress);
MemMapper::PAGE_PERMISSION pageProtect = MemMapper::PAGE_PERMISSION::P_NONE;
if (mode == OS_MAP_READ_ONLY)
pageProtect = MemMapper::PAGE_PERMISSION::P_READ;
else if (mode == OS_MAP_READ_WRITE)
pageProtect = MemMapper::PAGE_PERMISSION::P_RW;
else
cemu_assert_unimplemented();
void* allocationResult = MemMapper::AllocateMemory(virtualPtr, size, pageProtect, true);
if (!allocationResult)
{
cemuLog_log(LogType::Force, "OSMapMemory failed");
osLib_returnFromFunction(hCPU, 0);
return;
}
osLib_returnFromFunction(hCPU, 1);
}
void coreinitExport_OSUnmapMemory(PPCInterpreter_t* hCPU)
{
// parameters:
// r3 MPTR virtualAddress
// r4 uint32 size
MPTR virtualAddress = hCPU->gpr[3];
uint32 size = hCPU->gpr[4];
if (virtualAddress < MEMORY_MAPABLE_VIRT_AREA_OFFSET || virtualAddress >= (MEMORY_MAPABLE_VIRT_AREA_OFFSET + MEMORY_MAPABLE_VIRT_AREA_SIZE))
cemu_assert_suspicious();
cemu_assert((size % MemMapper::GetPageSize()) == 0);
uint8* virtualPtr = memory_getPointerFromVirtualOffset(virtualAddress);
MemMapper::FreeMemory(virtualPtr, size, true);
osLib_returnFromFunction(hCPU, 1);
}
void InitializeMemoryMapping()
{
osLib_addFunction("coreinit", "OSGetAvailPhysAddrRange", coreinitExport_OSGetAvailPhysAddrRange);
osLib_addFunction("coreinit", "OSAllocVirtAddr", coreinitExport_OSAllocVirtAddr);
osLib_addFunction("coreinit", "OSMapMemory", coreinitExport_OSMapMemory);
osLib_addFunction("coreinit", "OSUnmapMemory", coreinitExport_OSUnmapMemory);
}
}

View file

@ -0,0 +1,5 @@
namespace coreinit
{
void InitializeMemoryMapping();
}

View file

@ -0,0 +1,132 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_MessageQueue.h"
namespace coreinit
{
SysAllocator<OSMessageQueue> g_systemMessageQueue;
SysAllocator<OSMessage, 16> _systemMessageQueueArray;
void OSInitMessageQueueEx(OSMessageQueue* msgQueue, OSMessage* msgArray, uint32 msgCount, void* userData)
{
msgQueue->magic = 'mSgQ';
msgQueue->userData = userData;
msgQueue->msgArray = msgArray;
msgQueue->msgCount = msgCount;
msgQueue->firstIndex = 0;
msgQueue->usedCount = 0;
msgQueue->ukn08 = 0;
OSInitThreadQueueEx(&msgQueue->threadQueueReceive, msgQueue);
OSInitThreadQueueEx(&msgQueue->threadQueueSend, msgQueue);
}
void OSInitMessageQueue(OSMessageQueue* msgQueue, OSMessage* msgArray, uint32 msgCount)
{
OSInitMessageQueueEx(msgQueue, msgArray, msgCount, nullptr);
}
bool OSReceiveMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags)
{
__OSLockScheduler(msgQueue);
while (msgQueue->usedCount == (uint32be)0)
{
if ((flags & OS_MESSAGE_BLOCK))
{
msgQueue->threadQueueReceive.queueAndWait(OSGetCurrentThread());
}
else
{
__OSUnlockScheduler(msgQueue);
return false;
}
}
// copy message
sint32 messageIndex = msgQueue->firstIndex;
OSMessage* readMsg = &(msgQueue->msgArray[messageIndex]);
memcpy(msg, readMsg, sizeof(OSMessage));
msgQueue->firstIndex = ((uint32)msgQueue->firstIndex + 1) % (uint32)(msgQueue->msgCount);
msgQueue->usedCount = (uint32)msgQueue->usedCount - 1;
// wake up any thread waiting to add a message
if (!msgQueue->threadQueueSend.isEmpty())
msgQueue->threadQueueSend.wakeupSingleThreadWaitQueue(true);
__OSUnlockScheduler(msgQueue);
return true;
}
bool OSPeekMessage(OSMessageQueue* msgQueue, OSMessage* msg)
{
__OSLockScheduler(msgQueue);
if ((msgQueue->usedCount == (uint32be)0))
{
__OSUnlockScheduler(msgQueue);
return false;
}
// copy message
sint32 messageIndex = msgQueue->firstIndex;
if (msg)
{
OSMessage* readMsg = &(msgQueue->msgArray[messageIndex]);
memcpy(msg, readMsg, sizeof(OSMessage));
}
__OSUnlockScheduler(msgQueue);
return true;
}
sint32 OSSendMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags)
{
__OSLockScheduler();
while (msgQueue->usedCount >= msgQueue->msgCount)
{
if ((flags & OS_MESSAGE_BLOCK))
{
msgQueue->threadQueueSend.queueAndWait(OSGetCurrentThread());
}
else
{
__OSUnlockScheduler();
return 0;
}
}
// add message
if ((flags & OS_MESSAGE_HIGH_PRIORITY))
{
// decrease firstIndex
sint32 newFirstIndex = (sint32)((sint32)msgQueue->firstIndex + (sint32)msgQueue->msgCount - 1) % (sint32)msgQueue->msgCount;
msgQueue->firstIndex = newFirstIndex;
// insert message at new first index
msgQueue->usedCount = (uint32)msgQueue->usedCount + 1;
OSMessage* newMsg = &(msgQueue->msgArray[newFirstIndex]);
memcpy(newMsg, msg, sizeof(OSMessage));
}
else
{
sint32 messageIndex = (uint32)(msgQueue->firstIndex + msgQueue->usedCount) % (uint32)msgQueue->msgCount;
msgQueue->usedCount = (uint32)msgQueue->usedCount + 1;
OSMessage* newMsg = &(msgQueue->msgArray[messageIndex]);
memcpy(newMsg, msg, sizeof(OSMessage));
}
// wake up any thread waiting to read a message
if (!msgQueue->threadQueueReceive.isEmpty())
msgQueue->threadQueueReceive.wakeupSingleThreadWaitQueue(true);
__OSUnlockScheduler();
return 1;
}
OSMessageQueue* OSGetSystemMessageQueue()
{
return g_systemMessageQueue.GetPtr();
}
void InitializeMessageQueue()
{
OSInitMessageQueue(g_systemMessageQueue.GetPtr(), _systemMessageQueueArray.GetPtr(), _systemMessageQueueArray.GetCount());
cafeExportRegister("coreinit", OSInitMessageQueueEx, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSInitMessageQueue, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSReceiveMessage, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSPeekMessage, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSSendMessage, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSGetSystemMessageQueue, LogType::CoreinitThread);
}
};

View file

@ -0,0 +1,40 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
namespace coreinit
{
struct OSMessage
{
MPTR message;
uint32 data0;
uint32 data1;
uint32 data2;
};
struct OSMessageQueue
{
/* +0x00 */ uint32be magic;
/* +0x04 */ MEMPTR<void> userData;
/* +0x08 */ uint32be ukn08;
/* +0x0C */ OSThreadQueue threadQueueSend;
/* +0x1C */ OSThreadQueue threadQueueReceive;
/* +0x2C */ MEMPTR<OSMessage> msgArray;
/* +0x30 */ uint32be msgCount;
/* +0x34 */ uint32be firstIndex;
/* +0x38 */ uint32be usedCount;
};
static_assert(sizeof(OSMessageQueue) == 0x3C);
// flags
#define OS_MESSAGE_BLOCK 1 // blocking send/receive
#define OS_MESSAGE_HIGH_PRIORITY 2 // put message in front of all queued messages
void OSInitMessageQueueEx(OSMessageQueue* msgQueue, OSMessage* msgArray, uint32 msgCount, void* userData);
void OSInitMessageQueue(OSMessageQueue* msgQueue, OSMessage* msgArray, uint32 msgCount);
bool OSReceiveMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags);
bool OSPeekMessage(OSMessageQueue* msgQueue, OSMessage* msg);
sint32 OSSendMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags);
void InitializeMessageQueue();
};

View file

@ -0,0 +1,357 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
namespace coreinit
{
/* coreinit logging and string format */
sint32 ppcSprintf(const char* formatStr, char* strOut, sint32 maxLength, PPCInterpreter_t* hCPU, sint32 initialParamIndex)
{
char tempStr[4096];
sint32 integerParamIndex = initialParamIndex;
sint32 floatParamIndex = 0;
sint32 writeIndex = 0;
while (*formatStr)
{
char c = *formatStr;
if (c == '%')
{
const char* formatStart = formatStr;
formatStr++;
if (*formatStr == '%')
{
// percent sign
if (writeIndex >= maxLength)
break;
strOut[writeIndex] = '%';
writeIndex++;
formatStr++;
continue;
}
// flags
bool flag_leftJustify = false;
bool flag_zeroPadding = false;
if (*formatStr == '-')
{
flag_leftJustify = true;
formatStr++;
}
if (*formatStr == '+')
{
// todo
formatStr++;
}
if (*formatStr == ' ')
{
// todo
formatStr++;
}
if (*formatStr == '#')
{
// todo
formatStr++;
}
if (*formatStr == '0')
{
flag_zeroPadding = true;
formatStr++;
}
// width
if (*formatStr == '*')
{
cemu_assert_debug(false);
formatStr++;
}
bool widthIsSpecified = false;
sint32 width = 0;
while (*formatStr >= '0' && *formatStr <= '9')
{
width *= 10;
width += (*formatStr - '0');
formatStr++;
widthIsSpecified = true;
}
// precision
if (*formatStr == '.')
{
formatStr++;
if (*formatStr == '*')
{
cemu_assert_debug(false);
}
while (*formatStr >= '0' && *formatStr <= '9')
{
formatStr++;
}
}
// length + specifier
char tempFormat[64];
if (*formatStr == 'X' || *formatStr == 'x' || *formatStr == 'u' || *formatStr == 'd' || *formatStr == 'p' || *formatStr == 'i' ||
(formatStr[0] == 'l' && formatStr[1] == 'd'))
{
// number
formatStr++;
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
if ((formatStr - formatStart) < sizeof(tempFormat))
tempFormat[(formatStr - formatStart)] = '\0';
else
tempFormat[sizeof(tempFormat) - 1] = '\0';
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU32(hCPU, integerParamIndex));
integerParamIndex++;
for (sint32 i = 0; i < tempLen; i++)
{
if (writeIndex >= maxLength)
break;
strOut[writeIndex] = tempStr[i];
writeIndex++;
}
}
else if (*formatStr == 's')
{
// string
formatStr++;
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
if ((formatStr - formatStart) < sizeof(tempFormat))
tempFormat[(formatStr - formatStart)] = '\0';
else
tempFormat[sizeof(tempFormat) - 1] = '\0';
MPTR strOffset = PPCInterpreter_getCallParamU32(hCPU, integerParamIndex);
sint32 tempLen = 0;
if (strOffset == MPTR_NULL)
tempLen = sprintf(tempStr, "NULL");
else
tempLen = sprintf(tempStr, tempFormat, memory_getPointerFromVirtualOffset(strOffset));
integerParamIndex++;
for (sint32 i = 0; i < tempLen; i++)
{
if (writeIndex >= maxLength)
break;
strOut[writeIndex] = tempStr[i];
writeIndex++;
}
strOut[std::min(maxLength - 1, writeIndex)] = '\0';
}
else if (*formatStr == 'f')
{
// float
formatStr++;
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
if ((formatStr - formatStart) < sizeof(tempFormat))
tempFormat[(formatStr - formatStart)] = '\0';
else
tempFormat[sizeof(tempFormat) - 1] = '\0';
sint32 tempLen = sprintf(tempStr, tempFormat, (float)hCPU->fpr[1 + floatParamIndex].fp0);
floatParamIndex++;
for (sint32 i = 0; i < tempLen; i++)
{
if (writeIndex >= maxLength)
break;
strOut[writeIndex] = tempStr[i];
writeIndex++;
}
}
else if (*formatStr == 'c')
{
// character
formatStr++;
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
if ((formatStr - formatStart) < sizeof(tempFormat))
tempFormat[(formatStr - formatStart)] = '\0';
else
tempFormat[sizeof(tempFormat) - 1] = '\0';
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU32(hCPU, integerParamIndex));
integerParamIndex++;
for (sint32 i = 0; i < tempLen; i++)
{
if (writeIndex >= maxLength)
break;
strOut[writeIndex] = tempStr[i];
writeIndex++;
}
}
else if (formatStr[0] == 'l' && formatStr[1] == 'f')
{
// double
formatStr += 2;
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
if ((formatStr - formatStart) < sizeof(tempFormat))
tempFormat[(formatStr - formatStart)] = '\0';
else
tempFormat[sizeof(tempFormat) - 1] = '\0';
sint32 tempLen = sprintf(tempStr, tempFormat, (double)hCPU->fpr[1 + floatParamIndex].fp0);
floatParamIndex++;
for (sint32 i = 0; i < tempLen; i++)
{
if (writeIndex >= maxLength)
break;
strOut[writeIndex] = tempStr[i];
writeIndex++;
}
}
else if ((formatStr[0] == 'l' && formatStr[1] == 'l' && (formatStr[2] == 'x' || formatStr[2] == 'X')))
{
formatStr += 3;
// number (64bit)
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
if ((formatStr - formatStart) < sizeof(tempFormat))
tempFormat[(formatStr - formatStart)] = '\0';
else
tempFormat[sizeof(tempFormat) - 1] = '\0';
if (integerParamIndex & 1)
integerParamIndex++;
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU64(hCPU, integerParamIndex));
integerParamIndex += 2;
for (sint32 i = 0; i < tempLen; i++)
{
if (writeIndex >= maxLength)
break;
strOut[writeIndex] = tempStr[i];
writeIndex++;
}
}
else
{
// unsupported / unknown specifier
cemu_assert_debug(false);
break;
}
}
else
{
if (writeIndex >= maxLength)
break;
strOut[writeIndex] = c;
writeIndex++;
formatStr++;
}
}
strOut[std::min(writeIndex, maxLength - 1)] = '\0';
return std::min(writeIndex, maxLength - 1);
}
sint32 __os_snprintf(char* outputStr, sint32 maxLength, const char* formatStr)
{
sint32 r = ppcSprintf(formatStr, outputStr, maxLength, ppcInterpreterCurrentInstance, 3);
return r;
}
enum class CafeLogType
{
OSCONSOLE = 0,
};
struct CafeLogBuffer
{
std::array<char, 270> lineBuffer;
size_t lineLength{};
};
CafeLogBuffer g_logBuffer_OSReport;
CafeLogBuffer& getLogBuffer(CafeLogType cafeLogType)
{
if (cafeLogType == CafeLogType::OSCONSOLE)
return g_logBuffer_OSReport;
// default to OSReport
return g_logBuffer_OSReport;
}
std::string_view getLogBufferName(CafeLogType cafeLogType)
{
if (cafeLogType == CafeLogType::OSCONSOLE)
return "OSConsole";
return "Unknown";
}
void WriteCafeConsole(CafeLogType cafeLogType, const char* msg, sint32 len)
{
// once a line is full or \n is written it will be posted to log
CafeLogBuffer& logBuffer = getLogBuffer(cafeLogType);
auto flushLine = [](CafeLogBuffer& cafeLogBuffer, std::string_view cafeLogName) -> void
{
cemuLog_log(LogType::CoreinitLogging, "[{0}] {1}", cafeLogName, std::basic_string_view(cafeLogBuffer.lineBuffer.data(), cafeLogBuffer.lineLength));
cafeLogBuffer.lineLength = 0;
};
while (len)
{
char c = *msg;
msg++;
len--;
if (c == '\r')
continue;
if (c == '\n')
{
// flush line immediately
flushLine(logBuffer, getLogBufferName(cafeLogType));
continue;
}
logBuffer.lineBuffer[logBuffer.lineLength] = c;
logBuffer.lineLength++;
if (logBuffer.lineLength >= logBuffer.lineBuffer.size())
flushLine(logBuffer, getLogBufferName(cafeLogType));
}
}
void OSReport(const char* format)
{
char buffer[1024 * 2];
sint32 len = ppcSprintf(format, buffer, sizeof(buffer), ppcInterpreterCurrentInstance, 1);
WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len);
}
void OSVReport(const char* format, MPTR vaArgs)
{
cemu_assert_unimplemented();
}
void COSWarn()
{
cemu_assert_debug(false);
}
void OSLogPrintf()
{
cemu_assert_debug(false);
}
void OSConsoleWrite(const char* strPtr, sint32 length)
{
if (length < 0)
return;
WriteCafeConsole(CafeLogType::OSCONSOLE, strPtr, length);
}
/* home button menu */
bool g_homeButtonMenuEnabled = false;
bool OSIsHomeButtonMenuEnabled()
{
return g_homeButtonMenuEnabled;
}
bool OSEnableHomeButtonMenu(bool enable)
{
g_homeButtonMenuEnabled = enable;
return true;
}
void miscInit()
{
cafeExportRegister("coreinit", __os_snprintf, LogType::Placeholder);
cafeExportRegister("coreinit", OSReport, LogType::Placeholder);
cafeExportRegister("coreinit", OSVReport, LogType::Placeholder);
cafeExportRegister("coreinit", COSWarn, LogType::Placeholder);
cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder);
cafeExportRegister("coreinit", OSConsoleWrite, LogType::Placeholder);
g_homeButtonMenuEnabled = true; // enabled by default
// Disney Infinity 2.0 actually relies on home button menu being enabled by default. If it's false it will crash due to calling erreula->IsAppearHomeNixSign() before initializing erreula
cafeExportRegister("coreinit", OSIsHomeButtonMenuEnabled, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSEnableHomeButtonMenu, LogType::CoreinitThread);
}
};

View file

@ -0,0 +1,6 @@
#pragma once
namespace coreinit
{
void miscInit();
};

View file

@ -0,0 +1,193 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/gx2/GX2.h"
#include "Cafe/HW/Latte/Core/Latte.h"
#include "Cafe/OS/libs/coreinit/coreinit_OSScreen_font.h"
#include "Cafe/OS/libs/coreinit/coreinit_OSScreen.h"
#define OSSCREEN_TV (0)
#define OSSCREEN_DRC (1)
namespace coreinit
{
struct
{
sint32 x;
sint32 y;
sint32 pitch;
}screenSizes[2] =
{
{ 1280, 720, 1280}, // TV
{ 896, 480, 896 } // DRC (values might be incorrect)
};
void* currentScreenBasePtr[2] = { 0 };
void _OSScreen_Clear(uint32 screenIndex, uint32 color)
{
if (!currentScreenBasePtr[screenIndex])
return;
uint32* output = (uint32*)currentScreenBasePtr[screenIndex];
sint32 sizeInPixels = screenSizes[screenIndex].pitch * screenSizes[screenIndex].y;
color = _swapEndianU32(color);
for (sint32 i = 0; i < sizeInPixels; i++)
{
*output = color;
output++;
}
}
void coreinitExport_OSScreenInit(PPCInterpreter_t* hCPU)
{
// todo - init VI registers?
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_OSScreenGetBufferSizeEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(screenIndex, 0);
cemu_assert(screenIndex < 2);
uint32 bufferSize = screenSizes[screenIndex].pitch * screenSizes[screenIndex].y * 4 * 2;
osLib_returnFromFunction(hCPU, bufferSize);
}
void _updateCurrentDrawScreen(sint32 screenIndex)
{
uint32 screenDataSize = screenSizes[screenIndex].pitch * screenSizes[screenIndex].y * 4;
if ((LatteGPUState.osScreen.screen[screenIndex].flipRequestCount & 1) != 0)
currentScreenBasePtr[screenIndex] = memory_getPointerFromPhysicalOffset(LatteGPUState.osScreen.screen[screenIndex].physPtr + screenDataSize);
else
currentScreenBasePtr[screenIndex] = memory_getPointerFromPhysicalOffset(LatteGPUState.osScreen.screen[screenIndex].physPtr);
}
void coreinitExport_OSScreenSetBufferEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(screenIndex, 0);
ppcDefineParamU32(buffer, 1);
cemu_assert(screenIndex < 2);
LatteGPUState.osScreen.screen[screenIndex].physPtr = buffer;
_updateCurrentDrawScreen(screenIndex);
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_OSScreenEnableEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(screenIndex, 0);
ppcDefineParamU32(isEnabled, 1);
cemu_assert(screenIndex < 2);
LatteGPUState.osScreen.screen[screenIndex].isEnabled = isEnabled != 0;
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_OSScreenClearBufferEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(screenIndex, 0);
ppcDefineParamU32(color, 1);
cemu_assert(screenIndex < 2);
_OSScreen_Clear(screenIndex, color);
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_OSScreenFlipBuffersEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(screenIndex, 0);
cemu_assert(screenIndex < 2);
forceLogDebug_printf("OSScreenFlipBuffersEx %d", screenIndex);
LatteGPUState.osScreen.screen[screenIndex].flipRequestCount++;
_updateCurrentDrawScreen(screenIndex);
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_OSScreenPutPixelEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(screenIndex, 0);
ppcDefineParamS32(x, 1);
ppcDefineParamS32(y, 2);
ppcDefineParamU32(color, 3);
if (screenIndex >= 2)
{
osLib_returnFromFunction(hCPU, 0);
return;
}
if (x >= 0 && x < screenSizes[screenIndex].x && y >= 0 && y < screenSizes[screenIndex].y)
{
*(uint32*)((uint8*)currentScreenBasePtr[screenIndex] + (x + y * screenSizes[screenIndex].pitch) * 4) = _swapEndianS32(color);
}
osLib_returnFromFunction(hCPU, 0);
}
const char* osScreenCharset = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
sint32 _getOSScreenFontCharIndex(char c)
{
const char* charset = osScreenCharset;
while (*charset)
{
if (*charset == c)
{
return (sint32)(charset - osScreenCharset);
}
charset++;
}
return -1;
}
void coreinitExport_OSScreenPutFontEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(screenIndex, 0);
ppcDefineParamS32(x, 1);
ppcDefineParamS32(y, 2);
ppcDefineParamStr(str, 3);
// characters are:
// 16 x 32 (including the margin)
// with a margin of 4 x 8
if (y < 0)
{
debug_printf("OSScreenPutFontEx: y has invalid value\n");
osLib_returnFromFunction(hCPU, 0);
return;
}
sint32 px = x * 16;
sint32 py = y * 24;
while (*str)
{
sint32 charIndex = _getOSScreenFontCharIndex(*str);
if (charIndex >= 0)
{
const uint8* charBitmap = osscreenBitmapFont + charIndex * 50;
for (sint32 fy = 0; fy < 25; fy++)
{
for (sint32 fx = 0; fx < 14; fx++)
{
if (((charBitmap[(fx / 8) + (fy) * 2] >> (7 - (fx & 7))) & 1) == 0)
continue;
*(uint32*)((uint8*)currentScreenBasePtr[screenIndex] + ((px + fx) + (py + fy) * screenSizes[screenIndex].pitch) * 4) = 0xFFFFFFFF;
}
}
}
px += 16;
str++;
}
osLib_returnFromFunction(hCPU, 0);
}
void InitializeOSScreen()
{
osLib_addFunction("coreinit", "OSScreenInit", coreinitExport_OSScreenInit);
osLib_addFunction("coreinit", "OSScreenGetBufferSizeEx", coreinitExport_OSScreenGetBufferSizeEx);
osLib_addFunction("coreinit", "OSScreenSetBufferEx", coreinitExport_OSScreenSetBufferEx);
osLib_addFunction("coreinit", "OSScreenEnableEx", coreinitExport_OSScreenEnableEx);
osLib_addFunction("coreinit", "OSScreenClearBufferEx", coreinitExport_OSScreenClearBufferEx);
osLib_addFunction("coreinit", "OSScreenFlipBuffersEx", coreinitExport_OSScreenFlipBuffersEx);
osLib_addFunction("coreinit", "OSScreenPutPixelEx", coreinitExport_OSScreenPutPixelEx);
osLib_addFunction("coreinit", "OSScreenPutFontEx", coreinitExport_OSScreenPutFontEx);
}
}

View file

@ -0,0 +1,6 @@
#pragma once
namespace coreinit
{
void InitializeOSScreen();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/common/OSCommon.h"
#include "coreinit_OverlayArena.h"
namespace coreinit
{
struct
{
bool isEnabled;
}g_coreinitOverlayArena = { 0 };
uint32 OSIsEnabledOverlayArena()
{
return g_coreinitOverlayArena.isEnabled ? 1 : 0;
}
void OSEnableOverlayArena(uint32 uknParam, uint32be* areaOffset, uint32be* areaSize)
{
if (g_coreinitOverlayArena.isEnabled == false)
{
memory_enableOverlayArena();
g_coreinitOverlayArena.isEnabled = true;
}
*areaOffset = MEMORY_OVERLAY_AREA_OFFSET;
*areaSize = MEMORY_OVERLAY_AREA_SIZE;
}
void InitializeOverlayArena()
{
cafeExportRegister("coreinit", OSIsEnabledOverlayArena, LogType::Placeholder);
cafeExportRegister("coreinit", OSEnableOverlayArena, LogType::Placeholder);
g_coreinitOverlayArena.isEnabled = false;
}
}

View file

@ -0,0 +1,4 @@
namespace coreinit
{
void InitializeOverlayArena();
};

View file

@ -0,0 +1,128 @@
#include "Cafe/OS/common/OSCommon.h"
#include "coreinit_Scheduler.h"
thread_local sint32 s_schedulerLockCount = 0;
#if BOOST_OS_WINDOWS
#include <synchapi.h>
CRITICAL_SECTION s_csSchedulerLock;
#else
#include <pthread.h>
pthread_mutex_t s_ptmSchedulerLock;
#endif
void __OSLockScheduler(void* obj)
{
#if BOOST_OS_WINDOWS
EnterCriticalSection(&s_csSchedulerLock);
#else
pthread_mutex_lock(&s_ptmSchedulerLock);
#endif
s_schedulerLockCount++;
cemu_assert_debug(s_schedulerLockCount <= 1); // >= 2 should not happen. Scheduler lock does not allow recursion
}
bool __OSHasSchedulerLock()
{
return s_schedulerLockCount > 0;
}
bool __OSTryLockScheduler(void* obj)
{
bool r;
#if BOOST_OS_WINDOWS
r = TryEnterCriticalSection(&s_csSchedulerLock);
#else
r = pthread_mutex_trylock(&s_ptmSchedulerLock) == 0;
#endif
if (r)
{
s_schedulerLockCount++;
return true;
}
return false;
}
void __OSUnlockScheduler(void* obj)
{
s_schedulerLockCount--;
cemu_assert_debug(s_schedulerLockCount >= 0);
#if BOOST_OS_WINDOWS
LeaveCriticalSection(&s_csSchedulerLock);
#else
pthread_mutex_unlock(&s_ptmSchedulerLock);
#endif
}
namespace coreinit
{
uint32 OSIsInterruptEnabled()
{
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
if (hCPU == nullptr)
return 0;
return hCPU->coreInterruptMask;
}
// disables interrupts and scheduling
uint32 OSDisableInterrupts()
{
// todo - rename SchedulerLock.cpp/h to Scheduler.cpp and move this there?
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
if (hCPU == nullptr)
return 0;
uint32 prevInterruptMask = hCPU->coreInterruptMask;
if (hCPU->coreInterruptMask != 0)
{
// we have no efficient method to turn off scheduling completely, so instead we just increase the remaining cycles
if (hCPU->remainingCycles >= 0x40000000)
{
forceLogDebug_printf("OSDisableInterrupts(): Warning - Interrupts already disabled? remCycles %08x LR %08x", hCPU->remainingCycles, hCPU->spr.LR);
}
hCPU->remainingCycles += 0x40000000;
}
hCPU->coreInterruptMask = 0;
return prevInterruptMask;
}
uint32 OSRestoreInterrupts(uint32 interruptMask)
{
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
if (hCPU == nullptr)
return 0;
uint32 prevInterruptMask = hCPU->coreInterruptMask;
if (hCPU->coreInterruptMask == 0 && interruptMask != 0)
{
hCPU->remainingCycles -= 0x40000000;
}
hCPU->coreInterruptMask = interruptMask;
return prevInterruptMask;
}
uint32 OSEnableInterrupts()
{
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
uint32 prevInterruptMask = hCPU->coreInterruptMask;
OSRestoreInterrupts(1);
return prevInterruptMask;
}
void InitializeSchedulerLock()
{
#if BOOST_OS_WINDOWS
InitializeCriticalSection(&s_csSchedulerLock);
#else
pthread_mutexattr_t ma;
pthread_mutexattr_init(&ma);
pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&s_ptmSchedulerLock, &ma);
#endif
cafeExportRegister("coreinit", __OSLockScheduler, LogType::Placeholder);
cafeExportRegister("coreinit", __OSUnlockScheduler, LogType::Placeholder);
cafeExportRegister("coreinit", OSDisableInterrupts, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSEnableInterrupts, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSRestoreInterrupts, LogType::CoreinitThread);
}
};

View file

@ -0,0 +1,16 @@
#pragma once
void __OSLockScheduler(void* obj = nullptr);
bool __OSHasSchedulerLock();
bool __OSTryLockScheduler(void* obj = nullptr);
void __OSUnlockScheduler(void* obj = nullptr);
namespace coreinit
{
uint32 OSIsInterruptEnabled();
uint32 OSDisableInterrupts();
uint32 OSRestoreInterrupts(uint32 interruptMask);
uint32 OSEnableInterrupts();
void InitializeSchedulerLock();
}

View file

@ -0,0 +1,217 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/OS/libs/coreinit/coreinit.h"
namespace coreinit
{
void __OSBoostThread(OSThread_t* thread)
{
__OSLockScheduler();
thread->stateFlags |= 0x20000;
thread->context.boostCount += 1;
__OSUpdateThreadEffectivePriority(thread); // sets thread->effectivePriority to zero since boostCount != 0
__OSUnlockScheduler();
}
void __OSDeboostThread(OSThread_t* thread)
{
__OSLockScheduler();
cemu_assert_debug(thread->context.boostCount != 0);
thread->context.boostCount -= 1;
if (thread->context.boostCount == 0)
{
thread->stateFlags &= ~0x20000;
__OSUpdateThreadEffectivePriority(thread);
// todo - reschedule if lower priority than other threads on current core?
}
__OSUnlockScheduler();
}
void OSInitSpinLock(OSSpinLock* spinlock)
{
spinlock->userData = spinlock;
spinlock->ownerThread = nullptr;
spinlock->count = 0;
spinlock->interruptMask = 1;
}
bool OSAcquireSpinLock(OSSpinLock* spinlock)
{
OSThread_t* currentThread = OSGetCurrentThread();
if (spinlock->ownerThread == currentThread)
{
spinlock->count += 1;
return true;
}
else
{
// loop until lock acquired
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
{
OSYieldThread();
}
}
__OSBoostThread(currentThread);
return true;
}
bool OSTryAcquireSpinLock(OSSpinLock* spinlock)
{
OSThread_t* currentThread = OSGetCurrentThread();
if (spinlock->ownerThread == currentThread)
{
spinlock->count += 1;
return true;
}
// try acquire once
if (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
return false;
__OSBoostThread(currentThread);
return true;
}
bool OSTryAcquireSpinLockWithTimeout(OSSpinLock* spinlock, uint64 timeout)
{
// used by CoD: Ghosts
cemu_assert_debug((timeout >> 63) == 0); // negative?
OSThread_t* currentThread = OSGetCurrentThread();
if (spinlock->ownerThread == currentThread)
{
spinlock->count += 1;
return true;
}
else
{
// loop until lock acquired or timeout occurred
uint64 timeoutValue = coreinit_getTimerTick() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout);
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
{
OSYieldThread();
if (coreinit_getTimerTick() >= timeoutValue)
{
return false;
}
}
}
__OSBoostThread(currentThread);
return true;
}
bool OSReleaseSpinLock(OSSpinLock* spinlock)
{
OSThread_t* currentThread = OSGetCurrentThread();
cemu_assert_debug(spinlock->ownerThread == currentThread);
if (spinlock->count != 0)
{
spinlock->count -= 1;
return true;
}
// release spinlock
while (!spinlock->ownerThread.atomic_compare_exchange(currentThread, nullptr));
__OSDeboostThread(currentThread);
return true;
}
bool OSUninterruptibleSpinLock_Acquire(OSSpinLock* spinlock)
{
// frequently used by VC DS
OSThread_t* currentThread = OSGetCurrentThread();
cemu_assert_debug(currentThread != nullptr);
if (spinlock->ownerThread == currentThread)
{
spinlock->count += 1;
return true;
}
else
{
// loop until lock acquired
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
{
OSYieldThread();
}
}
__OSBoostThread(currentThread);
spinlock->interruptMask = OSDisableInterrupts();
cemu_assert_debug(spinlock->ownerThread == currentThread);
return true;
}
bool OSUninterruptibleSpinLock_TryAcquire(OSSpinLock* spinlock)
{
OSThread_t* currentThread = OSGetCurrentThread();
if (spinlock->ownerThread == currentThread)
{
spinlock->count += 1;
return true;
}
// try acquire once
if (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
return false;
__OSBoostThread(currentThread);
spinlock->interruptMask = OSDisableInterrupts();
return true;
}
bool OSUninterruptibleSpinLock_TryAcquireWithTimeout(OSSpinLock* spinlock, uint64 timeout)
{
cemu_assert_debug((timeout >> 63) == 0); // negative?
OSThread_t* currentThread = OSGetCurrentThread();
if (spinlock->ownerThread == currentThread)
{
spinlock->count += 1;
return true;
}
else
{
// loop until lock acquired or timeout occurred
uint64 timeoutValue = coreinit_getTimerTick() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout);
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
{
OSYieldThread();
if (coreinit_getTimerTick() >= timeoutValue)
{
return false;
}
}
}
__OSBoostThread(currentThread);
spinlock->interruptMask = OSDisableInterrupts();
return true;
}
bool OSUninterruptibleSpinLock_Release(OSSpinLock* spinlock)
{
OSThread_t* currentThread = OSGetCurrentThread();
cemu_assert_debug(spinlock->ownerThread == currentThread);
if (spinlock->count != 0)
{
spinlock->count -= 1;
return true;
}
// release spinlock
OSRestoreInterrupts(spinlock->interruptMask);
spinlock->interruptMask = 1;
while (!spinlock->ownerThread.atomic_compare_exchange(currentThread, nullptr));
__OSDeboostThread(currentThread);
return true;
}
void InitializeSpinlock()
{
cafeExportRegister("coreinit", OSInitSpinLock, LogType::Placeholder);
cafeExportRegister("coreinit", OSAcquireSpinLock, LogType::Placeholder);
cafeExportRegister("coreinit", OSTryAcquireSpinLock, LogType::Placeholder);
cafeExportRegister("coreinit", OSTryAcquireSpinLockWithTimeout, LogType::Placeholder);
cafeExportRegister("coreinit", OSReleaseSpinLock, LogType::Placeholder);
cafeExportRegister("coreinit", OSUninterruptibleSpinLock_Acquire, LogType::Placeholder);
cafeExportRegister("coreinit", OSUninterruptibleSpinLock_TryAcquire, LogType::Placeholder);
cafeExportRegister("coreinit", OSUninterruptibleSpinLock_TryAcquireWithTimeout, LogType::Placeholder);
cafeExportRegister("coreinit", OSUninterruptibleSpinLock_Release, LogType::Placeholder);
}
#pragma endregion
}

View file

@ -0,0 +1,28 @@
#pragma once
namespace coreinit
{
struct OSSpinLock
{
/* +0x00 */ MEMPTR<struct OSThread_t> ownerThread;
/* +0x04 */ MEMPTR<void> userData;
/* +0x08 */ uint32be count;
/* +0x0C */ uint32be interruptMask;
};
static_assert(sizeof(OSSpinLock) == 0x10);
void InitializeSpinlock();
void OSInitSpinLock(OSSpinLock* spinlock);
bool OSAcquireSpinLock(OSSpinLock* spinlock);
bool OSTryAcquireSpinLock(OSSpinLock* spinlock);
bool OSTryAcquireSpinLockWithTimeout(OSSpinLock* spinlock, uint64 timeout);
bool OSReleaseSpinLock(OSSpinLock* spinlock);
bool OSUninterruptibleSpinLock_Acquire(OSSpinLock* spinlock);
bool OSUninterruptibleSpinLock_TryAcquire(OSSpinLock* spinlock);
bool OSUninterruptibleSpinLock_TryAcquireWithTimeout(OSSpinLock* spinlock, uint64 timeout);
bool OSUninterruptibleSpinLock_Release(OSSpinLock* spinlock);
}

View file

@ -0,0 +1,669 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "util/helpers/fspinlock.h"
namespace coreinit
{
/************* OSEvent ************/
void OSInitEvent(OSEvent* event, OSEvent::EVENT_STATE initialState, OSEvent::EVENT_MODE mode)
{
event->magic = 'eVnT';
cemu_assert_debug(event->magic == 0x65566e54);
event->userData = nullptr;
event->ukn08 = 0;
event->state = initialState;
event->mode = mode;
OSInitThreadQueueEx(&event->threadQueue, event);
}
void OSInitEventEx(OSEvent* event, OSEvent::EVENT_STATE initialState, OSEvent::EVENT_MODE mode, void* userData)
{
OSInitEvent(event, initialState, mode);
event->userData = userData;
}
void OSResetEvent(OSEvent* event)
{
__OSLockScheduler();
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
event->state = OSEvent::EVENT_STATE::STATE_NOT_SIGNALED;
__OSUnlockScheduler();
}
void OSWaitEventInternal(OSEvent* event)
{
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
{
if (event->mode == OSEvent::EVENT_MODE::MODE_AUTO)
event->state = OSEvent::EVENT_STATE::STATE_NOT_SIGNALED;
}
else
{
// enter wait queue
event->threadQueue.queueAndWait(OSGetCurrentThread());
}
}
void OSWaitEvent(OSEvent* event)
{
__OSLockScheduler();
OSWaitEventInternal(event);
__OSUnlockScheduler();
}
struct WaitEventWithTimeoutData
{
OSThread_t* thread;
OSThreadQueue* threadQueue;
std::atomic_bool hasTimeout;
};
void _OSWaitEventWithTimeoutHandler(uint64 currentTick, void* context)
{
cemu_assert_debug(__OSHasSchedulerLock());
WaitEventWithTimeoutData* data = (WaitEventWithTimeoutData*)context;
if (data->thread->state == OSThread_t::THREAD_STATE::STATE_WAITING)
{
data->hasTimeout = true;
data->threadQueue->cancelWait(data->thread);
}
}
uint64 coreinit_getOSTime();
bool OSWaitEventWithTimeout(OSEvent* event, uint64 timeout)
{
__OSLockScheduler();
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
{
if (event->mode == OSEvent::EVENT_MODE::MODE_AUTO)
event->state = OSEvent::EVENT_STATE::STATE_NOT_SIGNALED;
}
else
{
if (timeout == 0)
{
// fail immediately
__OSUnlockScheduler();
return false;
}
// wait and set timeout
// workaround for a bad implementation in some Unity games (like Qube Directors Cut, see FEventWiiU::Wait)
// where the the return value of OSWaitEventWithTimeout is ignored and instead the game measures the elapsed time to determine if a timeout occurred
timeout = timeout * 98ULL / 100ULL; // 98% (we want the function to return slightly before the actual timeout)
WaitEventWithTimeoutData data;
data.thread = OSGetCurrentThread();
data.threadQueue = &event->threadQueue;
data.hasTimeout = false;
auto hostAlarm = coreinit::OSHostAlarmCreate(coreinit::coreinit_getOSTime() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout), 0, _OSWaitEventWithTimeoutHandler, &data);
event->threadQueue.queueAndWait(OSGetCurrentThread());
coreinit::OSHostAlarmDestroy(hostAlarm);
if (data.hasTimeout)
{
__OSUnlockScheduler();
return false;
}
}
__OSUnlockScheduler();
return true;
}
void OSSignalEventInternal(OSEvent* event)
{
cemu_assert_debug(__OSHasSchedulerLock());
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
{
return;
}
if (event->mode == OSEvent::EVENT_MODE::MODE_AUTO)
{
// in auto mode wake up one thread or if there is none then set signaled
if (event->threadQueue.isEmpty())
event->state = OSEvent::EVENT_STATE::STATE_SIGNALED;
else
event->threadQueue.wakeupSingleThreadWaitQueue(true);
}
else
{
// in manual mode wake up all threads and set to signaled
event->state = OSEvent::EVENT_STATE::STATE_SIGNALED;
event->threadQueue.wakeupEntireWaitQueue(true);
}
}
void OSSignalEvent(OSEvent* event)
{
__OSLockScheduler();
OSSignalEventInternal(event);
__OSUnlockScheduler();
}
void OSSignalEventAllInternal(OSEvent* event)
{
if (event->state == OSEvent::EVENT_STATE::STATE_SIGNALED)
{
return;
}
if (event->mode == OSEvent::EVENT_MODE::MODE_AUTO)
{
// in auto mode wake up one thread or if there is none then set signaled
if (event->threadQueue.isEmpty())
event->state = OSEvent::EVENT_STATE::STATE_SIGNALED;
else
event->threadQueue.wakeupEntireWaitQueue(true);
}
else
{
// in manual mode wake up all threads and set to signaled
event->state = OSEvent::EVENT_STATE::STATE_SIGNALED;
event->threadQueue.wakeupEntireWaitQueue(true);
}
}
void OSSignalEventAll(OSEvent* event)
{
__OSLockScheduler();
OSSignalEventAllInternal(event);
__OSUnlockScheduler();
}
/************* OSRendezvous ************/
SysAllocator<OSEvent> g_rendezvousEvent;
void OSInitRendezvous(OSRendezvous* rendezvous)
{
__OSLockScheduler();
rendezvous->userData = rendezvous;
for (sint32 i = 0; i < PPC_CORE_COUNT; i++)
rendezvous->coreHit[i] = 0;
__OSUnlockScheduler();
}
bool OSWaitRendezvous(OSRendezvous* rendezvous, uint32 coreMask)
{
__OSLockScheduler();
rendezvous->coreHit[OSGetCoreId()] = 1;
OSSignalEventAllInternal(g_rendezvousEvent.GetPtr());
while (true)
{
bool metAll = true;
for(sint32 i=0; i<PPC_CORE_COUNT; i++)
{
if( (coreMask & (1<<i)) == 0 )
continue; // core not required by core mask
if (rendezvous->coreHit[i] == 0)
{
metAll = false;
break;
}
}
if (metAll)
break;
OSWaitEventInternal(g_rendezvousEvent.GetPtr());
}
__OSUnlockScheduler();
return true;
}
/************* OSMutex ************/
void OSInitMutexEx(OSMutex* mutex, void* userData)
{
mutex->magic = 'mUtX';
mutex->userData = userData;
mutex->ukn08 = 0;
mutex->owner = nullptr;
mutex->lockCount = 0;
OSInitThreadQueueEx(&mutex->threadQueue, mutex);
}
void OSInitMutex(OSMutex* mutex)
{
OSInitMutexEx(mutex, nullptr);
}
void OSLockMutexInternal(OSMutex* mutex)
{
OSThread_t* currentThread = OSGetCurrentThread();
int_fast32_t failedAttempts = 0;
while (true)
{
if (mutex->owner == nullptr)
{
// acquire lock
mutex->owner = currentThread;
cemu_assert_debug(mutex->lockCount == 0);
mutex->lockCount = 1;
// cemu_assert_debug(mutex->next == nullptr && mutex->prev == nullptr); -> not zero initialized
currentThread->mutexQueue.addMutex(mutex);
break;
}
else if (mutex->owner == currentThread)
{
mutex->lockCount = mutex->lockCount + 1;
break;
}
else
{
if (failedAttempts >= 0x800)
cemuLog_force("Detected long-term contested OSLockMutex");
currentThread->waitingForMutex = mutex;
mutex->threadQueue.queueAndWait(currentThread);
currentThread->waitingForMutex = nullptr;
failedAttempts++;
}
}
}
void OSLockMutex(OSMutex* mutex)
{
__OSLockScheduler();
OSTestThreadCancelInternal();
OSLockMutexInternal(mutex);
__OSUnlockScheduler();
}
bool OSTryLockMutex(OSMutex* mutex)
{
OSThread_t* currentThread = OSGetCurrentThread();
__OSLockScheduler();
OSTestThreadCancelInternal();
if (mutex->owner == nullptr)
{
// acquire lock
mutex->owner = currentThread;
cemu_assert_debug(mutex->lockCount == 0);
mutex->lockCount = 1;
// cemu_assert_debug(mutex->next == nullptr && mutex->prev == nullptr); -> not zero initialized
currentThread->mutexQueue.addMutex(mutex);
// currentThread->cancelState = currentThread->cancelState | 0x10000;
}
else if (mutex->owner == currentThread)
{
mutex->lockCount = mutex->lockCount + 1;
}
else
{
__OSUnlockScheduler();
return false;
}
__OSUnlockScheduler();
return true;
}
void OSUnlockMutexInternal(OSMutex* mutex)
{
OSThread_t* currentThread = OSGetCurrentThread();
cemu_assert_debug(mutex->owner == currentThread);
cemu_assert_debug(mutex->lockCount > 0);
mutex->lockCount = mutex->lockCount - 1;
if (mutex->lockCount == 0)
{
currentThread->mutexQueue.removeMutex(mutex);
mutex->owner = nullptr;
if (!mutex->threadQueue.isEmpty())
mutex->threadQueue.wakeupSingleThreadWaitQueue(true);
}
// currentThread->cancelState = currentThread->cancelState & ~0x10000;
}
void OSUnlockMutex(OSMutex* mutex)
{
__OSLockScheduler();
OSUnlockMutexInternal(mutex);
__OSUnlockScheduler();
}
/************* OSCond ************/
void OSInitCond(OSCond* cond)
{
cond->magic = 0x634e6456;
cond->userData = nullptr;
cond->ukn08 = 0;
OSInitThreadQueueEx(&cond->threadQueue, cond);
}
void OSInitCondEx(OSCond* cond, void* userData)
{
OSInitCond(cond);
cond->userData = userData;
}
void OSSignalCond(OSCond* cond)
{
OSWakeupThread(&cond->threadQueue);
}
void OSWaitCond(OSCond* cond, OSMutex* mutex)
{
// seen in Bayonetta 2
// releases the mutex while waiting for the condition to be signaled
__OSLockScheduler();
OSThread_t* currentThread = OSGetCurrentThread();
cemu_assert_debug(mutex->owner == currentThread);
sint32 prevLockCount = mutex->lockCount;
// unlock mutex
mutex->lockCount = 0;
currentThread->mutexQueue.removeMutex(mutex);
mutex->owner = nullptr;
if (!mutex->threadQueue.isEmpty())
mutex->threadQueue.wakeupEntireWaitQueue(false);
// wait on condition
cond->threadQueue.queueAndWait(currentThread);
// reacquire mutex
OSLockMutexInternal(mutex);
mutex->lockCount = prevLockCount;
__OSUnlockScheduler();
}
/************* OSSemaphore ************/
void OSInitSemaphoreEx(OSSemaphore* semaphore, sint32 initialCount, void* userData)
{
__OSLockScheduler();
semaphore->magic = 0x73506852;
semaphore->userData = userData;
semaphore->ukn08 = 0;
semaphore->count = initialCount;
OSInitThreadQueueEx(&semaphore->threadQueue, semaphore);
__OSUnlockScheduler();
}
void OSInitSemaphore(OSSemaphore* semaphore, sint32 initialCount)
{
OSInitSemaphoreEx(semaphore, initialCount, nullptr);
}
sint32 OSWaitSemaphoreInternal(OSSemaphore* semaphore)
{
cemu_assert_debug(__OSHasSchedulerLock());
while (true)
{
sint32 prevCount = semaphore->count;
if (prevCount > 0)
{
semaphore->count = prevCount - 1;
return prevCount;
}
semaphore->threadQueue.queueAndWait(OSGetCurrentThread());
}
}
sint32 OSWaitSemaphore(OSSemaphore* semaphore)
{
__OSLockScheduler();
sint32 r = OSWaitSemaphoreInternal(semaphore);
__OSUnlockScheduler();
return r;
}
sint32 OSTryWaitSemaphore(OSSemaphore* semaphore)
{
__OSLockScheduler();
sint32 prevCount = semaphore->count;
if (prevCount > 0)
{
semaphore->count = prevCount - 1;
}
__OSUnlockScheduler();
return prevCount;
}
sint32 OSSignalSemaphoreInternal(OSSemaphore* semaphore, bool reschedule)
{
cemu_assert_debug(__OSHasSchedulerLock());
sint32 prevCount = semaphore->count;
semaphore->count = prevCount + 1;
semaphore->threadQueue.wakeupEntireWaitQueue(reschedule);
return prevCount;
}
sint32 OSSignalSemaphore(OSSemaphore* semaphore)
{
__OSLockScheduler();
sint32 r = OSSignalSemaphoreInternal(semaphore, true);
__OSUnlockScheduler();
return r;
}
sint32 OSGetSemaphoreCount(OSSemaphore* semaphore)
{
// seen in Assassin's Creed 4
__OSLockScheduler();
sint32 currentCount = semaphore->count;
__OSUnlockScheduler();
return currentCount;
}
/************* OSFastMutex ************/
void OSFastMutex_Init(OSFastMutex* fastMutex, void* userData)
{
fastMutex->magic = 0x664d7458;
fastMutex->userData = userData;
fastMutex->owner = nullptr;
fastMutex->lockCount = 0;
fastMutex->contendedState = 0;
fastMutex->threadQueueSmall.head = nullptr;
fastMutex->threadQueueSmall.tail = nullptr;
fastMutex->ownedLink.next = nullptr;
fastMutex->ownedLink.prev = nullptr;
fastMutex->contendedLink.next = nullptr;
fastMutex->contendedLink.prev = nullptr;
}
FSpinlock g_fastMutexSpinlock;
void _OSFastMutex_AcquireContention(OSFastMutex* fastMutex)
{
g_fastMutexSpinlock.acquire();
}
void _OSFastMutex_ReleaseContention(OSFastMutex* fastMutex)
{
g_fastMutexSpinlock.release();
}
void OSFastMutex_LockInternal(OSFastMutex* fastMutex)
{
cemu_assert_debug(!__OSHasSchedulerLock());
OSThread_t* currentThread = OSGetCurrentThread();
_OSFastMutex_AcquireContention(fastMutex);
while (true)
{
if (fastMutex->owner.atomic_compare_exchange(nullptr, currentThread))//(fastMutex->owner == nullptr)
{
// acquire lock
cemu_assert_debug(fastMutex->owner == currentThread);
cemu_assert_debug(fastMutex->lockCount == 0);
fastMutex->lockCount = 1;
// todo - add to thread owned fast mutex queue
break;
}
else if (fastMutex->owner == currentThread)
{
fastMutex->lockCount = fastMutex->lockCount + 1;
break;
}
else
{
currentThread->waitingForFastMutex = fastMutex;
__OSLockScheduler();
fastMutex->threadQueueSmall.queueOnly(currentThread);
_OSFastMutex_ReleaseContention(fastMutex);
PPCCore_switchToSchedulerWithLock();
currentThread->waitingForFastMutex = nullptr;
__OSUnlockScheduler();
_OSFastMutex_AcquireContention(fastMutex);
continue;
}
}
_OSFastMutex_ReleaseContention(fastMutex);
}
void OSFastMutex_Lock(OSFastMutex* fastMutex)
{
OSFastMutex_LockInternal(fastMutex);
}
bool OSFastMutex_TryLock(OSFastMutex* fastMutex)
{
OSThread_t* currentThread = OSGetCurrentThread();
_OSFastMutex_AcquireContention(fastMutex);
if (fastMutex->owner.atomic_compare_exchange(nullptr, currentThread))
{
// acquire lock
cemu_assert_debug(fastMutex->owner == currentThread);
cemu_assert_debug(fastMutex->lockCount == 0);
fastMutex->lockCount = 1;
// todo - add to thread owned fast mutex queue
}
else if (fastMutex->owner == currentThread)
{
fastMutex->lockCount = fastMutex->lockCount + 1;
}
else
{
_OSFastMutex_ReleaseContention(fastMutex);
return false;
}
_OSFastMutex_ReleaseContention(fastMutex);
return true;
}
void OSFastMutex_UnlockInternal(OSFastMutex* fastMutex)
{
cemu_assert_debug(!__OSHasSchedulerLock());
OSThread_t* currentThread = OSGetCurrentThread();
_OSFastMutex_AcquireContention(fastMutex);
if (fastMutex->owner != currentThread)
{
// seen in Paper Mario Color Splash
//forceLog_printf("OSFastMutex_Unlock() called on mutex which is not owned by current thread");
_OSFastMutex_ReleaseContention(fastMutex);
return;
}
cemu_assert_debug(fastMutex->lockCount > 0);
fastMutex->lockCount = fastMutex->lockCount - 1;
if (fastMutex->lockCount == 0)
{
// set owner to null
if (!fastMutex->owner.atomic_compare_exchange(currentThread, nullptr))
{
cemu_assert_debug(false); // should never happen
}
if (!fastMutex->threadQueueSmall.isEmpty())
{
__OSLockScheduler();
fastMutex->threadQueueSmall.wakeupSingleThreadWaitQueue(false);
__OSUnlockScheduler();
}
}
_OSFastMutex_ReleaseContention(fastMutex);
}
void OSFastMutex_Unlock(OSFastMutex* fastMutex)
{
//__OSLockScheduler();
OSFastMutex_UnlockInternal(fastMutex);
//__OSUnlockScheduler();
}
/************* OSFastCond ************/
void OSFastCond_Init(OSFastCond* fastCond, void* userData)
{
fastCond->magic = 0x664e6456;
fastCond->userData = userData;
fastCond->ukn08 = 0;
OSInitThreadQueueEx(&fastCond->threadQueue, fastCond);
}
void OSFastCond_Wait(OSFastCond* fastCond, OSFastMutex* fastMutex)
{
// releases the mutex while waiting for the condition to be signaled
__OSLockScheduler();
cemu_assert_debug(fastMutex->owner == OSGetCurrentThread());
sint32 prevLockCount = fastMutex->lockCount;
// unlock mutex
fastMutex->lockCount = 0;
fastMutex->owner = nullptr;
if (!fastMutex->threadQueueSmall.isEmpty())
fastMutex->threadQueueSmall.wakeupEntireWaitQueue(false);
// wait on condition
fastCond->threadQueue.queueAndWait(OSGetCurrentThread());
// reacquire mutex
__OSUnlockScheduler();
OSFastMutex_LockInternal(fastMutex);
fastMutex->lockCount = prevLockCount;
}
void OSFastCond_Signal(OSFastCond* fastCond)
{
OSWakeupThread(&fastCond->threadQueue);
}
/************* init ************/
void InitializeConcurrency()
{
OSInitEvent(g_rendezvousEvent.GetPtr(), OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_AUTO);
// OSEvent
cafeExportRegister("coreinit", OSInitEvent, LogType::ThreadSync);
cafeExportRegister("coreinit", OSInitEventEx, LogType::ThreadSync);
cafeExportRegister("coreinit", OSResetEvent, LogType::ThreadSync);
cafeExportRegister("coreinit", OSWaitEvent, LogType::ThreadSync);
cafeExportRegister("coreinit", OSWaitEventWithTimeout, LogType::ThreadSync);
cafeExportRegister("coreinit", OSSignalEvent, LogType::ThreadSync);
cafeExportRegister("coreinit", OSSignalEventAll, LogType::ThreadSync);
// OSRendezvous
cafeExportRegister("coreinit", OSInitRendezvous, LogType::ThreadSync);
cafeExportRegister("coreinit", OSWaitRendezvous, LogType::ThreadSync);
// OSMutex
cafeExportRegister("coreinit", OSInitMutex, LogType::ThreadSync);
cafeExportRegister("coreinit", OSInitMutexEx, LogType::ThreadSync);
cafeExportRegister("coreinit", OSLockMutex, LogType::ThreadSync);
cafeExportRegister("coreinit", OSTryLockMutex, LogType::ThreadSync);
cafeExportRegister("coreinit", OSUnlockMutex, LogType::ThreadSync);
// OSCond
cafeExportRegister("coreinit", OSInitCond, LogType::ThreadSync);
cafeExportRegister("coreinit", OSInitCondEx, LogType::ThreadSync);
cafeExportRegister("coreinit", OSSignalCond, LogType::ThreadSync);
cafeExportRegister("coreinit", OSWaitCond, LogType::ThreadSync);
// OSSemaphore
cafeExportRegister("coreinit", OSInitSemaphore, LogType::ThreadSync);
cafeExportRegister("coreinit", OSInitSemaphoreEx, LogType::ThreadSync);
cafeExportRegister("coreinit", OSWaitSemaphore, LogType::ThreadSync);
cafeExportRegister("coreinit", OSTryWaitSemaphore, LogType::ThreadSync);
cafeExportRegister("coreinit", OSSignalSemaphore, LogType::ThreadSync);
cafeExportRegister("coreinit", OSGetSemaphoreCount, LogType::ThreadSync);
// OSFastMutex
cafeExportRegister("coreinit", OSFastMutex_Init, LogType::ThreadSync);
cafeExportRegister("coreinit", OSFastMutex_Lock, LogType::ThreadSync);
cafeExportRegister("coreinit", OSFastMutex_TryLock, LogType::ThreadSync);
cafeExportRegister("coreinit", OSFastMutex_Unlock, LogType::ThreadSync);
// OSFastCond
cafeExportRegister("coreinit", OSFastCond_Init, LogType::ThreadSync);
cafeExportRegister("coreinit", OSFastCond_Wait, LogType::ThreadSync);
cafeExportRegister("coreinit", OSFastCond_Signal, LogType::ThreadSync);
}
};

View file

@ -0,0 +1,40 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_SysHeap.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h"
namespace coreinit
{
coreinit::MEMHeapHandle _sysHeapHandle = MPTR_NULL;
sint32 _sysHeapAllocCounter = 0;
sint32 _sysHeapFreeCounter = 0;
void* OSAllocFromSystem(uint32 size, uint32 alignment)
{
_sysHeapAllocCounter++;
return coreinit::MEMAllocFromExpHeapEx(_sysHeapHandle, size, alignment);
}
void export_OSAllocFromSystem(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(size, 0);
ppcDefineParamS32(alignment, 1);
MEMPTR<void> mem = OSAllocFromSystem(size, alignment);
forceLogDebug_printf("OSAllocFromSystem(0x%x, %d) -> 0x%08x", size, alignment, mem.GetMPTR());
osLib_returnFromFunction(hCPU, mem.GetMPTR());
}
void InitSysHeap()
{
uint32 sysHeapSize = 8 * 1024 * 1024; // actual size is unknown
MEMPTR<void> heapBaseAddress = memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sysHeapSize, 0x1000));
_sysHeapHandle = coreinit::MEMCreateExpHeapEx(heapBaseAddress.GetPtr(), sysHeapSize, MEM_HEAP_OPTION_THREADSAFE);
_sysHeapAllocCounter = 0;
_sysHeapFreeCounter = 0;
}
void InitializeSysHeap()
{
osLib_addFunction("coreinit", "OSAllocFromSystem", export_OSAllocFromSystem);
}
}

View file

@ -0,0 +1,8 @@
#pragma once
namespace coreinit
{
void InitSysHeap();
void InitializeSysHeap();
}

View file

@ -0,0 +1,25 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_SystemInfo.h"
namespace coreinit
{
SysAllocator<OSSystemInfo> g_system_info;
const OSSystemInfo& OSGetSystemInfo()
{
return *g_system_info.GetPtr();
}
void InitializeSystemInfo()
{
cemu_assert(ppcCyclesSince2000 != 0);
g_system_info->busClock = ESPRESSO_BUS_CLOCK;
g_system_info->coreClock = ESPRESSO_CORE_CLOCK;
g_system_info->ticksSince2000 = ESPRESSO_CORE_CLOCK_TO_TIMER_CLOCK(ppcCyclesSince2000);
g_system_info->l2cacheSize[0] = 512*1024; // 512KB // 512KB
g_system_info->l2cacheSize[1] = 2*1024*1924; // 2MB
g_system_info->l2cacheSize[2] = 512*1024; // 512KB
g_system_info->coreClockToBusClockRatio = 5;
cafeExportRegister("coreinit", OSGetSystemInfo, LogType::Placeholder);
}
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit.h"
namespace coreinit
{
struct OSSystemInfo
{
uint32be busClock;
uint32be coreClock;
uint64be ticksSince2000;
uint32be l2cacheSize[PPC_CORE_COUNT];
uint32be coreClockToBusClockRatio;
};
static_assert(sizeof(OSSystemInfo) == 0x20);
const OSSystemInfo& OSGetSystemInfo();
void InitializeSystemInfo();
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,605 @@
#pragma once
#include "Cafe/HW/Espresso/Const.h"
#include "Cafe/OS/libs/coreinit/coreinit_Scheduler.h"
#define OS_CONTEXT_MAGIC_0 'OSCo'
#define OS_CONTEXT_MAGIC_1 'ntxt'
struct OSThread_t;
struct OSContextRegFPSCR_t
{
// FPSCR is a 32bit register but it's stored as a 64bit double
/* +0x00 */ uint32be padding;
/* +0x04 */ uint32be fpscr;
};
struct OSContext_t
{
/* +0x000 */ betype<uint32> magic0;
/* +0x004 */ betype<uint32> magic1;
/* +0x008 */ uint32 gpr[32];
/* +0x088 */ uint32 cr;
/* +0x08C */ uint32 lr;
/* +0x090 */ uint32 ctr;
/* +0x094 */ uint32 xer;
/* +0x098 */ uint32 srr0;
/* +0x09C */ uint32 srr1;
/* +0x0A0 */ uint32 dsi_dsisr;
/* +0x0A4 */ uint32 dsi_dar;
/* +0x0A8 */ uint32 ukn0A8;
/* +0x0AC */ uint32 ukn0AC;
/* +0x0B0 */ OSContextRegFPSCR_t fpscr;
/* +0x0B8 */ uint64be fp_ps0[32];
/* +0x1B8 */ uint16be boostCount;
/* +0x1BA */ uint16 state; // OS_CONTEXT_STATE_*
/* +0x1BC */ uint32 gqr[8]; // GQR/UGQR
/* +0x1DC */ uint32be upir; // set to current core index
/* +0x1E0 */ uint64be fp_ps1[32];
/* +0x2E0 */ uint64 uknTime2E0;
/* +0x2E8 */ uint64 uknTime2E8;
/* +0x2F0 */ uint64 uknTime2F0;
/* +0x2F8 */ uint64 uknTime2F8;
/* +0x300 */ uint32 error; // returned by __gh_errno_ptr() (used by socketlasterr)
/* +0x304 */ uint32be affinity;
/* +0x308 */ uint32 ukn0308;
/* +0x30C */ uint32 ukn030C;
/* +0x310 */ uint32 ukn0310;
/* +0x314 */ uint32 ukn0314;
/* +0x318 */ uint32 ukn0318;
/* +0x31C */ uint32 ukn031C;
bool checkMagic()
{
return magic0 == (uint32)OS_CONTEXT_MAGIC_0 && magic1 == (uint32)OS_CONTEXT_MAGIC_1;
}
bool hasCoreAffinitySet(uint32 coreIndex) const
{
return (((uint32)affinity >> coreIndex) & 1) != 0;
}
void setAffinity(uint32 mask)
{
affinity = mask & 7;
}
uint32 getAffinity() const
{
return affinity;
}
};
static_assert(sizeof(OSContext_t) == 0x320);
typedef struct
{
/* +0x000 | +0x3A0 */ uint32 ukn000[0x68 / 4];
/* +0x068 | +0x408 */ MEMPTR<void> eh_globals;
/* +0x06C | +0x40C */ uint32 eh_mem_manage[9]; // struct
/* +0x090 | +0x430 */ MPTR eh_store_globals[6];
/* +0x0A8 | +0x448 */ MPTR eh_store_globals_tdeh[76];
}crt_t; // size: 0x1D8
static_assert(sizeof(crt_t) == 0x1D8, "");
#pragma pack(1)
namespace coreinit
{
/********* OSThreadQueue *********/
struct OSThreadLink
{
MEMPTR<struct OSThread_t> next;
MEMPTR<struct OSThread_t> prev;
};
static_assert(sizeof(OSThreadLink) == 8);
struct OSThreadQueueInternal
{
MEMPTR<OSThread_t> head;
MEMPTR<OSThread_t> tail;
bool isEmpty() const
{
cemu_assert_debug((!head.IsNull() == !tail.IsNull()) || (head.IsNull() == tail.IsNull()));
return head.IsNull();
}
void addThread(OSThread_t* thread, OSThreadLink* threadLink);
void addThreadByPriority(OSThread_t* thread, OSThreadLink* threadLink);
void removeThread(OSThread_t* thread, OSThreadLink* threadLink);
// puts the thread on the waiting queue and changes state to WAITING
// relinquishes timeslice
// always uses thread->waitQueueLink
void queueAndWait(OSThread_t* thread);
void queueOnly(OSThread_t* thread);
// counterparts for queueAndWait
void cancelWait(OSThread_t* thread);
void wakeupEntireWaitQueue(bool reschedule);
void wakeupSingleThreadWaitQueue(bool reschedule);
private:
OSThread_t* takeFirstFromQueue(size_t linkOffset)
{
cemu_assert_debug(__OSHasSchedulerLock());
if (head == nullptr)
return nullptr;
OSThread_t* thread = head.GetPtr();
OSThreadLink* link = _getThreadLink(thread, linkOffset);
removeThread(thread, link);
return thread;
}
static size_t getLinkOffset(OSThread_t* thread, OSThreadLink* threadLink)
{
cemu_assert_debug((void*)threadLink >= (void*)thread && (void*)threadLink < (void*)((uint8*)thread + 0x680));
return (uint8*)threadLink - (uint8*)thread;
}
static OSThreadLink* _getThreadLink(OSThread_t* thread, size_t linkOffset)
{
return (OSThreadLink*)((uint8*)thread + linkOffset);
}
void _debugCheckChain(OSThread_t* thread, OSThreadLink* threadLink)
{
#ifndef PUBLIC_RELEASE
cemu_assert_debug(tail.IsNull() == head.IsNull());
size_t linkOffset = getLinkOffset(thread, threadLink);
// expects thread to be in the chain
OSThread_t* threadItr = head.GetPtr();
while (threadItr)
{
if (threadItr == thread)
return;
threadItr = _getThreadLink(threadItr, linkOffset)->next.GetPtr();
}
cemu_assert_debug(false); // thread not in list!
#endif
}
};
static_assert(sizeof(OSThreadQueueInternal) == 0x8);
struct OSThreadQueueSmall : public OSThreadQueueInternal
{
// no extra members
};
static_assert(sizeof(OSThreadQueueSmall) == 8);
static_assert(offsetof(OSThreadQueueSmall, head) == 0x0);
static_assert(offsetof(OSThreadQueueSmall, tail) == 0x4);
struct OSThreadQueue : public OSThreadQueueInternal
{
MEMPTR<void> userData;
uint32be ukn0C;
};
static_assert(sizeof(OSThreadQueue) == 0x10);
static_assert(offsetof(OSThreadQueue, head) == 0x0);
static_assert(offsetof(OSThreadQueue, tail) == 0x4);
static_assert(offsetof(OSThreadQueue, userData) == 0x8);
static_assert(offsetof(OSThreadQueue, ukn0C) == 0xC);
/********* OSMutex *********/
struct OSMutex
{
/* +0x00 */ uint32 magic;
/* +0x04 */ MEMPTR<void> userData;
/* +0x08 */ uint32be ukn08;
/* +0x0C */ OSThreadQueue threadQueue;
/* +0x1C */ MEMPTR<OSThread_t> owner;
/* +0x20 */ sint32be lockCount;
/* +0x24 */ MEMPTR<OSMutex> next;
/* +0x28 */ MEMPTR<OSMutex> prev;
}; // size: 0x2C
static_assert(sizeof(OSMutex) == 0x2C);
struct OSMutexQueue
{
MEMPTR<OSMutex> head;
MEMPTR<OSMutex> tail;
MEMPTR<void> ukn08;
uint32be ukn0C;
bool isEmpty() const
{
cemu_assert_debug((!head.IsNull() == !tail.IsNull()) || (head.IsNull() == tail.IsNull()));
return head.IsNull();
}
void addMutex(OSMutex* mutex)
{
cemu_assert_debug(__OSHasSchedulerLock());
// insert at end
if (tail.IsNull())
{
mutex->next = nullptr;
mutex->prev = nullptr;
head = mutex;
tail = mutex;
}
else
{
tail->next = mutex;
mutex->prev = tail;
mutex->next = nullptr;
tail = mutex;
}
}
void removeMutex(OSMutex* mutex)
{
cemu_assert_debug(__OSHasSchedulerLock());
cemu_assert_debug(!head.IsNull() && !tail.IsNull());
if (mutex->prev)
mutex->prev->next = mutex->next;
else
head = mutex->next;
if (mutex->next)
mutex->next->prev = mutex->prev;
else
tail = mutex->prev;
mutex->next = nullptr;
mutex->prev = nullptr;
}
OSMutex* getFirst()
{
return head.GetPtr();
}
};
static_assert(sizeof(OSMutexQueue) == 0x10);
/********* OSFastMutex *********/
struct OSFastMutexLink
{
/* +0x00 */ MEMPTR<struct OSMutex> next;
/* +0x04 */ MEMPTR<struct OSMutex> prev;
};
struct OSFastMutex
{
/* +0x00 */ uint32be magic;
/* +0x04 */ MEMPTR<void> userData;
/* +0x08 */ uint32be contendedState; // tracks current contention state
/* +0x0C */ OSThreadQueueSmall threadQueueSmall;
/* +0x14 */ OSFastMutexLink ownedLink; // part of thread->fastMutexOwnedQueue
/* +0x1C */ MEMPTR<OSThread_t> owner;
/* +0x20 */ uint32be lockCount;
/* +0x24 */ OSFastMutexLink contendedLink;
};
static_assert(sizeof(OSFastMutex) == 0x2C);
/********* OSEvent *********/
struct OSEvent
{
enum class EVENT_MODE : uint32
{
MODE_MANUAL = 0,
MODE_AUTO = 1,
};
enum class EVENT_STATE : uint32
{
STATE_NOT_SIGNALED = 0,
STATE_SIGNALED = 1
};
/* +0x00 */ uint32be magic; // 'eVnT'
/* +0x04 */ MEMPTR<void> userData;
/* +0x08 */ uint32be ukn08;
/* +0x0C */ betype<EVENT_STATE> state; // 0 -> not signaled, 1 -> signaled
/* +0x10 */ OSThreadQueue threadQueue;
/* +0x20 */ betype<EVENT_MODE> mode;
};
static_assert(sizeof(OSEvent) == 0x24);
/********* OSRendezvous *********/
struct OSRendezvous
{
/* +0x00 */ uint32be coreHit[3];
/* +0x0C */ MEMPTR<void> userData;
};
static_assert(sizeof(OSRendezvous) == 0x10);
/********* OSCond *********/
struct OSCond
{
uint32be magic;
MEMPTR<void> userData;
uint32be ukn08;
OSThreadQueue threadQueue;
};
static_assert(sizeof(OSCond) == 0x1C);
/********* OSSemaphore *********/
struct OSSemaphore
{
uint32be magic;
MEMPTR<void> userData;
uint32be ukn08;
sint32be count;
OSThreadQueue threadQueue;
};
static_assert(sizeof(OSSemaphore) == 0x20);
/********* OSFastCond *********/
struct OSFastCond
{
uint32be magic;
MEMPTR<void> userData;
uint32be ukn08;
OSThreadQueue threadQueue;
};
static_assert(sizeof(OSFastCond) == 0x1C);
};
struct OSThread_t
{
enum class THREAD_TYPE : uint32
{
TYPE_DRIVER = 0,
TYPE_IO = 1,
TYPE_APP = 2
};
enum class THREAD_STATE : uint8
{
STATE_NONE = 0,
STATE_READY = 1,
STATE_RUNNING = 2,
STATE_WAITING = 4,
STATE_MORIBUND = 8,
};
enum ATTR_BIT : uint32
{
ATTR_AFFINITY_CORE0 = 0x1,
ATTR_AFFINITY_CORE1 = 0x2,
ATTR_AFFINITY_CORE2 = 0x4,
ATTR_DETACHED = 0x8,
// more flags?
};
enum REQUEST_FLAG_BIT : uint32
{
REQUEST_FLAG_NONE = 0,
REQUEST_FLAG_SUSPEND = 1,
REQUEST_FLAG_CANCEL = 2,
};
static sint32 GetTypePriorityBase(THREAD_TYPE threadType)
{
if (threadType == OSThread_t::THREAD_TYPE::TYPE_DRIVER)
return 0;
else if (threadType == OSThread_t::THREAD_TYPE::TYPE_IO)
return 32;
else if (threadType == OSThread_t::THREAD_TYPE::TYPE_APP)
return 64;
return 0;
}
/* +0x000 */ OSContext_t context;
/* +0x320 */ uint32be magic; // 'tHrD'
/* +0x324 */ betype<THREAD_STATE> state;
/* +0x325 */ uint8 attr;
/* +0x326 */ uint16be id; // Warriors Orochi 3 uses this to identify threads. Seems like this is always set to 0x8000 ?
/* +0x328 */ betype<sint32> suspendCounter;
/* +0x32C */ sint32be effectivePriority; // effective priority (lower is higher)
/* +0x330 */ sint32be basePriority; // base priority (lower is higher)
/* +0x334 */ uint32be exitValue; // set by OSExitThread() or by returning from the thread entrypoint
/* +0x338 */ MEMPTR<coreinit::OSThreadQueue> currentRunQueue[Espresso::CORE_COUNT]; // points to the OSThreadQueue on which this thread is (null if not in queue)
/* +0x344 */ coreinit::OSThreadLink linkRun[Espresso::CORE_COUNT];
// general wait queue / thread dependency queue
/* +0x35C */ MEMPTR<coreinit::OSThreadQueueInternal> currentWaitQueue; // shared between OSThreadQueue and OSThreadQueueSmall
/* +0x360 */ coreinit::OSThreadLink waitQueueLink;
/* +0x368 */ coreinit::OSThreadQueue joinQueue;
/* +0x378 */ MEMPTR<coreinit::OSMutex> waitingForMutex; // set when thread is waiting for OSMutex to unlock
/* +0x37C */ coreinit::OSMutexQueue mutexQueue;
/* +0x38C */ coreinit::OSThreadLink activeThreadChain; // queue of active threads (g_activeThreadQueue)
/* +0x394 */ MPTR_UINT8 stackBase; // upper limit of stack
/* +0x398 */ MPTR_UINT32 stackEnd; // lower limit of stack
/* +0x39C */ MPTR entrypoint;
/* +0x3A0 */ crt_t crt;
/* +0x578 */ sint32 alarmRelatedUkn;
/* +0x57C */ std::array<MEMPTR<void>, 16> specificArray;
/* +0x5BC */ betype<THREAD_TYPE> type;
/* +0x5C0 */ MEMPTR<char> threadName;
/* +0x5C4 */ MPTR waitAlarm; // used only by OSWaitEventWithTimeout/OSSignalEvent ?
/* +0x5C8 */ uint32 userStackPointer;
/* +0x5CC */ MEMPTR<void> cleanupCallback2;
/* +0x5D0 */ //MPTR deallocator;
MEMPTR<void> deallocatorFunc;
/* +0x5D4 */ uint32 stateFlags; // 0x5D4 | various flags? Controls if canceling/suspension is allowed (at cancel points) or not? If 1 -> Cancel/Suspension not allowed, if 0 -> Cancel/Suspension allowed
/* +0x5D8 */ betype<REQUEST_FLAG_BIT> requestFlags;
/* +0x5DC */ sint32 pendingSuspend;
/* +0x5E0 */ sint32 suspendResult;
/* +0x5E4 */ coreinit::OSThreadQueue suspendQueue;
/* +0x5F4 */ uint32 _padding5F4;
/* +0x5F8 */ uint64be quantumTicks;
/* +0x600 */ uint64 coretimeSumQuantumStart;
/* +0x608 */ uint64be wakeUpCount; // number of times the thread entered running state
/* +0x610 */ uint64be totalCycles; // sum of cycles this thread was active since it was created
/* +0x618 */ uint64be wakeUpTime; // time when thread first became active (Should only be set once(?) but we currently set this on every timeslice since thats more useful for debugging)
/* +0x620 */ uint64 wakeTimeRelatedUkn1;
/* +0x628 */ uint64 wakeTimeRelatedUkn2;
// set via OSSetExceptionCallback
/* +0x630 */ MPTR ukn630Callback[Espresso::CORE_COUNT];
/* +0x63C */ MPTR ukn63CCallback[Espresso::CORE_COUNT];
/* +0x648 */ MPTR ukn648Callback[Espresso::CORE_COUNT];
/* +0x654 */ MPTR ukn654Callback[Espresso::CORE_COUNT];
/* +0x660 */ uint32 ukn660;
// TLS
/* +0x664 */ uint16 numAllocatedTLSBlocks;
/* +0x666 */ sint16 tlsStatus;
/* +0x668 */ MPTR tlsBlocksMPTR;
/* +0x66C */ MEMPTR<coreinit::OSFastMutex> waitingForFastMutex;
/* +0x670 */ coreinit::OSFastMutexLink contendedFastMutex; // link or queue?
/* +0x678 */ coreinit::OSFastMutexLink ownedFastMutex; // link or queue?
/* +0x680 */ uint32 padding680[28 / 4];
};
static_assert(sizeof(OSThread_t) == 0x6A0-4); // todo - determine correct size
namespace coreinit
{
void InitializeThread();
void InitializeConcurrency();
OSThread_t* OSGetDefaultThread(sint32 coreIndex);
void* OSGetDefaultThreadStack(sint32 coreIndex, uint32& size);
bool OSCreateThreadType(OSThread_t* thread, MPTR entryPoint, sint32 numParam, void* ptrParam, void* stackTop2, sint32 stackSize, sint32 priority, uint32 attr, OSThread_t::THREAD_TYPE threadType);
void OSCreateThreadInternal(OSThread_t* thread, uint32 entryPoint, MPTR stackLowerBaseAddr, uint32 stackSize, uint8 affinityMask, OSThread_t::THREAD_TYPE threadType);
bool OSRunThread(OSThread_t* thread, MPTR funcAddress, sint32 numParam, void* ptrParam);
void OSExitThread(sint32 exitValue);
void OSDetachThread(OSThread_t* thread);
OSThread_t* OSGetCurrentThread();
void OSSetCurrentThread(uint32 coreIndex, OSThread_t* thread);
void __OSSetThreadBasePriority(OSThread_t* thread, sint32 newPriority);
void __OSUpdateThreadEffectivePriority(OSThread_t* thread);
bool OSSetThreadPriority(OSThread_t* thread, sint32 newPriority);
uint32 OSGetThreadAffinity(OSThread_t* thread);
void OSSetThreadName(OSThread_t* thread, char* name);
char* OSGetThreadName(OSThread_t* thread);
sint32 __OSResumeThreadInternal(OSThread_t* thread, sint32 resumeCount);
sint32 OSResumeThread(OSThread_t* thread);
void OSContinueThread(OSThread_t* thread);
void __OSSuspendThreadInternal(OSThread_t* thread);
void OSSuspendThread(OSThread_t* thread);
void OSSleepThread(OSThreadQueue* threadQueue);
void OSWakeupThread(OSThreadQueue* threadQueue);
void OSTestThreadCancelInternal();
void OSYieldThread();
void OSSleepTicks(uint64 ticks);
bool OSIsThreadTerminated(OSThread_t* thread);
bool OSIsThreadSuspended(OSThread_t* thread);
// OSThreadQueue
void OSInitThreadQueue(OSThreadQueue* threadQueue);
void OSInitThreadQueueEx(OSThreadQueue* threadQueue, void* userData);
// OSEvent
void OSInitEvent(OSEvent* event, OSEvent::EVENT_STATE initialState, OSEvent::EVENT_MODE mode);
void OSInitEventEx(OSEvent* event, OSEvent::EVENT_STATE initialState, OSEvent::EVENT_MODE mode, void* userData);
void OSResetEvent(OSEvent* event);
void OSWaitEventInternal(OSEvent* event); // assumes lock is already held
void OSWaitEvent(OSEvent* event);
bool OSWaitEventWithTimeout(OSEvent* event, uint64 timeout);
void OSSignalEventInternal(OSEvent* event); // assumes lock is already held
void OSSignalEvent(OSEvent* event);
void OSSignalEventAllInternal(OSEvent* event); // assumes lock is already held
void OSSignalEventAll(OSEvent* event);
// OSRendezvous
void OSInitRendezvous(OSRendezvous* rendezvous);
bool OSWaitRendezvous(OSRendezvous* rendezvous, uint32 coreMask);
// OSMutex
void OSInitMutex(OSMutex* mutex);
void OSInitMutexEx(OSMutex* mutex, void* userData);
void OSLockMutex(OSMutex* mutex);
bool OSTryLockMutex(OSMutex* mutex);
void OSUnlockMutex(OSMutex* mutex);
void OSUnlockMutexInternal(OSMutex* mutex);
// OSCond
void OSInitCond(OSCond* cond);
void OSInitCondEx(OSCond* cond, void* userData);
void OSSignalCond(OSCond* cond);
void OSWaitCond(OSCond* cond, OSMutex* mutex);
// OSSemaphore
void OSInitSemaphore(OSSemaphore* semaphore, sint32 initialCount);
void OSInitSemaphoreEx(OSSemaphore* semaphore, sint32 initialCount, void* userData);
sint32 OSWaitSemaphoreInternal(OSSemaphore* semaphore);
sint32 OSWaitSemaphore(OSSemaphore* semaphore);
sint32 OSTryWaitSemaphore(OSSemaphore* semaphore);
sint32 OSSignalSemaphore(OSSemaphore* semaphore);
sint32 OSSignalSemaphoreInternal(OSSemaphore* semaphore, bool reschedule);
sint32 OSGetSemaphoreCount(OSSemaphore* semaphore);
// OSFastMutex
void OSFastMutex_Init(OSFastMutex* fastMutex, void* userData);
void OSFastMutex_Lock(OSFastMutex* fastMutex);
bool OSFastMutex_TryLock(OSFastMutex* fastMutex);
void OSFastMutex_Unlock(OSFastMutex* fastMutex);
// OSFastCond
void OSFastCond_Init(OSFastCond* fastCond, void* userData);
void OSFastCond_Wait(OSFastCond* fastCond, OSFastMutex* fastMutex);
void OSFastCond_Signal(OSFastCond* fastCond);
// scheduler
void OSSchedulerBegin(sint32 numCPUEmulationThreads);
void OSSchedulerEnd();
// internal
void __OSAddReadyThreadToRunQueue(OSThread_t* thread);
bool __OSCoreShouldSwitchToThread(OSThread_t* currentThread, OSThread_t* newThread);
void __OSQueueThreadDeallocation(OSThread_t* thread);
}
#pragma pack()
// deprecated / clean up required
void coreinit_suspendThread(OSThread_t* OSThreadBE, sint32 count = 1);
void coreinit_resumeThread(OSThread_t* OSThreadBE, sint32 count = 1);
MPTR coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_t* hCPU);
OSThread_t* coreinitThread_getCurrentThreadDepr(PPCInterpreter_t* hCPU);
extern MPTR activeThread[256];
extern sint32 activeThreadCount;
extern SlimRWLock srwlock_activeThreadList;

View file

@ -0,0 +1,169 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
namespace coreinit
{
// puts the thread on the waiting queue and changes state to WAITING
// relinquishes timeslice
// always uses thread->waitQueueLink
void OSThreadQueueInternal::queueAndWait(OSThread_t* thread)
{
cemu_assert_debug(__OSHasSchedulerLock());
cemu_assert_debug(thread->waitQueueLink.next == nullptr && thread->waitQueueLink.prev == nullptr);
thread->currentWaitQueue = this;
this->addThreadByPriority(thread, &thread->waitQueueLink);
cemu_assert_debug(thread->state == OSThread_t::THREAD_STATE::STATE_RUNNING);
thread->state = OSThread_t::THREAD_STATE::STATE_WAITING;
PPCCore_switchToSchedulerWithLock();
cemu_assert_debug(thread->state == OSThread_t::THREAD_STATE::STATE_RUNNING);
}
void OSThreadQueueInternal::queueOnly(OSThread_t* thread)
{
cemu_assert_debug(__OSHasSchedulerLock());
cemu_assert_debug(thread->waitQueueLink.next == nullptr && thread->waitQueueLink.prev == nullptr);
thread->currentWaitQueue = this;
this->addThreadByPriority(thread, &thread->waitQueueLink);
cemu_assert_debug(thread->state == OSThread_t::THREAD_STATE::STATE_RUNNING);
thread->state = OSThread_t::THREAD_STATE::STATE_WAITING;
}
// remove thread from wait queue and wake it up
void OSThreadQueueInternal::cancelWait(OSThread_t* thread)
{
cemu_assert_debug(__OSHasSchedulerLock());
this->removeThread(thread, &thread->waitQueueLink);
thread->state = OSThread_t::THREAD_STATE::STATE_READY;
thread->currentWaitQueue = nullptr;
coreinit::__OSAddReadyThreadToRunQueue(thread);
// todo - if waking up a thread on the same core with higher priority, reschedule
}
void OSThreadQueueInternal::addThread(OSThread_t* thread, OSThreadLink* threadLink)
{
cemu_assert_debug(__OSHasSchedulerLock());
size_t linkOffset = getLinkOffset(thread, threadLink);
// insert after tail
if (tail.IsNull())
{
threadLink->next = nullptr;
threadLink->prev = nullptr;
head = thread;
tail = thread;
}
else
{
threadLink->next = nullptr;
threadLink->prev = tail;
_getThreadLink(tail.GetPtr(), linkOffset)->next = thread;
tail = thread;
}
_debugCheckChain(thread, threadLink);
}
void OSThreadQueueInternal::addThreadByPriority(OSThread_t* thread, OSThreadLink* threadLink)
{
cemu_assert_debug(tail.IsNull() == head.IsNull()); // either must be set or none at all
cemu_assert_debug(__OSHasSchedulerLock());
size_t linkOffset = getLinkOffset(thread, threadLink);
if (tail.IsNull())
{
threadLink->next = nullptr;
threadLink->prev = nullptr;
head = thread;
tail = thread;
}
else
{
// insert towards tail based on priority
OSThread_t* threadItr = tail.GetPtr();
while (threadItr && threadItr->effectivePriority > thread->effectivePriority)
threadItr = _getThreadLink(threadItr, linkOffset)->prev.GetPtr();
if (threadItr == nullptr)
{
// insert in front
threadLink->next = head;
threadLink->prev = nullptr;
_getThreadLink(head.GetPtr(), linkOffset)->prev = thread;
head = thread;
}
else
{
threadLink->prev = threadItr;
threadLink->next = _getThreadLink(threadItr, linkOffset)->next;
if (_getThreadLink(threadItr, linkOffset)->next)
{
OSThread_t* threadAfterItr = _getThreadLink(threadItr, linkOffset)->next.GetPtr();
_getThreadLink(threadAfterItr, linkOffset)->prev = thread;
_getThreadLink(threadItr, linkOffset)->next = thread;
}
else
{
tail = thread;
_getThreadLink(threadItr, linkOffset)->next = thread;
}
}
}
_debugCheckChain(thread, threadLink);
}
void OSThreadQueueInternal::removeThread(OSThread_t* thread, OSThreadLink* threadLink)
{
cemu_assert_debug(__OSHasSchedulerLock());
size_t linkOffset = getLinkOffset(thread, threadLink);
_debugCheckChain(thread, threadLink);
if (threadLink->prev)
_getThreadLink(threadLink->prev.GetPtr(), linkOffset)->next = threadLink->next;
else
head = threadLink->next;
if (threadLink->next)
_getThreadLink(threadLink->next.GetPtr(), linkOffset)->prev = threadLink->prev;
else
tail = threadLink->prev;
threadLink->next = nullptr;
threadLink->prev = nullptr;
}
// counterpart for queueAndWait
// if reschedule is true then scheduler will switch to woken up thread (if it is runnable on the same core)
void OSThreadQueueInternal::wakeupEntireWaitQueue(bool reschedule)
{
cemu_assert_debug(__OSHasSchedulerLock());
bool shouldReschedule = false;
while (OSThread_t* thread = takeFirstFromQueue(offsetof(OSThread_t, waitQueueLink)))
{
cemu_assert_debug(thread->state == OSThread_t::THREAD_STATE::STATE_WAITING);
cemu_assert_debug(thread->suspendCounter == 0);
thread->state = OSThread_t::THREAD_STATE::STATE_READY;
thread->currentWaitQueue = nullptr;
coreinit::__OSAddReadyThreadToRunQueue(thread);
if (reschedule && thread->suspendCounter == 0 && ppcInterpreterCurrentInstance && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread))
shouldReschedule = true;
}
if (shouldReschedule)
PPCCore_switchToSchedulerWithLock();
}
// counterpart for queueAndWait
// if reschedule is true then scheduler will switch to woken up thread (if it is runnable on the same core)
void OSThreadQueueInternal::wakeupSingleThreadWaitQueue(bool reschedule)
{
cemu_assert_debug(__OSHasSchedulerLock());
OSThread_t* thread = takeFirstFromQueue(offsetof(OSThread_t, waitQueueLink));
cemu_assert_debug(thread);
bool shouldReschedule = false;
if (thread)
{
thread->state = OSThread_t::THREAD_STATE::STATE_READY;
thread->currentWaitQueue = nullptr;
coreinit::__OSAddReadyThreadToRunQueue(thread);
if (reschedule && thread->suspendCounter == 0 && ppcInterpreterCurrentInstance && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread))
shouldReschedule = true;
}
if (shouldReschedule)
PPCCore_switchToSchedulerWithLock();
}
}

View file

@ -0,0 +1,379 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
namespace coreinit
{
uint64 coreinit_getTimerTick()
{
// bus clock is 1/5th of core clock
// timer clock is 1/4th of bus clock
return PPCInterpreter_getMainCoreCycleCounter() / 20ULL;
}
uint64 coreinit_getOSTime()
{
return coreinit_getTimerTick() + ppcCyclesSince2000TimerClock;
}
void export_OSGetTick(PPCInterpreter_t* hCPU)
{
uint64 osTime = coreinit_getOSTime();
osLib_returnFromFunction(hCPU, (uint32)osTime);
}
void export_OSGetTime(PPCInterpreter_t* hCPU)
{
uint64 osTime = coreinit_getOSTime();
osLib_returnFromFunction64(hCPU, osTime);
}
__declspec(noinline) uint64 coreinit_getTimeBase_dummy()
{
return __rdtsc();
}
void export_OSGetSystemTimeDummy(PPCInterpreter_t* hCPU)
{
osLib_returnFromFunction64(hCPU, coreinit_getTimeBase_dummy());
}
void export_OSGetSystemTime(PPCInterpreter_t* hCPU)
{
osLib_returnFromFunction64(hCPU, coreinit_getTimerTick());
}
uint32 getLeapDaysUntilYear(uint32 year)
{
if (year == 0)
return 0;
return (year + 3) / 4 - (year - 1) / 100 + (year - 1) / 400;
}
bool IsLeapYear(uint32 year)
{
if (((year & 3) == 0) && (year % 100) != 0)
return true;
if ((year % 400) == 0)
return true;
return false;
}
uint32 dayToMonth[12] =
{
0,31,59,90,120,151,181,212,243,273,304,334
};
uint32 dayToMonthLeapYear[12] =
{
0,31,60,91,121,152,182,213,244,274,305,335
};
uint32 getDayInYearByYearAndMonth(uint32 year, uint32 month)
{
// Project Zero Maiden of Black Water (JPN) gives us an invalid calendar object
month %= 12; // or return 0 if too big?
if (IsLeapYear(year))
return dayToMonthLeapYear[month];
return dayToMonth[month];
}
inline const uint64 DAY_BIAS_2000 = 0xB2575;
uint64 OSCalendarTimeToTicks(OSCalendarTime_t *calendar)
{
uint32 year = calendar->year;
uint32 leapDays = getLeapDaysUntilYear(year);
uint32 startDayOfCurrentMonth = getDayInYearByYearAndMonth(year, calendar->month);
uint64 dayInYear = (startDayOfCurrentMonth + calendar->dayOfMonth) - 1;
uint64 dayCount = dayInYear + year * 365 + leapDays - DAY_BIAS_2000;
// convert date to seconds
uint64 tSeconds = 0;
tSeconds += (uint64)dayCount * 24 * 60 * 60;
tSeconds += (uint64)calendar->hour * 60 * 60;
tSeconds += (uint64)calendar->minute * 60;
tSeconds += (uint64)calendar->second;
uint64 tSubSecondTicks = 0;
tSubSecondTicks += (uint64)calendar->millisecond * ESPRESSO_TIMER_CLOCK / 1000;
tSubSecondTicks += (uint64)calendar->microsecond * ESPRESSO_TIMER_CLOCK / 1000000;
return tSeconds * ESPRESSO_TIMER_CLOCK + tSubSecondTicks;
}
void OSTicksToCalendarTime(uint64 ticks, OSCalendarTime_t* calenderStruct)
{
uint64 tSubSecondTicks = ticks % ESPRESSO_TIMER_CLOCK;
uint64 tSeconds = ticks / ESPRESSO_TIMER_CLOCK;
uint64 microsecond = tSubSecondTicks * 1000000ull / ESPRESSO_TIMER_CLOCK;
microsecond %= 1000ull;
calenderStruct->microsecond = (uint32)microsecond;
uint64 millisecond = tSubSecondTicks * 1000ull / ESPRESSO_TIMER_CLOCK;
millisecond %= 1000ull;
calenderStruct->millisecond = (uint32)millisecond;
uint64 dayOfWeek = (tSeconds/(24ull * 60 * 60) + 6ull) % 7ull;
uint64 secondOfDay = (tSeconds % (24ull * 60 * 60));
calenderStruct->dayOfWeek = (sint32)dayOfWeek;
uint64 daysSince0AD = tSeconds / (24ull * 60 * 60) + DAY_BIAS_2000;
uint32 year = (uint32)(daysSince0AD / 365ull);
uint64 yearStartDay = year * 365 + getLeapDaysUntilYear(year);
while (yearStartDay > daysSince0AD)
{
year--;
if (IsLeapYear(year))
yearStartDay -= 366;
else
yearStartDay -= 365;
}
calenderStruct->year = year;
// calculate time of day
uint32 tempSecond = (uint32)secondOfDay;
calenderStruct->second = tempSecond % 60;
tempSecond /= 60;
calenderStruct->minute = tempSecond % 60;
tempSecond /= 60;
calenderStruct->hour = tempSecond % 24;
tempSecond /= 24;
// calculate month and day
uint32 dayInYear = (uint32)(daysSince0AD - yearStartDay);
bool isLeapYear = IsLeapYear(year);
uint32 month = 0; // 0-11
uint32 dayInMonth = 0;
if (isLeapYear && dayInYear < (31+29))
{
if (dayInYear < 31)
{
// January
month = 0;
dayInMonth = dayInYear;
}
else
{
// February
month = 1;
dayInMonth = dayInYear-31;
}
}
else
{
if (isLeapYear)
dayInYear--;
dayInMonth = dayInYear;
if (dayInYear >= 334)
{
// December
month = 11;
dayInMonth -= 334;
}
else if (dayInYear >= 304)
{
// November
month = 10;
dayInMonth -= 304;
}
else if (dayInYear >= 273)
{
// October
month = 9;
dayInMonth -= 273;
}
else if (dayInYear >= 243)
{
// September
month = 8;
dayInMonth -= 243;
}
else if (dayInYear >= 212)
{
// August
month = 7;
dayInMonth -= 212;
}
else if (dayInYear >= 181)
{
// July
month = 6;
dayInMonth -= 181;
}
else if (dayInYear >= 151)
{
// June
month = 5;
dayInMonth -= 151;
}
else if (dayInYear >= 120)
{
// May
month = 4;
dayInMonth -= 120;
}
else if (dayInYear >= 90)
{
// April
month = 3;
dayInMonth -= 90;
}
else if (dayInYear >= 59)
{
// March
month = 2;
dayInMonth -= 59;
}
else if (dayInYear >= 31)
{
// February
month = 1;
dayInMonth -= 31;
}
else
{
// January
month = 0;
dayInMonth -= 0;
}
}
calenderStruct->dayOfYear = dayInYear;
calenderStruct->month = month;
calenderStruct->dayOfMonth = dayInMonth + 1;
}
uint32 getDaysInMonth(uint32 year, uint32 month)
{
switch (month)
{
case 0: // January
return 31;
case 1: // February
return IsLeapYear(year) ? 29 : 28;
case 2: // March
return 31;
case 3: // April
return 30;
case 4: // May
return 31;
case 5: // June
return 30;
case 6: // July
return 31;
case 7: // August
return 31;
case 8: // September
return 30;
case 9: // October
return 31;
case 10: // November
return 30;
case 11: // December
return 31;
default:
cemu_assert(false);
}
return 31;
}
void verifyDateMatch(OSCalendarTime_t* ct1, OSCalendarTime_t* ct2)
{
sint64 m1 = ct1->millisecond * 1000 + ct1->microsecond;
sint64 m2 = ct2->millisecond * 1000 + ct2->microsecond;
sint64 microDif = std::abs(m1 - m2);
if (ct1->year != ct2->year ||
ct1->month != ct2->month ||
ct1->dayOfMonth != ct2->dayOfMonth ||
ct1->hour != ct2->hour ||
ct1->minute != ct2->minute ||
ct1->second != ct2->second ||
microDif > 1ull)
{
debug_printf("Mismatch\n");
assert_dbg();
}
}
void timeTest()
{
sint32 iterCount = 0;
OSCalendarTime_t ct{};
ct.year = 2000;
ct.month = 1;
ct.dayOfMonth = 1;
ct.hour = 1;
ct.minute = 1;
ct.second = 1;
ct.millisecond = 123;
ct.microsecond = 321;
while (true)
{
iterCount++;
uint64 ticks = OSCalendarTimeToTicks(&ct);
// make sure converting it back results in the same date
OSCalendarTime_t ctTemp;
OSTicksToCalendarTime(ticks, &ctTemp);
verifyDateMatch(&ct, &ctTemp);
// add a day
ticks += 24ull * 60 * 60 * ESPRESSO_TIMER_CLOCK;
OSCalendarTime_t ctOutput;
OSTicksToCalendarTime(ticks, &ctOutput);
OSCalendarTime_t ctExpected;
ctExpected = ct;
// add a day manually
sint32 daysInMonth = getDaysInMonth(ctExpected.year, ctExpected.month);
ctExpected.dayOfMonth = ctExpected.dayOfMonth + 1;
if (ctExpected.dayOfMonth >= daysInMonth+1)
{
ctExpected.dayOfMonth = 1;
ctExpected.month = ctExpected.month + 1;
if (ctExpected.month > 11)
{
ctExpected.month = 0;
ctExpected.year = ctExpected.year + 1;
}
}
verifyDateMatch(&ctExpected, &ctOutput);
ct = ctOutput;
}
}
void InitializeTimeAndCalendar()
{
osLib_addFunction("coreinit", "OSGetTime", export_OSGetTime);
osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTimeDummy); // register dummy HLE function to get Cemuhook to patch our dummy instead of the real function
osLib_addFunction("coreinit", "OSGetTick", export_OSGetTick);
cafeExportRegister("coreinit", OSTicksToCalendarTime, LogType::Placeholder);
cafeExportRegister("coreinit", OSCalendarTimeToTicks, LogType::Placeholder);
osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTime);
//timeTest();
}
};

View file

@ -0,0 +1,58 @@
#pragma once
#include "Cafe/HW/Espresso/Const.h"
namespace coreinit
{
typedef struct
{
/* +0x00 */ sint32be second; // 0-59
/* +0x04 */ sint32be minute; // 0-59
/* +0x08 */ sint32be hour; // 0-23
/* +0x0C */ sint32be dayOfMonth; // 1-31
/* +0x10 */ sint32be month; // 0-11
/* +0x14 */ sint32be year; // 2000-...
/* +0x18 */ sint32be dayOfWeek; // 0-6
/* +0x1C */ sint32be dayOfYear; // 0-365
/* +0x20 */ sint32be millisecond; // 0-999
/* +0x24 */ sint32be microsecond; // 0-999
}OSCalendarTime_t;
static_assert(sizeof(OSCalendarTime_t) == 0x28);
namespace EspressoTime
{
typedef sint64 TimerTicks;
constexpr sint64 GetCoreClock()
{
return Espresso::CORE_CLOCK;
}
constexpr sint64 GetBusClock()
{
return Espresso::BUS_CLOCK;
}
constexpr sint64 GetTimerClock()
{
return Espresso::TIMER_CLOCK;
}
inline TimerTicks ConvertNsToTimerTicks(uint64 ns)
{
return ((GetTimerClock() / 31250LL) * ((ns)) / 32000LL);
}
};
void OSTicksToCalendarTime(uint64 ticks, OSCalendarTime_t* calenderStruct);
uint64 coreinit_getOSTime();
uint64 coreinit_getTimerTick();
static uint64 OSGetSystemTime()
{
return coreinit_getTimerTick();
}
void InitializeTimeAndCalendar();
};

View file

@ -0,0 +1,119 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
#define DMAE_ENDIAN_NONE 0
#define DMAE_ENDIAN_16 1
#define DMAE_ENDIAN_32 2
#define DMAE_ENDIAN_64 3
uint64 dmaeRetiredTimestamp = 0;
uint64 dmae_getTimestamp()
{
return coreinit::coreinit_getTimerTick();
}
void dmae_setRetiredTimestamp(uint64 timestamp)
{
dmaeRetiredTimestamp = timestamp;
}
void dmaeExport_DMAECopyMem(PPCInterpreter_t* hCPU)
{
if( hCPU->gpr[6] == DMAE_ENDIAN_NONE )
{
// don't change endianness
memcpy(memory_getPointerFromVirtualOffset(hCPU->gpr[3]), memory_getPointerFromVirtualOffset(hCPU->gpr[4]), hCPU->gpr[5]*4);
}
else if( hCPU->gpr[6] == DMAE_ENDIAN_32 )
{
// swap per uint32
uint32* srcBuffer = (uint32*)memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
uint32* dstBuffer = (uint32*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
for(uint32 i=0; i<hCPU->gpr[5]; i++)
{
dstBuffer[i] = _swapEndianU32(srcBuffer[i]);
}
}
else
{
cemuLog_logDebug(LogType::Force, "DMAECopyMem(): Unsupported endian swap\n");
}
uint64 dmaeTimestamp = dmae_getTimestamp();
dmae_setRetiredTimestamp(dmaeTimestamp);
if(hCPU->gpr[5] > 0)
LatteBufferCache_notifyDCFlush(hCPU->gpr[3], hCPU->gpr[5]*4);
osLib_returnFromFunction64(hCPU, dmaeTimestamp);
}
void dmaeExport_DMAEFillMem(PPCInterpreter_t* hCPU)
{
uint32* dstBuffer = (uint32*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint32 value = hCPU->gpr[4];
uint32 numU32s = hCPU->gpr[5];
value = _swapEndianU32(value);
for(uint32 i=0; i<numU32s; i++)
{
*dstBuffer = value;
dstBuffer++;
}
uint64 dmaeTimestamp = dmae_getTimestamp();
dmae_setRetiredTimestamp(dmaeTimestamp);
osLib_returnFromFunction64(hCPU, dmaeTimestamp);
}
void dmaeExport_DMAEWaitDone(PPCInterpreter_t* hCPU)
{
//debug_printf("DMAEWaitDone(...)\n");
// parameter:
// r3/r4 uint64 dmaeTimestamp
osLib_returnFromFunction(hCPU, 1);
}
void dmaeExport_DMAESemaphore(PPCInterpreter_t* hCPU)
{
// parameter:
// r3 MPTR addr
// r4 uint32 actionType
uint32 actionType = hCPU->gpr[4];
std::atomic<uint64le>* semaphore = _rawPtrToAtomic((uint64le*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]));
if( actionType == 1 )
{
// Signal Semaphore
semaphore->fetch_add(1);
}
else if (actionType == 0) // wait
{
forceLogDebug_printf("DMAESemaphore: Unsupported wait operation");
semaphore->fetch_sub(1);
}
else
{
forceLogDebug_printf("DMAESemaphore unknown action type %d", actionType);
cemu_assert_debug(false);
}
uint64 dmaeTimestamp = dmae_getTimestamp();
dmae_setRetiredTimestamp(dmaeTimestamp);
osLib_returnFromFunction64(hCPU, dmaeTimestamp);
}
void dmaeExport_DMAEGetRetiredTimeStamp(PPCInterpreter_t* hCPU)
{
debug_printf("DMAEGetRetiredTimeStamp()\n");
osLib_returnFromFunction64(hCPU, dmaeRetiredTimestamp);
}
void dmae_load()
{
osLib_addFunction("dmae", "DMAECopyMem", dmaeExport_DMAECopyMem);
osLib_addFunction("dmae", "DMAEFillMem", dmaeExport_DMAEFillMem);
osLib_addFunction("dmae", "DMAEWaitDone", dmaeExport_DMAEWaitDone);
osLib_addFunction("dmae", "DMAESemaphore", dmaeExport_DMAESemaphore);
osLib_addFunction("dmae", "DMAEGetRetiredTimeStamp", dmaeExport_DMAEGetRetiredTimeStamp);
}

View file

@ -0,0 +1 @@
void dmae_load();

View file

@ -0,0 +1,16 @@
#include "Cafe/OS/common/OSCommon.h"
#include "drmapp.h"
namespace drmapp
{
uint32 NupChkIsFinished(uint32 ukn)
{
forceLogDebug_printf("drmapp.NupChkIsFinished() - placeholder");
return 1;
}
void Initialize()
{
cafeExportRegisterFunc(NupChkIsFinished, "drmapp", "NupChkIsFinished__3RplFv", LogType::Placeholder);
}
}

View file

@ -0,0 +1,5 @@
namespace drmapp
{
void Initialize();
}

View file

@ -0,0 +1,396 @@
#include "Cafe/OS/common/OSCommon.h"
#include "erreula.h"
#include "Cafe/HW/Latte/Renderer/Renderer.h"
#include "util/helpers/helpers.h"
#include <imgui.h>
#include "imgui/imgui_extension.h"
#include <wx/msgdlg.h>
#include "Cafe/OS/libs/coreinit/coreinit_FS.h"
#include "Cafe/OS/libs/vpad/vpad.h"
namespace nn
{
namespace erreula
{
#define RESULTTYPE_NONE 0
#define RESULTTYPE_FINISH 1
#define RESULTTYPE_NEXT 2
#define RESULTTYPE_JUMP 3
#define RESULTTYPE_PASSWORD 4
#define ERRORTYPE_CODE 0
#define ERRORTYPE_TEXT 1
#define ERRORTYPE_TEXT_ONE_BUTTON 2
#define ERRORTYPE_TEXT_TWO_BUTTON 3
#define ERREULA_STATE_HIDDEN 0
#define ERREULA_STATE_APPEARING 1
#define ERREULA_STATE_VISIBLE 2
#define ERREULA_STATE_DISAPPEARING 3
struct AppearArg_t
{
AppearArg_t() = default;
AppearArg_t(const AppearArg_t& o)
{
errorType = o.errorType;
screenType = o.screenType;
controllerType = o.controllerType;
holdType = o.holdType;
errorCode = o.errorCode;
framerate = o.framerate;
text = o.text;
button1Text = o.button1Text;
button2Text = o.button2Text;
title = o.title;
drawCursor = o.drawCursor;
}
uint32be errorType;
uint32be screenType;
uint32be controllerType;
uint32be holdType;
uint32be errorCode;
uint32be framerate;
MEMPTR<uint16be> text;
MEMPTR<uint16be> button1Text;
MEMPTR<uint16be> button2Text;
MEMPTR<uint16be> title;
uint8 padding[3];
bool drawCursor{};
};
static_assert(sizeof(AppearArg_t) == 0x2C); // maybe larger
struct HomeNixSignArg_t
{
uint32be framerate;
};
static_assert(sizeof(HomeNixSignArg_t) == 0x4); // maybe larger
struct ControllerInfo_t
{
MEMPTR<VPADStatus_t> vpadStatus;
MEMPTR<KPADStatus_t> kpadStatus[4]; // or 7 now like KPAD_MAX_CONTROLLERS?
};
static_assert(sizeof(ControllerInfo_t) == 0x14); // maybe larger
struct ErrEula_t
{
coreinit::OSMutex mutex;
uint32 regionType;
uint32 langType;
MEMPTR<coreinit::FSClient_t> fsClient;
AppearArg_t currentDialog;
uint32 state;
bool buttonPressed;
bool rightButtonPressed;
bool homeNixSignVisible;
std::chrono::steady_clock::time_point stateTimer{};
} g_errEula = {};
std::wstring GetText(uint16be* text)
{
std::wstringstream result;
while(*text != 0)
{
auto c = (uint16)*text;
result << static_cast<wchar_t>(c);
text++;
}
return result.str();
}
void export_ErrEulaCreate(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(thisptr, uint8, 0);
ppcDefineParamU32(regionType, 1);
ppcDefineParamU32(langType, 2);
ppcDefineParamMEMPTR(fsClient, coreinit::FSClient_t, 3);
coreinit::OSLockMutex(&g_errEula.mutex);
g_errEula.regionType = regionType;
g_errEula.langType = langType;
g_errEula.fsClient = fsClient;
coreinit::OSUnlockMutex(&g_errEula.mutex);
osLib_returnFromFunction(hCPU, 0);
}
void export_AppearHomeNixSign(PPCInterpreter_t* hCPU)
{
g_errEula.homeNixSignVisible = TRUE;
osLib_returnFromFunction(hCPU, 0);
}
void export_AppearError(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(arg, AppearArg_t, 0);
g_errEula.currentDialog = *arg.GetPtr();
g_errEula.state = ERREULA_STATE_APPEARING;
g_errEula.buttonPressed = false;
g_errEula.rightButtonPressed = false;
g_errEula.stateTimer = tick_cached();
osLib_returnFromFunction(hCPU, 0);
}
void export_GetStateErrorViewer(PPCInterpreter_t* hCPU)
{
osLib_returnFromFunction(hCPU, g_errEula.state);
}
void export_DisappearError(PPCInterpreter_t* hCPU)
{
g_errEula.state = ERREULA_STATE_HIDDEN;
osLib_returnFromFunction(hCPU, 0);
}
void export_ChangeLang(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(langType, 0);
g_errEula.langType = langType;
osLib_returnFromFunction(hCPU, 0);
}
void export_IsDecideSelectButtonError(PPCInterpreter_t* hCPU)
{
if (g_errEula.buttonPressed)
forceLogDebug_printf("IsDecideSelectButtonError: TRUE");
osLib_returnFromFunction(hCPU, g_errEula.buttonPressed);
}
void export_IsDecideSelectLeftButtonError(PPCInterpreter_t* hCPU)
{
if (g_errEula.buttonPressed)
forceLogDebug_printf("IsDecideSelectLeftButtonError: TRUE");
osLib_returnFromFunction(hCPU, g_errEula.buttonPressed);
}
void export_IsDecideSelectRightButtonError(PPCInterpreter_t* hCPU)
{
if (g_errEula.rightButtonPressed)
forceLogDebug_printf("IsDecideSelectRightButtonError: TRUE");
osLib_returnFromFunction(hCPU, g_errEula.rightButtonPressed);
}
void export_IsAppearHomeNixSign(PPCInterpreter_t* hCPU)
{
osLib_returnFromFunction(hCPU, g_errEula.homeNixSignVisible);
}
void export_DisappearHomeNixSign(PPCInterpreter_t* hCPU)
{
g_errEula.homeNixSignVisible = FALSE;
osLib_returnFromFunction(hCPU, 0);
}
void export_GetResultType(PPCInterpreter_t* hCPU)
{
uint32 result = RESULTTYPE_NONE;
if (g_errEula.buttonPressed || g_errEula.rightButtonPressed)
{
forceLogDebug_printf("GetResultType: FINISH");
result = RESULTTYPE_FINISH;
}
osLib_returnFromFunction(hCPU, result);
}
void export_Calc(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(controllerInfo, ControllerInfo_t, 0);
// TODO: check controller buttons bla to accept dialog?
osLib_returnFromFunction(hCPU, 0);
}
void render(bool mainWindow)
{
if(g_errEula.state == ERREULA_STATE_HIDDEN)
return;
if(g_errEula.state == ERREULA_STATE_APPEARING)
{
if(std::chrono::duration_cast<std::chrono::milliseconds>(tick_cached() - g_errEula.stateTimer).count() <= 1000)
{
return;
}
g_errEula.state = ERREULA_STATE_VISIBLE;
g_errEula.stateTimer = tick_cached();
}
/*else if(g_errEula.state == STATE_VISIBLE)
{
if (std::chrono::duration_cast<std::chrono::milliseconds>(tick_cached() - g_errEula.stateTimer).count() >= 1000)
{
g_errEula.state = STATE_DISAPPEARING;
g_errEula.stateTimer = tick_cached();
return;
}
}*/
else if(g_errEula.state == ERREULA_STATE_DISAPPEARING)
{
if (std::chrono::duration_cast<std::chrono::milliseconds>(tick_cached() - g_errEula.stateTimer).count() >= 2000)
{
g_errEula.state = ERREULA_STATE_HIDDEN;
g_errEula.stateTimer = tick_cached();
}
return;
}
const AppearArg_t& appearArg = g_errEula.currentDialog;
std::string text;
const uint32 errorCode = (uint32)appearArg.errorCode;
if (errorCode != 0)
{
const uint32 errorCodeHigh = errorCode / 10000;
const uint32 errorCodeLow = errorCode % 10000;
text = fmt::format("Error-Code: {:03}-{:04}\n", errorCodeHigh, errorCodeLow);
}
auto font = ImGui_GetFont(32.0f);
if (!font)
return;
const auto kPopupFlags = ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings;
auto& io = ImGui::GetIO();
ImVec2 position = { io.DisplaySize.x / 2.0f, io.DisplaySize.y / 2.0f };
ImVec2 pivot = { 0.5f, 0.5f };
ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot);
ImGui::SetNextWindowBgAlpha(0.9f);
ImGui::PushFont(font);
std::string title = "ErrEula";
if (appearArg.title)
title = boost::nowide::narrow(GetText(appearArg.title.GetPtr()));
if (ImGui::Begin(title.c_str(), nullptr, kPopupFlags))
{
const float startx = ImGui::GetWindowSize().x / 2.0f;
switch ((uint32)appearArg.errorType)
{
default:
{
// TODO layout based on error code
ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size());
ImGui::Spacing();
ImGui::SetCursorPosX(startx - 50);
g_errEula.buttonPressed |= ImGui::Button("OK", {100, 0});
break;
}
case ERRORTYPE_TEXT:
{
std::string txtTmp = "Unknown Error";
if (appearArg.text)
txtTmp = boost::nowide::narrow(GetText(appearArg.text.GetPtr()));
text += txtTmp;
ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size());
ImGui::Spacing();
ImGui::SetCursorPosX(startx - 50);
g_errEula.buttonPressed |= ImGui::Button("OK", { 100, 0 });
break;
}
case ERRORTYPE_TEXT_ONE_BUTTON:
{
std::string txtTmp = "Unknown Error";
if (appearArg.text)
txtTmp = boost::nowide::narrow(GetText(appearArg.text.GetPtr()));
text += txtTmp;
ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size());
ImGui::Spacing();
std::string button1 = "Yes";
if (appearArg.button1Text)
button1 = boost::nowide::narrow(GetText(appearArg.button1Text.GetPtr()));
float width = std::max(100.0f, ImGui::CalcTextSize(button1.c_str()).x + 10.0f);
ImGui::SetCursorPosX(startx - (width / 2.0f));
g_errEula.buttonPressed |= ImGui::Button(button1.c_str(), { width, 0 });
break;
}
case ERRORTYPE_TEXT_TWO_BUTTON:
{
std::string txtTmp = "Unknown Error";
if (appearArg.text)
txtTmp = boost::nowide::narrow(GetText(appearArg.text.GetPtr()));
text += txtTmp;
ImGui::TextUnformatted(text.c_str(), text.c_str() + text.size());
ImGui::Spacing();
std::string button1 = "Yes";
if (appearArg.button1Text)
button1 = boost::nowide::narrow(GetText(appearArg.button1Text.GetPtr()));
std::string button2 = "No";
if (appearArg.button2Text)
button2 = boost::nowide::narrow(GetText(appearArg.button2Text.GetPtr()));
float width1 = std::max(100.0f, ImGui::CalcTextSize(button1.c_str()).x + 10.0f);
float width2 = std::max(100.0f, ImGui::CalcTextSize(button2.c_str()).x + 10.0f);
ImGui::SetCursorPosX(startx - (width1 / 2.0f) - (width2 / 2.0f) - 10);
g_errEula.buttonPressed |= ImGui::Button(button1.c_str(), { width1, 0 });
ImGui::SameLine();
g_errEula.rightButtonPressed |= ImGui::Button(button2.c_str(), { width2, 0 });
break;
}
}
ImGui::End();
}
ImGui::PopFont();
if(g_errEula.buttonPressed || g_errEula.rightButtonPressed)
{
g_errEula.state = ERREULA_STATE_DISAPPEARING;
g_errEula.stateTimer = tick_cached();
}
}
void load()
{
OSInitMutexEx(&g_errEula.mutex, nullptr);
//osLib_addFunction("erreula", "ErrEulaCreate__3RplFPUcQ3_2nn7erreula10", export_ErrEulaCreate); // copy ctor?
osLib_addFunction("erreula", "ErrEulaCreate__3RplFPUcQ3_2nn7erreula10RegionTypeQ3_2nn7erreula8LangTypeP8FSClient", export_ErrEulaCreate);
osLib_addFunction("erreula", "ErrEulaAppearHomeNixSign__3RplFRCQ3_2nn7erreula14HomeNixSignArg", export_AppearHomeNixSign);
osLib_addFunction("erreula", "ErrEulaAppearError__3RplFRCQ3_2nn7erreula9AppearArg", export_AppearError);
osLib_addFunction("erreula", "ErrEulaGetStateErrorViewer__3RplFv", export_GetStateErrorViewer);
osLib_addFunction("erreula", "ErrEulaChangeLang__3RplFQ3_2nn7erreula8LangType", export_ChangeLang);
osLib_addFunction("erreula", "ErrEulaIsDecideSelectButtonError__3RplFv", export_IsDecideSelectButtonError);
osLib_addFunction("erreula", "ErrEulaCalc__3RplFRCQ3_2nn7erreula14ControllerInfo", export_Calc);
osLib_addFunction("erreula", "ErrEulaIsDecideSelectLeftButtonError__3RplFv", export_IsDecideSelectLeftButtonError);
osLib_addFunction("erreula", "ErrEulaIsDecideSelectRightButtonError__3RplFv", export_IsDecideSelectRightButtonError);
osLib_addFunction("erreula", "ErrEulaIsAppearHomeNixSign__3RplFv", export_IsAppearHomeNixSign);
osLib_addFunction("erreula", "ErrEulaDisappearHomeNixSign__3RplFv", export_DisappearHomeNixSign);
osLib_addFunction("erreula", "ErrEulaGetResultType__3RplFv", export_GetResultType);
osLib_addFunction("erreula", "ErrEulaDisappearError__3RplFv", export_DisappearError);
}
}
}

View file

@ -0,0 +1,12 @@
#pragma once
namespace nn
{
namespace erreula
{
void render(bool mainWindow);
void load();
}
}

View file

@ -0,0 +1,472 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/HW/Latte/ISA/RegDefines.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
#include "GX2.h"
#include "Cafe/HW/Latte/Core/Latte.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/CafeSystem.h"
#include "Cafe/HW/Latte/Core/LattePM4.h"
#include "GX2_Command.h"
#include "GX2_State.h"
#include "GX2_Memory.h"
#include "GX2_Event.h"
#include "GX2_Shader.h"
#include "GX2_Blit.h"
#include "GX2_Draw.h"
#include "GX2_Query.h"
#include "GX2_Misc.h"
#include "GX2_Surface.h"
#include "GX2_Surface_Copy.h"
#include "GX2_Texture.h"
#define GX2_TV_RENDER_NONE 0
#define GX2_TV_RENDER_480 1
#define GX2_TV_RENDER_480_WIDE 2
#define GX2_TV_RENDER_720 3
#define GX2_TV_RENDER_720I 4
#define GX2_TV_RENDER_1080 5
#define GX2_TV_RENDER_COUNT 6
struct
{
sint32 width;
sint32 height;
}tvScanBufferResolutions[GX2_TV_RENDER_COUNT] = {
0,0,
640,480,
854,480,
1280,720,
1280,720,
1920,1080
};
uint64 lastSwapTime = 0;
void gx2Export_GX2SwapScanBuffers(PPCInterpreter_t* hCPU)
{
gx2Log_printf("GX2SwapScanBuffers()");
bool isPokken = false;
uint64 titleId = CafeSystem::GetForegroundTitleId();
if (titleId == 0x00050000101DF500ull || titleId == 0x00050000101C5800ull || titleId == 0x00050000101DF400ull)
isPokken = true;
if (isPokken)
GX2::GX2DrawDone();
GX2ReserveCmdSpace(5+2);
uint64 tick64 = PPCInterpreter_getMainCoreCycleCounter() / 20ULL;
lastSwapTime = tick64;
// count flip request
// is this updated via a PM4 MEM_WRITE operation?
// Orochi Warriors seems to call GX2SwapScanBuffers on arbitrary threads/cores. The PM4 commands should go through to the GPU as long as there is no active display list and no other core is submitting commands simultaneously
// right now, we work around this by avoiding the infinite loop below (request counter incremented, but PM4 not sent)
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
if (GX2::sGX2MainCoreIndex == coreIndex)
LatteGPUState.sharedArea->flipRequestCountBE = _swapEndianU32(_swapEndianU32(LatteGPUState.sharedArea->flipRequestCountBE) + 1);
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_REQUEST_SWAP_BUFFERS, 1));
gx2WriteGather_submitU32AsBE(0); // reserved
// swap frames
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_TRIGGER_SCANBUFFER_SWAP, 1));
gx2WriteGather_submitU32AsBE(0); // reserved
// wait for flip if the CPU is too far ahead
// doing it after swap request is how the actual console does it, but that still causes issues in Pokken
while ((sint32)(_swapEndianU32(LatteGPUState.sharedArea->flipRequestCountBE) - _swapEndianU32(LatteGPUState.sharedArea->flipExecuteCountBE)) > 5)
{
GX2::GX2WaitForFlip();
}
GX2::GX2WriteGather_checkAndInsertWrapAroundMark();
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2CopyColorBufferToScanBuffer(PPCInterpreter_t* hCPU)
{
gx2Log_printf("GX2CopyColorBufferToScanBuffer(0x%08x,%d)\n", hCPU->gpr[3], hCPU->gpr[4]);
GX2ReserveCmdSpace(5);
// todo: proper implementation
// hack: Avoid running to far ahead of GPU. Normally this would be guaranteed by the circular buffer model, which we currently dont fully emulate
if(GX2::GX2WriteGather_getReadWriteDistance() > 32*1024*1024 )
{
debug_printf("Waiting for GPU to catch up...\n");
PPCInterpreter_relinquishTimeslice(); // release current thread
return;
}
GX2ColorBuffer* colorBuffer = (GX2ColorBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER, 9));
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(colorBuffer->surface.imagePtr));
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.width);
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.height);
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.pitch);
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.tileMode.value());
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.swizzle);
gx2WriteGather_submitU32AsBE(_swapEndianU32(colorBuffer->viewFirstSlice));
gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.format.value());
gx2WriteGather_submitU32AsBE(hCPU->gpr[4]);
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2WaitForFreeScanBuffer(PPCInterpreter_t* hCPU)
{
// todo: proper implementation
debug_printf("GX2WaitForFreeScanBuffer(): Unimplemented\n");
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2GetCurrentScanBuffer(PPCInterpreter_t* hCPU)
{
// todo: proper implementation
uint32 scanTarget = hCPU->gpr[3];
GX2ColorBuffer* colorBufferBE = (GX2ColorBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
memset(colorBufferBE, 0x00, sizeof(GX2ColorBuffer));
colorBufferBE->surface.width = 100;
colorBufferBE->surface.height = 100;
// note: For now we abuse the tiling aperture memory area as framebuffer pointers
if( scanTarget == GX2_SCAN_TARGET_TV )
{
colorBufferBE->surface.imagePtr = MEMORY_TILINGAPERTURE_AREA_ADDR+0x200000;
}
else if( scanTarget == GX2_SCAN_TARGET_DRC_FIRST )
{
colorBufferBE->surface.imagePtr = MEMORY_TILINGAPERTURE_AREA_ADDR+0x40000;
}
osLib_returnFromFunction(hCPU, 0);
}
void coreinitExport_GX2GetSystemTVScanMode(PPCInterpreter_t* hCPU)
{
// 1080p = 7
osLib_returnFromFunction(hCPU, 7);
}
void coreinitExport_GX2GetSystemTVAspectRatio(PPCInterpreter_t* hCPU)
{
osLib_returnFromFunction(hCPU, 1); // 16:9
}
void gx2Export_GX2TempGetGPUVersion(PPCInterpreter_t* hCPU)
{
osLib_returnFromFunction(hCPU, 2);
}
void _GX2InitScanBuffer(GX2ColorBuffer* colorBuffer, sint32 width, sint32 height, Latte::E_GX2SURFFMT format)
{
colorBuffer->surface.resFlag = GX2_RESFLAG_USAGE_TEXTURE | GX2_RESFLAG_USAGE_COLOR_BUFFER;
colorBuffer->surface.width = width;
colorBuffer->surface.height = height;
colorBuffer->viewFirstSlice = _swapEndianU32(0);
colorBuffer->viewNumSlices = _swapEndianU32(1);
colorBuffer->viewMip = _swapEndianU32(0);
colorBuffer->surface.numLevels = 1;
colorBuffer->surface.dim = Latte::E_DIM::DIM_2D;
colorBuffer->surface.swizzle = 0;
colorBuffer->surface.depth = 1;
colorBuffer->surface.tileMode = Latte::E_GX2TILEMODE::TM_LINEAR_GENERAL;
colorBuffer->surface.format = format;
colorBuffer->surface.mipPtr = MPTR_NULL;
colorBuffer->surface.aa = 0;
GX2::GX2CalcSurfaceSizeAndAlignment(&colorBuffer->surface);
colorBuffer->surface.resFlag = GX2_RESFLAG_USAGE_TEXTURE | GX2_RESFLAG_USAGE_COLOR_BUFFER | GX2_RESFLAG_USAGE_SCAN_BUFFER;
}
void gx2Export_GX2CalcTVSize(PPCInterpreter_t* hCPU)
{
uint32 tvRenderMode = hCPU->gpr[3];
Latte::E_GX2SURFFMT format = (Latte::E_GX2SURFFMT)hCPU->gpr[4];
uint32 bufferingMode = hCPU->gpr[5];
uint32 outputSizeMPTR = hCPU->gpr[6];
uint32 outputScaleNeededMPTR = hCPU->gpr[7];
cemu_assert(tvRenderMode < GX2_TV_RENDER_COUNT);
uint32 width = tvScanBufferResolutions[tvRenderMode].width;
uint32 height = tvScanBufferResolutions[tvRenderMode].height;
GX2ColorBuffer colorBuffer;
memset(&colorBuffer, 0, sizeof(GX2ColorBuffer));
_GX2InitScanBuffer(&colorBuffer, width, height, format);
uint32 imageSize = colorBuffer.surface.imageSize;
uint32 alignment = colorBuffer.surface.alignment;
uint32 alignmentPaddingSize = (alignment - (imageSize%alignment)) % alignment;
uint32 uknMult = 1; // probably for interlaced?
if (tvRenderMode == GX2_TV_RENDER_720I)
uknMult = 2;
uint32 adjustedBufferingMode = bufferingMode;
if (tvRenderMode < GX2_TV_RENDER_720)
adjustedBufferingMode = 4;
uint32 bufferedImageSize = (imageSize + alignmentPaddingSize) * adjustedBufferingMode;
bufferedImageSize = bufferedImageSize * uknMult - alignmentPaddingSize;
memory_writeU32(outputSizeMPTR, bufferedImageSize);
memory_writeU32(outputScaleNeededMPTR, 0); // todo
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2CalcDRCSize(PPCInterpreter_t* hCPU)
{
ppcDefineParamS32(drcMode, 0);
ppcDefineParamU32(format, 1);
ppcDefineParamU32(bufferingMode, 2);
ppcDefineParamMPTR(sizeMPTR, 3);
ppcDefineParamMPTR(scaleNeededMPTR, 4);
uint32 width = 0;
uint32 height = 0;
if (drcMode > 0)
{
width = 854;
height = 480;
}
GX2ColorBuffer colorBuffer = {};
memset(&colorBuffer, 0, sizeof(colorBuffer));
_GX2InitScanBuffer(&colorBuffer, width, height, (Latte::E_GX2SURFFMT)format);
uint32 imageSize = colorBuffer.surface.imageSize;
uint32 alignment = colorBuffer.surface.alignment;
uint32 alignmentPaddingSize = (alignment - (imageSize%alignment)) % alignment;
uint32 adjustedBufferingMode = bufferingMode;
uint32 bufferedImageSize = (imageSize + alignmentPaddingSize) * adjustedBufferingMode;
bufferedImageSize = bufferedImageSize - alignmentPaddingSize;
memory_writeU32(sizeMPTR, bufferedImageSize);
memory_writeU32(scaleNeededMPTR, 0);
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2SetDRCScale(PPCInterpreter_t* hCPU)
{
gx2Log_printf("GX2SetDRCScale(%d,%d)", hCPU->gpr[3], hCPU->gpr[4]);
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2SetDRCConnectCallback(PPCInterpreter_t* hCPU)
{
ppcDefineParamS32(channel, 0);
ppcDefineParamMEMPTR(callback, void, 1);
gx2Log_printf("GX2SetDRCConnectCallback(%d, 0x%08x)", channel, callback.GetMPTR());
if(callback.GetPtr())
PPCCoreCallback(callback, channel, TRUE);
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2SetSemaphore(PPCInterpreter_t* hCPU)
{
gx2Log_printf("GX2SetSemaphore(0x%08x,%d)", hCPU->gpr[3], hCPU->gpr[4]);
ppcDefineParamMPTR(semaphoreMPTR, 0);
ppcDefineParamS32(mode, 1);
uint32 SEM_SEL;
if (mode == 0)
{
// wait
SEM_SEL = 7;
}
else if (mode == 1)
{
// signal
SEM_SEL = 6;
}
else
{
cemu_assert_debug(false);
osLib_returnFromFunction(hCPU, 0);
return;
}
uint32 semaphoreControl = (SEM_SEL << 29);
semaphoreControl |= 0x1000; // WAIT_ON_SIGNAL
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_MEM_SEMAPHORE, 2));
gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(semaphoreMPTR)); // semaphore physical address
gx2WriteGather_submitU32AsBE(semaphoreControl); // control
osLib_returnFromFunction(hCPU, 0);
}
void gx2Export_GX2Flush(PPCInterpreter_t* hCPU)
{
gx2Log_printf("GX2Flush()");
_GX2SubmitToTCL();
osLib_returnFromFunction(hCPU, 0);
}
uint8* _GX2LastFlushPtr[PPC_CORE_COUNT] = {NULL};
uint64 _prevReturnedGPUTime = 0;
uint64 Latte_GetTime()
{
uint64 gpuTime = coreinit::coreinit_getTimerTick();
gpuTime *= 20000ULL;
if (gpuTime <= _prevReturnedGPUTime)
gpuTime = _prevReturnedGPUTime + 1; // avoid ever returning identical timestamps
_prevReturnedGPUTime = gpuTime;
return gpuTime;
}
void _GX2SubmitToTCL()
{
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
// do nothing if called from non-main GX2 core
if (GX2::sGX2MainCoreIndex != coreIndex)
{
forceLogDebug_printf("_GX2SubmitToTCL() called on non-main GX2 core");
return;
}
if( gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL )
return; // quit if in display list
_GX2LastFlushPtr[coreIndex] = (gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex]);
// update last submitted CB timestamp
uint64 commandBufferTimestamp = Latte_GetTime();
LatteGPUState.lastSubmittedCommandBufferTimestamp.store(commandBufferTimestamp);
gx2Log_printf("Submitting GX2 command buffer with timestamp %016I64x", commandBufferTimestamp);
// submit HLE packet to write retirement timestamp
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_SET_CB_RETIREMENT_TIMESTAMP, 2));
gx2WriteGather_submitU32AsBE((uint32)(commandBufferTimestamp>>32ULL));
gx2WriteGather_submitU32AsBE((uint32)(commandBufferTimestamp&0xFFFFFFFFULL));
}
uint32 _GX2GetUnflushedBytes(uint32 coreIndex)
{
uint32 unflushedBytes = 0;
if (_GX2LastFlushPtr[coreIndex] != NULL)
{
if (_GX2LastFlushPtr[coreIndex] > gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex])
unflushedBytes = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - gx2WriteGatherPipe.gxRingBuffer + 4); // this isn't 100% correct since we ignore the bytes between the last flush address and the start of the wrap around
else
unflushedBytes = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - _GX2LastFlushPtr[coreIndex]);
}
else
unflushedBytes = (uint32)(gx2WriteGatherPipe.writeGatherPtrGxBuffer[coreIndex] - gx2WriteGatherPipe.gxRingBuffer);
return unflushedBytes;
}
/*
* Guarantees that the requested amount of space is available on the current command buffer
* If the space is not available, the current command buffer is pushed to the GPU and a new one is allocated
*/
void GX2ReserveCmdSpace(uint32 reservedFreeSpaceInU32)
{
uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance);
// if we are in a display list then do nothing
if( gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL )
return;
uint32 unflushedBytes = _GX2GetUnflushedBytes(coreIndex);
if( unflushedBytes >= 0x1000 )
{
_GX2SubmitToTCL();
}
}
void gx2_load()
{
osLib_addFunction("gx2", "GX2GetContextStateDisplayList", gx2Export_GX2GetContextStateDisplayList);
// swap, vsync & timing
osLib_addFunction("gx2", "GX2SwapScanBuffers", gx2Export_GX2SwapScanBuffers);
osLib_addFunction("gx2", "GX2GetSwapStatus", gx2Export_GX2GetSwapStatus);
osLib_addFunction("gx2", "GX2CopyColorBufferToScanBuffer", gx2Export_GX2CopyColorBufferToScanBuffer);
osLib_addFunction("gx2", "GX2WaitForFreeScanBuffer", gx2Export_GX2WaitForFreeScanBuffer);
osLib_addFunction("gx2", "GX2GetCurrentScanBuffer", gx2Export_GX2GetCurrentScanBuffer);
// shader stuff
osLib_addFunction("gx2", "GX2GetVertexShaderGPRs", gx2Export_GX2GetVertexShaderGPRs);
osLib_addFunction("gx2", "GX2GetVertexShaderStackEntries", gx2Export_GX2GetVertexShaderStackEntries);
osLib_addFunction("gx2", "GX2GetPixelShaderGPRs", gx2Export_GX2GetPixelShaderGPRs);
osLib_addFunction("gx2", "GX2GetPixelShaderStackEntries", gx2Export_GX2GetPixelShaderStackEntries);
osLib_addFunction("gx2", "GX2SetFetchShader", gx2Export_GX2SetFetchShader);
osLib_addFunction("gx2", "GX2SetVertexShader", gx2Export_GX2SetVertexShader);
osLib_addFunction("gx2", "GX2SetPixelShader", gx2Export_GX2SetPixelShader);
osLib_addFunction("gx2", "GX2SetGeometryShader", gx2Export_GX2SetGeometryShader);
osLib_addFunction("gx2", "GX2SetComputeShader", gx2Export_GX2SetComputeShader);
osLib_addFunction("gx2", "GX2SetVertexUniformReg", gx2Export_GX2SetVertexUniformReg);
osLib_addFunction("gx2", "GX2SetVertexUniformBlock", gx2Export_GX2SetVertexUniformBlock);
osLib_addFunction("gx2", "GX2RSetVertexUniformBlock", gx2Export_GX2RSetVertexUniformBlock);
osLib_addFunction("gx2", "GX2SetPixelUniformBlock", gx2Export_GX2SetPixelUniformBlock);
osLib_addFunction("gx2", "GX2SetPixelUniformReg", gx2Export_GX2SetPixelUniformReg);
osLib_addFunction("gx2", "GX2SetGeometryUniformBlock", gx2Export_GX2SetGeometryUniformBlock);
osLib_addFunction("gx2", "GX2SetShaderModeEx", gx2Export_GX2SetShaderModeEx);
osLib_addFunction("gx2", "GX2CalcGeometryShaderInputRingBufferSize", gx2Export_GX2CalcGeometryShaderInputRingBufferSize);
osLib_addFunction("gx2", "GX2CalcGeometryShaderOutputRingBufferSize", gx2Export_GX2CalcGeometryShaderOutputRingBufferSize);
// color/depth buffers
osLib_addFunction("gx2", "GX2InitColorBufferRegs", gx2Export_GX2InitColorBufferRegs);
osLib_addFunction("gx2", "GX2InitDepthBufferRegs", gx2Export_GX2InitDepthBufferRegs);
osLib_addFunction("gx2", "GX2SetColorBuffer", gx2Export_GX2SetColorBuffer);
osLib_addFunction("gx2", "GX2SetDepthBuffer", gx2Export_GX2SetDepthBuffer);
osLib_addFunction("gx2", "GX2SetDRCBuffer", gx2Export_GX2SetDRCBuffer);
osLib_addFunction("gx2", "GX2MarkScanBufferCopied", gx2Export_GX2MarkScanBufferCopied);
// misc
osLib_addFunction("gx2", "GX2TempGetGPUVersion", gx2Export_GX2TempGetGPUVersion);
osLib_addFunction("gx2", "GX2CalcTVSize", gx2Export_GX2CalcTVSize);
osLib_addFunction("gx2", "GX2CalcDRCSize", gx2Export_GX2CalcDRCSize);
osLib_addFunction("gx2", "GX2SetDRCScale", gx2Export_GX2SetDRCScale);
osLib_addFunction("gx2", "GX2SetDRCConnectCallback", gx2Export_GX2SetDRCConnectCallback);
osLib_addFunction("gx2", "GX2GetSystemTVScanMode", coreinitExport_GX2GetSystemTVScanMode);
osLib_addFunction("gx2", "GX2GetSystemTVAspectRatio", coreinitExport_GX2GetSystemTVAspectRatio);
osLib_addFunction("gx2", "GX2SetSwapInterval", gx2Export_GX2SetSwapInterval);
osLib_addFunction("gx2", "GX2GetSwapInterval", gx2Export_GX2GetSwapInterval);
osLib_addFunction("gx2", "GX2GetGPUTimeout", gx2Export_GX2GetGPUTimeout);
osLib_addFunction("gx2", "GX2SampleTopGPUCycle", gx2Export_GX2SampleTopGPUCycle);
osLib_addFunction("gx2", "GX2SampleBottomGPUCycle", gx2Export_GX2SampleBottomGPUCycle);
osLib_addFunction("gx2", "GX2AllocateTilingApertureEx", gx2Export_GX2AllocateTilingApertureEx);
osLib_addFunction("gx2", "GX2FreeTilingAperture", gx2Export_GX2FreeTilingAperture);
// context state
osLib_addFunction("gx2", "GX2SetDefaultState", gx2Export_GX2SetDefaultState);
osLib_addFunction("gx2", "GX2SetupContextStateEx", gx2Export_GX2SetupContextStateEx);
osLib_addFunction("gx2", "GX2SetContextState", gx2Export_GX2SetContextState);
// semaphore
osLib_addFunction("gx2", "GX2SetSemaphore", gx2Export_GX2SetSemaphore);
// command buffer
osLib_addFunction("gx2", "GX2Flush", gx2Export_GX2Flush);
GX2::GX2Init_writeGather();
GX2::GX2MemInit();
GX2::GX2ResourceInit();
GX2::GX2CommandInit();
GX2::GX2SurfaceInit();
GX2::GX2SurfaceCopyInit();
GX2::GX2TextureInit();
GX2::GX2StateInit();
GX2::GX2ShaderInit();
GX2::GX2EventInit();
GX2::GX2BlitInit();
GX2::GX2DrawInit();
GX2::GX2StreamoutInit();
GX2::GX2QueryInit();
GX2::GX2MiscInit();
}

Some files were not shown because too many files have changed in this diff Show more