/** ****************************************************************************** * 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_MEMORY_H_ #define XENIA_MEMORY_H_ #include #include #include #include #include "xenia/base/memory.h" #include "xenia/base/mutex.h" #include "xenia/cpu/mmio_handler.h" namespace xe { class ByteStream; } // namespace xe namespace xe { enum SystemHeapFlag : uint32_t { kSystemHeapVirtual = 1 << 0, kSystemHeapPhysical = 1 << 1, kSystemHeapDefault = kSystemHeapVirtual, }; 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, }; // Equivalent to the Win32 MEMORY_BASIC_INFORMATION struct. struct HeapAllocationInfo { // A pointer to the base address of the region of pages. uint32_t base_address; // 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. uint32_t allocation_base; // The memory protection option when the region was initially allocated. uint32_t allocation_protect; // 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; // The type of pages in the region (private). uint32_t type; }; // Describes a single page in the page table. union PageEntry { struct { // 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. uint32_t allocation_protect : 4; // Current protection bits as of the last Protect. // Composed of bits from MemoryProtectFlag. uint32_t current_protect : 4; // Allocation state of the page as a MemoryAllocationFlag bit mask. uint32_t state : 2; uint32_t reserved : 14; }; uint64_t qword; }; // Heap abstraction for page-based allocation. class BaseHeap { public: virtual ~BaseHeap(); // Size of each page within the heap range in bytes. uint32_t page_size() const { return page_size_; } // Disposes and decommits all memory and clears the page table. virtual void Dispose(); // Dumps information about all allocations within the heap to the log. void DumpMap(); uint32_t GetUnreservedPageCount(); // 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. virtual bool Alloc(uint32_t size, uint32_t alignment, uint32_t allocation_type, uint32_t protect, bool top_down, uint32_t* out_address); // 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. virtual bool AllocFixed(uint32_t base_address, uint32_t size, uint32_t alignment, uint32_t allocation_type, uint32_t protect); // 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. 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); // Decommits pages in the given range. // Partial overlapping pages will also be decommitted. virtual bool Decommit(uint32_t address, uint32_t size); // Decommits and releases pages in the given range. // Partial overlapping pages will also be released. virtual bool Release(uint32_t address, uint32_t* out_region_size = nullptr); // Modifies the protection mode of pages within the given range. virtual bool Protect(uint32_t address, uint32_t size, uint32_t protect); // Queries information about the given region of pages. bool QueryRegionInfo(uint32_t base_address, HeapAllocationInfo* out_info); // Queries the size of the region containing the given address. bool QuerySize(uint32_t address, uint32_t* out_size); // Queries the current protection mode of the region containing the given // 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); bool Save(ByteStream* stream); bool Restore(ByteStream* stream); void Reset(); protected: BaseHeap(); void Initialize(uint8_t* membase, uint32_t heap_base, uint32_t heap_size, uint32_t page_size); uint8_t* membase_; uint32_t heap_base_; uint32_t heap_size_; uint32_t page_size_; xe::global_critical_region global_critical_region_; std::vector page_table_; }; // Normal heap allowing allocations from guest virtual address ranges. class VirtualHeap : public BaseHeap { public: VirtualHeap(); ~VirtualHeap() override; // Initializes the heap properties and allocates the page table. void Initialize(uint8_t* membase, uint32_t heap_base, uint32_t heap_size, uint32_t page_size); }; // 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. class PhysicalHeap : public BaseHeap { public: PhysicalHeap(); ~PhysicalHeap() override; // Initializes the heap properties and allocates the page table. void Initialize(uint8_t* membase, uint32_t heap_base, uint32_t heap_size, uint32_t page_size, VirtualHeap* parent_heap); 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; bool Decommit(uint32_t address, uint32_t size) override; bool Release(uint32_t base_address, uint32_t* out_region_size = nullptr) override; bool Protect(uint32_t address, uint32_t size, uint32_t protect) override; protected: VirtualHeap* parent_heap_; }; // 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. class Memory { public: Memory(); ~Memory(); // 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(); // Resets all memory to zero and resets all allocations. void Reset(); // Full file name and path of the memory-mapped file backing all memory. const std::wstring& file_name() const { return file_name_; } // Base address of virtual memory in the host address space. // This is often something like 0x100000000. inline uint8_t* virtual_membase() const { return virtual_membase_; } // 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. inline uint8_t* TranslateVirtual(uint32_t guest_address) const { return virtual_membase_ + guest_address; } template inline T TranslateVirtual(uint32_t guest_address) const { return reinterpret_cast(virtual_membase_ + guest_address); } // Base address of physical memory in the host address space. // This is often something like 0x200000000. inline uint8_t* physical_membase() const { return physical_membase_; } // 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. inline uint8_t* TranslatePhysical(uint32_t guest_address) const { return physical_membase_ + (guest_address & 0x1FFFFFFF); } template inline T TranslatePhysical(uint32_t guest_address) const { return reinterpret_cast(physical_membase_ + (guest_address & 0x1FFFFFFF)); } // Zeros out a range of memory at the given guest address. void Zero(uint32_t address, uint32_t size); // Fills a range of guest memory with the given byte value. void Fill(uint32_t address, uint32_t size, uint8_t value); // Copies a non-overlapping range of guest memory (like a memcpy). void Copy(uint32_t dest, uint32_t src, uint32_t size); // Searches the given range of guest memory for a run of dword values in // big-endian order. uint32_t SearchAligned(uint32_t start, uint32_t end, const uint32_t* values, size_t value_count); // Defines a memory-mapped IO (MMIO) virtual address range that when accessed // will trigger the specified read and write callbacks for dword read/writes. bool AddVirtualMappedRange(uint32_t virtual_address, uint32_t mask, uint32_t size, void* context, cpu::MMIOReadCallback read_callback, cpu::MMIOWriteCallback write_callback); // Gets the defined MMIO range for the given virtual address, if any. cpu::MMIORange* LookupVirtualMappedRange(uint32_t virtual_address); // Adds a write watch for the given physical address range that will trigger // the specified callback whenever any bytes are written in that range. // The returned handle can be used with CancelWriteWatch to remove the watch // if it is no longer required. // // This has a significant performance penalty for writes in in the range or // nearby (sharing 64KiB pages). uintptr_t AddPhysicalAccessWatch(uint32_t physical_address, uint32_t length, cpu::MMIOHandler::WatchType type, cpu::AccessWatchCallback callback, void* callback_context, void* callback_data); // Cancels a write watch requested with AddPhysicalAccessWatch. void CancelAccessWatch(uintptr_t watch_handle); // 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. uint32_t SystemHeapAlloc(uint32_t size, uint32_t alignment = 0x20, uint32_t system_heap_flags = kSystemHeapDefault); // Frees memory allocated with SystemHeapAlloc. void SystemHeapFree(uint32_t address); // Gets the heap for the address space containing the given address. BaseHeap* LookupHeap(uint32_t address); // Gets the heap with the given properties. BaseHeap* LookupHeapByType(bool physical, uint32_t page_size); // Dumps a map of all allocated memory to the log. void DumpMap(); bool Save(ByteStream* stream); bool Restore(ByteStream* stream); private: int MapViews(uint8_t* mapping_base); void UnmapViews(); private: std::wstring file_name_; uint32_t system_page_size_ = 0; uint8_t* virtual_membase_ = nullptr; uint8_t* physical_membase_ = nullptr; xe::memory::FileMappingHandle mapping_ = nullptr; uint8_t* mapping_base_ = nullptr; union { struct { uint8_t* v00000000; uint8_t* v40000000; uint8_t* v7F000000; uint8_t* v80000000; uint8_t* v90000000; uint8_t* vA0000000; uint8_t* vC0000000; uint8_t* vE0000000; uint8_t* physical; }; uint8_t* all_views[9]; } views_ = {{0}}; std::unique_ptr mmio_handler_; struct { VirtualHeap v00000000; VirtualHeap v40000000; VirtualHeap v80000000; VirtualHeap v90000000; VirtualHeap physical; PhysicalHeap vA0000000; PhysicalHeap vC0000000; PhysicalHeap vE0000000; } heaps_; friend class BaseHeap; }; } // namespace xe #endif // XENIA_MEMORY_H_