diff --git a/src/xenia/apu/xma_decoder.cc b/src/xenia/apu/xma_decoder.cc index 450944764..52e0b61a6 100644 --- a/src/xenia/apu/xma_decoder.cc +++ b/src/xenia/apu/xma_decoder.cc @@ -113,9 +113,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { context_data_last_ptr_ = context_data_first_ptr_ + (sizeof(XMA_CONTEXT_DATA) * kContextCount - 1); register_file_[XE_XMA_REG_CONTEXT_ARRAY_ADDRESS].u32 = - memory() - ->LookupHeap(context_data_first_ptr_) - ->GetPhysicalAddress(context_data_first_ptr_); + memory()->GetPhysicalAddress(context_data_first_ptr_); // Setup XMA contexts. for (int i = 0; i < kContextCount; ++i) { diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc index c27e2351b..0fc5be3d5 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc @@ -11,6 +11,7 @@ #include "xenia/apu/audio_system.h" #include "xenia/apu/xma_decoder.h" +#include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/emulator.h" #include "xenia/kernel/kernel_state.h" @@ -184,9 +185,19 @@ DECLARE_XBOXKRNL_EXPORT2(XMASetInputBufferReadOffset, kAudio, kImplemented, dword_result_t XMASetInputBuffer0(lpvoid_t context_ptr, lpvoid_t buffer, dword_t packet_count) { + uint32_t buffer_physical_address = + kernel_memory()->GetPhysicalAddress(buffer.guest_address()); + assert_true(buffer_physical_address != UINT32_MAX); + if (buffer_physical_address == UINT32_MAX) { + // Xenia-specific safety check. + XELOGE("XMASetInputBuffer0: Invalid buffer virtual address %.8X", + buffer.guest_address()); + return X_E_FALSE; + } + XMA_CONTEXT_DATA context(context_ptr); - context.input_buffer_0_ptr = buffer.guest_address(); + context.input_buffer_0_ptr = buffer_physical_address; context.input_buffer_0_packet_count = packet_count; context.Store(context_ptr); @@ -215,9 +226,19 @@ DECLARE_XBOXKRNL_EXPORT2(XMASetInputBuffer0Valid, kAudio, kImplemented, dword_result_t XMASetInputBuffer1(lpvoid_t context_ptr, lpvoid_t buffer, dword_t packet_count) { + uint32_t buffer_physical_address = + kernel_memory()->GetPhysicalAddress(buffer.guest_address()); + assert_true(buffer_physical_address != UINT32_MAX); + if (buffer_physical_address == UINT32_MAX) { + // Xenia-specific safety check. + XELOGE("XMASetInputBuffer1: Invalid buffer virtual address %.8X", + buffer.guest_address()); + return X_E_FALSE; + } + XMA_CONTEXT_DATA context(context_ptr); - context.input_buffer_1_ptr = buffer.guest_address(); + context.input_buffer_1_ptr = buffer_physical_address; context.input_buffer_1_packet_count = packet_count; context.Store(context_ptr); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc index 1609b7f70..9373269ec 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc @@ -502,13 +502,11 @@ dword_result_t MmGetPhysicalAddress(dword_t base_address) { // _In_ PVOID BaseAddress // ); // base_address = result of MmAllocatePhysicalMemory. - assert_true(base_address >= 0xA0000000); - - uint32_t physical_address = base_address & 0x1FFFFFFF; - if (base_address >= 0xE0000000) { - physical_address += 0x1000; + uint32_t physical_address = kernel_memory()->GetPhysicalAddress(base_address); + assert_true(physical_address != UINT32_MAX); + if (physical_address == UINT32_MAX) { + physical_address = 0; } - return physical_address; } DECLARE_XBOXKRNL_EXPORT1(MmGetPhysicalAddress, kMemory, kImplemented); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc index c2b3b273a..b6913e528 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc @@ -362,13 +362,26 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer xe::copy_and_swap_32_unaligned( &fetch, reinterpret_cast(fetch_ptr.host_address()), 6); + // Kernel virtual -> GPU physical. + uint32_t frontbuffer_address = fetch.base_address << 12; + assert_true(*frontbuffer_ptr == frontbuffer_address); + frontbuffer_address = + kernel_memory()->GetPhysicalAddress(frontbuffer_address); + assert_true(frontbuffer_address != UINT32_MAX); + if (frontbuffer_address == UINT32_MAX) { + // Xenia-specific safety check. + XELOGE("VdSwap: Invalid front buffer virtual address 0x%.8X", + fetch.base_address << 12); + return; + } + fetch.base_address = frontbuffer_address >> 12; + auto texture_format = gpu::TextureFormat(texture_format_ptr.value()); auto color_space = *color_space_ptr; assert_true(texture_format == gpu::TextureFormat::k_8_8_8_8 || texture_format == gpu::TextureFormat::k_2_10_10_10_AS_16_16_16_16); assert_true(color_space == 0); // RGB(0) - assert_true(*frontbuffer_ptr == fetch.base_address << 12); assert_true(*width == 1 + fetch.size_2d.width); assert_true(*height == 1 + fetch.size_2d.height); @@ -379,14 +392,6 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer // use this method. buffer_ptr.Zero(64 * 4); - // virtual -> physical - // Doom 3: BFG Edition uses front buffers from the 0xE0000000 range with 4 KB - // offset, so & 0x1FFFF is not enough for this. - fetch.base_address = kernel_memory() - ->LookupHeap(fetch.base_address << 12) - ->GetPhysicalAddress(fetch.base_address << 12) >> - 12; - uint32_t offset = 0; auto dwords = buffer_ptr.as_array(); @@ -402,9 +407,7 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer dwords[offset++] = xenos::MakePacketType3(xenos::PM4_XE_SWAP, 4); dwords[offset++] = 'SWAP'; - dwords[offset++] = kernel_memory() - ->LookupHeap(*frontbuffer_ptr) - ->GetPhysicalAddress(*frontbuffer_ptr); + dwords[offset++] = frontbuffer_address; dwords[offset++] = *width; dwords[offset++] = *height; diff --git a/src/xenia/memory.cc b/src/xenia/memory.cc index d69783bfe..cec4da18b 100644 --- a/src/xenia/memory.cc +++ b/src/xenia/memory.cc @@ -369,6 +369,14 @@ uint32_t Memory::HostToGuestVirtualThunk(const void* context, host_address); } +uint32_t Memory::GetPhysicalAddress(uint32_t address) const { + const BaseHeap* heap = LookupHeap(address); + if (!heap || !heap->IsGuestPhysicalHeap()) { + return UINT32_MAX; + } + return static_cast(heap)->GetPhysicalAddress(address); +} + void Memory::Zero(uint32_t address, uint32_t size) { std::memset(TranslateVirtual(address), 0, size); } @@ -440,8 +448,7 @@ bool Memory::AccessViolationCallback(void* host_address, bool is_write) { } uint32_t virtual_address = HostToGuestVirtual(host_address); BaseHeap* heap = LookupHeap(virtual_address); - if (heap == &heaps_.vA0000000 || heap == &heaps_.vC0000000 || - heap == &heaps_.vE0000000) { + if (heap->IsGuestPhysicalHeap()) { // Will be rounded to physical page boundaries internally, so just pass 1 as // the length - guranteed not to cross page boundaries also. return static_cast(heap)->TriggerWatches(virtual_address, 1, @@ -461,8 +468,7 @@ bool Memory::TriggerWatches(uint32_t virtual_address, uint32_t length, bool is_write, bool unwatch_exact_range, bool unprotect) { BaseHeap* heap = LookupHeap(virtual_address); - if (heap == &heaps_.vA0000000 || heap == &heaps_.vC0000000 || - heap == &heaps_.vE0000000) { + if (heap->IsGuestPhysicalHeap()) { return static_cast(heap)->TriggerWatches( virtual_address, length, is_write, unwatch_exact_range, unprotect); } @@ -1274,16 +1280,6 @@ bool BaseHeap::QueryProtect(uint32_t address, uint32_t* out_protect) { return true; } -uint32_t BaseHeap::GetPhysicalAddress(uint32_t address) { - // Only valid for memory in this range - will be bogus if the origin was - // outside of it. - uint32_t physical_address = address & 0x1FFFFFFF; - if (address >= 0xE0000000) { - physical_address += 0x1000; - } - return physical_address; -} - VirtualHeap::VirtualHeap() = default; VirtualHeap::~VirtualHeap() = default; @@ -1724,4 +1720,14 @@ bool PhysicalHeap::TriggerWatches(uint32_t virtual_address, uint32_t length, return true; } +uint32_t PhysicalHeap::GetPhysicalAddress(uint32_t address) const { + assert_true(address >= heap_base_); + address -= heap_base_; + assert_true(address <= heap_size_); + if (heap_base_ >= 0xE0000000) { + address += 0x1000; + } + return address; +} + } // namespace xe diff --git a/src/xenia/memory.h b/src/xenia/memory.h index 4d76e5490..570f98e1f 100644 --- a/src/xenia/memory.h +++ b/src/xenia/memory.h @@ -171,9 +171,8 @@ class BaseHeap { // address. bool QueryProtect(uint32_t address, uint32_t* out_protect); - // Gets the physical address of a virtual address. - // This is only valid if the page is backed by a physical allocation. - uint32_t GetPhysicalAddress(uint32_t address); + // Whether the heap is a guest virtual memory mapping of the physical memory. + virtual bool IsGuestPhysicalHeap() const { return false; } bool Save(ByteStream* stream); bool Restore(ByteStream* stream); @@ -244,6 +243,9 @@ class PhysicalHeap : public BaseHeap { bool TriggerWatches(uint32_t virtual_address, uint32_t length, bool is_write, bool unwatch_exact_range, bool unprotect = true); + bool IsGuestPhysicalHeap() const override { return true; } + uint32_t GetPhysicalAddress(uint32_t address) const; + protected: VirtualHeap* parent_heap_; @@ -317,6 +319,10 @@ class Memory { // Note that the contents at the returned host address are big-endian. uint32_t HostToGuestVirtual(const void* host_address) const; + // Returns the guest physical address for the guest virtual address, or + // UINT32_MAX if it can't be obtained. + uint32_t GetPhysicalAddress(uint32_t address) const; + // Zeros out a range of memory at the given guest address. void Zero(uint32_t address, uint32_t size);