Real modules and threads (mostly).
Need events/waiting to get proper launch thread behavior.
This commit is contained in:
parent
2ecacedaa6
commit
49af0dbc85
|
@ -7,10 +7,10 @@ case "$*" in
|
||||||
(*--debug*) CONFIG=debug;;
|
(*--debug*) CONFIG=debug;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
EXEC=$DIR/../build/xenia/$CONFIG/xenia-info
|
EXEC=$DIR/../build/xenia/$CONFIG/xenia-run
|
||||||
|
|
||||||
if [ ! -f "$EXEC" ]; then
|
if [ ! -f "$EXEC" ]; then
|
||||||
python $DIR/../xenia-build.py build --$CONFIG
|
python $DIR/../xenia-build.py build --$CONFIG
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$EXEC "$@"
|
$EXEC --abort_before_entry=true "$@"
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
# LLVM paths.
|
# LLVM paths.
|
||||||
# TODO(benvanik): switch based on configuration.
|
# TODO(benvanik): switch based on configuration.
|
||||||
'llvm_path': 'build/llvm/release/',
|
'llvm_path': 'build/llvm/debug/',
|
||||||
'llvm_config': '<(llvm_path)bin/llvm-config',
|
'llvm_config': '<(llvm_path)bin/llvm-config',
|
||||||
'llvm_includedir': '<(llvm_path)/include',
|
'llvm_includedir': '<(llvm_path)/include',
|
||||||
'llvm_cxxflags': [
|
'llvm_cxxflags': [
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <xenia/core/file.h>
|
#include <xenia/core/file.h>
|
||||||
#include <xenia/core/memory.h>
|
#include <xenia/core/memory.h>
|
||||||
#include <xenia/core/mmap.h>
|
#include <xenia/core/mmap.h>
|
||||||
|
#include <xenia/core/mutex.h>
|
||||||
#include <xenia/core/pal.h>
|
#include <xenia/core/pal.h>
|
||||||
#include <xenia/core/ref.h>
|
#include <xenia/core/ref.h>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_CORE_MUTEX_H_
|
||||||
|
#define XENIA_CORE_MUTEX_H_
|
||||||
|
|
||||||
|
#include <xenia/common.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct xe_mutex xe_mutex_t;
|
||||||
|
|
||||||
|
|
||||||
|
xe_mutex_t* xe_mutex_alloc(uint32_t spin_count);
|
||||||
|
void xe_mutex_free(xe_mutex_t* mutex);
|
||||||
|
|
||||||
|
int xe_mutex_lock(xe_mutex_t* mutex);
|
||||||
|
int xe_mutex_trylock(xe_mutex_t* mutex);
|
||||||
|
int xe_mutex_unlock(xe_mutex_t* mutex);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // XENIA_CORE_MUTEX_H_
|
|
@ -16,7 +16,6 @@
|
||||||
#include <xenia/cpu/sdb.h>
|
#include <xenia/cpu/sdb.h>
|
||||||
#include <xenia/core/memory.h>
|
#include <xenia/core/memory.h>
|
||||||
#include <xenia/kernel/export.h>
|
#include <xenia/kernel/export.h>
|
||||||
#include <xenia/kernel/user_module.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
#include <xenia/cpu/sdb.h>
|
#include <xenia/cpu/sdb.h>
|
||||||
#include <xenia/kernel/export.h>
|
#include <xenia/kernel/export.h>
|
||||||
#include <xenia/kernel/user_module.h>
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
@ -49,7 +49,7 @@ public:
|
||||||
shared_ptr<llvm::ExecutionEngine>& engine);
|
shared_ptr<llvm::ExecutionEngine>& engine);
|
||||||
~ExecModule();
|
~ExecModule();
|
||||||
|
|
||||||
int PrepareUserModule(kernel::UserModule* user_module);
|
int PrepareXex(xe_xex2_ref xex);
|
||||||
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
|
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
|
||||||
|
|
||||||
void AddFunctionsToMap(FunctionMap& map);
|
void AddFunctionsToMap(FunctionMap& map);
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include <xenia/cpu/exec_module.h>
|
#include <xenia/cpu/exec_module.h>
|
||||||
#include <xenia/cpu/thread_state.h>
|
#include <xenia/cpu/thread_state.h>
|
||||||
#include <xenia/kernel/export.h>
|
#include <xenia/kernel/export.h>
|
||||||
#include <xenia/kernel/user_module.h>
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
@ -40,17 +40,18 @@ public:
|
||||||
|
|
||||||
int Setup();
|
int Setup();
|
||||||
|
|
||||||
int PrepareModule(const char* module_name, const char* module_path,
|
int LoadBinary(const xechar_t* path, uint32_t start_address,
|
||||||
uint32_t start_address, uint32_t end_address,
|
shared_ptr<kernel::ExportResolver> export_resolver);
|
||||||
shared_ptr<kernel::ExportResolver> export_resolver);
|
|
||||||
int PrepareModule(kernel::UserModule* user_module,
|
int PrepareModule(const char* name, const char* path, xe_xex2_ref xex,
|
||||||
shared_ptr<kernel::ExportResolver> export_resolver);
|
shared_ptr<kernel::ExportResolver> export_resolver);
|
||||||
|
|
||||||
uint32_t CreateCallback(void (*callback)(void* data), void* data);
|
uint32_t CreateCallback(void (*callback)(void* data), void* data);
|
||||||
|
|
||||||
ThreadState* AllocThread(uint32_t stack_address, uint32_t stack_size);
|
ThreadState* AllocThread(uint32_t stack_size, uint32_t thread_state_address);
|
||||||
void DeallocThread(ThreadState* thread_state);
|
void DeallocThread(ThreadState* thread_state);
|
||||||
int Execute(ThreadState* thread_state, uint32_t address);
|
int Execute(ThreadState* thread_state, uint32_t address);
|
||||||
|
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
llvm::Function* GetFunction(uint32_t address);
|
llvm::Function* GetFunction(uint32_t address);
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <xenia/kernel/export.h>
|
#include <xenia/kernel/export.h>
|
||||||
#include <xenia/kernel/user_module.h>
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -205,7 +205,7 @@ class XexSymbolDatabase : public SymbolDatabase {
|
||||||
public:
|
public:
|
||||||
XexSymbolDatabase(xe_memory_ref memory,
|
XexSymbolDatabase(xe_memory_ref memory,
|
||||||
kernel::ExportResolver* export_resolver,
|
kernel::ExportResolver* export_resolver,
|
||||||
kernel::UserModule* module);
|
xe_xex2_ref xex);
|
||||||
virtual ~XexSymbolDatabase();
|
virtual ~XexSymbolDatabase();
|
||||||
|
|
||||||
virtual int Analyze();
|
virtual int Analyze();
|
||||||
|
@ -218,7 +218,7 @@ private:
|
||||||
virtual uint32_t GetEntryPoint();
|
virtual uint32_t GetEntryPoint();
|
||||||
virtual bool IsValueInTextRange(uint32_t value);
|
virtual bool IsValueInTextRange(uint32_t value);
|
||||||
|
|
||||||
kernel::UserModule* module_;
|
xe_xex2_ref xex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,16 +25,19 @@ class Processor;
|
||||||
class ThreadState {
|
class ThreadState {
|
||||||
public:
|
public:
|
||||||
ThreadState(Processor* processor,
|
ThreadState(Processor* processor,
|
||||||
uint32_t stack_address, uint32_t stack_size);
|
uint32_t stack_size, uint32_t thread_state_address);
|
||||||
~ThreadState();
|
~ThreadState();
|
||||||
|
|
||||||
xe_ppc_state_t* ppc_state();
|
xe_ppc_state_t* ppc_state();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t stack_address_;
|
|
||||||
uint32_t stack_size_;
|
uint32_t stack_size_;
|
||||||
|
uint32_t thread_state_address;
|
||||||
xe_memory_ref memory_;
|
xe_memory_ref memory_;
|
||||||
|
|
||||||
|
uint32_t stack_address_;
|
||||||
|
uint32_t thread_state_address_;
|
||||||
|
|
||||||
xe_ppc_state_t ppc_state_;
|
xe_ppc_state_t ppc_state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,30 +14,36 @@
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
#include <xenia/kernel/export.h>
|
#include <xenia/kernel/export.h>
|
||||||
|
#include <xenia/kernel/runtime.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
|
|
||||||
|
class Runtime;
|
||||||
|
|
||||||
|
|
||||||
class KernelModule {
|
class KernelModule {
|
||||||
public:
|
public:
|
||||||
KernelModule(xe_pal_ref pal, xe_memory_ref memory,
|
KernelModule(Runtime* runtime) {
|
||||||
shared_ptr<ExportResolver> resolver) {
|
runtime_ = runtime;
|
||||||
pal_ = xe_pal_retain(pal);
|
pal_ = runtime->pal();
|
||||||
memory_ = xe_memory_retain(memory);
|
memory_ = runtime->memory();
|
||||||
resolver_ = resolver;
|
export_resolver_ = runtime->export_resolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~KernelModule() {
|
virtual ~KernelModule() {
|
||||||
|
export_resolver_.reset();
|
||||||
xe_memory_release(memory_);
|
xe_memory_release(memory_);
|
||||||
xe_pal_release(pal_);
|
xe_pal_release(pal_);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
xe_pal_ref pal_;
|
Runtime* runtime_;
|
||||||
xe_memory_ref memory_;
|
xe_pal_ref pal_;
|
||||||
shared_ptr<ExportResolver> resolver_;
|
xe_memory_ref memory_;
|
||||||
|
shared_ptr<ExportResolver> export_resolver_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include <xenia/cpu.h>
|
#include <xenia/cpu.h>
|
||||||
|
|
||||||
#include <xenia/kernel/export.h>
|
#include <xenia/kernel/export.h>
|
||||||
#include <xenia/kernel/user_module.h>
|
|
||||||
#include <xenia/kernel/xex2.h>
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +22,14 @@ namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
class Processor;
|
class Processor;
|
||||||
}
|
}
|
||||||
|
namespace kernel {
|
||||||
|
namespace xboxkrnl {
|
||||||
|
class XboxkrnlModule;
|
||||||
|
}
|
||||||
|
namespace xam {
|
||||||
|
class XamModule;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,12 +51,7 @@ public:
|
||||||
shared_ptr<ExportResolver> export_resolver();
|
shared_ptr<ExportResolver> export_resolver();
|
||||||
const xechar_t* command_line();
|
const xechar_t* command_line();
|
||||||
|
|
||||||
int LoadBinaryModule(const xechar_t* path, uint32_t start_address);
|
int LaunchModule(const xechar_t* path);
|
||||||
|
|
||||||
int LoadModule(const xechar_t* path);
|
|
||||||
void LaunchModule(UserModule* user_module);
|
|
||||||
UserModule* GetModule(const xechar_t* path);
|
|
||||||
void UnloadModule(UserModule* user_module);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xe_pal_ref pal_;
|
xe_pal_ref pal_;
|
||||||
|
@ -58,8 +60,8 @@ private:
|
||||||
xechar_t command_line_[2048];
|
xechar_t command_line_[2048];
|
||||||
shared_ptr<ExportResolver> export_resolver_;
|
shared_ptr<ExportResolver> export_resolver_;
|
||||||
|
|
||||||
std::vector<KernelModule*> kernel_modules_;
|
auto_ptr<xboxkrnl::XboxkrnlModule> xboxkrnl_;
|
||||||
std::map<const xechar_t*, UserModule*> user_modules_;
|
auto_ptr<xam::XamModule> xam_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
|
||||||
******************************************************************************
|
|
||||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
|
||||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef XENIA_KERNEL_USER_MODULE_H_
|
|
||||||
#define XENIA_KERNEL_USER_MODULE_H_
|
|
||||||
|
|
||||||
#include <xenia/core.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <xenia/kernel/export.h>
|
|
||||||
#include <xenia/kernel/xex2.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace kernel {
|
|
||||||
|
|
||||||
|
|
||||||
#define kXEPESectionContainsCode 0x00000020
|
|
||||||
#define kXEPESectionContainsDataInit 0x00000040
|
|
||||||
#define kXEPESectionContainsDataUninit 0x00000080
|
|
||||||
#define kXEPESectionMemoryExecute 0x20000000
|
|
||||||
#define kXEPESectionMemoryRead 0x40000000
|
|
||||||
#define kXEPESectionMemoryWrite 0x80000000
|
|
||||||
|
|
||||||
class PESection {
|
|
||||||
public:
|
|
||||||
char name[9]; // 8 + 1 for \0
|
|
||||||
uint32_t raw_address;
|
|
||||||
size_t raw_size;
|
|
||||||
uint32_t address;
|
|
||||||
size_t size;
|
|
||||||
uint32_t flags; // kXEPESection*
|
|
||||||
};
|
|
||||||
|
|
||||||
class PEMethodInfo {
|
|
||||||
public:
|
|
||||||
uint32_t address;
|
|
||||||
size_t total_length; // in bytes
|
|
||||||
size_t prolog_length; // in bytes
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class UserModule {
|
|
||||||
public:
|
|
||||||
UserModule(xe_memory_ref memory);
|
|
||||||
~UserModule();
|
|
||||||
|
|
||||||
int Load(const void* addr, const size_t length, const xechar_t* path);
|
|
||||||
|
|
||||||
const xechar_t* path();
|
|
||||||
const xechar_t* name();
|
|
||||||
uint32_t handle();
|
|
||||||
xe_xex2_ref xex();
|
|
||||||
const xe_xex2_header_t* xex_header();
|
|
||||||
|
|
||||||
void* GetProcAddress(const uint32_t ordinal);
|
|
||||||
PESection* GetSection(const char* name);
|
|
||||||
int GetMethodHints(PEMethodInfo** out_method_infos,
|
|
||||||
size_t* out_method_info_count);
|
|
||||||
|
|
||||||
void Dump(ExportResolver* export_resolver);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int LoadPE();
|
|
||||||
|
|
||||||
xe_memory_ref memory_;
|
|
||||||
|
|
||||||
xechar_t path_[2048];
|
|
||||||
xechar_t name_[256];
|
|
||||||
|
|
||||||
uint32_t handle_;
|
|
||||||
xe_xex2_ref xex_;
|
|
||||||
|
|
||||||
std::vector<PESection*> sections_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace kernel
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_USER_MODULE_H_
|
|
|
@ -7,8 +7,8 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_H_
|
#ifndef XENIA_KERNEL_XBOX_H_
|
||||||
#define XENIA_KERNEL_MODULES_XBOXKRNL_H_
|
#define XENIA_KERNEL_XBOX_H_
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace xboxkrnl {
|
|
||||||
|
|
||||||
|
|
||||||
typedef uint32_t X_HANDLE;
|
typedef uint32_t X_HANDLE;
|
||||||
|
@ -26,6 +25,9 @@ typedef uint32_t X_HANDLE;
|
||||||
// NT_STATUS (STATUS_*)
|
// NT_STATUS (STATUS_*)
|
||||||
// http://msdn.microsoft.com/en-us/library/cc704588.aspx
|
// http://msdn.microsoft.com/en-us/library/cc704588.aspx
|
||||||
// Adding as needed.
|
// Adding as needed.
|
||||||
|
typedef uint32_t X_STATUS;
|
||||||
|
#define XFAILED(s) (s & X_STATUS_UNSUCCESSFUL)
|
||||||
|
#define XSUCCEEDED(s) !XFAILED(s)
|
||||||
#define X_STATUS_SUCCESS ((uint32_t)0x00000000L)
|
#define X_STATUS_SUCCESS ((uint32_t)0x00000000L)
|
||||||
#define X_STATUS_UNSUCCESSFUL ((uint32_t)0xC0000001L)
|
#define X_STATUS_UNSUCCESSFUL ((uint32_t)0xC0000001L)
|
||||||
#define X_STATUS_NOT_IMPLEMENTED ((uint32_t)0xC0000002L)
|
#define X_STATUS_NOT_IMPLEMENTED ((uint32_t)0xC0000002L)
|
||||||
|
@ -75,13 +77,16 @@ typedef uint32_t X_HANDLE;
|
||||||
#define X_PROCTYPE_SYSTEM 2
|
#define X_PROCTYPE_SYSTEM 2
|
||||||
|
|
||||||
|
|
||||||
|
// Thread enums.
|
||||||
|
#define X_CREATE_SUSPENDED 0x00000004
|
||||||
|
|
||||||
|
|
||||||
// TLS specials.
|
// TLS specials.
|
||||||
#define X_TLS_OUT_OF_INDEXES UINT32_MAX // (-1)
|
#define X_TLS_OUT_OF_INDEXES UINT32_MAX // (-1)
|
||||||
|
|
||||||
|
|
||||||
} // namespace xboxkrnl
|
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_H_
|
#endif // XENIA_KERNEL_XBOX_H_
|
|
@ -27,6 +27,24 @@ typedef struct {
|
||||||
uint32_t thunk_address; // NULL or address of thunk
|
uint32_t thunk_address; // NULL or address of thunk
|
||||||
} xe_xex2_import_info_t;
|
} xe_xex2_import_info_t;
|
||||||
|
|
||||||
|
enum xe_pe_section_flags_e {
|
||||||
|
kXEPESectionContainsCode = 0x00000020,
|
||||||
|
kXEPESectionContainsDataInit = 0x00000040,
|
||||||
|
kXEPESectionContainsDataUninit = 0x00000080,
|
||||||
|
kXEPESectionMemoryExecute = 0x20000000,
|
||||||
|
kXEPESectionMemoryRead = 0x40000000,
|
||||||
|
kXEPESectionMemoryWrite = 0x80000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
class PESection {
|
||||||
|
public:
|
||||||
|
char name[9]; // 8 + 1 for \0
|
||||||
|
uint32_t raw_address;
|
||||||
|
size_t raw_size;
|
||||||
|
uint32_t address;
|
||||||
|
size_t size;
|
||||||
|
uint32_t flags; // kXEPESection*
|
||||||
|
};
|
||||||
|
|
||||||
xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
|
xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
|
||||||
const void* addr, const size_t length,
|
const void* addr, const size_t length,
|
||||||
|
@ -36,6 +54,7 @@ void xe_xex2_release(xe_xex2_ref xex);
|
||||||
|
|
||||||
const xechar_t *xe_xex2_get_name(xe_xex2_ref xex);
|
const xechar_t *xe_xex2_get_name(xe_xex2_ref xex);
|
||||||
const xe_xex2_header_t *xe_xex2_get_header(xe_xex2_ref xex);
|
const xe_xex2_header_t *xe_xex2_get_header(xe_xex2_ref xex);
|
||||||
|
const PESection* xe_xex2_get_pe_section(xe_xex2_ref xex, const char* name);
|
||||||
|
|
||||||
int xe_xex2_get_import_infos(xe_xex2_ref xex,
|
int xe_xex2_get_import_infos(xe_xex2_ref xex,
|
||||||
const xe_xex2_import_library_t *library,
|
const xe_xex2_import_library_t *library,
|
||||||
|
|
|
@ -9,12 +9,14 @@
|
||||||
|
|
||||||
#include <xenia/core/memory.h>
|
#include <xenia/core/memory.h>
|
||||||
|
|
||||||
|
#include <xenia/core/mutex.h>
|
||||||
|
|
||||||
#if !XE_PLATFORM(WIN32)
|
#if !XE_PLATFORM(WIN32)
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
|
||||||
#define MSPACES 1
|
#define MSPACES 1
|
||||||
#define USE_LOCKS 1
|
#define USE_LOCKS 0
|
||||||
#define USE_DL_PREFIX 1
|
#define USE_DL_PREFIX 1
|
||||||
#define HAVE_MORECORE 0
|
#define HAVE_MORECORE 0
|
||||||
#define HAVE_MMAP 0
|
#define HAVE_MMAP 0
|
||||||
|
@ -43,10 +45,11 @@
|
||||||
struct xe_memory {
|
struct xe_memory {
|
||||||
xe_ref_t ref;
|
xe_ref_t ref;
|
||||||
|
|
||||||
size_t length;
|
size_t length;
|
||||||
void *ptr;
|
void* ptr;
|
||||||
|
|
||||||
mspace heap;
|
xe_mutex_t* heap_mutex;
|
||||||
|
mspace heap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,6 +73,9 @@ xe_memory_ref xe_memory_create(xe_pal_ref pal, xe_memory_options_t options) {
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
XEEXPECTNOTNULL(memory->ptr);
|
XEEXPECTNOTNULL(memory->ptr);
|
||||||
|
|
||||||
|
memory->heap_mutex = xe_mutex_alloc(0);
|
||||||
|
XEEXPECTNOTNULL(memory->heap_mutex);
|
||||||
|
|
||||||
// Allocate the mspace for our heap.
|
// Allocate the mspace for our heap.
|
||||||
// We skip the first page to make writes to 0 easier to find.
|
// We skip the first page to make writes to 0 easier to find.
|
||||||
offset = 64 * 1024;
|
offset = 64 * 1024;
|
||||||
|
@ -85,8 +91,15 @@ XECLEANUP:
|
||||||
}
|
}
|
||||||
|
|
||||||
void xe_memory_dealloc(xe_memory_ref memory) {
|
void xe_memory_dealloc(xe_memory_ref memory) {
|
||||||
if (memory->heap) {
|
if (memory->heap_mutex && memory->heap) {
|
||||||
|
xe_mutex_lock(memory->heap_mutex);
|
||||||
destroy_mspace(memory->heap);
|
destroy_mspace(memory->heap);
|
||||||
|
memory->heap = NULL;
|
||||||
|
xe_mutex_unlock(memory->heap_mutex);
|
||||||
|
}
|
||||||
|
if (memory->heap_mutex) {
|
||||||
|
xe_mutex_free(memory->heap_mutex);
|
||||||
|
memory->heap_mutex = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if XE_PLATFORM(WIN32)
|
#if XE_PLATFORM(WIN32)
|
||||||
|
@ -148,7 +161,9 @@ uint32_t xe_memory_heap_alloc(xe_memory_ref memory, uint32_t base_addr,
|
||||||
XEASSERT(base_addr == 0);
|
XEASSERT(base_addr == 0);
|
||||||
XEASSERT(flags == 0);
|
XEASSERT(flags == 0);
|
||||||
|
|
||||||
|
XEIGNORE(xe_mutex_lock(memory->heap_mutex));
|
||||||
uint8_t* p = (uint8_t*)mspace_malloc(memory->heap, size);
|
uint8_t* p = (uint8_t*)mspace_malloc(memory->heap, size);
|
||||||
|
XEIGNORE(xe_mutex_unlock(memory->heap_mutex));
|
||||||
if (!p) {
|
if (!p) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +181,9 @@ uint32_t xe_memory_heap_free(xe_memory_ref memory, uint32_t addr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XEIGNORE(xe_mutex_lock(memory->heap_mutex));
|
||||||
mspace_free(memory->heap, p);
|
mspace_free(memory->heap, p);
|
||||||
|
XEIGNORE(xe_mutex_unlock(memory->heap_mutex));
|
||||||
|
|
||||||
return (uint32_t)real_size;
|
return (uint32_t)real_size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xenia/core/mutex.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct xe_mutex {
|
||||||
|
pthread_mutex_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
xe_mutex_t* xe_mutex_alloc(uint32_t spin_count) {
|
||||||
|
xe_mutex_t* mutex = (xe_mutex_t*)xe_calloc(sizeof(xe_mutex_t));
|
||||||
|
|
||||||
|
int result = pthread_mutex_init(&mutex->value, NULL);
|
||||||
|
switch (result) {
|
||||||
|
case ENOMEM:
|
||||||
|
case EINVAL:
|
||||||
|
xe_free(mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xe_mutex_free(xe_mutex_t* mutex) {
|
||||||
|
int result = pthread_mutex_destroy(&mutex->value);
|
||||||
|
switch (result) {
|
||||||
|
case EBUSY:
|
||||||
|
case EINVAL:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
xe_free(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int xe_mutex_lock(xe_mutex_t* mutex) {
|
||||||
|
return pthread_mutex_lock(&mutex->value) == EINVAL ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xe_mutex_trylock(xe_mutex_t* mutex) {
|
||||||
|
int result = pthread_mutex_trylock(&mutex->value);
|
||||||
|
switch (result) {
|
||||||
|
case EBUSY:
|
||||||
|
case EINVAL:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int xe_mutex_unlock(xe_mutex_t* mutex) {
|
||||||
|
return pthread_mutex_unlock(&mutex->value) == EINVAL ? 1 : 0;
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xenia/core/mutex.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct xe_mutex {
|
||||||
|
CRITICAL_SECTION value;
|
||||||
|
};
|
||||||
|
|
||||||
|
xe_mutex_t* xe_mutex_alloc(uint32_t spin_count) {
|
||||||
|
xe_mutex_t* mutex = (xe_mutex_t*)xe_calloc(sizeof(xe_mutex_t));
|
||||||
|
|
||||||
|
if (spin_count) {
|
||||||
|
InitializeCriticalSectionAndSpinCount(&mutex->value, spin_count);
|
||||||
|
} else {
|
||||||
|
InitializeCriticalSection(&mutex->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xe_mutex_free(xe_mutex_t* mutex) {
|
||||||
|
DeleteCriticalSection(&mutex->value);
|
||||||
|
xe_free(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int xe_mutex_lock(xe_mutex_t* mutex) {
|
||||||
|
EnterCriticalSection(&mutex->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xe_mutex_trylock(xe_mutex_t* mutex) {
|
||||||
|
if (TryEnterCriticalSection(&mutex->value) == TRUE) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int xe_mutex_unlock(xe_mutex_t* mutex) {
|
||||||
|
LeaveCriticalSection(&mutex->value);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -11,11 +11,13 @@
|
||||||
['OS == "mac" or OS == "linux"', {
|
['OS == "mac" or OS == "linux"', {
|
||||||
'sources': [
|
'sources': [
|
||||||
'mmap_posix.cc',
|
'mmap_posix.cc',
|
||||||
|
'mutex_posix.cc',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
['OS == "win"', {
|
['OS == "win"', {
|
||||||
'sources': [
|
'sources': [
|
||||||
'mmap_win.cc',
|
'mmap_win.cc',
|
||||||
|
'mutex_win.cc',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
|
|
|
@ -87,6 +87,7 @@ int XeEmitBranchTo(
|
||||||
// registers?
|
// registers?
|
||||||
g.SpillRegisters();
|
g.SpillRegisters();
|
||||||
|
|
||||||
|
XEASSERTNOTNULL(fn_block->outgoing_function);
|
||||||
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
|
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
|
||||||
Function::arg_iterator args = g.gen_fn()->arg_begin();
|
Function::arg_iterator args = g.gen_fn()->arg_begin();
|
||||||
Value* state_ptr = args;
|
Value* state_ptr = args;
|
||||||
|
|
|
@ -70,9 +70,9 @@ ExecModule::~ExecModule() {
|
||||||
xe_memory_release(memory_);
|
xe_memory_release(memory_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExecModule::PrepareUserModule(kernel::UserModule* user_module) {
|
int ExecModule::PrepareXex(xe_xex2_ref xex) {
|
||||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||||
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), user_module));
|
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), xex));
|
||||||
|
|
||||||
int result_code = Prepare();
|
int result_code = Prepare();
|
||||||
if (result_code) {
|
if (result_code) {
|
||||||
|
@ -80,8 +80,7 @@ int ExecModule::PrepareUserModule(kernel::UserModule* user_module) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import variables.
|
// Import variables.
|
||||||
xe_xex2_ref xex = user_module->xex();
|
// TODO??
|
||||||
xe_xex2_release(xex);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,10 @@ void XeTraceInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
|
||||||
type && type->emit ? " " : "X",
|
type && type->emit ? " " : "X",
|
||||||
type ? type->name : "<unknown>");
|
type ? type->name : "<unknown>");
|
||||||
|
|
||||||
|
if (cia == 0x82014468) {
|
||||||
|
printf("BREAKBREAKBREAK\n");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(benvanik): better disassembly, printing of current register values/etc
|
// TODO(benvanik): better disassembly, printing of current register values/etc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,14 +113,35 @@ int Processor::Setup() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Processor::PrepareModule(
|
int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
|
||||||
const char* module_name, const char* module_path,
|
shared_ptr<ExportResolver> export_resolver) {
|
||||||
uint32_t start_address, uint32_t end_address,
|
ExecModule* exec_module = NULL;
|
||||||
shared_ptr<ExportResolver> export_resolver) {
|
const xechar_t* name = xestrrchr(path, '/') + 1;
|
||||||
ExecModule* exec_module = new ExecModule(
|
|
||||||
memory_, export_resolver, module_name, module_path, engine_);
|
|
||||||
|
|
||||||
if (exec_module->PrepareRawBinary(start_address, end_address)) {
|
// TODO(benvanik): map file from filesystem
|
||||||
|
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
|
||||||
|
if (!mmap) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
void* addr = xe_mmap_get_addr(mmap);
|
||||||
|
size_t length = xe_mmap_get_length(mmap);
|
||||||
|
|
||||||
|
int result_code = 1;
|
||||||
|
|
||||||
|
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
|
||||||
|
xe_memory_get_length(memory_),
|
||||||
|
addr, length));
|
||||||
|
|
||||||
|
// Prepare the module.
|
||||||
|
char name_a[2048];
|
||||||
|
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
||||||
|
char path_a[2048];
|
||||||
|
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path));
|
||||||
|
|
||||||
|
exec_module = new ExecModule(
|
||||||
|
memory_, export_resolver, name_a, path_a, engine_);
|
||||||
|
|
||||||
|
if (exec_module->PrepareRawBinary(start_address, start_address + length)) {
|
||||||
delete exec_module;
|
delete exec_module;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -130,20 +151,23 @@ int Processor::PrepareModule(
|
||||||
|
|
||||||
exec_module->Dump();
|
exec_module->Dump();
|
||||||
|
|
||||||
return 0;
|
result_code = 0;
|
||||||
|
XECLEANUP:
|
||||||
|
if (result_code) {
|
||||||
|
delete exec_module;
|
||||||
|
}
|
||||||
|
xe_mmap_release(mmap);
|
||||||
|
return result_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Processor::PrepareModule(UserModule* user_module,
|
int Processor::PrepareModule(const char* name, const char* path,
|
||||||
|
xe_xex2_ref xex,
|
||||||
shared_ptr<ExportResolver> export_resolver) {
|
shared_ptr<ExportResolver> export_resolver) {
|
||||||
char name_a[2048];
|
|
||||||
char path_a[2048];
|
|
||||||
XEIGNORE(xestrnarrow(name_a, XECOUNT(name_a), user_module->name()));
|
|
||||||
XEIGNORE(xestrnarrow(path_a, XECOUNT(path_a), user_module->path()));
|
|
||||||
ExecModule* exec_module = new ExecModule(
|
ExecModule* exec_module = new ExecModule(
|
||||||
memory_, export_resolver, name_a, path_a,
|
memory_, export_resolver, name, path,
|
||||||
engine_);
|
engine_);
|
||||||
|
|
||||||
if (exec_module->PrepareUserModule(user_module)) {
|
if (exec_module->PrepareXex(xex)) {
|
||||||
delete exec_module;
|
delete exec_module;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -151,7 +175,6 @@ int Processor::PrepareModule(UserModule* user_module,
|
||||||
exec_module->AddFunctionsToMap(all_fns_);
|
exec_module->AddFunctionsToMap(all_fns_);
|
||||||
modules_.push_back(exec_module);
|
modules_.push_back(exec_module);
|
||||||
|
|
||||||
//user_module->Dump(export_resolver.get());
|
|
||||||
exec_module->Dump();
|
exec_module->Dump();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -162,10 +185,10 @@ uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState* Processor::AllocThread(uint32_t stack_address,
|
ThreadState* Processor::AllocThread(uint32_t stack_size,
|
||||||
uint32_t stack_size) {
|
uint32_t thread_state_address) {
|
||||||
ThreadState* thread_state = new ThreadState(
|
ThreadState* thread_state = new ThreadState(
|
||||||
this, stack_address, stack_size);
|
this, stack_size, thread_state_address);
|
||||||
return thread_state;
|
return thread_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +233,16 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address,
|
||||||
|
uint64_t arg0) {
|
||||||
|
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
|
||||||
|
ppc_state->r[3] = arg0;
|
||||||
|
if (Execute(thread_state, address)) {
|
||||||
|
return 0xDEADBABE;
|
||||||
|
}
|
||||||
|
return ppc_state->r[3];
|
||||||
|
}
|
||||||
|
|
||||||
Function* Processor::GetFunction(uint32_t address) {
|
Function* Processor::GetFunction(uint32_t address) {
|
||||||
FunctionMap::iterator it = all_fns_.find(address);
|
FunctionMap::iterator it = all_fns_.find(address);
|
||||||
if (it != all_fns_.end()) {
|
if (it != all_fns_.end()) {
|
||||||
|
|
116
src/cpu/sdb.cc
116
src/cpu/sdb.cc
|
@ -23,6 +23,36 @@ using namespace xe::cpu::sdb;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
// IMAGE_CE_RUNTIME_FUNCTION_ENTRY
|
||||||
|
// http://msdn.microsoft.com/en-us/library/ms879748.aspx
|
||||||
|
typedef struct IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY_t {
|
||||||
|
uint32_t FuncStart; // Virtual address
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t PrologLen : 8; // # of prolog instructions (size = x4)
|
||||||
|
uint32_t FuncLen : 22; // # of instructions total (size = x4)
|
||||||
|
uint32_t ThirtyTwoBit : 1; // Always 1
|
||||||
|
uint32_t ExceptionFlag : 1; // 1 if PDATA_EH in .text -- unknown if used
|
||||||
|
} Flags;
|
||||||
|
uint32_t FlagsValue; // To make byte swapping easier
|
||||||
|
};
|
||||||
|
} IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY;
|
||||||
|
|
||||||
|
|
||||||
|
class PEMethodInfo {
|
||||||
|
public:
|
||||||
|
uint32_t address;
|
||||||
|
size_t total_length; // in bytes
|
||||||
|
size_t prolog_length; // in bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FunctionBlock::FunctionBlock() :
|
FunctionBlock::FunctionBlock() :
|
||||||
start_address(0), end_address(0),
|
start_address(0), end_address(0),
|
||||||
outgoing_type(kTargetUnknown), outgoing_address(0),
|
outgoing_type(kTargetUnknown), outgoing_address(0),
|
||||||
|
@ -405,7 +435,8 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||||
block->outgoing_type = FunctionBlock::kTargetLR;
|
block->outgoing_type = FunctionBlock::kTargetLR;
|
||||||
if (furthest_target > addr) {
|
if (furthest_target > addr) {
|
||||||
// Remaining targets within function, not end.
|
// Remaining targets within function, not end.
|
||||||
XELOGSDB(XT("ignoring blr %.8X (branch to %.8X)"), addr, furthest_target);
|
XELOGSDB(XT("ignoring blr %.8X (branch to %.8X)"), addr,
|
||||||
|
furthest_target);
|
||||||
} else {
|
} else {
|
||||||
// Function end point.
|
// Function end point.
|
||||||
XELOGSDB(XT("function end %.8X"), addr);
|
XELOGSDB(XT("function end %.8X"), addr);
|
||||||
|
@ -458,6 +489,11 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||||
|
|
||||||
if (!ends_fn) {
|
if (!ends_fn) {
|
||||||
furthest_target = MAX(furthest_target, target);
|
furthest_target = MAX(furthest_target, target);
|
||||||
|
|
||||||
|
// TODO(benvanik): perhaps queue up for a speculative check? I think
|
||||||
|
// we are running over tail-call functions here that branch to
|
||||||
|
// somewhere else.
|
||||||
|
//GetOrInsertFunction(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ends_block = true;
|
ends_block = true;
|
||||||
|
@ -467,9 +503,16 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||||
block->outgoing_address = target;
|
block->outgoing_address = target;
|
||||||
if (i.B.LK) {
|
if (i.B.LK) {
|
||||||
XELOGSDB(XT("bcl %.8X -> %.8X"), addr, target);
|
XELOGSDB(XT("bcl %.8X -> %.8X"), addr, target);
|
||||||
|
|
||||||
|
// Queue call target if needed.
|
||||||
|
// TODO(benvanik): see if this is correct - not sure anyone makes
|
||||||
|
// function calls with bcl.
|
||||||
|
//GetOrInsertFunction(target);
|
||||||
} else {
|
} else {
|
||||||
XELOGSDB(XT("bc %.8X -> %.8X"), addr, target);
|
XELOGSDB(XT("bc %.8X -> %.8X"), addr, target);
|
||||||
|
|
||||||
|
// TODO(benvanik): GetOrInsertFunction? it's likely a BB
|
||||||
|
|
||||||
furthest_target = MAX(furthest_target, target);
|
furthest_target = MAX(furthest_target, target);
|
||||||
}
|
}
|
||||||
ends_block = true;
|
ends_block = true;
|
||||||
|
@ -558,14 +601,16 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
||||||
block->outgoing_block = fn->SplitBlock(block->outgoing_address);
|
block->outgoing_block = fn->SplitBlock(block->outgoing_address);
|
||||||
}
|
}
|
||||||
if (!block->outgoing_block) {
|
if (!block->outgoing_block) {
|
||||||
printf("block target not found: %.8X\n", block->outgoing_address);
|
XELOGE(XT("block target not found: %.8X"), block->outgoing_address);
|
||||||
|
XEASSERTALWAYS();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Function call.
|
// Function call.
|
||||||
block->outgoing_type = FunctionBlock::kTargetFunction;
|
block->outgoing_type = FunctionBlock::kTargetFunction;
|
||||||
block->outgoing_function = GetFunction(block->outgoing_address);
|
block->outgoing_function = GetFunction(block->outgoing_address);
|
||||||
if (!block->outgoing_function) {
|
if (!block->outgoing_function) {
|
||||||
printf("call target not found: %.8X\n", block->outgoing_address);
|
XELOGE(XT("call target not found: %.8X"), block->outgoing_address);
|
||||||
|
XEASSERTALWAYS();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -702,16 +747,17 @@ bool RawSymbolDatabase::IsValueInTextRange(uint32_t value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
XexSymbolDatabase::XexSymbolDatabase(
|
XexSymbolDatabase::XexSymbolDatabase(
|
||||||
xe_memory_ref memory, ExportResolver* export_resolver, UserModule* module) :
|
xe_memory_ref memory, ExportResolver* export_resolver, xe_xex2_ref xex) :
|
||||||
SymbolDatabase(memory, export_resolver) {
|
SymbolDatabase(memory, export_resolver) {
|
||||||
module_ = module;
|
xex_ = xe_xex2_retain(xex);
|
||||||
}
|
}
|
||||||
|
|
||||||
XexSymbolDatabase::~XexSymbolDatabase() {
|
XexSymbolDatabase::~XexSymbolDatabase() {
|
||||||
|
xe_xex2_release(xex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int XexSymbolDatabase::Analyze() {
|
int XexSymbolDatabase::Analyze() {
|
||||||
const xe_xex2_header_t *header = module_->xex_header();
|
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||||
|
|
||||||
// Find __savegprlr_* and __restgprlr_*.
|
// Find __savegprlr_* and __restgprlr_*.
|
||||||
FindGplr();
|
FindGplr();
|
||||||
|
@ -785,7 +831,7 @@ int XexSymbolDatabase::FindGplr() {
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t gplr_start = 0;
|
uint32_t gplr_start = 0;
|
||||||
const xe_xex2_header_t* header = module_->xex_header();
|
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||||
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
||||||
const xe_xex2_section_t* section = &header->sections[n];
|
const xe_xex2_section_t* section = &header->sections[n];
|
||||||
const size_t start_address =
|
const size_t start_address =
|
||||||
|
@ -833,12 +879,10 @@ int XexSymbolDatabase::FindGplr() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
|
int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
|
||||||
xe_xex2_ref xex = module_->xex();
|
|
||||||
xe_xex2_import_info_t* import_infos;
|
xe_xex2_import_info_t* import_infos;
|
||||||
size_t import_info_count;
|
size_t import_info_count;
|
||||||
if (xe_xex2_get_import_infos(xex, library, &import_infos,
|
if (xe_xex2_get_import_infos(xex_, library, &import_infos,
|
||||||
&import_info_count)) {
|
&import_info_count)) {
|
||||||
xe_xex2_release(xex);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -878,18 +922,54 @@ int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
|
||||||
}
|
}
|
||||||
|
|
||||||
xe_free(import_infos);
|
xe_free(import_infos);
|
||||||
xe_xex2_release(xex);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int XexSymbolDatabase::AddMethodHints() {
|
int XexSymbolDatabase::AddMethodHints() {
|
||||||
PEMethodInfo* method_infos;
|
uint8_t* mem = xe_memory_addr(memory_, 0);
|
||||||
size_t method_info_count;
|
|
||||||
if (module_->GetMethodHints(&method_infos, &method_info_count)) {
|
const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY* entry = NULL;
|
||||||
return 1;
|
|
||||||
|
// Find pdata, which contains the exception handling entries.
|
||||||
|
const PESection* pdata = xe_xex2_get_pe_section(xex_, ".pdata");
|
||||||
|
if (!pdata) {
|
||||||
|
// No exception data to go on.
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t n = 0; n < method_info_count; n++) {
|
// Resolve.
|
||||||
|
const uint8_t* p = mem + pdata->address;
|
||||||
|
|
||||||
|
// Entry count = pdata size / sizeof(entry).
|
||||||
|
size_t entry_count = pdata->size / sizeof(IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY);
|
||||||
|
if (!entry_count) {
|
||||||
|
// Empty?
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate output.
|
||||||
|
PEMethodInfo* method_infos = (PEMethodInfo*)xe_calloc(
|
||||||
|
entry_count * sizeof(PEMethodInfo));
|
||||||
|
if (!method_infos) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse entries.
|
||||||
|
// NOTE: entries are in memory as big endian, so pull them out and swap the
|
||||||
|
// values before using them.
|
||||||
|
entry = (const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY*)p;
|
||||||
|
IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY temp_entry;
|
||||||
|
for (size_t n = 0; n < entry_count; n++, entry++) {
|
||||||
|
PEMethodInfo* method_info = &method_infos[n];
|
||||||
|
method_info->address = XESWAP32BE(entry->FuncStart);
|
||||||
|
|
||||||
|
// The bitfield needs to be swapped by hand.
|
||||||
|
temp_entry.FlagsValue = XESWAP32BE(entry->FlagsValue);
|
||||||
|
method_info->total_length = temp_entry.Flags.FuncLen * 4;
|
||||||
|
method_info->prolog_length = temp_entry.Flags.PrologLen * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t n = 0; n < entry_count; n++) {
|
||||||
PEMethodInfo* method_info = &method_infos[n];
|
PEMethodInfo* method_info = &method_infos[n];
|
||||||
FunctionSymbol* fn = GetOrInsertFunction(method_info->address);
|
FunctionSymbol* fn = GetOrInsertFunction(method_info->address);
|
||||||
fn->end_address = method_info->address + method_info->total_length - 4;
|
fn->end_address = method_info->address + method_info->total_length - 4;
|
||||||
|
@ -902,12 +982,12 @@ int XexSymbolDatabase::AddMethodHints() {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t XexSymbolDatabase::GetEntryPoint() {
|
uint32_t XexSymbolDatabase::GetEntryPoint() {
|
||||||
const xe_xex2_header_t* header = module_->xex_header();
|
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||||
return header->exe_entry_point;
|
return header->exe_entry_point;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool XexSymbolDatabase::IsValueInTextRange(uint32_t value) {
|
bool XexSymbolDatabase::IsValueInTextRange(uint32_t value) {
|
||||||
const xe_xex2_header_t* header = module_->xex_header();
|
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||||
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
||||||
const xe_xex2_section_t* section = &header->sections[n];
|
const xe_xex2_section_t* section = &header->sections[n];
|
||||||
const size_t start_address =
|
const size_t start_address =
|
||||||
|
|
|
@ -19,11 +19,12 @@ using namespace xe::cpu;
|
||||||
|
|
||||||
ThreadState::ThreadState(
|
ThreadState::ThreadState(
|
||||||
Processor* processor,
|
Processor* processor,
|
||||||
uint32_t stack_address, uint32_t stack_size) {
|
uint32_t stack_size, uint32_t thread_state_address) :
|
||||||
stack_address_ = stack_address;
|
stack_size_(stack_size), thread_state_address_(thread_state_address) {
|
||||||
stack_size_ = stack_size;
|
|
||||||
memory_ = processor->memory();
|
memory_ = processor->memory();
|
||||||
|
|
||||||
|
stack_address_ = xe_memory_heap_alloc(memory_, 0, stack_size, 0);
|
||||||
|
|
||||||
xe_zero_struct(&ppc_state_, sizeof(ppc_state_));
|
xe_zero_struct(&ppc_state_, sizeof(ppc_state_));
|
||||||
|
|
||||||
// Stash pointers to common structures that callbacks may need.
|
// Stash pointers to common structures that callbacks may need.
|
||||||
|
@ -33,9 +34,11 @@ ThreadState::ThreadState(
|
||||||
|
|
||||||
// Set initial registers.
|
// Set initial registers.
|
||||||
ppc_state_.r[1] = stack_address_;
|
ppc_state_.r[1] = stack_address_;
|
||||||
|
ppc_state_.r[13] = thread_state_address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState::~ThreadState() {
|
ThreadState::~ThreadState() {
|
||||||
|
xe_memory_heap_free(memory_, stack_address_, 0);
|
||||||
xe_memory_release(memory_);
|
xe_memory_release(memory_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,17 +19,16 @@ using namespace xe::kernel;
|
||||||
using namespace xe::kernel::xam;
|
using namespace xe::kernel::xam;
|
||||||
|
|
||||||
|
|
||||||
XamModule::XamModule(xe_pal_ref pal, xe_memory_ref memory,
|
XamModule::XamModule(Runtime* runtime) :
|
||||||
shared_ptr<ExportResolver> resolver) :
|
KernelModule(runtime) {
|
||||||
KernelModule(pal, memory, resolver) {
|
export_resolver_->RegisterTable(
|
||||||
resolver->RegisterTable(
|
|
||||||
"xam.xex", xam_export_table, XECOUNT(xam_export_table));
|
"xam.xex", xam_export_table, XECOUNT(xam_export_table));
|
||||||
|
|
||||||
// Setup the xam state instance.
|
// Setup the xam state instance.
|
||||||
xam_state = auto_ptr<XamState>(new XamState(pal, memory, resolver));
|
xam_state = auto_ptr<XamState>(new XamState(pal_, memory_, export_resolver_));
|
||||||
|
|
||||||
// Register all exported functions.
|
// Register all exported functions.
|
||||||
RegisterInfoExports(resolver.get(), xam_state.get());
|
RegisterInfoExports(export_resolver_.get(), xam_state.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
XamModule::~XamModule() {
|
XamModule::~XamModule() {
|
||||||
|
|
|
@ -26,8 +26,7 @@ class XamState;
|
||||||
|
|
||||||
class XamModule : public KernelModule {
|
class XamModule : public KernelModule {
|
||||||
public:
|
public:
|
||||||
XamModule(xe_pal_ref pal, xe_memory_ref memory,
|
XamModule(Runtime* runtime);
|
||||||
shared_ptr<ExportResolver> resolver);
|
|
||||||
virtual ~XamModule();
|
virtual ~XamModule();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include "kernel/modules/xboxkrnl/kernel_state.h"
|
#include "kernel/modules/xboxkrnl/kernel_state.h"
|
||||||
|
|
||||||
|
#include "kernel/modules/xboxkrnl/xobject.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
|
@ -20,14 +22,88 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
KernelState::KernelState(xe_pal_ref pal, xe_memory_ref memory,
|
KernelState::KernelState(Runtime* runtime) :
|
||||||
shared_ptr<ExportResolver> export_resolver) {
|
runtime_(runtime),
|
||||||
this->pal = xe_pal_retain(pal);
|
next_handle_(0) {
|
||||||
this->memory = xe_memory_retain(memory);
|
pal_ = runtime->pal();
|
||||||
export_resolver_ = export_resolver;
|
memory_ = runtime->memory();
|
||||||
|
processor_ = runtime->processor();
|
||||||
|
|
||||||
|
objects_mutex_ = xe_mutex_alloc(0);
|
||||||
|
XEASSERTNOTNULL(objects_mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelState::~KernelState() {
|
KernelState::~KernelState() {
|
||||||
xe_memory_release(memory);
|
// Delete all objects.
|
||||||
xe_pal_release(pal);
|
// We first copy the list to another list so that the deletion of the objects
|
||||||
|
// doesn't mess up iteration.
|
||||||
|
std::vector<XObject*> all_objects;
|
||||||
|
xe_mutex_lock(objects_mutex_);
|
||||||
|
for (std::tr1::unordered_map<X_HANDLE, XObject*>::iterator it =
|
||||||
|
objects_.begin(); it != objects_.end(); ++it) {
|
||||||
|
all_objects.push_back(it->second);
|
||||||
|
}
|
||||||
|
objects_.clear();
|
||||||
|
xe_mutex_unlock(objects_mutex_);
|
||||||
|
for (std::vector<XObject*>::iterator it = all_objects.begin();
|
||||||
|
it != all_objects.end(); ++it) {
|
||||||
|
// Perhaps call a special ForceRelease method or something?
|
||||||
|
XObject* obj = *it;
|
||||||
|
delete obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
xe_mutex_free(objects_mutex_);
|
||||||
|
objects_mutex_ = NULL;
|
||||||
|
|
||||||
|
processor_.reset();
|
||||||
|
xe_memory_release(memory_);
|
||||||
|
xe_pal_release(pal_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Runtime* KernelState::runtime() {
|
||||||
|
return runtime_;
|
||||||
|
}
|
||||||
|
|
||||||
|
xe_pal_ref KernelState::pal() {
|
||||||
|
return pal_;
|
||||||
|
}
|
||||||
|
|
||||||
|
xe_memory_ref KernelState::memory() {
|
||||||
|
return memory_;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu::Processor* KernelState::processor() {
|
||||||
|
return processor_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(benvanik): invesitgate better handle storage/structure.
|
||||||
|
// A much better way of doing handles, if performance becomes an issue, would
|
||||||
|
// be to try to make the pointers 32bit. Then we could round-trip them through
|
||||||
|
// PPC code without needing to keep a map.
|
||||||
|
// To achieve this we could try doing allocs in the 32-bit address space via
|
||||||
|
// the OS alloc calls, or maybe create a section with a reserved size at load
|
||||||
|
// time (65k handles * 4 is more than enough?).
|
||||||
|
// We could then use a free list of handle IDs and allocate/release lock-free.
|
||||||
|
|
||||||
|
XObject* KernelState::GetObject(X_HANDLE handle) {
|
||||||
|
xe_mutex_lock(objects_mutex_);
|
||||||
|
std::tr1::unordered_map<X_HANDLE, XObject*>::iterator it =
|
||||||
|
objects_.find(handle);
|
||||||
|
XObject* value = it != objects_.end() ? it->second : NULL;
|
||||||
|
xe_mutex_unlock(objects_mutex_);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_HANDLE KernelState::InsertObject(XObject* obj) {
|
||||||
|
xe_mutex_lock(objects_mutex_);
|
||||||
|
X_HANDLE handle = 0x00001000 + (++next_handle_);
|
||||||
|
objects_.insert(std::pair<X_HANDLE, XObject*>(handle, obj));
|
||||||
|
xe_mutex_unlock(objects_mutex_);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KernelState::RemoveObject(XObject* obj) {
|
||||||
|
xe_mutex_lock(objects_mutex_);
|
||||||
|
objects_.erase(obj->handle());
|
||||||
|
xe_mutex_unlock(objects_mutex_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include <xenia/kernel/export.h>
|
#include <xenia/kernel/export.h>
|
||||||
#include <xenia/kernel/kernel_module.h>
|
#include <xenia/kernel/kernel_module.h>
|
||||||
|
#include <xenia/kernel/xbox.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -22,17 +23,35 @@ namespace kernel {
|
||||||
namespace xboxkrnl {
|
namespace xboxkrnl {
|
||||||
|
|
||||||
|
|
||||||
|
class XObject;
|
||||||
|
|
||||||
|
|
||||||
class KernelState {
|
class KernelState {
|
||||||
public:
|
public:
|
||||||
KernelState(xe_pal_ref pal, xe_memory_ref memory,
|
KernelState(Runtime* runtime);
|
||||||
shared_ptr<ExportResolver> export_resolver);
|
|
||||||
~KernelState();
|
~KernelState();
|
||||||
|
|
||||||
xe_pal_ref pal;
|
XObject* GetObject(X_HANDLE handle);
|
||||||
xe_memory_ref memory;
|
|
||||||
|
Runtime* runtime();
|
||||||
|
xe_pal_ref pal();
|
||||||
|
xe_memory_ref memory();
|
||||||
|
cpu::Processor* processor();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
shared_ptr<ExportResolver> export_resolver_;
|
X_HANDLE InsertObject(XObject* obj);
|
||||||
|
void RemoveObject(XObject* obj);
|
||||||
|
|
||||||
|
Runtime* runtime_;
|
||||||
|
xe_pal_ref pal_;
|
||||||
|
xe_memory_ref memory_;
|
||||||
|
shared_ptr<cpu::Processor> processor_;
|
||||||
|
|
||||||
|
xe_mutex_t* objects_mutex_;
|
||||||
|
X_HANDLE next_handle_;
|
||||||
|
std::tr1::unordered_map<X_HANDLE, XObject*> objects_;
|
||||||
|
|
||||||
|
friend class XObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include "kernel/modules/xboxkrnl/module.h"
|
#include "kernel/modules/xboxkrnl/module.h"
|
||||||
|
|
||||||
#include "kernel/modules/xboxkrnl/kernel_state.h"
|
#include "kernel/modules/xboxkrnl/kernel_state.h"
|
||||||
|
#include "kernel/modules/xboxkrnl/objects/xmodule.h"
|
||||||
|
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl_hal.h"
|
#include "kernel/modules/xboxkrnl/xboxkrnl_hal.h"
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl_memory.h"
|
#include "kernel/modules/xboxkrnl/xboxkrnl_memory.h"
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl_module.h"
|
#include "kernel/modules/xboxkrnl/xboxkrnl_module.h"
|
||||||
|
@ -29,27 +31,28 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
XboxkrnlModule::XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory,
|
XboxkrnlModule::XboxkrnlModule(Runtime* runtime) :
|
||||||
shared_ptr<ExportResolver> resolver) :
|
KernelModule(runtime) {
|
||||||
KernelModule(pal, memory, resolver) {
|
ExportResolver* resolver = export_resolver_.get();
|
||||||
|
|
||||||
resolver->RegisterTable(
|
resolver->RegisterTable(
|
||||||
"xboxkrnl.exe", xboxkrnl_export_table, XECOUNT(xboxkrnl_export_table));
|
"xboxkrnl.exe", xboxkrnl_export_table, XECOUNT(xboxkrnl_export_table));
|
||||||
|
|
||||||
// Setup the kernel state instance.
|
// Setup the kernel state instance.
|
||||||
// This is where all kernel objects are kept while running.
|
// This is where all kernel objects are kept while running.
|
||||||
kernel_state = auto_ptr<KernelState>(new KernelState(pal, memory, resolver));
|
kernel_state_ = auto_ptr<KernelState>(new KernelState(runtime));
|
||||||
|
|
||||||
// Register all exported functions.
|
// Register all exported functions.
|
||||||
RegisterHalExports(resolver.get(), kernel_state.get());
|
RegisterHalExports(resolver, kernel_state_.get());
|
||||||
RegisterMemoryExports(resolver.get(), kernel_state.get());
|
RegisterMemoryExports(resolver, kernel_state_.get());
|
||||||
RegisterModuleExports(resolver.get(), kernel_state.get());
|
RegisterModuleExports(resolver, kernel_state_.get());
|
||||||
RegisterRtlExports(resolver.get(), kernel_state.get());
|
RegisterRtlExports(resolver, kernel_state_.get());
|
||||||
RegisterThreadingExports(resolver.get(), kernel_state.get());
|
RegisterThreadingExports(resolver, kernel_state_.get());
|
||||||
|
|
||||||
// TODO(benvanik): alloc heap memory somewhere in user space
|
// TODO(benvanik): alloc heap memory somewhere in user space
|
||||||
// TODO(benvanik): tools for reading/writing to heap memory
|
// TODO(benvanik): tools for reading/writing to heap memory
|
||||||
|
|
||||||
uint8_t* mem = xe_memory_addr(memory, 0);
|
uint8_t* mem = xe_memory_addr(memory_, 0);
|
||||||
|
|
||||||
// KeDebugMonitorData (?*)
|
// KeDebugMonitorData (?*)
|
||||||
// I'm not sure what this is for, but games make sure it's not null and
|
// I'm not sure what this is for, but games make sure it's not null and
|
||||||
|
@ -100,3 +103,42 @@ XboxkrnlModule::XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory,
|
||||||
|
|
||||||
XboxkrnlModule::~XboxkrnlModule() {
|
XboxkrnlModule::~XboxkrnlModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int XboxkrnlModule::LaunchModule(const xechar_t* path) {
|
||||||
|
// TODO(benvanik): setup the virtual filesystem map and use LoadFromFile.
|
||||||
|
|
||||||
|
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
|
||||||
|
if (!mmap) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
void* addr = xe_mmap_get_addr(mmap);
|
||||||
|
size_t length = xe_mmap_get_length(mmap);
|
||||||
|
|
||||||
|
char path_a[2048];
|
||||||
|
XEIGNORE(xestrnarrow(path_a, XECOUNT(path_a), path));
|
||||||
|
|
||||||
|
// Create and load the module.
|
||||||
|
XModule* module = new XModule(kernel_state_.get(), path_a);
|
||||||
|
X_STATUS status = module->LoadFromMemory(addr, length);
|
||||||
|
|
||||||
|
// TODO(benvanik): retain memory somehow? is it needed?
|
||||||
|
xe_mmap_release(mmap);
|
||||||
|
|
||||||
|
if (XFAILED(status)) {
|
||||||
|
XELOGE(XT("Failed to load module"));
|
||||||
|
module->Release();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch the module.
|
||||||
|
status = module->Launch(0);
|
||||||
|
if (XFAILED(status)) {
|
||||||
|
XELOGE(XT("Failed to launch module"));
|
||||||
|
module->Release();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
module->Release();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -26,12 +26,13 @@ class KernelState;
|
||||||
|
|
||||||
class XboxkrnlModule : public KernelModule {
|
class XboxkrnlModule : public KernelModule {
|
||||||
public:
|
public:
|
||||||
XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory,
|
XboxkrnlModule(Runtime* runtime);
|
||||||
shared_ptr<ExportResolver> resolver);
|
|
||||||
virtual ~XboxkrnlModule();
|
virtual ~XboxkrnlModule();
|
||||||
|
|
||||||
|
int LaunchModule(const xechar_t* path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto_ptr<KernelState> kernel_state;
|
auto_ptr<KernelState> kernel_state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||||
|
{
|
||||||
|
'sources': [
|
||||||
|
'xmodule.cc',
|
||||||
|
'xthread.cc',
|
||||||
|
],
|
||||||
|
}
|
|
@ -7,244 +7,124 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <xenia/kernel/user_module.h>
|
#include "kernel/modules/xboxkrnl/objects/xmodule.h"
|
||||||
|
|
||||||
#include <third_party/pe/pe_image.h>
|
#include "kernel/modules/xboxkrnl/objects/xthread.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
using namespace kernel;
|
using namespace xe::kernel;
|
||||||
|
using namespace xe::kernel::xboxkrnl;
|
||||||
|
|
||||||
|
|
||||||
UserModule::UserModule(xe_memory_ref memory) {
|
namespace {
|
||||||
memory_ = xe_memory_retain(memory);
|
|
||||||
xex_ = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UserModule::~UserModule() {
|
|
||||||
for (std::vector<PESection*>::iterator it = sections_.begin();
|
|
||||||
it != sections_.end(); ++it) {
|
|
||||||
delete *it;
|
|
||||||
}
|
|
||||||
|
|
||||||
xe_xex2_release(xex_);
|
XModule::XModule(KernelState* kernel_state, const char* path) :
|
||||||
xe_memory_release(memory_);
|
XObject(kernel_state, kTypeModule),
|
||||||
}
|
xex_(NULL) {
|
||||||
|
|
||||||
int UserModule::Load(const void* addr, const size_t length,
|
|
||||||
const xechar_t* path) {
|
|
||||||
XEIGNORE(xestrcpy(path_, XECOUNT(path_), path));
|
XEIGNORE(xestrcpy(path_, XECOUNT(path_), path));
|
||||||
const xechar_t *slash = xestrrchr(path, '/');
|
const xechar_t *slash = xestrrchr(path, '/');
|
||||||
if (slash) {
|
if (slash) {
|
||||||
XEIGNORE(xestrcpy(name_, XECOUNT(name_), slash + 1));
|
XEIGNORE(xestrcpy(name_, XECOUNT(name_), slash + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
xe_xex2_options_t xex_options;
|
|
||||||
xex_ = xe_xex2_load(memory_, addr, length, xex_options);
|
|
||||||
XEEXPECTNOTNULL(xex_);
|
|
||||||
|
|
||||||
XEEXPECTZERO(LoadPE());
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
XECLEANUP:
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const xechar_t* UserModule::path() {
|
XModule::~XModule() {
|
||||||
|
xe_xex2_release(xex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* XModule::path() {
|
||||||
return path_;
|
return path_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const xechar_t* UserModule::name() {
|
const char* XModule::name() {
|
||||||
return name_;
|
return name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t UserModule::handle() {
|
xe_xex2_ref XModule::xex() {
|
||||||
return handle_;
|
|
||||||
}
|
|
||||||
|
|
||||||
xe_xex2_ref UserModule::xex() {
|
|
||||||
return xe_xex2_retain(xex_);
|
return xe_xex2_retain(xex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
const xe_xex2_header_t* UserModule::xex_header() {
|
const xe_xex2_header_t* XModule::xex_header() {
|
||||||
return xe_xex2_get_header(xex_);
|
return xe_xex2_get_header(xex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* UserModule::GetProcAddress(const uint32_t ordinal) {
|
X_STATUS XModule::LoadFromFile(const char* path) {
|
||||||
|
// TODO(benvanik): load from virtual filesystem.
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
return NULL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMAGE_CE_RUNTIME_FUNCTION_ENTRY
|
X_STATUS XModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||||
// http://msdn.microsoft.com/en-us/library/ms879748.aspx
|
// Load the XEX into memory and decrypt.
|
||||||
typedef struct IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY_t {
|
xe_xex2_options_t xex_options;
|
||||||
uint32_t FuncStart; // Virtual address
|
xex_ = xe_xex2_load(kernel_state()->memory(), addr, length, xex_options);
|
||||||
union {
|
XEEXPECTNOTNULL(xex_);
|
||||||
struct {
|
|
||||||
uint32_t PrologLen : 8; // # of prolog instructions (size = x4)
|
|
||||||
uint32_t FuncLen : 22; // # of instructions total (size = x4)
|
|
||||||
uint32_t ThirtyTwoBit : 1; // Always 1
|
|
||||||
uint32_t ExceptionFlag : 1; // 1 if PDATA_EH in .text -- unknown if used
|
|
||||||
} Flags;
|
|
||||||
uint32_t FlagsValue; // To make byte swapping easier
|
|
||||||
};
|
|
||||||
} IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY;
|
|
||||||
|
|
||||||
int UserModule::LoadPE() {
|
// Prepare the module for execution.
|
||||||
const xe_xex2_header_t* xex_header = xe_xex2_get_header(xex_);
|
XEEXPECTZERO(kernel_state()->processor()->PrepareModule(
|
||||||
uint8_t* mem = xe_memory_addr(memory_, 0);
|
name_, path_, xex_, runtime()->export_resolver()));
|
||||||
const uint8_t* p = mem + xex_header->exe_address;
|
|
||||||
|
|
||||||
// Verify DOS signature (MZ).
|
return X_STATUS_SUCCESS;
|
||||||
const IMAGE_DOS_HEADER* doshdr = (const IMAGE_DOS_HEADER*)p;
|
|
||||||
if (doshdr->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the NT header offset from the DOS header.
|
|
||||||
p += doshdr->e_lfanew;
|
|
||||||
|
|
||||||
// Verify NT signature (PE\0\0).
|
|
||||||
const IMAGE_NT_HEADERS32* nthdr = (const IMAGE_NT_HEADERS32*)(p);
|
|
||||||
if (nthdr->Signature != IMAGE_NT_SIGNATURE) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify matches an Xbox PE.
|
|
||||||
const IMAGE_FILE_HEADER* filehdr = &nthdr->FileHeader;
|
|
||||||
if ((filehdr->Machine != IMAGE_FILE_MACHINE_POWERPCBE) ||
|
|
||||||
!(filehdr->Characteristics & IMAGE_FILE_32BIT_MACHINE)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// Verify the expected size.
|
|
||||||
if (filehdr->SizeOfOptionalHeader != IMAGE_SIZEOF_NT_OPTIONAL_HEADER) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify optional header is 32bit.
|
|
||||||
const IMAGE_OPTIONAL_HEADER32* opthdr = &nthdr->OptionalHeader;
|
|
||||||
if (opthdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// Verify subsystem.
|
|
||||||
if (opthdr->Subsystem != IMAGE_SUBSYSTEM_XBOX) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Linker version - likely 8+
|
|
||||||
// Could be useful for recognizing certain patterns
|
|
||||||
//opthdr->MajorLinkerVersion; opthdr->MinorLinkerVersion;
|
|
||||||
|
|
||||||
// Data directories of interest:
|
|
||||||
// EXPORT IMAGE_EXPORT_DIRECTORY
|
|
||||||
// IMPORT IMAGE_IMPORT_DESCRIPTOR[]
|
|
||||||
// EXCEPTION IMAGE_CE_RUNTIME_FUNCTION_ENTRY[]
|
|
||||||
// BASERELOC
|
|
||||||
// DEBUG IMAGE_DEBUG_DIRECTORY[]
|
|
||||||
// ARCHITECTURE /IMAGE_ARCHITECTURE_HEADER/ ----- import thunks!
|
|
||||||
// TLS IMAGE_TLS_DIRECTORY
|
|
||||||
// IAT Import Address Table ptr
|
|
||||||
//opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_X].VirtualAddress / .Size
|
|
||||||
|
|
||||||
// Quick scan to determine bounds of sections.
|
|
||||||
size_t upper_address = 0;
|
|
||||||
const IMAGE_SECTION_HEADER* sechdr = IMAGE_FIRST_SECTION(nthdr);
|
|
||||||
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
|
|
||||||
const size_t physical_address = opthdr->ImageBase + sechdr->VirtualAddress;
|
|
||||||
upper_address =
|
|
||||||
MAX(upper_address, physical_address + sechdr->Misc.VirtualSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup/load sections.
|
|
||||||
sechdr = IMAGE_FIRST_SECTION(nthdr);
|
|
||||||
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
|
|
||||||
PESection* section = (PESection*)xe_calloc(sizeof(PESection));
|
|
||||||
xe_copy_memory(section->name, sizeof(section->name),
|
|
||||||
sechdr->Name, sizeof(sechdr->Name));
|
|
||||||
section->name[8] = 0;
|
|
||||||
section->raw_address = sechdr->PointerToRawData;
|
|
||||||
section->raw_size = sechdr->SizeOfRawData;
|
|
||||||
section->address = xex_header->exe_address + sechdr->VirtualAddress;
|
|
||||||
section->size = sechdr->Misc.VirtualSize;
|
|
||||||
section->flags = sechdr->Characteristics;
|
|
||||||
sections_.push_back(section);
|
|
||||||
}
|
|
||||||
|
|
||||||
//DumpTLSDirectory(pImageBase, pNTHeader, (PIMAGE_TLS_DIRECTORY32)0);
|
|
||||||
//DumpExportsSection(pImageBase, pNTHeader);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PESection* UserModule::GetSection(const char *name) {
|
|
||||||
for (std::vector<PESection*>::iterator it = sections_.begin();
|
|
||||||
it != sections_.end(); ++it) {
|
|
||||||
if (!xestrcmpa((*it)->name, name)) {
|
|
||||||
return *it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int UserModule::GetMethodHints(PEMethodInfo** out_method_infos,
|
|
||||||
size_t* out_method_info_count) {
|
|
||||||
uint8_t* mem = xe_memory_addr(memory_, 0);
|
|
||||||
|
|
||||||
*out_method_infos = NULL;
|
|
||||||
*out_method_info_count = 0;
|
|
||||||
|
|
||||||
const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY* entry = NULL;
|
|
||||||
|
|
||||||
// Find pdata, which contains the exception handling entries.
|
|
||||||
PESection* pdata = GetSection(".pdata");
|
|
||||||
if (!pdata) {
|
|
||||||
// No exception data to go on.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve.
|
|
||||||
const uint8_t* p = mem + pdata->address;
|
|
||||||
|
|
||||||
// Entry count = pdata size / sizeof(entry).
|
|
||||||
size_t entry_count = pdata->size / sizeof(IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY);
|
|
||||||
if (!entry_count) {
|
|
||||||
// Empty?
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate output.
|
|
||||||
PEMethodInfo* method_infos = (PEMethodInfo*)xe_calloc(
|
|
||||||
entry_count * sizeof(PEMethodInfo));
|
|
||||||
XEEXPECTNOTNULL(method_infos);
|
|
||||||
|
|
||||||
// Parse entries.
|
|
||||||
// NOTE: entries are in memory as big endian, so pull them out and swap the
|
|
||||||
// values before using them.
|
|
||||||
entry = (const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY*)p;
|
|
||||||
IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY temp_entry;
|
|
||||||
for (size_t n = 0; n < entry_count; n++, entry++) {
|
|
||||||
PEMethodInfo* method_info = &method_infos[n];
|
|
||||||
method_info->address = XESWAP32BE(entry->FuncStart);
|
|
||||||
|
|
||||||
// The bitfield needs to be swapped by hand.
|
|
||||||
temp_entry.FlagsValue = XESWAP32BE(entry->FlagsValue);
|
|
||||||
method_info->total_length = temp_entry.Flags.FuncLen * 4;
|
|
||||||
method_info->prolog_length = temp_entry.Flags.PrologLen * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_method_infos = method_infos;
|
|
||||||
*out_method_info_count = entry_count;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
XECLEANUP:
|
XECLEANUP:
|
||||||
if (method_infos) {
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
xe_free(method_infos);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserModule::Dump(ExportResolver* export_resolver) {
|
X_STATUS XModule::GetSection(const char* name,
|
||||||
//const uint8_t *mem = (const uint8_t*)xe_memory_addr(memory_, 0);
|
uint32_t* out_data, uint32_t* out_size) {
|
||||||
|
const PESection* section = xe_xex2_get_pe_section(xex_, name);
|
||||||
|
if (!section) {
|
||||||
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
*out_data = section->address;
|
||||||
|
*out_size = section->size;
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* XModule::GetProcAddressByOrdinal(uint16_t ordinal) {
|
||||||
|
// TODO(benvanik): check export tables.
|
||||||
|
XELOGE(XT("GetProcAddressByOrdinal not implemented"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS XModule::Launch(uint32_t flags) {
|
||||||
|
const xe_xex2_header_t* header = xex_header();
|
||||||
|
|
||||||
|
// TODO(benvanik): set as main module/etc
|
||||||
|
// xekXexExecutableModuleHandle = xe_module_get_handle(module);
|
||||||
|
|
||||||
|
// Create a thread to run in.
|
||||||
|
XThread* thread = new XThread(
|
||||||
|
kernel_state(),
|
||||||
|
header->exe_stack_size,
|
||||||
|
0,
|
||||||
|
header->exe_entry_point, NULL,
|
||||||
|
0);
|
||||||
|
|
||||||
|
X_STATUS result = thread->Create();
|
||||||
|
if (XFAILED(result)) {
|
||||||
|
XELOGE(XT("Could not create launch thread: %.8X"), result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until thread completes.
|
||||||
|
// XLARGE_INTEGER timeout = XINFINITE;
|
||||||
|
// xekNtWaitForSingleObjectEx(thread_handle, TRUE, &timeout);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread->Release();
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XModule::Dump() {
|
||||||
|
ExportResolver* export_resolver = runtime()->export_resolver().get();
|
||||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||||
|
|
||||||
// XEX info.
|
// XEX info.
|
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_XMODULE_H_
|
||||||
|
#define XENIA_KERNEL_MODULES_XBOXKRNL_XMODULE_H_
|
||||||
|
|
||||||
|
#include "kernel/modules/xboxkrnl/xobject.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <xenia/kernel/export.h>
|
||||||
|
#include <xenia/kernel/xbox.h>
|
||||||
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
namespace xboxkrnl {
|
||||||
|
|
||||||
|
|
||||||
|
class XModule : public XObject {
|
||||||
|
public:
|
||||||
|
XModule(KernelState* kernel_state, const char* path);
|
||||||
|
virtual ~XModule();
|
||||||
|
|
||||||
|
const char* path();
|
||||||
|
const char* name();
|
||||||
|
xe_xex2_ref xex();
|
||||||
|
const xe_xex2_header_t* xex_header();
|
||||||
|
|
||||||
|
X_STATUS LoadFromFile(const char* path);
|
||||||
|
X_STATUS LoadFromMemory(const void* addr, const size_t length);
|
||||||
|
|
||||||
|
X_STATUS GetSection(const char* name, uint32_t* out_data, uint32_t* out_size);
|
||||||
|
void* GetProcAddressByOrdinal(uint16_t ordinal);
|
||||||
|
|
||||||
|
X_STATUS Launch(uint32_t flags);
|
||||||
|
|
||||||
|
void Dump();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int LoadPE();
|
||||||
|
|
||||||
|
char name_[256];
|
||||||
|
char path_[2048];
|
||||||
|
|
||||||
|
xe_xex2_ref xex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace xboxkrnl
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
|
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_XMODULE_H_
|
|
@ -0,0 +1,232 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/modules/xboxkrnl/objects/xthread.h"
|
||||||
|
|
||||||
|
|
||||||
|
using namespace xe;
|
||||||
|
using namespace xe::kernel;
|
||||||
|
using namespace xe::kernel::xboxkrnl;
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static uint32_t next_xthread_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) :
|
||||||
|
XObject(kernel_state, kTypeThread),
|
||||||
|
thread_id_(++next_xthread_id),
|
||||||
|
thread_handle_(0),
|
||||||
|
thread_state_address_(0),
|
||||||
|
processor_state_(0) {
|
||||||
|
creation_params_.stack_size = stack_size;
|
||||||
|
creation_params_.xapi_thread_startup = xapi_thread_startup;
|
||||||
|
creation_params_.start_address = start_address;
|
||||||
|
creation_params_.start_context = start_context;
|
||||||
|
creation_params_.creation_flags = creation_flags;
|
||||||
|
|
||||||
|
// Adjust stack size - min of 16k.
|
||||||
|
if (creation_params_.stack_size < 16 * 1024 * 1024) {
|
||||||
|
creation_params_.stack_size = 16 * 1024 * 1024;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XThread::~XThread() {
|
||||||
|
// TODO(benvanik): if executing, kill it?
|
||||||
|
|
||||||
|
if (processor_state_) {
|
||||||
|
kernel_state()->processor()->DeallocThread(processor_state_);
|
||||||
|
}
|
||||||
|
if (thread_state_address_) {
|
||||||
|
xe_memory_heap_free(kernel_state()->memory(), thread_state_address_, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread_handle_) {
|
||||||
|
// TODO(benvanik): platform kill
|
||||||
|
XELOGE(XT("Thread disposed without exiting"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t XThread::thread_id() {
|
||||||
|
return thread_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t XThread::last_error() {
|
||||||
|
uint8_t *p = xe_memory_addr(memory(), thread_state_address_);
|
||||||
|
return XEGETUINT32BE(p + 0x160);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XThread::set_last_error(uint32_t error_code) {
|
||||||
|
uint8_t *p = xe_memory_addr(memory(), thread_state_address_);
|
||||||
|
XESETUINT32BE(p + 0x160, error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS XThread::Create() {
|
||||||
|
// Allocate thread state block from heap.
|
||||||
|
// This is set as r13 for user code and some special inlined Win32 calls
|
||||||
|
// (like GetLastError/etc) will poke it directly.
|
||||||
|
// We try to use it as our primary store of data just to keep things all
|
||||||
|
// consistent.
|
||||||
|
thread_state_address_ = xe_memory_heap_alloc(memory(), 0, 2048, 0);
|
||||||
|
if (!thread_state_address_) {
|
||||||
|
XELOGW(XT("Unable to allocate thread state block"));
|
||||||
|
return X_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the thread state block (last error/etc).
|
||||||
|
// 0x100: pointer to self?
|
||||||
|
// 0x14C: thread id
|
||||||
|
// 0x150: if >0 then error states don't get set
|
||||||
|
// 0x160: last error
|
||||||
|
// So, at offset 0x100 we have a 4b pointer to offset 200, then have the
|
||||||
|
// structure.
|
||||||
|
uint8_t *p = xe_memory_addr(memory(), thread_state_address_);
|
||||||
|
XESETUINT32BE(p + 0x100, thread_state_address_);
|
||||||
|
XESETUINT32BE(p + 0x14C, thread_id_);
|
||||||
|
XESETUINT32BE(p + 0x150, 0); // ?
|
||||||
|
XESETUINT32BE(p + 0x160, 0); // last error
|
||||||
|
|
||||||
|
// Allocate processor thread state.
|
||||||
|
// This is thread safe.
|
||||||
|
processor_state_ = kernel_state()->processor()->AllocThread(
|
||||||
|
creation_params_.stack_size, thread_state_address_);
|
||||||
|
if (!processor_state_) {
|
||||||
|
XELOGW(XT("Unable to allocate processor thread state"));
|
||||||
|
return X_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS return_code = PlatformCreate();
|
||||||
|
if (XFAILED(return_code)) {
|
||||||
|
XELOGW(XT("Unable to create platform thread (%.8X)"), return_code);
|
||||||
|
return return_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS XThread::Exit(int exit_code) {
|
||||||
|
// TODO(benvanik): set exit code in thread state block
|
||||||
|
// TODO(benvanik); dispatch events? waiters? etc?
|
||||||
|
|
||||||
|
// NOTE: unless PlatformExit fails, expect it to never return!
|
||||||
|
X_STATUS return_code = PlatformExit(exit_code);
|
||||||
|
if (XFAILED(return_code)) {
|
||||||
|
return return_code;
|
||||||
|
}
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if XE_PLATFORM(WIN32)
|
||||||
|
|
||||||
|
static uint32_t __stdcall XThreadStartCallbackWin32(void* param) {
|
||||||
|
XThread* thread = reinterpret_cast<XThread*>(param);
|
||||||
|
thread->Execute();
|
||||||
|
thread->Release();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS XThread::PlatformCreate() {
|
||||||
|
XEASSERTALWAYS();
|
||||||
|
|
||||||
|
thread_handle_ = CreateThread(
|
||||||
|
NULL,
|
||||||
|
creation_params_.stack_size,
|
||||||
|
(LPTHREAD_START_ROUTINE)XThreadStartCallbackWin32,
|
||||||
|
this,
|
||||||
|
creation_params.creation_flags,
|
||||||
|
NULL);
|
||||||
|
if (!handle) {
|
||||||
|
uint32_t last_error = GetLastError();
|
||||||
|
// TODO(benvanik): translate?
|
||||||
|
XELOGE(XT("CreateThread failed with %d"), last_error);
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS XThread::PlatformExit(int exit_code) {
|
||||||
|
// NOTE: does not return.
|
||||||
|
ExitThread(exit_code);
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void* XThreadStartCallbackPthreads(void* param) {
|
||||||
|
XThread* thread = reinterpret_cast<XThread*>(param);
|
||||||
|
thread->Execute();
|
||||||
|
thread->Release();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS XThread::PlatformCreate() {
|
||||||
|
pthread_attr_t attr;
|
||||||
|
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_setstacksize(&attr, creation_params_.stack_size);
|
||||||
|
|
||||||
|
int result_code;
|
||||||
|
if (creation_params_.creation_flags & X_CREATE_SUSPENDED) {
|
||||||
|
result_code = pthread_create_suspended_np(
|
||||||
|
reinterpret_cast<pthread_t*>(&thread_handle_),
|
||||||
|
&attr,
|
||||||
|
&XThreadStartCallbackPthreads,
|
||||||
|
this);
|
||||||
|
} else {
|
||||||
|
result_code = pthread_create(
|
||||||
|
reinterpret_cast<pthread_t*>(&thread_handle_),
|
||||||
|
&attr,
|
||||||
|
&XThreadStartCallbackPthreads,
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
|
|
||||||
|
switch (result_code) {
|
||||||
|
case 0:
|
||||||
|
// Succeeded!
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
default:
|
||||||
|
case EAGAIN:
|
||||||
|
return X_STATUS_NO_MEMORY;
|
||||||
|
case EINVAL:
|
||||||
|
case EPERM:
|
||||||
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS XThread::PlatformExit(int exit_code) {
|
||||||
|
// NOTE: does not return.
|
||||||
|
pthread_exit((void*)exit_code);
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WIN32
|
||||||
|
|
||||||
|
void XThread::Execute() {
|
||||||
|
// Run XapiThreadStartup first, if present.
|
||||||
|
if (creation_params_.xapi_thread_startup) {
|
||||||
|
XELOGE(XT("xapi_thread_startup not implemented"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run user code.
|
||||||
|
int exit_code = kernel_state()->processor()->Execute(
|
||||||
|
processor_state_,
|
||||||
|
creation_params_.start_address, creation_params_.start_context);
|
||||||
|
|
||||||
|
// If we got here it means the execute completed without an exit being called.
|
||||||
|
// Treat the return code as an implicit exit code.
|
||||||
|
Exit(exit_code);
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_XTHREAD_H_
|
||||||
|
#define XENIA_KERNEL_MODULES_XBOXKRNL_XTHREAD_H_
|
||||||
|
|
||||||
|
#include "kernel/modules/xboxkrnl/xobject.h"
|
||||||
|
|
||||||
|
#include <xenia/kernel/xbox.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
namespace xboxkrnl {
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
virtual ~XThread();
|
||||||
|
|
||||||
|
uint32_t thread_id();
|
||||||
|
uint32_t last_error();
|
||||||
|
void set_last_error(uint32_t error_code);
|
||||||
|
|
||||||
|
X_STATUS Create();
|
||||||
|
X_STATUS Exit(int exit_code);
|
||||||
|
|
||||||
|
void Execute();
|
||||||
|
|
||||||
|
private:
|
||||||
|
X_STATUS PlatformCreate();
|
||||||
|
X_STATUS PlatformExit(int exit_code);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint32_t stack_size;
|
||||||
|
uint32_t xapi_thread_startup;
|
||||||
|
uint32_t start_address;
|
||||||
|
uint32_t start_context;
|
||||||
|
uint32_t creation_flags;
|
||||||
|
} creation_params_;
|
||||||
|
|
||||||
|
uint32_t thread_id_;
|
||||||
|
void* thread_handle_;
|
||||||
|
uint32_t thread_state_address_;
|
||||||
|
cpu::ThreadState* processor_state_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace xboxkrnl
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
|
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_XTHREAD_H_
|
|
@ -8,5 +8,10 @@
|
||||||
'xboxkrnl_module.cc',
|
'xboxkrnl_module.cc',
|
||||||
'xboxkrnl_rtl.cc',
|
'xboxkrnl_rtl.cc',
|
||||||
'xboxkrnl_threading.cc',
|
'xboxkrnl_threading.cc',
|
||||||
|
'xobject.cc',
|
||||||
|
],
|
||||||
|
|
||||||
|
'includes': [
|
||||||
|
'objects/sources.gypi',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
|
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl_hal.h"
|
#include "kernel/modules/xboxkrnl/xboxkrnl_hal.h"
|
||||||
|
|
||||||
|
#include <xenia/kernel/xbox.h>
|
||||||
|
|
||||||
#include "kernel/shim_utils.h"
|
#include "kernel/shim_utils.h"
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
|
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl_memory.h"
|
#include "kernel/modules/xboxkrnl/xboxkrnl_memory.h"
|
||||||
|
|
||||||
|
#include <xenia/kernel/xbox.h>
|
||||||
|
|
||||||
#include "kernel/shim_utils.h"
|
#include "kernel/shim_utils.h"
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
|
@ -82,7 +83,7 @@ void NtAllocateVirtualMemory_shim(
|
||||||
// Allocate.
|
// Allocate.
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
uint32_t addr = xe_memory_heap_alloc(
|
uint32_t addr = xe_memory_heap_alloc(
|
||||||
state->memory, base_addr_value, adjusted_size, flags);
|
state->memory(), base_addr_value, adjusted_size, flags);
|
||||||
if (!addr) {
|
if (!addr) {
|
||||||
// Failed - assume no memory available.
|
// Failed - assume no memory available.
|
||||||
SHIM_SET_RETURN(X_STATUS_NO_MEMORY);
|
SHIM_SET_RETURN(X_STATUS_NO_MEMORY);
|
||||||
|
@ -123,7 +124,7 @@ void NtFreeVirtualMemory_shim(
|
||||||
|
|
||||||
// Free.
|
// Free.
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
uint32_t freed_size = xe_memory_heap_free(state->memory, base_addr_value,
|
uint32_t freed_size = xe_memory_heap_free(state->memory(), base_addr_value,
|
||||||
flags);
|
flags);
|
||||||
if (!freed_size) {
|
if (!freed_size) {
|
||||||
SHIM_SET_RETURN(X_STATUS_UNSUCCESSFUL);
|
SHIM_SET_RETURN(X_STATUS_UNSUCCESSFUL);
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
|
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl_module.h"
|
#include "kernel/modules/xboxkrnl/xboxkrnl_module.h"
|
||||||
|
|
||||||
|
#include <xenia/kernel/xbox.h>
|
||||||
#include <xenia/kernel/xex2.h>
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
#include "kernel/shim_utils.h"
|
#include "kernel/shim_utils.h"
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
|
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl_rtl.h"
|
#include "kernel/modules/xboxkrnl/xboxkrnl_rtl.h"
|
||||||
|
|
||||||
|
#include <xenia/kernel/xbox.h>
|
||||||
#include <xenia/kernel/xex2.h>
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
#include "kernel/shim_utils.h"
|
#include "kernel/shim_utils.h"
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
|
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl_threading.h"
|
#include "kernel/modules/xboxkrnl/xboxkrnl_threading.h"
|
||||||
|
|
||||||
|
#include <xenia/kernel/xbox.h>
|
||||||
|
|
||||||
#include "kernel/shim_utils.h"
|
#include "kernel/shim_utils.h"
|
||||||
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
|
#include "kernel/modules/xboxkrnl/objects/xthread.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
|
@ -54,19 +56,55 @@ namespace {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
void ExCreateThread() {
|
|
||||||
// launch native thread
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ExCreateThread_shim(
|
void ExCreateThread_shim(
|
||||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||||
|
// DWORD
|
||||||
|
// LPHANDLE Handle,
|
||||||
|
// DWORD StackSize,
|
||||||
|
// LPDWORD ThreadId,
|
||||||
|
// LPVOID XapiThreadStartup, ?? often 0
|
||||||
|
// LPVOID StartAddress,
|
||||||
|
// LPVOID StartContext,
|
||||||
|
// DWORD CreationFlags // 0x80?
|
||||||
|
|
||||||
|
uint32_t handle_ptr = SHIM_GET_ARG_32(0);
|
||||||
|
uint32_t stack_size = SHIM_GET_ARG_32(1);
|
||||||
|
uint32_t thread_id_ptr = SHIM_GET_ARG_32(2);
|
||||||
|
uint32_t xapi_thread_startup = SHIM_GET_ARG_32(3);
|
||||||
|
uint32_t start_address = SHIM_GET_ARG_32(4);
|
||||||
|
uint32_t start_context = SHIM_GET_ARG_32(5);
|
||||||
|
uint32_t creation_flags = SHIM_GET_ARG_32(6);
|
||||||
|
|
||||||
XELOGD(
|
XELOGD(
|
||||||
XT("ExCreateThread()"));
|
XT("ExCreateThread(%.8X, %d, %.8X, %.8X, %.8X, %.8X, %.8X)"),
|
||||||
|
handle_ptr,
|
||||||
|
stack_size,
|
||||||
|
thread_id_ptr,
|
||||||
|
xapi_thread_startup,
|
||||||
|
start_address,
|
||||||
|
start_context,
|
||||||
|
creation_flags);
|
||||||
|
|
||||||
SHIM_SET_RETURN(0);
|
XThread* thread = new XThread(
|
||||||
|
state, stack_size, xapi_thread_startup, start_address, start_context,
|
||||||
|
creation_flags);
|
||||||
|
|
||||||
|
X_STATUS result_code = thread->Create();
|
||||||
|
if (XFAILED(result_code)) {
|
||||||
|
// Failed!
|
||||||
|
thread->Release();
|
||||||
|
XELOGE(XT("Thread creation failed: %.8X"), result_code);
|
||||||
|
SHIM_SET_RETURN(result_code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle_ptr) {
|
||||||
|
SHIM_SET_MEM_32(handle_ptr, thread->handle());
|
||||||
|
}
|
||||||
|
if (thread_id_ptr) {
|
||||||
|
SHIM_SET_MEM_32(thread_id_ptr, thread->thread_id());
|
||||||
|
}
|
||||||
|
SHIM_SET_RETURN(result_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -205,7 +243,7 @@ void xe::kernel::xboxkrnl::RegisterThreadingExports(
|
||||||
export_resolver->SetFunctionMapping("xboxkrnl.exe", ordinal, \
|
export_resolver->SetFunctionMapping("xboxkrnl.exe", ordinal, \
|
||||||
state, (xe_kernel_export_shim_fn)shim, (xe_kernel_export_impl_fn)impl)
|
state, (xe_kernel_export_shim_fn)shim, (xe_kernel_export_impl_fn)impl)
|
||||||
|
|
||||||
SHIM_SET_MAPPING(0x0000000D, ExCreateThread_shim, ExCreateThread);
|
SHIM_SET_MAPPING(0x0000000D, ExCreateThread_shim, NULL);
|
||||||
|
|
||||||
SHIM_SET_MAPPING(0x00000066, KeGetCurrentProcessType_shim, NULL);
|
SHIM_SET_MAPPING(0x00000066, KeGetCurrentProcessType_shim, NULL);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/modules/xboxkrnl/xobject.h"
|
||||||
|
|
||||||
|
|
||||||
|
using namespace xe;
|
||||||
|
using namespace xe::kernel;
|
||||||
|
using namespace xe::kernel::xboxkrnl;
|
||||||
|
|
||||||
|
|
||||||
|
XObject::XObject(KernelState* kernel_state, Type type) :
|
||||||
|
kernel_state_(kernel_state),
|
||||||
|
ref_count_(1),
|
||||||
|
type_(type), handle_(X_INVALID_HANDLE_VALUE) {
|
||||||
|
handle_ = kernel_state->InsertObject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
XObject::~XObject() {
|
||||||
|
XEASSERTZERO(ref_count_);
|
||||||
|
|
||||||
|
if (handle_ != X_INVALID_HANDLE_VALUE) {
|
||||||
|
// Remove from state table.
|
||||||
|
kernel_state_->RemoveObject(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Runtime* XObject::runtime() {
|
||||||
|
return kernel_state_->runtime_;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelState* XObject::kernel_state() {
|
||||||
|
return kernel_state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
xe_memory_ref XObject::memory() {
|
||||||
|
return kernel_state_->memory();
|
||||||
|
}
|
||||||
|
|
||||||
|
XObject::Type XObject::type() {
|
||||||
|
return type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_HANDLE XObject::handle() {
|
||||||
|
return handle_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XObject::Retain() {
|
||||||
|
xe_atomic_inc_32(&ref_count_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XObject::Release() {
|
||||||
|
if (!xe_atomic_dec_32(&ref_count_)) {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_XOBJECT_H_
|
||||||
|
#define XENIA_KERNEL_MODULES_XBOXKRNL_XOBJECT_H_
|
||||||
|
|
||||||
|
#include "kernel/modules/xboxkrnl/kernel_state.h"
|
||||||
|
|
||||||
|
#include <xenia/kernel/xbox.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
class Runtime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
namespace xboxkrnl {
|
||||||
|
|
||||||
|
|
||||||
|
class XObject {
|
||||||
|
public:
|
||||||
|
enum Type {
|
||||||
|
kTypeModule = 0x00000001,
|
||||||
|
kTypeThread = 0x00000002,
|
||||||
|
};
|
||||||
|
|
||||||
|
XObject(KernelState* kernel_state, Type type);
|
||||||
|
virtual ~XObject();
|
||||||
|
|
||||||
|
KernelState* kernel_state();
|
||||||
|
|
||||||
|
Type type();
|
||||||
|
X_HANDLE handle();
|
||||||
|
|
||||||
|
void Retain();
|
||||||
|
void Release();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Runtime* runtime();
|
||||||
|
xe_memory_ref memory(); // unretained
|
||||||
|
|
||||||
|
private:
|
||||||
|
KernelState* kernel_state_;
|
||||||
|
|
||||||
|
volatile int32_t ref_count_;
|
||||||
|
|
||||||
|
Type type_;
|
||||||
|
X_HANDLE handle_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace xboxkrnl
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
|
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_XOBJECT_H_
|
|
@ -25,22 +25,13 @@ Runtime::Runtime(xe_pal_ref pal, shared_ptr<cpu::Processor> processor,
|
||||||
XEIGNORE(xestrcpy(command_line_, XECOUNT(command_line_), command_line));
|
XEIGNORE(xestrcpy(command_line_, XECOUNT(command_line_), command_line));
|
||||||
export_resolver_ = shared_ptr<ExportResolver>(new ExportResolver());
|
export_resolver_ = shared_ptr<ExportResolver>(new ExportResolver());
|
||||||
|
|
||||||
kernel_modules_.push_back(
|
xboxkrnl_ = auto_ptr<xboxkrnl::XboxkrnlModule>(
|
||||||
new xboxkrnl::XboxkrnlModule(pal_, memory_, export_resolver_));
|
new xboxkrnl::XboxkrnlModule(this));
|
||||||
kernel_modules_.push_back(
|
xam_ = auto_ptr<xam::XamModule>(
|
||||||
new xam::XamModule(pal_, memory_, export_resolver_));
|
new xam::XamModule(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
Runtime::~Runtime() {
|
Runtime::~Runtime() {
|
||||||
for (std::map<const xechar_t*, UserModule*>::iterator it =
|
|
||||||
user_modules_.begin(); it != user_modules_.end(); ++it) {
|
|
||||||
delete it->second;
|
|
||||||
}
|
|
||||||
for (std::vector<KernelModule*>::iterator it = kernel_modules_.begin();
|
|
||||||
it != kernel_modules_.end(); ++it) {
|
|
||||||
delete *it;
|
|
||||||
}
|
|
||||||
|
|
||||||
xe_memory_release(memory_);
|
xe_memory_release(memory_);
|
||||||
xe_pal_release(pal_);
|
xe_pal_release(pal_);
|
||||||
}
|
}
|
||||||
|
@ -65,117 +56,6 @@ const xechar_t* Runtime::command_line() {
|
||||||
return command_line_;
|
return command_line_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Runtime::LoadBinaryModule(const xechar_t* path, uint32_t start_address) {
|
int Runtime::LaunchModule(const xechar_t* path) {
|
||||||
const xechar_t* name = xestrrchr(path, '/') + 1;
|
return xboxkrnl_->LaunchModule(path);
|
||||||
|
|
||||||
// TODO(benvanik): map file from filesystem
|
|
||||||
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
|
|
||||||
if (!mmap) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
void* addr = xe_mmap_get_addr(mmap);
|
|
||||||
size_t length = xe_mmap_get_length(mmap);
|
|
||||||
|
|
||||||
int result_code = 1;
|
|
||||||
|
|
||||||
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
|
|
||||||
xe_memory_get_length(memory_),
|
|
||||||
addr, length));
|
|
||||||
|
|
||||||
// Prepare the module.
|
|
||||||
char name_a[2048];
|
|
||||||
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
|
||||||
char path_a[2048];
|
|
||||||
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path));
|
|
||||||
XEEXPECTZERO(processor_->PrepareModule(
|
|
||||||
name_a, path_a, start_address, start_address + length,
|
|
||||||
export_resolver_));
|
|
||||||
|
|
||||||
result_code = 0;
|
|
||||||
XECLEANUP:
|
|
||||||
xe_mmap_release(mmap);
|
|
||||||
return result_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Runtime::LoadModule(const xechar_t* path) {
|
|
||||||
if (GetModule(path)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(benvanik): map file from filesystem
|
|
||||||
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
|
|
||||||
if (!mmap) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
void* addr = xe_mmap_get_addr(mmap);
|
|
||||||
size_t length = xe_mmap_get_length(mmap);
|
|
||||||
|
|
||||||
UserModule* module = new UserModule(memory_);
|
|
||||||
int result_code = module->Load(addr, length, path);
|
|
||||||
|
|
||||||
// TODO(benvanik): retain memory somehow? is it needed?
|
|
||||||
xe_mmap_release(mmap);
|
|
||||||
|
|
||||||
if (result_code) {
|
|
||||||
delete module;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the module.
|
|
||||||
XEEXPECTZERO(processor_->PrepareModule(module, export_resolver_));
|
|
||||||
|
|
||||||
// Stash in modules list (takes reference).
|
|
||||||
user_modules_.insert(std::pair<const xechar_t*, UserModule*>(path, module));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
XECLEANUP:
|
|
||||||
delete module;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Runtime::LaunchModule(UserModule* user_module) {
|
|
||||||
const xe_xex2_header_t *xex_header = user_module->xex_header();
|
|
||||||
|
|
||||||
// TODO(benvanik): set as main module/etc
|
|
||||||
// xekXexExecutableModuleHandle = xe_module_get_handle(module);
|
|
||||||
|
|
||||||
// Setup the heap (and TLS?).
|
|
||||||
// xex_header->exe_heap_size;
|
|
||||||
|
|
||||||
// Launch thread.
|
|
||||||
// XHANDLE thread_handle;
|
|
||||||
// XDWORD thread_id;
|
|
||||||
// XBOOL result = xekExCreateThread(&thread_handle, xex_header->exe_stack_size, &thread_id, NULL, (void*)xex_header->exe_entry_point, NULL, 0);
|
|
||||||
|
|
||||||
// Wait until thread completes.
|
|
||||||
// XLARGE_INTEGER timeout = XINFINITE;
|
|
||||||
// xekNtWaitForSingleObjectEx(thread_handle, TRUE, &timeout);
|
|
||||||
|
|
||||||
// Simulate a thread.
|
|
||||||
uint32_t stack_size = xex_header->exe_stack_size;
|
|
||||||
if (stack_size < 16 * 1024 * 1024) {
|
|
||||||
stack_size = 16 * 1024 * 1024;
|
|
||||||
}
|
|
||||||
ThreadState* thread_state = processor_->AllocThread(
|
|
||||||
0x80000000, stack_size);
|
|
||||||
|
|
||||||
// Execute test.
|
|
||||||
processor_->Execute(thread_state, xex_header->exe_entry_point);
|
|
||||||
|
|
||||||
processor_->DeallocThread(thread_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
UserModule* Runtime::GetModule(const xechar_t* path) {
|
|
||||||
std::map<const xechar_t*, UserModule*>::iterator it =
|
|
||||||
user_modules_.find(path);
|
|
||||||
if (it != user_modules_.end()) {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Runtime::UnloadModule(UserModule* user_module) {
|
|
||||||
// TODO(benvanik): unload module
|
|
||||||
XEASSERTALWAYS();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
'sources': [
|
'sources': [
|
||||||
'export.cc',
|
'export.cc',
|
||||||
'runtime.cc',
|
'runtime.cc',
|
||||||
'user_module.cc',
|
|
||||||
'xex2.cc',
|
'xex2.cc',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,14 @@
|
||||||
|
|
||||||
#include <xenia/kernel/xex2.h>
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <third_party/crypto/rijndael-alg-fst.h>
|
#include <third_party/crypto/rijndael-alg-fst.h>
|
||||||
#include <third_party/crypto/rijndael-alg-fst.c>
|
#include <third_party/crypto/rijndael-alg-fst.c>
|
||||||
#include <third_party/mspack/lzx.h>
|
#include <third_party/mspack/lzx.h>
|
||||||
#include <third_party/mspack/lzxd.c>
|
#include <third_party/mspack/lzxd.c>
|
||||||
#include <third_party/mspack/mspack.h>
|
#include <third_party/mspack/mspack.h>
|
||||||
|
#include <third_party/pe/pe_image.h>
|
||||||
|
|
||||||
|
|
||||||
typedef struct xe_xex2 {
|
typedef struct xe_xex2 {
|
||||||
|
@ -22,6 +25,8 @@ typedef struct xe_xex2 {
|
||||||
xe_memory_ref memory;
|
xe_memory_ref memory;
|
||||||
|
|
||||||
xe_xex2_header_t header;
|
xe_xex2_header_t header;
|
||||||
|
|
||||||
|
std::vector<PESection*>* sections;
|
||||||
} xe_xex2_t;
|
} xe_xex2_t;
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +36,7 @@ int xe_xex2_decrypt_key(xe_xex2_header_t *header);
|
||||||
int xe_xex2_read_image(xe_xex2_ref xex,
|
int xe_xex2_read_image(xe_xex2_ref xex,
|
||||||
const uint8_t *xex_addr, const size_t xex_length,
|
const uint8_t *xex_addr, const size_t xex_length,
|
||||||
xe_memory_ref memory);
|
xe_memory_ref memory);
|
||||||
|
int xe_xex2_load_pe(xe_xex2_ref xex);
|
||||||
|
|
||||||
|
|
||||||
xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
|
xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
|
||||||
|
@ -40,6 +46,7 @@ xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
|
||||||
xe_ref_init((xe_ref)xex);
|
xe_ref_init((xe_ref)xex);
|
||||||
|
|
||||||
xex->memory = xe_memory_retain(memory);
|
xex->memory = xe_memory_retain(memory);
|
||||||
|
xex->sections = new std::vector<PESection*>();
|
||||||
|
|
||||||
XEEXPECTZERO(xe_xex2_read_header((const uint8_t*)addr, length, &xex->header));
|
XEEXPECTZERO(xe_xex2_read_header((const uint8_t*)addr, length, &xex->header));
|
||||||
|
|
||||||
|
@ -47,6 +54,8 @@ xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
|
||||||
|
|
||||||
XEEXPECTZERO(xe_xex2_read_image(xex, (const uint8_t*)addr, length, memory));
|
XEEXPECTZERO(xe_xex2_read_image(xex, (const uint8_t*)addr, length, memory));
|
||||||
|
|
||||||
|
XEEXPECTZERO(xe_xex2_load_pe(xex));
|
||||||
|
|
||||||
return xex;
|
return xex;
|
||||||
|
|
||||||
XECLEANUP:
|
XECLEANUP:
|
||||||
|
@ -55,6 +64,11 @@ XECLEANUP:
|
||||||
}
|
}
|
||||||
|
|
||||||
void xe_xex2_dealloc(xe_xex2_ref xex) {
|
void xe_xex2_dealloc(xe_xex2_ref xex) {
|
||||||
|
for (std::vector<PESection*>::iterator it = xex->sections->begin();
|
||||||
|
it != xex->sections->end(); ++it) {
|
||||||
|
delete *it;
|
||||||
|
}
|
||||||
|
|
||||||
xe_xex2_header_t *header = &xex->header;
|
xe_xex2_header_t *header = &xex->header;
|
||||||
xe_free(header->sections);
|
xe_free(header->sections);
|
||||||
if (header->file_format_info.compression_type == XEX_COMPRESSION_BASIC) {
|
if (header->file_format_info.compression_type == XEX_COMPRESSION_BASIC) {
|
||||||
|
@ -743,6 +757,100 @@ int xe_xex2_read_image(xe_xex2_ref xex, const uint8_t *xex_addr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int xe_xex2_load_pe(xe_xex2_ref xex) {
|
||||||
|
const xe_xex2_header_t* header = &xex->header;
|
||||||
|
const uint8_t* p = xe_memory_addr(xex->memory, header->exe_address);
|
||||||
|
|
||||||
|
// Verify DOS signature (MZ).
|
||||||
|
const IMAGE_DOS_HEADER* doshdr = (const IMAGE_DOS_HEADER*)p;
|
||||||
|
if (doshdr->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the NT header offset from the DOS header.
|
||||||
|
p += doshdr->e_lfanew;
|
||||||
|
|
||||||
|
// Verify NT signature (PE\0\0).
|
||||||
|
const IMAGE_NT_HEADERS32* nthdr = (const IMAGE_NT_HEADERS32*)(p);
|
||||||
|
if (nthdr->Signature != IMAGE_NT_SIGNATURE) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify matches an Xbox PE.
|
||||||
|
const IMAGE_FILE_HEADER* filehdr = &nthdr->FileHeader;
|
||||||
|
if ((filehdr->Machine != IMAGE_FILE_MACHINE_POWERPCBE) ||
|
||||||
|
!(filehdr->Characteristics & IMAGE_FILE_32BIT_MACHINE)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// Verify the expected size.
|
||||||
|
if (filehdr->SizeOfOptionalHeader != IMAGE_SIZEOF_NT_OPTIONAL_HEADER) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify optional header is 32bit.
|
||||||
|
const IMAGE_OPTIONAL_HEADER32* opthdr = &nthdr->OptionalHeader;
|
||||||
|
if (opthdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// Verify subsystem.
|
||||||
|
if (opthdr->Subsystem != IMAGE_SUBSYSTEM_XBOX) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linker version - likely 8+
|
||||||
|
// Could be useful for recognizing certain patterns
|
||||||
|
//opthdr->MajorLinkerVersion; opthdr->MinorLinkerVersion;
|
||||||
|
|
||||||
|
// Data directories of interest:
|
||||||
|
// EXPORT IMAGE_EXPORT_DIRECTORY
|
||||||
|
// IMPORT IMAGE_IMPORT_DESCRIPTOR[]
|
||||||
|
// EXCEPTION IMAGE_CE_RUNTIME_FUNCTION_ENTRY[]
|
||||||
|
// BASERELOC
|
||||||
|
// DEBUG IMAGE_DEBUG_DIRECTORY[]
|
||||||
|
// ARCHITECTURE /IMAGE_ARCHITECTURE_HEADER/ ----- import thunks!
|
||||||
|
// TLS IMAGE_TLS_DIRECTORY
|
||||||
|
// IAT Import Address Table ptr
|
||||||
|
//opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_X].VirtualAddress / .Size
|
||||||
|
|
||||||
|
// Quick scan to determine bounds of sections.
|
||||||
|
size_t upper_address = 0;
|
||||||
|
const IMAGE_SECTION_HEADER* sechdr = IMAGE_FIRST_SECTION(nthdr);
|
||||||
|
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
|
||||||
|
const size_t physical_address = opthdr->ImageBase + sechdr->VirtualAddress;
|
||||||
|
upper_address =
|
||||||
|
MAX(upper_address, physical_address + sechdr->Misc.VirtualSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup/load sections.
|
||||||
|
sechdr = IMAGE_FIRST_SECTION(nthdr);
|
||||||
|
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
|
||||||
|
PESection* section = (PESection*)xe_calloc(sizeof(PESection));
|
||||||
|
xe_copy_memory(section->name, sizeof(section->name),
|
||||||
|
sechdr->Name, sizeof(sechdr->Name));
|
||||||
|
section->name[8] = 0;
|
||||||
|
section->raw_address = sechdr->PointerToRawData;
|
||||||
|
section->raw_size = sechdr->SizeOfRawData;
|
||||||
|
section->address = header->exe_address + sechdr->VirtualAddress;
|
||||||
|
section->size = sechdr->Misc.VirtualSize;
|
||||||
|
section->flags = sechdr->Characteristics;
|
||||||
|
xex->sections->push_back(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
//DumpTLSDirectory(pImageBase, pNTHeader, (PIMAGE_TLS_DIRECTORY32)0);
|
||||||
|
//DumpExportsSection(pImageBase, pNTHeader);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PESection* xe_xex2_get_pe_section(xe_xex2_ref xex, const char* name) {
|
||||||
|
for (std::vector<PESection*>::iterator it = xex->sections->begin();
|
||||||
|
it != xex->sections->end(); ++it) {
|
||||||
|
if (!xestrcmpa((*it)->name, name)) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int xe_xex2_get_import_infos(xe_xex2_ref xex,
|
int xe_xex2_get_import_infos(xe_xex2_ref xex,
|
||||||
const xe_xex2_import_library_t *library,
|
const xe_xex2_import_library_t *library,
|
||||||
xe_xex2_import_info_t **out_import_infos,
|
xe_xex2_import_info_t **out_import_infos,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||||
{
|
{
|
||||||
'includes': [
|
'includes': [
|
||||||
'xenia-info/xenia-info.gypi',
|
|
||||||
'xenia-run/xenia-run.gypi',
|
'xenia-run/xenia-run.gypi',
|
||||||
'xenia-test/xenia-test.gypi',
|
'xenia-test/xenia-test.gypi',
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
|
||||||
******************************************************************************
|
|
||||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
|
||||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <xenia/xenia.h>
|
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
|
||||||
using namespace xe::cpu;
|
|
||||||
using namespace xe::kernel;
|
|
||||||
|
|
||||||
|
|
||||||
int xenia_info(int argc, xechar_t **argv) {
|
|
||||||
std::string usage = "usage: ";
|
|
||||||
usage += "xenia-info some.xex";
|
|
||||||
google::SetUsageMessage(usage);
|
|
||||||
google::SetVersionString("1.0");
|
|
||||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
|
||||||
|
|
||||||
int result_code = 1;
|
|
||||||
|
|
||||||
xe_pal_ref pal = NULL;
|
|
||||||
xe_memory_ref memory = NULL;
|
|
||||||
shared_ptr<Processor> processor;
|
|
||||||
shared_ptr<Runtime> runtime;
|
|
||||||
|
|
||||||
// Grab path.
|
|
||||||
if (argc < 2) {
|
|
||||||
google::ShowUsageWithFlags("xenia-info");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
const xechar_t *path = argv[1];
|
|
||||||
|
|
||||||
xe_pal_options_t pal_options;
|
|
||||||
xe_zero_struct(&pal_options, sizeof(pal_options));
|
|
||||||
pal = xe_pal_create(pal_options);
|
|
||||||
XEEXPECTNOTNULL(pal);
|
|
||||||
|
|
||||||
xe_memory_options_t memory_options;
|
|
||||||
xe_zero_struct(&memory_options, sizeof(memory_options));
|
|
||||||
memory = xe_memory_create(pal, memory_options);
|
|
||||||
XEEXPECTNOTNULL(memory);
|
|
||||||
|
|
||||||
processor = shared_ptr<Processor>(new Processor(pal, memory));
|
|
||||||
XEEXPECTZERO(processor->Setup());
|
|
||||||
|
|
||||||
runtime = shared_ptr<Runtime>(new Runtime(pal, processor, XT("")));
|
|
||||||
|
|
||||||
XEEXPECTZERO(runtime->LoadModule(path));
|
|
||||||
|
|
||||||
result_code = 0;
|
|
||||||
XECLEANUP:
|
|
||||||
xe_memory_release(memory);
|
|
||||||
xe_pal_release(pal);
|
|
||||||
|
|
||||||
google::ShutDownCommandLineFlags();
|
|
||||||
return result_code;
|
|
||||||
}
|
|
||||||
XE_MAIN_THUNK(xenia_info);
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
|
||||||
{
|
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name': 'xenia-info',
|
|
||||||
'type': 'executable',
|
|
||||||
|
|
||||||
'dependencies': [
|
|
||||||
'xeniacore',
|
|
||||||
'xeniacpu',
|
|
||||||
'xeniakernel',
|
|
||||||
],
|
|
||||||
|
|
||||||
'include_dirs': [
|
|
||||||
'.',
|
|
||||||
],
|
|
||||||
|
|
||||||
'sources': [
|
|
||||||
'xenia-info.cc',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
|
@ -26,15 +26,14 @@ public:
|
||||||
Run();
|
Run();
|
||||||
~Run();
|
~Run();
|
||||||
|
|
||||||
int Setup(const xechar_t* path);
|
int Setup();
|
||||||
int Launch();
|
int Launch(const xechar_t* path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xe_pal_ref pal_;
|
xe_pal_ref pal_;
|
||||||
xe_memory_ref memory_;
|
xe_memory_ref memory_;
|
||||||
shared_ptr<Processor> processor_;
|
shared_ptr<Processor> processor_;
|
||||||
shared_ptr<Runtime> runtime_;
|
shared_ptr<Runtime> runtime_;
|
||||||
UserModule* module_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Run::Run() {
|
Run::Run() {
|
||||||
|
@ -45,7 +44,7 @@ Run::~Run() {
|
||||||
xe_pal_release(pal_);
|
xe_pal_release(pal_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Run::Setup(const xechar_t* path) {
|
int Run::Setup() {
|
||||||
xe_pal_options_t pal_options;
|
xe_pal_options_t pal_options;
|
||||||
xe_zero_struct(&pal_options, sizeof(pal_options));
|
xe_zero_struct(&pal_options, sizeof(pal_options));
|
||||||
pal_ = xe_pal_create(pal_options);
|
pal_ = xe_pal_create(pal_options);
|
||||||
|
@ -61,21 +60,19 @@ int Run::Setup(const xechar_t* path) {
|
||||||
|
|
||||||
runtime_ = shared_ptr<Runtime>(new Runtime(pal_, processor_, XT("")));
|
runtime_ = shared_ptr<Runtime>(new Runtime(pal_, processor_, XT("")));
|
||||||
|
|
||||||
XEEXPECTZERO(runtime_->LoadModule(path));
|
|
||||||
module_ = runtime_->GetModule(path);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
XECLEANUP:
|
XECLEANUP:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Run::Launch() {
|
int Run::Launch(const xechar_t* path) {
|
||||||
if (FLAGS_abort_before_entry) {
|
if (FLAGS_abort_before_entry) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(benvanik): wait until the module thread exits
|
// TODO(benvanik): wait until the module thread exits
|
||||||
runtime_->LaunchModule(module_);
|
runtime_->LaunchModule(path);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,12 +97,12 @@ int xenia_run(int argc, xechar_t **argv) {
|
||||||
|
|
||||||
auto_ptr<Run> run = auto_ptr<Run>(new Run());
|
auto_ptr<Run> run = auto_ptr<Run>(new Run());
|
||||||
|
|
||||||
result_code = run->Setup(path);
|
result_code = run->Setup();
|
||||||
XEEXPECTZERO(result_code);
|
XEEXPECTZERO(result_code);
|
||||||
|
|
||||||
//xe_module_dump(run->module);
|
//xe_module_dump(run->module);
|
||||||
|
|
||||||
run->Launch();
|
run->Launch(path);
|
||||||
|
|
||||||
result_code = 0;
|
result_code = 0;
|
||||||
XECLEANUP:
|
XECLEANUP:
|
||||||
|
|
|
@ -126,10 +126,11 @@ int run_test(xe_pal_ref pal, string& src_file_path) {
|
||||||
runtime = shared_ptr<Runtime>(new Runtime(pal, processor, XT("")));
|
runtime = shared_ptr<Runtime>(new Runtime(pal, processor, XT("")));
|
||||||
|
|
||||||
// Load the binary module.
|
// Load the binary module.
|
||||||
XEEXPECTZERO(runtime->LoadBinaryModule(bin_file_path.c_str(), 0x82010000));
|
XEEXPECTZERO(processor->LoadBinary(bin_file_path.c_str(), 0x82010000,
|
||||||
|
runtime->export_resolver()));
|
||||||
|
|
||||||
// Simulate a thread.
|
// Simulate a thread.
|
||||||
thread_state = processor->AllocThread(0x80000000, 256 * 1024 * 1024);
|
thread_state = processor->AllocThread(256 * 1024 * 1024, 0);
|
||||||
|
|
||||||
// Setup test state from annotations.
|
// Setup test state from annotations.
|
||||||
XEEXPECTZERO(setup_test_state(memory, processor.get(), thread_state,
|
XEEXPECTZERO(setup_test_state(memory, processor.get(), thread_state,
|
||||||
|
@ -157,6 +158,10 @@ int discover_tests(string& test_path,
|
||||||
vector<string>& test_files) {
|
vector<string>& test_files) {
|
||||||
// TODO(benvanik): use PAL instead of this
|
// TODO(benvanik): use PAL instead of this
|
||||||
DIR* d = opendir(test_path.c_str());
|
DIR* d = opendir(test_path.c_str());
|
||||||
|
if (!d) {
|
||||||
|
XELOGE(XT("Unable to find test path %s"), test_path.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
struct dirent* dir;
|
struct dirent* dir;
|
||||||
while ((dir = readdir(d))) {
|
while ((dir = readdir(d))) {
|
||||||
if (dir->d_type == DT_REG) {
|
if (dir->d_type == DT_REG) {
|
||||||
|
|
Loading…
Reference in New Issue