diff --git a/bin/xenia-info b/bin/xenia-info index 4d7135fa8..eb69dab3a 100755 --- a/bin/xenia-info +++ b/bin/xenia-info @@ -7,10 +7,10 @@ case "$*" in (*--debug*) CONFIG=debug;; esac -EXEC=$DIR/../build/xenia/$CONFIG/xenia-info +EXEC=$DIR/../build/xenia/$CONFIG/xenia-run if [ ! -f "$EXEC" ]; then python $DIR/../xenia-build.py build --$CONFIG fi -$EXEC "$@" +$EXEC --abort_before_entry=true "$@" diff --git a/common.gypi b/common.gypi index 3bec009e0..cebf7569d 100644 --- a/common.gypi +++ b/common.gypi @@ -12,7 +12,7 @@ # LLVM paths. # TODO(benvanik): switch based on configuration. - 'llvm_path': 'build/llvm/release/', + 'llvm_path': 'build/llvm/debug/', 'llvm_config': '<(llvm_path)bin/llvm-config', 'llvm_includedir': '<(llvm_path)/include', 'llvm_cxxflags': [ diff --git a/include/xenia/core.h b/include/xenia/core.h index 1c807dea4..2c6391931 100644 --- a/include/xenia/core.h +++ b/include/xenia/core.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/include/xenia/core/mutex.h b/include/xenia/core/mutex.h new file mode 100644 index 000000000..41b0c4c67 --- /dev/null +++ b/include/xenia/core/mutex.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 + + +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_ diff --git a/include/xenia/cpu/codegen/module_generator.h b/include/xenia/cpu/codegen/module_generator.h index addcda89f..2e2bbba4e 100644 --- a/include/xenia/cpu/codegen/module_generator.h +++ b/include/xenia/cpu/codegen/module_generator.h @@ -16,7 +16,6 @@ #include #include #include -#include namespace llvm { diff --git a/include/xenia/cpu/exec_module.h b/include/xenia/cpu/exec_module.h index dec1cf36d..364807cd0 100644 --- a/include/xenia/cpu/exec_module.h +++ b/include/xenia/cpu/exec_module.h @@ -15,7 +15,7 @@ #include #include -#include +#include namespace llvm { @@ -49,7 +49,7 @@ public: shared_ptr& engine); ~ExecModule(); - int PrepareUserModule(kernel::UserModule* user_module); + int PrepareXex(xe_xex2_ref xex); int PrepareRawBinary(uint32_t start_address, uint32_t end_address); void AddFunctionsToMap(FunctionMap& map); diff --git a/include/xenia/cpu/processor.h b/include/xenia/cpu/processor.h index fa925c2e2..13e30c6ba 100644 --- a/include/xenia/cpu/processor.h +++ b/include/xenia/cpu/processor.h @@ -17,7 +17,7 @@ #include #include #include -#include +#include namespace llvm { @@ -40,17 +40,18 @@ public: int Setup(); - int PrepareModule(const char* module_name, const char* module_path, - uint32_t start_address, uint32_t end_address, - shared_ptr export_resolver); - int PrepareModule(kernel::UserModule* user_module, + int LoadBinary(const xechar_t* path, uint32_t start_address, + shared_ptr export_resolver); + + int PrepareModule(const char* name, const char* path, xe_xex2_ref xex, shared_ptr export_resolver); 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); int Execute(ThreadState* thread_state, uint32_t address); + uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0); private: llvm::Function* GetFunction(uint32_t address); diff --git a/include/xenia/cpu/sdb.h b/include/xenia/cpu/sdb.h index d55b0d37f..70529b186 100644 --- a/include/xenia/cpu/sdb.h +++ b/include/xenia/cpu/sdb.h @@ -17,7 +17,7 @@ #include #include -#include +#include namespace xe { @@ -205,7 +205,7 @@ class XexSymbolDatabase : public SymbolDatabase { public: XexSymbolDatabase(xe_memory_ref memory, kernel::ExportResolver* export_resolver, - kernel::UserModule* module); + xe_xex2_ref xex); virtual ~XexSymbolDatabase(); virtual int Analyze(); @@ -218,7 +218,7 @@ private: virtual uint32_t GetEntryPoint(); virtual bool IsValueInTextRange(uint32_t value); - kernel::UserModule* module_; + xe_xex2_ref xex_; }; diff --git a/include/xenia/cpu/thread_state.h b/include/xenia/cpu/thread_state.h index 60ab75edc..2adfafe83 100644 --- a/include/xenia/cpu/thread_state.h +++ b/include/xenia/cpu/thread_state.h @@ -25,16 +25,19 @@ class Processor; class ThreadState { public: ThreadState(Processor* processor, - uint32_t stack_address, uint32_t stack_size); + uint32_t stack_size, uint32_t thread_state_address); ~ThreadState(); xe_ppc_state_t* ppc_state(); private: - uint32_t stack_address_; uint32_t stack_size_; + uint32_t thread_state_address; xe_memory_ref memory_; + uint32_t stack_address_; + uint32_t thread_state_address_; + xe_ppc_state_t ppc_state_; }; diff --git a/include/xenia/kernel/kernel_module.h b/include/xenia/kernel/kernel_module.h index 06c03cb46..5602ea6ed 100644 --- a/include/xenia/kernel/kernel_module.h +++ b/include/xenia/kernel/kernel_module.h @@ -14,30 +14,36 @@ #include #include +#include namespace xe { namespace kernel { +class Runtime; + + class KernelModule { public: - KernelModule(xe_pal_ref pal, xe_memory_ref memory, - shared_ptr resolver) { - pal_ = xe_pal_retain(pal); - memory_ = xe_memory_retain(memory); - resolver_ = resolver; + KernelModule(Runtime* runtime) { + runtime_ = runtime; + pal_ = runtime->pal(); + memory_ = runtime->memory(); + export_resolver_ = runtime->export_resolver(); } virtual ~KernelModule() { + export_resolver_.reset(); xe_memory_release(memory_); xe_pal_release(pal_); } protected: - xe_pal_ref pal_; - xe_memory_ref memory_; - shared_ptr resolver_; + Runtime* runtime_; + xe_pal_ref pal_; + xe_memory_ref memory_; + shared_ptr export_resolver_; }; diff --git a/include/xenia/kernel/runtime.h b/include/xenia/kernel/runtime.h index 8db0035e9..36f52aad0 100644 --- a/include/xenia/kernel/runtime.h +++ b/include/xenia/kernel/runtime.h @@ -15,7 +15,6 @@ #include #include -#include #include @@ -23,6 +22,14 @@ namespace xe { namespace cpu { class Processor; } +namespace kernel { +namespace xboxkrnl { + class XboxkrnlModule; +} +namespace xam { + class XamModule; +} +} } @@ -44,12 +51,7 @@ public: shared_ptr export_resolver(); const xechar_t* command_line(); - int LoadBinaryModule(const xechar_t* path, uint32_t start_address); - - int LoadModule(const xechar_t* path); - void LaunchModule(UserModule* user_module); - UserModule* GetModule(const xechar_t* path); - void UnloadModule(UserModule* user_module); + int LaunchModule(const xechar_t* path); private: xe_pal_ref pal_; @@ -58,8 +60,8 @@ private: xechar_t command_line_[2048]; shared_ptr export_resolver_; - std::vector kernel_modules_; - std::map user_modules_; + auto_ptr xboxkrnl_; + auto_ptr xam_; }; diff --git a/include/xenia/kernel/user_module.h b/include/xenia/kernel/user_module.h deleted file mode 100644 index 6ba0a9490..000000000 --- a/include/xenia/kernel/user_module.h +++ /dev/null @@ -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 - -#include - -#include -#include - - -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 sections_; -}; - - -} // namespace kernel -} // namespace xe - - -#endif // XENIA_KERNEL_USER_MODULE_H_ diff --git a/src/kernel/modules/xboxkrnl/xboxkrnl.h b/include/xenia/kernel/xbox.h similarity index 92% rename from src/kernel/modules/xboxkrnl/xboxkrnl.h rename to include/xenia/kernel/xbox.h index c6c3fd91e..ecbb233eb 100644 --- a/src/kernel/modules/xboxkrnl/xboxkrnl.h +++ b/include/xenia/kernel/xbox.h @@ -7,8 +7,8 @@ ****************************************************************************** */ -#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_H_ -#define XENIA_KERNEL_MODULES_XBOXKRNL_H_ +#ifndef XENIA_KERNEL_XBOX_H_ +#define XENIA_KERNEL_XBOX_H_ #include #include @@ -16,7 +16,6 @@ namespace xe { namespace kernel { -namespace xboxkrnl { typedef uint32_t X_HANDLE; @@ -26,6 +25,9 @@ typedef uint32_t X_HANDLE; // NT_STATUS (STATUS_*) // http://msdn.microsoft.com/en-us/library/cc704588.aspx // 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_UNSUCCESSFUL ((uint32_t)0xC0000001L) #define X_STATUS_NOT_IMPLEMENTED ((uint32_t)0xC0000002L) @@ -75,13 +77,16 @@ typedef uint32_t X_HANDLE; #define X_PROCTYPE_SYSTEM 2 +// Thread enums. +#define X_CREATE_SUSPENDED 0x00000004 + + // TLS specials. #define X_TLS_OUT_OF_INDEXES UINT32_MAX // (-1) -} // namespace xboxkrnl } // namespace kernel } // namespace xe -#endif // XENIA_KERNEL_MODULES_XBOXKRNL_H_ +#endif // XENIA_KERNEL_XBOX_H_ diff --git a/include/xenia/kernel/xex2.h b/include/xenia/kernel/xex2.h index 2f69ee4de..e3b15d092 100644 --- a/include/xenia/kernel/xex2.h +++ b/include/xenia/kernel/xex2.h @@ -27,6 +27,24 @@ typedef struct { uint32_t thunk_address; // NULL or address of thunk } 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, 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 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, const xe_xex2_import_library_t *library, diff --git a/src/core/memory.cc b/src/core/memory.cc index 42cb1e994..c57aa041d 100644 --- a/src/core/memory.cc +++ b/src/core/memory.cc @@ -9,12 +9,14 @@ #include +#include + #if !XE_PLATFORM(WIN32) #include #endif // WIN32 #define MSPACES 1 -#define USE_LOCKS 1 +#define USE_LOCKS 0 #define USE_DL_PREFIX 1 #define HAVE_MORECORE 0 #define HAVE_MMAP 0 @@ -43,10 +45,11 @@ struct xe_memory { xe_ref_t ref; - size_t length; - void *ptr; + size_t length; + 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 XEEXPECTNOTNULL(memory->ptr); + memory->heap_mutex = xe_mutex_alloc(0); + XEEXPECTNOTNULL(memory->heap_mutex); + // Allocate the mspace for our heap. // We skip the first page to make writes to 0 easier to find. offset = 64 * 1024; @@ -85,8 +91,15 @@ XECLEANUP: } 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); + 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) @@ -148,7 +161,9 @@ uint32_t xe_memory_heap_alloc(xe_memory_ref memory, uint32_t base_addr, XEASSERT(base_addr == 0); XEASSERT(flags == 0); + XEIGNORE(xe_mutex_lock(memory->heap_mutex)); uint8_t* p = (uint8_t*)mspace_malloc(memory->heap, size); + XEIGNORE(xe_mutex_unlock(memory->heap_mutex)); if (!p) { return 0; } @@ -166,7 +181,9 @@ uint32_t xe_memory_heap_free(xe_memory_ref memory, uint32_t addr, return 0; } + XEIGNORE(xe_mutex_lock(memory->heap_mutex)); mspace_free(memory->heap, p); + XEIGNORE(xe_mutex_unlock(memory->heap_mutex)); return (uint32_t)real_size; } diff --git a/src/core/mutex_posix.cc b/src/core/mutex_posix.cc new file mode 100644 index 000000000..aae3d042c --- /dev/null +++ b/src/core/mutex_posix.cc @@ -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 + + +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; +} diff --git a/src/core/mutex_win.cc b/src/core/mutex_win.cc new file mode 100644 index 000000000..575786aec --- /dev/null +++ b/src/core/mutex_win.cc @@ -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 + + +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; +} diff --git a/src/core/sources.gypi b/src/core/sources.gypi index 459b11a1a..6f7ece69d 100644 --- a/src/core/sources.gypi +++ b/src/core/sources.gypi @@ -11,11 +11,13 @@ ['OS == "mac" or OS == "linux"', { 'sources': [ 'mmap_posix.cc', + 'mutex_posix.cc', ], }], ['OS == "win"', { 'sources': [ 'mmap_win.cc', + 'mutex_win.cc', ], }], ], diff --git a/src/cpu/codegen/emit_control.cc b/src/cpu/codegen/emit_control.cc index e8ba9e847..2a58e28c8 100644 --- a/src/cpu/codegen/emit_control.cc +++ b/src/cpu/codegen/emit_control.cc @@ -87,6 +87,7 @@ int XeEmitBranchTo( // registers? g.SpillRegisters(); + XEASSERTNOTNULL(fn_block->outgoing_function); Function* target_fn = g.GetFunction(fn_block->outgoing_function); Function::arg_iterator args = g.gen_fn()->arg_begin(); Value* state_ptr = args; diff --git a/src/cpu/exec_module.cc b/src/cpu/exec_module.cc index 8075af366..f4af6d0ad 100644 --- a/src/cpu/exec_module.cc +++ b/src/cpu/exec_module.cc @@ -70,9 +70,9 @@ ExecModule::~ExecModule() { xe_memory_release(memory_); } -int ExecModule::PrepareUserModule(kernel::UserModule* user_module) { +int ExecModule::PrepareXex(xe_xex2_ref xex) { sdb_ = shared_ptr( - new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), user_module)); + new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), xex)); int result_code = Prepare(); if (result_code) { @@ -80,8 +80,7 @@ int ExecModule::PrepareUserModule(kernel::UserModule* user_module) { } // Import variables. - xe_xex2_ref xex = user_module->xex(); - xe_xex2_release(xex); + // TODO?? return 0; } diff --git a/src/cpu/llvm_exports.cc b/src/cpu/llvm_exports.cc index 7843bb31c..bb32bcf15 100644 --- a/src/cpu/llvm_exports.cc +++ b/src/cpu/llvm_exports.cc @@ -91,6 +91,10 @@ void XeTraceInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) { type && type->emit ? " " : "X", type ? type->name : ""); + if (cia == 0x82014468) { + printf("BREAKBREAKBREAK\n"); + } + // TODO(benvanik): better disassembly, printing of current register values/etc } diff --git a/src/cpu/processor.cc b/src/cpu/processor.cc index 12f54d292..80f26fbd9 100644 --- a/src/cpu/processor.cc +++ b/src/cpu/processor.cc @@ -113,14 +113,35 @@ int Processor::Setup() { return 0; } -int Processor::PrepareModule( - const char* module_name, const char* module_path, - uint32_t start_address, uint32_t end_address, - shared_ptr export_resolver) { - ExecModule* exec_module = new ExecModule( - memory_, export_resolver, module_name, module_path, engine_); +int Processor::LoadBinary(const xechar_t* path, uint32_t start_address, + shared_ptr export_resolver) { + ExecModule* exec_module = NULL; + const xechar_t* name = xestrrchr(path, '/') + 1; - 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; return 1; } @@ -130,20 +151,23 @@ int Processor::PrepareModule( 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 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( - memory_, export_resolver, name_a, path_a, + memory_, export_resolver, name, path, engine_); - if (exec_module->PrepareUserModule(user_module)) { + if (exec_module->PrepareXex(xex)) { delete exec_module; return 1; } @@ -151,7 +175,6 @@ int Processor::PrepareModule(UserModule* user_module, exec_module->AddFunctionsToMap(all_fns_); modules_.push_back(exec_module); - //user_module->Dump(export_resolver.get()); exec_module->Dump(); return 0; @@ -162,10 +185,10 @@ uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) { return 0; } -ThreadState* Processor::AllocThread(uint32_t stack_address, - uint32_t stack_size) { +ThreadState* Processor::AllocThread(uint32_t stack_size, + uint32_t thread_state_address) { ThreadState* thread_state = new ThreadState( - this, stack_address, stack_size); + this, stack_size, thread_state_address); return thread_state; } @@ -210,6 +233,16 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) { 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) { FunctionMap::iterator it = all_fns_.find(address); if (it != all_fns_.end()) { diff --git a/src/cpu/sdb.cc b/src/cpu/sdb.cc index 42bcb6371..6450989e9 100644 --- a/src/cpu/sdb.cc +++ b/src/cpu/sdb.cc @@ -23,6 +23,36 @@ using namespace xe::cpu::sdb; 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() : start_address(0), end_address(0), outgoing_type(kTargetUnknown), outgoing_address(0), @@ -405,7 +435,8 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) { block->outgoing_type = FunctionBlock::kTargetLR; if (furthest_target > addr) { // 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 { // Function end point. XELOGSDB(XT("function end %.8X"), addr); @@ -458,6 +489,11 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) { if (!ends_fn) { 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; @@ -467,9 +503,16 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) { block->outgoing_address = target; if (i.B.LK) { 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 { XELOGSDB(XT("bc %.8X -> %.8X"), addr, target); + // TODO(benvanik): GetOrInsertFunction? it's likely a BB + furthest_target = MAX(furthest_target, target); } ends_block = true; @@ -558,14 +601,16 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) { block->outgoing_block = fn->SplitBlock(block->outgoing_address); } 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 { // Function call. block->outgoing_type = FunctionBlock::kTargetFunction; block->outgoing_function = GetFunction(block->outgoing_address); 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( - xe_memory_ref memory, ExportResolver* export_resolver, UserModule* module) : + xe_memory_ref memory, ExportResolver* export_resolver, xe_xex2_ref xex) : SymbolDatabase(memory, export_resolver) { - module_ = module; + xex_ = xe_xex2_retain(xex); } XexSymbolDatabase::~XexSymbolDatabase() { + xe_xex2_release(xex_); } 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_*. FindGplr(); @@ -785,7 +831,7 @@ int XexSymbolDatabase::FindGplr() { }; 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++) { const xe_xex2_section_t* section = &header->sections[n]; const size_t start_address = @@ -833,12 +879,10 @@ int XexSymbolDatabase::FindGplr() { } int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) { - xe_xex2_ref xex = module_->xex(); xe_xex2_import_info_t* import_infos; 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)) { - xe_xex2_release(xex); return 1; } @@ -878,18 +922,54 @@ int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) { } xe_free(import_infos); - xe_xex2_release(xex); return 0; } int XexSymbolDatabase::AddMethodHints() { - PEMethodInfo* method_infos; - size_t method_info_count; - if (module_->GetMethodHints(&method_infos, &method_info_count)) { - return 1; + uint8_t* mem = xe_memory_addr(memory_, 0); + + const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY* entry = NULL; + + // 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]; FunctionSymbol* fn = GetOrInsertFunction(method_info->address); fn->end_address = method_info->address + method_info->total_length - 4; @@ -902,12 +982,12 @@ int XexSymbolDatabase::AddMethodHints() { } 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; }; 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++) { const xe_xex2_section_t* section = &header->sections[n]; const size_t start_address = diff --git a/src/cpu/thread_state.cc b/src/cpu/thread_state.cc index 18f1478a2..17e2c3a01 100644 --- a/src/cpu/thread_state.cc +++ b/src/cpu/thread_state.cc @@ -19,11 +19,12 @@ using namespace xe::cpu; ThreadState::ThreadState( Processor* processor, - uint32_t stack_address, uint32_t stack_size) { - stack_address_ = stack_address; - stack_size_ = stack_size; + uint32_t stack_size, uint32_t thread_state_address) : + stack_size_(stack_size), thread_state_address_(thread_state_address) { memory_ = processor->memory(); + stack_address_ = xe_memory_heap_alloc(memory_, 0, stack_size, 0); + xe_zero_struct(&ppc_state_, sizeof(ppc_state_)); // Stash pointers to common structures that callbacks may need. @@ -33,9 +34,11 @@ ThreadState::ThreadState( // Set initial registers. ppc_state_.r[1] = stack_address_; + ppc_state_.r[13] = thread_state_address_; } ThreadState::~ThreadState() { + xe_memory_heap_free(memory_, stack_address_, 0); xe_memory_release(memory_); } diff --git a/src/kernel/modules/xam/xam_module.cc b/src/kernel/modules/xam/xam_module.cc index bd2cb9f11..a5eb8bb56 100644 --- a/src/kernel/modules/xam/xam_module.cc +++ b/src/kernel/modules/xam/xam_module.cc @@ -19,17 +19,16 @@ using namespace xe::kernel; using namespace xe::kernel::xam; -XamModule::XamModule(xe_pal_ref pal, xe_memory_ref memory, - shared_ptr resolver) : - KernelModule(pal, memory, resolver) { - resolver->RegisterTable( +XamModule::XamModule(Runtime* runtime) : + KernelModule(runtime) { + export_resolver_->RegisterTable( "xam.xex", xam_export_table, XECOUNT(xam_export_table)); // Setup the xam state instance. - xam_state = auto_ptr(new XamState(pal, memory, resolver)); + xam_state = auto_ptr(new XamState(pal_, memory_, export_resolver_)); // Register all exported functions. - RegisterInfoExports(resolver.get(), xam_state.get()); + RegisterInfoExports(export_resolver_.get(), xam_state.get()); } XamModule::~XamModule() { diff --git a/src/kernel/modules/xam/xam_module.h b/src/kernel/modules/xam/xam_module.h index d629f2ddb..5880665f7 100644 --- a/src/kernel/modules/xam/xam_module.h +++ b/src/kernel/modules/xam/xam_module.h @@ -26,8 +26,7 @@ class XamState; class XamModule : public KernelModule { public: - XamModule(xe_pal_ref pal, xe_memory_ref memory, - shared_ptr resolver); + XamModule(Runtime* runtime); virtual ~XamModule(); private: diff --git a/src/kernel/modules/xboxkrnl/kernel_state.cc b/src/kernel/modules/xboxkrnl/kernel_state.cc index 93aa333b7..dcc1e39d7 100644 --- a/src/kernel/modules/xboxkrnl/kernel_state.cc +++ b/src/kernel/modules/xboxkrnl/kernel_state.cc @@ -9,6 +9,8 @@ #include "kernel/modules/xboxkrnl/kernel_state.h" +#include "kernel/modules/xboxkrnl/xobject.h" + using namespace xe; using namespace xe::kernel; @@ -20,14 +22,88 @@ namespace { } -KernelState::KernelState(xe_pal_ref pal, xe_memory_ref memory, - shared_ptr export_resolver) { - this->pal = xe_pal_retain(pal); - this->memory = xe_memory_retain(memory); - export_resolver_ = export_resolver; +KernelState::KernelState(Runtime* runtime) : + runtime_(runtime), + next_handle_(0) { + pal_ = runtime->pal(); + memory_ = runtime->memory(); + processor_ = runtime->processor(); + + objects_mutex_ = xe_mutex_alloc(0); + XEASSERTNOTNULL(objects_mutex_); } KernelState::~KernelState() { - xe_memory_release(memory); - xe_pal_release(pal); + // Delete all objects. + // We first copy the list to another list so that the deletion of the objects + // doesn't mess up iteration. + std::vector all_objects; + xe_mutex_lock(objects_mutex_); + for (std::tr1::unordered_map::iterator it = + objects_.begin(); it != objects_.end(); ++it) { + all_objects.push_back(it->second); + } + objects_.clear(); + xe_mutex_unlock(objects_mutex_); + for (std::vector::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::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(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_); } diff --git a/src/kernel/modules/xboxkrnl/kernel_state.h b/src/kernel/modules/xboxkrnl/kernel_state.h index 348e59721..1e630f465 100644 --- a/src/kernel/modules/xboxkrnl/kernel_state.h +++ b/src/kernel/modules/xboxkrnl/kernel_state.h @@ -15,6 +15,7 @@ #include #include +#include namespace xe { @@ -22,17 +23,35 @@ namespace kernel { namespace xboxkrnl { +class XObject; + + class KernelState { public: - KernelState(xe_pal_ref pal, xe_memory_ref memory, - shared_ptr export_resolver); + KernelState(Runtime* runtime); ~KernelState(); - xe_pal_ref pal; - xe_memory_ref memory; + XObject* GetObject(X_HANDLE handle); + + Runtime* runtime(); + xe_pal_ref pal(); + xe_memory_ref memory(); + cpu::Processor* processor(); private: - shared_ptr export_resolver_; + X_HANDLE InsertObject(XObject* obj); + void RemoveObject(XObject* obj); + + Runtime* runtime_; + xe_pal_ref pal_; + xe_memory_ref memory_; + shared_ptr processor_; + + xe_mutex_t* objects_mutex_; + X_HANDLE next_handle_; + std::tr1::unordered_map objects_; + + friend class XObject; }; diff --git a/src/kernel/modules/xboxkrnl/module.cc b/src/kernel/modules/xboxkrnl/module.cc index 07599f944..fa2850095 100644 --- a/src/kernel/modules/xboxkrnl/module.cc +++ b/src/kernel/modules/xboxkrnl/module.cc @@ -10,6 +10,8 @@ #include "kernel/modules/xboxkrnl/module.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_memory.h" #include "kernel/modules/xboxkrnl/xboxkrnl_module.h" @@ -29,27 +31,28 @@ namespace { } -XboxkrnlModule::XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory, - shared_ptr resolver) : - KernelModule(pal, memory, resolver) { +XboxkrnlModule::XboxkrnlModule(Runtime* runtime) : + KernelModule(runtime) { + ExportResolver* resolver = export_resolver_.get(); + resolver->RegisterTable( "xboxkrnl.exe", xboxkrnl_export_table, XECOUNT(xboxkrnl_export_table)); // Setup the kernel state instance. // This is where all kernel objects are kept while running. - kernel_state = auto_ptr(new KernelState(pal, memory, resolver)); + kernel_state_ = auto_ptr(new KernelState(runtime)); // Register all exported functions. - RegisterHalExports(resolver.get(), kernel_state.get()); - RegisterMemoryExports(resolver.get(), kernel_state.get()); - RegisterModuleExports(resolver.get(), kernel_state.get()); - RegisterRtlExports(resolver.get(), kernel_state.get()); - RegisterThreadingExports(resolver.get(), kernel_state.get()); + RegisterHalExports(resolver, kernel_state_.get()); + RegisterMemoryExports(resolver, kernel_state_.get()); + RegisterModuleExports(resolver, kernel_state_.get()); + RegisterRtlExports(resolver, kernel_state_.get()); + RegisterThreadingExports(resolver, kernel_state_.get()); // TODO(benvanik): alloc heap memory somewhere in user space // 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 (?*) // 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() { } + +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; +} diff --git a/src/kernel/modules/xboxkrnl/module.h b/src/kernel/modules/xboxkrnl/module.h index acb0f207c..86562b3c9 100644 --- a/src/kernel/modules/xboxkrnl/module.h +++ b/src/kernel/modules/xboxkrnl/module.h @@ -26,12 +26,13 @@ class KernelState; class XboxkrnlModule : public KernelModule { public: - XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory, - shared_ptr resolver); + XboxkrnlModule(Runtime* runtime); virtual ~XboxkrnlModule(); + int LaunchModule(const xechar_t* path); + private: - auto_ptr kernel_state; + auto_ptr kernel_state_; }; diff --git a/src/kernel/modules/xboxkrnl/objects/sources.gypi b/src/kernel/modules/xboxkrnl/objects/sources.gypi new file mode 100644 index 000000000..6e37d0a86 --- /dev/null +++ b/src/kernel/modules/xboxkrnl/objects/sources.gypi @@ -0,0 +1,7 @@ +# Copyright 2013 Ben Vanik. All Rights Reserved. +{ + 'sources': [ + 'xmodule.cc', + 'xthread.cc', + ], +} diff --git a/src/kernel/user_module.cc b/src/kernel/modules/xboxkrnl/objects/xmodule.cc similarity index 54% rename from src/kernel/user_module.cc rename to src/kernel/modules/xboxkrnl/objects/xmodule.cc index d09cee74e..c7fd3ce7e 100644 --- a/src/kernel/user_module.cc +++ b/src/kernel/modules/xboxkrnl/objects/xmodule.cc @@ -7,244 +7,124 @@ ****************************************************************************** */ -#include +#include "kernel/modules/xboxkrnl/objects/xmodule.h" -#include +#include "kernel/modules/xboxkrnl/objects/xthread.h" using namespace xe; -using namespace kernel; +using namespace xe::kernel; +using namespace xe::kernel::xboxkrnl; -UserModule::UserModule(xe_memory_ref memory) { - memory_ = xe_memory_retain(memory); - xex_ = NULL; +namespace { } -UserModule::~UserModule() { - for (std::vector::iterator it = sections_.begin(); - it != sections_.end(); ++it) { - delete *it; - } - xe_xex2_release(xex_); - xe_memory_release(memory_); -} - -int UserModule::Load(const void* addr, const size_t length, - const xechar_t* path) { +XModule::XModule(KernelState* kernel_state, const char* path) : + XObject(kernel_state, kTypeModule), + xex_(NULL) { XEIGNORE(xestrcpy(path_, XECOUNT(path_), path)); const xechar_t *slash = xestrrchr(path, '/'); if (slash) { 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_; } -const xechar_t* UserModule::name() { +const char* XModule::name() { return name_; } -uint32_t UserModule::handle() { - return handle_; -} - -xe_xex2_ref UserModule::xex() { +xe_xex2_ref XModule::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_); } -void* UserModule::GetProcAddress(const uint32_t ordinal) { +X_STATUS XModule::LoadFromFile(const char* path) { + // TODO(benvanik): load from virtual filesystem. XEASSERTALWAYS(); - return NULL; + return X_STATUS_UNSUCCESSFUL; } -// 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; +X_STATUS XModule::LoadFromMemory(const void* addr, const size_t length) { + // Load the XEX into memory and decrypt. + xe_xex2_options_t xex_options; + xex_ = xe_xex2_load(kernel_state()->memory(), addr, length, xex_options); + XEEXPECTNOTNULL(xex_); -int UserModule::LoadPE() { - const xe_xex2_header_t* xex_header = xe_xex2_get_header(xex_); - uint8_t* mem = xe_memory_addr(memory_, 0); - const uint8_t* p = mem + xex_header->exe_address; + // Prepare the module for execution. + XEEXPECTZERO(kernel_state()->processor()->PrepareModule( + name_, path_, xex_, runtime()->export_resolver())); - // 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 = 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::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; + return X_STATUS_SUCCESS; XECLEANUP: - if (method_infos) { - xe_free(method_infos); - } - return 1; + return X_STATUS_UNSUCCESSFUL; } -void UserModule::Dump(ExportResolver* export_resolver) { - //const uint8_t *mem = (const uint8_t*)xe_memory_addr(memory_, 0); +X_STATUS XModule::GetSection(const char* name, + 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_); // XEX info. diff --git a/src/kernel/modules/xboxkrnl/objects/xmodule.h b/src/kernel/modules/xboxkrnl/objects/xmodule.h new file mode 100644 index 000000000..fc196e630 --- /dev/null +++ b/src/kernel/modules/xboxkrnl/objects/xmodule.h @@ -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 + +#include +#include +#include + + +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_ diff --git a/src/kernel/modules/xboxkrnl/objects/xthread.cc b/src/kernel/modules/xboxkrnl/objects/xthread.cc new file mode 100644 index 000000000..825665079 --- /dev/null +++ b/src/kernel/modules/xboxkrnl/objects/xthread.cc @@ -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(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(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(&thread_handle_), + &attr, + &XThreadStartCallbackPthreads, + this); + } else { + result_code = pthread_create( + reinterpret_cast(&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); +} diff --git a/src/kernel/modules/xboxkrnl/objects/xthread.h b/src/kernel/modules/xboxkrnl/objects/xthread.h new file mode 100644 index 000000000..69dacb05e --- /dev/null +++ b/src/kernel/modules/xboxkrnl/objects/xthread.h @@ -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 + + +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_ diff --git a/src/kernel/modules/xboxkrnl/sources.gypi b/src/kernel/modules/xboxkrnl/sources.gypi index dde3ff855..a1e37111c 100644 --- a/src/kernel/modules/xboxkrnl/sources.gypi +++ b/src/kernel/modules/xboxkrnl/sources.gypi @@ -8,5 +8,10 @@ 'xboxkrnl_module.cc', 'xboxkrnl_rtl.cc', 'xboxkrnl_threading.cc', + 'xobject.cc', + ], + + 'includes': [ + 'objects/sources.gypi', ], } diff --git a/src/kernel/modules/xboxkrnl/xboxkrnl_hal.cc b/src/kernel/modules/xboxkrnl/xboxkrnl_hal.cc index 333f86660..35475b0fc 100644 --- a/src/kernel/modules/xboxkrnl/xboxkrnl_hal.cc +++ b/src/kernel/modules/xboxkrnl/xboxkrnl_hal.cc @@ -9,8 +9,9 @@ #include "kernel/modules/xboxkrnl/xboxkrnl_hal.h" +#include + #include "kernel/shim_utils.h" -#include "kernel/modules/xboxkrnl/xboxkrnl.h" using namespace xe; diff --git a/src/kernel/modules/xboxkrnl/xboxkrnl_memory.cc b/src/kernel/modules/xboxkrnl/xboxkrnl_memory.cc index 46520c1d3..0c226de9f 100644 --- a/src/kernel/modules/xboxkrnl/xboxkrnl_memory.cc +++ b/src/kernel/modules/xboxkrnl/xboxkrnl_memory.cc @@ -9,8 +9,9 @@ #include "kernel/modules/xboxkrnl/xboxkrnl_memory.h" +#include + #include "kernel/shim_utils.h" -#include "kernel/modules/xboxkrnl/xboxkrnl.h" using namespace xe; @@ -82,7 +83,7 @@ void NtAllocateVirtualMemory_shim( // Allocate. uint32_t flags = 0; 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) { // Failed - assume no memory available. SHIM_SET_RETURN(X_STATUS_NO_MEMORY); @@ -123,7 +124,7 @@ void NtFreeVirtualMemory_shim( // Free. 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); if (!freed_size) { SHIM_SET_RETURN(X_STATUS_UNSUCCESSFUL); diff --git a/src/kernel/modules/xboxkrnl/xboxkrnl_module.cc b/src/kernel/modules/xboxkrnl/xboxkrnl_module.cc index e10511fbc..51645b08b 100644 --- a/src/kernel/modules/xboxkrnl/xboxkrnl_module.cc +++ b/src/kernel/modules/xboxkrnl/xboxkrnl_module.cc @@ -9,10 +9,10 @@ #include "kernel/modules/xboxkrnl/xboxkrnl_module.h" +#include #include #include "kernel/shim_utils.h" -#include "kernel/modules/xboxkrnl/xboxkrnl.h" using namespace xe; diff --git a/src/kernel/modules/xboxkrnl/xboxkrnl_rtl.cc b/src/kernel/modules/xboxkrnl/xboxkrnl_rtl.cc index b0bb3387b..8e2eb23bb 100644 --- a/src/kernel/modules/xboxkrnl/xboxkrnl_rtl.cc +++ b/src/kernel/modules/xboxkrnl/xboxkrnl_rtl.cc @@ -9,10 +9,10 @@ #include "kernel/modules/xboxkrnl/xboxkrnl_rtl.h" +#include #include #include "kernel/shim_utils.h" -#include "kernel/modules/xboxkrnl/xboxkrnl.h" using namespace xe; diff --git a/src/kernel/modules/xboxkrnl/xboxkrnl_threading.cc b/src/kernel/modules/xboxkrnl/xboxkrnl_threading.cc index 287e791cb..905122cab 100644 --- a/src/kernel/modules/xboxkrnl/xboxkrnl_threading.cc +++ b/src/kernel/modules/xboxkrnl/xboxkrnl_threading.cc @@ -9,8 +9,10 @@ #include "kernel/modules/xboxkrnl/xboxkrnl_threading.h" +#include + #include "kernel/shim_utils.h" -#include "kernel/modules/xboxkrnl/xboxkrnl.h" +#include "kernel/modules/xboxkrnl/objects/xthread.h" using namespace xe; @@ -54,19 +56,55 @@ namespace { // } -void ExCreateThread() { - // launch native thread - // -} - - void ExCreateThread_shim( 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( - 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, \ 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); diff --git a/src/kernel/modules/xboxkrnl/xobject.cc b/src/kernel/modules/xboxkrnl/xobject.cc new file mode 100644 index 000000000..4f3beb63e --- /dev/null +++ b/src/kernel/modules/xboxkrnl/xobject.cc @@ -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; + } +} diff --git a/src/kernel/modules/xboxkrnl/xobject.h b/src/kernel/modules/xboxkrnl/xobject.h new file mode 100644 index 000000000..c8121fa25 --- /dev/null +++ b/src/kernel/modules/xboxkrnl/xobject.h @@ -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 + + +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_ diff --git a/src/kernel/runtime.cc b/src/kernel/runtime.cc index be3a180fa..35165093d 100644 --- a/src/kernel/runtime.cc +++ b/src/kernel/runtime.cc @@ -25,22 +25,13 @@ Runtime::Runtime(xe_pal_ref pal, shared_ptr processor, XEIGNORE(xestrcpy(command_line_, XECOUNT(command_line_), command_line)); export_resolver_ = shared_ptr(new ExportResolver()); - kernel_modules_.push_back( - new xboxkrnl::XboxkrnlModule(pal_, memory_, export_resolver_)); - kernel_modules_.push_back( - new xam::XamModule(pal_, memory_, export_resolver_)); + xboxkrnl_ = auto_ptr( + new xboxkrnl::XboxkrnlModule(this)); + xam_ = auto_ptr( + new xam::XamModule(this)); } Runtime::~Runtime() { - for (std::map::iterator it = - user_modules_.begin(); it != user_modules_.end(); ++it) { - delete it->second; - } - for (std::vector::iterator it = kernel_modules_.begin(); - it != kernel_modules_.end(); ++it) { - delete *it; - } - xe_memory_release(memory_); xe_pal_release(pal_); } @@ -65,117 +56,6 @@ const xechar_t* Runtime::command_line() { return command_line_; } -int Runtime::LoadBinaryModule(const xechar_t* path, uint32_t start_address) { - const xechar_t* name = xestrrchr(path, '/') + 1; - - // 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(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::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(); +int Runtime::LaunchModule(const xechar_t* path) { + return xboxkrnl_->LaunchModule(path); } diff --git a/src/kernel/sources.gypi b/src/kernel/sources.gypi index 195a13224..9b88b39d5 100644 --- a/src/kernel/sources.gypi +++ b/src/kernel/sources.gypi @@ -3,7 +3,6 @@ 'sources': [ 'export.cc', 'runtime.cc', - 'user_module.cc', 'xex2.cc', ], diff --git a/src/kernel/xex2.cc b/src/kernel/xex2.cc index 00a1dc480..172722dc8 100644 --- a/src/kernel/xex2.cc +++ b/src/kernel/xex2.cc @@ -9,11 +9,14 @@ #include +#include + #include #include #include #include #include +#include typedef struct xe_xex2 { @@ -22,6 +25,8 @@ typedef struct xe_xex2 { xe_memory_ref memory; xe_xex2_header_t header; + + std::vector* sections; } 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, const uint8_t *xex_addr, const size_t xex_length, xe_memory_ref memory); +int xe_xex2_load_pe(xe_xex2_ref xex); 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); xex->memory = xe_memory_retain(memory); + xex->sections = new std::vector(); 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_load_pe(xex)); + return xex; XECLEANUP: @@ -55,6 +64,11 @@ XECLEANUP: } void xe_xex2_dealloc(xe_xex2_ref xex) { + for (std::vector::iterator it = xex->sections->begin(); + it != xex->sections->end(); ++it) { + delete *it; + } + xe_xex2_header_t *header = &xex->header; xe_free(header->sections); 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::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, const xe_xex2_import_library_t *library, xe_xex2_import_info_t **out_import_infos, diff --git a/tools/tools.gypi b/tools/tools.gypi index 9b1934551..4abe34578 100644 --- a/tools/tools.gypi +++ b/tools/tools.gypi @@ -1,7 +1,6 @@ # Copyright 2013 Ben Vanik. All Rights Reserved. { 'includes': [ - 'xenia-info/xenia-info.gypi', 'xenia-run/xenia-run.gypi', 'xenia-test/xenia-test.gypi', ], diff --git a/tools/xenia-info/xenia-info.cc b/tools/xenia-info/xenia-info.cc deleted file mode 100644 index 15cee825c..000000000 --- a/tools/xenia-info/xenia-info.cc +++ /dev/null @@ -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 - -#include - - -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; - shared_ptr 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(new Processor(pal, memory)); - XEEXPECTZERO(processor->Setup()); - - runtime = shared_ptr(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); diff --git a/tools/xenia-info/xenia-info.gypi b/tools/xenia-info/xenia-info.gypi deleted file mode 100644 index 479aa05c1..000000000 --- a/tools/xenia-info/xenia-info.gypi +++ /dev/null @@ -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', - ], - }, - ], -} diff --git a/tools/xenia-run/xenia-run.cc b/tools/xenia-run/xenia-run.cc index ba8a5edcd..8fb3a643b 100644 --- a/tools/xenia-run/xenia-run.cc +++ b/tools/xenia-run/xenia-run.cc @@ -26,15 +26,14 @@ public: Run(); ~Run(); - int Setup(const xechar_t* path); - int Launch(); + int Setup(); + int Launch(const xechar_t* path); private: xe_pal_ref pal_; xe_memory_ref memory_; shared_ptr processor_; shared_ptr runtime_; - UserModule* module_; }; Run::Run() { @@ -45,7 +44,7 @@ Run::~Run() { xe_pal_release(pal_); } -int Run::Setup(const xechar_t* path) { +int Run::Setup() { xe_pal_options_t pal_options; xe_zero_struct(&pal_options, sizeof(pal_options)); pal_ = xe_pal_create(pal_options); @@ -61,21 +60,19 @@ int Run::Setup(const xechar_t* path) { runtime_ = shared_ptr(new Runtime(pal_, processor_, XT(""))); - XEEXPECTZERO(runtime_->LoadModule(path)); - module_ = runtime_->GetModule(path); - return 0; XECLEANUP: return 1; } -int Run::Launch() { +int Run::Launch(const xechar_t* path) { if (FLAGS_abort_before_entry) { return 0; } // TODO(benvanik): wait until the module thread exits - runtime_->LaunchModule(module_); + runtime_->LaunchModule(path); + return 0; } @@ -100,12 +97,12 @@ int xenia_run(int argc, xechar_t **argv) { auto_ptr run = auto_ptr(new Run()); - result_code = run->Setup(path); + result_code = run->Setup(); XEEXPECTZERO(result_code); //xe_module_dump(run->module); - run->Launch(); + run->Launch(path); result_code = 0; XECLEANUP: diff --git a/tools/xenia-test/xenia-test.cc b/tools/xenia-test/xenia-test.cc index 4c93daa8a..a36069036 100644 --- a/tools/xenia-test/xenia-test.cc +++ b/tools/xenia-test/xenia-test.cc @@ -126,10 +126,11 @@ int run_test(xe_pal_ref pal, string& src_file_path) { runtime = shared_ptr(new Runtime(pal, processor, XT(""))); // 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. - thread_state = processor->AllocThread(0x80000000, 256 * 1024 * 1024); + thread_state = processor->AllocThread(256 * 1024 * 1024, 0); // Setup test state from annotations. XEEXPECTZERO(setup_test_state(memory, processor.get(), thread_state, @@ -157,6 +158,10 @@ int discover_tests(string& test_path, vector& test_files) { // TODO(benvanik): use PAL instead of this 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; while ((dir = readdir(d))) { if (dir->d_type == DT_REG) {