HLE: Make calling mips funcs simpler.

This makes their return value handling, scheduling, etc. more
straight-forward.
This commit is contained in:
Unknown W. Brackets 2020-03-21 16:09:23 -07:00
parent a70f00ca8e
commit 54e1afda1e
9 changed files with 234 additions and 32 deletions

View file

@ -59,6 +59,8 @@ enum
HLE_AFTER_DEBUG_BREAK = 0x20,
// Don't fill temp regs with 0xDEADBEEF.
HLE_AFTER_SKIP_DEADBEEF = 0x40,
// Execute pending mips calls.
HLE_AFTER_QUEUED_CALLS = 0x80,
};
static std::vector<HLEModule> moduleDB;
@ -68,6 +70,33 @@ static const char *hleAfterSyscallReschedReason;
static const HLEFunction *latestSyscall = nullptr;
static int idleOp;
struct HLEMipsCallInfo {
u32 func;
PSPAction *action;
std::vector<u32> args;
};
struct HLEMipsCallStack {
u32_le nextOff;
union {
struct {
u32_le func;
u32_le actionIndex;
u32_le argc;
};
struct {
u32_le ra;
u32_le v0;
u32_le v1;
};
};
};
// No need to save state, always flushed at a syscall end.
static std::vector<HLEMipsCallInfo> enqueuedMipsCalls;
// Does need to be saved, referenced by the stack and owned.
static std::vector<PSPAction *> mipsCallActions;
void hleDelayResultFinish(u64 userdata, int cycleslate)
{
u32 error;
@ -86,16 +115,14 @@ void hleDelayResultFinish(u64 userdata, int cycleslate)
WARN_LOG(HLE, "Someone else woke up HLE-blocked thread?");
}
void HLEInit()
{
void HLEInit() {
RegisterAllModules();
delayedResultEvent = CoreTiming::RegisterEvent("HLEDelayedResult", hleDelayResultFinish);
idleOp = GetSyscallOp("FakeSysCalls", NID_IDLE);
}
void HLEDoState(PointerWrap &p)
{
auto s = p.Section("HLE", 1);
void HLEDoState(PointerWrap &p) {
auto s = p.Section("HLE", 1, 2);
if (!s)
return;
@ -103,13 +130,35 @@ void HLEDoState(PointerWrap &p)
latestSyscall = nullptr;
p.Do(delayedResultEvent);
CoreTiming::RestoreRegisterEvent(delayedResultEvent, "HLEDelayedResult", hleDelayResultFinish);
if (s >= 2) {
int actions = (int)mipsCallActions.size();
p.Do(actions);
if (actions != (int)mipsCallActions.size()) {
mipsCallActions.resize(actions);
}
for (auto &action : mipsCallActions) {
int actionTypeID = action != nullptr ? action->actionTypeID : -1;
p.Do(actionTypeID);
if (actionTypeID != -1) {
if (p.mode == p.MODE_READ)
action = __KernelCreateAction(actionTypeID);
action->DoState(p);
}
}
}
}
void HLEShutdown()
{
void HLEShutdown() {
hleAfterSyscall = HLE_AFTER_NOTHING;
latestSyscall = nullptr;
moduleDB.clear();
enqueuedMipsCalls.clear();
for (auto p : mipsCallActions) {
delete p;
}
mipsCallActions.clear();
}
void RegisterModule(const char *name, int numFunctions, const HLEFunction *funcTable)
@ -354,6 +403,136 @@ bool hleIsKernelMode() {
return latestSyscall && (latestSyscall->flags & HLE_KERNEL_SYSCALL) != 0;
}
void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction) {
std::vector<u32> args;
args.resize(argc);
memcpy(args.data(), argv, argc * sizeof(u32));
enqueuedMipsCalls.push_back({ func, afterAction, args });
hleAfterSyscall |= HLE_AFTER_QUEUED_CALLS;
}
void hleFlushCalls() {
u32 &sp = currentMIPS->r[MIPS_REG_SP];
PSPPointer<HLEMipsCallStack> stackData;
VERBOSE_LOG(HLE, "Flushing %d HLE mips calls from %s, sp=%08x", (int)enqueuedMipsCalls.size(), latestSyscall ? latestSyscall->name : "?", sp);
// First, we'll add a marker for the final return.
sp -= sizeof(HLEMipsCallStack);
stackData.ptr = sp;
stackData->nextOff = 0xFFFFFFFF;
stackData->ra = currentMIPS->pc;
stackData->v0 = currentMIPS->r[MIPS_REG_V0];
stackData->v1 = currentMIPS->r[MIPS_REG_V1];
// Now we'll set up the first in the chain.
currentMIPS->pc = enqueuedMipsCalls[0].func;
currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress();
for (int i = 0; i < (int)enqueuedMipsCalls[0].args.size(); i++) {
currentMIPS->r[MIPS_REG_A0 + i] = enqueuedMipsCalls[0].args[i];
}
// For stack info, process the first enqueued call last, so we run it first.
// We don't actually need to store 0's args, but keep it consistent.
for (int i = (int)enqueuedMipsCalls.size() - 1; i >= 0; --i) {
auto &info = enqueuedMipsCalls[i];
u32 stackRequired = (int)info.args.size() * sizeof(u32) + sizeof(HLEMipsCallStack);
u32 stackAligned = (stackRequired + 0xF) & ~0xF;
sp -= stackAligned;
stackData.ptr = sp;
stackData->nextOff = stackAligned;
stackData->func = info.func;
if (info.action) {
stackData->actionIndex = (int)mipsCallActions.size();
mipsCallActions.push_back(info.action);
} else {
stackData->actionIndex = 0xFFFFFFFF;
}
stackData->argc = (int)info.args.size();
for (int j = 0; j < (int)info.args.size(); ++j) {
Memory::Write_U32(info.args[j], sp + sizeof(HLEMipsCallStack) + j * sizeof(u32));
}
}
enqueuedMipsCalls.clear();
DEBUG_LOG(HLE, "Executing HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp);
}
void HLEReturnFromMipsCall() {
u32 &sp = currentMIPS->r[MIPS_REG_SP];
PSPPointer<HLEMipsCallStack> stackData;
// At this point, we may have another mips call to run, or be at the end...
stackData.ptr = sp;
if ((stackData->nextOff & 0x0000000F) != 0 || !Memory::IsValidAddress(sp + stackData->nextOff)) {
ERROR_LOG(HLE, "Corrupt stack on HLE mips call return: %08x", stackData->nextOff);
Core_UpdateState(CORE_ERROR);
return;
}
if (stackData->actionIndex != 0xFFFFFFFF && stackData->actionIndex < (u32)mipsCallActions.size()) {
PSPAction *&action = mipsCallActions[stackData->actionIndex];
VERBOSE_LOG(HLE, "Executing action for HLE mips call at %08x, sp=%08x", stackData->func, sp);
// Search for the saved v0/v1 values, to preserve the PSPAction API...
PSPPointer<HLEMipsCallStack> finalMarker = stackData;
while ((finalMarker->nextOff & 0x0000000F) == 0 && Memory::IsValidAddress(finalMarker.ptr + finalMarker->nextOff)) {
finalMarker.ptr += finalMarker->nextOff;
}
if (finalMarker->nextOff != 0xFFFFFFFF) {
ERROR_LOG(HLE, "Corrupt stack on HLE mips call return action: %08x", finalMarker->nextOff);
Core_UpdateState(CORE_ERROR);
return;
}
MipsCall mc;
mc.savedV0 = finalMarker->v0;
mc.savedV1 = finalMarker->v1;
action->run(mc);
finalMarker->v0 = mc.savedV0;
finalMarker->v1 = mc.savedV1;
delete action;
action = nullptr;
// Note: the action could actually enqueue more, adding another layer on stack after this.
}
sp += stackData->nextOff;
stackData.ptr = sp;
if (stackData->nextOff == 0xFFFFFFFF) {
// We're done. Grab the HLE result's v0/v1 and return from the syscall.
currentMIPS->pc = stackData->ra;
currentMIPS->r[MIPS_REG_V0] = stackData->v0;
currentMIPS->r[MIPS_REG_V1] = stackData->v1;
sp += sizeof(HLEMipsCallStack);
bool canClear = true;
for (auto p : mipsCallActions) {
canClear = canClear && p == nullptr;
}
if (canClear) {
mipsCallActions.clear();
}
VERBOSE_LOG(HLE, "Finished HLE mips calls, v0=%08x, sp=%08x", currentMIPS->r[MIPS_REG_V0], sp);
return;
}
// Alright, we have another to call.
DEBUG_LOG(HLE, "Executing HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp);
currentMIPS->pc = stackData->func;
currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress();
for (int i = 0; i < (int)stackData->argc; i++) {
currentMIPS->r[MIPS_REG_A0 + i] = Memory::Read_U32(sp + sizeof(HLEMipsCallStack) + i * sizeof(u32));
}
}
const static u32 deadbeefRegs[12] = {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF};
inline static void SetDeadbeefRegs()
{
@ -375,6 +554,8 @@ inline void hleFinishSyscall(const HLEFunction &info)
if ((hleAfterSyscall & HLE_AFTER_SKIP_DEADBEEF) == 0)
SetDeadbeefRegs();
if ((hleAfterSyscall & HLE_AFTER_QUEUED_CALLS) != 0)
hleFlushCalls();
if ((hleAfterSyscall & HLE_AFTER_CURRENT_CALLBACKS) != 0 && (hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) == 0)
__KernelForceCallbacks();

View file

@ -24,6 +24,7 @@
#include "Core/MIPS/MIPS.h"
class PointerWrap;
class PSPAction;
typedef void (* HLEFunc)();
enum {
@ -116,6 +117,8 @@ void hleSkipDeadbeef();
void hleSetSteppingTime(double t);
// Check if the current syscall context is kernel.
bool hleIsKernelMode();
// Enqueue a MIPS function to be called after this HLE call finishes.
void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction = nullptr);
// Delays the result for usec microseconds, allowing other threads to run during this time.
u32 hleDelayResult(u32 result, const char *reason, int usec);
@ -144,6 +147,8 @@ void CallSyscall(MIPSOpcode op);
void WriteFuncStub(u32 stubAddr, u32 symAddr);
void WriteFuncMissingStub(u32 stubAddr, u32 nid);
void HLEReturnFromMipsCall();
const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op);
// For jit, takes arg: const HLEFunction *
void *GetQuickSyscallFunc(MIPSOpcode op);

View file

@ -96,6 +96,7 @@ const HLEFunction FakeSysCalls[] = {
{NID_MODULERETURN, __KernelReturnFromModuleFunc, "__KernelReturnFromModuleFunc"},
{NID_IDLE, __KernelIdle, "_sceKernelIdle"},
{NID_GPUREPLAY, __KernelGPUReplay, "__KernelGPUReplay"},
{NID_HLECALLRETURN, HLEReturnFromMipsCall, "HLEReturnFromMipsCall"},
};
const HLEFunction UtilsForUser[] =

View file

@ -22,6 +22,7 @@
#define NID_INTERRUPTRETURN 0xbadd00d5
#define NID_EXTENDRETURN 0xbad0b0c9
#define NID_MODULERETURN 0xbad0d318
#define NID_HLECALLRETURN 0xbad0259b
#define NID_IDLE 0x1d7e1d7e
#define NID_GPUREPLAY 0x9e45bd95

View file

@ -492,8 +492,8 @@ public:
PostAllocCallback *action = (PostAllocCallback *) __KernelCreateAction(actionPostAllocCallback);
action->SetFontLib(GetListID(), errorCodePtr);
u32 args[2] = { params_.userDataAddr, allocSize };
__KernelDirectMipsCall(params_.allocFuncAddr, action, args, 2, true);
u32 args[2] = { userDataAddr(), allocSize };
hleEnqueueCall(allocFuncAddr(), 2, args, action);
}
u32 GetListID() {
@ -508,11 +508,11 @@ public:
fontMap.erase(fonts_[i]);
}
}
u32 args[2] = { params_.userDataAddr, (u32)handle_ };
u32 args[2] = { userDataAddr(), (u32)handle_ };
// TODO: The return value of this is leaking.
if (handle_) { // Avoid calling free-callback on double-free
if (coreState != CORE_POWERDOWN) {
__KernelDirectMipsCall(params_.freeFuncAddr, 0, args, 2, false);
hleEnqueueCall(freeFuncAddr(), 2, args);
}
}
handle_ = 0;
@ -620,8 +620,8 @@ public:
action->SetFontLib(GetListID());
action->SetFont(loadedFont->Handle(), freeFontIndex);
u32 args[2] = { params_.userDataAddr, allocSize };
__KernelDirectMipsCall(params_.allocFuncAddr, action, args, 2, true);
u32 args[2] = { userDataAddr(), allocSize };
hleEnqueueCall(allocFuncAddr(), 2, args, action);
return loadedFont;
}
@ -632,7 +632,7 @@ public:
isfontopen_[i] = 0;
if (openAllocatedAddresses_[i] != 0 && coreState != CORE_POWERDOWN) {
u32 args[2] = { userDataAddr(), openAllocatedAddresses_[i] };
__KernelDirectMipsCall(freeFuncAddr(), 0, args, 2, true);
hleEnqueueCall(freeFuncAddr(), 2, args);
openAllocatedAddresses_[i] = 0;
}
break;
@ -645,7 +645,7 @@ public:
void flushFont() {
if (charInfoBitmapAddress_ != 0 && coreState != CORE_POWERDOWN) {
u32 args[2] = { userDataAddr(), charInfoBitmapAddress_ };
__KernelDirectMipsCall(freeFuncAddr(), 0, args, 2, true);
hleEnqueueCall(freeFuncAddr(), 2, args);
charInfoBitmapAddress_ = 0;
}
}
@ -727,6 +727,7 @@ void PostAllocCallback::run(MipsCall &call) {
FontLib *fontLib = fontLibList[fontLibID_];
fontLib->AllocDone(v0);
fontLibMap[fontLib->handle()] = fontLibID_;
// This is the same as v0 above.
call.setReturnValue(fontLib->handle());
}
INFO_LOG(SCEFONT, "Leaving PostAllocCallback::run");
@ -742,7 +743,6 @@ void PostOpenAllocCallback::run(MipsCall &call) {
FontLib *fontLib = fontLibList[fontLibID_];
u32 v0 = currentMIPS->r[MIPS_REG_V0];
fontLib->SetOpenAllocatedAddress(fontIndex_, v0);
call.setReturnValue(fontHandle_);
}
void PostCharInfoAllocCallback::run(MipsCall &call) {
@ -752,7 +752,6 @@ void PostCharInfoAllocCallback::run(MipsCall &call) {
call.setReturnValue(ERROR_FONT_OUT_OF_MEMORY); // From JPCSP, if alloc size is 0, still this error value?
} else {
fontLib->SetCharInfoBitmapAddress(v0);
call.setReturnValue(0);
}
}
@ -765,7 +764,7 @@ void PostCharInfoFreeCallback::run(MipsCall &call) {
action->SetFontLib(fontLibID_);
u32 args[2] = { fontLib->userDataAddr(), allocSize };
__KernelDirectMipsCall(fontLib->allocFuncAddr(), action, args, 2, true);
hleEnqueueCall(fontLib->allocFuncAddr(), 2, args, action);
}
inline bool LoadedFont::GetCharInfo(int charCode, PGFCharInfo *charInfo, int glyphType) const {
@ -917,8 +916,8 @@ void __FontDoState(PointerWrap &p) {
p.Do(actionPostAllocCallback);
__KernelRestoreActionType(actionPostAllocCallback, PostAllocCallback::Create);
p.Do(actionPostOpenCallback);
__KernelRestoreActionType(actionPostOpenCallback, PostOpenCallback::Create);
if (s >= 2) {
__KernelRestoreActionType(actionPostOpenCallback, PostOpenCallback::Create);
p.Do(actionPostOpenAllocCallback);
__KernelRestoreActionType(actionPostOpenAllocCallback, PostOpenAllocCallback::Create);
p.Do(actionPostCharInfoAllocCallback);
@ -963,7 +962,6 @@ static int sceFontDoneLib(u32 fontLibHandle) {
INFO_LOG(SCEFONT, "sceFontDoneLib(%08x)", fontLibHandle);
FontLib *fl = GetFontLib(fontLibHandle);
if (fl) {
currentMIPS->r[MIPS_REG_V0] = 0;
fl->Done();
}
return 0;
@ -1098,7 +1096,6 @@ static int sceFontClose(u32 fontHandle) {
DEBUG_LOG(SCEFONT, "sceFontClose(%x)", fontHandle);
FontLib *fontLib = font->GetFontLib();
if (fontLib) {
currentMIPS->r[MIPS_REG_V0] = 0;
fontLib->CloseFont(font);
}
} else
@ -1293,13 +1290,13 @@ static int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) {
action->SetCharInfo(charInfo);
u32 args[2] = { font->GetFontLib()->userDataAddr(), font->GetFontLib()->GetCharInfoBitmapAddress() };
__KernelDirectMipsCall(font->GetFontLib()->freeFuncAddr(), action, args, 2, true);
hleEnqueueCall(font->GetFontLib()->freeFuncAddr(), 2, args, action);
} else {
PostCharInfoAllocCallback *action = (PostCharInfoAllocCallback *)__KernelCreateAction(actionPostCharInfoAllocCallback);
action->SetFontLib(font->GetFontLib()->GetListID());
u32 args[2] = { font->GetFontLib()->userDataAddr(), allocSize };
__KernelDirectMipsCall(font->GetFontLib()->allocFuncAddr(), action, args, 2, true);
hleEnqueueCall(font->GetFontLib()->allocFuncAddr(), 2, args, action);
}
}
@ -1425,7 +1422,6 @@ static int sceFontFlush(u32 fontHandle) {
return ERROR_FONT_INVALID_PARAMETER;
}
currentMIPS->r[MIPS_REG_V0] = 0;
font->GetFontLib()->flushFont();
return 0;

View file

@ -630,6 +630,7 @@ SceUID currentThread;
PSPThread *currentThreadPtr;
u32 idleThreadHackAddr;
u32 threadReturnHackAddr;
u32 hleReturnHackAddr;
u32 cbReturnHackAddr;
u32 intReturnHackAddr;
u32 extendReturnHackAddr;
@ -742,7 +743,7 @@ inline void __SetCurrentThread(PSPThread *thread, SceUID threadID, const char *n
hleCurrentThreadName = name;
}
u32 __KernelMipsCallReturnAddress() {
u32 __KernelCallbackReturnAddress() {
return cbReturnHackAddr;
}
@ -889,6 +890,16 @@ static void __KernelWriteFakeSysCall(u32 nid, u32 *ptr, u32 &pos)
MIPSAnalyst::PrecompileFunction(*ptr, 8);
}
u32 HLEMipsCallReturnAddress() {
if (hleReturnHackAddr == 0) {
// From an old save state, likely... try to recover.
u32 blockSize = 2 * sizeof(u32);
u32 pos = kernelMemory.Alloc(blockSize, false, "hlerethack");
__KernelWriteFakeSysCall(NID_HLECALLRETURN, &hleReturnHackAddr, pos);
}
return hleReturnHackAddr;
}
void __KernelThreadingInit()
{
struct ThreadHack
@ -912,6 +923,7 @@ void __KernelThreadingInit()
{NID_INTERRUPTRETURN, &intReturnHackAddr},
{NID_EXTENDRETURN, &extendReturnHackAddr},
{NID_MODULERETURN, &moduleReturnHackAddr},
{NID_HLECALLRETURN, &hleReturnHackAddr},
};
u32 blockSize = sizeof(idleThreadCode) + ARRAY_SIZE(threadHacks) * 2 * 4; // The thread code above plus 8 bytes per "hack"
@ -953,7 +965,7 @@ void __KernelThreadingInit()
void __KernelThreadingDoState(PointerWrap &p)
{
auto s = p.Section("sceKernelThread", 1, 3);
auto s = p.Section("sceKernelThread", 1, 4);
if (!s)
return;
@ -967,6 +979,12 @@ void __KernelThreadingDoState(PointerWrap &p)
p.Do(extendReturnHackAddr);
p.Do(moduleReturnHackAddr);
if (s >= 4) {
p.Do(hleReturnHackAddr);
} else {
hleReturnHackAddr = 0;
}
p.Do(currentThread);
SceUID dv = 0;
p.Do(threadqueue, dv);
@ -1158,6 +1176,7 @@ void __KernelThreadingShutdown() {
mipsCalls.clear();
threadReturnHackAddr = 0;
cbReturnHackAddr = 0;
hleReturnHackAddr = 0;
__SetCurrentThread(NULL, 0, NULL);
intReturnHackAddr = 0;
pausedDelays.clear();
@ -3188,7 +3207,7 @@ bool __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter)
// Set up the new state
currentMIPS->pc = call->entryPoint;
currentMIPS->r[MIPS_REG_RA] = __KernelMipsCallReturnAddress();
currentMIPS->r[MIPS_REG_RA] = __KernelCallbackReturnAddress();
cur->currentMipscallId = callId;
for (int i = 0; i < call->numArgs; i++) {
currentMIPS->r[MIPS_REG_A0 + i] = call->args[i];

View file

@ -204,7 +204,8 @@ void __KernelReturnFromExtendStack();
void __KernelIdle();
u32 __KernelMipsCallReturnAddress();
u32 HLEMipsCallReturnAddress();
u32 __KernelCallbackReturnAddress();
u32 __KernelInterruptReturnAddress(); // TODO: remove
SceUID sceKernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg);

View file

@ -1524,7 +1524,7 @@ static u32 sceMpegRingbufferPut(u32 ringbufferAddr, int numPackets, int availabl
int writeOffset = ringbuffer->packetsWritePos % (s32)ringbuffer->packets;
u32 packetsThisRound = std::min(numPackets, (s32)ringbuffer->packets - writeOffset);
u32 args[3] = {(u32)ringbuffer->data + (u32)writeOffset * 2048, packetsThisRound, (u32)ringbuffer->callback_args};
__KernelDirectMipsCall(ringbuffer->callback_addr, action, args, 3, false);
hleEnqueueCall(ringbuffer->callback_addr, 3, args, action);
} else {
ERROR_LOG_REPORT(ME, "sceMpegRingbufferPut: callback_addr zero");
}

View file

@ -75,8 +75,7 @@ static void __UpdateApctlHandlers(int oldState, int newState, int flag, int erro
for(std::map<int, ApctlHandler>::iterator it = apctlHandlers.begin(); it != apctlHandlers.end(); ++it) {
args[4] = it->second.argument;
__KernelDirectMipsCall(it->second.entryPoint, NULL, args, 5, true);
hleEnqueueCall(it->second.entryPoint, 5, args);
}
}
@ -474,7 +473,6 @@ static int sceNetApctlDisconnect() {
ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__);
// Like its 'sister' function sceNetAdhocctlDisconnect, we need to alert Apctl handlers that a disconnect took place
// or else games like Phantasy Star Portable 2 will hang at certain points (e.g. returning to the main menu after trying to connect to PSN).
currentMIPS->r[MIPS_REG_V0] = 0;
__UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST, 0);
return 0;
}