Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HLE/HLE.cpp
3187 views
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/Math/CrossSIMD.h"
24
25
#include "Common/Profiler/Profiler.h"
26
27
#include "Common/Log.h"
28
#include "Common/Serialize/SerializeFuncs.h"
29
#include "Common/TimeUtil.h"
30
#include "Core/Config.h"
31
#include "Core/Core.h"
32
#include "Core/CoreTiming.h"
33
#include "Core/MemMapHelpers.h"
34
#include "Core/Reporting.h"
35
#include "Core/System.h"
36
#include "Core/MIPS/MIPS.h"
37
#include "Core/MIPS/MIPSCodeUtils.h"
38
#include "Core/HLE/HLETables.h"
39
#include "Core/HLE/ErrorCodes.h"
40
#include "Core/HLE/sceKernelThread.h"
41
#include "Core/HLE/sceKernelInterrupt.h"
42
#include "Core/HLE/HLE.h"
43
44
enum {
45
// Do nothing after the syscall.
46
HLE_AFTER_NOTHING = 0x00,
47
// Reschedule immediately after the syscall.
48
HLE_AFTER_RESCHED = 0x01,
49
// Call current thread's callbacks after the syscall.
50
HLE_AFTER_CURRENT_CALLBACKS = 0x02,
51
// Reschedule and process current thread's callbacks after the syscall.
52
HLE_AFTER_RESCHED_CALLBACKS = 0x08,
53
// Run interrupts (and probably reschedule) after the syscall.
54
HLE_AFTER_RUN_INTERRUPTS = 0x10,
55
// Switch to CORE_STEPPING after the syscall (for debugging.)
56
HLE_AFTER_DEBUG_BREAK = 0x20,
57
// Don't fill temp regs with 0xDEADBEEF.
58
HLE_AFTER_SKIP_DEADBEEF = 0x40,
59
// Execute pending mips calls.
60
HLE_AFTER_QUEUED_CALLS = 0x80,
61
// Call CoreTiming::ForceCheck
62
HLE_AFTER_CORETIMING_FORCE_CHECK = 0x100,
63
// Split syscall over GE execution
64
HLE_SPLIT_SYSCALL_OVER_GE = 0x200,
65
HLE_SPLIT_SYSCALL_PART2 = 0x400,
66
};
67
68
static std::vector<HLEModule> moduleDB;
69
static int delayedResultEvent = -1;
70
static int hleAfterSyscall = HLE_AFTER_NOTHING;
71
static const char *hleAfterSyscallReschedReason;
72
73
#define MAX_SYSCALL_RECURSION 16
74
75
// Keep track of syscalls in flight. Note that they can call each other! But they'll have to use hleCall<>.
76
static const HLEFunction *g_stack[MAX_SYSCALL_RECURSION];
77
u32 g_syscallPC;
78
int g_stackSize;
79
80
static int idleOp;
81
82
// Split syscall support. NOTE: This needs to be saved in DoState somehow!
83
static int splitSyscallEatCycles = 0;
84
85
// Stats
86
static double hleSteppingTime = 0.0;
87
static double hleFlipTime = 0.0;
88
89
struct HLEMipsCallInfo {
90
u32 func;
91
PSPAction *action;
92
std::vector<u32> args;
93
};
94
95
struct HLEMipsCallStack {
96
u32_le nextOff;
97
union {
98
struct {
99
u32_le func;
100
u32_le actionIndex;
101
u32_le argc;
102
};
103
struct {
104
u32_le ra;
105
u32_le v0;
106
u32_le v1;
107
};
108
};
109
};
110
111
// No need to save state, always flushed at a syscall end.
112
static std::vector<HLEMipsCallInfo> enqueuedMipsCalls;
113
// Does need to be saved, referenced by the stack and owned.
114
static std::vector<PSPAction *> mipsCallActions;
115
116
// Modules that games try to load from disk, that we should not load normally. Instead we just HLE hook them.
117
// TODO: Merge with moduleDB?
118
static const HLEModuleMeta g_moduleMeta[] = {
119
{"sceATRAC3plus_Library", "sceAtrac3plus", DisableHLEFlags::sceAtrac},
120
{"sceFont_Library", "sceLibFont", DisableHLEFlags::sceFont},
121
{"SceFont_Library", "sceLibFont", DisableHLEFlags::sceFont},
122
{"SceFont_Library", "sceLibFttt", DisableHLEFlags::sceFont},
123
{"SceHttp_Library", "sceHttp"},
124
{"sceMpeg_library", "sceMpeg", DisableHLEFlags::sceMpeg},
125
{"sceMp3_Library", "sceMp3", DisableHLEFlags::sceMp3},
126
{"sceNetAdhocctl_Library"},
127
{"sceNetAdhocDownload_Library"},
128
{"sceNetAdhocMatching_Library"},
129
{"sceNetApDialogDummy_Library"},
130
{"sceNetAdhoc_Library"},
131
{"sceNetApctl_Library"},
132
{"sceNetInet_Library"},
133
{"sceNetResolver_Library"},
134
{"sceNet_Library"},
135
{"sceNetAdhoc_Library"},
136
{"sceNetAdhocAuth_Service"},
137
{"sceNetAdhocctl_Library"},
138
{"sceNetIfhandle_Service"},
139
{"sceSsl_Module"},
140
{"sceDEFLATE_Library"},
141
{"sceMD5_Library"},
142
{"sceMemab"}, // Underlying AdHoc crypto library
143
{"sceAvcodec_driver"},
144
{"sceAudiocodec_Driver"},
145
{"sceAudiocodec"},
146
{"sceVideocodec_Driver"},
147
{"sceVideocodec"},
148
{"sceMpegbase_Driver"},
149
{"sceMpegbase"},
150
{"scePsmf_library", "scePsmf", DisableHLEFlags::scePsmf},
151
{"scePsmfP_library", "scePsmfPlayer", DisableHLEFlags::scePsmfPlayer},
152
{"scePsmfPlayer", "scePsmfPlayer", DisableHLEFlags::scePsmfPlayer},
153
{"sceSAScore", "sceSasCore"},
154
{"sceCcc_Library", "sceCcc", DisableHLEFlags::sceCcc},
155
{"SceParseHTTPheader_Library", "sceParseHttp", DisableHLEFlags::sceParseHttp},
156
{"SceParseURI_Library"},
157
// Guessing these names
158
{"sceJpeg", "sceJpeg"},
159
{"sceJpeg_library", "sceJpeg"},
160
{"sceJpeg_Library", "sceJpeg"},
161
};
162
163
const HLEModuleMeta *GetHLEModuleMeta(std::string_view modname) {
164
for (size_t i = 0; i < ARRAY_SIZE(g_moduleMeta); i++) {
165
if (equalsNoCase(modname, g_moduleMeta[i].modname)) {
166
return &g_moduleMeta[i];
167
}
168
}
169
return nullptr;
170
}
171
172
const HLEModuleMeta *GetHLEModuleMetaByFlag(DisableHLEFlags flag) {
173
for (size_t i = 0; i < ARRAY_SIZE(g_moduleMeta); i++) {
174
if (g_moduleMeta[i].disableFlag == flag) {
175
return &g_moduleMeta[i];
176
}
177
}
178
return nullptr;
179
}
180
181
const HLEModuleMeta *GetHLEModuleMetaByImport(std::string_view importModuleName) {
182
for (size_t i = 0; i < ARRAY_SIZE(g_moduleMeta); i++) {
183
if (g_moduleMeta[i].importName && equalsNoCase(importModuleName, g_moduleMeta[i].importName)) {
184
return &g_moduleMeta[i];
185
}
186
}
187
return nullptr;
188
}
189
190
DisableHLEFlags AlwaysDisableHLEFlags() {
191
// Once a module seems stable to load properly, we'll graduate it here.
192
// This will hide the checkbox in Developer Settings, too.
193
//
194
// PSMF testing issue: #20200
195
// sceCcc is simply a character conversion library, zero deps. If available we just load it.
196
return DisableHLEFlags::scePsmf | DisableHLEFlags::scePsmfPlayer | DisableHLEFlags::sceCcc;
197
}
198
199
// Process compat flags.
200
static DisableHLEFlags GetDisableHLEFlags() {
201
DisableHLEFlags flags = (DisableHLEFlags)g_Config.iDisableHLE | AlwaysDisableHLEFlags();
202
if (PSP_CoreParameter().compat.flags().DisableHLESceFont) {
203
flags |= DisableHLEFlags::sceFont;
204
}
205
if (PSP_CoreParameter().compat.flags().ForceHLEPsmf) {
206
flags &= ~(DisableHLEFlags::scePsmf | DisableHLEFlags::scePsmfPlayer);
207
}
208
209
flags &= ~(DisableHLEFlags)g_Config.iForceEnableHLE;
210
return flags;
211
}
212
213
// Note: name is the modname from prx, not the export module name!
214
bool ShouldHLEModule(std::string_view modname, bool *wasDisabledManually) {
215
if (wasDisabledManually) {
216
*wasDisabledManually = false;
217
}
218
const HLEModuleMeta *meta = GetHLEModuleMeta(modname);
219
if (!meta) {
220
return false;
221
}
222
223
bool disabled = meta->disableFlag & GetDisableHLEFlags();
224
if (disabled) {
225
if (wasDisabledManually) {
226
// We don't show notifications if a flag has "graduated".
227
if (!(meta->disableFlag & AlwaysDisableHLEFlags())) {
228
*wasDisabledManually = true;
229
}
230
}
231
return false;
232
}
233
return true;
234
}
235
236
bool ShouldHLEModuleByImportName(std::string_view name) {
237
// Check our special metadata lookup. Should probably be merged with the main one.
238
const HLEModuleMeta *meta = GetHLEModuleMetaByImport(name);
239
if (meta) {
240
bool disabled = meta->disableFlag & GetDisableHLEFlags();
241
return !disabled;
242
}
243
244
// Otherwise, just fall back to the regular db. If it's in there, we should HLE it.
245
return GetHLEModuleByName(name) != nullptr;
246
}
247
248
static void hleDelayResultFinish(u64 userdata, int cycleslate) {
249
u32 error;
250
SceUID threadID = (SceUID) userdata;
251
SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_HLEDELAY, error);
252
// The top 32 bits of userdata are the top 32 bits of the 64 bit result.
253
// We can't just put it all in userdata because we need to know the threadID...
254
u64 result = (userdata & 0xFFFFFFFF00000000ULL) | __KernelGetWaitValue(threadID, error);
255
256
if (error == 0 && verify == 1) {
257
__KernelResumeThreadFromWait(threadID, result);
258
__KernelReSchedule("woke from hle delay");
259
}
260
else
261
WARN_LOG(Log::HLE, "Someone else woke up HLE-blocked thread %d?", threadID);
262
}
263
264
void HLEInit() {
265
RegisterAllModules();
266
g_stackSize = 0;
267
delayedResultEvent = CoreTiming::RegisterEvent("HLEDelayedResult", hleDelayResultFinish);
268
idleOp = GetSyscallOp("FakeSysCalls", NID_IDLE);
269
}
270
271
void HLEDoState(PointerWrap &p) {
272
auto s = p.Section("HLE", 1, 2);
273
if (!s)
274
return;
275
276
// Can't be inside a syscall when saving state, reset this so errors aren't misleading.
277
if (g_stackSize) {
278
ERROR_LOG(Log::HLE, "Can't save state while in a HLE syscall");
279
}
280
281
g_stackSize = 0;
282
Do(p, delayedResultEvent);
283
CoreTiming::RestoreRegisterEvent(delayedResultEvent, "HLEDelayedResult", hleDelayResultFinish);
284
285
if (s >= 2) {
286
int actions = (int)mipsCallActions.size();
287
Do(p, actions);
288
if (actions != (int)mipsCallActions.size()) {
289
mipsCallActions.resize(actions);
290
}
291
292
for (auto &action : mipsCallActions) {
293
int actionTypeID = action != nullptr ? action->actionTypeID : -1;
294
Do(p, actionTypeID);
295
if (actionTypeID != -1) {
296
if (p.mode == p.MODE_READ)
297
action = __KernelCreateAction(actionTypeID);
298
action->DoState(p);
299
}
300
}
301
}
302
}
303
304
void HLEShutdown() {
305
hleAfterSyscall = HLE_AFTER_NOTHING;
306
moduleDB.clear();
307
enqueuedMipsCalls.clear();
308
for (auto p : mipsCallActions) {
309
delete p;
310
}
311
mipsCallActions.clear();
312
}
313
314
int GetNumRegisteredHLEModules() {
315
return (int)moduleDB.size();
316
}
317
318
void RegisterHLEModule(std::string_view name, int numFunctions, const HLEFunction *funcTable) {
319
HLEModule module = {name, numFunctions, funcTable};
320
moduleDB.push_back(module);
321
}
322
323
const HLEModule *GetHLEModuleByIndex(int index) {
324
return &moduleDB[index];
325
}
326
327
// TODO: Do something faster.
328
const HLEModule *GetHLEModuleByName(std::string_view name) {
329
for (auto &module : moduleDB) {
330
if (name == module.name) {
331
return &module;
332
}
333
}
334
return nullptr;
335
}
336
337
// TODO: Do something faster.
338
const HLEFunction *GetHLEFuncByName(const HLEModule *module, std::string_view name) {
339
for (int i = 0; i < module->numFunctions; i++) {
340
auto &func = module->funcTable[i];
341
if (func.name == name) {
342
return &func;
343
}
344
}
345
return nullptr;
346
}
347
348
int GetHLEModuleIndex(std::string_view moduleName) {
349
for (size_t i = 0; i < moduleDB.size(); i++)
350
if (moduleDB[i].name == moduleName)
351
return (int)i;
352
return -1;
353
}
354
355
int GetHLEFuncIndexByNib(int moduleIndex, u32 nib) {
356
const HLEModule &module = moduleDB[moduleIndex];
357
for (int i = 0; i < module.numFunctions; i++) {
358
if (module.funcTable[i].ID == nib)
359
return i;
360
}
361
return -1;
362
}
363
364
u32 GetNibByName(std::string_view moduleName, std::string_view function) {
365
int moduleIndex = GetHLEModuleIndex(moduleName);
366
if (moduleIndex == -1)
367
return -1;
368
369
const HLEModule &module = moduleDB[moduleIndex];
370
for (int i = 0; i < module.numFunctions; i++) {
371
if (function == module.funcTable[i].name)
372
return module.funcTable[i].ID;
373
}
374
return -1;
375
}
376
377
const HLEFunction *GetHLEFunc(std::string_view moduleName, u32 nib) {
378
int moduleIndex = GetHLEModuleIndex(moduleName);
379
if (moduleIndex != -1) {
380
int idx = GetHLEFuncIndexByNib(moduleIndex, nib);
381
if (idx != -1)
382
return &(moduleDB[moduleIndex].funcTable[idx]);
383
}
384
return 0;
385
}
386
387
// WARNING: Not thread-safe!
388
const char *GetHLEFuncName(std::string_view moduleName, u32 nib) {
389
_dbg_assert_msg_(!moduleName.empty(), "Invalid module name.");
390
391
const HLEFunction *func = GetHLEFunc(moduleName, nib);
392
if (func)
393
return func->name;
394
395
static char temp[64];
396
snprintf(temp, sizeof(temp), "[UNK: 0x%08x]", nib);
397
return temp;
398
}
399
400
const char *GetHLEFuncName(int moduleIndex, int func) {
401
if (moduleIndex >= 0 && moduleIndex < (int)moduleDB.size()) {
402
const HLEModule &module = moduleDB[moduleIndex];
403
if (func >= 0 && func < module.numFunctions) {
404
return module.funcTable[func].name;
405
}
406
}
407
return "[unknown]";
408
}
409
410
u32 GetSyscallOp(std::string_view moduleName, u32 nib) {
411
// Special case to hook up bad imports.
412
if (moduleName.empty()) {
413
return 0x03FFFFCC; // invalid syscall
414
}
415
416
int modindex = GetHLEModuleIndex(moduleName);
417
if (modindex != -1) {
418
int funcindex = GetHLEFuncIndexByNib(modindex, nib);
419
if (funcindex != -1) {
420
return (0x0000000c | (modindex<<18) | (funcindex<<6));
421
} else {
422
INFO_LOG(Log::HLE, "Syscall (%.*s, %08x) unknown", (int)moduleName.size(), moduleName.data(), nib);
423
return (0x0003FFCC | (modindex<<18)); // invalid syscall
424
}
425
} else {
426
ERROR_LOG(Log::HLE, "Unknown module %.*s!", (int)moduleName.size(), moduleName.data());
427
return 0x03FFFFCC; // invalid syscall (invalid mod index and func index..)
428
}
429
}
430
431
void WriteFuncStub(u32 stubAddr, u32 symAddr)
432
{
433
// Note that this should be J not JAL, as otherwise control will return to the stub..
434
Memory::Write_U32(MIPS_MAKE_J(symAddr), stubAddr);
435
// Note: doing that, we can't trace external module calls, so maybe something else should be done to debug more efficiently
436
// Perhaps a syscall here (and verify support in jit), marking the module by uid (debugIdentifier)?
437
Memory::Write_U32(MIPS_MAKE_NOP(), stubAddr + 4);
438
}
439
440
void WriteFuncMissingStub(u32 stubAddr, u32 nid)
441
{
442
// Write a trap so we notice this func if it's called before resolving.
443
Memory::Write_U32(MIPS_MAKE_JR_RA(), stubAddr); // jr ra
444
Memory::Write_U32(GetSyscallOp("", nid), stubAddr + 4);
445
}
446
447
bool WriteHLESyscall(std::string_view moduleName, u32 nib, u32 address)
448
{
449
if (nib == 0)
450
{
451
WARN_LOG_REPORT(Log::HLE, "Wrote patched out nid=0 syscall (%.*s)", (int)moduleName.size(), moduleName.data());
452
Memory::Write_U32(MIPS_MAKE_JR_RA(), address); //patched out?
453
Memory::Write_U32(MIPS_MAKE_NOP(), address+4); //patched out?
454
return true;
455
}
456
int modindex = GetHLEModuleIndex(moduleName);
457
if (modindex != -1)
458
{
459
Memory::Write_U32(MIPS_MAKE_JR_RA(), address); // jr ra
460
Memory::Write_U32(GetSyscallOp(moduleName, nib), address + 4);
461
return true;
462
}
463
else
464
{
465
ERROR_LOG_REPORT(Log::HLE, "Unable to write unknown syscall: %.*s/%08x", (int)moduleName.size(), moduleName.data(), nib);
466
return false;
467
}
468
}
469
470
void hleCheckCurrentCallbacks()
471
{
472
hleAfterSyscall |= HLE_AFTER_CURRENT_CALLBACKS;
473
}
474
475
void hleReSchedule(const char *reason)
476
{
477
#ifdef _DEBUG
478
_dbg_assert_msg_(reason != nullptr && strlen(reason) < 256, "hleReSchedule: Invalid or too long reason.");
479
#endif
480
481
hleAfterSyscall |= HLE_AFTER_RESCHED;
482
483
if (!reason)
484
hleAfterSyscallReschedReason = "Invalid reason";
485
else
486
hleAfterSyscallReschedReason = reason;
487
}
488
489
void hleReSchedule(bool callbacks, const char *reason)
490
{
491
hleReSchedule(reason);
492
if (callbacks)
493
hleAfterSyscall |= HLE_AFTER_RESCHED_CALLBACKS;
494
}
495
496
void hleRunInterrupts()
497
{
498
hleAfterSyscall |= HLE_AFTER_RUN_INTERRUPTS;
499
}
500
501
void hleDebugBreak()
502
{
503
hleAfterSyscall |= HLE_AFTER_DEBUG_BREAK;
504
}
505
506
void hleSkipDeadbeef()
507
{
508
hleAfterSyscall |= HLE_AFTER_SKIP_DEADBEEF;
509
}
510
511
void hleCoreTimingForceCheck() {
512
hleAfterSyscall |= HLE_AFTER_CORETIMING_FORCE_CHECK;
513
}
514
515
// Pauses execution after an HLE call.
516
static bool hleExecuteDebugBreak(const HLEFunction *func) {
517
if (!func || coreState == CORE_RUNNING_GE) {
518
// Let's break on the next one.
519
return false;
520
}
521
522
const u32 NID_SUSPEND_INTR = 0x092968F4, NID_RESUME_INTR = 0x5F10D406;
523
524
// Never break on these, they're noise.
525
u32 blacklistedNIDs[] = {NID_SUSPEND_INTR, NID_RESUME_INTR, NID_IDLE};
526
for (size_t i = 0; i < ARRAY_SIZE(blacklistedNIDs); ++i)
527
{
528
if (func->ID == blacklistedNIDs[i])
529
return false;
530
}
531
532
INFO_LOG(Log::CPU, "Broke after syscall: %s", func->name);
533
Core_Break(BreakReason::HLEDebugBreak, g_syscallPC);
534
return true;
535
}
536
537
// Should be used *outside* hleLogError for example. Not the other way around.
538
u32 hleDelayResult(u32 result, const char *reason, int usec) {
539
// _dbg_assert_(g_stackSize == 0);
540
541
if (!__KernelIsDispatchEnabled()) {
542
WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", g_stackSize ? g_stack[0]->name : "?");
543
} else {
544
SceUID thread = __KernelGetCurThread();
545
if (KernelIsThreadWaiting(thread))
546
ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", g_stackSize ? g_stack[0]->name : "?");
547
CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, thread);
548
__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, reason);
549
}
550
return result;
551
}
552
553
u64 hleDelayResult(u64 result, const char *reason, int usec) {
554
// Note: hleDelayResult is called at the outer level, *outside* logging.
555
// So, we read from the entry that was just popped. This is OK.
556
// _dbg_assert_(g_stackSize == 0);
557
if (!__KernelIsDispatchEnabled()) {
558
WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", g_stack[0]->name ? g_stack[0]->name : "N/A");
559
} else {
560
// TODO: Defer this, so you can call this multiple times, in case of syscalls calling syscalls? Although, return values are tricky.
561
SceUID thread = __KernelGetCurThread();
562
if (KernelIsThreadWaiting(thread))
563
ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", g_stack[0]->name ? g_stack[0]->name : "N/A");
564
u64 param = (result & 0xFFFFFFFF00000000) | thread;
565
CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, param);
566
__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, (u32)result, 0, false, reason);
567
}
568
return result;
569
}
570
571
void hleEatCycles(int cycles) {
572
if (hleAfterSyscall & HLE_SPLIT_SYSCALL_OVER_GE) {
573
splitSyscallEatCycles = cycles;
574
} else {
575
// Maybe this should Idle, at least for larger delays? Could that cause issues?
576
currentMIPS->downcount -= cycles;
577
}
578
}
579
580
void hleSplitSyscallOverGe() {
581
hleAfterSyscall |= HLE_SPLIT_SYSCALL_OVER_GE;
582
}
583
584
void hleEatMicro(int usec) {
585
hleEatCycles((int) usToCycles(usec));
586
}
587
588
bool hleIsKernelMode() {
589
return g_stackSize && (g_stack[0]->flags & HLE_KERNEL_SYSCALL) != 0;
590
}
591
592
void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction) {
593
std::vector<u32> args;
594
args.resize(argc);
595
memcpy(args.data(), argv, argc * sizeof(u32));
596
597
enqueuedMipsCalls.push_back({ func, afterAction, args });
598
599
hleAfterSyscall |= HLE_AFTER_QUEUED_CALLS;
600
}
601
602
void hleFlushCalls() {
603
u32 &sp = currentMIPS->r[MIPS_REG_SP];
604
PSPPointer<HLEMipsCallStack> stackData;
605
_dbg_assert_(g_stackSize == 0);
606
VERBOSE_LOG(Log::HLE, "Flushing %d HLE mips calls from %s, sp=%08x", (int)enqueuedMipsCalls.size(), g_stackSize ? g_stack[0]->name : "?", sp);
607
608
// First, we'll add a marker for the final return.
609
sp -= sizeof(HLEMipsCallStack);
610
stackData.ptr = sp;
611
stackData->nextOff = 0xFFFFFFFF;
612
stackData->ra = currentMIPS->pc;
613
stackData->v0 = currentMIPS->r[MIPS_REG_V0];
614
stackData->v1 = currentMIPS->r[MIPS_REG_V1];
615
616
// Now we'll set up the first in the chain.
617
currentMIPS->pc = enqueuedMipsCalls[0].func;
618
currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress();
619
for (int i = 0; i < (int)enqueuedMipsCalls[0].args.size(); i++) {
620
currentMIPS->r[MIPS_REG_A0 + i] = enqueuedMipsCalls[0].args[i];
621
}
622
623
// For stack info, process the first enqueued call last, so we run it first.
624
// We don't actually need to store 0's args, but keep it consistent.
625
for (int i = (int)enqueuedMipsCalls.size() - 1; i >= 0; --i) {
626
auto &info = enqueuedMipsCalls[i];
627
u32 stackRequired = (int)info.args.size() * sizeof(u32) + sizeof(HLEMipsCallStack);
628
u32 stackAligned = (stackRequired + 0xF) & ~0xF;
629
630
sp -= stackAligned;
631
stackData.ptr = sp;
632
stackData->nextOff = stackAligned;
633
stackData->func = info.func;
634
if (info.action) {
635
stackData->actionIndex = (int)mipsCallActions.size();
636
mipsCallActions.push_back(info.action);
637
} else {
638
stackData->actionIndex = 0xFFFFFFFF;
639
}
640
stackData->argc = (int)info.args.size();
641
for (int j = 0; j < (int)info.args.size(); ++j) {
642
Memory::Write_U32(info.args[j], sp + sizeof(HLEMipsCallStack) + j * sizeof(u32));
643
}
644
}
645
enqueuedMipsCalls.clear();
646
647
DEBUG_LOG(Log::HLE, "Executing HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp);
648
}
649
650
void HLEReturnFromMipsCall() {
651
u32 &sp = currentMIPS->r[MIPS_REG_SP];
652
PSPPointer<HLEMipsCallStack> stackData;
653
654
// At this point, we may have another mips call to run, or be at the end...
655
stackData.ptr = sp;
656
657
if ((stackData->nextOff & 0x0000000F) != 0 || !Memory::IsValidAddress(sp + stackData->nextOff)) {
658
ERROR_LOG(Log::HLE, "Corrupt stack on HLE mips call return: %08x", stackData->nextOff);
659
Core_UpdateState(CORE_RUNTIME_ERROR);
660
hleNoLogVoid();
661
return;
662
}
663
664
if (stackData->actionIndex != 0xFFFFFFFF && stackData->actionIndex < (u32)mipsCallActions.size()) {
665
PSPAction *&action = mipsCallActions[stackData->actionIndex];
666
VERBOSE_LOG(Log::HLE, "Executing action for HLE mips call at %08x, sp=%08x", stackData->func, sp);
667
668
// Search for the saved v0/v1 values, to preserve the PSPAction API...
669
PSPPointer<HLEMipsCallStack> finalMarker = stackData;
670
while ((finalMarker->nextOff & 0x0000000F) == 0 && Memory::IsValidAddress(finalMarker.ptr + finalMarker->nextOff)) {
671
finalMarker.ptr += finalMarker->nextOff;
672
}
673
674
if (finalMarker->nextOff != 0xFFFFFFFF) {
675
ERROR_LOG(Log::HLE, "Corrupt stack on HLE mips call return action: %08x", finalMarker->nextOff);
676
Core_UpdateState(CORE_RUNTIME_ERROR);
677
hleNoLogVoid();
678
return;
679
}
680
681
MipsCall mc;
682
mc.savedV0 = finalMarker->v0;
683
mc.savedV1 = finalMarker->v1;
684
action->run(mc);
685
finalMarker->v0 = mc.savedV0;
686
finalMarker->v1 = mc.savedV1;
687
688
delete action;
689
action = nullptr;
690
691
// Note: the action could actually enqueue more, adding another layer on stack after this.
692
}
693
694
sp += stackData->nextOff;
695
stackData.ptr = sp;
696
697
if (stackData->nextOff == 0xFFFFFFFF) {
698
// We're done. Grab the HLE result's v0/v1 and return from the syscall.
699
currentMIPS->pc = stackData->ra;
700
currentMIPS->r[MIPS_REG_V0] = stackData->v0;
701
currentMIPS->r[MIPS_REG_V1] = stackData->v1;
702
703
sp += sizeof(HLEMipsCallStack);
704
705
bool canClear = true;
706
for (auto p : mipsCallActions) {
707
canClear = canClear && p == nullptr;
708
}
709
if (canClear) {
710
mipsCallActions.clear();
711
}
712
713
VERBOSE_LOG(Log::HLE, "Finished HLE mips calls, v0=%08x, sp=%08x", currentMIPS->r[MIPS_REG_V0], sp);
714
hleNoLogVoid();
715
return;
716
}
717
718
// Alright, we have another to call.
719
hleSkipDeadbeef();
720
currentMIPS->pc = stackData->func;
721
currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress();
722
for (int i = 0; i < (int)stackData->argc; i++) {
723
currentMIPS->r[MIPS_REG_A0 + i] = Memory::Read_U32(sp + sizeof(HLEMipsCallStack) + i * sizeof(u32));
724
}
725
DEBUG_LOG(Log::HLE, "Executing next HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp);
726
hleNoLogVoid();
727
}
728
729
const static u32 deadbeefRegs[12] = {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF};
730
inline static void SetDeadbeefRegs()
731
{
732
// Not exactly the same, but any time a syscall happens, it should clear ll.
733
currentMIPS->llBit = 0;
734
735
if (g_Config.bSkipDeadbeefFilling)
736
return;
737
738
currentMIPS->r[MIPS_REG_COMPILER_SCRATCH] = 0xDEADBEEF;
739
// Set all the arguments and temp regs. TODO: Use SIMD to just do three writes.
740
memcpy(&currentMIPS->r[MIPS_REG_A0], deadbeefRegs, sizeof(deadbeefRegs));
741
currentMIPS->r[MIPS_REG_T8] = 0xDEADBEEF;
742
currentMIPS->r[MIPS_REG_T9] = 0xDEADBEEF;
743
744
currentMIPS->lo = 0xDEADBEEF;
745
currentMIPS->hi = 0xDEADBEEF;
746
}
747
748
static void hleFinishSyscall(const HLEFunction *info) {
749
if (hleAfterSyscall & HLE_SPLIT_SYSCALL_OVER_GE) {
750
hleAfterSyscall &= ~HLE_SPLIT_SYSCALL_OVER_GE;
751
hleAfterSyscall |= HLE_SPLIT_SYSCALL_PART2;
752
// Switch to GE execution immediately.
753
// coreState is checked after the syscall, always.
754
Core_SwitchToGe();
755
return;
756
}
757
758
if (hleAfterSyscall & HLE_SPLIT_SYSCALL_PART2) {
759
// Eat the extra cycle we added above.
760
hleEatCycles(splitSyscallEatCycles + 1);
761
// Make sure to zero it so it's not accidentally re-used.
762
splitSyscallEatCycles = 0;
763
}
764
765
if (hleAfterSyscall & HLE_AFTER_CORETIMING_FORCE_CHECK) {
766
CoreTiming::ForceCheck();
767
}
768
769
if ((hleAfterSyscall & HLE_AFTER_SKIP_DEADBEEF) == 0)
770
SetDeadbeefRegs();
771
772
if ((hleAfterSyscall & HLE_AFTER_QUEUED_CALLS) != 0)
773
hleFlushCalls();
774
if ((hleAfterSyscall & HLE_AFTER_CURRENT_CALLBACKS) != 0 && (hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) == 0)
775
__KernelForceCallbacks();
776
777
if ((hleAfterSyscall & HLE_AFTER_RUN_INTERRUPTS) != 0)
778
__RunOnePendingInterrupt();
779
780
if ((hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) != 0)
781
__KernelReSchedule(true, hleAfterSyscallReschedReason);
782
else if ((hleAfterSyscall & HLE_AFTER_RESCHED) != 0)
783
__KernelReSchedule(hleAfterSyscallReschedReason);
784
785
if ((hleAfterSyscall & HLE_AFTER_DEBUG_BREAK) != 0) {
786
if (!hleExecuteDebugBreak(info)) {
787
// We'll do it next syscall.
788
hleAfterSyscall = HLE_AFTER_DEBUG_BREAK;
789
hleAfterSyscallReschedReason = 0;
790
return;
791
}
792
}
793
794
hleAfterSyscall = HLE_AFTER_NOTHING;
795
hleAfterSyscallReschedReason = 0;
796
}
797
798
void hleFinishSyscallAfterGe() {
799
hleFinishSyscall(nullptr);
800
}
801
802
static void updateSyscallStats(int modulenum, int funcnum, double total)
803
{
804
const char *name = moduleDB[modulenum].funcTable[funcnum].name;
805
// Ignore this one, especially for msInSyscalls (although that ignores CoreTiming events.)
806
if (0 == strcmp(name, "_sceKernelIdle"))
807
return;
808
809
if (total > kernelStats.slowestSyscallTime)
810
{
811
kernelStats.slowestSyscallTime = total;
812
kernelStats.slowestSyscallName = name;
813
}
814
kernelStats.msInSyscalls += total;
815
816
KernelStatsSyscall statCall(modulenum, funcnum);
817
auto summedStat = kernelStats.summedMsInSyscalls.find(statCall);
818
if (summedStat == kernelStats.summedMsInSyscalls.end())
819
{
820
kernelStats.summedMsInSyscalls[statCall] = total;
821
if (total > kernelStats.summedSlowestSyscallTime)
822
{
823
kernelStats.summedSlowestSyscallTime = total;
824
kernelStats.summedSlowestSyscallName = name;
825
}
826
}
827
else
828
{
829
double newTotal = kernelStats.summedMsInSyscalls[statCall] += total;
830
if (newTotal > kernelStats.summedSlowestSyscallTime)
831
{
832
kernelStats.summedSlowestSyscallTime = newTotal;
833
kernelStats.summedSlowestSyscallName = name;
834
}
835
}
836
}
837
838
static void CallSyscallWithFlags(const HLEFunction *info) {
839
// _dbg_assert_(g_stackSize == 0);
840
g_stackSize = 0;
841
842
const int stackSize = g_stackSize;
843
if (stackSize == 0) {
844
g_stack[0] = info;
845
g_stackSize = 1;
846
}
847
g_syscallPC = currentMIPS->pc;
848
849
const u32 flags = info->flags;
850
851
if (flags & HLE_CLEAR_STACK_BYTES) {
852
u32 stackStart = __KernelGetCurThreadStackStart();
853
if (currentMIPS->r[MIPS_REG_SP] - info->stackBytesToClear >= stackStart) {
854
Memory::Memset(currentMIPS->r[MIPS_REG_SP] - info->stackBytesToClear, 0, info->stackBytesToClear, "HLEStackClear");
855
}
856
}
857
858
if ((flags & HLE_NOT_DISPATCH_SUSPENDED) && !__KernelIsDispatchEnabled()) {
859
RETURN(hleLogDebug(Log::HLE, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch suspended"));
860
} else if ((flags & HLE_NOT_IN_INTERRUPT) && __IsInInterrupt()) {
861
RETURN(hleLogDebug(Log::HLE, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "in interrupt"));
862
} else {
863
info->func();
864
}
865
866
// Now, g_stackSize should be back to 0. Enable this for "pedantic mode", will find a lot of problems.
867
// Check g_stack[0] in the debugger.
868
// _dbg_assert_(g_stackSize == 0);
869
870
if (hleAfterSyscall != HLE_AFTER_NOTHING)
871
hleFinishSyscall(info);
872
else
873
SetDeadbeefRegs();
874
875
g_stackSize = 0;
876
}
877
878
static void CallSyscallWithoutFlags(const HLEFunction *info) {
879
// _dbg_assert_(g_stackSize == 0);
880
g_stackSize = 0;
881
882
const int stackSize = g_stackSize;
883
if (stackSize == 0) {
884
g_stack[0] = info;
885
g_stackSize = 1;
886
}
887
g_syscallPC = currentMIPS->pc;
888
889
info->func();
890
891
// Now, g_stackSize should be back to 0. Enable this for "pedantic mode", will find a lot of problems.
892
// Check g_stack[0] in the debugger.
893
// _dbg_assert_(g_stackSize == 0);
894
895
if (hleAfterSyscall != HLE_AFTER_NOTHING)
896
hleFinishSyscall(info);
897
else
898
SetDeadbeefRegs();
899
900
g_stackSize = 0;
901
}
902
903
const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op) {
904
u32 callno = (op >> 6) & 0xFFFFF; //20 bits
905
int funcnum = callno & 0xFFF;
906
int modulenum = (callno & 0xFF000) >> 12;
907
if (funcnum == 0xfff) {
908
std::string_view modName = modulenum > (int)moduleDB.size() ? "(unknown)" : moduleDB[modulenum].name;
909
ERROR_LOG(Log::HLE, "Unknown syscall: Module: '%.*s' (module: %d func: %d)", (int)modName.size(), modName.data(), modulenum, funcnum);
910
return NULL;
911
}
912
if (modulenum >= (int)moduleDB.size()) {
913
ERROR_LOG(Log::HLE, "Syscall had bad module number %d - probably executing garbage", modulenum);
914
return NULL;
915
}
916
if (funcnum >= moduleDB[modulenum].numFunctions) {
917
ERROR_LOG(Log::HLE, "Syscall had bad function number %d in module %d - probably executing garbage", funcnum, modulenum);
918
return NULL;
919
}
920
return &moduleDB[modulenum].funcTable[funcnum];
921
}
922
923
void *GetQuickSyscallFunc(MIPSOpcode op) {
924
if (coreCollectDebugStats)
925
return nullptr;
926
927
const HLEFunction *info = GetSyscallFuncPointer(op);
928
if (!info || !info->func)
929
return nullptr;
930
931
VERBOSE_LOG(Log::HLE, "Compiling syscall to '%s'", info->name);
932
933
// TODO: Do this with a flag?
934
if (op == idleOp)
935
return (void *)info->func;
936
if (info->flags != 0)
937
return (void *)&CallSyscallWithFlags;
938
return (void *)&CallSyscallWithoutFlags;
939
}
940
941
void hleSetFlipTime(double t) {
942
hleFlipTime = t;
943
}
944
945
void CallSyscall(MIPSOpcode op) {
946
PROFILE_THIS_SCOPE("syscall");
947
double start = 0.0; // need to initialize to fix the race condition where coreCollectDebugStats is enabled in the middle of this func.
948
if (coreCollectDebugStats) {
949
start = time_now_d();
950
}
951
952
const HLEFunction *info = GetSyscallFuncPointer(op);
953
if (!info) {
954
// We haven't incremented the stack yet.
955
RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED);
956
return;
957
}
958
959
if (info->func) {
960
if (op == idleOp)
961
info->func();
962
else if (info->flags != 0)
963
CallSyscallWithFlags(info);
964
else
965
CallSyscallWithoutFlags(info);
966
} else {
967
// We haven't incremented the stack yet.
968
RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED);
969
ERROR_LOG_REPORT(Log::HLE, "Unimplemented HLE function %s", info->name ? info->name : "(\?\?\?)");
970
}
971
972
if (coreCollectDebugStats) {
973
u32 callno = (op >> 6) & 0xFFFFF; //20 bits
974
int funcnum = callno & 0xFFF;
975
int modulenum = (callno & 0xFF000) >> 12;
976
double total = time_now_d() - start;
977
if (total >= hleFlipTime)
978
total -= hleFlipTime;
979
_dbg_assert_msg_(total >= 0.0, "Time spent in syscall became negative");
980
hleFlipTime = 0.0;
981
updateSyscallStats(modulenum, funcnum, total);
982
}
983
}
984
985
void hlePushFuncDesc(std::string_view module, std::string_view funcName) {
986
const HLEModule *mod = GetHLEModuleByName(module);
987
_dbg_assert_(mod != nullptr);
988
if (!mod) {
989
return;
990
}
991
const HLEFunction *func = GetHLEFuncByName(mod, funcName);
992
_dbg_assert_(func != nullptr);
993
// Push to the stack. Be careful (due to the nasty adhoc thread..)
994
int stackSize = g_stackSize;
995
if (stackSize >= 0 && stackSize < ARRAY_SIZE(g_stack)) {
996
g_stack[stackSize] = func;
997
g_stackSize = stackSize + 1;
998
}
999
}
1000
1001
// TODO: Also add support for argument names.
1002
size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) {
1003
char *p = message;
1004
size_t used = 0;
1005
1006
#define APPEND_FMT(...) do { \
1007
if (used < sz) { \
1008
size_t c = snprintf(p, sz - used, __VA_ARGS__); \
1009
used += c; \
1010
p += c; \
1011
} \
1012
} while (false)
1013
1014
int reg = 0;
1015
int regf = 0;
1016
for (size_t i = 0, n = strlen(argmask); i < n; ++i, ++reg) {
1017
u32 regval;
1018
if (reg < 8) {
1019
regval = PARAM(reg);
1020
} else {
1021
u32 sp = currentMIPS->r[MIPS_REG_SP];
1022
// Goes upward on stack.
1023
// NOTE: Currently we only support > 8 for 32-bit integer args.
1024
regval = Memory::Read_U32(sp + (reg - 8) * 4);
1025
}
1026
1027
switch (argmask[i]) {
1028
case 'p':
1029
if (Memory::IsValidAddress(regval)) {
1030
APPEND_FMT("%08x[%08x]", regval, Memory::Read_U32(regval));
1031
} else {
1032
APPEND_FMT("%08x[invalid]", regval);
1033
}
1034
break;
1035
1036
case 'P':
1037
if (Memory::IsValidAddress(regval)) {
1038
APPEND_FMT("%08x[%016llx]", regval, Memory::Read_U64(regval));
1039
} else {
1040
APPEND_FMT("%08x[invalid]", regval);
1041
}
1042
break;
1043
1044
case 's':
1045
if (Memory::IsValidAddress(regval)) {
1046
const char *s = Memory::GetCharPointer(regval);
1047
const int safeLen = Memory::ValidSize(regval, 128);
1048
if (strnlen(s, safeLen) >= safeLen) {
1049
APPEND_FMT("%.*s...", safeLen, Memory::GetCharPointer(regval));
1050
} else {
1051
APPEND_FMT("%.*s", safeLen, Memory::GetCharPointer(regval));
1052
}
1053
} else {
1054
APPEND_FMT("(invalid)");
1055
}
1056
break;
1057
1058
case 'x':
1059
APPEND_FMT("%08x", regval);
1060
break;
1061
1062
case 'i':
1063
APPEND_FMT("%d", regval);
1064
break;
1065
1066
case 'X':
1067
case 'I':
1068
// 64-bit regs are always aligned.
1069
if ((reg & 1))
1070
++reg;
1071
APPEND_FMT("%016llx", PARAM64(reg));
1072
++reg;
1073
break;
1074
1075
case 'f':
1076
APPEND_FMT("%f", PARAMF(regf++));
1077
// This doesn't consume a gp reg.
1078
--reg;
1079
break;
1080
1081
// TODO: Double? Does it ever happen?
1082
1083
default:
1084
_dbg_assert_msg_(false, "Invalid argmask character: %c", argmask[i]);
1085
APPEND_FMT(" -- invalid arg format: %c -- %08x", argmask[i], regval);
1086
break;
1087
}
1088
if (i + 1 < n) {
1089
APPEND_FMT(", ");
1090
}
1091
}
1092
1093
if (used > sz) {
1094
message[sz - 1] = '\0';
1095
} else {
1096
message[used] = '\0';
1097
}
1098
1099
#undef APPEND_FMT
1100
return used;
1101
}
1102
1103
void hleLeave() {
1104
int stackSize = g_stackSize;
1105
//_dbg_assert_(stackSize > 0);
1106
if (stackSize > 0) {
1107
g_stackSize = stackSize - 1;
1108
} // else warn?
1109
}
1110
1111
void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, const char *reason, const char *formatted_reason) {
1112
char formatted_args[2048];
1113
const char *funcName = "?";
1114
u32 funcFlags = 0;
1115
1116
const int stackSize = g_stackSize;
1117
if (stackSize <= 0) {
1118
ERROR_LOG(Log::HLE, "HLE function stack mismatch (%s:%d)! stackSize = %d", file, line, stackSize);
1119
return;
1120
}
1121
1122
const HLEFunction *hleFunc = g_stack[g_stackSize - 1];
1123
1124
char retmask = hleFunc->retmask;
1125
if (stackSize) {
1126
_dbg_assert_(hleFunc->argmask != nullptr);
1127
1128
// NOTE: For second stack level, we can't get arguments (unless we somehow get them from the host stack!)
1129
// Need to do something smart in hleCall. But it's better than printing function name and args from the wrong function.
1130
1131
if (stackSize == 1) {
1132
hleFormatLogArgs(formatted_args, sizeof(formatted_args), hleFunc->argmask);
1133
} else {
1134
truncate_cpy(formatted_args, "...N/A...");
1135
}
1136
1137
funcName = hleFunc->name;
1138
funcFlags = hleFunc->flags;
1139
} else {
1140
formatted_args[0] = '?';
1141
formatted_args[1] = '\0';
1142
}
1143
1144
const char *fmt;
1145
const char *errStr = nullptr;
1146
switch (retmask) {
1147
case 'x':
1148
// Truncate the high bits of the result (from any sign extension.)
1149
res = (u32)res;
1150
if ((int)res < 0 && (errStr = KernelErrorToString((u32)res))) {
1151
// It's a known syscall error code, let's display it as string.
1152
fmt = "%sSCE_KERNEL_ERROR_%s=%s(%s)%s";
1153
} else {
1154
fmt = "%s%08llx=%s(%s)%s";
1155
errStr = nullptr; // We check errstr later to determine which format to use.
1156
}
1157
break;
1158
case 'i':
1159
case 'I':
1160
if ((int)res < 0 && (errStr = KernelErrorToString((u32)res))) {
1161
// It's a known syscall error code, let's display it as string.
1162
fmt = "%s%s=%s(%s)%s";
1163
} else {
1164
fmt = "%s%lld=%s(%s)%s";
1165
errStr = nullptr; // We check errstr later to determine which format to use.
1166
}
1167
break;
1168
case 'f':
1169
// TODO: For now, floats are just shown as bits.
1170
fmt = "%s%08llx=%s(%s)%s";
1171
break;
1172
case 'v':
1173
// Void. Return value should not be shown. (the first %s is the "K " string, see below).
1174
fmt = "%s%s(%s)%s";
1175
break;
1176
default:
1177
_dbg_assert_msg_(false, "Invalid return format: %c", retmask);
1178
fmt = "%s%08llx=%s(%s)%s";
1179
break;
1180
}
1181
1182
const char *kernelFlag = (funcFlags & HLE_KERNEL_SYSCALL) ? "K " : "";
1183
if (retmask != 'v') {
1184
if (errStr) {
1185
GenericLog(t, level, file, line, fmt, kernelFlag, errStr, funcName, formatted_args, formatted_reason);
1186
} else {
1187
GenericLog(t, level, file, line, fmt, kernelFlag, res, funcName, formatted_args, formatted_reason);
1188
}
1189
} else {
1190
// Skipping the res argument for this format string.
1191
GenericLog(t, level, file, line, fmt, kernelFlag, funcName, formatted_args, formatted_reason);
1192
}
1193
1194
if (reportTag) {
1195
// A blank string means always log, not just once.
1196
if (reportTag[0] == '\0' || Reporting::ShouldLogNTimes(reportTag, 1)) {
1197
// Here we want the original key, so that different args, etc. group together.
1198
std::string key = std::string(kernelFlag) + std::string("%08x=") + funcName + "(%s)";
1199
if (reason != nullptr) {
1200
key += ": ";
1201
key += reason;
1202
}
1203
1204
char formatted_message[8192];
1205
if (retmask != 'v') {
1206
if (errStr) {
1207
snprintf(formatted_message, sizeof(formatted_message), fmt, kernelFlag, errStr, funcName, formatted_args, formatted_reason);
1208
} else {
1209
snprintf(formatted_message, sizeof(formatted_message), fmt, kernelFlag, res, funcName, formatted_args, formatted_reason);
1210
}
1211
} else {
1212
snprintf(formatted_message, sizeof(formatted_message), fmt, kernelFlag, funcName, formatted_args, formatted_reason);
1213
}
1214
Reporting::ReportMessageFormatted(key.c_str(), formatted_message);
1215
}
1216
}
1217
}
1218
1219