diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index af6f6e588..d73553a3f 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -43,7 +43,8 @@ XexModule::XexModule(Processor* processor, KernelState* kernel_state) xex_(nullptr), base_address_(0), low_address_(0), - high_address_(0) {} + high_address_(0), + loaded_(false) {} XexModule::~XexModule() { xe_xex2_dealloc(xex_); } @@ -80,7 +81,7 @@ bool XexModule::GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, } bool XexModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const { - return XexModule::GetOptHeader(xex_header_, key, out_ptr); + return XexModule::GetOptHeader(xex_header(), key, out_ptr); } const xex2_security_info* XexModule::GetSecurityInfo( @@ -198,17 +199,20 @@ bool XexModule::Load(const std::string& name, const std::string& path, // Make a copy of the xex header. auto src_header = reinterpret_cast(xex_addr); - xex_header_ = (xex2_header*)new char[src_header->header_size]; + xex_header_mem_.resize(src_header->header_size); - std::memcpy(xex_header_, src_header, src_header->header_size); + std::memcpy(xex_header_mem_.data(), src_header, src_header->header_size); return Load(name, path, xex_); } bool XexModule::Load(const std::string& name, const std::string& path, xe_xex2_ref xex) { + assert_false(loaded_); + loaded_ = true; xex_ = xex; - auto header = xex_header_; + + auto header = xex_header(); auto old_header = xe_xex2_get_header(xex_); // Setup debug info. @@ -287,6 +291,23 @@ bool XexModule::Load(const std::string& name, const std::string& path, return true; } +bool XexModule::Unload() { + if (!loaded_) { + return true; + } + loaded_ = false; + + // Just deallocate the memory occupied by the exe + xe::be* exe_address = 0; + GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address); + assert_not_zero(exe_address); + + memory()->LookupHeap(*exe_address)->Release(*exe_address); + xex_header_mem_.resize(0); + + return true; +} + bool XexModule::SetupLibraryImports(const char* name, const xex2_import_library* library) { ExportResolver* kernel_resolver = nullptr; diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index f4723a6cb..b4fc3c4db 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -33,9 +33,12 @@ class XexModule : public xe::cpu::Module { virtual ~XexModule(); xe_xex2_ref xex() const { return xex_; } - const xex2_header* xex_header() const { return xex_header_; } + bool loaded() const { return loaded_; } + const xex2_header* xex_header() const { + return reinterpret_cast(xex_header_mem_.data()); + } const xex2_security_info* xex_security_info() const { - return GetSecurityInfo(xex_header_); + return GetSecurityInfo(xex_header()); } // Gets an optional header. Returns NULL if not found. @@ -68,6 +71,7 @@ class XexModule : public xe::cpu::Module { bool Load(const std::string& name, const std::string& path, const void* xex_addr, size_t xex_length); bool Load(const std::string& name, const std::string& path, xe_xex2_ref xex); + bool Unload(); const std::string& name() const override { return name_; } @@ -84,7 +88,8 @@ class XexModule : public xe::cpu::Module { std::string name_; std::string path_; xe_xex2_ref xex_; - xex2_header* xex_header_; + std::vector xex_header_mem_; // Holds the xex header + bool loaded_; // Loaded into memory? uint32_t base_address_; uint32_t low_address_; diff --git a/src/xenia/debug/debugger.cc b/src/xenia/debug/debugger.cc index e8b887e47..44c033337 100644 --- a/src/xenia/debug/debugger.cc +++ b/src/xenia/debug/debugger.cc @@ -58,7 +58,8 @@ Breakpoint::~Breakpoint() = default; Debugger::Debugger(Emulator* emulator) : emulator_(emulator), listen_socket_(INVALID_SOCKET), - client_socket_(INVALID_SOCKET) { + client_socket_(INVALID_SOCKET), + accept_thread_running_(false) { WSADATA wsa_data; WSAStartup(MAKEWORD(2, 2), &wsa_data); } @@ -134,67 +135,70 @@ void SendResponse(SOCKET client_socket, flatbuffers::FlatBufferBuilder& fbb, } void Debugger::PreLaunch() { - accept_thread_ = std::thread([this]() { - xe::threading::set_name("Debugger Server"); + if (!accept_thread_running_) { + accept_thread_running_ = true; + accept_thread_ = std::thread([this]() { + xe::threading::set_name("Debugger Server"); - while (listen_socket_ != INVALID_SOCKET) { - sockaddr_in6 client_addr; - int client_count = sizeof(client_addr); - SOCKET client_socket_id = - accept(listen_socket_, reinterpret_cast(&client_addr), - &client_count); - if (client_socket_id == INVALID_SOCKET) { - XELOGE("Failed to accept socket"); - continue; - } - - // Only one debugger at a time. - if (client_socket_ != INVALID_SOCKET) { - XELOGW("Ignoring debugger connection as one is already connected"); - closesocket(client_socket_id); - continue; - } - - // Setup recv thread. - client_socket_ = client_socket_id; - receive_thread_ = std::thread([this]() { - xe::threading::set_name("Debugger Connection"); - - while (client_socket_ != INVALID_SOCKET) { - // Read length prefix. - uint32_t length = 0; - int r = recv(client_socket_, reinterpret_cast(&length), 4, - MSG_WAITALL); - if (r != 4) { - // Failed? - XELOGE("Failed to recv debug data length - dead connection?"); - if (FLAGS_exit_with_debugger) { - exit(1); - } - break; - } - - // Read body. - std::vector body(length); - r = recv(client_socket_, reinterpret_cast(body.data()), length, - MSG_WAITALL); - if (r != length) { - // Failed? - XELOGE("Failed to recv debug data body - dead connection?"); - if (FLAGS_exit_with_debugger) { - exit(1); - } - break; - } - - // Read message contents and dispatch. - OnMessage(std::move(body)); + while (listen_socket_ != INVALID_SOCKET) { + sockaddr_in6 client_addr; + int client_count = sizeof(client_addr); + SOCKET client_socket_id = + accept(listen_socket_, reinterpret_cast(&client_addr), + &client_count); + if (client_socket_id == INVALID_SOCKET) { + XELOGE("Failed to accept socket"); + continue; } - }); - // This will WaitForClient if it was waiting. - } - }); + // Only one debugger at a time. + if (client_socket_ != INVALID_SOCKET) { + XELOGW("Ignoring debugger connection as one is already connected"); + closesocket(client_socket_id); + continue; + } + + // Setup recv thread. + client_socket_ = client_socket_id; + receive_thread_ = std::thread([this]() { + xe::threading::set_name("Debugger Connection"); + + while (client_socket_ != INVALID_SOCKET) { + // Read length prefix. + uint32_t length = 0; + int r = recv(client_socket_, reinterpret_cast(&length), 4, + MSG_WAITALL); + if (r != 4) { + // Failed? + XELOGE("Failed to recv debug data length - dead connection?"); + if (FLAGS_exit_with_debugger) { + exit(1); + } + break; + } + + // Read body. + std::vector body(length); + r = recv(client_socket_, reinterpret_cast(body.data()), + length, MSG_WAITALL); + if (r != length) { + // Failed? + XELOGE("Failed to recv debug data body - dead connection?"); + if (FLAGS_exit_with_debugger) { + exit(1); + } + break; + } + + // Read message contents and dispatch. + OnMessage(std::move(body)); + } + }); + + // This will WaitForClient if it was waiting. + } + }); + } if (FLAGS_wait_for_debugger) { // Wait for the first client. diff --git a/src/xenia/debug/debugger.h b/src/xenia/debug/debugger.h index 9bb446679..08b3d4266 100644 --- a/src/xenia/debug/debugger.h +++ b/src/xenia/debug/debugger.h @@ -111,6 +111,7 @@ class Debugger { Emulator* emulator_; uintptr_t listen_socket_; + bool accept_thread_running_; std::thread accept_thread_; xe::threading::Fence accept_fence_; uintptr_t client_socket_; diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 93fdd4ee8..2ce75b66e 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -274,10 +274,25 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path) { X_STATUS Emulator::CompleteLaunch(const std::wstring& path, const std::string& module_path) { - auto xboxkrnl_module = kernel_state_->GetModule("xboxkrnl.exe"); - auto xboxkrnl = kernel::object_ref( - reinterpret_cast(xboxkrnl_module.release())); - int result = xboxkrnl->LaunchModule(module_path.c_str()); + // Allow xam to request module loads. + auto xam = kernel_state()->GetKernelModule("xam.xex"); + auto xboxkrnl = + kernel_state()->GetKernelModule("xboxkrnl.exe"); + + int result = 0; + auto next_module = module_path; + while (next_module != "") { + XELOGI("Launching module %s", next_module.c_str()); + result = xboxkrnl->LaunchModule(next_module.c_str()); + + // Check xam and see if they want us to load another module. + auto& loader_data = xam->loader_data(); + next_module = loader_data.launch_path; + + // And blank out the launch path to avoid an infinite loop. + loader_data.launch_path = ""; + } + if (result == 0) { return X_STATUS_SUCCESS; } else { diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 5c1f32ea8..b90571b06 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -161,6 +161,18 @@ bool KernelState::IsKernelModule(const char* name) { return false; } +object_ref KernelState::GetKernelModule(const char* name) { + assert_true(IsKernelModule(name)); + + for (auto kernel_module : kernel_modules_) { + if (kernel_module->Matches(name)) { + return retain_object(kernel_module.get()); + } + } + + return nullptr; +} + object_ref KernelState::GetModule(const char* name) { if (!name) { // NULL name = self. @@ -223,26 +235,27 @@ void KernelState::SetExecutableModule(object_ref module) { // Spin up deferred dispatch worker. // TODO(benvanik): move someplace more appropriate (out of ctor, but around // here). - assert_false(dispatch_thread_running_); - dispatch_thread_running_ = true; - dispatch_thread_ = - object_ref(new XHostThread(this, 128 * 1024, 0, [this]() { - while (dispatch_thread_running_) { - std::unique_lock lock(dispatch_mutex_); - if (dispatch_queue_.empty()) { - dispatch_cond_.wait(lock); - if (!dispatch_thread_running_) { - break; + if (!dispatch_thread_running_) { + dispatch_thread_running_ = true; + dispatch_thread_ = + object_ref(new XHostThread(this, 128 * 1024, 0, [this]() { + while (dispatch_thread_running_) { + std::unique_lock lock(dispatch_mutex_); + if (dispatch_queue_.empty()) { + dispatch_cond_.wait(lock); + if (!dispatch_thread_running_) { + break; + } } + auto fn = std::move(dispatch_queue_.front()); + dispatch_queue_.pop_front(); + fn(); } - auto fn = std::move(dispatch_queue_.front()); - dispatch_queue_.pop_front(); - fn(); - } - return 0; - })); - dispatch_thread_->set_name("Kernel Dispatch Thread"); - dispatch_thread_->Create(); + return 0; + })); + dispatch_thread_->set_name("Kernel Dispatch Thread"); + dispatch_thread_->Create(); + } } void KernelState::LoadKernelModule(object_ref kernel_module) { @@ -255,6 +268,7 @@ object_ref KernelState::LoadUserModule(const char* raw_name) { std::string name = xe::find_name_from_path(raw_name); std::string path(raw_name); if (name == raw_name) { + assert_not_null(executable_module_); path = xe::join_paths(xe::find_base_path(executable_module_->path()), name); } @@ -284,7 +298,7 @@ object_ref KernelState::LoadUserModule(const char* raw_name) { module->Dump(); - if (module->entry_point()) { + if (module->dll_module() && module->entry_point()) { // Call DllMain(DLL_PROCESS_ATTACH): // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx uint64_t args[] = { @@ -300,6 +314,52 @@ object_ref KernelState::LoadUserModule(const char* raw_name) { return module; } +void KernelState::TerminateTitle(bool from_guest_thread) { + std::lock_guard lock(object_mutex_); + + // First: Kill all guest threads. + for (auto it = threads_by_id_.begin(); it != threads_by_id_.end();) { + if (it->second->guest_thread()) { + auto thread = it->second; + + if (from_guest_thread && XThread::IsInThread(thread)) { + // Don't terminate ourselves. + ++it; + continue; + } + + if (it->second->running()) { + thread->Terminate(0); + } + + // Erase it from the thread list. + it = threads_by_id_.erase(it); + } else { + ++it; + } + } + + // Second: Unload all user modules (including the executable) + for (int i = 0; i < user_modules_.size(); i++) { + X_STATUS status = user_modules_[i]->Unload(); + assert_true(XSUCCEEDED(status)); + + object_table_->RemoveHandle(user_modules_[i]->handle()); + } + user_modules_.clear(); + + if (from_guest_thread) { + threads_by_id_.erase(XThread::GetCurrentThread()->thread_id()); + + // Now commit suicide (using Terminate, because we can't call into guest + // code anymore) + // Also, manually invoke the lock guard's destructor, because Terminate + // does not return. + lock.~lock_guard(); + XThread::GetCurrentThread()->Terminate(0); + } +} + void KernelState::RegisterThread(XThread* thread) { std::lock_guard lock(object_mutex_); threads_by_id_[thread->thread_id()] = thread; @@ -331,7 +391,7 @@ void KernelState::OnThreadExecute(XThread* thread) { // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx auto thread_state = thread->thread_state(); for (auto user_module : user_modules_) { - if (user_module->entry_point()) { + if (user_module->dll_module() && user_module->entry_point()) { uint64_t args[] = { user_module->handle(), 2, // DLL_THREAD_ATTACH @@ -353,7 +413,7 @@ void KernelState::OnThreadExit(XThread* thread) { // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx auto thread_state = thread->thread_state(); for (auto user_module : user_modules_) { - if (user_module->entry_point()) { + if (user_module->dll_module() && user_module->entry_point()) { uint64_t args[] = { user_module->handle(), 3, // DLL_THREAD_DETACH diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index 8b448ea5f..28e074698 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -112,15 +112,26 @@ class KernelState { void UnregisterModule(XModule* module); bool IsKernelModule(const char* name); object_ref GetModule(const char* name); + object_ref GetExecutableModule(); void SetExecutableModule(object_ref module); + object_ref LoadUserModule(const char* name); + + object_ref GetKernelModule(const char* name); template object_ref LoadKernelModule() { auto kernel_module = object_ref(new T(emulator_, this)); LoadKernelModule(kernel_module); return kernel_module; } - object_ref LoadUserModule(const char* name); + template + object_ref GetKernelModule(const char* name) { + auto module = GetKernelModule(name); + return object_ref(reinterpret_cast(module.release())); + } + + // Terminates a title: Unloads all modules, and kills all guest threads. + void TerminateTitle(bool from_guest_thread = false); void RegisterThread(XThread* thread); void UnregisterThread(XThread* thread); diff --git a/src/xenia/kernel/objects/xmodule.cc b/src/xenia/kernel/objects/xmodule.cc index 28d5e1d3d..183fa86fe 100644 --- a/src/xenia/kernel/objects/xmodule.cc +++ b/src/xenia/kernel/objects/xmodule.cc @@ -67,6 +67,8 @@ bool XModule::Matches(const std::string& name) const { void XModule::OnLoad() { kernel_state_->RegisterModule(this); } +void XModule::OnUnload() { kernel_state_->UnregisterModule(this); } + X_STATUS XModule::GetSection(const char* name, uint32_t* out_section_data, uint32_t* out_section_size) { return X_STATUS_UNSUCCESSFUL; diff --git a/src/xenia/kernel/objects/xmodule.h b/src/xenia/kernel/objects/xmodule.h index d6951b63b..6573b9d1f 100644 --- a/src/xenia/kernel/objects/xmodule.h +++ b/src/xenia/kernel/objects/xmodule.h @@ -86,6 +86,7 @@ class XModule : public XObject { protected: void OnLoad(); + void OnUnload(); ModuleType module_type_; std::string name_; diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 494086b85..538f82324 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -41,7 +41,8 @@ xe::mutex critical_region_; XThread::XThread(KernelState* kernel_state, uint32_t stack_size, uint32_t xapi_thread_startup, uint32_t start_address, - uint32_t start_context, uint32_t creation_flags) + uint32_t start_context, uint32_t creation_flags, + bool guest_thread) : XObject(kernel_state, kTypeThread), thread_id_(++next_xthread_id), thread_handle_(0), @@ -50,7 +51,9 @@ XThread::XThread(KernelState* kernel_state, uint32_t stack_size, thread_state_(0), priority_(0), affinity_(0), - irql_(0) { + irql_(0), + guest_thread_(guest_thread), + running_(false) { creation_params_.stack_size = stack_size; creation_params_.xapi_thread_startup = xapi_thread_startup; creation_params_.start_address = start_address; @@ -67,9 +70,6 @@ XThread::XThread(KernelState* kernel_state, uint32_t stack_size, apc_list_ = new NativeList(kernel_state->memory()); - event_ = object_ref(new XEvent(kernel_state)); - event_->Initialize(true, false); - char thread_name[32]; snprintf(thread_name, xe::countof(thread_name), "XThread%04X", handle()); set_name(thread_name); @@ -84,8 +84,6 @@ XThread::~XThread() { delete apc_list_; - event_.reset(); - PlatformDestroy(); if (thread_state_) { @@ -335,12 +333,11 @@ X_STATUS XThread::Create() { } X_STATUS XThread::Exit(int exit_code) { + assert_true(XThread::GetCurrentThread() == this); + // TODO(benvanik): set exit code in thread state block // TODO(benvanik); dispatch events? waiters? etc? - if (event_) { - event_->Set(0, false); - } RundownAPCs(); kernel_state()->OnThreadExit(this); @@ -349,6 +346,7 @@ X_STATUS XThread::Exit(int exit_code) { current_thread_tls = nullptr; xe::Profiler::ThreadExit(); + running_ = false; Release(); X_STATUS return_code = PlatformExit(exit_code); if (XFAILED(return_code)) { @@ -357,6 +355,19 @@ X_STATUS XThread::Exit(int exit_code) { return X_STATUS_SUCCESS; } +X_STATUS XThread::Terminate(int exit_code) { + // TODO: Inform the profiler that this thread is exiting. + + running_ = false; + Release(); + X_STATUS status = PlatformTerminate(exit_code); + if (XFAILED(status)) { + return status; + } + + return X_STATUS_SUCCESS; +} + #if XE_PLATFORM_WIN32 static uint32_t __stdcall XThreadStartCallbackWin32(void* param) { @@ -404,6 +415,14 @@ X_STATUS XThread::PlatformExit(int exit_code) { return X_STATUS_SUCCESS; } +X_STATUS XThread::PlatformTerminate(int exit_code) { + if (!TerminateThread(thread_handle_, exit_code)) { + return X_STATUS_UNSUCCESSFUL; + } + + return X_STATUS_SUCCESS; +} + #else static void* XThreadStartCallbackPthreads(void* param) { @@ -463,12 +482,18 @@ X_STATUS XThread::PlatformExit(int exit_code) { return X_STATUS_SUCCESS; } +X_STATUS XThread::PlatformTerminate(int exit_code) { + // TODO! + assert_always(); +} + #endif // WIN32 void XThread::Execute() { XELOGKERNEL("XThread::Execute thid %d (handle=%.8X, '%s', native=%.8X)", thread_id_, handle(), name_.c_str(), xe::threading::current_thread_id()); + running_ = true; // Let the kernel know we are starting. kernel_state()->OnThreadExecute(this); @@ -498,6 +523,7 @@ void XThread::Execute() { // Treat the return code as an implicit exit code. } + running_ = false; Exit(exit_code); } @@ -754,11 +780,11 @@ X_STATUS XThread::Delay(uint32_t processor_mode, uint32_t alertable, } } -void* XThread::GetWaitHandle() { return event_->GetWaitHandle(); } +void* XThread::GetWaitHandle() { return thread_handle_; } XHostThread::XHostThread(KernelState* kernel_state, uint32_t stack_size, uint32_t creation_flags, std::function host_fn) - : XThread(kernel_state, stack_size, 0, 0, 0, creation_flags), + : XThread(kernel_state, stack_size, 0, 0, 0, creation_flags, false), host_fn_(host_fn) {} void XHostThread::Execute() { diff --git a/src/xenia/kernel/objects/xthread.h b/src/xenia/kernel/objects/xthread.h index 03516833f..8793381e7 100644 --- a/src/xenia/kernel/objects/xthread.h +++ b/src/xenia/kernel/objects/xthread.h @@ -81,7 +81,7 @@ class XThread : public XObject { public: XThread(KernelState* kernel_state, uint32_t stack_size, uint32_t xapi_thread_startup, uint32_t start_address, - uint32_t start_context, uint32_t creation_flags); + uint32_t start_context, uint32_t creation_flags, bool guest_thread); virtual ~XThread(); static bool IsInThread(XThread* other); @@ -92,6 +92,8 @@ class XThread : public XObject { uint32_t tls_ptr() const { return tls_address_; } uint32_t pcr_ptr() const { return pcr_address_; } uint32_t thread_state_ptr() const { return thread_state_address_; } + bool guest_thread() const { return guest_thread_; } + bool running() const { return running_; } cpu::ThreadState* thread_state() const { return thread_state_; } uint32_t thread_id() const { return thread_id_; } @@ -102,6 +104,7 @@ class XThread : public XObject { X_STATUS Create(); X_STATUS Exit(int exit_code); + X_STATUS Terminate(int exit_code); virtual void Execute(); @@ -136,6 +139,7 @@ class XThread : public XObject { X_STATUS PlatformCreate(); void PlatformDestroy(); X_STATUS PlatformExit(int exit_code); + X_STATUS PlatformTerminate(int exit_code); static void DeliverAPCs(void* data); void RundownAPCs(); @@ -156,6 +160,8 @@ class XThread : public XObject { uint32_t pcr_address_; uint32_t thread_state_address_; cpu::ThreadState* thread_state_; + bool guest_thread_; // Launched into guest code? + bool running_; std::string name_; @@ -165,8 +171,6 @@ class XThread : public XObject { std::atomic irql_; xe::mutex apc_lock_; NativeList* apc_list_; - - object_ref event_; }; class XHostThread : public XThread { diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index b0fd110b9..037611d37 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -24,7 +24,7 @@ using namespace xe::cpu; XUserModule::XUserModule(KernelState* kernel_state, const char* path) : XModule(kernel_state, ModuleType::kUserModule, path) {} -XUserModule::~XUserModule() {} +XUserModule::~XUserModule() { Unload(); } X_STATUS XUserModule::LoadFromFile(std::string path) { X_STATUS result = X_STATUS_UNSUCCESSFUL; @@ -104,12 +104,27 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { // Cache some commonly used headers... this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_); this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, &stack_size_); + dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE); OnLoad(); return X_STATUS_SUCCESS; } +X_STATUS XUserModule::Unload() { + if (!xex_module()->loaded()) { + // Quick abort. + return X_STATUS_SUCCESS; + } + + if (xex_module()->Unload()) { + OnUnload(); + return X_STATUS_SUCCESS; + } + + return X_STATUS_UNSUCCESSFUL; +} + uint32_t XUserModule::GetProcAddressByOrdinal(uint16_t ordinal) { return xex_module()->GetProcAddress(ordinal); } @@ -201,11 +216,10 @@ X_STATUS XUserModule::GetOptHeader(uint8_t* membase, const xex2_header* header, X_STATUS XUserModule::Launch(uint32_t flags) { XELOGI("Launching module..."); - Dump(); // Create a thread to run in. auto thread = object_ref( - new XThread(kernel_state(), stack_size_, 0, entry_point_, 0, 0)); + new XThread(kernel_state(), stack_size_, 0, entry_point_, 0, 0, true)); X_STATUS result = thread->Create(); if (XFAILED(result)) { diff --git a/src/xenia/kernel/objects/xuser_module.h b/src/xenia/kernel/objects/xuser_module.h index e8896fede..dc9a168dd 100644 --- a/src/xenia/kernel/objects/xuser_module.h +++ b/src/xenia/kernel/objects/xuser_module.h @@ -29,15 +29,20 @@ class XUserModule : public XModule { const xe::cpu::XexModule* xex_module() const { return reinterpret_cast(processor_module_); } + xe::cpu::XexModule* xex_module() { + return reinterpret_cast(processor_module_); + } const xex2_header* xex_header() const { return xex_module()->xex_header(); } uint32_t guest_xex_header() const { return guest_xex_header_; } + bool dll_module() const { return dll_module_; } uint32_t entry_point() const { return entry_point_; } uint32_t stack_size() const { return stack_size_; } X_STATUS LoadFromFile(std::string path); X_STATUS LoadFromMemory(const void* addr, const size_t length); + X_STATUS Unload(); uint32_t GetProcAddressByOrdinal(uint16_t ordinal) override; uint32_t GetProcAddressByName(const char* name) override; @@ -67,6 +72,7 @@ class XUserModule : public XModule { private: uint32_t guest_xex_header_; + bool dll_module_; uint32_t entry_point_; uint32_t stack_size_; }; diff --git a/src/xenia/kernel/xam_info.cc b/src/xenia/kernel/xam_info.cc index 980e565d4..ee095bb5a 100644 --- a/src/xenia/kernel/xam_info.cc +++ b/src/xenia/kernel/xam_info.cc @@ -11,8 +11,10 @@ #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/objects/xenumerator.h" #include "xenia/kernel/objects/xuser_module.h" +#include "xenia/kernel/objects/xthread.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/util/xex2.h" +#include "xenia/kernel/xam_module.h" #include "xenia/kernel/xam_private.h" #include "xenia/xbox.h" @@ -90,53 +92,84 @@ SHIM_CALL XamGetExecutionId_shim(PPCContext* ppc_context, SHIM_SET_RETURN_32(X_STATUS_SUCCESS); } -SHIM_CALL XamLoaderSetLaunchData_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t data_ptr = SHIM_GET_ARG_32(0); - uint32_t data_size = SHIM_GET_ARG_32(1); +dword_result_t XamLoaderSetLaunchData(lpvoid_t data, dword_t size) { + auto xam = kernel_state()->GetKernelModule("xam.xex"); - XELOGD("XamLoaderSetLaunchData(%.8X, %d)", data_ptr, data_size); + auto& loader_data = xam->loader_data(); + if (loader_data.launch_data_ptr) { + kernel_memory()->SystemHeapFree(loader_data.launch_data_ptr); + } - // Unknown return value. - SHIM_SET_RETURN_32(0); + loader_data.launch_data_ptr = kernel_memory()->SystemHeapAlloc(size); + loader_data.launch_data_size = size; + + std::memcpy(kernel_memory()->TranslateVirtual(loader_data.launch_data_ptr), + data, size); + + // FIXME: Unknown return value. + return 0; } +DECLARE_XAM_EXPORT(XamLoaderSetLaunchData, ExportTag::kSketchy); -SHIM_CALL XamLoaderGetLaunchDataSize_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t size_ptr = SHIM_GET_ARG_32(0); +dword_result_t XamLoaderGetLaunchDataSize(lpdword_t size_ptr) { + auto xam = kernel_state()->GetKernelModule("xam.xex"); - XELOGD("XamLoaderGetLaunchDataSize(%.8X)", size_ptr); + *size_ptr = xam->loader_data().launch_data_size; - SHIM_SET_MEM_32(size_ptr, 0); - - SHIM_SET_RETURN_32(1); + return 0; } +DECLARE_XAM_EXPORT(XamLoaderGetLaunchDataSize, ExportTag::kSketchy); -SHIM_CALL XamLoaderGetLaunchData_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t buffer_ptr = SHIM_GET_ARG_32(0); - uint32_t buffer_size = SHIM_GET_ARG_32(1); +dword_result_t XamLoaderGetLaunchData(lpvoid_t buffer_ptr, + dword_t buffer_size) { + auto xam = kernel_state()->GetKernelModule("xam.xex"); - XELOGD("XamLoaderGetLaunchData(%.8X, %d)", buffer_ptr, buffer_size); + auto& loader_data = xam->loader_data(); + if (loader_data.launch_data_ptr) { + uint8_t* loader_buffer_ptr = + kernel_memory()->TranslateVirtual(loader_data.launch_data_ptr); - SHIM_SET_RETURN_32(0); + uint32_t copy_size = + std::min(loader_data.launch_data_size, (uint32_t)buffer_size); + + std::memcpy(buffer_ptr, loader_buffer_ptr, copy_size); + } + + return 0; } +DECLARE_XAM_EXPORT(XamLoaderGetLaunchData, ExportTag::kSketchy); -SHIM_CALL XamLoaderLaunchTitle_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t name_ptr = SHIM_GET_ARG_32(0); - const char* name = (const char*)SHIM_MEM_ADDR(name_ptr); - uint32_t flags = SHIM_GET_ARG_32(1); +void XamLoaderLaunchTitle(lpstring_t raw_name, dword_t flags) { + auto xam = kernel_state()->GetKernelModule("xam.xex"); - XELOGD("XamLoaderLaunchTitle(%.8X(%s), %.8X)", name_ptr, name, flags); - assert_always(); + auto& loader_data = xam->loader_data(); + loader_data.launch_flags = flags; + + // Translate the launch path to a full path. + if (raw_name) { + std::string name = xe::find_name_from_path(std::string(raw_name)); + std::string path(raw_name); + if (name == std::string(raw_name)) { + path = xe::join_paths( + xe::find_base_path(kernel_state()->GetExecutableModule()->path()), + name); + } + + loader_data.launch_path = path; + } else { + assert_always("Game requested exit to dashboard via XamLoaderLaunchTitle"); + } + + // This function does not return. + kernel_state()->TerminateTitle(true); } +DECLARE_XAM_EXPORT(XamLoaderLaunchTitle, ExportTag::kSketchy); -SHIM_CALL XamLoaderTerminateTitle_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - XELOGD("XamLoaderTerminateTitle()"); - assert_always(); +void XamLoaderTerminateTitle() { + // This function does not return. + kernel_state()->TerminateTitle(true); } +DECLARE_XAM_EXPORT(XamLoaderTerminateTitle, ExportTag::kSketchy); SHIM_CALL XamAlloc_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t unk = SHIM_GET_ARG_32(0); @@ -223,12 +256,6 @@ void xe::kernel::xam::RegisterInfoExports( SHIM_SET_MAPPING("xam.xex", XamGetExecutionId, state); - SHIM_SET_MAPPING("xam.xex", XamLoaderSetLaunchData, state); - SHIM_SET_MAPPING("xam.xex", XamLoaderGetLaunchDataSize, state); - SHIM_SET_MAPPING("xam.xex", XamLoaderGetLaunchData, state); - SHIM_SET_MAPPING("xam.xex", XamLoaderLaunchTitle, state); - SHIM_SET_MAPPING("xam.xex", XamLoaderTerminateTitle, state); - SHIM_SET_MAPPING("xam.xex", XamAlloc, state); SHIM_SET_MAPPING("xam.xex", XamFree, state); diff --git a/src/xenia/kernel/xam_module.cc b/src/xenia/kernel/xam_module.cc index df3455eeb..79199217d 100644 --- a/src/xenia/kernel/xam_module.cc +++ b/src/xenia/kernel/xam_module.cc @@ -17,7 +17,7 @@ namespace xe { namespace kernel { XamModule::XamModule(Emulator* emulator, KernelState* kernel_state) - : XKernelModule(kernel_state, "xe:\\xam.xex") { + : XKernelModule(kernel_state, "xe:\\xam.xex"), loader_data_() { RegisterExportTable(export_resolver_); // Register all exported functions. diff --git a/src/xenia/kernel/xam_module.h b/src/xenia/kernel/xam_module.h index 370ee472f..50897457e 100644 --- a/src/xenia/kernel/xam_module.h +++ b/src/xenia/kernel/xam_module.h @@ -24,7 +24,18 @@ class XamModule : public XKernelModule { static void RegisterExportTable(xe::cpu::ExportResolver* export_resolver); + struct LoaderData { + uint32_t launch_data_ptr = 0; + uint32_t launch_data_size = 0; + uint32_t launch_flags = 0; + std::string launch_path; // Full path to next xex + }; + + const LoaderData& loader_data() const { return loader_data_; } + LoaderData& loader_data() { return loader_data_; } + private: + LoaderData loader_data_; }; } // namespace kernel diff --git a/src/xenia/kernel/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl_io.cc index 9e8795b69..8ca2b0665 100644 --- a/src/xenia/kernel/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl_io.cc @@ -824,6 +824,9 @@ SHIM_CALL NtFlushBuffersFile_shim(PPCContext* ppc_context, SHIM_SET_RETURN_32(result); } +dword_result_t FscGetCacheElementCount(dword_t r3) { return 0; } +DECLARE_XBOXKRNL_EXPORT(FscGetCacheElementCount, ExportTag::kStub); + SHIM_CALL FscSetCacheElementCount_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t unk_0 = SHIM_GET_ARG_32(0); diff --git a/src/xenia/kernel/xboxkrnl_module.cc b/src/xenia/kernel/xboxkrnl_module.cc index 016da3eb9..6af08be9e 100644 --- a/src/xenia/kernel/xboxkrnl_module.cc +++ b/src/xenia/kernel/xboxkrnl_module.cc @@ -177,14 +177,7 @@ XboxkrnlModule::~XboxkrnlModule() { int XboxkrnlModule::LaunchModule(const char* path) { // Create and register the module. We keep it local to this function and // dispose it on exit. - auto module = object_ref(new XUserModule(kernel_state_, path)); - - // Load the module into memory from the filesystem. - X_STATUS result_code = module->LoadFromFile(path); - if (XFAILED(result_code)) { - XELOGE("Failed to load module %s: %.8X", path, result_code); - return 1; - } + auto module = kernel_state_->LoadUserModule(path); // Set as the main module, while running. kernel_state_->SetExecutableModule(module); @@ -196,7 +189,10 @@ int XboxkrnlModule::LaunchModule(const char* path) { // Launch the module. // NOTE: this won't return until the module exits. - result_code = module->Launch(0); + X_STATUS result_code = module->Launch(0); + + // Main thread exited. Terminate the title. + kernel_state_->TerminateTitle(); kernel_state_->SetExecutableModule(NULL); if (XFAILED(result_code)) { XELOGE("Failed to launch module %s: %.8X", path, result_code); diff --git a/src/xenia/kernel/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl_modules.cc index 6f95ef3a1..ab195b5b5 100644 --- a/src/xenia/kernel/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl_modules.cc @@ -221,6 +221,7 @@ SHIM_CALL XexLoadImage_shim(PPCContext* ppc_context, if (module) { // Existing module found. hmodule = module->hmodule_ptr(); + result = X_STATUS_SUCCESS; } else { // Not found; attempt to load as a user module. auto user_module = kernel_state->LoadUserModule(module_name); @@ -232,9 +233,11 @@ SHIM_CALL XexLoadImage_shim(PPCContext* ppc_context, } // Increment the module's load count. - auto ldr_data = - kernel_memory()->TranslateVirtual(hmodule); - ldr_data->load_count++; + if (hmodule) { + auto ldr_data = + kernel_memory()->TranslateVirtual(hmodule); + ldr_data->load_count++; + } SHIM_SET_MEM_32(hmodule_ptr, hmodule); @@ -253,12 +256,15 @@ SHIM_CALL XexUnloadImage_shim(PPCContext* ppc_context, return; } - auto ldr_data = - kernel_state->memory()->TranslateVirtual( - hmodule); - if (ldr_data->load_count-- <= 0) { - // No more references, free it. - kernel_state->object_table()->RemoveHandle(module->handle()); + // Can't unload kernel modules from user code. + if (module->module_type() != XModule::ModuleType::kKernelModule) { + auto ldr_data = + kernel_state->memory()->TranslateVirtual( + hmodule); + if (--ldr_data->load_count == 0) { + // No more references, free it. + kernel_state->object_table()->RemoveHandle(module->handle()); + } } SHIM_SET_RETURN_32(X_STATUS_SUCCESS); diff --git a/src/xenia/kernel/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl_threading.cc index d24aa08f4..4769b8df3 100644 --- a/src/xenia/kernel/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl_threading.cc @@ -117,7 +117,7 @@ SHIM_CALL ExCreateThread_shim(PPCContext* ppc_context, auto thread = object_ref( new XThread(kernel_state, stack_size, xapi_thread_startup, start_address, - start_context, creation_flags)); + start_context, creation_flags, true)); X_STATUS result = thread->Create(); if (XFAILED(result)) { @@ -1348,38 +1348,31 @@ SHIM_CALL KeRemoveQueueDpc_shim(PPCContext* ppc_context, xe::mutex global_list_mutex_; -// http://www.nirsoft.net/kernel_struct/vista/SLIST_HEADER.html -struct SLIST_HEADER { - xe::be next; - xe::be depth; - xe::be sequence; -}; - -pointer_result_t InterlockedPopEntrySList(pointer_t plist_ptr) { +pointer_result_t InterlockedPopEntrySList(pointer_t plist_ptr) { std::lock_guard lock(global_list_mutex_); - uint32_t first = plist_ptr->next; - if (first == 0) { + if (plist_ptr->next.next == 0) { // List empty! return 0; } - // Get the second element. - uint8_t* p = kernel_memory()->TranslateVirtual(first); - auto second = xe::load_and_swap(p); + // Get the first element. + auto result = kernel_memory()->TranslateVirtual( + plist_ptr->next.next); - plist_ptr->next = second; + uint32_t popped = plist_ptr->next.next; + plist_ptr->next.next = result->next; // Return the one we popped - return first; + return popped; } DECLARE_XBOXKRNL_EXPORT(InterlockedPopEntrySList, ExportTag::kImplemented); -pointer_result_t InterlockedFlushSList(pointer_t plist_ptr) { +pointer_result_t InterlockedFlushSList(pointer_t plist_ptr) { std::lock_guard lock(global_list_mutex_); - uint32_t next = plist_ptr->next; - plist_ptr->next = 0; + uint32_t next = plist_ptr->next.next; + plist_ptr->next.next = 0; return next; } diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index 671bc4c9f..5c24c965a 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -280,9 +280,9 @@ struct X_ANSI_STRING { }; struct X_UNICODE_STRING { - xe::be length; - xe::be maximum_length; - xe::be pointer; + xe::be length; // 0x0 + xe::be maximum_length; // 0x2 + xe::be pointer; // 0x4 void reset() { length = 0; @@ -322,9 +322,28 @@ static_assert_size(X_VIDEO_MODE, 48); struct X_LIST_ENTRY { be flink_ptr; // next entry / head be blink_ptr; // previous entry / head + + // Assumes X_LIST_ENTRY is within guest memory! + void initialize(uint8_t* virtual_membase) { + flink_ptr = (uint32_t)((uint8_t*)this - virtual_membase); + blink_ptr = (uint32_t)((uint8_t*)this - virtual_membase); + } }; static_assert_size(X_LIST_ENTRY, 8); +struct X_SINGLE_LIST_ENTRY { + be next; // 0x0 pointer to next entry +}; +static_assert_size(X_SINGLE_LIST_ENTRY, 4); + +// http://www.nirsoft.net/kernel_struct/vista/SLIST_HEADER.html +struct X_SLIST_HEADER { + X_SINGLE_LIST_ENTRY next; // 0x0 + be depth; // 0x4 + be sequence; // 0x6 +}; +static_assert_size(X_SLIST_HEADER, 8); + #pragma pack(pop) } // namespace xe