1
0
Fork 0
mirror of https://github.com/hrydgard/ppsspp.git synced 2025-04-02 11:01:50 -04:00
ppsspp/Core/HLE/sceKernelModule.cpp
Unknown W. Brackets acac847af2 Cleanup sceKernelGetModuleIdByAddress().
Actual firmware seems to accept any address in the range, and also
correct the error result.

Now people won't think this is broken anymore.
2013-04-10 21:03:43 -07:00

1040 lines
30 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <fstream>
#include <algorithm>
#include "HLE.h"
#include "Core/Reporting.h"
#include "Common/FileUtil.h"
#include "../Host.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/ELF/ElfReader.h"
#include "Core/ELF/PBPReader.h"
#include "Core/ELF/PrxDecrypter.h"
#include "../Debugger/SymbolMap.h"
#include "../FileSystems/FileSystem.h"
#include "../FileSystems/MetaFileSystem.h"
#include "../Util/BlockAllocator.h"
#include "Core/CoreTiming.h"
#include "Core/PSPLoaders.h"
#include "Core/System.h"
#include "Core/MemMap.h"
#include "Core/Debugger/SymbolMap.h"
#include "sceKernel.h"
#include "sceKernelModule.h"
#include "sceKernelThread.h"
#include "sceKernelMemory.h"
#include "sceIo.h"
enum {
PSP_THREAD_ATTR_USER = 0x80000000
};
enum {
// Function exports.
NID_MODULE_START = 0xD632ACDB,
NID_MODULE_STOP = 0xCEE8593C,
NID_MODULE_REBOOT_BEFORE = 0x2F064FA6,
NID_MODULE_REBOOT_PHASE = 0xADF12745,
NID_MODULE_BOOTSTART = 0xD3744BE0,
// Variable exports.
NID_MODULE_INFO = 0xF01D73A7,
NID_MODULE_START_THREAD_PARAMETER = 0x0F7C276C,
NID_MODULE_STOP_THREAD_PARAMETER = 0xCF0CC697,
NID_MODULE_REBOOT_BEFORE_THREAD_PARAMETER = 0xF4F4299D,
NID_MODULE_SDK_VERSION = 0x11B97506,
};
static const char *blacklistedModules[] = {
"sceATRAC3plus_Library",
"sceFont_Library",
"SceFont_Library",
"SceHttp_Library",
"sceMpeg_library",
"sceNetAdhocctl_Library",
"sceNetAdhocDownload_Library",
"sceNetAdhocMatching_Library",
"sceNetApDialogDummy_Library",
"sceNetAdhoc_Library",
"sceNetApctl_Library",
"sceNetInet_Library",
"sceNetResolver_Library",
"sceNet_Library",
"sceSsl_Module",
};
struct NativeModule {
u32 next;
u16 attribute;
u8 version[2];
char name[28];
u32 status;
u32 unk1;
u32 usermod_thid;
u32 memid;
u32 mpidtext;
u32 mpiddata;
u32 ent_top;
u32 ent_size;
u32 stub_top;
u32 stub_size;
u32 module_start_func;
u32 module_stop_func;
u32 module_bootstart_func;
u32 module_reboot_before_func;
u32 module_reboot_phase_func;
u32 entry_addr;
u32 gp_value;
u32 text_addr;
u32 text_size;
u32 data_size;
u32 bss_size;
u32 nsegment;
u32 segmentaddr[4];
u32 segmentsize[4];
u32 module_start_thread_priority;
u32 module_start_thread_stacksize;
u32 module_start_thread_attr;
u32 module_stop_thread_priority;
u32 module_stop_thread_stacksize;
u32 module_stop_thread_attr;
u32 module_reboot_before_thread_priority;
u32 module_reboot_before_thread_stacksize;
u32 module_reboot_before_thread_attr;
};
// by QueryModuleInfo
struct ModuleInfo {
u32 nsegment;
u32 segmentaddr[4];
u32 segmentsize[4];
u32 entry_addr;
u32 gp_value;
u32 text_addr;
u32 text_size;
u32 data_size;
u32 bss_size;
u16 attribute;
u8 version[2];
char name[28];
};
class Module : public KernelObject
{
public:
Module() : memoryBlockAddr(0), isFake(false) {}
~Module() {
if (memoryBlockAddr) {
userMemory.Free(memoryBlockAddr);
}
}
const char *GetName() {return nm.name;}
const char *GetTypeName() {return "Module";}
void GetQuickInfo(char *ptr, int size)
{
// ignore size
sprintf(ptr, "%sname=%s gp=%08x entry=%08x",
isFake ? "faked " : "",
nm.name,
nm.gp_value,
nm.entry_addr);
}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MODULE; }
int GetIDType() const { return PPSSPP_KERNEL_TMID_Module; }
virtual void DoState(PointerWrap &p)
{
p.Do(nm);
p.Do(memoryBlockAddr);
p.Do(memoryBlockSize);
p.DoMarker("Module");
}
NativeModule nm;
u32 memoryBlockAddr;
u32 memoryBlockSize;
bool isFake;
};
KernelObject *__KernelModuleObject()
{
return new Module;
}
class AfterModuleEntryCall : public Action {
public:
AfterModuleEntryCall() {}
SceUID moduleID_;
u32 retValAddr;
virtual void run(MipsCall &call);
virtual void DoState(PointerWrap &p) {
p.Do(moduleID_);
p.Do(retValAddr);
p.DoMarker("AfterModuleEntryCall");
}
static Action *Create() {
return new AfterModuleEntryCall;
}
};
void AfterModuleEntryCall::run(MipsCall &call) {
Memory::Write_U32(retValAddr, currentMIPS->r[2]);
}
//////////////////////////////////////////////////////////////////////////
// MODULES
//////////////////////////////////////////////////////////////////////////
struct StartModuleInfo
{
u32 size;
u32 mpidtext;
u32 mpiddata;
u32 threadpriority;
u32 threadattributes;
};
struct SceKernelLMOption {
SceSize size;
SceUID mpidtext;
SceUID mpiddata;
unsigned int flags;
char position;
char access;
char creserved[2];
};
struct SceKernelSMOption {
SceSize size;
SceUID mpidstack;
SceSize stacksize;
int priority;
unsigned int attribute;
};
//////////////////////////////////////////////////////////////////////////
// STATE BEGIN
static int actionAfterModule;
static SceUID mainModuleID; // hack
// STATE END
//////////////////////////////////////////////////////////////////////////
void __KernelModuleInit()
{
actionAfterModule = __KernelRegisterActionType(AfterModuleEntryCall::Create);
}
void __KernelModuleDoState(PointerWrap &p)
{
p.Do(mainModuleID);
p.Do(actionAfterModule);
__KernelRestoreActionType(actionAfterModule, AfterModuleEntryCall::Create);
p.DoMarker("sceKernelModule");
}
Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *error_string)
{
Module *module = new Module;
kernelObjects.Create(module);
memset(&module->nm, 0, sizeof(module->nm));
u8 *newptr = 0;
if (*(u32*)ptr == 0x4543537e) { // "~SCE"
INFO_LOG(HLE, "~SCE module, skipping header");
ptr += *(u32*)(ptr + 4);
}
if (*(u32*)ptr == 0x5053507e) { // "~PSP"
// Decrypt module! YAY!
INFO_LOG(HLE, "Decrypting ~PSP file");
PSP_Header *head = (PSP_Header*)ptr;
const u8 *in = ptr;
u32 size = head->elf_size;
if (head->psp_size > size)
{
size = head->psp_size;
}
newptr = new u8[head->elf_size + head->psp_size];
ptr = newptr;
int ret = pspDecryptPRX(in, (u8*)ptr, head->psp_size);
if (ret == MISSING_KEY) {
// This should happen for all "kernel" modules so disabling.
// Reporting::ReportMessage("Missing PRX decryption key!");
*error_string = "Missing key";
delete [] newptr;
module->isFake = true;
strncpy(module->nm.name, head->modname, 28);
module->nm.entry_addr = -1;
module->nm.gp_value = -1;
return module;
}
else if (ret <= 0)
{
ERROR_LOG(HLE, "Failed decrypting PRX! That's not normal! ret = %i\n", ret);
Reporting::ReportMessage("Failed decrypting the PRX (ret = %i, size = %i, psp_size = %i)!", ret, head->elf_size, head->psp_size);
}
}
if (*(u32*)ptr != 0x464c457f)
{
ERROR_LOG_REPORT(HLE, "Wrong magic number %08x", *(u32*)ptr);
*error_string = "File corrupt";
if (newptr) {
delete [] newptr;
}
kernelObjects.Destroy<Module>(module->GetUID());
return 0;
}
// Open ELF reader
ElfReader reader((void*)ptr);
if (!reader.LoadInto(loadAddress))
{
ERROR_LOG(HLE, "LoadInto failed");
if (newptr)
{
delete [] newptr;
}
kernelObjects.Destroy<Module>(module->GetUID());
return 0;
}
module->memoryBlockAddr = reader.GetVaddr();
module->memoryBlockSize = reader.GetTotalSize();
struct libent
{
u32 exportName; //default 0
u16 bcdVersion;
u16 moduleAttributes;
u8 exportEntrySize;
u8 numVariables;
u16 numFunctions;
u32 __entrytableAddr;
};
struct PspModuleInfo
{
// 0, 0, 1, 1 ?
u16 moduleAttrs; //0x0000 User Mode, 0x1000 Kernel Mode
u16 moduleVersion;
// 28 bytes of module name, packed with 0's.
char name[28];
u32 gp; // ptr to MIPS GOT data (global offset table)
u32 libent; // ptr to .lib.ent section
u32 libentend; // ptr to end of .lib.ent section
u32 libstub; // ptr to .lib.stub section
u32 libstubend; // ptr to end of .lib.stub section
};
SectionID sceModuleInfoSection = reader.GetSectionByName(".rodata.sceModuleInfo");
PspModuleInfo *modinfo;
if (sceModuleInfoSection != -1)
modinfo = (PspModuleInfo *)Memory::GetPointer(reader.GetSectionAddr(sceModuleInfoSection));
else
modinfo = (PspModuleInfo *)Memory::GetPointer(reader.GetSegmentVaddr(0) + (reader.GetSegmentPaddr(0) & 0x7FFFFFFF) - reader.GetSegmentOffset(0));
module->nm.gp_value = modinfo->gp;
strncpy(module->nm.name, modinfo->name, 28);
// Check for module blacklist - we don't allow games to load these modules from disc
// as we have HLE implementations and the originals won't run in the emu because they
// directly access hardware or for other reasons.
for (u32 i = 0; i < ARRAY_SIZE(blacklistedModules); i++) {
if (strcmp(modinfo->name, blacklistedModules[i]) == 0) {
*error_string = "Blacklisted";
if (newptr)
{
delete [] newptr;
}
module->isFake = true;
module->nm.entry_addr = -1;
return module;
}
}
bool hasSymbols = false;
bool dontadd = false;
SectionID textSection = reader.GetSectionByName(".text");
if (textSection != -1)
{
u32 textStart = reader.GetSectionAddr(textSection);
u32 textSize = reader.GetSectionSize(textSection);
if (!host->AttemptLoadSymbolMap())
{
hasSymbols = reader.LoadSymbols();
if (!hasSymbols)
{
symbolMap.ResetSymbolMap();
MIPSAnalyst::ScanForFunctions(textStart, textStart+textSize);
}
}
else
{
dontadd = true;
}
}
else if (host->AttemptLoadSymbolMap())
{
dontadd = true;
}
INFO_LOG(LOADER,"Module %s: %08x %08x %08x", modinfo->name, modinfo->gp, modinfo->libent,modinfo->libstub);
struct PspLibStubEntry
{
u32 name;
u16 version;
u16 flags;
u16 size;
u16 numFuncs;
// each symbol has an associated nid; nidData is a pointer
// (in .rodata.sceNid section) to an array of longs, one
// for each function, which identifies the function whose
// address is to be inserted.
//
// The hash is the first 4 bytes of a SHA-1 hash of the function
// name. (Represented as a little-endian long, so the order
// of the bytes is reversed.)
u32 nidData;
// the address of the function stubs where the function address jumps
// should be filled in
u32 firstSymAddr;
};
int numModules = (modinfo->libstubend - modinfo->libstub)/sizeof(PspLibStubEntry);
DEBUG_LOG(LOADER,"Num Modules: %i",numModules);
DEBUG_LOG(LOADER,"===================================================");
PspLibStubEntry *entry = (PspLibStubEntry *)Memory::GetPointer(modinfo->libstub);
int numSyms=0;
for (int m = 0; m < numModules; m++) {
const char *modulename;
if (Memory::IsValidAddress(entry[m].name))
modulename = (const char*)Memory::GetPointer(entry[m].name);
else
modulename = "(invalidname)";
if (!Memory::IsValidAddress(entry[m].nidData)) {
ERROR_LOG(LOADER, "Crazy niddata address %08x, skipping entire module", entry[m].nidData);
continue;
}
u32 *nidDataPtr = (u32*)Memory::GetPointer(entry[m].nidData);
// u32 *stubs = (u32*)Memory::GetPointer(entry[m].firstSymAddr);
DEBUG_LOG(LOADER,"Importing Module %s, stubs at %08x",modulename,entry[m].firstSymAddr);
for (int i=0; i<entry[m].numFuncs; i++)
{
u32 addrToWriteSyscall = entry[m].firstSymAddr+i*8;
DEBUG_LOG(LOADER,"%s : %08x",GetFuncName(modulename, nidDataPtr[i]), addrToWriteSyscall);
//write a syscall here
if (Memory::IsValidAddress(addrToWriteSyscall))
WriteSyscall(modulename, nidDataPtr[i], addrToWriteSyscall);
if (!dontadd)
{
char temp[256];
sprintf(temp,"zz_%s", GetFuncName(modulename, nidDataPtr[i]));
symbolMap.AddSymbol(temp, addrToWriteSyscall, 8, ST_FUNCTION);
}
numSyms++;
}
DEBUG_LOG(LOADER,"-------------------------------------------------------------");
}
// Look at the exports, too.
struct PspLibEntEntry
{
u32 name; /* ent's name (module name) address */
u16 version;
u16 flags;
u8 size;
u8 vcount;
u16 fcount;
u32 resident;
};
int numEnts = (modinfo->libentend - modinfo->libent)/sizeof(PspLibEntEntry);
PspLibEntEntry *ent = (PspLibEntEntry *)Memory::GetPointer(modinfo->libent);
for (int m=0; m<numEnts; m++)
{
const char *name;
if (ent->size == 0)
continue;
if (ent->name == 0) {
// ?
name = module->nm.name;
}
else if (Memory::IsValidAddress(ent->name)) {
name = (const char*)Memory::GetPointer(ent->name);
}
else {
name = "invalid?"; // God Eater Burst
}
INFO_LOG(HLE,"Exporting ent %d named %s, %d funcs, %d vars, resident %08x", m, name, ent->fcount, ent->vcount, ent->resident);
// Seen 0x00060005 in God Eater Burst
if (Memory::IsValidAddress(ent->resident))
{
u32 *residentPtr = (u32*)Memory::GetPointer(ent->resident);
for (u32 j = 0; j < ent->fcount; j++)
{
u32 nid = residentPtr[j];
u32 exportAddr = residentPtr[ent->fcount + ent->vcount + j];
switch (nid)
{
case NID_MODULE_START:
module->nm.module_start_func = exportAddr;
break;
case NID_MODULE_STOP:
module->nm.module_stop_func = exportAddr;
break;
case NID_MODULE_REBOOT_BEFORE:
module->nm.module_reboot_before_func = exportAddr;
break;
case NID_MODULE_REBOOT_PHASE:
module->nm.module_reboot_phase_func = exportAddr;
break;
case NID_MODULE_BOOTSTART:
module->nm.module_bootstart_func = exportAddr;
break;
default:
ResolveSyscall(name, nid, exportAddr);
}
}
for (u32 j = 0; j < ent->vcount; j++)
{
u32 nid = residentPtr[ent->fcount + j];
u32 exportAddr = residentPtr[ent->fcount + ent->vcount + ent->fcount + j];
switch (nid)
{
case NID_MODULE_INFO:
break;
case NID_MODULE_START_THREAD_PARAMETER:
if (Memory::Read_U32(exportAddr) != 3)
WARN_LOG_REPORT(LOADER, "Strange value at module_start_thread_parameter export: %08x", Memory::Read_U32(exportAddr));
module->nm.module_start_thread_priority = Memory::Read_U32(exportAddr + 4);
module->nm.module_start_thread_stacksize = Memory::Read_U32(exportAddr + 8);
module->nm.module_start_thread_attr = Memory::Read_U32(exportAddr + 12);
break;
case NID_MODULE_STOP_THREAD_PARAMETER:
if (Memory::Read_U32(exportAddr) != 3)
WARN_LOG_REPORT(LOADER, "Strange value at module_stop_thread_parameter export: %08x", Memory::Read_U32(exportAddr));
module->nm.module_stop_thread_priority = Memory::Read_U32(exportAddr + 4);
module->nm.module_stop_thread_stacksize = Memory::Read_U32(exportAddr + 8);
module->nm.module_stop_thread_attr = Memory::Read_U32(exportAddr + 12);
break;
case NID_MODULE_REBOOT_BEFORE_THREAD_PARAMETER:
if (Memory::Read_U32(exportAddr) != 3)
WARN_LOG_REPORT(LOADER, "Strange value at module_reboot_before_thread_parameter export: %08x", Memory::Read_U32(exportAddr));
module->nm.module_reboot_before_thread_priority = Memory::Read_U32(exportAddr + 4);
module->nm.module_reboot_before_thread_stacksize = Memory::Read_U32(exportAddr + 8);
module->nm.module_reboot_before_thread_attr = Memory::Read_U32(exportAddr + 12);
break;
case NID_MODULE_SDK_VERSION:
DEBUG_LOG(LOADER, "Module SDK: %08x", Memory::Read_U32(exportAddr));
break;
default:
DEBUG_LOG(LOADER, "Unexpected variable with nid: %08x", nid);
break;
}
}
}
if (ent->size > 4)
{
ent = (PspLibEntEntry*)((u8*)ent + ent->size * 4);
}
else
{
ent++;
}
}
module->nm.entry_addr = reader.GetEntryPoint();
// use module_start_func instead of entry_addr if entry_addr is 0
if (module->nm.entry_addr == 0)
module->nm.entry_addr = module->nm.module_start_func;
if (newptr)
{
delete [] newptr;
}
return module;
}
bool __KernelLoadPBP(const char *filename, std::string *error_string)
{
static const char *FileNames[] =
{
"PARAM.SFO", "ICON0.PNG", "ICON1.PMF", "UNKNOWN.PNG",
"PIC1.PNG", "SND0.AT3", "UNKNOWN.PSP", "UNKNOWN.PSAR"
};
PBPReader pbp(filename);
if (!pbp.IsValid()) {
ERROR_LOG(LOADER,"%s is not a valid homebrew PSP1.0 PBP",filename);
*error_string = "Not a valid homebrew PBP";
return false;
}
size_t elfSize;
u8 *elfData = pbp.GetSubFile(PBP_EXECUTABLE_PSP, &elfSize);
Module *module = __KernelLoadELFFromPtr(elfData, PSP_GetDefaultLoadAddress(), error_string);
if (!module) {
delete [] elfData;
return false;
}
mipsr4k.pc = module->nm.entry_addr;
delete [] elfData;
return true;
}
Module *__KernelLoadModule(u8 *fileptr, SceKernelLMOption *options, std::string *error_string)
{
Module *module = 0;
// Check for PBP
if (memcmp(fileptr, "\0PBP", 4) == 0)
{
// PBP!
u32 version;
memcpy(&version, fileptr + 4, 4);
u32 offset0, offsets[16];
int numfiles;
memcpy(&offset0, fileptr + 8, 4);
numfiles = (offset0 - 8)/4;
offsets[0] = offset0;
for (int i = 1; i < numfiles; i++)
memcpy(&offsets[i], fileptr + 12 + 4*i, 4);
module = __KernelLoadELFFromPtr(fileptr + offsets[5], PSP_GetDefaultLoadAddress(), error_string);
}
else
{
module = __KernelLoadELFFromPtr(fileptr, PSP_GetDefaultLoadAddress(), error_string);
}
return module;
}
void __KernelStartModule(Module *m, int args, const char *argp, SceKernelSMOption *options)
{
if (m->nm.module_start_func != 0 && m->nm.module_start_func != (u32)-1)
{
if (m->nm.module_start_func != m->nm.entry_addr)
WARN_LOG_REPORT(LOADER, "Main module has start func (%08x) different from entry (%08x)?", m->nm.module_start_func, m->nm.entry_addr);
}
__KernelSetupRootThread(m->GetUID(), args, argp, options->priority, options->stacksize, options->attribute);
mainModuleID = m->GetUID();
//TODO: if current thread, put it in wait state, waiting for the new thread
}
u32 __KernelGetModuleGP(SceUID uid)
{
u32 error;
Module *module = kernelObjects.Get<Module>(uid, error);
if (module)
{
return module->nm.gp_value;
}
else
{
return 0;
}
}
bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std::string *error_string)
{
// Wipe kernel here, loadexec should reset the entire system
if (__KernelIsRunning())
__KernelShutdown();
__KernelModuleInit();
__KernelInit();
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
u32 handle = pspFileSystem.OpenFile(filename, FILEACCESS_READ);
u8 *temp = new u8[(int)info.size + 0x1000000];
pspFileSystem.ReadFile(handle, temp, (size_t)info.size);
Module *module = __KernelLoadModule(temp, 0, error_string);
if (!module) {
ERROR_LOG(LOADER, "Failed to load module %s", filename);
return false;
}
mipsr4k.pc = module->nm.entry_addr;
INFO_LOG(LOADER, "Module entry: %08x", mipsr4k.pc);
delete [] temp;
pspFileSystem.CloseFile(handle);
SceKernelSMOption option;
option.size = sizeof(SceKernelSMOption);
option.attribute = PSP_THREAD_ATTR_USER;
option.mpidstack = 2;
option.priority = 0x20;
option.stacksize = 0x40000; // crazy? but seems to be the truth
// Replace start options with module-specified values if they exist.
if (module->nm.module_start_thread_attr != 0)
option.attribute = module->nm.module_start_thread_attr;
if (module->nm.module_start_thread_priority != 0)
option.priority = module->nm.module_start_thread_priority;
if (module->nm.module_start_thread_stacksize != 0)
option.stacksize = module->nm.module_start_thread_stacksize;
__KernelStartModule(module, (u32)strlen(filename) + 1, filename, &option);
__KernelStartIdleThreads();
return true;
}
//TODO: second param
int sceKernelLoadExec(const char *filename, u32 paramPtr)
{
SceKernelLoadExecParam *param = 0;
if (paramPtr)
{
param = (SceKernelLoadExecParam*)Memory::GetPointer(paramPtr);
}
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
if (!info.exists) {
ERROR_LOG(LOADER, "sceKernelLoadExec(%s, ...): File does not exist", filename);
return SCE_KERNEL_ERROR_NOFILE;
}
s64 size = (s64)info.size;
if (!size)
{
ERROR_LOG(LOADER, "sceKernelLoadExec(%s, ...): File is size 0", filename);
return SCE_KERNEL_ERROR_ILLEGAL_OBJECT;
}
DEBUG_LOG(HLE,"sceKernelLoadExec(name=%s,...)", filename);
std::string error_string;
if (!__KernelLoadExec(filename, param, &error_string)) {
ERROR_LOG(HLE, "sceKernelLoadExec failed: %s", error_string.c_str());
return -1;
}
return 0;
}
u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr)
{
if (!name) {
ERROR_LOG(LOADER, "sceKernelLoadModule(NULL, %08x): Bad name", flags);
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
}
PSPFileInfo info = pspFileSystem.GetFileInfo(name);
std::string error_string;
s64 size = (s64)info.size;
if (!info.exists) {
ERROR_LOG(LOADER, "sceKernelLoadModule(%s, %08x): File does not exist", name, flags);
return SCE_KERNEL_ERROR_NOFILE;
}
if (!size) {
ERROR_LOG(LOADER, "sceKernelLoadModule(%s, %08x): Module file is size 0", name, flags);
return SCE_KERNEL_ERROR_ILLEGAL_OBJECT;
}
DEBUG_LOG(LOADER, "sceKernelLoadModule(%s, %08x)", name, flags);
SceKernelLMOption *lmoption = 0;
int position = 0;
// TODO: Use position to decide whether to load high or low
if (optionAddr) {
lmoption = (SceKernelLMOption *)Memory::GetPointer(optionAddr);
}
Module *module = 0;
u8 *temp = new u8[(int)size];
u32 handle = pspFileSystem.OpenFile(name, FILEACCESS_READ);
pspFileSystem.ReadFile(handle, temp, (size_t)size);
module = __KernelLoadELFFromPtr(temp, 0, &error_string);
delete [] temp;
pspFileSystem.CloseFile(handle);
if (!module) {
// Module was blacklisted or couldn't be decrypted, which means it's a kernel module we don't want to run.
// Let's just act as if it worked.
NOTICE_LOG(LOADER, "Module %s is blacklisted or undecryptable - we lie about success", name);
return 1;
}
if (lmoption) {
INFO_LOG(HLE,"%i=sceKernelLoadModule(name=%s,flag=%08x,%08x,%08x,%08x,position = %08x)",
module->GetUID(),name,flags,
lmoption->size,lmoption->mpidtext,lmoption->mpiddata,lmoption->position);
} else {
INFO_LOG(HLE,"%i=sceKernelLoadModule(name=%s,flag=%08x,(...))", module->GetUID(), name, flags);
}
// TODO: This is not the right timing and probably not the right wait type, just an approximation.
return hleDelayResult(module->GetUID(), "module loaded", 500);
}
void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr)
{
// Dunno what these three defaults should be...
u32 priority = 0x20;
u32 stacksize = 0x40000;
u32 attr = 0;
int stackPartition = 0;
if (optionAddr) {
SceKernelSMOption smoption;
Memory::ReadStruct(optionAddr, &smoption);;
priority = smoption.priority;
attr = smoption.attribute;
stacksize = smoption.stacksize;
stackPartition = smoption.mpidstack;
}
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
if (!module) {
RETURN(error);
return;
} else if (module->isFake) {
INFO_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): faked (undecryptable module)",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
} else {
WARN_LOG(HLE, "UNIMPL sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
u32 entryAddr = module->nm.entry_addr;
if (entryAddr == (u32)-1 || entryAddr == module->memoryBlockAddr - 1) {
// TODO: Do these always take effect, or do they not override optionAddr?
if (module->nm.module_start_func != 0 && module->nm.module_start_func != (u32)-1) {
entryAddr = module->nm.module_start_func;
priority = module->nm.module_start_thread_priority;
attr = module->nm.module_start_thread_attr;
stacksize = module->nm.module_start_thread_stacksize;
} else {
// TODO: Fix, check return value? Or do we call nothing?
RETURN(moduleId);
return;
}
}
SceUID threadID = __KernelCreateThread(module->nm.name, moduleId, entryAddr, priority, stacksize, attr, 0);
sceKernelStartThread(threadID, argsize, argAddr);
// TODO: This will probably return the wrong value?
sceKernelWaitThreadEnd(threadID, 0);
}
// TODO: Is this the correct return value?
RETURN(moduleId);
}
u32 sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 returnValueAddr, u32 optionAddr)
{
ERROR_LOG(HLE,"UNIMPL sceKernelStopModule(%08x, %08x, %08x, %08x, %08x)", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
// We should call the "stop" entry point and return the value in returnValueAddr. See StartModule.
return 0;
}
u32 sceKernelUnloadModule(u32 moduleId)
{
INFO_LOG(HLE,"sceKernelUnloadModule(%i)", moduleId);
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
if (!module)
return error;
kernelObjects.Destroy<Module>(moduleId);
return 0;
}
u32 sceKernelStopUnloadSelfModuleWithStatus(u32 moduleId, u32 argSize, u32 argp, u32 statusAddr, u32 optionAddr)
{
ERROR_LOG(HLE,"UNIMPL sceKernelStopUnloadSelfModuleWithStatus(%08x, %08x, %08x, %08x, %08x,)", moduleId, argSize, argp, statusAddr, optionAddr);
return 0;
}
struct GetModuleIdByAddressArg
{
u32 addr;
SceUID result;
};
bool __GetModuleIdByAddressIterator(Module *module, GetModuleIdByAddressArg *state)
{
const u32 start = module->memoryBlockAddr, size = module->memoryBlockSize;
printf("%08x - %08x ? %08x\n", start, start + size, state->addr);
if (start <= state->addr && start + size > state->addr)
{
state->result = module->GetUID();
return false;
}
return true;
}
u32 sceKernelGetModuleIdByAddress(u32 moduleAddr)
{
GetModuleIdByAddressArg state;
state.addr = moduleAddr;
state.result = SCE_KERNEL_ERROR_UNKNOWN_MODULE;
kernelObjects.Iterate(&__GetModuleIdByAddressIterator, &state);
if (state.result == SCE_KERNEL_ERROR_UNKNOWN_MODULE)
ERROR_LOG(HLE, "sceKernelGetModuleIdByAddress(%08x): module not found", moduleAddr)
else
DEBUG_LOG(HLE, "%x=sceKernelGetModuleIdByAddress(%08x)", state.result, moduleAddr);
return state.result;
}
u32 sceKernelGetModuleId()
{
INFO_LOG(HLE,"sceKernelGetModuleId()");
return __KernelGetCurThreadModuleId();
}
u32 sceKernelFindModuleByName()
{
ERROR_LOG(HLE,"UNIMPL sceKernelFindModuleByName()");
return 1;
}
u32 sceKernelLoadModuleByID(u32 id, u32 flags, u32 lmoptionPtr)
{
u32 error;
u32 handle = __IoGetFileHandleFromId(id, error);
if (handle == (u32)-1) {
ERROR_LOG(HLE,"sceKernelLoadModuleByID(%08x, %08x, %08x): could not open file id",id,flags,lmoptionPtr);
return error;
}
SceKernelLMOption *lmoption = 0;
if (lmoptionPtr) {
lmoption = (SceKernelLMOption *)Memory::GetPointer(lmoptionPtr);
}
u32 pos = (u32) pspFileSystem.SeekFile(handle, 0, FILEMOVE_CURRENT);
size_t size = pspFileSystem.SeekFile(handle, 0, FILEMOVE_END);
std::string error_string;
pspFileSystem.SeekFile(handle, pos, FILEMOVE_BEGIN);
Module *module = 0;
u8 *temp = new u8[size];
pspFileSystem.ReadFile(handle, temp, size);
module = __KernelLoadELFFromPtr(temp, 0, &error_string);
delete [] temp;
if (!module) {
// Module was blacklisted or couldn't be decrypted, which means it's a kernel module we don't want to run.
// Let's just act as if it worked.
NOTICE_LOG(LOADER, "Module %d is blacklisted or undecryptable - we lie about success", id);
return 1;
}
if (lmoption) {
INFO_LOG(HLE,"%i=sceKernelLoadModuleByID(%d,flag=%08x,%08x,%08x,%08x,position = %08x)",
module->GetUID(),id,flags,
lmoption->size,lmoption->mpidtext,lmoption->mpiddata,lmoption->position);
} else {
INFO_LOG(HLE,"%i=sceKernelLoadModuleByID(%d,flag=%08x,(...))", module->GetUID(), id, flags);
}
return module->GetUID();
}
u32 sceKernelLoadModuleDNAS(const char *name, u32 flags)
{
ERROR_LOG(HLE,"UNIMPL 0=sceKernelLoadModuleDNAS()");
return 0;
}
u32 sceKernelQueryModuleInfo(u32 uid, u32 infoAddr)
{
INFO_LOG(HLE, "sceKernelQueryModuleInfo(%i, %08x)", uid, infoAddr);
u32 error;
Module *module = kernelObjects.Get<Module>(uid, error);
if (!module)
return error;
if (!Memory::IsValidAddress(infoAddr)) {
ERROR_LOG(HLE, "sceKernelQueryModuleInfo(%i, %08x) - bad infoAddr", uid, infoAddr);
return -1;
}
ModuleInfo info;
memcpy(info.segmentaddr, module->nm.segmentaddr, sizeof(info.segmentaddr));
memcpy(info.segmentsize, module->nm.segmentsize, sizeof(info.segmentsize));
info.nsegment = module->nm.nsegment;
info.entry_addr = module->nm.entry_addr;
info.gp_value = module->nm.gp_value;
info.text_addr = module->nm.text_addr;
info.text_size = module->nm.text_size;
info.data_size = module->nm.data_size;
info.bss_size = module->nm.bss_size;
info.attribute = module->nm.attribute;
info.version[0] = module->nm.version[0];
info.version[1] = module->nm.version[1];
memcpy(info.name, module->nm.name, 28);
Memory::WriteStruct(infoAddr, &info);
return 0;
}
const HLEFunction ModuleMgrForUser[] =
{
{0x977DE386,&WrapU_CUU<sceKernelLoadModule>,"sceKernelLoadModule"},
{0xb7f46618,&WrapU_UUU<sceKernelLoadModuleByID>,"sceKernelLoadModuleByID"},
{0x50F0C1EC,&WrapV_UUUUU<sceKernelStartModule>,"sceKernelStartModule"},
{0xD675EBB8,&sceKernelExitGame,"sceKernelSelfStopUnloadModule"}, //HACK
{0xd1ff982a,&WrapU_UUUUU<sceKernelStopModule>,"sceKernelStopModule"},
{0x2e0911aa,WrapU_U<sceKernelUnloadModule>,"sceKernelUnloadModule"},
{0x710F61B5,0,"sceKernelLoadModuleMs"},
{0xF9275D98,0,"sceKernelLoadModuleBufferUsbWlan"}, ///???
{0xCC1D3699,0,"sceKernelStopUnloadSelfModule"},
{0x748CBED9,WrapU_UU<sceKernelQueryModuleInfo>,"sceKernelQueryModuleInfo"},
{0xd8b73127,&WrapU_U<sceKernelGetModuleIdByAddress>, "sceKernelGetModuleIdByAddress"},
{0xf0a26395,WrapU_V<sceKernelGetModuleId>, "sceKernelGetModuleId"},
{0x8f2df740,WrapU_UUUUU<sceKernelStopUnloadSelfModuleWithStatus>,"sceKernelStopUnloadSelfModuleWithStatus"},
{0xfef27dc1,&WrapU_CU<sceKernelLoadModuleDNAS> , "sceKernelLoadModuleDNAS"},
};
void Register_ModuleMgrForUser()
{
RegisterModule("ModuleMgrForUser", ARRAY_SIZE(ModuleMgrForUser), ModuleMgrForUser);
}