Merge pull request #328 from DrChat/xam_loader

Implement XamLoaderLaunchTitle/XamLoaderTerminateTitle
This commit is contained in:
Ben Vanik 2015-07-07 07:46:56 -07:00
commit b09ab1f886
21 changed files with 414 additions and 189 deletions

View File

@ -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<const xex2_header*>(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<uint32_t>* 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;

View File

@ -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<const xex2_header*>(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<uint8_t> xex_header_mem_; // Holds the xex header
bool loaded_; // Loaded into memory?
uint32_t base_address_;
uint32_t low_address_;

View File

@ -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<sockaddr*>(&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<char*>(&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<uint8_t> body(length);
r = recv(client_socket_, reinterpret_cast<char*>(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<sockaddr*>(&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<char*>(&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<uint8_t> body(length);
r = recv(client_socket_, reinterpret_cast<char*>(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.

View File

@ -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_;

View File

@ -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<kernel::XboxkrnlModule>(
reinterpret_cast<kernel::XboxkrnlModule*>(xboxkrnl_module.release()));
int result = xboxkrnl->LaunchModule(module_path.c_str());
// Allow xam to request module loads.
auto xam = kernel_state()->GetKernelModule<kernel::XamModule>("xam.xex");
auto xboxkrnl =
kernel_state()->GetKernelModule<kernel::XboxkrnlModule>("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 {

View File

@ -161,6 +161,18 @@ bool KernelState::IsKernelModule(const char* name) {
return false;
}
object_ref<XKernelModule> 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<XModule> KernelState::GetModule(const char* name) {
if (!name) {
// NULL name = self.
@ -223,26 +235,27 @@ void KernelState::SetExecutableModule(object_ref<XUserModule> 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<XHostThread>(new XHostThread(this, 128 * 1024, 0, [this]() {
while (dispatch_thread_running_) {
std::unique_lock<std::mutex> 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<XHostThread>(new XHostThread(this, 128 * 1024, 0, [this]() {
while (dispatch_thread_running_) {
std::unique_lock<std::mutex> 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<XKernelModule> kernel_module) {
@ -255,6 +268,7 @@ object_ref<XUserModule> 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<XUserModule> 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<XUserModule> KernelState::LoadUserModule(const char* raw_name) {
return module;
}
void KernelState::TerminateTitle(bool from_guest_thread) {
std::lock_guard<xe::recursive_mutex> 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<xe::recursive_mutex> 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

View File

@ -112,15 +112,26 @@ class KernelState {
void UnregisterModule(XModule* module);
bool IsKernelModule(const char* name);
object_ref<XModule> GetModule(const char* name);
object_ref<XUserModule> GetExecutableModule();
void SetExecutableModule(object_ref<XUserModule> module);
object_ref<XUserModule> LoadUserModule(const char* name);
object_ref<XKernelModule> GetKernelModule(const char* name);
template <typename T>
object_ref<XKernelModule> LoadKernelModule() {
auto kernel_module = object_ref<XKernelModule>(new T(emulator_, this));
LoadKernelModule(kernel_module);
return kernel_module;
}
object_ref<XUserModule> LoadUserModule(const char* name);
template <typename T>
object_ref<T> GetKernelModule(const char* name) {
auto module = GetKernelModule(name);
return object_ref<T>(reinterpret_cast<T*>(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);

View File

@ -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;

View File

@ -86,6 +86,7 @@ class XModule : public XObject {
protected:
void OnLoad();
void OnUnload();
ModuleType module_type_;
std::string name_;

View File

@ -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<XEvent>(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<int()> 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() {

View File

@ -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<uint32_t> irql_;
xe::mutex apc_lock_;
NativeList* apc_list_;
object_ref<XEvent> event_;
};
class XHostThread : public XThread {

View File

@ -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<XThread>(
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)) {

View File

@ -29,15 +29,20 @@ class XUserModule : public XModule {
const xe::cpu::XexModule* xex_module() const {
return reinterpret_cast<xe::cpu::XexModule*>(processor_module_);
}
xe::cpu::XexModule* xex_module() {
return reinterpret_cast<xe::cpu::XexModule*>(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_;
};

View File

@ -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<kernel::XamModule>("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<kernel::XamModule>("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<kernel::XamModule>("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<kernel::XamModule>("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);

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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<XUserModule>(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);

View File

@ -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<X_LDR_DATA_TABLE_ENTRY*>(hmodule);
ldr_data->load_count++;
if (hmodule) {
auto ldr_data =
kernel_memory()->TranslateVirtual<X_LDR_DATA_TABLE_ENTRY*>(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<X_LDR_DATA_TABLE_ENTRY*>(
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<X_LDR_DATA_TABLE_ENTRY*>(
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);

View File

@ -117,7 +117,7 @@ SHIM_CALL ExCreateThread_shim(PPCContext* ppc_context,
auto thread = object_ref<XThread>(
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<uint32_t> next;
xe::be<uint16_t> depth;
xe::be<uint16_t> sequence;
};
pointer_result_t InterlockedPopEntrySList(pointer_t<SLIST_HEADER> plist_ptr) {
pointer_result_t InterlockedPopEntrySList(pointer_t<X_SLIST_HEADER> plist_ptr) {
std::lock_guard<xe::mutex> 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<uint32_t>(p);
// Get the first element.
auto result = kernel_memory()->TranslateVirtual<X_SINGLE_LIST_ENTRY*>(
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<SLIST_HEADER> plist_ptr) {
pointer_result_t InterlockedFlushSList(pointer_t<X_SLIST_HEADER> plist_ptr) {
std::lock_guard<xe::mutex> 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;
}

View File

@ -280,9 +280,9 @@ struct X_ANSI_STRING {
};
struct X_UNICODE_STRING {
xe::be<uint16_t> length;
xe::be<uint16_t> maximum_length;
xe::be<uint32_t> pointer;
xe::be<uint16_t> length; // 0x0
xe::be<uint16_t> maximum_length; // 0x2
xe::be<uint32_t> pointer; // 0x4
void reset() {
length = 0;
@ -322,9 +322,28 @@ static_assert_size(X_VIDEO_MODE, 48);
struct X_LIST_ENTRY {
be<uint32_t> flink_ptr; // next entry / head
be<uint32_t> 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<uint32_t> 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<uint16_t> depth; // 0x4
be<uint16_t> sequence; // 0x6
};
static_assert_size(X_SLIST_HEADER, 8);
#pragma pack(pop)
} // namespace xe