2013-12-07 06:57:16 +00:00
|
|
|
/**
|
|
|
|
******************************************************************************
|
|
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
|
|
******************************************************************************
|
2020-03-02 15:37:11 +00:00
|
|
|
* Copyright 2020 Ben Vanik. All rights reserved. *
|
2013-12-07 06:57:16 +00:00
|
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
|
|
******************************************************************************
|
|
|
|
*/
|
|
|
|
|
2014-08-20 04:02:15 +00:00
|
|
|
#ifndef XENIA_MEMORY_H_
|
|
|
|
#define XENIA_MEMORY_H_
|
2013-12-07 06:57:16 +00:00
|
|
|
|
2015-03-24 14:46:18 +00:00
|
|
|
#include <cstdint>
|
2014-07-30 05:12:39 +00:00
|
|
|
#include <memory>
|
2020-02-15 18:35:24 +00:00
|
|
|
#include <mutex>
|
2015-05-24 07:02:47 +00:00
|
|
|
#include <string>
|
2019-07-30 21:18:12 +00:00
|
|
|
#include <utility>
|
2015-03-24 14:46:18 +00:00
|
|
|
#include <vector>
|
2014-07-30 05:12:39 +00:00
|
|
|
|
2015-07-16 05:10:05 +00:00
|
|
|
#include "xenia/base/memory.h"
|
2015-05-25 06:16:43 +00:00
|
|
|
#include "xenia/base/mutex.h"
|
2015-02-01 06:49:47 +00:00
|
|
|
#include "xenia/cpu/mmio_handler.h"
|
2023-10-10 12:50:10 +00:00
|
|
|
#include "xenia/guest_pointers.h"
|
2015-12-01 23:26:55 +00:00
|
|
|
namespace xe {
|
|
|
|
class ByteStream;
|
|
|
|
} // namespace xe
|
|
|
|
|
2013-12-07 06:57:16 +00:00
|
|
|
namespace xe {
|
|
|
|
|
2019-07-30 05:00:20 +00:00
|
|
|
class Memory;
|
|
|
|
|
2015-03-28 22:54:44 +00:00
|
|
|
enum SystemHeapFlag : uint32_t {
|
|
|
|
kSystemHeapVirtual = 1 << 0,
|
|
|
|
kSystemHeapPhysical = 1 << 1,
|
|
|
|
|
|
|
|
kSystemHeapDefault = kSystemHeapVirtual,
|
|
|
|
};
|
2013-12-07 06:57:16 +00:00
|
|
|
|
2020-09-16 18:09:32 +00:00
|
|
|
enum class HeapType : uint8_t {
|
|
|
|
kGuestVirtual,
|
|
|
|
kGuestXex,
|
|
|
|
kGuestPhysical,
|
|
|
|
kHostPhysical,
|
|
|
|
};
|
|
|
|
|
2015-05-16 07:23:13 +00:00
|
|
|
enum MemoryAllocationFlag : uint32_t {
|
|
|
|
kMemoryAllocationReserve = 1 << 0,
|
|
|
|
kMemoryAllocationCommit = 1 << 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum MemoryProtectFlag : uint32_t {
|
|
|
|
kMemoryProtectRead = 1 << 0,
|
|
|
|
kMemoryProtectWrite = 1 << 1,
|
|
|
|
kMemoryProtectNoCache = 1 << 2,
|
|
|
|
kMemoryProtectWriteCombine = 1 << 3,
|
|
|
|
|
|
|
|
kMemoryProtectNoAccess = 0,
|
2014-08-20 04:02:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Equivalent to the Win32 MEMORY_BASIC_INFORMATION struct.
|
2015-05-16 07:23:13 +00:00
|
|
|
struct HeapAllocationInfo {
|
|
|
|
// A pointer to the base address of the region of pages.
|
2015-03-25 02:41:29 +00:00
|
|
|
uint32_t base_address;
|
2015-05-16 07:23:13 +00:00
|
|
|
// A pointer to the base address of a range of pages allocated by the
|
|
|
|
// VirtualAlloc function. The page pointed to by the BaseAddress member is
|
|
|
|
// contained within this allocation range.
|
2015-03-25 02:41:29 +00:00
|
|
|
uint32_t allocation_base;
|
2015-05-16 07:23:13 +00:00
|
|
|
// The memory protection option when the region was initially allocated.
|
|
|
|
uint32_t allocation_protect;
|
2018-02-11 01:14:58 +00:00
|
|
|
// The size specified when the region was initially allocated, in bytes.
|
|
|
|
uint32_t allocation_size;
|
2015-05-16 07:23:13 +00:00
|
|
|
// The size of the region beginning at the base address in which all pages
|
|
|
|
// have identical attributes, in bytes.
|
|
|
|
uint32_t region_size;
|
|
|
|
// The state of the pages in the region (commit/free/reserve).
|
|
|
|
uint32_t state;
|
|
|
|
// The access protection of the pages in the region.
|
|
|
|
uint32_t protect;
|
|
|
|
};
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Describes a single page in the page table.
|
2015-05-16 07:23:13 +00:00
|
|
|
union PageEntry {
|
2021-11-01 19:56:24 +00:00
|
|
|
uint64_t qword;
|
2015-05-16 07:23:13 +00:00
|
|
|
struct {
|
2015-12-03 01:37:48 +00:00
|
|
|
// Base address of the allocated region in 4k pages.
|
|
|
|
uint32_t base_address : 20;
|
|
|
|
// Total number of pages in the allocated region in 4k pages.
|
|
|
|
uint32_t region_page_count : 20;
|
|
|
|
// Protection bits specified during region allocation.
|
|
|
|
// Composed of bits from MemoryProtectFlag.
|
2015-05-16 07:23:13 +00:00
|
|
|
uint32_t allocation_protect : 4;
|
2015-12-03 01:37:48 +00:00
|
|
|
// Current protection bits as of the last Protect.
|
|
|
|
// Composed of bits from MemoryProtectFlag.
|
2015-05-16 07:23:13 +00:00
|
|
|
uint32_t current_protect : 4;
|
2015-12-03 01:37:48 +00:00
|
|
|
// Allocation state of the page as a MemoryAllocationFlag bit mask.
|
2015-05-16 07:23:13 +00:00
|
|
|
uint32_t state : 2;
|
|
|
|
uint32_t reserved : 14;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Heap abstraction for page-based allocation.
|
2015-05-16 07:23:13 +00:00
|
|
|
class BaseHeap {
|
|
|
|
public:
|
|
|
|
virtual ~BaseHeap();
|
|
|
|
|
2019-08-13 20:49:49 +00:00
|
|
|
// Offset of the heap in relative to membase, without host_address_offset
|
|
|
|
// adjustment.
|
|
|
|
uint32_t heap_base() const { return heap_base_; }
|
|
|
|
|
|
|
|
// Length of the heap range.
|
|
|
|
uint32_t heap_size() const { return heap_size_; }
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Size of each page within the heap range in bytes.
|
2015-05-16 07:23:13 +00:00
|
|
|
uint32_t page_size() const { return page_size_; }
|
|
|
|
|
2021-05-30 10:52:34 +00:00
|
|
|
// Amount of pages assigned to heap
|
|
|
|
uint32_t total_page_count() const { return uint32_t(page_table_.size()); }
|
|
|
|
|
|
|
|
// Sum of unreserved pages in heap
|
|
|
|
uint32_t unreserved_page_count() const { return unreserved_page_count_; }
|
|
|
|
|
|
|
|
// Sum of reserved pages in heap
|
|
|
|
uint32_t reserved_page_count() const {
|
|
|
|
return total_page_count() - unreserved_page_count();
|
|
|
|
}
|
|
|
|
|
2020-09-16 18:09:32 +00:00
|
|
|
// Type of specified heap
|
|
|
|
HeapType heap_type() const { return heap_type_; }
|
|
|
|
|
2019-08-04 20:55:54 +00:00
|
|
|
// Offset added to the virtual addresses to convert them to host addresses
|
|
|
|
// (not including membase).
|
|
|
|
uint32_t host_address_offset() const { return host_address_offset_; }
|
|
|
|
|
2019-08-14 21:31:21 +00:00
|
|
|
template <typename T = uint8_t*>
|
|
|
|
inline T TranslateRelative(size_t relative_address) const {
|
|
|
|
return reinterpret_cast<T>(membase_ + heap_base_ + host_address_offset_ +
|
|
|
|
relative_address);
|
|
|
|
}
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Disposes and decommits all memory and clears the page table.
|
2015-05-16 07:23:13 +00:00
|
|
|
virtual void Dispose();
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Dumps information about all allocations within the heap to the log.
|
2015-05-16 07:23:13 +00:00
|
|
|
void DumpMap();
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Allocates pages with the given properties and allocation strategy.
|
|
|
|
// This can reserve and commit the pages as well as set protection modes.
|
|
|
|
// This will fail if not enough contiguous pages can be found.
|
2015-05-16 07:23:13 +00:00
|
|
|
virtual bool Alloc(uint32_t size, uint32_t alignment,
|
|
|
|
uint32_t allocation_type, uint32_t protect, bool top_down,
|
|
|
|
uint32_t* out_address);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Allocates pages at the given address.
|
|
|
|
// This can reserve and commit the pages as well as set protection modes.
|
|
|
|
// This will fail if the pages are already allocated.
|
2015-05-16 07:23:13 +00:00
|
|
|
virtual bool AllocFixed(uint32_t base_address, uint32_t size,
|
|
|
|
uint32_t alignment, uint32_t allocation_type,
|
|
|
|
uint32_t protect);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Allocates pages at an address within the given address range.
|
|
|
|
// This can reserve and commit the pages as well as set protection modes.
|
|
|
|
// This will fail if not enough contiguous pages can be found.
|
2015-05-16 07:23:13 +00:00
|
|
|
virtual bool AllocRange(uint32_t low_address, uint32_t high_address,
|
|
|
|
uint32_t size, uint32_t alignment,
|
|
|
|
uint32_t allocation_type, uint32_t protect,
|
|
|
|
bool top_down, uint32_t* out_address);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2022-02-14 18:26:31 +00:00
|
|
|
virtual bool AllocSystemHeap(uint32_t size, uint32_t alignment,
|
|
|
|
uint32_t allocation_type, uint32_t protect,
|
|
|
|
bool top_down, uint32_t* out_address);
|
2015-12-03 01:37:48 +00:00
|
|
|
// Decommits pages in the given range.
|
|
|
|
// Partial overlapping pages will also be decommitted.
|
2015-05-16 07:23:13 +00:00
|
|
|
virtual bool Decommit(uint32_t address, uint32_t size);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Decommits and releases pages in the given range.
|
|
|
|
// Partial overlapping pages will also be released.
|
2015-05-16 07:23:13 +00:00
|
|
|
virtual bool Release(uint32_t address, uint32_t* out_region_size = nullptr);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Modifies the protection mode of pages within the given range.
|
2017-07-25 02:41:47 +00:00
|
|
|
virtual bool Protect(uint32_t address, uint32_t size, uint32_t protect,
|
|
|
|
uint32_t* old_protect = nullptr);
|
2015-05-16 07:23:13 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Queries information about the given region of pages.
|
2015-05-16 07:23:13 +00:00
|
|
|
bool QueryRegionInfo(uint32_t base_address, HeapAllocationInfo* out_info);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Queries the size of the region containing the given address.
|
2015-05-16 07:23:13 +00:00
|
|
|
bool QuerySize(uint32_t address, uint32_t* out_size);
|
2018-02-11 03:58:44 +00:00
|
|
|
|
|
|
|
// Queries the base and size of a region containing the given address.
|
|
|
|
bool QueryBaseAndSize(uint32_t* in_out_address, uint32_t* out_size);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Queries the current protection mode of the region containing the given
|
|
|
|
// address.
|
2015-05-16 07:23:13 +00:00
|
|
|
bool QueryProtect(uint32_t address, uint32_t* out_protect);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2020-02-22 15:06:56 +00:00
|
|
|
// Queries the currently strictest readability and writability for the entire
|
|
|
|
// range.
|
|
|
|
xe::memory::PageAccess QueryRangeAccess(uint32_t low_address,
|
|
|
|
uint32_t high_address);
|
|
|
|
|
2015-12-01 23:26:55 +00:00
|
|
|
bool Save(ByteStream* stream);
|
|
|
|
bool Restore(ByteStream* stream);
|
|
|
|
|
|
|
|
void Reset();
|
|
|
|
|
2015-05-16 07:23:13 +00:00
|
|
|
protected:
|
|
|
|
BaseHeap();
|
|
|
|
|
2020-09-16 18:09:32 +00:00
|
|
|
void Initialize(Memory* memory, uint8_t* membase, HeapType heap_type,
|
|
|
|
uint32_t heap_base, uint32_t heap_size, uint32_t page_size,
|
2019-08-04 20:55:54 +00:00
|
|
|
uint32_t host_address_offset = 0);
|
2015-05-16 07:23:13 +00:00
|
|
|
|
2019-07-30 05:00:20 +00:00
|
|
|
Memory* memory_;
|
2015-05-16 07:23:13 +00:00
|
|
|
uint8_t* membase_;
|
2020-09-16 18:09:32 +00:00
|
|
|
HeapType heap_type_;
|
2015-05-16 07:23:13 +00:00
|
|
|
uint32_t heap_base_;
|
|
|
|
uint32_t heap_size_;
|
|
|
|
uint32_t page_size_;
|
2022-09-17 11:04:53 +00:00
|
|
|
uint32_t page_size_shift_;
|
2019-08-04 20:55:54 +00:00
|
|
|
uint32_t host_address_offset_;
|
2021-05-30 10:52:34 +00:00
|
|
|
uint32_t unreserved_page_count_;
|
2015-09-06 16:30:54 +00:00
|
|
|
xe::global_critical_region global_critical_region_;
|
2015-05-16 07:23:13 +00:00
|
|
|
std::vector<PageEntry> page_table_;
|
|
|
|
};
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Normal heap allowing allocations from guest virtual address ranges.
|
2015-05-16 07:23:13 +00:00
|
|
|
class VirtualHeap : public BaseHeap {
|
|
|
|
public:
|
|
|
|
VirtualHeap();
|
|
|
|
~VirtualHeap() override;
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Initializes the heap properties and allocates the page table.
|
2020-09-16 18:09:32 +00:00
|
|
|
void Initialize(Memory* memory, uint8_t* membase, HeapType heap_type,
|
|
|
|
uint32_t heap_base, uint32_t heap_size, uint32_t page_size);
|
2015-05-16 07:23:13 +00:00
|
|
|
};
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// A heap for ranges of memory that are mapped to physical ranges.
|
|
|
|
// Physical ranges are used by the audio and graphics subsystems representing
|
|
|
|
// hardware wired directly to memory in the console.
|
|
|
|
//
|
|
|
|
// The physical heap and the behavior of sharing pages with virtual pages is
|
|
|
|
// implemented by having a 'parent' heap that is used to perform allocation in
|
|
|
|
// the guest virtual address space 1:1 with the physical address space.
|
2015-05-16 07:23:13 +00:00
|
|
|
class PhysicalHeap : public BaseHeap {
|
|
|
|
public:
|
|
|
|
PhysicalHeap();
|
|
|
|
~PhysicalHeap() override;
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Initializes the heap properties and allocates the page table.
|
2020-09-16 18:09:32 +00:00
|
|
|
void Initialize(Memory* memory, uint8_t* membase, HeapType heap_type,
|
|
|
|
uint32_t heap_base, uint32_t heap_size, uint32_t page_size,
|
2019-07-30 05:00:20 +00:00
|
|
|
VirtualHeap* parent_heap);
|
2015-05-16 07:23:13 +00:00
|
|
|
|
|
|
|
bool Alloc(uint32_t size, uint32_t alignment, uint32_t allocation_type,
|
|
|
|
uint32_t protect, bool top_down, uint32_t* out_address) override;
|
|
|
|
bool AllocFixed(uint32_t base_address, uint32_t size, uint32_t alignment,
|
|
|
|
uint32_t allocation_type, uint32_t protect) override;
|
|
|
|
bool AllocRange(uint32_t low_address, uint32_t high_address, uint32_t size,
|
|
|
|
uint32_t alignment, uint32_t allocation_type,
|
|
|
|
uint32_t protect, bool top_down,
|
|
|
|
uint32_t* out_address) override;
|
2022-02-14 18:26:31 +00:00
|
|
|
bool AllocSystemHeap(uint32_t size, uint32_t alignment,
|
|
|
|
uint32_t allocation_type, uint32_t protect,
|
|
|
|
bool top_down, uint32_t* out_address) override;
|
2015-05-16 07:23:13 +00:00
|
|
|
bool Decommit(uint32_t address, uint32_t size) override;
|
|
|
|
bool Release(uint32_t base_address,
|
|
|
|
uint32_t* out_region_size = nullptr) override;
|
2017-07-25 02:41:47 +00:00
|
|
|
bool Protect(uint32_t address, uint32_t size, uint32_t protect,
|
|
|
|
uint32_t* old_protect = nullptr) override;
|
2015-05-16 07:23:13 +00:00
|
|
|
|
2020-02-15 18:35:24 +00:00
|
|
|
void EnableAccessCallbacks(uint32_t physical_address, uint32_t length,
|
|
|
|
bool enable_invalidation_notifications,
|
|
|
|
bool enable_data_providers);
|
2022-09-17 11:04:53 +00:00
|
|
|
template <bool enable_invalidation_notifications>
|
|
|
|
XE_NOINLINE void EnableAccessCallbacksInner(
|
|
|
|
const uint32_t system_page_first, const uint32_t system_page_last,
|
|
|
|
xe::memory::PageAccess protect_access) XE_RESTRICT;
|
|
|
|
|
2019-07-30 05:00:20 +00:00
|
|
|
// Returns true if any page in the range was watched.
|
2022-08-14 15:59:11 +00:00
|
|
|
bool TriggerCallbacks(global_unique_lock_type global_lock_locked_once,
|
2022-09-17 11:04:53 +00:00
|
|
|
uint32_t virtual_address, uint32_t length,
|
|
|
|
bool is_write, bool unwatch_exact_range,
|
|
|
|
bool unprotect = true);
|
2018-09-24 20:18:16 +00:00
|
|
|
|
2019-08-24 14:40:59 +00:00
|
|
|
uint32_t GetPhysicalAddress(uint32_t address) const;
|
|
|
|
|
2022-09-17 11:04:53 +00:00
|
|
|
uint32_t SystemPagenumToGuestPagenum(uint32_t num) const {
|
2022-11-05 17:50:33 +00:00
|
|
|
return ((num << system_page_shift_) - host_address_offset()) >>
|
|
|
|
page_size_shift_;
|
2022-09-17 11:04:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GuestPagenumToSystemPagenum(uint32_t num) {
|
|
|
|
num <<= page_size_shift_;
|
|
|
|
num += host_address_offset();
|
|
|
|
num >>= system_page_shift_;
|
|
|
|
return num;
|
|
|
|
}
|
2022-11-05 17:50:33 +00:00
|
|
|
|
2015-05-16 07:23:13 +00:00
|
|
|
protected:
|
|
|
|
VirtualHeap* parent_heap_;
|
2019-07-30 05:00:20 +00:00
|
|
|
|
|
|
|
uint32_t system_page_size_;
|
|
|
|
uint32_t system_page_count_;
|
2022-09-17 11:04:53 +00:00
|
|
|
uint32_t system_page_shift_;
|
|
|
|
uint32_t padding1_;
|
2020-02-15 18:35:24 +00:00
|
|
|
|
|
|
|
struct SystemPageFlagsBlock {
|
|
|
|
// Whether writing to each page should result trigger invalidation
|
|
|
|
// callbacks.
|
|
|
|
uint64_t notify_on_invalidation;
|
|
|
|
};
|
|
|
|
// Protected by global_critical_region. Flags for each 64 system pages,
|
|
|
|
// interleaved as blocks, so bit scan can be used to quickly extract ranges.
|
|
|
|
std::vector<SystemPageFlagsBlock> system_page_flags_;
|
2014-08-20 04:02:15 +00:00
|
|
|
};
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Models the entire guest memory system on the console.
|
|
|
|
// This exposes interfaces to both virtual and physical memory and a TLB and
|
|
|
|
// page table for allocation, mapping, and protection.
|
|
|
|
//
|
|
|
|
// The memory is backed by a memory mapped file and is placed at a stable
|
|
|
|
// fixed address in the host address space (like 0x100000000). This allows
|
|
|
|
// efficient guest<->host address translations as well as easy sharing of the
|
|
|
|
// memory across various subsystems.
|
|
|
|
//
|
|
|
|
// The guest memory address space is split into several ranges that have varying
|
|
|
|
// properties such as page sizes, caching strategies, protections, and
|
|
|
|
// overlap with other ranges. Each range is represented by a BaseHeap of either
|
|
|
|
// VirtualHeap or PhysicalHeap depending on type. Heaps model the page tables
|
|
|
|
// and can handle reservation and committing of requested pages.
|
2015-03-24 14:46:18 +00:00
|
|
|
class Memory {
|
2014-08-19 05:12:21 +00:00
|
|
|
public:
|
2014-08-20 04:02:15 +00:00
|
|
|
Memory();
|
2015-03-24 14:46:18 +00:00
|
|
|
~Memory();
|
2013-12-07 06:57:16 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Initializes the memory system.
|
|
|
|
// This may fail if the host address space could not be reserved or the
|
|
|
|
// mapping to the file system fails.
|
|
|
|
bool Initialize();
|
2015-03-24 14:46:18 +00:00
|
|
|
|
2015-12-27 20:03:30 +00:00
|
|
|
// Resets all memory to zero and resets all allocations.
|
|
|
|
void Reset();
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Full file name and path of the memory-mapped file backing all memory.
|
2020-03-02 15:37:11 +00:00
|
|
|
const std::filesystem::path& file_name() const { return file_name_; }
|
2015-05-24 07:02:47 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Base address of virtual memory in the host address space.
|
|
|
|
// This is often something like 0x100000000.
|
2015-03-29 18:11:35 +00:00
|
|
|
inline uint8_t* virtual_membase() const { return virtual_membase_; }
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Translates a guest virtual address to a host address that can be accessed
|
|
|
|
// as a normal pointer.
|
|
|
|
// Note that the contents at the specified host address are big-endian.
|
2019-08-13 20:49:49 +00:00
|
|
|
template <typename T = uint8_t*>
|
2015-03-29 18:11:35 +00:00
|
|
|
inline T TranslateVirtual(uint32_t guest_address) const {
|
2022-11-05 17:50:33 +00:00
|
|
|
#if XE_PLATFORM_WIN32 == 1
|
|
|
|
uint8_t* host_address = virtual_membase_ + guest_address;
|
|
|
|
if (guest_address >= 0xE0000000) {
|
|
|
|
host_address += 0x1000;
|
|
|
|
}
|
|
|
|
return reinterpret_cast<T>(host_address);
|
|
|
|
#else
|
2019-08-13 21:07:27 +00:00
|
|
|
uint8_t* host_address = virtual_membase_ + guest_address;
|
|
|
|
const auto heap = LookupHeap(guest_address);
|
|
|
|
if (heap) {
|
|
|
|
host_address += heap->host_address_offset();
|
|
|
|
}
|
|
|
|
return reinterpret_cast<T>(host_address);
|
2022-11-05 17:50:33 +00:00
|
|
|
|
|
|
|
#endif
|
2015-08-07 03:17:01 +00:00
|
|
|
}
|
2023-10-10 12:50:10 +00:00
|
|
|
template <typename T>
|
|
|
|
inline T* TranslateVirtual(TypedGuestPointer<T> guest_address) {
|
|
|
|
return TranslateVirtual<T*>(guest_address.m_ptr);
|
|
|
|
}
|
2023-10-14 13:08:02 +00:00
|
|
|
template <typename T>
|
|
|
|
inline xe::be<T>* TranslateVirtualBE(uint32_t guest_address)
|
|
|
|
XE_RESTRICT const {
|
|
|
|
static_assert(!std::is_pointer_v<T> &&
|
|
|
|
sizeof(T) > 1); // maybe assert is_integral?
|
|
|
|
return TranslateVirtual<xe::be<T>*>(guest_address);
|
|
|
|
}
|
2015-03-29 18:11:35 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Base address of physical memory in the host address space.
|
|
|
|
// This is often something like 0x200000000.
|
2015-03-29 18:11:35 +00:00
|
|
|
inline uint8_t* physical_membase() const { return physical_membase_; }
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Translates a guest physical address to a host address that can be accessed
|
|
|
|
// as a normal pointer.
|
|
|
|
// Note that the contents at the specified host address are big-endian.
|
2019-08-13 20:49:49 +00:00
|
|
|
template <typename T = uint8_t*>
|
2015-03-29 18:11:35 +00:00
|
|
|
inline T TranslatePhysical(uint32_t guest_address) const {
|
|
|
|
return reinterpret_cast<T>(physical_membase_ +
|
|
|
|
(guest_address & 0x1FFFFFFF));
|
|
|
|
}
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2019-08-13 20:49:49 +00:00
|
|
|
// Translates a host address to a guest virtual address.
|
|
|
|
// Note that the contents at the returned host address are big-endian.
|
|
|
|
uint32_t HostToGuestVirtual(const void* host_address) const;
|
|
|
|
|
2019-08-24 14:40:59 +00:00
|
|
|
// 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;
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Zeros out a range of memory at the given guest address.
|
2015-03-25 02:41:29 +00:00
|
|
|
void Zero(uint32_t address, uint32_t size);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Fills a range of guest memory with the given byte value.
|
2015-03-25 02:41:29 +00:00
|
|
|
void Fill(uint32_t address, uint32_t size, uint8_t value);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Copies a non-overlapping range of guest memory (like a memcpy).
|
2015-03-25 02:41:29 +00:00
|
|
|
void Copy(uint32_t dest, uint32_t src, uint32_t size);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Searches the given range of guest memory for a run of dword values in
|
|
|
|
// big-endian order.
|
2015-03-25 02:41:29 +00:00
|
|
|
uint32_t SearchAligned(uint32_t start, uint32_t end, const uint32_t* values,
|
2015-03-24 14:46:18 +00:00
|
|
|
size_t value_count);
|
2014-06-02 06:36:18 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Defines a memory-mapped IO (MMIO) virtual address range that when accessed
|
|
|
|
// will trigger the specified read and write callbacks for dword read/writes.
|
2015-05-16 07:23:13 +00:00
|
|
|
bool AddVirtualMappedRange(uint32_t virtual_address, uint32_t mask,
|
|
|
|
uint32_t size, void* context,
|
|
|
|
cpu::MMIOReadCallback read_callback,
|
|
|
|
cpu::MMIOWriteCallback write_callback);
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Gets the defined MMIO range for the given virtual address, if any.
|
2015-06-03 03:15:43 +00:00
|
|
|
cpu::MMIORange* LookupVirtualMappedRange(uint32_t virtual_address);
|
2013-12-07 06:57:16 +00:00
|
|
|
|
2020-02-15 18:35:24 +00:00
|
|
|
// Physical memory access callbacks, two types of them.
|
|
|
|
//
|
|
|
|
// This is simple per-system-page protection without reference counting or
|
|
|
|
// stored ranges. Whenever a watched page is accessed, all callbacks for it
|
|
|
|
// are triggered. Also the only way to remove callbacks is to trigger them
|
|
|
|
// somehow. Since there are no references from pages to individual callbacks,
|
|
|
|
// there's no way to disable only a specific callback for a page. Also
|
|
|
|
// callbacks may be triggered spuriously, and handlers should properly ignore
|
|
|
|
// pages they don't care about.
|
|
|
|
//
|
|
|
|
// Once callbacks are triggered for a page, the page is not watched anymore
|
|
|
|
// until requested again later. It is, however, unwatched only in one guest
|
|
|
|
// view of physical memory (because different views may have different
|
|
|
|
// protection for the same memory) - but it's rare when the same memory is
|
|
|
|
// used with different guest page sizes, and it's okay to fire a callback more
|
|
|
|
// than once.
|
2019-07-30 05:00:20 +00:00
|
|
|
//
|
2020-02-15 18:35:24 +00:00
|
|
|
// Only accessing the guest virtual memory views of physical memory triggers
|
|
|
|
// callbacks - data providers, for instance, must write to the host physical
|
|
|
|
// heap directly, otherwise their threads may infinitely await themselves.
|
2019-07-30 05:00:20 +00:00
|
|
|
//
|
2020-02-15 18:35:24 +00:00
|
|
|
// - Invalidation notifications:
|
2019-07-30 05:00:20 +00:00
|
|
|
//
|
2020-02-15 18:35:24 +00:00
|
|
|
// Protecting from writing. One-shot callbacks for invalidation of various
|
|
|
|
// kinds of physical memory caches (such as the GPU copy of the memory).
|
2019-07-30 05:00:20 +00:00
|
|
|
//
|
2020-02-15 18:35:24 +00:00
|
|
|
// May be triggered for a single page (in case of a write access violation or
|
|
|
|
// when need to synchronize data given by data providers) or for multiple
|
2020-02-23 22:04:30 +00:00
|
|
|
// pages (like when memory is released, or explicitly to trigger callbacks
|
|
|
|
// when host-side code can't rely on regular access violations, like when
|
|
|
|
// accessing a file).
|
2019-07-30 05:00:20 +00:00
|
|
|
//
|
2020-02-15 18:35:24 +00:00
|
|
|
// Since granularity of callbacks is one single page, an invalidation
|
|
|
|
// notification handler must invalidate the all the data stored in the touched
|
|
|
|
// pages.
|
|
|
|
//
|
|
|
|
// Because large ranges (like whole framebuffers) may be written to and
|
|
|
|
// exceptions are expensive, it's better to unprotect multiple pages as a
|
|
|
|
// result of a write access violation, so the shortest common range returned
|
|
|
|
// by all the invalidation callbacks (clamped to a sane range and also not to
|
|
|
|
// touch pages with provider callbacks) is unprotected.
|
|
|
|
//
|
|
|
|
// - Data providers:
|
|
|
|
//
|
|
|
|
// TODO(Triang3l): Implement data providers - more complicated because they
|
|
|
|
// will need to be able to release the global lock.
|
|
|
|
|
|
|
|
// Returns start and length of the smallest physical memory region surrounding
|
|
|
|
// the watched region that can be safely unwatched, if it doesn't matter,
|
|
|
|
// return (0, UINT32_MAX).
|
|
|
|
typedef std::pair<uint32_t, uint32_t> (*PhysicalMemoryInvalidationCallback)(
|
|
|
|
void* context_ptr, uint32_t physical_address_start, uint32_t length,
|
|
|
|
bool exact_range);
|
|
|
|
// Returns a handle for unregistering or for skipping one notification handler
|
|
|
|
// while triggering data providers.
|
|
|
|
void* RegisterPhysicalMemoryInvalidationCallback(
|
|
|
|
PhysicalMemoryInvalidationCallback callback, void* callback_context);
|
|
|
|
// Unregisters a physical memory invalidation callback previously added with
|
|
|
|
// RegisterPhysicalMemoryInvalidationCallback.
|
|
|
|
void UnregisterPhysicalMemoryInvalidationCallback(void* callback_handle);
|
|
|
|
|
|
|
|
// Enables physical memory access callbacks for the specified memory range,
|
|
|
|
// snapped to system page boundaries.
|
|
|
|
void EnablePhysicalMemoryAccessCallbacks(
|
|
|
|
uint32_t physical_address, uint32_t length,
|
|
|
|
bool enable_invalidation_notifications, bool enable_data_providers);
|
2018-07-26 19:52:26 +00:00
|
|
|
|
2019-07-30 06:06:23 +00:00
|
|
|
// Forces triggering of watch callbacks for a virtual address range if pages
|
|
|
|
// are watched there and unwatching them. Returns whether any page was
|
2020-02-15 18:35:24 +00:00
|
|
|
// watched. Must be called with global critical region locking depth of 1.
|
|
|
|
// TODO(Triang3l): Implement data providers - this is why locking depth of 1
|
|
|
|
// will be required in the future.
|
|
|
|
bool TriggerPhysicalMemoryCallbacks(
|
2022-09-17 11:04:53 +00:00
|
|
|
global_unique_lock_type global_lock_locked_once, uint32_t virtual_address,
|
|
|
|
uint32_t length, bool is_write, bool unwatch_exact_range,
|
|
|
|
bool unprotect = true);
|
2019-07-30 06:06:23 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Allocates virtual memory from the 'system' heap.
|
|
|
|
// System memory is kept separate from game memory but is still accessible
|
|
|
|
// using normal guest virtual addresses. Kernel structures and other internal
|
|
|
|
// 'system' allocations should come from this heap when possible.
|
2015-03-28 22:54:44 +00:00
|
|
|
uint32_t SystemHeapAlloc(uint32_t size, uint32_t alignment = 0x20,
|
|
|
|
uint32_t system_heap_flags = kSystemHeapDefault);
|
2015-12-03 01:37:48 +00:00
|
|
|
|
|
|
|
// Frees memory allocated with SystemHeapAlloc.
|
2023-10-15 16:33:54 +00:00
|
|
|
void SystemHeapFree(uint32_t address, uint32_t* out_region_size=nullptr);
|
2013-12-07 06:57:16 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Gets the heap for the address space containing the given address.
|
use Sleep(0) instead of SwitchToThread, should waste less power and help the os with scheduling.
PM4 buffer handling made a virtual member of commandprocessor, place the implementation/declaration into reusable macro files. this is probably the biggest boost here.
Optimized SET_CONSTANT/ LOAD_CONSTANT pm4 ops based on the register range they start writing at, this was also a nice boost
Expose X64 extension flags to code outside of x64 backend, so we can detect and use things like avx512, xop, avx2, etc in normal code
Add freelists for HIR structures to try to reduce the number of last level cache misses during optimization (currently disabled... fixme later)
Analyzed PGO feedback and reordered branches, uninlined functions, moved code out into different functions based on info from it in the PM4 functions, this gave like a 2% boost at best.
Added support for the db16cyc opcode, which is used often in xb360 spinlocks. before it was just being translated to nop, now on x64 we translate it to _mm_pause but may change that in the future to reduce cpu time wasted
texture util - all our divisors were powers of 2, instead we look up a shift. this made texture scaling slightly faster, more so on intel processors which seem to be worse at int divs. GetGuestTextureLayout is now a little faster, although it is still one of the heaviest functions in the emulator when scaling is on.
xe_unlikely_mutex was not a good choice for the guest clock lock, (running theory) on intel processors another thread may take a significant time to update the clock? maybe because of the uint64 division? really not sure, but switched it to xe_mutex. This fixed audio stutter that i had introduced to 1 or 2 games, fixed performance on that n64 rare game with the monkeys.
Took another crack at DMA implementation, another failure.
Instead of passing as a parameter, keep the ringbuffer reader as the first member of commandprocessor so it can be accessed through this
Added macro for noalias
Applied noalias to Memory::LookupHeap. This reduced the size of the executable by 7 kb.
Reworked kernel shim template, this shaved like 100kb off the exe and eliminated the indirect calls from the shim to the actual implementation. We still unconditionally generate string representations of kernel calls though :(, unless it is kHighFrequency
Add nvapi extensions support, currently unused. Will use CPUVISIBLE memory at some point
Inserted prefetches in a few places based on feedback from vtune.
Add native implementation of SHA int8 if all elements are the same
Vectorized comparisons for SetViewport, SetScissorRect
Vectorized ranged comparisons for WriteRegister
Add XE_MSVC_ASSUME
Move FormatInfo::name out of the structure, instead look up the name in a different table. Debug related data and critical runtime data are best kept apart
Templated UpdateSystemConstantValues based on ROV/RTV and primitive_polygonal
Add ArchFloatMask functions, these are for storing the results of floating point comparisons without doing costly float->int pipeline transfers (vucomiss/setb)
Use floatmasks in UpdateSystemConstantValues for checking if dirty, only transfer to int at end of function.
Instead of dirty |= (x == y) in UpdateSystemConstantValues, now we do dirty_u32 |= (x^y). if any of them are not equal, dirty_u32 will be nz, else if theyre all equal it will be zero. This is more friendly to register renaming and the lack of dependencies on EFLAGS lets the compiler reorder better
Add PrefetchSamplerParameters to D3D12TextureCache
use PrefetchSamplerParameters in UpdateBindings to eliminate cache misses that vtune detected
Add PrefetchTextureBinding to D3D12TextureCache
Prefetch texture bindings to get rid of more misses vtune detected (more accesses out of order with random strides)
Rewrote DMAC, still terrible though and have disabled it for now.
Replace tiny memcmp of 6 U64 in render_target_cache with inline loop, msvc fails to make it a loop and instead does a thunk to their memcmp function, which is optimized for larger sizes
PrefetchTextureBinding in AreActiveTextureSRVKeysUpToDate
Replace memcmp calls for pipelinedescription with handwritten cmp
Directly write some registers that dont have special handling in PM4 functions
Changed EstimateMaxY to try to eliminate mispredictions that vtune was reporting, msvc ended up turning the changed code into a series of blends
in ExecutePacketType3_EVENT_WRITE_EXT, instead of writing extents to an array on the stack and then doing xe_copy_and_swap_16 of the data to its dest, pre-swap each constant and then store those. msvc manages to unroll that into wider stores
stop logging XE_SWAP every time we receive XE_SWAP, stop logging the start and end of each viz query
Prefetch watch nodes in FireWatches based on feedback from vtune
Removed dead code from texture_info.cc
NOINLINE on GpuSwap, PGO builds did it so we should too.
2022-09-11 21:14:48 +00:00
|
|
|
XE_NOALIAS
|
2019-08-13 21:07:27 +00:00
|
|
|
const BaseHeap* LookupHeap(uint32_t address) const;
|
use Sleep(0) instead of SwitchToThread, should waste less power and help the os with scheduling.
PM4 buffer handling made a virtual member of commandprocessor, place the implementation/declaration into reusable macro files. this is probably the biggest boost here.
Optimized SET_CONSTANT/ LOAD_CONSTANT pm4 ops based on the register range they start writing at, this was also a nice boost
Expose X64 extension flags to code outside of x64 backend, so we can detect and use things like avx512, xop, avx2, etc in normal code
Add freelists for HIR structures to try to reduce the number of last level cache misses during optimization (currently disabled... fixme later)
Analyzed PGO feedback and reordered branches, uninlined functions, moved code out into different functions based on info from it in the PM4 functions, this gave like a 2% boost at best.
Added support for the db16cyc opcode, which is used often in xb360 spinlocks. before it was just being translated to nop, now on x64 we translate it to _mm_pause but may change that in the future to reduce cpu time wasted
texture util - all our divisors were powers of 2, instead we look up a shift. this made texture scaling slightly faster, more so on intel processors which seem to be worse at int divs. GetGuestTextureLayout is now a little faster, although it is still one of the heaviest functions in the emulator when scaling is on.
xe_unlikely_mutex was not a good choice for the guest clock lock, (running theory) on intel processors another thread may take a significant time to update the clock? maybe because of the uint64 division? really not sure, but switched it to xe_mutex. This fixed audio stutter that i had introduced to 1 or 2 games, fixed performance on that n64 rare game with the monkeys.
Took another crack at DMA implementation, another failure.
Instead of passing as a parameter, keep the ringbuffer reader as the first member of commandprocessor so it can be accessed through this
Added macro for noalias
Applied noalias to Memory::LookupHeap. This reduced the size of the executable by 7 kb.
Reworked kernel shim template, this shaved like 100kb off the exe and eliminated the indirect calls from the shim to the actual implementation. We still unconditionally generate string representations of kernel calls though :(, unless it is kHighFrequency
Add nvapi extensions support, currently unused. Will use CPUVISIBLE memory at some point
Inserted prefetches in a few places based on feedback from vtune.
Add native implementation of SHA int8 if all elements are the same
Vectorized comparisons for SetViewport, SetScissorRect
Vectorized ranged comparisons for WriteRegister
Add XE_MSVC_ASSUME
Move FormatInfo::name out of the structure, instead look up the name in a different table. Debug related data and critical runtime data are best kept apart
Templated UpdateSystemConstantValues based on ROV/RTV and primitive_polygonal
Add ArchFloatMask functions, these are for storing the results of floating point comparisons without doing costly float->int pipeline transfers (vucomiss/setb)
Use floatmasks in UpdateSystemConstantValues for checking if dirty, only transfer to int at end of function.
Instead of dirty |= (x == y) in UpdateSystemConstantValues, now we do dirty_u32 |= (x^y). if any of them are not equal, dirty_u32 will be nz, else if theyre all equal it will be zero. This is more friendly to register renaming and the lack of dependencies on EFLAGS lets the compiler reorder better
Add PrefetchSamplerParameters to D3D12TextureCache
use PrefetchSamplerParameters in UpdateBindings to eliminate cache misses that vtune detected
Add PrefetchTextureBinding to D3D12TextureCache
Prefetch texture bindings to get rid of more misses vtune detected (more accesses out of order with random strides)
Rewrote DMAC, still terrible though and have disabled it for now.
Replace tiny memcmp of 6 U64 in render_target_cache with inline loop, msvc fails to make it a loop and instead does a thunk to their memcmp function, which is optimized for larger sizes
PrefetchTextureBinding in AreActiveTextureSRVKeysUpToDate
Replace memcmp calls for pipelinedescription with handwritten cmp
Directly write some registers that dont have special handling in PM4 functions
Changed EstimateMaxY to try to eliminate mispredictions that vtune was reporting, msvc ended up turning the changed code into a series of blends
in ExecutePacketType3_EVENT_WRITE_EXT, instead of writing extents to an array on the stack and then doing xe_copy_and_swap_16 of the data to its dest, pre-swap each constant and then store those. msvc manages to unroll that into wider stores
stop logging XE_SWAP every time we receive XE_SWAP, stop logging the start and end of each viz query
Prefetch watch nodes in FireWatches based on feedback from vtune
Removed dead code from texture_info.cc
NOINLINE on GpuSwap, PGO builds did it so we should too.
2022-09-11 21:14:48 +00:00
|
|
|
XE_NOALIAS
|
2019-08-13 21:07:27 +00:00
|
|
|
inline BaseHeap* LookupHeap(uint32_t address) {
|
|
|
|
return const_cast<BaseHeap*>(
|
|
|
|
const_cast<const Memory*>(this)->LookupHeap(address));
|
|
|
|
}
|
2015-12-04 03:52:02 +00:00
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Gets the heap with the given properties.
|
2015-05-16 07:23:13 +00:00
|
|
|
BaseHeap* LookupHeapByType(bool physical, uint32_t page_size);
|
2014-01-05 19:19:02 +00:00
|
|
|
|
2018-02-10 22:45:06 +00:00
|
|
|
// Gets the physical base heap.
|
|
|
|
VirtualHeap* GetPhysicalHeap();
|
|
|
|
|
2021-05-30 10:52:34 +00:00
|
|
|
void GetHeapsPageStatsSummary(const BaseHeap* const* provided_heaps,
|
|
|
|
size_t heaps_count, uint32_t& unreserved_pages,
|
|
|
|
uint32_t& reserved_pages, uint32_t& used_pages,
|
|
|
|
uint32_t& reserved_bytes);
|
|
|
|
|
2015-12-03 01:37:48 +00:00
|
|
|
// Dumps a map of all allocated memory to the log.
|
2015-05-16 07:23:13 +00:00
|
|
|
void DumpMap();
|
2013-12-07 06:57:16 +00:00
|
|
|
|
2015-12-01 23:26:55 +00:00
|
|
|
bool Save(ByteStream* stream);
|
|
|
|
bool Restore(ByteStream* stream);
|
|
|
|
|
Huge set of performance improvements, combined with an architecture specific build and clang-cl users have reported absurd gains over master for some gains, in the range 50%-90%
But for normal msvc builds i would put it at around 30-50%
Added per-xexmodule caching of information per instruction, can be used to remember what code needs compiling at start up
Record what guest addresses wrote mmio and backpropagate that to future runs, eliminating dependence on exception trapping. this makes many games like h3 actually tolerable to run under a debugger
fixed a number of errors where temporaries were being passed by reference/pointer
Can now be compiled with clang-cl 14.0.1, requires -Werror off though and some other solution/project changes.
Added macros wrapping compiler extensions like noinline, forceinline, __expect, and cold.
Removed the "global lock" in guest code completely. It does not properly emulate the behavior of mfmsrd/mtmsr and it seriously cripples amd cpus. Removing this yielded around a 3x speedup in Halo Reach for me.
Disabled the microprofiler for now. The microprofiler has a huge performance cost associated with it. Developers can re-enable it in the base/profiling header if they really need it
Disable the trace writer in release builds. despite just returning after checking if the file was open the trace functions were consuming about 0.60% cpu time total
Add IsValidReg, GetRegisterInfo is a huge (about 45k) branching function and using that to check if a register was valid consumed a significant chunk of time
Optimized RingBuffer::ReadAndSwap and RingBuffer::read_count. This gave us the largest overall boost in performance. The memcpies were unnecessary and one of them was always a no-op
Added simplification rules for multiplicative patterns like (x+x), (x<<1)+x
For the most frequently called win32 functions i added code to call their underlying NT implementations, which lets us skip a lot of MS code we don't care about/isnt relevant to our usecases
^this can be toggled off in the platform_win header
handle indirect call true with constant function pointer, was occurring in h3
lookup host format swizzle in denser array
by default, don't check if a gpu register is unknown, instead just check if its out of range. controlled by a cvar
^looking up whether its known or not took approx 0.3% cpu time
Changed some things in /cpu to make the project UNITYBUILD friendly
The timer thread was spinning way too much and consuming a ton of cpu, changed it to use a blocking wait instead
tagged some conditions as XE_UNLIKELY/LIKELY based on profiler feedback (will only affect clang builds)
Shifted around some code in CommandProcessor::WriteRegister based on how frequently it was executed
added support for docdecaduple precision floating point so that we can represent our performance gains numerically
tons of other stuff im probably forgetting
2022-08-13 19:59:00 +00:00
|
|
|
void SetMMIOExceptionRecordingCallback(cpu::MmioAccessRecordCallback callback,
|
|
|
|
void* context);
|
|
|
|
|
2014-08-19 05:12:21 +00:00
|
|
|
private:
|
2013-12-07 06:57:16 +00:00
|
|
|
int MapViews(uint8_t* mapping_base);
|
|
|
|
void UnmapViews();
|
|
|
|
|
2019-08-13 20:49:49 +00:00
|
|
|
static uint32_t HostToGuestVirtualThunk(const void* context,
|
|
|
|
const void* host_address);
|
|
|
|
|
2022-08-14 15:59:11 +00:00
|
|
|
bool AccessViolationCallback(global_unique_lock_type global_lock_locked_once,
|
2022-09-17 11:04:53 +00:00
|
|
|
void* host_address, bool is_write);
|
2020-02-15 18:35:24 +00:00
|
|
|
static bool AccessViolationCallbackThunk(
|
2022-09-17 11:04:53 +00:00
|
|
|
global_unique_lock_type global_lock_locked_once, void* context,
|
|
|
|
void* host_address, bool is_write);
|
2019-07-30 05:00:20 +00:00
|
|
|
|
2020-03-02 15:37:11 +00:00
|
|
|
std::filesystem::path file_name_;
|
2015-07-16 05:10:05 +00:00
|
|
|
uint32_t system_page_size_ = 0;
|
2019-08-04 20:10:59 +00:00
|
|
|
uint32_t system_allocation_granularity_ = 0;
|
2015-07-16 05:10:05 +00:00
|
|
|
uint8_t* virtual_membase_ = nullptr;
|
|
|
|
uint8_t* physical_membase_ = nullptr;
|
2015-03-24 14:46:18 +00:00
|
|
|
|
2020-11-22 11:17:37 +00:00
|
|
|
xe::memory::FileMappingHandle mapping_ =
|
|
|
|
xe::memory::kFileMappingHandleInvalid;
|
2015-07-16 05:10:05 +00:00
|
|
|
uint8_t* mapping_base_ = nullptr;
|
2013-12-07 06:57:16 +00:00
|
|
|
union {
|
|
|
|
struct {
|
2014-08-19 05:12:21 +00:00
|
|
|
uint8_t* v00000000;
|
|
|
|
uint8_t* v40000000;
|
2014-11-02 07:02:17 +00:00
|
|
|
uint8_t* v7F000000;
|
2014-08-19 05:12:21 +00:00
|
|
|
uint8_t* v80000000;
|
2014-09-14 02:32:37 +00:00
|
|
|
uint8_t* v90000000;
|
2014-08-19 05:12:21 +00:00
|
|
|
uint8_t* vA0000000;
|
|
|
|
uint8_t* vC0000000;
|
|
|
|
uint8_t* vE0000000;
|
2015-05-16 07:23:13 +00:00
|
|
|
uint8_t* physical;
|
2013-12-07 06:57:16 +00:00
|
|
|
};
|
2014-11-02 07:02:17 +00:00
|
|
|
uint8_t* all_views[9];
|
2015-07-16 06:26:58 +00:00
|
|
|
} views_ = {{0}};
|
2013-12-07 06:57:16 +00:00
|
|
|
|
2014-08-20 04:02:15 +00:00
|
|
|
std::unique_ptr<cpu::MMIOHandler> mmio_handler_;
|
2014-07-30 05:12:39 +00:00
|
|
|
|
2015-05-16 07:23:13 +00:00
|
|
|
struct {
|
|
|
|
VirtualHeap v00000000;
|
|
|
|
VirtualHeap v40000000;
|
|
|
|
VirtualHeap v80000000;
|
|
|
|
VirtualHeap v90000000;
|
|
|
|
|
|
|
|
VirtualHeap physical;
|
|
|
|
PhysicalHeap vA0000000;
|
|
|
|
PhysicalHeap vC0000000;
|
|
|
|
PhysicalHeap vE0000000;
|
|
|
|
} heaps_;
|
2013-12-07 06:57:16 +00:00
|
|
|
|
2015-05-16 07:23:13 +00:00
|
|
|
friend class BaseHeap;
|
2019-07-30 05:00:20 +00:00
|
|
|
|
|
|
|
friend class PhysicalHeap;
|
|
|
|
xe::global_critical_region global_critical_region_;
|
2020-02-15 18:35:24 +00:00
|
|
|
std::vector<std::pair<PhysicalMemoryInvalidationCallback, void*>*>
|
|
|
|
physical_memory_invalidation_callbacks_;
|
2013-12-07 06:57:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace xe
|
|
|
|
|
2014-08-20 04:02:15 +00:00
|
|
|
#endif // XENIA_MEMORY_H_
|