diff --git a/src/xenia/cpu/raw_module.cc b/src/xenia/cpu/raw_module.cc index 11dde5213..a1251714d 100644 --- a/src/xenia/cpu/raw_module.cc +++ b/src/xenia/cpu/raw_module.cc @@ -61,6 +61,12 @@ bool RawModule::LoadFile(uint32_t base_address, const std::wstring& path) { return true; } +void RawModule::SetAddressRange(uint32_t base_address, uint32_t size) { + base_address_ = base_address; + low_address_ = base_address; + high_address_ = base_address + size; +} + bool RawModule::ContainsAddress(uint32_t address) { return address >= low_address_ && address < high_address_; } diff --git a/src/xenia/cpu/raw_module.h b/src/xenia/cpu/raw_module.h index 86d19f81b..d743f15e2 100644 --- a/src/xenia/cpu/raw_module.h +++ b/src/xenia/cpu/raw_module.h @@ -24,7 +24,12 @@ class RawModule : public Module { bool LoadFile(uint32_t base_address, const std::wstring& path); + // Set address range if you've already allocated memory and placed code + // in it. + void SetAddressRange(uint32_t base_address, uint32_t size); + const std::string& name() const override { return name_; } + void set_name(const std::string& name) { name_ = name; } bool ContainsAddress(uint32_t address) override; diff --git a/src/xenia/kernel/kernel_module.cc b/src/xenia/kernel/kernel_module.cc index 6b34cb23b..d26d9aa53 100644 --- a/src/xenia/kernel/kernel_module.cc +++ b/src/xenia/kernel/kernel_module.cc @@ -10,6 +10,8 @@ #include "xenia/kernel/kernel_module.h" #include "xenia/base/logging.h" +#include "xenia/base/mutex.h" +#include "xenia/cpu/raw_module.h" #include "xenia/emulator.h" #include "xenia/kernel/xthread.h" @@ -22,6 +24,26 @@ KernelModule::KernelModule(KernelState* kernel_state, const char* path) memory_ = emulator_->memory(); export_resolver_ = kernel_state->emulator()->export_resolver(); + // HACK: Allocates memory where xboxkrnl.exe would be! + // TODO: Need to free this memory when necessary. + auto heap = memory()->LookupHeap(0x80040000); + if (heap->AllocRange(0x80040000, 0x801C0000, kTrampolineSize, 16, + kMemoryAllocationCommit, + kMemoryProtectRead | kMemoryProtectWrite, false, + &guest_trampoline_)) { + guest_trampoline_size_ = kTrampolineSize; + + auto module = std::make_unique(emulator_->processor()); + guest_trampoline_module_ = module.get(); + + module->set_name(name_ + "_trampoline"); + module->SetAddressRange(guest_trampoline_, guest_trampoline_size_); + emulator_->processor()->AddModule(std::move(module)); + } else { + XELOGW("KernelModule %s could not allocate trampoline for GetProcAddress!", + path); + } + OnLoad(); } @@ -45,10 +67,51 @@ uint32_t KernelModule::GetProcAddressByOrdinal(uint16_t ordinal) { return 0; } } else { - if (export_entry->function_data.shim) { - // Implemented. Dynamically generate trampoline. - XELOGE("GetProcAddressByOrdinal not implemented"); - return 0; + if (export_entry->function_data.trampoline || + export_entry->function_data.shim) { + global_critical_region_.Acquire(); + + // See if the function has been generated already. + if (guest_trampoline_map_.find(ordinal) != guest_trampoline_map_.end()) { + auto entry = guest_trampoline_map_.find(ordinal); + return entry->second; + } + + // Generate the function. + assert_true(guest_trampoline_next_ * 8 < guest_trampoline_size_); + if (guest_trampoline_next_ * 8 >= guest_trampoline_size_) { + assert_always(); // If you hit this, increase kTrampolineSize + XELOGE("KernelModule::GetProcAddressByOrdinal trampoline exhausted"); + return 0; + } + + uint32_t guest_addr = guest_trampoline_ + (guest_trampoline_next_++ * 8); + + auto mem = memory()->TranslateVirtual(guest_addr); + xe::store_and_swap(mem + 0x0, 0x44000002); // sc + xe::store_and_swap(mem + 0x4, 0x4E800020); // blr + + // Declare/define the extern function. + cpu::Function* function; + guest_trampoline_module_->DeclareFunction(guest_addr, &function); + function->set_end_address(guest_addr + 8); + function->set_name(std::string("__T_") + export_entry->name); + + cpu::GuestFunction::ExternHandler handler = nullptr; + if (export_entry->function_data.trampoline) { + handler = (cpu::GuestFunction::ExternHandler) + export_entry->function_data.trampoline; + } else { + handler = + (cpu::GuestFunction::ExternHandler)export_entry->function_data.shim; + } + static_cast(function)->SetupExtern(handler); + + function->set_status(cpu::Symbol::Status::kDeclared); + + // Register the function in our map. + guest_trampoline_map_[ordinal] = guest_addr; + return guest_addr; } else { // Not implemented. XELOGW( @@ -61,7 +124,8 @@ uint32_t KernelModule::GetProcAddressByOrdinal(uint16_t ordinal) { } uint32_t KernelModule::GetProcAddressByName(const char* name) { - XELOGE("GetProcAddressByName not implemented"); + // TODO: Does this even work for kernel modules? + XELOGE("KernelModule::GetProcAddressByName not implemented"); return 0; } diff --git a/src/xenia/kernel/kernel_module.h b/src/xenia/kernel/kernel_module.h index 3341bd9a7..0aaba2fb4 100644 --- a/src/xenia/kernel/kernel_module.h +++ b/src/xenia/kernel/kernel_module.h @@ -14,6 +14,10 @@ #include "xenia/kernel/xmodule.h" namespace xe { +namespace cpu { +class RawModule; +} // namespace cpu + namespace kernel { class KernelState; @@ -30,6 +34,16 @@ class KernelModule : public XModule { Emulator* emulator_; Memory* memory_; xe::cpu::ExportResolver* export_resolver_; + + // Guest trampoline for GetProcAddress + static const uint32_t kTrampolineSize = 400 * 8; + uint32_t guest_trampoline_ = 0; + uint32_t guest_trampoline_next_ = 0; // Next free entry to be generated. + uint32_t guest_trampoline_size_ = 0; + cpu::RawModule* guest_trampoline_module_ = nullptr; + std::map guest_trampoline_map_; + + xe::global_critical_region global_critical_region_; }; } // namespace kernel