Adding some comments.

This commit is contained in:
Ben Vanik 2015-12-02 17:37:48 -08:00
parent 66cab7b76e
commit 249b952de9
6 changed files with 178 additions and 14 deletions

View File

@ -25,6 +25,9 @@ bool IsDebuggerAttached();
// If no debugger is present, a signal will be raised. // If no debugger is present, a signal will be raised.
void Break(); void Break();
// Prints a message to the attached debugger.
// This bypasses the normal logging mechanism. If no debugger is attached it's
// likely to no-op.
void DebugPrint(const char* fmt, ...); void DebugPrint(const char* fmt, ...);
} // namespace debugging } // namespace debugging

View File

@ -20,16 +20,34 @@
namespace xe { namespace xe {
namespace filesystem { namespace filesystem {
// Canonicalizes a path, removing ..'s.
std::string CanonicalizePath(const std::string& original_path); std::string CanonicalizePath(const std::string& original_path);
// Returns true of the specified path exists as either a directory or file.
bool PathExists(const std::wstring& path); bool PathExists(const std::wstring& path);
// Creates the parent folder of the specified path if needed.
// This can be used to ensure the destination path for a new file exists before
// attempting to create it.
bool CreateParentFolder(const std::wstring& path); bool CreateParentFolder(const std::wstring& path);
// Creates a folder at the specified path.
// Returns true if the path was created.
bool CreateFolder(const std::wstring& path); bool CreateFolder(const std::wstring& path);
// Recursively deletes the files and folders at the specified path.
// Returns true if the path was found and removed.
bool DeleteFolder(const std::wstring& path); bool DeleteFolder(const std::wstring& path);
// Returns true if the given path exists and is a folder.
bool IsFolder(const std::wstring& path); bool IsFolder(const std::wstring& path);
// Opens the file at the given path with the specified mode.
// This behaves like fopen and the returned handle can be used with stdio.
FILE* OpenFile(const std::wstring& path, const char* mode); FILE* OpenFile(const std::wstring& path, const char* mode);
// Deletes the file at the given path.
// Returns true if the file was found and removed.
bool DeleteFile(const std::wstring& path); bool DeleteFile(const std::wstring& path);
struct FileAccess { struct FileAccess {

View File

@ -95,9 +95,8 @@ X_STATUS Emulator::Setup(
// Create memory system first, as it is required for other systems. // Create memory system first, as it is required for other systems.
memory_ = std::make_unique<Memory>(); memory_ = std::make_unique<Memory>();
result = memory_->Initialize(); if (!memory_->Initialize()) {
if (result) { return false;
return result;
} }
// Shared export resolver used to attach and query for HLE exports. // Shared export resolver used to attach and query for HLE exports.

View File

@ -43,33 +43,57 @@ class Window;
namespace xe { namespace xe {
// The main type that runs the whole emulator.
// This is responsible for initializing and managing all the various subsystems.
class Emulator { class Emulator {
public: public:
explicit Emulator(const std::wstring& command_line); explicit Emulator(const std::wstring& command_line);
~Emulator(); ~Emulator();
// Full command line used when launching the process.
const std::wstring& command_line() const { return command_line_; } const std::wstring& command_line() const { return command_line_; }
// Window used for displaying graphical output.
ui::Window* display_window() const { return display_window_; } ui::Window* display_window() const { return display_window_; }
// Guest memory system modelling the RAM (both virtual and physical) of the
// system.
Memory* memory() const { return memory_.get(); } Memory* memory() const { return memory_.get(); }
// Active debugger, which may or may not be attached.
debug::Debugger* debugger() const { return debugger_.get(); } debug::Debugger* debugger() const { return debugger_.get(); }
// Virtualized processor that can execute PPC code.
cpu::Processor* processor() const { return processor_.get(); } cpu::Processor* processor() const { return processor_.get(); }
// Audio hardware emulation for decoding and playback.
apu::AudioSystem* audio_system() const { return audio_system_.get(); } apu::AudioSystem* audio_system() const { return audio_system_.get(); }
// GPU emulation for command list processing.
gpu::GraphicsSystem* graphics_system() const { gpu::GraphicsSystem* graphics_system() const {
return graphics_system_.get(); return graphics_system_.get();
} }
// Human-interface Device (HID) adapters for controllers.
hid::InputSystem* input_system() const { return input_system_.get(); } hid::InputSystem* input_system() const { return input_system_.get(); }
// Kernel function export table used to resolve exports when JITing code.
cpu::ExportResolver* export_resolver() const { cpu::ExportResolver* export_resolver() const {
return export_resolver_.get(); return export_resolver_.get();
} }
// File systems mapped to disc images, folders, etc for games and save data.
vfs::VirtualFileSystem* file_system() const { return file_system_.get(); } vfs::VirtualFileSystem* file_system() const { return file_system_.get(); }
// The 'kernel', tracking all kernel objects and other state.
// This is effectively the guest operating system.
kernel::KernelState* kernel_state() const { return kernel_state_.get(); } kernel::KernelState* kernel_state() const { return kernel_state_.get(); }
// Initializes the emulator and configures all components.
// The given window is used for display and the provided functions are used
// to create subsystems as required.
// Once this function returns a game can be launched using one of the Launch
// functions.
X_STATUS Setup( X_STATUS Setup(
ui::Window* display_window, ui::Window* display_window,
std::function<std::unique_ptr<apu::AudioSystem>(cpu::Processor*)> std::function<std::unique_ptr<apu::AudioSystem>(cpu::Processor*)>
@ -79,9 +103,19 @@ class Emulator {
std::function<std::vector<std::unique_ptr<hid::InputDriver>>(ui::Window*)> std::function<std::vector<std::unique_ptr<hid::InputDriver>>(ui::Window*)>
input_driver_factory); input_driver_factory);
// Launches a game from the given file path.
// This will attempt to infer the type of the given file (such as an iso, etc)
// using heuristics.
X_STATUS LaunchPath(std::wstring path); X_STATUS LaunchPath(std::wstring path);
// Launches a game from a .xex file by mounting the containing folder as if it
// was an extracted STFS container.
X_STATUS LaunchXexFile(std::wstring path); X_STATUS LaunchXexFile(std::wstring path);
// Launches a game from a disc image file (.iso, etc).
X_STATUS LaunchDiscImage(std::wstring path); X_STATUS LaunchDiscImage(std::wstring path);
// Launches a game from an STFS container file.
X_STATUS LaunchStfsContainer(std::wstring path); X_STATUS LaunchStfsContainer(std::wstring path);
private: private:

View File

@ -115,7 +115,7 @@ Memory::~Memory() {
physical_membase_ = nullptr; physical_membase_ = nullptr;
} }
int Memory::Initialize() { bool Memory::Initialize() {
file_name_ = std::wstring(L"Local\\xenia_memory_") + file_name_ = std::wstring(L"Local\\xenia_memory_") +
std::to_wstring(Clock::QueryHostTickCount()); std::to_wstring(Clock::QueryHostTickCount());
@ -128,7 +128,7 @@ int Memory::Initialize() {
if (!mapping_) { if (!mapping_) {
XELOGE("Unable to reserve the 4gb guest address space."); XELOGE("Unable to reserve the 4gb guest address space.");
assert_not_null(mapping_); assert_not_null(mapping_);
return 1; return false;
} }
// Attempt to create our views. This may fail at the first address // Attempt to create our views. This may fail at the first address
@ -144,7 +144,7 @@ int Memory::Initialize() {
if (!mapping_base_) { if (!mapping_base_) {
XELOGE("Unable to find a continuous block in the 64bit address space."); XELOGE("Unable to find a continuous block in the 64bit address space.");
assert_always(); assert_always();
return 1; return false;
} }
virtual_membase_ = mapping_base_; virtual_membase_ = mapping_base_;
physical_membase_ = mapping_base_ + 0x100000000ull; physical_membase_ = mapping_base_ + 0x100000000ull;
@ -189,7 +189,7 @@ int Memory::Initialize() {
if (!mmio_handler_) { if (!mmio_handler_) {
XELOGE("Unable to install MMIO handlers"); XELOGE("Unable to install MMIO handlers");
assert_always(); assert_always();
return 1; return false;
} }
// ? // ?
@ -197,7 +197,7 @@ int Memory::Initialize() {
heaps_.vA0000000.Alloc(0x340000, 64 * 1024, kMemoryAllocationReserve, heaps_.vA0000000.Alloc(0x340000, 64 * 1024, kMemoryAllocationReserve,
kMemoryProtectNoAccess, true, &unk_phys_alloc); kMemoryProtectNoAccess, true, &unk_phys_alloc);
return 0; return true;
} }
static const struct { static const struct {

View File

@ -63,45 +63,85 @@ struct HeapAllocationInfo {
uint32_t type; uint32_t type;
}; };
// Describes a single page in the page table.
union PageEntry { union PageEntry {
struct { struct {
uint32_t base_address : 20; // in 4k pages // Base address of the allocated region in 4k pages.
uint32_t region_page_count : 20; // 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; uint32_t allocation_protect : 4;
// Current protection bits as of the last Protect.
// Composed of bits from MemoryProtectFlag.
uint32_t current_protect : 4; uint32_t current_protect : 4;
// Allocation state of the page as a MemoryAllocationFlag bit mask.
uint32_t state : 2; uint32_t state : 2;
uint32_t reserved : 14; uint32_t reserved : 14;
}; };
uint64_t qword; uint64_t qword;
}; };
// Heap abstraction for page-based allocation.
class BaseHeap { class BaseHeap {
public: public:
virtual ~BaseHeap(); virtual ~BaseHeap();
// Size of each page within the heap range in bytes.
uint32_t page_size() const { return page_size_; } uint32_t page_size() const { return page_size_; }
// Disposes and decommits all memory and clears the page table.
virtual void Dispose(); virtual void Dispose();
// Dumps information about all allocations within the heap to the log.
void DumpMap(); void DumpMap();
// 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, virtual bool Alloc(uint32_t size, uint32_t alignment,
uint32_t allocation_type, uint32_t protect, bool top_down, uint32_t allocation_type, uint32_t protect, bool top_down,
uint32_t* out_address); 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, virtual bool AllocFixed(uint32_t base_address, uint32_t size,
uint32_t alignment, uint32_t allocation_type, uint32_t alignment, uint32_t allocation_type,
uint32_t protect); 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, virtual bool AllocRange(uint32_t low_address, uint32_t high_address,
uint32_t size, uint32_t alignment, uint32_t size, uint32_t alignment,
uint32_t allocation_type, uint32_t protect, uint32_t allocation_type, uint32_t protect,
bool top_down, uint32_t* out_address); 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); 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); 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); 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); 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); 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); 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); uint32_t GetPhysicalAddress(uint32_t address);
protected: protected:
@ -118,20 +158,30 @@ class BaseHeap {
std::vector<PageEntry> page_table_; std::vector<PageEntry> page_table_;
}; };
// Normal heap allowing allocations from guest virtual address ranges.
class VirtualHeap : public BaseHeap { class VirtualHeap : public BaseHeap {
public: public:
VirtualHeap(); VirtualHeap();
~VirtualHeap() override; ~VirtualHeap() override;
// Initializes the heap properties and allocates the page table.
void Initialize(uint8_t* membase, uint32_t heap_base, uint32_t heap_size, void Initialize(uint8_t* membase, uint32_t heap_base, uint32_t heap_size,
uint32_t page_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 { class PhysicalHeap : public BaseHeap {
public: public:
PhysicalHeap(); PhysicalHeap();
~PhysicalHeap() override; ~PhysicalHeap() override;
// Initializes the heap properties and allocates the page table.
void Initialize(uint8_t* membase, uint32_t heap_base, uint32_t heap_size, void Initialize(uint8_t* membase, uint32_t heap_base, uint32_t heap_size,
uint32_t page_size, VirtualHeap* parent_heap); uint32_t page_size, VirtualHeap* parent_heap);
@ -152,16 +202,40 @@ class PhysicalHeap : public BaseHeap {
VirtualHeap* parent_heap_; 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 { class Memory {
public: public:
Memory(); Memory();
~Memory(); ~Memory();
int Initialize(); // 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();
// Full file name and path of the memory-mapped file backing all memory.
const std::wstring& file_name() const { return file_name_; } 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_; } 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 { inline uint8_t* TranslateVirtual(uint32_t guest_address) const {
return virtual_membase_ + guest_address; return virtual_membase_ + guest_address;
} }
@ -170,7 +244,13 @@ class Memory {
return reinterpret_cast<T>(virtual_membase_ + guest_address); return reinterpret_cast<T>(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_; } 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 { inline uint8_t* TranslatePhysical(uint32_t guest_address) const {
return physical_membase_ + (guest_address & 0x1FFFFFFF); return physical_membase_ + (guest_address & 0x1FFFFFFF);
} }
@ -179,32 +259,62 @@ class Memory {
return reinterpret_cast<T>(physical_membase_ + return reinterpret_cast<T>(physical_membase_ +
(guest_address & 0x1FFFFFFF)); (guest_address & 0x1FFFFFFF));
} }
// TODO(benvanik): make poly memory utils for these. // Zeros out a range of memory at the given guest address.
void Zero(uint32_t address, uint32_t size); 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); 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); 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, uint32_t SearchAligned(uint32_t start, uint32_t end, const uint32_t* values,
size_t value_count); 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, bool AddVirtualMappedRange(uint32_t virtual_address, uint32_t mask,
uint32_t size, void* context, uint32_t size, void* context,
cpu::MMIOReadCallback read_callback, cpu::MMIOReadCallback read_callback,
cpu::MMIOWriteCallback write_callback); cpu::MMIOWriteCallback write_callback);
// Gets the defined MMIO range for the given virtual address, if any.
cpu::MMIORange* LookupVirtualMappedRange(uint32_t virtual_address); 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 AddPhysicalWriteWatch(uint32_t physical_address, uint32_t length, uintptr_t AddPhysicalWriteWatch(uint32_t physical_address, uint32_t length,
cpu::WriteWatchCallback callback, cpu::WriteWatchCallback callback,
void* callback_context, void* callback_data); void* callback_context, void* callback_data);
// Cancels a write watch requested with AddPhysicalWriteWatch.
void CancelWriteWatch(uintptr_t watch_handle); void CancelWriteWatch(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 SystemHeapAlloc(uint32_t size, uint32_t alignment = 0x20,
uint32_t system_heap_flags = kSystemHeapDefault); uint32_t system_heap_flags = kSystemHeapDefault);
// Frees memory allocated with SystemHeapAlloc.
void SystemHeapFree(uint32_t address); void SystemHeapFree(uint32_t address);
// Gets the heap for the address space containing the given address.
BaseHeap* LookupHeap(uint32_t address); BaseHeap* LookupHeap(uint32_t address);
// Gets the heap with the given properties.
BaseHeap* LookupHeapByType(bool physical, uint32_t page_size); BaseHeap* LookupHeapByType(bool physical, uint32_t page_size);
// Dumps a map of all allocated memory to the log.
void DumpMap(); void DumpMap();
private: private: