From 5c2060af72af61f9825d351801a3cc79359de507 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Mon, 28 Jan 2013 03:03:37 -0800 Subject: [PATCH] Now running up to the first required kernel call. If memory address validation is turned off it runs a lot further. --- TODO.md | 5 +- common.gypi | 3 +- .../xenia/cpu/codegen/function_generator.h | 2 +- include/xenia/cpu/sdb.h | 3 + include/xenia/kernel/export.h | 31 +++++-- private/runtest.sh | 6 +- src/cpu/codegen/emit_alu.cc | 35 ++++---- src/cpu/codegen/emit_control.cc | 13 ++- src/cpu/codegen/function_generator.cc | 81 ++++++++++++++---- src/cpu/codegen/module_generator.cc | 51 +++++++++-- src/cpu/exec_module.cc | 44 +++++++++- src/cpu/processor.cc | 26 ++++-- src/cpu/sdb.cc | 13 ++- src/kernel/export.cc | 44 ++++++---- .../modules/xboxkrnl/xboxkrnl_module.cc | 44 ++++++++++ src/kernel/user_module.cc | 4 +- test/codegen/extrwi.bin | Bin 0 -> 8 bytes test/codegen/extrwi.dis | 9 ++ test/codegen/extrwi.s | 11 +++ tools/xenia-run/xenia-run.cc | 8 ++ 20 files changed, 342 insertions(+), 91 deletions(-) create mode 100755 test/codegen/extrwi.bin create mode 100644 test/codegen/extrwi.dis create mode 100644 test/codegen/extrwi.s diff --git a/TODO.md b/TODO.md index 8a20b909f..63d7eaf4d 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,7 @@ +## Kernel + +NtAllocateVirtualMemory + ## Instructions ### XER CA bit (carry) @@ -6,7 +10,6 @@ Not sure the way I'm doing this is right. addic/subficx/etc set it to the value of the overflow bit from the LLVM *_with_overflow intrinsic. ``` -rlwinmx rlwimix rldiclx ``` diff --git a/common.gypi b/common.gypi index 5699206b0..216b4dd41 100644 --- a/common.gypi +++ b/common.gypi @@ -11,7 +11,8 @@ }, # LLVM paths. - 'llvm_path': 'build/llvm/release/', + # TODO(benvanik): switch based on configuration. + 'llvm_path': 'build/llvm/debug/', 'llvm_config': '<(llvm_path)bin/llvm-config', }, diff --git a/include/xenia/cpu/codegen/function_generator.h b/include/xenia/cpu/codegen/function_generator.h index 8889c0d99..600a63403 100644 --- a/include/xenia/cpu/codegen/function_generator.h +++ b/include/xenia/cpu/codegen/function_generator.h @@ -85,7 +85,7 @@ public: void update_gpr_value(uint32_t n, llvm::Value* value); llvm::Value* GetMembase(); - llvm::Value* memory_addr(uint32_t addr); + llvm::Value* GetMemoryAddress(llvm::Value* addr); llvm::Value* ReadMemory(llvm::Value* addr, uint32_t size, bool extend); void WriteMemory(llvm::Value* addr, uint32_t size, llvm::Value* value); diff --git a/include/xenia/cpu/sdb.h b/include/xenia/cpu/sdb.h index fe9e70ec2..d55b0d37f 100644 --- a/include/xenia/cpu/sdb.h +++ b/include/xenia/cpu/sdb.h @@ -128,6 +128,8 @@ public: uint32_t address; char* name; + + kernel::KernelExport* kernel_export; }; class ExceptionEntrySymbol : public Symbol { @@ -154,6 +156,7 @@ public: VariableSymbol* GetVariable(uint32_t address); Symbol* GetSymbol(uint32_t address); + int GetAllVariables(std::vector& variables); int GetAllFunctions(std::vector& functions); void Write(const char* file_name); diff --git a/include/xenia/kernel/export.h b/include/xenia/kernel/export.h index 128da29e4..e8a3d7473 100644 --- a/include/xenia/kernel/export.h +++ b/include/xenia/kernel/export.h @@ -15,11 +15,15 @@ #include +typedef struct xe_ppc_state xe_ppc_state_t; + + namespace xe { namespace kernel { -typedef void (*xe_kernel_export_fn)(); +typedef void (*xe_kernel_export_impl_fn)(); +typedef void (*xe_kernel_export_shim_fn)(xe_ppc_state_t* state); class KernelExport { public: @@ -34,21 +38,24 @@ public: char signature[16]; char name[96]; + bool is_implemented; + union { // Variable data. Only valid when kXEKernelExportFlagVariable is set. - void *variable_data; + // This is an address in the client memory space that the variable can + // be found at. + uint32_t variable_ptr; struct { - // Real function implementation (if present). - xe_kernel_export_fn impl; + // Shimmed implementation. + // This is called directly from generated code. + // It should parse args, do fixups, and call the impl. + xe_kernel_export_shim_fn shim; - // Shimmed implementation (call if param structs are big endian). - // This may be NULL if no shim is needed or present. - xe_kernel_export_fn shim; + // Real function implementation. + xe_kernel_export_impl_fn impl; } function_data; }; - - bool IsImplemented(); }; #define XE_DECLARE_EXPORT(module, ordinal, name, signature, type, flags) \ @@ -73,6 +80,12 @@ public: const uint32_t ordinal); KernelExport* GetExportByName(const char* library_name, const char* name); + void SetVariableMapping(const char* library_name, const uint32_t ordinal, + uint32_t value); + void SetFunctionMapping(const char* library_name, const uint32_t ordinal, + xe_kernel_export_shim_fn shim, + xe_kernel_export_impl_fn impl); + private: class ExportTable { public: diff --git a/private/runtest.sh b/private/runtest.sh index c8c145ef5..5f6ab943d 100755 --- a/private/runtest.sh +++ b/private/runtest.sh @@ -10,9 +10,11 @@ fi private/$1 \ --optimize_ir_modules=true \ --optimize_ir_functions=true \ + --memory_address_verification=true \ --trace_kernel_calls=true \ - --trace_user_calls=false \ - --trace_instructions=false \ + --trace_user_calls=true \ + --trace_instructions=true \ + --abort_before_entry=true \ 1>build/run.txt #2>build/run.llvm.txt \ diff --git a/src/cpu/codegen/emit_alu.cc b/src/cpu/codegen/emit_alu.cc index e1eaacc0f..469ca86d0 100644 --- a/src/cpu/codegen/emit_alu.cc +++ b/src/cpu/codegen/emit_alu.cc @@ -779,12 +779,13 @@ XEEMITTER(rlwinmx, 0x54000000, M )(FunctionGenerator& g, IRBuilder<>& b, I // m <- MASK(MB+32, ME+32) // RA <- r & m - // The compiler will generate a bunch of these for the special case of - // SH=0, MB=ME - // Which seems to just select a single bit and set cr0 for use with a branch. + // The compiler will generate a bunch of these for the special case of SH=0. + // Which seems to just select some bits and set cr0 for use with a branch. // We can detect this and do less work. - if (!i.M.SH && i.M.MB == i.M.ME) { - Value* v = b.CreateAnd(g.gpr_value(i.M.RS), 1 << i.M.MB); + if (!i.M.SH) { + Value* v = b.CreateAnd(b.CreateTrunc(g.gpr_value(i.M.RS), b.getInt32Ty()), + b.getInt32(XEMASK(i.M.MB + 32, i.M.ME + 32))); + v = b.CreateZExt(v, b.getInt64Ty()); g.update_gpr_value(i.M.RA, v); if (i.M.Rc) { // With cr0 update. @@ -793,20 +794,20 @@ XEEMITTER(rlwinmx, 0x54000000, M )(FunctionGenerator& g, IRBuilder<>& b, I return 0; } - // // ROTL32(x, y) = rotl(i64.(x||x), y) - // Value* v = b.CreateZExt(b.CreateTrunc(g.gpr_value(i.M.RS)), b.getInt64Ty()); - // v = b.CreateOr(b.CreateLShr(v, 32), v); - // // (v << shift) | (v >> (64 - shift)); - // v = b.CreateOr(b.CreateShl(v, i.M.SH), b.CreateLShr(v, 32 - i.M.SH)); - // v = b.CreateAnd(v, XEMASK(i.M.MB + 32, i.M.ME + 32)); + // ROTL32(x, y) = rotl(i64.(x||x), y) + Value* v = b.CreateAnd(g.gpr_value(i.M.RS), UINT32_MAX); + v = b.CreateOr(b.CreateShl(v, 32), v); + // (v << shift) | (v >> (32 - shift)); + v = b.CreateOr(b.CreateShl(v, i.M.SH), b.CreateLShr(v, 32 - i.M.SH)); + v = b.CreateAnd(v, XEMASK(i.M.MB + 32, i.M.ME + 32)); + g.update_gpr_value(i.M.RA, v); - // if (i.M.Rc) { - // // With cr0 update. - // g.update_cr_with_cond(0, v, b.getInt64(0), true); - // } + if (i.M.Rc) { + // With cr0 update. + g.update_cr_with_cond(0, v, b.getInt64(0), true); + } - XEINSTRNOTIMPLEMENTED(); - return 1; + return 0; } XEEMITTER(rlwnmx, 0x5C000000, M )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { diff --git a/src/cpu/codegen/emit_control.cc b/src/cpu/codegen/emit_control.cc index dc9557cac..d8f8ee981 100644 --- a/src/cpu/codegen/emit_control.cc +++ b/src/cpu/codegen/emit_control.cc @@ -90,13 +90,18 @@ int XeEmitBranchTo( Function* target_fn = g.GetFunction(fn_block->outgoing_function); Function::arg_iterator args = g.gen_fn()->arg_begin(); Value* state_ptr = args; - b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4)); BasicBlock* next_bb = g.GetNextBasicBlock(); if (!lk || !next_bb) { // Tail. No need to refill the local register values, just return. + // We optimize this by passing in the LR from our parent instead of the + // next instruction. This allows the return from our callee to pop + // all the way up. + b.CreateCall2(target_fn, state_ptr, ++args); b.CreateRetVoid(); } else { + // Will return here eventually. // Refill registers from state. + b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4)); g.FillRegisters(); b.CreateBr(next_bb); } @@ -188,7 +193,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I // Ignore cond. } else { Value* cr = g.cr_value(i.B.BI >> 2); - cr = b.CreateAnd(cr, 1 << (i.B.BI >> 2)); + cr = b.CreateAnd(cr, 1 << (i.B.BI & 3)); if (XESELECTBITS(i.B.BO, 3, 3)) { cond_ok = b.CreateICmpNE(cr, b.getInt64(0)); } else { @@ -257,7 +262,7 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I // Ignore cond. } else { Value* cr = g.cr_value(i.XL.BI >> 2); - cr = b.CreateAnd(cr, 1 << (i.XL.BI >> 2)); + cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3)); if (XESELECTBITS(i.XL.BO, 3, 3)) { cond_ok = b.CreateICmpNE(cr, b.getInt64(0)); } else { @@ -336,7 +341,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I // Ignore cond. } else { Value* cr = g.cr_value(i.XL.BI >> 2); - cr = b.CreateAnd(cr, 1 << (i.XL.BI >> 2)); + cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3)); if (XESELECTBITS(i.XL.BO, 3, 3)) { cond_ok = b.CreateICmpNE(cr, b.getInt64(0)); } else { diff --git a/src/cpu/codegen/function_generator.cc b/src/cpu/codegen/function_generator.cc index 4a9630c87..fd28a8b9e 100644 --- a/src/cpu/codegen/function_generator.cc +++ b/src/cpu/codegen/function_generator.cc @@ -9,6 +9,8 @@ #include +#include + #include #include "cpu/cpu-private.h" @@ -20,6 +22,10 @@ using namespace xe::cpu::ppc; using namespace xe::cpu::sdb; +DEFINE_bool(memory_address_verification, false, + "Whether to add additional checks to generated memory load/stores."); + + /** * This generates function code. * One context is created for each function to generate. Each basic block in @@ -752,9 +758,6 @@ void FunctionGenerator::update_cr_with_cond( // bit1 = RA > RB // bit2 = RA = RB // bit3 = XER[SO] - // Bits are reversed: - // 0123 - // 3210 // TODO(benvanik): inline this using the x86 cmp instruction - this prevents // the need for a lot of the compares and ensures we lower to the best @@ -770,8 +773,8 @@ void FunctionGenerator::update_cr_with_cond( b.CreateICmpSLT(lhs, rhs) : b.CreateICmpULT(lhs, rhs); Value* is_gt = is_signed ? b.CreateICmpSGT(lhs, rhs) : b.CreateICmpUGT(lhs, rhs); - Value* cp = b.CreateSelect(is_gt, b.getInt8(1 << 2), b.getInt8(1 << 1)); - Value* c = b.CreateSelect(is_lt, b.getInt8(1 << 3), cp); + Value* cp = b.CreateSelect(is_gt, b.getInt8(1 << 1), b.getInt8(1 << 2)); + Value* c = b.CreateSelect(is_lt, b.getInt8(1 << 0), cp); // TODO(benvanik): set bit 4 to XER[SO] @@ -834,26 +837,53 @@ Value* FunctionGenerator::GetMembase() { return builder_->CreateLoad(v); } -Value* FunctionGenerator::memory_addr(uint32_t addr) { - return NULL; +Value* FunctionGenerator::GetMemoryAddress(Value* addr) { + IRBuilder<>& b = *builder_; + + // Input address is always in 32-bit space. + addr = b.CreateAnd(addr, UINT_MAX); + + // Add runtime memory address checks, if needed. + if (FLAGS_memory_address_verification) { + BasicBlock* invalid_bb = BasicBlock::Create(*context_, "", gen_fn_); + BasicBlock* valid_bb = BasicBlock::Create(*context_, "", gen_fn_); + + Value* gt = b.CreateICmpUGE(addr, b.getInt64(0x10000000)); + b.CreateCondBr(gt, valid_bb, invalid_bb); + + b.SetInsertPoint(invalid_bb); + Function* debugtrap = Intrinsic::getDeclaration( + gen_module_, Intrinsic::debugtrap); + b.CreateCall(debugtrap); + b.CreateBr(valid_bb); + + b.SetInsertPoint(valid_bb); + } + + // Rebase off of memory base pointer. + return b.CreateInBoundsGEP(GetMembase(), addr); } Value* FunctionGenerator::ReadMemory(Value* addr, uint32_t size, bool extend) { IRBuilder<>& b = *builder_; Type* dataTy = NULL; + bool needs_swap = false; switch (size) { case 1: dataTy = b.getInt8Ty(); break; case 2: dataTy = b.getInt16Ty(); + needs_swap = true; break; case 4: dataTy = b.getInt32Ty(); + needs_swap = true; break; case 8: dataTy = b.getInt64Ty(); + needs_swap = true; break; default: XEASSERTALWAYS(); @@ -861,31 +891,41 @@ Value* FunctionGenerator::ReadMemory(Value* addr, uint32_t size, bool extend) { } PointerType* pointerTy = PointerType::getUnqual(dataTy); - // Input address is always in 32-bit space. - addr = b.CreateAnd(addr, UINT_MAX); - - Value* offset_addr = b.CreateAdd(addr, b.getInt64(size)); - Value* address = b.CreateInBoundsGEP(GetMembase(), offset_addr); + Value* address = GetMemoryAddress(addr); Value* ptr = b.CreatePointerCast(address, pointerTy); - return b.CreateLoad(ptr); + Value* value = b.CreateLoad(ptr); + + // Swap after loading. + // TODO(benvanik): find a way to avoid this! + if (needs_swap) { + Function* bswap = Intrinsic::getDeclaration( + gen_module_, Intrinsic::bswap, dataTy); + value = b.CreateCall(bswap, value); + } + + return value; } void FunctionGenerator::WriteMemory(Value* addr, uint32_t size, Value* value) { IRBuilder<>& b = *builder_; Type* dataTy = NULL; + bool needs_swap = false; switch (size) { case 1: dataTy = b.getInt8Ty(); break; case 2: dataTy = b.getInt16Ty(); + needs_swap = true; break; case 4: dataTy = b.getInt32Ty(); + needs_swap = true; break; case 8: dataTy = b.getInt64Ty(); + needs_swap = true; break; default: XEASSERTALWAYS(); @@ -893,16 +933,21 @@ void FunctionGenerator::WriteMemory(Value* addr, uint32_t size, Value* value) { } PointerType* pointerTy = PointerType::getUnqual(dataTy); - // Input address is always in 32-bit space. - addr = b.CreateAnd(addr, UINT_MAX); - - Value* offset_addr = b.CreateAdd(addr, b.getInt64(size)); - Value* address = b.CreateInBoundsGEP(GetMembase(), offset_addr); + Value* address = GetMemoryAddress(addr); Value* ptr = b.CreatePointerCast(address, pointerTy); // Truncate, if required. if (value->getType() != dataTy) { value = b.CreateTrunc(value, dataTy); } + + // Swap before storing. + // TODO(benvanik): find a way to avoid this! + if (needs_swap) { + Function* bswap = Intrinsic::getDeclaration( + gen_module_, Intrinsic::bswap, dataTy); + value = b.CreateCall(bswap, value); + } + b.CreateStore(value, ptr); } diff --git a/src/cpu/codegen/module_generator.cc b/src/cpu/codegen/module_generator.cc index 79a7ba734..e5af317a3 100644 --- a/src/cpu/codegen/module_generator.cc +++ b/src/cpu/codegen/module_generator.cc @@ -102,7 +102,7 @@ int ModuleGenerator::Generate() { PrepareFunction(fn); break; case FunctionSymbol::Kernel: - if (fn->kernel_export && fn->kernel_export->IsImplemented()) { + if (fn->kernel_export && fn->kernel_export->is_implemented) { AddPresentImport(fn); } else { AddMissingImport(fn); @@ -191,20 +191,19 @@ void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) { // Create the function (and setup args/attributes/etc). Function* f = CreateFunctionDefinition(fn->name); - // TODO(benvanik): log errors. BasicBlock* block = BasicBlock::Create(context, "entry", f); - IRBuilder<> builder(block); + IRBuilder<> b(block); if (FLAGS_trace_kernel_calls) { Value* traceKernelCall = m->getFunction("XeTraceKernelCall"); - builder.CreateCall3( + b.CreateCall3( traceKernelCall, f->arg_begin(), - builder.getInt64(fn->start_address), + b.getInt64(fn->start_address), ++f->arg_begin()); } - builder.CreateRetVoid(); + b.CreateRetVoid(); OptimizeFunction(m, f); @@ -219,10 +218,44 @@ void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) { } void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) { - // Module *m = gen_module_; - // LLVMContext& context = m->getContext(); + Module *m = gen_module_; + LLVMContext& context = m->getContext(); - // TODO(benvanik): add import thunk code. + // Add the extern. + char shim_name[256]; + xesnprintfa(shim_name, XECOUNT(shim_name), + "__shim__%s", fn->kernel_export->name); + std::vector shimArgs; + shimArgs.push_back(PointerType::getUnqual(Type::getInt8Ty(context))); + FunctionType* shimTy = FunctionType::get( + Type::getVoidTy(context), shimArgs, false); + Function* shim = Function::Create( + shimTy, Function::ExternalLinkage, shim_name, m); + // engine_->addGlobalMapping(shim, + // (void*)fn->kernel_export->function_data.shim); + + // Create the function (and setup args/attributes/etc). + Function* f = CreateFunctionDefinition(fn->name); + + BasicBlock* block = BasicBlock::Create(context, "entry", f); + IRBuilder<> b(block); + + if (FLAGS_trace_kernel_calls) { + Value* traceKernelCall = m->getFunction("XeTraceKernelCall"); + b.CreateCall3( + traceKernelCall, + f->arg_begin(), + b.getInt64(fn->start_address), + ++f->arg_begin()); + } + + b.CreateCall( + shim, + f->arg_begin()); + + b.CreateRetVoid(); + + OptimizeFunction(m, f); } void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) { diff --git a/src/cpu/exec_module.cc b/src/cpu/exec_module.cc index 133d08810..719206782 100644 --- a/src/cpu/exec_module.cc +++ b/src/cpu/exec_module.cc @@ -41,6 +41,7 @@ using namespace llvm; using namespace xe; using namespace xe::cpu; using namespace xe::cpu::codegen; +using namespace xe::cpu::sdb; using namespace xe::kernel; @@ -72,7 +73,16 @@ int ExecModule::PrepareUserModule(kernel::UserModule* user_module) { sdb_ = shared_ptr( new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), user_module)); - return Prepare(); + int result_code = Prepare(); + if (result_code) { + return result_code; + } + + // Import variables. + xe_xex2_ref xex = user_module->xex(); + xe_xex2_release(xex); + + return 0; } int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) { @@ -339,6 +349,38 @@ int ExecModule::InjectGlobals() { } int ExecModule::Init() { + // Setup all kernel variables. + std::vector variables; + if (sdb_->GetAllVariables(variables)) { + return 1; + } + uint8_t* mem = xe_memory_addr(memory_, 0); + for (std::vector::iterator it = variables.begin(); + it != variables.end(); ++it) { + VariableSymbol* var = *it; + if (!var->kernel_export) { + continue; + } + KernelExport* kernel_export = var->kernel_export; + + // Grab, if available. + uint32_t* slot = (uint32_t*)(mem + var->address); + if (kernel_export->type == KernelExport::Function) { + // Not exactly sure what this should be... + // TODO(benvanik): find out what import variables are. + } else { + if (kernel_export->is_implemented) { + // Implemented - replace with pointer. + *slot = kernel_export->variable_ptr; + } else { + // Not implemented - write with a dummy value. + *slot = 0xDEADBEEF; + XELOGCPU("WARNING: imported a variable with no value: %s", + kernel_export->name); + } + } + } + // Run static initializers. I'm not sure we'll have any, but who knows. engine_->runStaticConstructorsDestructors(gen_module_.get(), false); diff --git a/src/cpu/processor.cc b/src/cpu/processor.cc index b6ba7e753..8be6b2160 100644 --- a/src/cpu/processor.cc +++ b/src/cpu/processor.cc @@ -43,6 +43,8 @@ namespace { LLVMLinkInJIT(); InitializeNativeTarget(); + llvm_start_multithreaded(); + // TODO(benvanik): only do this once codegen::RegisterEmitCategoryALU(); codegen::RegisterEmitCategoryControl(); @@ -89,17 +91,21 @@ xe_memory_ref Processor::memory() { int Processor::Setup() { XEASSERTNULL(engine_); - if (!llvm_start_multithreaded()) { - return 1; - } - dummy_context_ = auto_ptr(new LLVMContext()); Module* dummy_module = new Module("dummy", *dummy_context_.get()); std::string error_message; - engine_ = shared_ptr( - ExecutionEngine::create(dummy_module, false, &error_message, - CodeGenOpt::Aggressive, false)); + + EngineBuilder builder(dummy_module); + builder.setEngineKind(EngineKind::JIT); + builder.setErrorStr(&error_message); + builder.setOptLevel(CodeGenOpt::None); + //builder.setOptLevel(CodeGenOpt::Aggressive); + //builder.setTargetOptions(); + builder.setAllocateGVsWithCode(false); + //builder.setUseMCJIT(true); + + engine_ = shared_ptr(builder.create()); if (!engine_) { return 1; } @@ -178,7 +184,7 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) { uint32_t lr = 0xBEBEBEBE; // Setup registers. - ppc_state->lr = 0xBEBEBEBE; + ppc_state->lr = lr; // Args: // - i8* state @@ -191,6 +197,10 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) { GenericValue ret = engine_->runFunction(f, args); + // typedef void (*fnptr)(xe_ppc_state_t*, uint64_t); + // fnptr ptr = (fnptr)engine_->getPointerToFunction(f); + // ptr(ppc_state, lr); + //return (uint32_t)ret.IntVal.getSExtValue(); return 0; } diff --git a/src/cpu/sdb.cc b/src/cpu/sdb.cc index 4e499d527..7dc0038a6 100644 --- a/src/cpu/sdb.cc +++ b/src/cpu/sdb.cc @@ -84,7 +84,8 @@ FunctionBlock* FunctionSymbol::SplitBlock(uint32_t address) { VariableSymbol::VariableSymbol() : Symbol(Variable), - address(0), name(0) { + address(0), name(0), + kernel_export(0) { } VariableSymbol::~VariableSymbol() { @@ -213,6 +214,15 @@ VariableSymbol* SymbolDatabase::GetVariable(uint32_t address) { return NULL; } +int SymbolDatabase::GetAllVariables(std::vector& variables) { + for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) { + if (it->second->symbol_type == Symbol::Variable) { + variables.push_back(static_cast(it->second)); + } + } + return 0; +} + int SymbolDatabase::GetAllFunctions(vector& functions) { for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) { if (it->second->symbol_type == Symbol::Function) { @@ -847,6 +857,7 @@ int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) { info->ordinal); } var->name = xestrdupa(name); + var->kernel_export = kernel_export; if (info->thunk_address) { FunctionSymbol* fn = GetOrInsertFunction(info->thunk_address); fn->end_address = fn->start_address + 16 - 4; diff --git a/src/kernel/export.cc b/src/kernel/export.cc index d194e378a..e9ecf3c7c 100644 --- a/src/kernel/export.cc +++ b/src/kernel/export.cc @@ -14,23 +14,6 @@ using namespace xe; using namespace xe::kernel; -bool KernelExport::IsImplemented() { - switch (type) { - case Function: - if (function_data.impl) { - return true; - } - break; - case Variable: - if (variable_data) { - return true; - } - break; - } - return false; -} - - ExportResolver::ExportResolver() { } @@ -44,6 +27,13 @@ void ExportResolver::RegisterTable( table.exports = exports; table.count = count; tables_.push_back(table); + + for (size_t n = 0; n < count; n++) { + exports[n].is_implemented = false; + exports[n].variable_ptr = 0; + exports[n].function_data.shim = NULL; + exports[n].function_data.impl = NULL; + } } KernelExport* ExportResolver::GetExportByOrdinal(const char* library_name, @@ -69,3 +59,23 @@ KernelExport* ExportResolver::GetExportByName(const char* library_name, XEASSERTALWAYS(); return NULL; } + +void ExportResolver::SetVariableMapping(const char* library_name, + const uint32_t ordinal, + uint32_t value) { + KernelExport* kernel_export = GetExportByOrdinal(library_name, ordinal); + XEASSERTNOTNULL(kernel_export); + kernel_export->is_implemented = true; + kernel_export->variable_ptr = value; +} + +void ExportResolver::SetFunctionMapping(const char* library_name, + const uint32_t ordinal, + xe_kernel_export_shim_fn shim, + xe_kernel_export_impl_fn impl) { + KernelExport* kernel_export = GetExportByOrdinal(library_name, ordinal); + XEASSERTNOTNULL(kernel_export); + kernel_export->is_implemented = true; + kernel_export->function_data.shim = shim; + kernel_export->function_data.impl = impl; +} diff --git a/src/kernel/modules/xboxkrnl/xboxkrnl_module.cc b/src/kernel/modules/xboxkrnl/xboxkrnl_module.cc index 1309bb561..4599a9cd2 100644 --- a/src/kernel/modules/xboxkrnl/xboxkrnl_module.cc +++ b/src/kernel/modules/xboxkrnl/xboxkrnl_module.cc @@ -17,11 +17,55 @@ using namespace xe::kernel; using namespace xe::kernel::xboxkrnl; +namespace { + + // bit 27 = something? + typedef struct { + uint32_t flags; + uint8_t processor_count; + uint8_t unknown0; + uint8_t unknown1; + uint8_t unknown2; + uint32_t unknown4; + uint16_t unknown5; + uint16_t unknown6; + } XboxHardwareInfo_t; + // XboxHardwareInfo_t XboxHardwareInfo = { + // 0x00000000, 3, 0, 0, 0, 0, 0, 0, + // }; + + // This should remain zero for now. + // The pointer returned should have a 4b value at 0x58 that is the first + // argument to RtlImageXexHeaderField (header base?) + //uint32_t XexExecutableModuleHandle = 0x00000000; + +} + + XboxkrnlModule::XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory, shared_ptr resolver) : KernelModule(pal, memory, resolver) { resolver->RegisterTable( "xboxkrnl.exe", xboxkrnl_export_table, XECOUNT(xboxkrnl_export_table)); + + // TODO(benvanik): alloc heap memory somewhere in user space + // TODO(benvanik): tools for reading/writing to heap memory + + // HACK: register some dummy globals for now. + // KeDebugMonitorData + resolver->SetVariableMapping( + "xboxkrnl.exe", 0x00000059, + 0); + // XboxHardwareInfo + resolver->SetVariableMapping( + "xboxkrnl.exe", 0x00000156, + 0); + // XexExecutableModuleHandle + resolver->SetVariableMapping( + "xboxkrnl.exe", 0x00000193, + 0); + + // 0x0000012B, RtlImageXexHeaderField } XboxkrnlModule::~XboxkrnlModule() { diff --git a/src/kernel/user_module.cc b/src/kernel/user_module.cc index 6718b1426..d09cee74e 100644 --- a/src/kernel/user_module.cc +++ b/src/kernel/user_module.cc @@ -369,7 +369,7 @@ void UserModule::Dump(ExportResolver* export_resolver) { export_resolver->GetExportByOrdinal(library->name, info->ordinal); if (kernel_export) { known_count++; - if (kernel_export->IsImplemented()) { + if (kernel_export->is_implemented) { impl_count++; } } else { @@ -395,7 +395,7 @@ void UserModule::Dump(ExportResolver* export_resolver) { bool implemented = false; if (kernel_export) { name = kernel_export->name; - implemented = kernel_export->IsImplemented(); + implemented = kernel_export->is_implemented; } if (info->thunk_address) { printf(" F %.8X %.8X %.3X (%3d) %s %s\n", diff --git a/test/codegen/extrwi.bin b/test/codegen/extrwi.bin new file mode 100755 index 0000000000000000000000000000000000000000..2143beece0a4595acf656d05fd80a4fb89c9ad97 GIT binary patch literal 8 PcmWGp{@%{7fk6QP5a9zC literal 0 HcmV?d00001 diff --git a/test/codegen/extrwi.dis b/test/codegen/extrwi.dis new file mode 100644 index 000000000..c20aa67d8 --- /dev/null +++ b/test/codegen/extrwi.dis @@ -0,0 +1,9 @@ + +extrwi.o: file format elf64-powerpc + + +Disassembly of section .text: + +0000000082010000 <.text>: + 82010000: 54 a7 ef 3e rlwinm r7,r5,29,28,31 + 82010004: 4e 80 00 20 blr diff --git a/test/codegen/extrwi.s b/test/codegen/extrwi.s new file mode 100644 index 000000000..5fb8486c9 --- /dev/null +++ b/test/codegen/extrwi.s @@ -0,0 +1,11 @@ +# This is a variant of rlwinmx: +# extrwi ra,rs,n,b (n > 0) == rlwinm ra,rs,b+n,32-n,31 + +# REGISTER_IN r5 0x30 + +# rlwinm r7, r5, 29, 28, 31 +extrwi r7, r5, 4, 25 + +blr +# REGISTER_OUT r5 0x30 +# REGISTER_OUT r7 0x06 diff --git a/tools/xenia-run/xenia-run.cc b/tools/xenia-run/xenia-run.cc index 56eeb4c34..20e25683e 100644 --- a/tools/xenia-run/xenia-run.cc +++ b/tools/xenia-run/xenia-run.cc @@ -17,6 +17,10 @@ using namespace xe::cpu; using namespace xe::kernel; +DEFINE_bool(abort_before_entry, false, + "Abort execution right before launching the module."); + + class Run { public: Run(); @@ -66,6 +70,10 @@ XECLEANUP: } int Run::Launch() { + if (FLAGS_abort_before_entry) { + return 0; + } + // TODO(benvanik): wait until the module thread exits runtime_->LaunchModule(module_); return 0;