Merge pull request #22069 from hrydgard/read-u32-cleanups

Tech debt: Start cleaning up HLE memory access
This commit is contained in:
Henrik Rydgård
2026-08-10 12:09:50 +02:00
committed by GitHub
22 changed files with 387 additions and 276 deletions
+72 -4
View File
@@ -602,9 +602,11 @@ const char *MemoryExceptionTypeAsString(MemoryExceptionType type) {
switch (type) {
case MemoryExceptionType::UNKNOWN: return "Unknown";
case MemoryExceptionType::READ_WORD: return "Read Word";
case MemoryExceptionType::WRITE_WORD: return "Write Word";
case MemoryExceptionType::READ_BLOCK: return "Read Block";
case MemoryExceptionType::WRITE_WORD: return "Write Word";
case MemoryExceptionType::WRITE_BLOCK: return "Read/Write Block";
case MemoryExceptionType::HLE_READ: return "HLE Read";
case MemoryExceptionType::HLE_WRITE: return "HLE Write";
case MemoryExceptionType::ALIGNMENT: return "Alignment";
default:
return "N/A";
@@ -638,14 +640,13 @@ static std::string ModuleAddressSuffix(u32 address) {
}
}
void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo, bool forceReport) {
const char *desc = MemoryExceptionTypeAsString(type);
void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo) {
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
char pcDetails[128];
pcDetails[0] = 0;
if ((CPUCore)g_Config.iCpuCore == CPUCore::INTERPRETER) {
snprintf(pcDetails, sizeof(pcDetails), " PC %08x%s LR %08x%s", currentMIPS->pc, ModuleAddressSuffix(currentMIPS->pc).c_str(), currentMIPS->r[MIPS_REG_RA], ModuleAddressSuffix(currentMIPS->r[MIPS_REG_RA]).c_str());
snprintf(pcDetails, sizeof(pcDetails), " PC %08x%s RA %08x%s", currentMIPS->pc, ModuleAddressSuffix(currentMIPS->pc).c_str(), currentMIPS->r[MIPS_REG_RA], ModuleAddressSuffix(currentMIPS->r[MIPS_REG_RA]).c_str());
}
const std::string addressSuffix = ModuleAddressSuffix(address);
@@ -663,6 +664,7 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy
break;
}
const char *desc = MemoryExceptionTypeAsString(type);
if (action == ExceptionAction::Ignore) {
// Simplest logging and continue.
WARN_LOG(Log::MemMap, "%s: Invalid access at %08x%s (size %08x) %s%.*s", desc, address, addressSuffix.c_str(), accessSize, pcDetails, (int)additionalInfo.length(), additionalInfo.data());
@@ -686,6 +688,72 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy
}
}
void Core_MemoryExceptionHLE(MIPSState *mips, u32 address, u32 accessSize, MemoryExceptionType type) {
ExceptionAction action;
switch (type) {
case MemoryExceptionType::HLE_WRITE:
action = ResolveExceptionAction((ExceptionAction)g_Config.iExceptionActionMemWrite);
break;
case MemoryExceptionType::HLE_READ:
action = ResolveExceptionAction((ExceptionAction)g_Config.iExceptionActionMemRead);
break;
default:
_dbg_assert_(false);
action = ExceptionAction::Break;
break;
}
const HLEFunction *func = HLEGetFunctionBeingCalled();
const char *funcName = func ? func->name : "unknown";
char args[512] = "";
if (func) {
HLEFormatLogArgs(mips, args, sizeof(args), func->argmask);
}
const char *extra = "";
// We do report some unaligned addresses. There are probably more that should report.
// We try to derive the reason here, though maybe it should be passed in explicitly?
// TODO: This check should probably be added to regular memory accesses too.
if (Memory::IsValidAddress(address)) {
if (accessSize == 2 || accessSize == 4 || accessSize == 8 || (address & (accessSize - 1))) {
extra = " (unaligned)";
} else if (accessSize > 8 && (accessSize & 3)) {
extra = " (unaligned struct)";
}
}
const u32 pc = mips->pc;
char msg[512];
snprintf(msg, sizeof(msg), "Invalid access in %s(%s) %s at %08x%s (size %08x) PC %08x%s RA %08x%s",
funcName, args,
extra, address, ModuleAddressSuffix(address).c_str(), accessSize,
pc, ModuleAddressSuffix(pc).c_str(),
mips->r[MIPS_REG_RA], ModuleAddressSuffix(mips->r[MIPS_REG_RA]).c_str());
const char *desc = MemoryExceptionTypeAsString(type);
if (action == ExceptionAction::Ignore) {
// Simplest logging and continue.
WARN_LOG(Log::MemMap, "HLE %s: %s", MemoryExceptionTypeAsString(type), msg);
return;
}
const std::string stackTrace = FormatStackTrace(WalkCurrentStack(-1));
ERROR_LOG(Log::MemMap, "%s: %s\n%s", desc, msg, stackTrace.c_str());
if (action == ExceptionAction::Break) {
MIPSExceptionInfo &e = g_exceptionInfo;
e = {};
e.type = MIPSExceptionType::MEMORY;
e.info.clear();
e.memory_type = type;
e.address = address;
e.accessSize = accessSize;
e.stackTrace = stackTrace;
e.pc = pc;
Core_Break(BreakReason::MemoryException, address);
}
}
// Can't be ignored, must break. Not sure we can get a meaningful stack trace here (since the PC is invalid).
void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) {
const char *desc = ExecExceptionTypeAsString(type);
+7 -2
View File
@@ -20,7 +20,6 @@
#include <cstdint>
#include <functional>
#include <mutex>
#include <string>
#include <string_view>
#include "Common/CommonTypes.h"
@@ -199,6 +198,8 @@ enum class MemoryExceptionType {
UNKNOWN,
READ_WORD,
WRITE_WORD,
HLE_READ,
HLE_WRITE,
READ_BLOCK,
WRITE_BLOCK,
ALIGNMENT,
@@ -208,12 +209,16 @@ enum class ExecExceptionType {
THREAD,
};
void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo = "", bool forceReport = false);
void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo = "");
void Core_ExecException(u32 address, u32 pc, ExecExceptionType type);
void Core_BreakException(u32 pc);
// Call when loading save states, etc.
void Core_ResetException();
class MIPSState;
// Shortcut, just calls Core_MemoryException with automatically determined parameters (function name, etc).
void Core_MemoryExceptionHLE(MIPSState *mips, u32 address, u32 accessSize, MemoryExceptionType type);
enum class MIPSExceptionType {
NONE,
MEMORY,
+5 -2
View File
@@ -919,7 +919,7 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat,
float f;
uint32_t u;
} value;
value.u = Memory::Read_U32(op.addr);
value.u = Memory::ReadUnchecked_U32(op.addr); // we check the range above
std::string shaderName = shaderChain[op.PostShaderUniform.shader]->section;
switch (op.PostShaderUniform.format) {
case 0:
@@ -1035,8 +1035,11 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat,
case CheatOp::CwCheatPointerCommands:
{
if (!Memory::IsValidAddress(op.addr + op.pointerCommands.baseOffset)) {
break;
}
InvalidateICache(op.addr + op.pointerCommands.baseOffset, 4); // See note at top of file
u32 base = Memory::Read_U32(op.addr + op.pointerCommands.baseOffset);
u32 base = Memory::ReadUnchecked_U32(op.addr + op.pointerCommands.baseOffset);
u32 val = op.val;
int type = op.pointerCommands.type;
for (int a = 0; a < op.pointerCommands.count; ++a) {
+10 -5
View File
@@ -790,7 +790,12 @@ void DisassemblyData::createLines()
lineAddresses.clear();
u32 pos = address;
const u32 end = address+size;
const u32 end = address + size;
if (!Memory::IsValidRange(address, size)) {
ERROR_LOG(Log::CPU, "DisassemblyData can't create lines for invalid range 0x%08X-0x%08X", address, end);
}
const u32 maxChars = g_disassemblyManager.getMaxParamChars();
std::string currentLine;
@@ -802,7 +807,7 @@ void DisassemblyData::createLines()
bool inString = false;
while (pos < end)
{
u8 b = Memory::Read_U8(pos++);
u8 b = Memory::ReadUnchecked_U8(pos++);
if (b >= 0x20 && b <= 0x7F)
{
if (currentLine.size()+1 >= maxChars)
@@ -879,18 +884,18 @@ void DisassemblyData::createLines()
switch (type)
{
case DATATYPE_BYTE:
value = Memory::Read_U8(pos);
value = Memory::ReadUnchecked_U8(pos);
snprintf(buffer, sizeof(buffer), "0x%02X", value);
pos++;
break;
case DATATYPE_HALFWORD:
value = Memory::Read_U16(pos);
value = Memory::ReadUnchecked_U16(pos);
snprintf(buffer, sizeof(buffer), "0x%04X", value);
pos += 2;
break;
case DATATYPE_WORD:
{
value = Memory::Read_U32(pos);
value = Memory::ReadUnchecked_U32(pos);
const std::string label = g_symbolMap->GetLabelString(value);
if (!label.empty())
snprintf(buffer, sizeof(buffer), "%s", label.c_str());
+8 -8
View File
@@ -182,7 +182,7 @@ void WebSocketMemoryReadU32(DebuggerRequest &req) {
Core_RunOnCPUThread([&] {
AutoDisabledReplacements memLock = LockMemory(true);
JsonWriter &json = req.Respond();
json.writeUint("value", Memory::Read_U32(addr));
json.writeUint("value", Memory::ReadUnchecked_U32(addr));
});
}
@@ -334,7 +334,7 @@ void WebSocketMemoryWriteU8(DebuggerRequest &req) {
// Write two bytes to memory (memory.write_u16)
//
// Parameters:
// - address: unsigned integer
// - address: unsigned integer (can be unaligned! But not recommended. Should maybe disallow).
// - value: unsigned integer
//
// Response (same event name):
@@ -352,7 +352,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) {
return req.Fail("CPU not started");
// This only depends on addr, not on anything CPU-thread-owned, so fail fast here rather than
// making a round trip through the queue for a request we already know is invalid.
if (!Memory::IsValidAddress(addr))
if (!Memory::IsValidRange(addr, 2))
return req.Fail("Invalid address");
// Route the actual memory write to the CPU thread instead of poking at it directly
@@ -360,7 +360,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) {
Core_RunOnCPUThread([&] {
AutoDisabledReplacements memLock = LockMemory(true);
currentMIPS->InvalidateICache(addr, 2);
Memory::Write_U16(val, addr);
Memory::WriteUnchecked_U16(val, addr);
Reporting::NotifyDebugger();
JsonWriter &json = req.Respond();
@@ -371,7 +371,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) {
// Write four bytes to memory (memory.write_u32)
//
// Parameters:
// - address: unsigned integer
// - address: unsigned integer (can be unaligned! But not recommended. Should maybe disallow).
// - value: unsigned integer
//
// Response (same event name):
@@ -389,7 +389,7 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) {
return req.Fail("CPU not started");
// This only depends on addr, not on anything CPU-thread-owned, so fail fast here rather than
// making a round trip through the queue for a request we already know is invalid.
if (!Memory::IsValidAddress(addr))
if (!Memory::IsValidRange(addr, 4))
return req.Fail("Invalid address");
// Route the actual memory write to the CPU thread instead of poking at it directly
@@ -397,11 +397,11 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) {
Core_RunOnCPUThread([&] {
AutoDisabledReplacements memLock = LockMemory(true);
currentMIPS->InvalidateICache(addr, 4);
Memory::Write_U32(val, addr);
Memory::WriteUnchecked_U32(val, addr);
Reporting::NotifyDebugger();
JsonWriter &json = req.Respond();
json.writeUint("value", Memory::Read_U32(addr));
json.writeUint("value", Memory::ReadUnchecked_U32(addr));
});
}
+15 -18
View File
@@ -43,25 +43,16 @@ const u32 PSP_UTILITY_GAMEDATA_MODE_SHOW_PROGRESS = 1;
static const std::string SFO_FILENAME = "PARAM.SFO";
namespace
{
std::vector<std::string> GetPSPFileList (const std::string &dirpath) {
std::vector<std::string> FileList;
auto Fileinfos = pspFileSystem.GetDirListing(dirpath);
FileList.reserve(Fileinfos.size());
static std::vector<std::string> GetPSPFileList(std::string_view dirpath) {
std::vector<std::string> FileList;
auto Fileinfos = pspFileSystem.GetDirListing(dirpath);
FileList.reserve(Fileinfos.size());
for (auto it = Fileinfos.begin(); it != Fileinfos.end(); ++it) {
std::string info = (*it).name;
FileList.push_back(info);
}
return FileList;
for (auto it = Fileinfos.begin(); it != Fileinfos.end(); ++it) {
std::string info = (*it).name;
FileList.push_back(info);
}
}
PSPGamedataInstallDialog::PSPGamedataInstallDialog(UtilityDialogType type) : PSPDialog(type) {
}
PSPGamedataInstallDialog::~PSPGamedataInstallDialog() {
return FileList;
}
int PSPGamedataInstallDialog::Init(u32 paramAddr) {
@@ -70,6 +61,12 @@ int PSPGamedataInstallDialog::Init(u32 paramAddr) {
return SCE_ERROR_UTILITY_INVALID_STATUS;
}
if (!Memory::IsValidRange(paramAddr, sizeof(SceUtilityGamedataInstallParam))) {
// This should probably crash
ERROR_LOG(Log::sceUtility, "sceGamedataInstallInitStart: invalid param address 0x%08X", paramAddr);
return SCE_KERNEL_ERROR_INVALID_POINTER;
}
param.ptr = paramAddr;
inFileNames = GetPSPFileList("disc0:/PSP_GAME/INSDIR");
numFiles = (int)inFileNames.size();
@@ -90,7 +87,7 @@ int PSPGamedataInstallDialog::Init(u32 paramAddr) {
return -1;
}
int size = Memory::Read_U32(paramAddr);
const int size = Memory::ReadUnchecked_U32(paramAddr);
if (size != 1424 && size != 1432) {
ERROR_LOG_REPORT(Log::sceUtility, "sceGamedataInstallInitStart: invalid param size %d", size);
return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE;
+1 -2
View File
@@ -35,8 +35,7 @@ struct SceUtilityGamedataInstallParam {
class PSPGamedataInstallDialog: public PSPDialog {
public:
PSPGamedataInstallDialog(UtilityDialogType type);
~PSPGamedataInstallDialog();
PSPGamedataInstallDialog(UtilityDialogType type) : PSPDialog(type) {}
int Init(u32 paramAddr);
int Update(int animSpeed) override;
+7 -5
View File
@@ -57,12 +57,14 @@ int PSPMsgDialog::Init(unsigned int paramAddr) {
}
messageDialogAddr = paramAddr;
if (!Memory::IsValidAddress(messageDialogAddr))
{
return 0;
if (!Memory::IsValid4AlignedAddress(paramAddr)) {
// What to do?
return SCE_KERNEL_ERROR_BAD_ARGUMENT;
}
int size = Memory::Read_U32(paramAddr);
memset(&messageDialog,0,sizeof(messageDialog));
int size = Memory::ReadUnchecked_U32(paramAddr);
memset(&messageDialog, 0, sizeof(messageDialog));
// Only copy the right size to support different request format
Memory::Memcpy(&messageDialog,paramAddr,size);
+8 -3
View File
@@ -59,16 +59,21 @@ int PSPNetconfDialog::Init(u32 paramAddr) {
if (ReadStatus() != SCE_UTILITY_STATUS_NONE)
return SCE_ERROR_UTILITY_INVALID_STATUS;
if (!Memory::IsValid4AlignedRange(paramAddr, sizeof(request))) {
// What to do?
return SCE_KERNEL_ERROR_BAD_ARGUMENT;
}
NOTICE_LOG(Log::sceUtility, "PSPNetConfDialog Init");
jsonReady_ = false;
// Kick off a request to the infra-dns.json since we'll need it later.
StartInfraJsonDownload();
requestAddr = paramAddr;
int size = Memory::Read_U32(paramAddr);
const u32 size = Memory::ReadUnchecked_U32(paramAddr);
memset(&request, 0, sizeof(request));
// Only copy the right size to support different request format
Memory::Memcpy(&request, paramAddr, size);
// Only copy the right size (bounded by the struct) to support different request format
Memory::Memcpy(&request, paramAddr, std::min(size, (u32)sizeof(request)));
ChangeStatusInit(NET_INIT_DELAY_US);
+17 -8
View File
@@ -1009,7 +1009,7 @@ void hlePushFuncDesc(std::string_view module, std::string_view funcName) {
}
// TODO: Also add support for argument names.
size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) {
size_t HLEFormatLogArgs(const MIPSState *mips, char *message, size_t sz, const char *argmask) {
char *p = message;
size_t used = 0;
@@ -1026,9 +1026,9 @@ size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) {
for (size_t i = 0, n = strlen(argmask); i < n; ++i, ++reg) {
u32 regval;
if (reg < 8) {
regval = PARAM(reg);
regval = PARAM_MIPS(mips, reg);
} else {
u32 sp = currentMIPS->r[MIPS_REG_SP];
u32 sp = mips->r[MIPS_REG_SP];
// Goes upward on stack.
// NOTE: Currently we only support > 8 for 32-bit integer args.
regval = Memory::Read_U32(sp + (reg - 8) * 4);
@@ -1036,16 +1036,16 @@ size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) {
switch (argmask[i]) {
case 'p':
if (Memory::IsValidAddress(regval)) {
APPEND_FMT("%08x[%08x]", regval, Memory::Read_U32(regval));
if (Memory::IsValidRange(regval, 4)) {
APPEND_FMT("%08x[%08x]", regval, Memory::ReadUnchecked_U32(regval));
} else {
APPEND_FMT("%08x[invalid]", regval);
}
break;
case 'P':
if (Memory::IsValidAddress(regval)) {
APPEND_FMT("%08x[%016llx]", regval, Memory::Read_U64(regval));
if (Memory::IsValidRange(regval, 8)) {
APPEND_FMT("%08x[%016llx]", regval, Memory::ReadUnchecked_U64(regval));
} else {
APPEND_FMT("%08x[invalid]", regval);
}
@@ -1088,6 +1088,7 @@ size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) {
--reg;
break;
// TODO: Double? Does it ever happen?
default:
@@ -1118,6 +1119,14 @@ void hleLeave() {
} // else warn?
}
const HLEFunction *HLEGetFunctionBeingCalled() {
int stackSize = g_stackSize;
if (stackSize > 0) {
return g_stack[stackSize - 1];
}
return nullptr;
}
void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, const char *reason, const char *formatted_reason) {
char formatted_args[2048];
const char *funcName = "?";
@@ -1139,7 +1148,7 @@ void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line
// Need to do something smart in hleCall. But it's better than printing function name and args from the wrong function.
if (stackSize == 1) {
hleFormatLogArgs(formatted_args, sizeof(formatted_args), hleFunc->argmask);
HLEFormatLogArgs(currentMIPS, formatted_args, sizeof(formatted_args), hleFunc->argmask);
} else {
truncate_cpy(formatted_args, "...N/A...");
}
+4
View File
@@ -98,6 +98,8 @@ struct Syscall {
#define RETURN64(n) {u64 RETURN64_tmp = n; currentMIPS->r[MIPS_REG_V0] = RETURN64_tmp & 0xFFFFFFFF; currentMIPS->r[MIPS_REG_V1] = RETURN64_tmp >> 32;}
#define RETURNF(fl) currentMIPS->f[0] = fl
#define PARAM_MIPS(mips, n) mips->r[MIPS_REG_A0 + n]
struct HLEModuleMeta {
// This is the modname (name from the PRX header). Probably, we should really blacklist on the module names of the exported symbol metadata.
const char *modname;
@@ -174,6 +176,8 @@ inline s64 hleDelayResult(s64 result, const char *reason, int usec) {
void HLEInit();
void HLEDoState(PointerWrap &p);
void HLEShutdown();
const HLEFunction *HLEGetFunctionBeingCalled();
size_t HLEFormatLogArgs(const MIPSState *mips, char *message, size_t sz, const char *argmask);
u32 GetSyscallOp(std::string_view module, u32 nib);
bool WriteHLESyscall(std::string_view module, u32 nib, u32 address);
void CallSyscall(MIPSOpcode op);
+12 -7
View File
@@ -728,16 +728,21 @@ static bool GetMIPSGPAddress(u32 &addr, s32 offset) {
static int Hook_godseaterburst_blit_texture() {
u32 texaddr;
// Only if there's no texture.
if (!GetMIPSStaticAddress(texaddr, 0x000c, 0x0030)) {
return 0;
}
u32 fb_infoaddr;
if (Memory::Read_U32(texaddr) != 0 || !GetMIPSStaticAddress(fb_infoaddr, 0x01d0, 0x01d4)) {
if (!GetMIPSStaticAddress(texaddr, 0x000c, 0x0030) || !Memory::IsValid4AlignedAddress(texaddr)) {
return 0;
}
const u32 fb_info = Memory::Read_U32(fb_infoaddr);
const u32 fb_address = Memory::Read_U32(fb_info);
u32 fb_infoaddr;
if (Memory::ReadUnchecked_U32(texaddr) != 0 || !GetMIPSStaticAddress(fb_infoaddr, 0x01d0, 0x01d4) || !Memory::IsValid4AlignedAddress(fb_infoaddr)) {
return 0;
}
const u32 fb_info = Memory::ReadUnchecked_U32(fb_infoaddr);
if (!Memory::IsValid4AlignedAddress(fb_info)) {
return 0;
}
const u32 fb_address = Memory::ReadUnchecked_U32(fb_info);
if (Memory::IsVRAMAddress(fb_address)) {
gpu->PerformReadbackToMemory(fb_address, 0x00044000);
NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "godseaterburst_blit_texture");
+83 -69
View File
@@ -18,11 +18,13 @@
#include <algorithm>
#include <map>
#include <unordered_map>
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/Serialize/SerializeMap.h"
#include "Core/MemMapHelpers.h"
#include "Core/HLE/HLE.h"
#include "Core/Core.h"
#include "Core/HLE/ErrorCodes.h"
#include "Core/MIPS/MIPS.h"
#include "Core/CoreTiming.h"
@@ -317,9 +319,13 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio
}
if (optionsPtr != 0) {
u32 size = Memory::Read_U32(optionsPtr);
if (size > 4)
WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateMutex(%s) unsupported options parameter, size = %d", name, size);
if (Memory::IsValid4AlignedAddress(optionsPtr)) {
u32 size = Memory::ReadUnchecked_U32(optionsPtr);
if (size > 4)
WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateMutex(%s) unsupported options parameter, size = %d", name, size);
} else {
Core_MemoryExceptionHLE(currentMIPS, optionsPtr, 4, MemoryExceptionType::HLE_READ);
}
}
if ((attr & ~PSP_MUTEX_ATTR_KNOWN) != 0)
WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateMutex(%s) unsupported attr parameter: %08x", name, attr);
@@ -327,8 +333,7 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio
return hleLogDebug(Log::sceKernel, id);
}
int sceKernelDeleteMutex(SceUID id)
{
int sceKernelDeleteMutex(SceUID id) {
u32 error;
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(id, error);
if (!mutex) {
@@ -365,8 +370,7 @@ static bool __KernelLockMutexCheck(PSPMutex *mutex, int count, u32 &error) {
else if (count + mutex->nm.lockLevel < 0)
error = SCE_MUTEX_ERROR_LOCK_OVERFLOW;
// Only a recursive mutex can re-lock.
else if (mutex->nm.lockThread == __KernelGetCurThread())
{
else if (mutex->nm.lockThread == __KernelGetCurThread()) {
if (mutexIsRecursive)
return true;
@@ -422,14 +426,12 @@ static bool __KernelUnlockMutex(PSPMutex *mutex, u32 &error) {
return wokeThreads;
}
void __KernelMutexTimeout(u64 userdata, int cyclesLate)
{
void __KernelMutexTimeout(u64 userdata, int cyclesLate) {
SceUID threadID = (SceUID)userdata;
HLEKernel::WaitExecTimeout<PSPMutex, WAITTYPE_MUTEX>(threadID);
}
void __KernelMutexThreadEnd(SceUID threadID)
{
void __KernelMutexThreadEnd(SceUID threadID) {
u32 error;
// If it was waiting on the mutex, it should finish now.
@@ -457,11 +459,14 @@ void __KernelMutexThreadEnd(SceUID threadID)
}
}
// The timeoutPtr is assumed to be checked by the caller to either be 0 or valid.
static void __KernelWaitMutex(PSPMutex *mutex, u32 timeoutPtr) {
_dbg_assert_(mutexWaitTimer != -1); // this could only come from extremely old savestates.
if (timeoutPtr == 0 || mutexWaitTimer == -1)
return;
int micro = (int) Memory::Read_U32(timeoutPtr);
int micro = (int) Memory::ReadUnchecked_U32(timeoutPtr);
// This happens to be how the hardware seems to time things.
if (micro <= 3)
@@ -473,8 +478,7 @@ static void __KernelWaitMutex(PSPMutex *mutex, u32 timeoutPtr) {
CoreTiming::ScheduleEvent(usToCycles(micro), mutexWaitTimer, __KernelGetCurThread());
}
int sceKernelCancelMutex(SceUID uid, int count, u32 numWaitThreadsPtr)
{
int sceKernelCancelMutex(SceUID uid, int count, u32 numWaitThreadsPtr) {
u32 error;
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(uid, error);
if (!mutex) {
@@ -520,15 +524,20 @@ int sceKernelCancelMutex(SceUID uid, int count, u32 numWaitThreadsPtr)
}
}
// int sceKernelLockMutex(SceUID id, int count, int *timeout)
int sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr)
{
int sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) {
// Tekken 6 hack: Let's avoid the unnecessary logspam. It does this on hardware too.
// This ID is always invalid.
if (id == 0x80020001 && timeoutPtr == 0) {
return hleNoLog(0);
}
if (timeoutPtr != 0) {
if (!Memory::IsValid4AlignedAddress(timeoutPtr)) {
Core_MemoryExceptionHLE(currentMIPS, timeoutPtr, 4, MemoryExceptionType::HLE_READ);
return hleNoLog(0);
}
}
u32 error;
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(id, error);
@@ -554,9 +563,14 @@ int sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr)
return hleLogDebug(Log::sceKernel, 0);
}
// int sceKernelLockMutexCB(SceUID id, int count, int *timeout)
int sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr)
{
int sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) {
if (timeoutPtr != 0) {
if (!Memory::IsValid4AlignedAddress(timeoutPtr)) {
Core_MemoryExceptionHLE(currentMIPS, timeoutPtr, 4, MemoryExceptionType::HLE_READ);
return hleNoLog(0);
}
}
u32 error;
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(id, error);
@@ -592,7 +606,6 @@ int sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr)
}
}
// int sceKernelTryLockMutex(SceUID id, int count)
int sceKernelTryLockMutex(SceUID id, int count) {
u32 error;
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(id, error);
@@ -605,9 +618,7 @@ int sceKernelTryLockMutex(SceUID id, int count) {
return hleLogDebug(Log::sceKernel, SCE_MUTEX_ERROR_TRYLOCK_FAILED);
}
// int sceKernelUnlockMutex(SceUID id, int count)
int sceKernelUnlockMutex(SceUID id, int count)
{
int sceKernelUnlockMutex(SceUID id, int count) {
// Tekken 6 hack: Let's avoid the unnecessary logspam. It does this on hardware too.
// This ID is always invalid.
if (id == 0x80020001) {
@@ -698,11 +709,14 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init
workarea->attr = attr;
workarea->uid = id;
if (optionsPtr != 0)
{
u32 size = Memory::Read_U32(optionsPtr);
if (size > 4)
WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateLwMutex(%s) unsupported options parameter, size = %d", name, size);
if (optionsPtr != 0) {
if (Memory::IsValid4AlignedAddress(optionsPtr)) {
u32 size = Memory::ReadUnchecked_U32(optionsPtr);
if (size > 4)
WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateLwMutex(%s) unsupported options parameter, size = %d", name, size);
} else {
Core_MemoryExceptionHLE(currentMIPS, optionsPtr, 4, MemoryExceptionType::HLE_READ);
}
}
if ((attr & ~PSP_MUTEX_ATTR_KNOWN) != 0)
WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateLwMutex(%s) unsupported attr parameter: %08x", name, attr);
@@ -711,8 +725,7 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init
}
template <typename T>
bool __KernelUnlockLwMutexForThread(LwMutex *mutex, T workarea, SceUID threadID, u32 &error, int result)
{
bool __KernelUnlockLwMutexForThread(LwMutex *mutex, T workarea, SceUID threadID, u32 &error, int result) {
if (!HLEKernel::VerifyWait(threadID, WAITTYPE_LWMUTEX, mutex->GetUID()))
return false;
@@ -723,9 +736,8 @@ bool __KernelUnlockLwMutexForThread(LwMutex *mutex, T workarea, SceUID threadID,
workarea->lockThread = threadID;
}
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
if (timeoutPtr != 0 && lwMutexWaitTimer != -1)
{
const u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
if (timeoutPtr != 0 && lwMutexWaitTimer != -1) {
// Remove any event for this thread.
s64 cyclesLeft = CoreTiming::UnscheduleEvent(lwMutexWaitTimer, threadID);
Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr);
@@ -763,8 +775,7 @@ int sceKernelDeleteLwMutex(u32 workareaPtr) {
}
}
static bool __KernelLockLwMutex(NativeLwMutexWorkarea *workarea, int count, u32 &error)
{
static bool __KernelLockLwMutex(NativeLwMutexWorkarea *workarea, int count, u32 &error) {
if (!error)
{
if (count <= 0)
@@ -815,11 +826,9 @@ static bool __KernelLockLwMutex(NativeLwMutexWorkarea *workarea, int count, u32
}
template <typename T>
bool __KernelUnlockLwMutex(T workarea, u32 &error)
{
bool __KernelUnlockLwMutex(T workarea, u32 &error) {
LwMutex *mutex = kernelObjects.Get<LwMutex>(workarea->uid, error);
if (error)
{
if (error) {
workarea->lockThread = 0;
return false;
}
@@ -849,12 +858,12 @@ void __KernelLwMutexTimeout(u64 userdata, int cyclesLate)
HLEKernel::WaitExecTimeout<LwMutex, WAITTYPE_LWMUTEX>(threadID);
}
static void __KernelWaitLwMutex(LwMutex *mutex, u32 timeoutPtr)
{
// timeoutPtr is assumed to be checked by the caller to either be 0 or valid.
static void __KernelWaitLwMutex(LwMutex *mutex, u32 timeoutPtr) {
if (timeoutPtr == 0 || lwMutexWaitTimer == -1)
return;
int micro = (int) Memory::Read_U32(timeoutPtr);
int micro = (int) Memory::ReadUnchecked_U32(timeoutPtr);
// This happens to be how the hardware seems to time things.
if (micro <= 3)
@@ -889,8 +898,7 @@ void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId)
DEBUG_LOG(Log::sceKernel, "sceKernelLockLwMutexCB: Resuming lock wait for callback");
}
int sceKernelTryLockLwMutex(u32 workareaPtr, int count)
{
int sceKernelTryLockLwMutex(u32 workareaPtr, int count) {
if (!Memory::IsValidAddress(workareaPtr)) {
return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex");
}
@@ -908,8 +916,7 @@ int sceKernelTryLockLwMutex(u32 workareaPtr, int count)
return hleLogDebug(Log::sceKernel, SCE_MUTEX_ERROR_TRYLOCK_FAILED);
}
int sceKernelTryLockLwMutex_600(u32 workareaPtr, int count)
{
int sceKernelTryLockLwMutex_600(u32 workareaPtr, int count) {
if (!Memory::IsValidAddress(workareaPtr)) {
return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex");
}
@@ -926,22 +933,27 @@ int sceKernelTryLockLwMutex_600(u32 workareaPtr, int count)
return hleLogDebug(Log::sceKernel, SCE_LWMUTEX_ERROR_TRYLOCK_FAILED);
}
int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr)
{
int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr) {
if (!Memory::IsValidAddress(workareaPtr)) {
return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex");
}
if (timeoutPtr) {
if (!Memory::IsValid4AlignedAddress(timeoutPtr)) {
Core_MemoryExceptionHLE(currentMIPS, timeoutPtr, 4, MemoryExceptionType::HLE_READ);
return hleNoLog(0);
}
}
auto workarea = PSPPointer<NativeLwMutexWorkarea>::Create(workareaPtr);
hleEatCycles(48);
u32 error = 0;
if (__KernelLockLwMutex(workarea, count, error))
if (__KernelLockLwMutex(workarea, count, error)) {
return hleLogVerbose(Log::sceKernel, 0);
else if (error)
} else if (error) {
return hleLogVerbose(Log::sceKernel, error);
else
{
} else {
LwMutex *mutex = kernelObjects.Get<LwMutex>(workarea->uid, error);
if (!mutex) {
return hleLogError(Log::sceKernel, error);
@@ -959,24 +971,27 @@ int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr)
}
}
int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr)
{
VERBOSE_LOG(Log::sceKernel, "sceKernelLockLwMutexCB(%08x, %i, %08x)", workareaPtr, count, timeoutPtr);
int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr) {
if (!Memory::IsValidAddress(workareaPtr)) {
return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex");
}
if (timeoutPtr) {
if (!Memory::IsValid4AlignedAddress(timeoutPtr)) {
Core_MemoryExceptionHLE(currentMIPS, timeoutPtr, 4, MemoryExceptionType::HLE_READ);
return hleNoLog(0);
}
}
auto workarea = PSPPointer<NativeLwMutexWorkarea>::Create(workareaPtr);
hleEatCycles(48);
u32 error = 0;
if (__KernelLockLwMutex(workarea, count, error))
if (__KernelLockLwMutex(workarea, count, error)) {
return hleLogVerbose(Log::sceKernel, 0);
else if (error)
} else if (error) {
return hleLogVerbose(Log::sceKernel, error);
else
{
} else {
LwMutex *mutex = kernelObjects.Get<LwMutex>(workarea->uid, error);
if (!mutex) {
return hleLogError(Log::sceKernel, error);
@@ -994,8 +1009,7 @@ int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr)
}
}
int sceKernelUnlockLwMutex(u32 workareaPtr, int count)
{
int sceKernelUnlockLwMutex(u32 workareaPtr, int count) {
if (!Memory::IsValidAddress(workareaPtr)) {
return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex");
}
@@ -1018,8 +1032,7 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count)
workarea->lockLevel -= count;
if (workarea->lockLevel == 0)
{
if (workarea->lockLevel == 0) {
u32 error;
if (__KernelUnlockLwMutex(workarea, error))
hleReSchedule("lwmutex unlocked");
@@ -1032,16 +1045,17 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count)
static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) {
u32 error;
LwMutex *m = kernelObjects.Get<LwMutex>(uid, error);
if (!m)
if (!m) {
return hleLogError(Log::sceKernel, error, "invalid id");
}
// Should we crash the thread somehow?
auto info = PSPPointer<NativeLwMutex>::Create(infoPtr);
if (!info.IsValid())
if (!info.IsValid()) {
return hleLogError(Log::sceKernel, -1, "invalid pointer");
}
if (info->size != 0)
{
if (info->size != 0) {
auto workarea = m->nm.workarea;
HLEKernel::CleanupWaitingThreads(WAITTYPE_LWMUTEX, uid, m->waitingThreads);
+95 -117
View File
@@ -28,6 +28,7 @@
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/Serialize/SerializeList.h"
#include "Common/Serialize/SerializeMap.h"
#include "Core/Core.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/ErrorCodes.h"
#include "Core/HLE/HLETables.h"
@@ -357,8 +358,7 @@ bool PSPThread::AllocateStack(u32 &stackSize) {
bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0;
currentStack.start = StackAllocator().Alloc(stackSize, fromTop, StringFromFormat("stack/%s", nt.name).c_str());
if (currentStack.start == (u32)-1)
{
if (currentStack.start == (u32)-1) {
currentStack.start = 0;
nt.initialStack = 0;
ERROR_LOG(Log::sceKernel, "Failed to allocate stack for thread");
@@ -561,19 +561,16 @@ static u64 lastSwitchCycles = 0;
//STATE END
//////////////////////////////////////////////////////////////////////////
int __KernelRegisterActionType(ActionCreator creator)
{
int __KernelRegisterActionType(ActionCreator creator) {
return mipsCalls.registerActionType(creator);
}
void __KernelRestoreActionType(int actionType, ActionCreator creator)
{
void __KernelRestoreActionType(int actionType, ActionCreator creator) {
_assert_(actionType >= 0);
mipsCalls.restoreActionType(actionType, creator);
}
PSPAction *__KernelCreateAction(int actionType)
{
PSPAction *__KernelCreateAction(int actionType) {
return mipsCalls.createActionByType(actionType);
}
@@ -611,13 +608,11 @@ void MipsCall::DoState(PointerWrap &p)
}
}
void MipsCall::setReturnValue(u32 value)
{
void MipsCall::setReturnValue(u32 value) {
savedV0 = value;
}
void MipsCall::setReturnValue(u64 value)
{
void MipsCall::setReturnValue(u64 value) {
savedV0 = value & 0xFFFFFFFF;
savedV1 = (value >> 32) & 0xFFFFFFFF;
}
@@ -670,10 +665,9 @@ static void __KernelDelayEndCallback(SceUID threadID, SceUID prevCallbackId) {
// TODO: Don't wake up if __KernelCurHasReadyCallbacks()?
s64 cyclesLeft = delayDeadline - CoreTiming::GetTicks();
if (cyclesLeft < 0)
if (cyclesLeft < 0) {
__KernelResumeThreadFromWait(threadID, 0);
else
{
} else {
CoreTiming::ScheduleEvent(cyclesLeft, eventScheduledWakeup, __KernelGetCurThread());
DEBUG_LOG(Log::sceKernel, "sceKernelDelayThreadCB: Resuming delay after callback");
}
@@ -703,8 +697,7 @@ static void __KernelSleepEndCallback(SceUID threadID, SceUID prevCallbackId) {
}
}
static void __KernelThreadEndBeginCallback(SceUID threadID, SceUID prevCallbackId)
{
static void __KernelThreadEndBeginCallback(SceUID threadID, SceUID prevCallbackId) {
auto result = HLEKernel::WaitBeginCallback<PSPThread, WAITTYPE_THREADEND, SceUID>(threadID, prevCallbackId, eventThreadEndTimeout);
if (result == HLEKernel::WAIT_CB_SUCCESS)
DEBUG_LOG(Log::sceKernel, "sceKernelWaitThreadEndCB: Suspending wait for callback");
@@ -738,11 +731,9 @@ static void __KernelThreadEndEndCallback(SceUID threadID, SceUID prevCallbackId)
DEBUG_LOG(Log::sceKernel, "sceKernelWaitThreadEndCB: Resuming wait from callback");
}
u32 __KernelSetThreadRA(SceUID threadID, u32 nid)
{
u32 __KernelSetThreadRA(SceUID threadID, u32 nid) {
u32 newRA;
switch (nid)
{
switch (nid) {
case NID_MODULERETURN:
newRA = moduleReturnHackAddr;
break;
@@ -751,10 +742,9 @@ u32 __KernelSetThreadRA(SceUID threadID, u32 nid)
return -1;
}
if (threadID == currentThread)
if (threadID == currentThread) {
currentMIPS->r[MIPS_REG_RA] = newRA;
else
{
} else {
u32 error;
PSPThread *thread = kernelObjects.Get<PSPThread>(threadID, error);
if (!thread)
@@ -769,8 +759,7 @@ u32 __KernelSetThreadRA(SceUID threadID, u32 nid)
void hleScheduledWakeup(u64 userdata, int cyclesLate);
void hleThreadEndTimeout(u64 userdata, int cyclesLate);
static void __KernelWriteFakeSysCall(u32 nid, u32 *ptr, u32 &pos)
{
static void __KernelWriteFakeSysCall(u32 nid, u32 *ptr, u32 &pos) {
*ptr = pos;
pos += 8;
WriteHLESyscall("FakeSysCalls", nid, *ptr);
@@ -786,16 +775,14 @@ u32 HLEMipsCallReturnAddress() {
return hleReturnHackAddr;
}
void __KernelThreadingInit()
{
struct ThreadHack
{
void __KernelThreadingInit() {
struct ThreadHack {
u32 nid;
u32 *addr;
};
// Yeah, this is straight out of JPCSP, I should be ashamed.
const static u32_le idleThreadCode[] = {
static const u32_le idleThreadCode[] = {
MIPS_MAKE_LUI(MIPS_REG_RA, 0x0800),
MIPS_MAKE_JR_RA(),
MIPS_MAKE_SYSCALL("FakeSysCalls", "_sceKernelIdle"),
@@ -803,7 +790,7 @@ void __KernelThreadingInit()
};
// If you add another func here, don't forget __KernelThreadingDoState() below.
static ThreadHack threadHacks[] = {
static const ThreadHack threadHacks[] = {
{NID_THREADRETURN, &threadReturnHackAddr},
{NID_CALLBACKRETURN, &cbReturnHackAddr},
{NID_INTERRUPTRETURN, &intReturnHackAddr},
@@ -905,32 +892,26 @@ void __KernelThreadingDoState(PointerWrap &p)
Do(p, pendingDeleteThreads);
}
void __KernelThreadingDoStateLate(PointerWrap &p)
{
void __KernelThreadingDoStateLate(PointerWrap &p) {
// We do this late to give modules time to register actions.
mipsCalls.DoState(p);
p.DoMarker("sceKernelThread Late");
}
KernelObject *__KernelThreadObject()
{
return new PSPThread;
KernelObject *__KernelThreadObject() {
return new PSPThread();
}
KernelObject *__KernelCallbackObject()
{
return new PSPCallback;
KernelObject *__KernelCallbackObject() {
return new PSPCallback();
}
void __KernelListenThreadEnd(ThreadCallback callback)
{
void __KernelListenThreadEnd(ThreadCallback callback) {
threadEndListeners.push_back(callback);
}
static void __KernelFireThreadEnd(SceUID threadID)
{
for (auto iter = threadEndListeners.begin(), end = threadEndListeners.end(); iter != end; ++iter)
{
static void __KernelFireThreadEnd(SceUID threadID) {
for (auto iter = threadEndListeners.begin(), end = threadEndListeners.end(); iter != end; ++iter) {
ThreadCallback cb = *iter;
cb(threadID);
}
@@ -957,8 +938,7 @@ static void __KernelChangeReadyState(PSPThread *thread, SceUID threadID, bool re
}
}
static void __KernelChangeReadyState(SceUID threadID, bool ready)
{
static void __KernelChangeReadyState(SceUID threadID, bool ready) {
u32 error;
PSPThread *thread = kernelObjects.Get<PSPThread>(threadID, error);
if (thread)
@@ -967,10 +947,8 @@ static void __KernelChangeReadyState(SceUID threadID, bool ready)
WARN_LOG(Log::sceKernel, "Trying to change the ready state of an unknown thread?");
}
void __KernelStartIdleThreads(SceUID moduleId)
{
for (int i = 0; i < 2; i++)
{
void __KernelStartIdleThreads(SceUID moduleId) {
for (int i = 0; i < 2; i++) {
u32 error;
PSPThread *t = kernelObjects.Get<PSPThread>(threadIdleID[i], error);
t->nt.gpreg = __KernelGetModuleGP(moduleId);
@@ -987,8 +965,7 @@ void KernelValidateThreadTarget(uint32_t pc) {
}
}
bool __KernelSwitchOffThread(const char *reason)
{
bool __KernelSwitchOffThread(const char *reason) {
if (!reason)
reason = "switch off thread";
@@ -1155,8 +1132,7 @@ SceUID __KernelGetCurrentCallbackID(SceUID threadID, u32 &error) {
}
}
u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr)
{
u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr) {
static const u32 THREADINFO_SIZE = 104;
static const u32 THREADINFO_SIZE_AFTER_260 = 108;
@@ -1171,7 +1147,12 @@ u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr)
return hleLogError(Log::sceKernel, error, "bad thread");
}
u32 wantedSize = Memory::Read_U32(statusPtr);
if (!Memory::IsValid4AlignedAddress(statusPtr)) {
Core_MemoryExceptionHLE(currentMIPS, statusPtr, 0, MemoryExceptionType::HLE_READ);
return hleNoLog(0);
}
u32 wantedSize = Memory::ReadUnchecked_U32(statusPtr);
if (sceKernelGetCompiledSdkVersion() > 0x02060010) {
if (wantedSize > THREADINFO_SIZE_AFTER_260) {
@@ -1199,8 +1180,7 @@ u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr)
}
// Thanks JPCSP
u32 sceKernelReferThreadRunStatus(u32 threadID, u32 statusPtr)
{
u32 sceKernelReferThreadRunStatus(u32 threadID, u32 statusPtr) {
if (threadID == 0)
threadID = __KernelGetCurThread();
@@ -1210,8 +1190,10 @@ u32 sceKernelReferThreadRunStatus(u32 threadID, u32 statusPtr)
return hleLogError(Log::sceKernel, error, "bad thread");
}
if (!Memory::IsValidAddress(statusPtr))
if (!Memory::IsValidRange(statusPtr, sizeof(SceKernelThreadRunStatus))) {
// Raise exception?
return hleLogError(Log::sceKernel, -1);
}
auto runStatus = PSPPointer<SceKernelThreadRunStatus>::Create(statusPtr);
@@ -2574,7 +2556,7 @@ int sceKernelWaitThreadEnd(SceUID threadID, u32 timeoutPtr) {
if (t->nt.status != THREADSTATUS_DORMANT)
{
if (Memory::IsValidAddress(timeoutPtr))
__KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::Read_U32(timeoutPtr));
__KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::ReadUnchecked_U32(timeoutPtr));
if (std::find(t->waitingThreads.begin(), t->waitingThreads.end(), currentThread) == t->waitingThreads.end())
t->waitingThreads.push_back(currentThread);
__KernelWaitCurThread(WAITTYPE_THREADEND, threadID, 0, timeoutPtr, false, "thread wait end");
@@ -2601,7 +2583,7 @@ int sceKernelWaitThreadEndCB(SceUID threadID, u32 timeoutPtr) {
if (t->nt.status != THREADSTATUS_DORMANT)
{
if (Memory::IsValidAddress(timeoutPtr))
__KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::Read_U32(timeoutPtr));
__KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::ReadUnchecked_U32(timeoutPtr));
if (std::find(t->waitingThreads.begin(), t->waitingThreads.end(), currentThread) == t->waitingThreads.end())
t->waitingThreads.push_back(currentThread);
__KernelWaitCurThread(WAITTYPE_THREADEND, threadID, 0, timeoutPtr, true, "thread wait end");
@@ -2724,8 +2706,7 @@ SceUID sceKernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg)
return hleLogInfo(Log::sceKernel, id);
}
int sceKernelDeleteCallback(SceUID cbId)
{
int sceKernelDeleteCallback(SceUID cbId) {
u32 error;
PSPCallback *cb = kernelObjects.Get<PSPCallback>(cbId, error);
if (cb)
@@ -2743,8 +2724,7 @@ int sceKernelDeleteCallback(SceUID cbId)
}
// Generally very rarely used, but Numblast uses it like candy.
int sceKernelNotifyCallback(SceUID cbId, int notifyArg)
{
int sceKernelNotifyCallback(SceUID cbId, int notifyArg) {
u32 error;
PSPCallback *cb = kernelObjects.Get<PSPCallback>(cbId, error);
if (cb) {
@@ -2755,8 +2735,7 @@ int sceKernelNotifyCallback(SceUID cbId, int notifyArg)
}
}
int sceKernelCancelCallback(SceUID cbId)
{
int sceKernelCancelCallback(SceUID cbId) {
u32 error;
PSPCallback *cb = kernelObjects.Get<PSPCallback>(cbId, error);
if (cb) {
@@ -2768,8 +2747,7 @@ int sceKernelCancelCallback(SceUID cbId)
}
}
int sceKernelGetCallbackCount(SceUID cbId)
{
int sceKernelGetCallbackCount(SceUID cbId) {
u32 error;
PSPCallback *cb = kernelObjects.Get<PSPCallback>(cbId, error);
if (cb) {
@@ -2796,8 +2774,7 @@ int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr) {
}
}
u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter)
{
u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter) {
if (size < 512) {
return hleReportError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_STACK_SIZE, "stack size too small");
}
@@ -2828,25 +2805,28 @@ u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter)
return hleLogDebug(Log::sceKernel, 0);
}
void __KernelReturnFromExtendStack()
{
void __KernelReturnFromExtendStack() {
hleSkipDeadbeef();
PSPThread *thread = __GetCurrentThread();
if (!thread)
{
if (!thread) {
ERROR_LOG_REPORT(Log::sceKernel, "__KernelReturnFromExtendStack() - not on a thread?");
hleNoLogVoid();
return;
}
// Grab the saved regs at the top of the stack.
u32 restoreRA = Memory::Read_U32(thread->currentStack.end - 4);
u32 restoreSP = Memory::Read_U32(thread->currentStack.end - 8);
u32 restorePC = Memory::Read_U32(thread->currentStack.end - 12);
if (!Memory::IsValid4AlignedRange(thread->currentStack.end - 12, 12)) {
Core_MemoryExceptionHLE(currentMIPS, thread->currentStack.end, 12, MemoryExceptionType::HLE_READ);
hleNoLogVoid();
return;
}
if (!thread->PopExtendedStack())
{
// Grab the saved regs at the top of the stack.
u32 restoreRA = Memory::ReadUnchecked_U32(thread->currentStack.end - 4);
u32 restoreSP = Memory::ReadUnchecked_U32(thread->currentStack.end - 8);
u32 restorePC = Memory::ReadUnchecked_U32(thread->currentStack.end - 12);
if (!thread->PopExtendedStack()) {
ERROR_LOG_REPORT(Log::sceKernel, "__KernelReturnFromExtendStack() - no stack to restore?");
return;
}
@@ -2944,16 +2924,15 @@ void __KernelSwitchContext(PSPThread *target, const char *reason) {
__KernelChangeReadyState(cur, oldUID, true);
}
if (target)
{
if (target) {
__SetCurrentThread(target, target->GetUID(), target->nt.name);
__KernelChangeReadyState(target, currentThread, false);
target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY;
__KernelLoadContext(&target->context, (target->nt.attr & PSP_THREAD_ATTR_VFPU) != 0);
}
else
} else {
__SetCurrentThread(NULL, 0, NULL);
}
const bool fromIdle = oldUID == threadIdleID[0] || oldUID == threadIdleID[1];
const bool toIdle = currentThread == threadIdleID[0] || currentThread == threadIdleID[1];
@@ -3187,14 +3166,21 @@ void __KernelReturnFromMipsCall() {
call->doAfter = nullptr;
}
u32 &sp = currentMIPS->r[MIPS_REG_SP];
for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; ++i) {
currentMIPS->r[i] = Memory::Read_U32(sp + i * 4);
u32 sp = currentMIPS->r[MIPS_REG_SP];
if (!Memory::IsValid4AlignedRange(sp, 32 * 4)) {
// We're really screwed.
Core_MemoryExceptionHLE(currentMIPS, sp, 4, MemoryExceptionType::HLE_READ);
return hleNoLogVoid();
}
currentMIPS->r[MIPS_REG_T8] = Memory::Read_U32(sp + MIPS_REG_T8 * 4);
currentMIPS->r[MIPS_REG_T9] = Memory::Read_U32(sp + MIPS_REG_T9 * 4);
currentMIPS->r[MIPS_REG_RA] = Memory::Read_U32(sp + MIPS_REG_RA * 4);
sp += 32 * 4;
for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; ++i) {
currentMIPS->r[i] = Memory::ReadUnchecked_U32(sp + i * 4);
}
currentMIPS->r[MIPS_REG_T8] = Memory::ReadUnchecked_U32(sp + MIPS_REG_T8 * 4);
currentMIPS->r[MIPS_REG_T9] = Memory::ReadUnchecked_U32(sp + MIPS_REG_T9 * 4);
currentMIPS->r[MIPS_REG_RA] = Memory::ReadUnchecked_U32(sp + MIPS_REG_RA * 4);
// Increment SP.
currentMIPS->r[MIPS_REG_SP] += 32 * 4;
KernelValidateThreadTarget(call->savedPc);
@@ -3210,15 +3196,11 @@ void __KernelReturnFromMipsCall() {
}
currentCallbackThreadID = 0;
if (cur->nt.waitType != WAITTYPE_NONE)
{
if (call->cbId > 0)
{
if (waitTypeFuncs[cur->nt.waitType].endFunc != NULL)
waitTypeFuncs[cur->nt.waitType].endFunc(cur->GetUID(), cur->currentCallbackId);
else
ERROR_LOG_REPORT(Log::HLE, "Missing begin/restore funcs for wait type %d", cur->nt.waitType);
}
if (cur->nt.waitType != WAITTYPE_NONE && call->cbId > 0) {
if (waitTypeFuncs[cur->nt.waitType].endFunc != NULL)
waitTypeFuncs[cur->nt.waitType].endFunc(cur->GetUID(), cur->currentCallbackId);
else
ERROR_LOG_REPORT(Log::HLE, "Missing begin/restore funcs for wait type %d", cur->nt.waitType);
}
// yeah! back in the real world, let's keep going. Should we process more callbacks?
@@ -3506,12 +3488,10 @@ void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus) {
__KernelChangeThreadState(t, newStatus);
}
int sceKernelRegisterExitCallback(SceUID cbId)
{
int sceKernelRegisterExitCallback(SceUID cbId) {
u32 error;
PSPCallback *cb = kernelObjects.Get<PSPCallback>(cbId, error);
if (!cb)
{
if (!cb) {
WARN_LOG(Log::sceKernel, "sceKernelRegisterExitCallback(%i): invalid callback id", cbId);
if (sceKernelGetCompiledSdkVersion() >= 0x3090500)
return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT);
@@ -3568,8 +3548,7 @@ bool __KernelIsExitCallbackPending() {
}
// Update the exit callback status?
int LoadExecForUser_362A956B()
{
int LoadExecForUser_362A956B() {
WARN_LOG_REPORT(Log::sceKernel, "LoadExecForUser_362A956B()");
u32 error;
PSPCallback *cb = kernelObjects.Get<PSPCallback>(registeredExitCbId, error);
@@ -3577,24 +3556,23 @@ int LoadExecForUser_362A956B()
return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_UNKNOWN_CBID, "registeredExitCbId not found 0x%x", registeredExitCbId);
}
int cbArg = cb->nc.commonArgument;
if (!Memory::IsValidAddress(cbArg)) {
if (!Memory::IsValidRange(cbArg - 8, 8)) {
return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "invalid address for cbArg (0x%08X)", cbArg);
}
u32 unknown1 = Memory::Read_U32(cbArg - 8);
const u32 unknown1 = Memory::ReadUnchecked_U32(cbArg - 8);
if (unknown1 >= 4) {
return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid value unknown1 (0x%08X)", unknown1);
}
u32 parameterArea = Memory::Read_U32(cbArg - 4);
if (!Memory::IsValidAddress(parameterArea)) {
const u32 parameterArea = Memory::ReadUnchecked_U32(cbArg - 4);
if (!Memory::IsValid4AlignedRange(parameterArea, 12)) {
return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "invalid address for parameterArea on userMemory (0x%08X)", parameterArea);
}
u32 size = Memory::Read_U32(parameterArea);
const u32 size = Memory::ReadUnchecked_U32(parameterArea);
if (size < 12) {
return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_SIZE, "invalid parameterArea size %d", size);
}
Memory::Write_U32(0, parameterArea + 4);
Memory::Write_U32(-1, parameterArea + 8);
Memory::WriteUnchecked_U32(0, parameterArea + 4);
Memory::WriteUnchecked_U32(-1, parameterArea + 8);
return hleLogDebug(Log::sceKernel, 0);
}
@@ -3630,7 +3608,7 @@ struct ThreadEventHandler : public KernelObject {
KernelObject *__KernelThreadEventHandlerObject() {
// Default object to load from state.
return new ThreadEventHandler;
return new ThreadEventHandler();
}
bool __KernelThreadTriggerEvent(const ThreadEventHandlerList &handlers, SceUID threadID, ThreadEventType type) {
+2 -2
View File
@@ -1642,7 +1642,7 @@ static int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
avcAu.write(auAddr);
if (result == 0) {
// Jeanne d'Arc return 00000000 as attrAddr here and cause WriteToHardware error
// Jeanne d'Arc return 00000000 as attrAddr here and cause WriteMemoryOrRaiseException error
if (Memory::IsValidAddress(attrAddr)) {
Memory::Write_U32(1, attrAddr);
}
@@ -1742,7 +1742,7 @@ static int sceMpegGetAtracAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
atracAu.write(auAddr);
if (result == 0) {
// 3rd birthday return 00000000 as attrAddr here and cause WriteToHardware error
// 3rd birthday return 00000000 as attrAddr here and cause WriteMemoryOrRaiseException error
if (Memory::IsValidAddress(attrAddr)) {
Memory::Write_U32(0, attrAddr);
}
+2 -3
View File
@@ -524,7 +524,7 @@ void Jit::Comp_Jump(MIPSOpcode op) {
// Might be a stubbed address or something?
if (!Memory::IsValidAddress(targetAddr) || (targetAddr & 3) != 0) {
if (js.nextExit == 0) {
ERROR_LOG(Log::JIT, "Jump to invalid address: %08x PC %08x LR %08x", targetAddr, GetCompilerPC(), currentMIPS->r[MIPS_REG_RA]);
ERROR_LOG(Log::JIT, "Jump to invalid address: %08x PC %08x RA %08x", targetAddr, GetCompilerPC(), currentMIPS->r[MIPS_REG_RA]);
} else {
js.compiling = false;
}
@@ -562,8 +562,7 @@ void Jit::Comp_Jump(MIPSOpcode op) {
js.compiling = false;
}
void Jit::Comp_JumpReg(MIPSOpcode op)
{
void Jit::Comp_JumpReg(MIPSOpcode op) {
CONDITIONAL_LOG;
if (js.inDelaySlot) {
ERROR_LOG_REPORT(Log::JIT, "Branch in JumpReg delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
+5 -1
View File
@@ -320,7 +320,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
uint32_t approximatePC = currentMIPS->pc;
// TODO: Determine access size from the disassembled native instruction. We have some partial info already,
// just need to clean it up.
Core_MemoryException(guestAddress, 0, approximatePC, type, infoString, true);
Core_MemoryException(guestAddress, 0, approximatePC, type, infoString);
// There's a small chance we can resume from this type of crash.
g_lastCrashAddress = codePtr;
@@ -373,6 +373,10 @@ std::vector<MIPSStackWalk::StackFrame> WalkCurrentStack(int threadID) {
std::string FormatStackTrace(const std::vector<MIPSStackWalk::StackFrame> &frames) {
std::stringstream str;
for (const auto &frame : frames) {
if (frame.pc == 0xFFFFFFFF) {
// Bottom of stack, probably.
continue;
}
std::string desc = g_symbolMap->GetDescription(frame.entry);
char moduleDesc[96];
if (DescribeKernelModuleAddress(frame.entry, moduleDesc, sizeof(moduleDesc))) {
+10 -10
View File
@@ -86,7 +86,7 @@ const u8 *GetPointerRange(const u32 address, const u32 size) {
}
template <typename T>
inline void ReadFromHardware(T &var, const u32 address) {
inline void ReadMemoryOrRaiseException(T &var, const u32 address) {
if ((address & 0x3E000000) == 0x08000000 || // RAM
(address & 0xBF800000) == 0x04000000 || // VRAM
(address & 0xBFFFC000) == 0x00010000 || // Scratchpad
@@ -99,7 +99,7 @@ inline void ReadFromHardware(T &var, const u32 address) {
}
template <typename T>
inline void WriteToHardware(u32 address, const T data) {
inline void WriteMemoryOrRaiseException(u32 address, const T data) {
if ((address & 0x3E000000) == 0x08000000 || // RAM
(address & 0xBF800000) == 0x04000000 || // VRAM
(address & 0xBFFFC000) == 0x00010000 || // Scratchpad
@@ -126,25 +126,25 @@ bool IsScratchpadAddress(const u32 address) {
u8 Read_U8(const u32 address) {
u8 value = 0;
ReadFromHardware<u8>(value, address);
ReadMemoryOrRaiseException<u8>(value, address);
return (u8)value;
}
u16 Read_U16(const u32 address) {
u16_le value = 0;
ReadFromHardware<u16_le>(value, address);
ReadMemoryOrRaiseException<u16_le>(value, address);
return (u16)value;
}
u32 Read_U32(const u32 address) {
u32_le value = 0;
ReadFromHardware<u32_le>(value, address);
ReadMemoryOrRaiseException<u32_le>(value, address);
return value;
}
u64 Read_U64(const u32 address) {
u64_le value = 0;
ReadFromHardware<u64_le>(value, address);
ReadMemoryOrRaiseException<u64_le>(value, address);
return value;
}
@@ -157,19 +157,19 @@ u32 Read_U16_ZX(const u32 address) {
}
void Write_U8(const u8 _Data, const u32 address) {
WriteToHardware<u8>(address, _Data);
WriteMemoryOrRaiseException<u8>(address, _Data);
}
void Write_U16(const u16 _Data, const u32 address) {
WriteToHardware<u16_le>(address, _Data);
WriteMemoryOrRaiseException<u16_le>(address, _Data);
}
void Write_U32(const u32 _Data, const u32 address) {
WriteToHardware<u32_le>(address, _Data);
WriteMemoryOrRaiseException<u32_le>(address, _Data);
}
void Write_U64(const u64 _Data, const u32 address) {
WriteToHardware<u64_le>(address, _Data);
WriteMemoryOrRaiseException<u64_le>(address, _Data);
}
} // namespace Memory
+6 -1
View File
@@ -628,8 +628,13 @@ void Recorder::NotifyCommand(u32 pc) {
return;
}
if (!Memory::IsValid4AlignedAddress(pc)) {
ERROR_LOG(Log::G3D, "Bad pc in Recorder: %08x", pc);
return;
}
CheckEdramTrans();
const u32 op = Memory::Read_U32(pc);
const u32 op = Memory::ReadUnchecked_U32(pc);
const GECommand cmd = GECommand(op >> 24);
switch (cmd) {
+6 -1
View File
@@ -1576,7 +1576,12 @@ int GPUCommon::GetCurrentPrim(GEPrimitiveType *prim, GECommand *outCmd) const {
DisplayList list;
u32 cmdWord;
if (GetCurrentDisplayList(list)) {
cmdWord = Memory::Read_U32(list.pc);
if (Memory::IsValid4AlignedAddress(list.pc)) {
cmdWord = Memory::ReadUnchecked_U32(list.pc);
} else {
// We are screwed.
return 0;
}
} else {
// Current prim value.
cmdWord = gstate.cmdmem[GE_CMD_PRIM];
+8 -8
View File
@@ -896,21 +896,21 @@ void ImDisasmView::updateStatusBarText() {
}
if (line.info.isDataAccess) {
if (!Memory::IsValidAddress(line.info.dataAddress)) {
snprintf(text, sizeof(text), "Invalid address %08X", line.info.dataAddress);
if (!Memory::IsValidRange(line.info.dataAddress, line.info.dataSize)) {
snprintf(text, sizeof(text), "Invalid address range %08X (size %d)", line.info.dataAddress, line.info.dataSize);
} else {
bool isFloat = MIPSGetInfo(line.info.encodedOpcode) & (IS_FPU | IS_VFPU);
switch (line.info.dataSize) {
case 1:
snprintf(text, sizeof(text), "[%08X] = %02X", line.info.dataAddress, Memory::Read_U8(line.info.dataAddress));
snprintf(text, sizeof(text), "[%08X] = %02X", line.info.dataAddress, Memory::ReadUnchecked_U8(line.info.dataAddress));
break;
case 2:
snprintf(text, sizeof(text), "[%08X] = %04X", line.info.dataAddress, Memory::Read_U16(line.info.dataAddress));
snprintf(text, sizeof(text), "[%08X] = %04X", line.info.dataAddress, Memory::ReadUnchecked_U16(line.info.dataAddress));
break;
case 4:
{
u32 dataInt = Memory::Read_U32(line.info.dataAddress);
u32 dataFloat = Memory::Read_Float(line.info.dataAddress);
u32 dataInt = Memory::ReadUnchecked_U32(line.info.dataAddress);
u32 dataFloat = Memory::ReadUnchecked_Float(line.info.dataAddress);
std::string dataString;
if (isFloat)
dataString = StringFromFormat("%08X / %f", dataInt, dataFloat);
@@ -930,8 +930,8 @@ void ImDisasmView::updateStatusBarText() {
uint32_t dataInt[4];
float dataFloat[4];
for (int i = 0; i < 4; ++i) {
dataInt[i] = Memory::Read_U32(line.info.dataAddress + i * 4);
dataFloat[i] = Memory::Read_Float(line.info.dataAddress + i * 4);
dataInt[i] = Memory::ReadUnchecked_U32(line.info.dataAddress + i * 4);
dataFloat[i] = Memory::ReadUnchecked_Float(line.info.dataAddress + i * 4);
}
std::string dataIntString = StringFromFormat("%08X,%08X,%08X,%08X", dataInt[0], dataInt[1], dataInt[2], dataInt[3]);
std::string dataFloatString = StringFromFormat("%f,%f,%f,%f", dataFloat[0], dataFloat[1], dataFloat[2], dataFloat[3]);
+4
View File
@@ -635,6 +635,10 @@ void ImStructViewer::DrawType(
}
const u32 address = base + offset;
if (!Memory::IsValidAddress(address)) {
// Bad!
return;
}
ImGui::PushID(static_cast<int>(address));
ImGui::PushID(watchId); // We push watch id too as it's possible to have multiple watches on the same address