vita3k: refactoring module load and start, use LLE for libsmart and libface

This commit is contained in:
bookmist 2023-06-18 15:28:58 +03:00
parent 569fe85f5a
commit c7440923c2
18 changed files with 285 additions and 297 deletions

View File

@ -17,6 +17,8 @@
#include "interface.h"
#include "module/load_module.h"
#include <config/state.h>
#include <ctrl/functions.h>
#include <ctrl/state.h>
@ -413,36 +415,7 @@ uint32_t install_contents(EmuEnvState &emuenv, GuiState *gui, const fs::path &pa
return installed;
}
static auto pre_load_module(EmuEnvState &emuenv, const std::vector<std::string> &lib_load_list, const VitaIoDevice &device) {
for (const auto &module_path : lib_load_list) {
vfs::FileBuffer module_buffer;
Ptr<const void> lib_entry_point;
bool res;
const auto MODULE_PATH_ABS = fmt::format("{}:{}", device._to_string(), module_path);
if (device == VitaIoDevice::app0)
res = vfs::read_app_file(module_buffer, emuenv.pref_path, emuenv.io.app_path, module_path);
else
res = vfs::read_file(device, module_buffer, emuenv.pref_path, module_path);
if (res) {
SceUID module_id = load_self(lib_entry_point, emuenv.kernel, emuenv.mem, module_buffer.data(), MODULE_PATH_ABS);
if (module_id >= 0) {
const auto module = emuenv.kernel.loaded_modules[module_id];
LOG_INFO("Pre-load module {} (at \"{}\") loaded", module->module_name, module_path);
} else
return FileNotFound;
} else {
LOG_DEBUG("Pre-load module at \"{}\" not present", module_path);
return FileNotFound;
}
}
return Success;
}
static ExitCode load_app_impl(Ptr<const void> &entry_point, EmuEnvState &emuenv, const std::wstring &path) {
static ExitCode load_app_impl(SceUID &main_module_id, EmuEnvState &emuenv, const std::wstring &path) {
if (path.empty())
return InvalidApplicationPath;
@ -490,63 +463,59 @@ static ExitCode load_app_impl(Ptr<const void> &entry_point, EmuEnvState &emuenv,
init_device_paths(emuenv.io);
init_savedata_app_path(emuenv.io, emuenv.pref_path);
// todo: VAR_NID(__sce_libcparam, 0xDF084DFA) is loaded wrong
for (const auto &var : get_var_exports()) {
auto addr = var.factory(emuenv);
emuenv.kernel.export_nids.emplace(var.nid, addr);
}
// FIXME: The application EBOOT should be the first module ever loaded in the address space!
// Load pre-loaded libraries
const auto module_app_path{ fs::path(emuenv.pref_path) / "ux0/app" / emuenv.io.app_path / "sce_module" };
const auto is_app = fs::exists(module_app_path) && !fs::is_empty(module_app_path);
if (is_app) {
// Load application module
const std::vector<std::string> lib_load_list = {
"sce_module/libc.suprx",
"sce_module/libfios2.suprx",
"sce_module/libult.suprx",
};
pre_load_module(emuenv, lib_load_list, VitaIoDevice::app0);
}
// Load pre-loaded font fw libraries
std::vector<std::string> lib_load_list = {
"sys/external/libSceFt2.suprx",
"sys/external/libpvf.suprx",
};
if (!is_app) {
// Load pre-loaded fw libraries if app libraries not exist
const std::vector<std::string> lib_load_list_to_add = {
"sys/external/libc.suprx",
"sys/external/libfios2.suprx",
"sys/external/libult.suprx"
};
lib_load_list.insert(lib_load_list.begin(), lib_load_list_to_add.begin(), lib_load_list_to_add.end());
}
pre_load_module(emuenv, lib_load_list, VitaIoDevice::vs0);
// Load main executable
emuenv.self_path = !emuenv.cfg.self_path.empty() ? emuenv.cfg.self_path : EBOOT_PATH;
vfs::FileBuffer eboot_buffer;
if (vfs::read_app_file(eboot_buffer, emuenv.pref_path, emuenv.io.app_path, emuenv.self_path)) {
SceUID module_id = load_self(entry_point, emuenv.kernel, emuenv.mem, eboot_buffer.data(), "app0:" + emuenv.self_path);
if (module_id >= 0) {
const auto module = emuenv.kernel.loaded_modules[module_id];
LOG_INFO("Main executable {} ({}) loaded", module->module_name, emuenv.self_path);
} else
return FileNotFound;
main_module_id = load_module(emuenv, "app0:" + emuenv.self_path);
if (main_module_id >= 0) {
const auto module = emuenv.kernel.loaded_modules[main_module_id];
LOG_INFO("Main executable {} ({}) loaded", module->module_name, emuenv.self_path);
} else
return FileNotFound;
// Set self name from self path, can contain folder, get file name only
emuenv.self_name = fs::path(emuenv.self_path).filename().string();
// get list of preload modules
SceUInt32 process_preload_disabled = 0;
auto process_param = emuenv.kernel.process_param.get(emuenv.mem);
if (process_param) {
auto preload_disabled_ptr = Ptr<SceUInt32>(process_param->process_preload_disabled);
if (preload_disabled_ptr) {
process_preload_disabled = *preload_disabled_ptr.get(emuenv.mem);
}
}
const auto module_app_path{ fs::path(emuenv.pref_path) / "ux0/app" / emuenv.io.app_path / "sce_module" };
const auto is_app = fs::exists(module_app_path) && !fs::is_empty(module_app_path);
std::vector<std::string> lib_load_list = {};
// todo: check if module is imported
auto add_preload_module = [&](uint32_t code, const std::string &name, bool load_from_app) {
if ((process_preload_disabled & code) == 0 && is_lle_module(name, emuenv)) {
if (load_from_app)
lib_load_list.emplace_back(fmt::format("app0:sce_module/{}.suprx", name));
else
lib_load_list.emplace_back(fmt::format("vs0:sys/external/{}.suprx", name));
}
};
add_preload_module(0x00010000, "libc", is_app);
add_preload_module(0x00020000, "libdbg", false);
add_preload_module(0x00080000, "libshellsvc", false);
add_preload_module(0x00100000, "libcdlg", false);
add_preload_module(0x00200000, "libfios2", is_app);
add_preload_module(0x00400000, "apputil", false);
add_preload_module(0x00800000, "libSceFt2", false);
add_preload_module(0x01000000, "libpvf", false);
add_preload_module(0x02000000, "libperf", false); // if DEVELOPMENT_MODE dipsw is set
for (const auto &module_path : lib_load_list) {
auto res = load_module(emuenv, module_path);
if (res < 0)
return FileNotFound;
}
return Success;
}
@ -818,8 +787,8 @@ bool handle_events(EmuEnvState &emuenv, GuiState &gui) {
return true;
}
ExitCode load_app(Ptr<const void> &entry_point, EmuEnvState &emuenv, const std::wstring &path) {
if (load_app_impl(entry_point, emuenv, path) != Success) {
ExitCode load_app(int32_t &main_module_id, EmuEnvState &emuenv, const std::wstring &path) {
if (load_app_impl(main_module_id, emuenv, path) != Success) {
std::string message = "Failed to load \"";
message += string_utils::wide_to_utf(path);
message += "\"";
@ -852,37 +821,42 @@ static std::vector<std::string> split(const std::string &input, const std::strin
return { first, last };
}
ExitCode run_app(EmuEnvState &emuenv, Ptr<const void> &entry_point) {
const ThreadStatePtr thread = emuenv.kernel.create_thread(emuenv.mem, emuenv.io.title_id.c_str(), entry_point, SCE_KERNEL_DEFAULT_PRIORITY_USER, SCE_KERNEL_THREAD_CPU_AFFINITY_MASK_DEFAULT, static_cast<int>(SCE_KERNEL_STACK_SIZE_USER_MAIN), nullptr);
if (!thread) {
ExitCode run_app(EmuEnvState &emuenv, int32_t main_module_id) {
auto entry_point = emuenv.kernel.loaded_modules[main_module_id]->start_entry;
auto process_param = emuenv.kernel.process_param.get(emuenv.mem);
SceInt32 priority = SCE_KERNEL_DEFAULT_PRIORITY_USER;
SceInt32 stack_size = SCE_KERNEL_STACK_SIZE_USER_MAIN;
SceInt32 affinity = SCE_KERNEL_THREAD_CPU_AFFINITY_MASK_DEFAULT;
if (process_param) {
auto priority_ptr = Ptr<int32_t>(process_param->main_thread_priority);
if (priority_ptr) {
priority = *priority_ptr.get(emuenv.mem);
}
auto stack_size_ptr = Ptr<int32_t>(process_param->main_thread_stacksize);
if (stack_size_ptr) {
stack_size = *stack_size_ptr.get(emuenv.mem);
}
auto affinity_ptr = Ptr<SceInt32>(process_param->main_thread_cpu_affinity_mask);
if (affinity_ptr) {
affinity = *affinity_ptr.get(emuenv.mem);
}
}
const ThreadStatePtr main_thread = emuenv.kernel.create_thread(emuenv.mem, emuenv.io.title_id.c_str(), entry_point, priority, affinity, stack_size, nullptr);
if (!main_thread) {
app::error_dialog("Failed to init main thread.", emuenv.window.get());
return InitThreadFailed;
}
const SceUID main_thread_id = thread->id;
const ThreadStatePtr main_thread = util::find(main_thread_id, emuenv.kernel.threads);
emuenv.main_thread_id = main_thread->id;
// Run `module_start` export (entry point) of loaded libraries
for (auto &mod : emuenv.kernel.loaded_modules) {
const auto module = mod.second;
const auto module_start = module->start_entry;
const auto module_name = module->module_name;
if (!module_start || (std::string(module->path) == "app0:" + emuenv.self_path))
continue;
LOG_DEBUG("Running module_start of library: {} at address {}", module_name, log_hex(module_start.address()));
// TODO: why does fios need separate thread its stack freed anyways?
const ThreadStatePtr module_thread = emuenv.kernel.create_thread(emuenv.mem, module_name);
const auto ret = module_thread->run_guest_function(emuenv.kernel, module_start.address());
module_thread->exit_delete(emuenv.kernel);
LOG_INFO("Module {} (at \"{}\") module_start returned {}", module_name, module->path, log_hex(ret));
for (auto &[_, module] : emuenv.kernel.loaded_modules) {
if (module->modid != main_module_id)
start_module(emuenv, module);
}
emuenv.main_thread_id = main_thread_id;
SceKernelThreadOptParam param{ 0, 0 };
if (!emuenv.cfg.app_args.empty()) {
auto args = split(emuenv.cfg.app_args, ",\\s+");

View File

@ -57,5 +57,5 @@ bool handle_events(EmuEnvState &emuenv, GuiState &gui);
std::vector<ContentInfo> install_archive(EmuEnvState &emuenv, GuiState *gui, const fs::path &archive_path, const std::function<void(ArchiveContents)> &progress_callback = nullptr);
uint32_t install_contents(EmuEnvState &emuenv, GuiState *gui, const fs::path &path);
ExitCode load_app(Ptr<const void> &entry_point, EmuEnvState &emuenv, const std::wstring &path);
ExitCode run_app(EmuEnvState &emuenv, Ptr<const void> &entry_point);
ExitCode load_app(int32_t &main_module_id, EmuEnvState &emuenv, const std::wstring &path);
ExitCode run_app(EmuEnvState &emuenv, int32_t main_module_id);

View File

@ -64,7 +64,7 @@ bool copy_directories(const fs::path &src_path, const fs::path &dst_path);
* @return true Success
* @return false Error
*/
bool copy_path(const fs::path src_path, std::wstring pref_path, std::string app_title_id, std::string app_category);
bool copy_path(const fs::path &src_path, const std::wstring &pref_path, const std::string &app_title_id, const std::string &app_category);
SceUID open_file(IOState &io, const char *path, const int flags, const std::wstring &pref_path, const char *export_name);
int read_file(void *data, IOState &io, SceUID fd, SceSize size, const char *export_name);

View File

@ -783,7 +783,7 @@ bool copy_directories(const fs::path &src_path, const fs::path &dst_path) {
}
}
bool copy_path(const fs::path src_path, std::wstring pref_path, std::string app_title_id, std::string app_category) {
bool copy_path(const fs::path &src_path, const std::wstring &pref_path, const std::string &app_title_id, const std::string &app_category) {
// Check if is path
if (app_category.find("gp") != std::string::npos) {
const auto app_path{ fs::path(pref_path) / "ux0/app" / app_title_id };
@ -884,7 +884,7 @@ SceUID create_overlay(IOState &io, SceFiosProcessOverlay *fios_overlay) {
};
// find location where to put it
int overlay_index = 0;
size_t overlay_index = 0;
// lower order first and in case of equality, last one inserted first
while (overlay_index < io.overlays.size() && overlay.order < io.overlays[overlay_index].order)
overlay_index++;
@ -899,7 +899,7 @@ std::string resolve_path(IOState &io, const char *input, const bool is_write, co
std::string curr_path = input;
int overlay_idx = 0;
size_t overlay_idx = 0;
while (overlay_idx < io.overlays.size() && io.overlays[overlay_idx].order < min_order)
overlay_idx++;

View File

@ -27,4 +27,4 @@ struct MemState;
template <class T>
class Ptr;
SceUID load_self(Ptr<const void> &entry_point, KernelState &kernel, MemState &mem, const void *self, const std::string &path);
SceUID load_self(KernelState &kernel, MemState &mem, const void *self, const std::string &path);

View File

@ -57,7 +57,6 @@ typedef std::map<SceUID, Ptr<Ptr<void>>> SlotToAddress;
typedef std::map<SceUID, ThreadStatePtr> ThreadStatePtrs;
typedef std::shared_ptr<SDL_Thread> ThreadPtr;
typedef std::map<SceUID, ThreadPtr> ThreadPtrs;
typedef std::shared_ptr<SceKernelModuleInfo> SceKernelModuleInfoPtr;
typedef std::map<SceUID, SceKernelModuleInfoPtr> SceKernelModuleInfoPtrs;
typedef std::map<SceUID, CallbackPtr> CallbackPtrs;
typedef std::unordered_map<uint32_t, Address> ExportNids;

View File

@ -114,8 +114,8 @@ struct ThreadState {
// this function is called from another thread when this one is dormant
// it is only used for module loading and gxm display queue right now
// support one argument
uint32_t run_guest_function(KernelState &kernel, Address callback_address, uint32_t arg = 0);
// args and argp are passed to thread->start as is
uint32_t run_guest_function(KernelState &kernel, Address callback_address, SceSize args = 0, const Ptr<void> argp = Ptr<void>{});
void suspend();
void resume(bool step = false);

View File

@ -39,6 +39,7 @@
#define SCE_KERNEL_STACK_SIZE_USER_MAIN KiB(256)
#define SCE_KERNEL_STACK_SIZE_USER_DEFAULT KiB(4)
#define SCE_KERNEL_THREAD_STACK_SIZE_MAX MiB(32)
#define SCE_KERNEL_ATTR_TH_FIFO 0x00000000U
#define SCE_KERNEL_ATTR_TH_PRIO 0x00002000U
@ -590,6 +591,8 @@ struct SceKernelModuleInfo {
SceUInt state; //!< see:SceKernelModuleState
};
typedef std::shared_ptr<SceKernelModuleInfo> SceKernelModuleInfoPtr;
struct SceKernelStartModuleOpt {
SceSize size;
SceUInt32 flags;

View File

@ -135,8 +135,7 @@ ThreadStatePtr KernelState::get_thread(SceUID thread_id) {
}
ThreadStatePtr KernelState::create_thread(MemState &mem, const char *name, Ptr<const void> entry_point) {
constexpr size_t DEFAULT_STACK_SIZE = 0x1000;
return create_thread(mem, name, entry_point, SCE_KERNEL_DEFAULT_PRIORITY, SCE_KERNEL_THREAD_CPU_AFFINITY_MASK_DEFAULT, DEFAULT_STACK_SIZE, nullptr);
return create_thread(mem, name, entry_point, SCE_KERNEL_DEFAULT_PRIORITY, SCE_KERNEL_THREAD_CPU_AFFINITY_MASK_DEFAULT, SCE_KERNEL_STACK_SIZE_USER_MAIN, nullptr);
}
ThreadStatePtr KernelState::create_thread(MemState &mem, const char *name, Ptr<const void> entry_point, int init_priority, SceInt32 affinity_mask, int stack_size, const SceKernelThreadOptParam *option) {

View File

@ -211,18 +211,24 @@ static bool load_imports(const sce_module_info_raw &module, Ptr<const void> segm
return true;
}
static bool load_func_exports(Ptr<const void> &entry_point, const uint32_t *nids, const Ptr<uint32_t> *entries, size_t count, KernelState &kernel) {
static bool load_func_exports(SceKernelModuleInfo *kernel_module_info, const uint32_t *nids, const Ptr<uint32_t> *entries, size_t count, KernelState &kernel) {
for (size_t i = 0; i < count; ++i) {
const uint32_t nid = nids[i];
const Ptr<uint32_t> entry = entries[i];
if (nid == NID_MODULE_START) {
entry_point = entry;
kernel_module_info->start_entry = entry;
continue;
}
if (nid == NID_MODULE_STOP || nid == NID_MODULE_EXIT)
if (nid == NID_MODULE_STOP) {
kernel_module_info->stop_entry = entry;
continue;
}
if (nid == NID_MODULE_EXIT) {
kernel_module_info->exit_entry = entry;
continue;
}
{
const std::unique_lock<std::shared_mutex> lock(kernel.export_nids_mutex);
@ -330,7 +336,7 @@ static bool load_var_exports(const uint32_t *nids, const Ptr<uint32_t> *entries,
return true;
}
static bool load_exports(Ptr<const void> &entry_point, const sce_module_info_raw &module, Ptr<const void> segment_address, KernelState &kernel, MemState &mem) {
static bool load_exports(SceKernelModuleInfo *kernel_module_info, const sce_module_info_raw &module, Ptr<const void> segment_address, KernelState &kernel, MemState &mem) {
const uint8_t *const base = segment_address.cast<const uint8_t>().get(mem);
const sce_module_exports_raw *const exports_begin = reinterpret_cast<const sce_module_exports_raw *>(base + module.export_top);
const sce_module_exports_raw *const exports_end = reinterpret_cast<const sce_module_exports_raw *>(base + module.export_end);
@ -344,7 +350,7 @@ static bool load_exports(Ptr<const void> &entry_point, const sce_module_info_raw
const uint32_t *const nids = Ptr<const uint32_t>(exports->nid_table).get(mem);
const Ptr<uint32_t> *const entries = Ptr<Ptr<uint32_t>>(exports->entry_table).get(mem);
if (!load_func_exports(entry_point, nids, entries, exports->num_syms_funcs, kernel)) {
if (!load_func_exports(kernel_module_info, nids, entries, exports->num_syms_funcs, kernel)) {
return false;
}
@ -365,7 +371,7 @@ static bool load_exports(Ptr<const void> &entry_point, const sce_module_info_raw
/**
* \return Negative on failure
*/
SceUID load_self(Ptr<const void> &entry_point, KernelState &kernel, MemState &mem, const void *self, const std::string &self_path) {
SceUID load_self(KernelState &kernel, MemState &mem, const void *self, const std::string &self_path) {
// TODO: use raw I/O from path when io becomes less bad
const uint8_t *const self_bytes = static_cast<const uint8_t *>(self);
const SCE_header &self_header = *static_cast<const SCE_header *>(self);
@ -463,8 +469,7 @@ SceUID load_self(Ptr<const void> &entry_point, KernelState &kernel, MemState &me
SegmentInfosForReloc segment_reloc_info;
auto free_all_segments = [](MemState &mem, SegmentInfosForReloc &segs_info) {
for (auto _seg : segs_info) {
const SegmentInfoForReloc &segment = _seg.second;
for (auto &[_, segment] : segs_info) {
free(mem, segment.addr);
}
};
@ -593,21 +598,15 @@ SceUID load_self(Ptr<const void> &entry_point, KernelState &kernel, MemState &me
strncpy(sceKernelModuleInfo->module_name, module_info->name, 28);
// unk28
if (module_info->module_start != 0xffffffff && module_info->module_start != 0)
entry_point = module_info_segment_address + module_info->module_start;
else
entry_point = Ptr<const void>(0);
sceKernelModuleInfo->start_entry = module_info_segment_address + module_info->module_start;
// unk30
if (module_info->module_stop != 0xffffffff && module_info->module_stop != 0)
sceKernelModuleInfo->stop_entry = module_info_segment_address + module_info->module_stop;
const Ptr<const void> exidx_top = Ptr<const void>(module_info->exidx_top);
sceKernelModuleInfo->exidx_top = exidx_top;
const Ptr<const void> exidx_btm = Ptr<const void>(module_info->exidx_end);
sceKernelModuleInfo->exidx_btm = exidx_btm;
const Ptr<const void> extab_top = Ptr<const void>(module_info->extab_top);
sceKernelModuleInfo->extab_top = extab_top;
const Ptr<const void> extab_end = Ptr<const void>(module_info->extab_end);
sceKernelModuleInfo->extab_btm = extab_end;
sceKernelModuleInfo->exidx_top = Ptr<const void>(module_info->exidx_top);
sceKernelModuleInfo->exidx_btm = Ptr<const void>(module_info->exidx_end);
sceKernelModuleInfo->extab_top = Ptr<const void>(module_info->extab_top);
sceKernelModuleInfo->extab_btm = Ptr<const void>(module_info->extab_end);
sceKernelModuleInfo->tlsInit = Ptr<const void>((!module_info->tls_start ? 0 : (module_info_segment_address.address() + module_info->tls_start)));
sceKernelModuleInfo->tlsInitSize = module_info->tls_filesz;
@ -643,7 +642,7 @@ SceUID load_self(Ptr<const void> &entry_point, KernelState &kernel, MemState &me
LOG_INFO("Linking SELF {}...", self_path);
if (!load_exports(entry_point, *module_info, module_info_segment_address, kernel, mem)) {
if (!load_exports(sceKernelModuleInfo.get(), *module_info, module_info_segment_address, kernel, mem)) {
return -1;
}
@ -651,9 +650,6 @@ SceUID load_self(Ptr<const void> &entry_point, KernelState &kernel, MemState &me
return -1;
}
sceKernelModuleInfo->start_entry = entry_point;
// TODO: module_stop
const SceUID uid = kernel.get_next_uid();
sceKernelModuleInfo->modid = uid;
{

View File

@ -167,7 +167,7 @@ int ThreadState::start(KernelState &kernel, SceSize arglen, const Ptr<void> &arg
void ThreadState::exit(KernelState &kernel, SceInt32 status) {
if (kernel.thread_event_end) {
int ret = run_callback(kernel.thread_event_end.address(), { SCE_KERNEL_THREAD_EVENT_TYPE_START, static_cast<uint32_t>(id), 0, kernel.thread_event_end_arg });
int ret = run_callback(kernel.thread_event_end.address(), { SCE_KERNEL_THREAD_EVENT_TYPE_END, static_cast<uint32_t>(id), 0, kernel.thread_event_end_arg });
if (ret != 0)
LOG_WARN("Thread end event handler returned {}", log_hex(ret));
}
@ -179,7 +179,7 @@ void ThreadState::exit(KernelState &kernel, SceInt32 status) {
void ThreadState::exit_delete(KernelState &kernel, bool exit) {
if (exit && kernel.thread_event_end) {
int ret = run_callback(kernel.thread_event_end.address(), { SCE_KERNEL_THREAD_EVENT_TYPE_START, static_cast<uint32_t>(id), 0, kernel.thread_event_end_arg });
int ret = run_callback(kernel.thread_event_end.address(), { SCE_KERNEL_THREAD_EVENT_TYPE_END, static_cast<uint32_t>(id), 0, kernel.thread_event_end_arg });
if (ret != 0)
LOG_WARN("Thread end event handler returned {}", log_hex(ret));
}
@ -309,13 +309,12 @@ uint32_t ThreadState::run_callback(Address callback_address, const std::vector<u
return returned_value;
}
uint32_t ThreadState::run_guest_function(KernelState &kernel, Address callback_address, uint32_t arg) {
uint32_t ThreadState::run_guest_function(KernelState &kernel, Address callback_address, SceSize args, const Ptr<void> argp) {
// save the previous entry point, just in case
const auto old_entry_point = entry_point;
entry_point = callback_address;
// this puts arg in the first register
start(kernel, arg, Ptr<void>(0));
start(kernel, args, argp);
{
// wait for the function to return
std::unique_lock<std::mutex> lock(mutex);

View File

@ -340,7 +340,7 @@ int main(int argc, char *argv[]) {
gui::init_app_background(gui, emuenv, emuenv.io.app_path);
gui::update_last_time_app_used(gui, emuenv, emuenv.io.app_path);
const auto draw_app_background = [&](GuiState &gui, EmuEnvState &emuenv) {
const auto draw_app_background = [](GuiState &gui, EmuEnvState &emuenv) {
const auto pos_min = ImVec2(emuenv.viewport_pos.x, emuenv.viewport_pos.y);
const auto pos_max = ImVec2(pos_min.x + emuenv.viewport_size.x, pos_min.y + emuenv.viewport_size.y);
@ -352,10 +352,12 @@ int main(int argc, char *argv[]) {
gui::draw_background(gui, emuenv);
};
Ptr<const void> entry_point;
if (const auto err = load_app(entry_point, emuenv, string_utils::utf_to_wide(emuenv.io.app_path)) != Success)
return err;
int32_t main_module_id;
{
const auto err = load_app(main_module_id, emuenv, string_utils::utf_to_wide(emuenv.io.app_path));
if (err != Success)
return err;
}
gui.vita_area.information_bar = false;
// Pre-Compile Shaders
@ -376,10 +378,11 @@ int main(int argc, char *argv[]) {
emuenv.renderer->swap_window(emuenv.window.get());
}
}
if (const auto err = run_app(emuenv, entry_point) != Success)
return err;
{
const auto err = run_app(emuenv, main_module_id);
if (err != Success)
return err;
}
SDL_SetWindowTitle(emuenv.window.get(), fmt::format("{} | {} ({}) | Please wait, loading...", window_title, emuenv.current_app_title, emuenv.io.title_id).c_str());
while (handle_events(emuenv, gui) && (emuenv.frame_count == 0) && !emuenv.load_exec) {

View File

@ -117,4 +117,5 @@ inline SysmodulePaths init_sysmodule_paths() {
const SysmodulePaths sysmodule_paths = init_sysmodule_paths();
bool is_lle_module(SceSysmoduleModuleId module_id, EmuEnvState &emuenv);
bool is_lle_module(const std::string &module_name, EmuEnvState &emuenv);
bool is_module_loaded(KernelState &kernel, SceSysmoduleModuleId module_id);

View File

@ -22,27 +22,31 @@
#include <kernel/load_self.h>
#include <kernel/state.h>
// Current modules works for loading
static constexpr auto auto_lle_modules = {
SCE_SYSMODULE_SAS,
SCE_SYSMODULE_PGF,
SCE_SYSMODULE_SYSTEM_GESTURE,
SCE_SYSMODULE_XML,
SCE_SYSMODULE_MP4,
SCE_SYSMODULE_ATRAC,
SCE_SYSMODULE_AVPLAYER,
SCE_SYSMODULE_JSON,
SCE_SYSMODULE_HTTP,
SCE_SYSMODULE_SSL,
SCE_SYSMODULE_HTTPS,
SCE_SYSMODULE_SMART,
SCE_SYSMODULE_FACE,
SCE_SYSMODULE_ULT,
SCE_SYSMODULE_FIOS2
};
bool is_lle_module(SceSysmoduleModuleId module_id, EmuEnvState &emuenv) {
const auto &paths = sysmodule_paths[module_id];
// Do we know the module and its dependencies' paths?
const bool have_paths = !paths.empty();
// Current modules works for loading
const auto auto_lle_modules = {
SCE_SYSMODULE_SAS,
SCE_SYSMODULE_PGF,
SCE_SYSMODULE_SYSTEM_GESTURE,
SCE_SYSMODULE_XML,
SCE_SYSMODULE_MP4,
SCE_SYSMODULE_ATRAC,
SCE_SYSMODULE_AVPLAYER,
SCE_SYSMODULE_JSON,
SCE_SYSMODULE_HTTP,
SCE_SYSMODULE_SSL,
SCE_SYSMODULE_HTTPS,
};
if (have_paths) {
if (emuenv.cfg.current_config.modules_mode != ModulesMode::MANUAL) {
if (std::find(auto_lle_modules.begin(), auto_lle_modules.end(), module_id) != auto_lle_modules.end())
@ -60,6 +64,31 @@ bool is_lle_module(SceSysmoduleModuleId module_id, EmuEnvState &emuenv) {
return false;
}
std::vector<std::string> init_auto_lle_module_names() {
std::vector<std::string> auto_lle_module_names = { "libc", "libSceFt2", "libpvf" };
for (const auto module_id : auto_lle_modules) {
for (const auto module : sysmodule_paths[module_id]) {
auto_lle_module_names.emplace_back(module);
}
}
return auto_lle_module_names;
}
bool is_lle_module(const std::string &module_name, EmuEnvState &emuenv) {
static std::vector<std::string> auto_lle_module_names{};
if (auto_lle_module_names.empty())
auto_lle_module_names = init_auto_lle_module_names();
if (emuenv.cfg.current_config.modules_mode != ModulesMode::AUTOMATIC) {
if (std::find(emuenv.cfg.current_config.lle_modules.begin(), emuenv.cfg.current_config.lle_modules.end(), module_name) != emuenv.cfg.current_config.lle_modules.end())
return true;
}
if (emuenv.cfg.current_config.modules_mode != ModulesMode::MANUAL) {
if (std::find(auto_lle_module_names.begin(), auto_lle_module_names.end(), module_name) != auto_lle_module_names.end())
return true;
}
return false;
}
bool is_module_loaded(KernelState &kernel, SceSysmoduleModuleId module_id) {
return std::find(kernel.loaded_sysmodules.begin(), kernel.loaded_sysmodules.end(), module_id) != kernel.loaded_sysmodules.end();
}

View File

@ -26,71 +26,6 @@
#include <util/tracy.h>
TRACY_MODULE_NAME(SceModulemgr);
/**
* \brief Loads a dynamic module into memory if it wasn't already loaded. If it was, find it and return it. First 3 arguments are outputs.
* \param mod_id UID of the loaded module object
* \param entry_point Entry point (module_start) of the loaded module
* \param module Module info
* \param emuenv PlayStation Vita emulated environment
* \param export_name
* \param path File name of module file
* \param error_val Error value on failure
* \return True on success, false on failure
*/
static bool load_module(SceUID &mod_id, Ptr<const void> &entry_point, SceKernelModuleInfoPtr &module, EmuEnvState &emuenv, const char *export_name, const char *path, int &error_val) {
const auto &loaded_modules = emuenv.kernel.loaded_modules;
auto module_iter = std::find_if(loaded_modules.begin(), loaded_modules.end(), [path](const auto &p) {
return std::string(p.second->path) == path;
});
if (module_iter == loaded_modules.end()) {
// module is not loaded, load it here
const auto file = open_file(emuenv.io, path, SCE_O_RDONLY, emuenv.pref_path, export_name);
if (file < 0) {
error_val = RET_ERROR(file);
return false;
}
const auto size = seek_file(file, 0, SCE_SEEK_END, emuenv.io, export_name);
if (size < 0) {
error_val = RET_ERROR(SCE_ERROR_ERRNO_EINVAL);
return false;
}
if (seek_file(file, 0, SCE_SEEK_SET, emuenv.io, export_name) < 0) {
error_val = RET_ERROR(static_cast<int>(size));
return false;
}
std::vector<char> data(static_cast<int>(size) + 1); // null-terminated char array
if (read_file(data.data(), emuenv.io, file, SceSize(size), export_name) < 0) {
data.clear();
error_val = RET_ERROR(static_cast<int>(size));
return false;
}
mod_id = load_self(entry_point, emuenv.kernel, emuenv.mem, data.data(), path);
close_file(emuenv.io, file, export_name);
data.clear();
if (mod_id < 0) {
error_val = RET_ERROR(mod_id);
return false;
}
module_iter = loaded_modules.find(mod_id);
module = module_iter->second;
} else {
// module is already loaded
module = module_iter->second;
mod_id = module_iter->first;
entry_point = module->start_entry;
}
return true;
}
EXPORT(int, _sceKernelCloseModule) {
TRACY_FUNC(_sceKernelCloseModule);
return UNIMPLEMENTED();
@ -98,25 +33,16 @@ EXPORT(int, _sceKernelCloseModule) {
EXPORT(SceUID, _sceKernelLoadModule, char *path, int flags, SceKernelLMOption *option) {
TRACY_FUNC(_sceKernelLoadModule, path, flags, option);
SceUID mod_id;
Ptr<const void> entry_point;
SceKernelModuleInfoPtr module;
int error_val;
if (!load_module(mod_id, entry_point, module, emuenv, export_name, path, error_val))
return error_val;
return mod_id;
return load_module(emuenv, path);
}
static SceUID start_module(KernelState &kernel, SceUID thread_id, const SceKernelModuleInfoPtr &module, SceSize args, const Ptr<void> argp, int *pRes) {
const auto thread = kernel.get_thread(thread_id);
uint32_t result = 0;
if (module->start_entry)
result = thread->run_callback(module->start_entry.address(), { args, argp.address() });
LOG_INFO("Module {} (at \"{}\") module_start returned {}", module->module_name, module->path, log_hex(result));
static SceUID start_module(EmuEnvState &emuenv, SceUID module_id, SceSize args, const Ptr<void> argp, int *pRes) {
const SceKernelModuleInfoPtr module = lock_and_find(module_id, emuenv.kernel.loaded_modules, emuenv.kernel.mutex);
if (!module) {
const char *export_name = __FUNCTION__;
return RET_ERROR(SCE_KERNEL_ERROR_MODULEMGR_NO_MOD);
}
auto result = start_module(emuenv, module, args, argp);
if (pRes)
*pRes = result;
@ -131,15 +57,10 @@ EXPORT(SceUID, _sceKernelLoadStartModule, const char *moduleFileName, SceSize ar
return SCE_KERNEL_ERROR_MODULEMGR_INVALID_TYPE;
}
SceUID mod_id;
Ptr<const void> entry_point;
SceKernelModuleInfoPtr module;
int error_val;
if (!load_module(mod_id, entry_point, module, emuenv, export_name, moduleFileName, error_val))
return error_val;
return start_module(emuenv.kernel, thread_id, module, args, argp, pRes);
SceUID module_id = load_module(emuenv, moduleFileName);
if (module_id < 0)
return module_id;
return start_module(emuenv, module_id, args, argp, pRes);
}
EXPORT(int, _sceKernelOpenModule) {
@ -149,9 +70,8 @@ EXPORT(int, _sceKernelOpenModule) {
EXPORT(int, _sceKernelStartModule, SceUID uid, SceSize args, const Ptr<void> argp, SceUInt32 flags, const Ptr<SceKernelStartModuleOpt> pOpt, int *pRes) {
TRACY_FUNC(_sceKernelStartModule, uid, args, argp, flags, pOpt, pRes);
const SceKernelModuleInfoPtr module = lock_and_find(uid, emuenv.kernel.loaded_modules, emuenv.kernel.mutex);
return start_module(emuenv.kernel, thread_id, module, args, argp, pRes);
return start_module(emuenv, uid, args, argp, pRes);
}
EXPORT(int, _sceKernelStopModule) {
@ -215,11 +135,11 @@ EXPORT(int, sceKernelGetModuleList, int flags, SceUID *modids, int *num) {
// for Maidump main module should be the last module
int i = 0;
SceUID main_module_id = 0;
for (SceKernelModuleInfoPtrs::iterator module = emuenv.kernel.loaded_modules.begin(); module != emuenv.kernel.loaded_modules.end(); ++module) {
if (module->second->path == "app0:" + emuenv.self_path) {
main_module_id = module->first;
for (auto [module_id, module] : emuenv.kernel.loaded_modules) {
if (module->path == "app0:" + emuenv.self_path) {
main_module_id = module_id;
} else {
modids[i] = module->first;
modids[i] = module_id;
i++;
}
}

View File

@ -185,9 +185,9 @@ EXPORT(int, sceSysmoduleLoadModule, SceSysmoduleModuleId module_id) {
TRACY_FUNC(sceSysmoduleLoadModule, module_id);
if (module_id < 0 || module_id > SYSMODULE_COUNT)
return RET_ERROR(SCE_SYSMODULE_ERROR_INVALID_VALUE);
LOG_INFO("Loading module ID: {}", to_debug_str(emuenv.mem, module_id));
if (is_modules_enable(emuenv, module_id)) {
if (load_module(emuenv, thread_id, module_id))
if (load_sys_module(emuenv, module_id))
return SCE_SYSMODULE_LOADED;
else
return RET_ERROR(SCE_SYSMODULE_ERROR_FATAL);

View File

@ -27,7 +27,25 @@ struct KernelState;
void init_libraries(EmuEnvState &emuenv);
void call_import(EmuEnvState &emuenv, CPUState &cpu, uint32_t nid, SceUID thread_id);
bool load_module(EmuEnvState &emuenv, SceUID thread_id, SceSysmoduleModuleId module_id);
/**
* \brief Loads a dynamic module into memory if it wasn't already loaded. If it was, find it and return it.
* \param emuenv PlayStation Vita emulated environment
* \param module_path Full path of module file (with device)
* \return UID of the loaded module object or SCE_ERROR on failure
*/
SceUID load_module(EmuEnvState &emuenv, const std::string &module_path);
uint32_t start_module(EmuEnvState &emuenv, const std::shared_ptr<SceKernelModuleInfo> &module, SceSize args = 0, const Ptr<void> argp = Ptr<void>{});
/**
* \brief Loads and run a system module
* \param emuenv PlayStation Vita emulated environment
* \param module_id System Module ID of the module to load
* \return False on failure, true on success
*/
bool load_sys_module(EmuEnvState &emuenv, SceSysmoduleModuleId module_id);
Address resolve_export(KernelState &kernel, uint32_t nid);
uint32_t resolve_nid(KernelState &kernel, Address addr);

View File

@ -15,11 +15,15 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include "io/functions.h"
#include "io/io.h"
#include <modules/module_parent.h>
#include <cpu/functions.h>
#include <emuenv/state.h>
#include <io/device.h>
#include <io/state.h>
#include <io/vfs.h>
#include <kernel/load_self.h>
#include <kernel/state.h>
@ -151,43 +155,86 @@ void call_import(EmuEnvState &emuenv, CPUState &cpu, uint32_t nid, SceUID thread
}
}
SceUID load_module(EmuEnvState &emuenv, const std::string &module_path) {
// Check if module is already loaded
const auto &loaded_modules = emuenv.kernel.loaded_modules;
auto module_iter = std::find_if(loaded_modules.begin(), loaded_modules.end(), [&](const auto &p) {
return std::string(p.second->path) == module_path;
});
if (module_iter != loaded_modules.end()) {
return module_iter->first;
}
LOG_INFO("Loading module \"{}\"", module_path);
vfs::FileBuffer module_buffer;
bool res;
VitaIoDevice device = device::get_device(module_path);
auto translated_module_path = translate_path(module_path.c_str(), device, emuenv.io.device_paths);
if (device == VitaIoDevice::app0)
res = vfs::read_app_file(module_buffer, emuenv.pref_path, emuenv.io.app_path, translated_module_path);
else
res = vfs::read_file(device, module_buffer, emuenv.pref_path, translated_module_path);
if (!res) {
LOG_ERROR("Failed to read module file {}", module_path);
return SCE_ERROR_ERRNO_ENOENT;
}
SceUID module_id = load_self(emuenv.kernel, emuenv.mem, module_buffer.data(), module_path);
if (module_id >= 0) {
const auto module = emuenv.kernel.loaded_modules[module_id];
LOG_INFO("Module {} (at \"{}\") loaded", module->module_name, module_path);
} else {
LOG_ERROR("Failed to load module {}", module_path);
}
return module_id;
}
uint32_t start_module(EmuEnvState &emuenv, const std::shared_ptr<SceKernelModuleInfo> &module, SceSize args, const Ptr<void> argp) {
const auto module_start = module->start_entry;
if (module_start) {
const auto module_name = module->module_name;
LOG_DEBUG("Running module_start of library: {} at address {}", module_name, log_hex(module_start.address()));
SceInt32 priority = SCE_KERNEL_DEFAULT_PRIORITY_USER;
SceInt32 stack_size = SCE_KERNEL_STACK_SIZE_USER_MAIN;
SceInt32 affinity = SCE_KERNEL_THREAD_CPU_AFFINITY_MASK_DEFAULT;
// module_start is always called from new thread
const ThreadStatePtr module_thread = emuenv.kernel.create_thread(emuenv.mem, module_name, module_start, priority, affinity, stack_size, nullptr);
const auto ret = module_thread->run_guest_function(emuenv.kernel, module_start.address(), args, argp);
module_thread->exit_delete(emuenv.kernel);
LOG_INFO("Module {} (at \"{}\") module_start returned {}", module_name, module->path, log_hex(ret));
return ret;
}
return 0;
}
/**
* \return False on failure, true on success
*/
bool load_module(EmuEnvState &emuenv, SceUID thread_id, SceSysmoduleModuleId module_id) {
LOG_INFO("Loading module ID: {}", log_hex(module_id));
const auto module_paths = sysmodule_paths[module_id];
for (std::string module_path : module_paths) {
module_path = "sys/external/" + module_path + ".suprx";
vfs::FileBuffer module_buffer;
Ptr<const void> lib_entry_point;
if (vfs::read_file(VitaIoDevice::vs0, module_buffer, emuenv.pref_path, module_path)) {
SceUID loaded_module_uid = load_self(lib_entry_point, emuenv.kernel, emuenv.mem, module_buffer.data(), module_path);
if (loaded_module_uid < 0) {
LOG_ERROR("Error when loading module at \"{}\"", module_path);
return false;
}
const auto module = emuenv.kernel.loaded_modules[loaded_module_uid];
const auto module_name = module->module_name;
LOG_INFO("Module {} (at \"{}\") loaded", module_name, module_path);
if (lib_entry_point) {
LOG_DEBUG("Running module_start of module: {}", module_name);
Ptr<void> argp = Ptr<void>();
const auto thread = emuenv.kernel.get_thread(thread_id);
const auto ret = thread->run_callback(lib_entry_point.address(), { 0, argp.address() });
LOG_INFO("Module {} (at \"{}\") module_start returned {}", module_name, module->path, log_hex(ret));
}
bool load_sys_module(EmuEnvState &emuenv, SceSysmoduleModuleId module_id) {
const auto &module_paths = sysmodule_paths[module_id];
for (const auto module_filename : module_paths) {
std::string module_path;
if (module_id == SCE_SYSMODULE_SMART || module_id == SCE_SYSMODULE_FACE || module_id == SCE_SYSMODULE_ULT) {
module_path = fmt::format("app0:sce_module/{}.suprx", module_filename);
} else {
LOG_ERROR("Module at \"{}\" not present", module_path);
// ignore and assume it was loaded
module_path = fmt::format("vs0:sys/external/{}.suprx", module_filename);
}
auto loaded_module_uid = load_module(emuenv, module_path);
if (loaded_module_uid < 0) {
if (module_id == SCE_SYSMODULE_ULT && loaded_module_uid == SCE_ERROR_ERRNO_ENOENT) {
module_path = fmt::format("vs0:sys/external/{}.suprx", module_filename);
loaded_module_uid = load_module(emuenv, module_path);
if (loaded_module_uid < 0)
return false;
} else
return false;
}
const auto module = emuenv.kernel.loaded_modules[loaded_module_uid];
start_module(emuenv, module);
}
emuenv.kernel.loaded_sysmodules.push_back(module_id);