CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HLE/HLE.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <cstdarg>
19
#include <map>
20
#include <vector>
21
#include <string>
22
23
#include "Common/Profiler/Profiler.h"
24
25
#include "Common/Log.h"
26
#include "Common/Serialize/SerializeFuncs.h"
27
#include "Common/TimeUtil.h"
28
#include "Core/Config.h"
29
#include "Core/Core.h"
30
#include "Core/CoreTiming.h"
31
#include "Core/MemMapHelpers.h"
32
#include "Core/Reporting.h"
33
#include "Core/System.h"
34
#include "Core/MIPS/MIPS.h"
35
#include "Core/MIPS/MIPSCodeUtils.h"
36
#include "Core/HLE/HLETables.h"
37
#include "Core/HLE/sceIo.h"
38
#include "Core/HLE/sceAudio.h"
39
#include "Core/HLE/sceKernelMemory.h"
40
#include "Core/HLE/sceKernelThread.h"
41
#include "Core/HLE/sceKernelInterrupt.h"
42
#include "Core/HLE/HLE.h"
43
44
enum
45
{
46
// Do nothing after the syscall.
47
HLE_AFTER_NOTHING = 0x00,
48
// Reschedule immediately after the syscall.
49
HLE_AFTER_RESCHED = 0x01,
50
// Call current thread's callbacks after the syscall.
51
HLE_AFTER_CURRENT_CALLBACKS = 0x02,
52
// Reschedule and process current thread's callbacks after the syscall.
53
HLE_AFTER_RESCHED_CALLBACKS = 0x08,
54
// Run interrupts (and probably reschedule) after the syscall.
55
HLE_AFTER_RUN_INTERRUPTS = 0x10,
56
// Switch to CORE_STEPPING after the syscall (for debugging.)
57
HLE_AFTER_DEBUG_BREAK = 0x20,
58
// Don't fill temp regs with 0xDEADBEEF.
59
HLE_AFTER_SKIP_DEADBEEF = 0x40,
60
// Execute pending mips calls.
61
HLE_AFTER_QUEUED_CALLS = 0x80,
62
};
63
64
static std::vector<HLEModule> moduleDB;
65
static int delayedResultEvent = -1;
66
static int hleAfterSyscall = HLE_AFTER_NOTHING;
67
static const char *hleAfterSyscallReschedReason;
68
static const HLEFunction *latestSyscall = nullptr;
69
static uint32_t latestSyscallPC = 0;
70
static int idleOp;
71
72
struct HLEMipsCallInfo {
73
u32 func;
74
PSPAction *action;
75
std::vector<u32> args;
76
};
77
78
struct HLEMipsCallStack {
79
u32_le nextOff;
80
union {
81
struct {
82
u32_le func;
83
u32_le actionIndex;
84
u32_le argc;
85
};
86
struct {
87
u32_le ra;
88
u32_le v0;
89
u32_le v1;
90
};
91
};
92
};
93
94
// No need to save state, always flushed at a syscall end.
95
static std::vector<HLEMipsCallInfo> enqueuedMipsCalls;
96
// Does need to be saved, referenced by the stack and owned.
97
static std::vector<PSPAction *> mipsCallActions;
98
99
void hleDelayResultFinish(u64 userdata, int cycleslate)
100
{
101
u32 error;
102
SceUID threadID = (SceUID) userdata;
103
SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_HLEDELAY, error);
104
// The top 32 bits of userdata are the top 32 bits of the 64 bit result.
105
// We can't just put it all in userdata because we need to know the threadID...
106
u64 result = (userdata & 0xFFFFFFFF00000000ULL) | __KernelGetWaitValue(threadID, error);
107
108
if (error == 0 && verify == 1)
109
{
110
__KernelResumeThreadFromWait(threadID, result);
111
__KernelReSchedule("woke from hle delay");
112
}
113
else
114
WARN_LOG(Log::HLE, "Someone else woke up HLE-blocked thread %d?", threadID);
115
}
116
117
void HLEInit() {
118
RegisterAllModules();
119
delayedResultEvent = CoreTiming::RegisterEvent("HLEDelayedResult", hleDelayResultFinish);
120
idleOp = GetSyscallOp("FakeSysCalls", NID_IDLE);
121
}
122
123
void HLEDoState(PointerWrap &p) {
124
auto s = p.Section("HLE", 1, 2);
125
if (!s)
126
return;
127
128
// Can't be inside a syscall, reset this so errors aren't misleading.
129
latestSyscall = nullptr;
130
latestSyscallPC = 0;
131
Do(p, delayedResultEvent);
132
CoreTiming::RestoreRegisterEvent(delayedResultEvent, "HLEDelayedResult", hleDelayResultFinish);
133
134
if (s >= 2) {
135
int actions = (int)mipsCallActions.size();
136
Do(p, actions);
137
if (actions != (int)mipsCallActions.size()) {
138
mipsCallActions.resize(actions);
139
}
140
141
for (auto &action : mipsCallActions) {
142
int actionTypeID = action != nullptr ? action->actionTypeID : -1;
143
Do(p, actionTypeID);
144
if (actionTypeID != -1) {
145
if (p.mode == p.MODE_READ)
146
action = __KernelCreateAction(actionTypeID);
147
action->DoState(p);
148
}
149
}
150
}
151
}
152
153
void HLEShutdown() {
154
hleAfterSyscall = HLE_AFTER_NOTHING;
155
latestSyscall = nullptr;
156
latestSyscallPC = 0;
157
moduleDB.clear();
158
enqueuedMipsCalls.clear();
159
for (auto p : mipsCallActions) {
160
delete p;
161
}
162
mipsCallActions.clear();
163
}
164
165
void RegisterModule(const char *name, int numFunctions, const HLEFunction *funcTable)
166
{
167
HLEModule module = {name, numFunctions, funcTable};
168
moduleDB.push_back(module);
169
}
170
171
int GetModuleIndex(const char *moduleName)
172
{
173
for (size_t i = 0; i < moduleDB.size(); i++)
174
if (strcmp(moduleName, moduleDB[i].name) == 0)
175
return (int)i;
176
return -1;
177
}
178
179
int GetFuncIndex(int moduleIndex, u32 nib)
180
{
181
const HLEModule &module = moduleDB[moduleIndex];
182
for (int i = 0; i < module.numFunctions; i++)
183
{
184
if (module.funcTable[i].ID == nib)
185
return i;
186
}
187
return -1;
188
}
189
190
u32 GetNibByName(const char *moduleName, const char *function)
191
{
192
int moduleIndex = GetModuleIndex(moduleName);
193
if (moduleIndex == -1)
194
return -1;
195
196
const HLEModule &module = moduleDB[moduleIndex];
197
for (int i = 0; i < module.numFunctions; i++)
198
{
199
if (!strcmp(module.funcTable[i].name, function))
200
return module.funcTable[i].ID;
201
}
202
return -1;
203
}
204
205
const HLEFunction *GetFunc(const char *moduleName, u32 nib)
206
{
207
int moduleIndex = GetModuleIndex(moduleName);
208
if (moduleIndex != -1)
209
{
210
int idx = GetFuncIndex(moduleIndex, nib);
211
if (idx != -1)
212
return &(moduleDB[moduleIndex].funcTable[idx]);
213
}
214
return 0;
215
}
216
217
const char *GetFuncName(const char *moduleName, u32 nib)
218
{
219
_dbg_assert_msg_(moduleName != nullptr, "Invalid module name.");
220
221
const HLEFunction *func = GetFunc(moduleName,nib);
222
if (func)
223
return func->name;
224
225
static char temp[256];
226
snprintf(temp, sizeof(temp), "[UNK: 0x%08x]", nib);
227
return temp;
228
}
229
230
u32 GetSyscallOp(const char *moduleName, u32 nib) {
231
// Special case to hook up bad imports.
232
if (moduleName == NULL) {
233
return (0x03FFFFCC); // invalid syscall
234
}
235
236
int modindex = GetModuleIndex(moduleName);
237
if (modindex != -1) {
238
int funcindex = GetFuncIndex(modindex, nib);
239
if (funcindex != -1) {
240
return (0x0000000c | (modindex<<18) | (funcindex<<6));
241
} else {
242
INFO_LOG(Log::HLE, "Syscall (%s, %08x) unknown", moduleName, nib);
243
return (0x0003FFCC | (modindex<<18)); // invalid syscall
244
}
245
}
246
else
247
{
248
ERROR_LOG(Log::HLE, "Unknown module %s!", moduleName);
249
return (0x03FFFFCC); // invalid syscall
250
}
251
}
252
253
bool FuncImportIsSyscall(const char *module, u32 nib)
254
{
255
return GetFunc(module, nib) != NULL;
256
}
257
258
void WriteFuncStub(u32 stubAddr, u32 symAddr)
259
{
260
// Note that this should be J not JAL, as otherwise control will return to the stub..
261
Memory::Write_U32(MIPS_MAKE_J(symAddr), stubAddr);
262
// Note: doing that, we can't trace external module calls, so maybe something else should be done to debug more efficiently
263
// Perhaps a syscall here (and verify support in jit), marking the module by uid (debugIdentifier)?
264
Memory::Write_U32(MIPS_MAKE_NOP(), stubAddr + 4);
265
}
266
267
void WriteFuncMissingStub(u32 stubAddr, u32 nid)
268
{
269
// Write a trap so we notice this func if it's called before resolving.
270
Memory::Write_U32(MIPS_MAKE_JR_RA(), stubAddr); // jr ra
271
Memory::Write_U32(GetSyscallOp(NULL, nid), stubAddr + 4);
272
}
273
274
bool WriteSyscall(const char *moduleName, u32 nib, u32 address)
275
{
276
if (nib == 0)
277
{
278
WARN_LOG_REPORT(Log::HLE, "Wrote patched out nid=0 syscall (%s)", moduleName);
279
Memory::Write_U32(MIPS_MAKE_JR_RA(), address); //patched out?
280
Memory::Write_U32(MIPS_MAKE_NOP(), address+4); //patched out?
281
return true;
282
}
283
int modindex = GetModuleIndex(moduleName);
284
if (modindex != -1)
285
{
286
Memory::Write_U32(MIPS_MAKE_JR_RA(), address); // jr ra
287
Memory::Write_U32(GetSyscallOp(moduleName, nib), address + 4);
288
return true;
289
}
290
else
291
{
292
ERROR_LOG_REPORT(Log::HLE, "Unable to write unknown syscall: %s/%08x", moduleName, nib);
293
return false;
294
}
295
}
296
297
const char *GetFuncName(int moduleIndex, int func)
298
{
299
if (moduleIndex >= 0 && moduleIndex < (int)moduleDB.size())
300
{
301
const HLEModule &module = moduleDB[moduleIndex];
302
if (func >= 0 && func < module.numFunctions)
303
{
304
return module.funcTable[func].name;
305
}
306
}
307
return "[unknown]";
308
}
309
310
void hleCheckCurrentCallbacks()
311
{
312
hleAfterSyscall |= HLE_AFTER_CURRENT_CALLBACKS;
313
}
314
315
void hleReSchedule(const char *reason)
316
{
317
#ifdef _DEBUG
318
_dbg_assert_msg_(reason != nullptr && strlen(reason) < 256, "hleReSchedule: Invalid or too long reason.");
319
#endif
320
321
hleAfterSyscall |= HLE_AFTER_RESCHED;
322
323
if (!reason)
324
hleAfterSyscallReschedReason = "Invalid reason";
325
else
326
hleAfterSyscallReschedReason = reason;
327
}
328
329
void hleReSchedule(bool callbacks, const char *reason)
330
{
331
hleReSchedule(reason);
332
if (callbacks)
333
hleAfterSyscall |= HLE_AFTER_RESCHED_CALLBACKS;
334
}
335
336
void hleRunInterrupts()
337
{
338
hleAfterSyscall |= HLE_AFTER_RUN_INTERRUPTS;
339
}
340
341
void hleDebugBreak()
342
{
343
hleAfterSyscall |= HLE_AFTER_DEBUG_BREAK;
344
}
345
346
void hleSkipDeadbeef()
347
{
348
hleAfterSyscall |= HLE_AFTER_SKIP_DEADBEEF;
349
}
350
351
// Pauses execution after an HLE call.
352
bool hleExecuteDebugBreak(const HLEFunction &func)
353
{
354
const u32 NID_SUSPEND_INTR = 0x092968F4, NID_RESUME_INTR = 0x5F10D406;
355
356
// Never break on these, they're noise.
357
u32 blacklistedNIDs[] = {NID_SUSPEND_INTR, NID_RESUME_INTR, NID_IDLE};
358
for (size_t i = 0; i < ARRAY_SIZE(blacklistedNIDs); ++i)
359
{
360
if (func.ID == blacklistedNIDs[i])
361
return false;
362
}
363
364
Core_EnableStepping(true, "hle.step", latestSyscallPC);
365
return true;
366
}
367
368
u32 hleDelayResult(u32 result, const char *reason, int usec) {
369
if (!__KernelIsDispatchEnabled()) {
370
WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?");
371
} else {
372
SceUID thread = __KernelGetCurThread();
373
if (KernelIsThreadWaiting(thread))
374
ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?");
375
CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, thread);
376
__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, reason);
377
}
378
return result;
379
}
380
381
u64 hleDelayResult(u64 result, const char *reason, int usec) {
382
if (!__KernelIsDispatchEnabled()) {
383
WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?");
384
} else {
385
SceUID thread = __KernelGetCurThread();
386
if (KernelIsThreadWaiting(thread))
387
ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?");
388
u64 param = (result & 0xFFFFFFFF00000000) | thread;
389
CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, param);
390
__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, (u32)result, 0, false, reason);
391
}
392
return result;
393
}
394
395
void hleEatCycles(int cycles) {
396
// Maybe this should Idle, at least for larger delays? Could that cause issues?
397
currentMIPS->downcount -= cycles;
398
}
399
400
void hleEatMicro(int usec) {
401
hleEatCycles((int) usToCycles(usec));
402
}
403
404
bool hleIsKernelMode() {
405
return latestSyscall && (latestSyscall->flags & HLE_KERNEL_SYSCALL) != 0;
406
}
407
408
void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction) {
409
std::vector<u32> args;
410
args.resize(argc);
411
memcpy(args.data(), argv, argc * sizeof(u32));
412
413
enqueuedMipsCalls.push_back({ func, afterAction, args });
414
415
hleAfterSyscall |= HLE_AFTER_QUEUED_CALLS;
416
}
417
418
void hleFlushCalls() {
419
u32 &sp = currentMIPS->r[MIPS_REG_SP];
420
PSPPointer<HLEMipsCallStack> stackData;
421
VERBOSE_LOG(Log::HLE, "Flushing %d HLE mips calls from %s, sp=%08x", (int)enqueuedMipsCalls.size(), latestSyscall ? latestSyscall->name : "?", sp);
422
423
// First, we'll add a marker for the final return.
424
sp -= sizeof(HLEMipsCallStack);
425
stackData.ptr = sp;
426
stackData->nextOff = 0xFFFFFFFF;
427
stackData->ra = currentMIPS->pc;
428
stackData->v0 = currentMIPS->r[MIPS_REG_V0];
429
stackData->v1 = currentMIPS->r[MIPS_REG_V1];
430
431
// Now we'll set up the first in the chain.
432
currentMIPS->pc = enqueuedMipsCalls[0].func;
433
currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress();
434
for (int i = 0; i < (int)enqueuedMipsCalls[0].args.size(); i++) {
435
currentMIPS->r[MIPS_REG_A0 + i] = enqueuedMipsCalls[0].args[i];
436
}
437
438
// For stack info, process the first enqueued call last, so we run it first.
439
// We don't actually need to store 0's args, but keep it consistent.
440
for (int i = (int)enqueuedMipsCalls.size() - 1; i >= 0; --i) {
441
auto &info = enqueuedMipsCalls[i];
442
u32 stackRequired = (int)info.args.size() * sizeof(u32) + sizeof(HLEMipsCallStack);
443
u32 stackAligned = (stackRequired + 0xF) & ~0xF;
444
445
sp -= stackAligned;
446
stackData.ptr = sp;
447
stackData->nextOff = stackAligned;
448
stackData->func = info.func;
449
if (info.action) {
450
stackData->actionIndex = (int)mipsCallActions.size();
451
mipsCallActions.push_back(info.action);
452
} else {
453
stackData->actionIndex = 0xFFFFFFFF;
454
}
455
stackData->argc = (int)info.args.size();
456
for (int j = 0; j < (int)info.args.size(); ++j) {
457
Memory::Write_U32(info.args[j], sp + sizeof(HLEMipsCallStack) + j * sizeof(u32));
458
}
459
}
460
enqueuedMipsCalls.clear();
461
462
DEBUG_LOG(Log::HLE, "Executing HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp);
463
}
464
465
void HLEReturnFromMipsCall() {
466
u32 &sp = currentMIPS->r[MIPS_REG_SP];
467
PSPPointer<HLEMipsCallStack> stackData;
468
469
// At this point, we may have another mips call to run, or be at the end...
470
stackData.ptr = sp;
471
472
if ((stackData->nextOff & 0x0000000F) != 0 || !Memory::IsValidAddress(sp + stackData->nextOff)) {
473
ERROR_LOG(Log::HLE, "Corrupt stack on HLE mips call return: %08x", stackData->nextOff);
474
Core_UpdateState(CORE_RUNTIME_ERROR);
475
return;
476
}
477
478
if (stackData->actionIndex != 0xFFFFFFFF && stackData->actionIndex < (u32)mipsCallActions.size()) {
479
PSPAction *&action = mipsCallActions[stackData->actionIndex];
480
VERBOSE_LOG(Log::HLE, "Executing action for HLE mips call at %08x, sp=%08x", stackData->func, sp);
481
482
// Search for the saved v0/v1 values, to preserve the PSPAction API...
483
PSPPointer<HLEMipsCallStack> finalMarker = stackData;
484
while ((finalMarker->nextOff & 0x0000000F) == 0 && Memory::IsValidAddress(finalMarker.ptr + finalMarker->nextOff)) {
485
finalMarker.ptr += finalMarker->nextOff;
486
}
487
488
if (finalMarker->nextOff != 0xFFFFFFFF) {
489
ERROR_LOG(Log::HLE, "Corrupt stack on HLE mips call return action: %08x", finalMarker->nextOff);
490
Core_UpdateState(CORE_RUNTIME_ERROR);
491
return;
492
}
493
494
MipsCall mc;
495
mc.savedV0 = finalMarker->v0;
496
mc.savedV1 = finalMarker->v1;
497
action->run(mc);
498
finalMarker->v0 = mc.savedV0;
499
finalMarker->v1 = mc.savedV1;
500
501
delete action;
502
action = nullptr;
503
504
// Note: the action could actually enqueue more, adding another layer on stack after this.
505
}
506
507
sp += stackData->nextOff;
508
stackData.ptr = sp;
509
510
if (stackData->nextOff == 0xFFFFFFFF) {
511
// We're done. Grab the HLE result's v0/v1 and return from the syscall.
512
currentMIPS->pc = stackData->ra;
513
currentMIPS->r[MIPS_REG_V0] = stackData->v0;
514
currentMIPS->r[MIPS_REG_V1] = stackData->v1;
515
516
sp += sizeof(HLEMipsCallStack);
517
518
bool canClear = true;
519
for (auto p : mipsCallActions) {
520
canClear = canClear && p == nullptr;
521
}
522
if (canClear) {
523
mipsCallActions.clear();
524
}
525
526
VERBOSE_LOG(Log::HLE, "Finished HLE mips calls, v0=%08x, sp=%08x", currentMIPS->r[MIPS_REG_V0], sp);
527
return;
528
}
529
530
// Alright, we have another to call.
531
hleSkipDeadbeef();
532
currentMIPS->pc = stackData->func;
533
currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress();
534
for (int i = 0; i < (int)stackData->argc; i++) {
535
currentMIPS->r[MIPS_REG_A0 + i] = Memory::Read_U32(sp + sizeof(HLEMipsCallStack) + i * sizeof(u32));
536
}
537
DEBUG_LOG(Log::HLE, "Executing next HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp);
538
}
539
540
const static u32 deadbeefRegs[12] = {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF};
541
inline static void SetDeadbeefRegs()
542
{
543
// Not exactly the same, but any time a syscall happens, it should clear ll.
544
currentMIPS->llBit = 0;
545
546
if (g_Config.bSkipDeadbeefFilling)
547
return;
548
549
currentMIPS->r[MIPS_REG_COMPILER_SCRATCH] = 0xDEADBEEF;
550
// Set all the arguments and temp regs.
551
memcpy(&currentMIPS->r[MIPS_REG_A0], deadbeefRegs, sizeof(deadbeefRegs));
552
currentMIPS->r[MIPS_REG_T8] = 0xDEADBEEF;
553
currentMIPS->r[MIPS_REG_T9] = 0xDEADBEEF;
554
555
currentMIPS->lo = 0xDEADBEEF;
556
currentMIPS->hi = 0xDEADBEEF;
557
}
558
559
inline void hleFinishSyscall(const HLEFunction &info)
560
{
561
if ((hleAfterSyscall & HLE_AFTER_SKIP_DEADBEEF) == 0)
562
SetDeadbeefRegs();
563
564
if ((hleAfterSyscall & HLE_AFTER_QUEUED_CALLS) != 0)
565
hleFlushCalls();
566
if ((hleAfterSyscall & HLE_AFTER_CURRENT_CALLBACKS) != 0 && (hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) == 0)
567
__KernelForceCallbacks();
568
569
if ((hleAfterSyscall & HLE_AFTER_RUN_INTERRUPTS) != 0)
570
__RunOnePendingInterrupt();
571
572
if ((hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) != 0)
573
__KernelReSchedule(true, hleAfterSyscallReschedReason);
574
else if ((hleAfterSyscall & HLE_AFTER_RESCHED) != 0)
575
__KernelReSchedule(hleAfterSyscallReschedReason);
576
577
if ((hleAfterSyscall & HLE_AFTER_DEBUG_BREAK) != 0)
578
{
579
if (!hleExecuteDebugBreak(info))
580
{
581
// We'll do it next syscall.
582
hleAfterSyscall = HLE_AFTER_DEBUG_BREAK;
583
hleAfterSyscallReschedReason = 0;
584
return;
585
}
586
}
587
588
hleAfterSyscall = HLE_AFTER_NOTHING;
589
hleAfterSyscallReschedReason = 0;
590
}
591
592
static void updateSyscallStats(int modulenum, int funcnum, double total)
593
{
594
const char *name = moduleDB[modulenum].funcTable[funcnum].name;
595
// Ignore this one, especially for msInSyscalls (although that ignores CoreTiming events.)
596
if (0 == strcmp(name, "_sceKernelIdle"))
597
return;
598
599
if (total > kernelStats.slowestSyscallTime)
600
{
601
kernelStats.slowestSyscallTime = total;
602
kernelStats.slowestSyscallName = name;
603
}
604
kernelStats.msInSyscalls += total;
605
606
KernelStatsSyscall statCall(modulenum, funcnum);
607
auto summedStat = kernelStats.summedMsInSyscalls.find(statCall);
608
if (summedStat == kernelStats.summedMsInSyscalls.end())
609
{
610
kernelStats.summedMsInSyscalls[statCall] = total;
611
if (total > kernelStats.summedSlowestSyscallTime)
612
{
613
kernelStats.summedSlowestSyscallTime = total;
614
kernelStats.summedSlowestSyscallName = name;
615
}
616
}
617
else
618
{
619
double newTotal = kernelStats.summedMsInSyscalls[statCall] += total;
620
if (newTotal > kernelStats.summedSlowestSyscallTime)
621
{
622
kernelStats.summedSlowestSyscallTime = newTotal;
623
kernelStats.summedSlowestSyscallName = name;
624
}
625
}
626
}
627
628
inline void CallSyscallWithFlags(const HLEFunction *info)
629
{
630
latestSyscall = info;
631
latestSyscallPC = currentMIPS->pc;
632
const u32 flags = info->flags;
633
634
if (flags & HLE_CLEAR_STACK_BYTES) {
635
u32 stackStart = __KernelGetCurThreadStackStart();
636
if (currentMIPS->r[MIPS_REG_SP] - info->stackBytesToClear >= stackStart) {
637
Memory::Memset(currentMIPS->r[MIPS_REG_SP] - info->stackBytesToClear, 0, info->stackBytesToClear, "HLEStackClear");
638
}
639
}
640
641
if ((flags & HLE_NOT_DISPATCH_SUSPENDED) && !__KernelIsDispatchEnabled()) {
642
RETURN(hleLogDebug(Log::HLE, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch suspended"));
643
} else if ((flags & HLE_NOT_IN_INTERRUPT) && __IsInInterrupt()) {
644
RETURN(hleLogDebug(Log::HLE, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "in interrupt"));
645
} else {
646
info->func();
647
}
648
649
if (hleAfterSyscall != HLE_AFTER_NOTHING)
650
hleFinishSyscall(*info);
651
else
652
SetDeadbeefRegs();
653
}
654
655
inline void CallSyscallWithoutFlags(const HLEFunction *info)
656
{
657
latestSyscall = info;
658
latestSyscallPC = currentMIPS->pc;
659
info->func();
660
661
if (hleAfterSyscall != HLE_AFTER_NOTHING)
662
hleFinishSyscall(*info);
663
else
664
SetDeadbeefRegs();
665
}
666
667
const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op)
668
{
669
u32 callno = (op >> 6) & 0xFFFFF; //20 bits
670
int funcnum = callno & 0xFFF;
671
int modulenum = (callno & 0xFF000) >> 12;
672
if (funcnum == 0xfff) {
673
ERROR_LOG(Log::HLE, "Unknown syscall: Module: %s (module: %d func: %d)", modulenum > (int)moduleDB.size() ? "(unknown)" : moduleDB[modulenum].name, modulenum, funcnum);
674
return NULL;
675
}
676
if (modulenum >= (int)moduleDB.size()) {
677
ERROR_LOG(Log::HLE, "Syscall had bad module number %d - probably executing garbage", modulenum);
678
return NULL;
679
}
680
if (funcnum >= moduleDB[modulenum].numFunctions) {
681
ERROR_LOG(Log::HLE, "Syscall had bad function number %d in module %d - probably executing garbage", funcnum, modulenum);
682
return NULL;
683
}
684
return &moduleDB[modulenum].funcTable[funcnum];
685
}
686
687
void *GetQuickSyscallFunc(MIPSOpcode op) {
688
if (coreCollectDebugStats)
689
return nullptr;
690
691
const HLEFunction *info = GetSyscallFuncPointer(op);
692
if (!info || !info->func)
693
return nullptr;
694
DEBUG_LOG(Log::HLE, "Compiling syscall to %s", info->name);
695
696
// TODO: Do this with a flag?
697
if (op == idleOp)
698
return (void *)info->func;
699
if (info->flags != 0)
700
return (void *)&CallSyscallWithFlags;
701
return (void *)&CallSyscallWithoutFlags;
702
}
703
704
static double hleSteppingTime = 0.0;
705
void hleSetSteppingTime(double t) {
706
hleSteppingTime += t;
707
}
708
709
static double hleFlipTime = 0.0;
710
void hleSetFlipTime(double t) {
711
hleFlipTime = t;
712
}
713
714
void CallSyscall(MIPSOpcode op)
715
{
716
PROFILE_THIS_SCOPE("syscall");
717
double start = 0.0; // need to initialize to fix the race condition where coreCollectDebugStats is enabled in the middle of this func.
718
if (coreCollectDebugStats) {
719
start = time_now_d();
720
}
721
722
const HLEFunction *info = GetSyscallFuncPointer(op);
723
if (!info) {
724
RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED);
725
return;
726
}
727
728
if (info->func) {
729
if (op == idleOp)
730
info->func();
731
else if (info->flags != 0)
732
CallSyscallWithFlags(info);
733
else
734
CallSyscallWithoutFlags(info);
735
}
736
else {
737
RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED);
738
ERROR_LOG_REPORT(Log::HLE, "Unimplemented HLE function %s", info->name ? info->name : "(\?\?\?)");
739
}
740
741
if (coreCollectDebugStats) {
742
u32 callno = (op >> 6) & 0xFFFFF; //20 bits
743
int funcnum = callno & 0xFFF;
744
int modulenum = (callno & 0xFF000) >> 12;
745
double total = time_now_d() - start - hleSteppingTime;
746
if (total >= hleFlipTime)
747
total -= hleFlipTime;
748
_dbg_assert_msg_(total >= 0.0, "Time spent in syscall became negative");
749
hleSteppingTime = 0.0;
750
hleFlipTime = 0.0;
751
updateSyscallStats(modulenum, funcnum, total);
752
}
753
}
754
755
size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) {
756
char *p = message;
757
size_t used = 0;
758
759
#define APPEND_FMT(...) do { \
760
if (used < sz) { \
761
size_t c = snprintf(p, sz - used, __VA_ARGS__); \
762
used += c; \
763
p += c; \
764
} \
765
} while (false)
766
767
int reg = 0;
768
int regf = 0;
769
for (size_t i = 0, n = strlen(argmask); i < n; ++i, ++reg) {
770
u32 regval;
771
if (reg < 8) {
772
regval = PARAM(reg);
773
} else {
774
u32 sp = currentMIPS->r[MIPS_REG_SP];
775
// Goes upward on stack.
776
// NOTE: Currently we only support > 8 for 32-bit integer args.
777
regval = Memory::Read_U32(sp + (reg - 8) * 4);
778
}
779
780
switch (argmask[i]) {
781
case 'p':
782
if (Memory::IsValidAddress(regval)) {
783
APPEND_FMT("%08x[%08x]", regval, Memory::Read_U32(regval));
784
} else {
785
APPEND_FMT("%08x[invalid]", regval);
786
}
787
break;
788
789
case 'P':
790
if (Memory::IsValidAddress(regval)) {
791
APPEND_FMT("%08x[%016llx]", regval, Memory::Read_U64(regval));
792
} else {
793
APPEND_FMT("%08x[invalid]", regval);
794
}
795
break;
796
797
case 's':
798
if (Memory::IsValidAddress(regval)) {
799
const char *s = Memory::GetCharPointer(regval);
800
const int safeLen = Memory::ValidSize(regval, 128);
801
if (strnlen(s, safeLen) >= safeLen) {
802
APPEND_FMT("%.*s...", safeLen, Memory::GetCharPointer(regval));
803
} else {
804
APPEND_FMT("%.*s", safeLen, Memory::GetCharPointer(regval));
805
}
806
} else {
807
APPEND_FMT("(invalid)");
808
}
809
break;
810
811
case 'x':
812
APPEND_FMT("%08x", regval);
813
break;
814
815
case 'i':
816
APPEND_FMT("%d", regval);
817
break;
818
819
case 'X':
820
case 'I':
821
// 64-bit regs are always aligned.
822
if ((reg & 1))
823
++reg;
824
APPEND_FMT("%016llx", PARAM64(reg));
825
++reg;
826
break;
827
828
case 'f':
829
APPEND_FMT("%f", PARAMF(regf++));
830
// This doesn't consume a gp reg.
831
--reg;
832
break;
833
834
// TODO: Double? Does it ever happen?
835
836
default:
837
_dbg_assert_msg_(false, "Invalid argmask character: %c", argmask[i]);
838
APPEND_FMT(" -- invalid arg format: %c -- %08x", argmask[i], regval);
839
break;
840
}
841
if (i + 1 < n) {
842
APPEND_FMT(", ");
843
}
844
}
845
846
if (used > sz) {
847
message[sz - 1] = '\0';
848
} else {
849
message[used] = '\0';
850
}
851
852
#undef APPEND_FMT
853
return used;
854
}
855
856
void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, char retmask, const char *reason, const char *formatted_reason) {
857
char formatted_args[4096];
858
const char *funcName = "?";
859
u32 funcFlags = 0;
860
if (latestSyscall) {
861
_dbg_assert_(latestSyscall->argmask != nullptr);
862
hleFormatLogArgs(formatted_args, sizeof(formatted_args), latestSyscall->argmask);
863
864
// This acts as an override (for error returns which are usually hex.)
865
if (retmask == '\0')
866
retmask = latestSyscall->retmask;
867
868
funcName = latestSyscall->name;
869
funcFlags = latestSyscall->flags;
870
} else {
871
strcpy(formatted_args, "?");
872
}
873
874
const char *fmt;
875
if (retmask == 'x') {
876
fmt = "%s%08llx=%s(%s)%s";
877
// Truncate the high bits of the result (from any sign extension.)
878
res = (u32)res;
879
} else if (retmask == 'i' || retmask == 'I') {
880
fmt = "%s%lld=%s(%s)%s";
881
} else if (retmask == 'f') {
882
// TODO: For now, floats are just shown as bits.
883
fmt = "%s%08x=%s(%s)%s";
884
} else {
885
_dbg_assert_msg_(false, "Invalid return format: %c", retmask);
886
fmt = "%s%08llx=%s(%s)%s";
887
}
888
889
const char *kernelFlag = (funcFlags & HLE_KERNEL_SYSCALL) != 0 ? "K " : "";
890
GenericLog(level, t, file, line, fmt, kernelFlag, res, funcName, formatted_args, formatted_reason);
891
892
if (reportTag != nullptr) {
893
// A blank string means always log, not just once.
894
if (reportTag[0] == '\0' || Reporting::ShouldLogNTimes(reportTag, 1)) {
895
// Here we want the original key, so that different args, etc. group together.
896
std::string key = std::string(kernelFlag) + std::string("%08x=") + funcName + "(%s)";
897
if (reason != nullptr)
898
key += std::string(": ") + reason;
899
900
char formatted_message[8192];
901
snprintf(formatted_message, sizeof(formatted_message), fmt, kernelFlag, res, funcName, formatted_args, formatted_reason);
902
Reporting::ReportMessageFormatted(key.c_str(), formatted_message);
903
}
904
}
905
}
906
907