Skip to content

Commit

Permalink
HLE: Make calling mips funcs simpler.
Browse files Browse the repository at this point in the history
This makes their return value handling, scheduling, etc. more
straight-forward.
  • Loading branch information
unknownbrackets committed Mar 22, 2020
1 parent a70f00c commit 54e1afd
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 32 deletions.
195 changes: 188 additions & 7 deletions Core/HLE/HLE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -86,30 +115,50 @@ 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;

// Can't be inside a syscall, reset this so errors aren't misleading.
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)
Expand Down Expand Up @@ -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()
{
Expand All @@ -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();

Expand Down
5 changes: 5 additions & 0 deletions Core/HLE/HLE.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "Core/MIPS/MIPS.h"

class PointerWrap;
class PSPAction;
typedef void (* HLEFunc)();

enum {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions Core/HLE/HLETables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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[] =
Expand Down
1 change: 1 addition & 0 deletions Core/HLE/HLETables.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading

1 comment on commit 54e1afd

@hrydgard
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a report in #12305 that this broke Ghost in the Shell, hm.

Please sign in to comment.