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;;
|
||||
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 "$@"
|
||||
|
|
|
@ -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': [
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <xenia/core/file.h>
|
||||
#include <xenia/core/memory.h>
|
||||
#include <xenia/core/mmap.h>
|
||||
#include <xenia/core/mutex.h>
|
||||
#include <xenia/core/pal.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/core/memory.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/user_module.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/user_module.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
|
@ -49,7 +49,7 @@ public:
|
|||
shared_ptr<llvm::ExecutionEngine>& 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);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <xenia/cpu/exec_module.h>
|
||||
#include <xenia/cpu/thread_state.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/user_module.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
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<kernel::ExportResolver> export_resolver);
|
||||
int PrepareModule(kernel::UserModule* user_module,
|
||||
int LoadBinary(const xechar_t* path, uint32_t start_address,
|
||||
shared_ptr<kernel::ExportResolver> export_resolver);
|
||||
|
||||
int PrepareModule(const char* name, const char* path, xe_xex2_ref xex,
|
||||
shared_ptr<kernel::ExportResolver> 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);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/user_module.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
||||
|
|
|
@ -14,30 +14,36 @@
|
|||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/runtime.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
|
||||
class Runtime;
|
||||
|
||||
|
||||
class KernelModule {
|
||||
public:
|
||||
KernelModule(xe_pal_ref pal, xe_memory_ref memory,
|
||||
shared_ptr<ExportResolver> 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<ExportResolver> resolver_;
|
||||
Runtime* runtime_;
|
||||
xe_pal_ref pal_;
|
||||
xe_memory_ref memory_;
|
||||
shared_ptr<ExportResolver> export_resolver_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <xenia/cpu.h>
|
||||
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/user_module.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
|
@ -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<ExportResolver> 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<ExportResolver> export_resolver_;
|
||||
|
||||
std::vector<KernelModule*> kernel_modules_;
|
||||
std::map<const xechar_t*, UserModule*> user_modules_;
|
||||
auto_ptr<xboxkrnl::XboxkrnlModule> xboxkrnl_;
|
||||
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_
|
||||
#define XENIA_KERNEL_MODULES_XBOXKRNL_H_
|
||||
#ifndef XENIA_KERNEL_XBOX_H_
|
||||
#define XENIA_KERNEL_XBOX_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
@ -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_
|
|
@ -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,
|
||||
|
|
|
@ -9,12 +9,14 @@
|
|||
|
||||
#include <xenia/core/memory.h>
|
||||
|
||||
#include <xenia/core/mutex.h>
|
||||
|
||||
#if !XE_PLATFORM(WIN32)
|
||||
#include <sys/mman.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -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"', {
|
||||
'sources': [
|
||||
'mmap_posix.cc',
|
||||
'mutex_posix.cc',
|
||||
],
|
||||
}],
|
||||
['OS == "win"', {
|
||||
'sources': [
|
||||
'mmap_win.cc',
|
||||
'mutex_win.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;
|
||||
|
|
|
@ -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<sdb::SymbolDatabase>(
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -91,6 +91,10 @@ void XeTraceInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
|
|||
type && type->emit ? " " : "X",
|
||||
type ? type->name : "<unknown>");
|
||||
|
||||
if (cia == 0x82014468) {
|
||||
printf("BREAKBREAKBREAK\n");
|
||||
}
|
||||
|
||||
// TODO(benvanik): better disassembly, printing of current register values/etc
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ExportResolver> 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<ExportResolver> 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<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(
|
||||
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()) {
|
||||
|
|
116
src/cpu/sdb.cc
116
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 =
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ExportResolver> 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<XamState>(new XamState(pal, memory, resolver));
|
||||
xam_state = auto_ptr<XamState>(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() {
|
||||
|
|
|
@ -26,8 +26,7 @@ class XamState;
|
|||
|
||||
class XamModule : public KernelModule {
|
||||
public:
|
||||
XamModule(xe_pal_ref pal, xe_memory_ref memory,
|
||||
shared_ptr<ExportResolver> resolver);
|
||||
XamModule(Runtime* runtime);
|
||||
virtual ~XamModule();
|
||||
|
||||
private:
|
||||
|
|
|
@ -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<ExportResolver> 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<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/kernel_module.h>
|
||||
#include <xenia/kernel/xbox.h>
|
||||
|
||||
|
||||
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<ExportResolver> 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<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/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<ExportResolver> 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<KernelState>(new KernelState(pal, memory, resolver));
|
||||
kernel_state_ = auto_ptr<KernelState>(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;
|
||||
}
|
||||
|
|
|
@ -26,12 +26,13 @@ class KernelState;
|
|||
|
||||
class XboxkrnlModule : public KernelModule {
|
||||
public:
|
||||
XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory,
|
||||
shared_ptr<ExportResolver> resolver);
|
||||
XboxkrnlModule(Runtime* runtime);
|
||||
virtual ~XboxkrnlModule();
|
||||
|
||||
int LaunchModule(const xechar_t* path);
|
||||
|
||||
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 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<PESection*>::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<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;
|
||||
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.
|
|
@ -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_rtl.cc',
|
||||
'xboxkrnl_threading.cc',
|
||||
'xobject.cc',
|
||||
],
|
||||
|
||||
'includes': [
|
||||
'objects/sources.gypi',
|
||||
],
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
|
||||
#include "kernel/modules/xboxkrnl/xboxkrnl_hal.h"
|
||||
|
||||
#include <xenia/kernel/xbox.h>
|
||||
|
||||
#include "kernel/shim_utils.h"
|
||||
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
|
||||
|
||||
|
||||
using namespace xe;
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
|
||||
#include "kernel/modules/xboxkrnl/xboxkrnl_memory.h"
|
||||
|
||||
#include <xenia/kernel/xbox.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
#include "kernel/modules/xboxkrnl/xboxkrnl_module.h"
|
||||
|
||||
#include <xenia/kernel/xbox.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
#include "kernel/shim_utils.h"
|
||||
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
|
||||
|
||||
|
||||
using namespace xe;
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
#include "kernel/modules/xboxkrnl/xboxkrnl_rtl.h"
|
||||
|
||||
#include <xenia/kernel/xbox.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
#include "kernel/shim_utils.h"
|
||||
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
|
||||
|
||||
|
||||
using namespace xe;
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
|
||||
#include "kernel/modules/xboxkrnl/xboxkrnl_threading.h"
|
||||
|
||||
#include <xenia/kernel/xbox.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
export_resolver_ = shared_ptr<ExportResolver>(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<xboxkrnl::XboxkrnlModule>(
|
||||
new xboxkrnl::XboxkrnlModule(this));
|
||||
xam_ = auto_ptr<xam::XamModule>(
|
||||
new xam::XamModule(this));
|
||||
}
|
||||
|
||||
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_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<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();
|
||||
int Runtime::LaunchModule(const xechar_t* path) {
|
||||
return xboxkrnl_->LaunchModule(path);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
'sources': [
|
||||
'export.cc',
|
||||
'runtime.cc',
|
||||
'user_module.cc',
|
||||
'xex2.cc',
|
||||
],
|
||||
|
||||
|
|
|
@ -9,11 +9,14 @@
|
|||
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <third_party/crypto/rijndael-alg-fst.h>
|
||||
#include <third_party/crypto/rijndael-alg-fst.c>
|
||||
#include <third_party/mspack/lzx.h>
|
||||
#include <third_party/mspack/lzxd.c>
|
||||
#include <third_party/mspack/mspack.h>
|
||||
#include <third_party/pe/pe_image.h>
|
||||
|
||||
|
||||
typedef struct xe_xex2 {
|
||||
|
@ -22,6 +25,8 @@ typedef struct xe_xex2 {
|
|||
xe_memory_ref memory;
|
||||
|
||||
xe_xex2_header_t header;
|
||||
|
||||
std::vector<PESection*>* 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<PESection*>();
|
||||
|
||||
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<PESection*>::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<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,
|
||||
const xe_xex2_import_library_t *library,
|
||||
xe_xex2_import_info_t **out_import_infos,
|
||||
|
|
|
@ -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',
|
||||
],
|
||||
|
|
|
@ -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();
|
||||
|
||||
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> processor_;
|
||||
shared_ptr<Runtime> 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<Runtime>(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> run = auto_ptr<Run>(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:
|
||||
|
|
|
@ -126,10 +126,11 @@ int run_test(xe_pal_ref pal, string& src_file_path) {
|
|||
runtime = shared_ptr<Runtime>(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<string>& 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) {
|
||||
|
|
Loading…
Reference in New Issue