/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/kernel/xboxkrnl/xboxkrnl_modules.h" #include "xenia/base/logging.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/xbox.h" namespace xe { namespace kernel { namespace xboxkrnl { dword_result_t XexCheckExecutablePrivilege_entry(dword_t privilege) { // BOOL // DWORD Privilege // Privilege is bit position in xe_xex2_system_flags enum - so: // Privilege=6 -> 0x00000040 -> XEX_SYSTEM_INSECURE_SOCKETS uint32_t mask = 1 << privilege; auto module = kernel_state()->GetExecutableModule(); if (!module) { return 0; } uint32_t flags = 0; module->GetOptHeader(XEX_HEADER_SYSTEM_FLAGS, &flags); return (flags & mask) > 0; } DECLARE_XBOXKRNL_EXPORT1(XexCheckExecutablePrivilege, kModules, kImplemented); dword_result_t XexGetModuleHandle(std::string module_name, xe::be* hmodule_ptr) { object_ref module; if (module_name.empty()) { module = kernel_state()->GetExecutableModule(); } else { module = kernel_state()->GetModule(module_name); } if (!module) { *hmodule_ptr = 0; return X_ERROR_NOT_FOUND; } // NOTE: we don't retain the handle for return. *hmodule_ptr = module->hmodule_ptr(); return X_ERROR_SUCCESS; } dword_result_t XexGetModuleHandle_entry(lpstring_t module_name, lpdword_t hmodule_ptr) { return XexGetModuleHandle(module_name ? module_name.value() : "", hmodule_ptr); } DECLARE_XBOXKRNL_EXPORT1(XexGetModuleHandle, kModules, kImplemented); dword_result_t XexGetModuleSection_entry(lpvoid_t hmodule, lpstring_t name, lpdword_t data_ptr, lpdword_t size_ptr) { X_STATUS result = X_STATUS_SUCCESS; auto module = XModule::GetFromHModule(kernel_state(), hmodule); if (module) { uint32_t section_data = 0; uint32_t section_size = 0; result = module->GetSection(name.value(), §ion_data, §ion_size); if (XSUCCEEDED(result)) { *data_ptr = section_data; *size_ptr = section_size; } } else { result = X_STATUS_INVALID_HANDLE; } return result; } DECLARE_XBOXKRNL_EXPORT1(XexGetModuleSection, kModules, kImplemented); dword_result_t xeXexLoadImage( lpstring_t module_name, dword_t module_flags, dword_t min_version, lpdword_t hmodule_ptr, const std::function()>& load_callback, bool isFromMemory) { X_STATUS result = X_STATUS_NO_SUCH_FILE; if (!hmodule_ptr) { return X_ERROR_INVALID_PARAMETER; } uint32_t hmodule = 0; auto module = kernel_state()->GetModule(module_name.value()); if (module) { if (isFromMemory) { // Existing module found; return error status. *hmodule_ptr = hmodule; return X_STATUS_OBJECT_NAME_COLLISION; } else { // Existing module found. hmodule = module->hmodule_ptr(); result = X_STATUS_SUCCESS; } } else { // Not found; attempt to load as a user module. auto user_module = load_callback(); if (user_module) { kernel_state()->ApplyTitleUpdate(user_module); kernel_state()->FinishLoadingUserModule(user_module); // Give up object ownership, this reference will be released by the last // XexUnloadImage call auto user_module_raw = user_module.release(); hmodule = user_module_raw->hmodule_ptr(); result = X_STATUS_SUCCESS; } } // Increment the module's load count. if (hmodule) { auto ldr_data = kernel_memory()->TranslateVirtual(hmodule); ldr_data->load_count++; } *hmodule_ptr = hmodule; return result; } dword_result_t XexLoadImage_entry(lpstring_t module_name, dword_t module_flags, dword_t min_version, lpdword_t hmodule_ptr) { auto load_callback = [module_name] { return kernel_state()->LoadUserModule(module_name.value()); }; return xeXexLoadImage(module_name, module_flags, min_version, hmodule_ptr, load_callback, false); } DECLARE_XBOXKRNL_EXPORT1(XexLoadImage, kModules, kImplemented); dword_result_t XexLoadImageFromMemory_entry(lpdword_t buffer, dword_t size, lpstring_t module_name, dword_t module_flags, dword_t min_version, lpdword_t hmodule_ptr) { auto load_callback = [module_name, buffer, size] { return kernel_state()->LoadUserModuleFromMemory(module_name.value(), buffer, size); }; return xeXexLoadImage(module_name, module_flags, min_version, hmodule_ptr, load_callback, true); } DECLARE_XBOXKRNL_EXPORT1(XexLoadImageFromMemory, kModules, kImplemented); dword_result_t XexLoadExecutable_entry(lpstring_t module_name, dword_t module_flags, dword_t min_version, lpdword_t hmodule_ptr) { return XexLoadImage_entry(module_name, module_flags, min_version, hmodule_ptr); } DECLARE_XBOXKRNL_EXPORT1(XexLoadExecutable, kModules, kSketchy); dword_result_t XexUnloadImage_entry(lpvoid_t hmodule) { auto module = XModule::GetFromHModule(kernel_state(), hmodule); if (!module) { return X_STATUS_INVALID_HANDLE; } // Can't unload kernel modules from user code. if (module->module_type() != XModule::ModuleType::kKernelModule) { auto ldr_data = hmodule.as(); if (--ldr_data->load_count == 0) { // No more references, free it. module->Release(); kernel_state()->UnloadUserModule(object_ref( reinterpret_cast(module.release()))); } } return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XexUnloadImage, kModules, kImplemented); dword_result_t XexGetProcedureAddress_entry(lpvoid_t hmodule, dword_t ordinal, lpdword_t out_function_ptr) { // May be entry point? assert_not_zero(ordinal); bool is_string_name = (ordinal & 0xFFFF0000) != 0; auto string_name = reinterpret_cast(kernel_memory()->TranslateVirtual(ordinal)); X_STATUS result = X_STATUS_INVALID_HANDLE; object_ref module; if (!hmodule) { module = kernel_state()->GetExecutableModule(); } else { module = XModule::GetFromHModule(kernel_state(), hmodule); } if (module) { uint32_t ptr; if (is_string_name) { ptr = module->GetProcAddressByName(string_name); } else { ptr = module->GetProcAddressByOrdinal(ordinal); } if (ptr) { *out_function_ptr = ptr; result = X_STATUS_SUCCESS; } else { if (is_string_name) { XELOGW("ERROR: XexGetProcedureAddress export '{}' in '{}' not found!", string_name, module->name()); } else { XELOGW( "ERROR: XexGetProcedureAddress ordinal {} (0x{:X}) in '{}' not " "found!", static_cast(ordinal), static_cast(ordinal), module->name()); } *out_function_ptr = 0; result = X_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND; } } return result; } DECLARE_XBOXKRNL_EXPORT1(XexGetProcedureAddress, kModules, kImplemented); void ExRegisterTitleTerminateNotification_entry( pointer_t reg, dword_t create) { if (create) { // Adding. kernel_state()->RegisterTitleTerminateNotification( reg->notification_routine, reg->priority); } else { // Removing. kernel_state()->RemoveTitleTerminateNotification(reg->notification_routine); } } DECLARE_XBOXKRNL_EXPORT1(ExRegisterTitleTerminateNotification, kModules, kImplemented); // todo: replace magic numbers dword_result_t XexLoadImageHeaders_entry(pointer_t path, pointer_t header, dword_t buffer_size, const ppc_context_t& ctx) { if (buffer_size < 0x800) { return X_STATUS_BUFFER_TOO_SMALL; } auto current_kernel = ctx->kernel_state; auto target_path = util::TranslateAnsiPath(current_kernel->memory(), path); vfs::File* vfs_file = nullptr; vfs::FileAction file_action; X_STATUS result = current_kernel->file_system()->OpenFile( nullptr, target_path, vfs::FileDisposition::kOpen, vfs::FileAccess::kGenericRead, false, true, &vfs_file, &file_action); if (!vfs_file) { return result; } size_t bytes_read = 0; X_STATUS result_status = vfs_file->ReadSync( std::span(reinterpret_cast(header.host_address()), 2048), 0, &bytes_read); if (result_status < 0) { vfs_file->Destroy(); return result_status; } if (header->magic != 'XEX2') { vfs_file->Destroy(); return X_STATUS_INVALID_IMAGE_FORMAT; } unsigned int header_size = header->header_size; if (header_size < 0x800 || header_size > 0x10000 || (header_size & 0x7FF) != 0) { result_status = X_STATUS_INVALID_IMAGE_FORMAT; } else if (header_size <= buffer_size) { if (header_size <= 0x800) { result_status = X_STATUS_SUCCESS; } else { result_status = vfs_file->ReadSync( std::span( reinterpret_cast(header.host_address() + 2048), header_size - 2048), 2048, &bytes_read); if (result_status >= X_STATUS_SUCCESS) { result_status = X_STATUS_SUCCESS; } } } else { result_status = X_STATUS_BUFFER_TOO_SMALL; } vfs_file->Destroy(); return result_status; } DECLARE_XBOXKRNL_EXPORT1(XexLoadImageHeaders, kModules, kImplemented); } // namespace xboxkrnl } // namespace kernel } // namespace xe DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Module);