/* PCSX2 - PS2 Emulator for PCs * Copyright (C) 2002-2010 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with PCSX2. * If not, see . */ // [TODO] Rename this file to VirtualMemory.h !! #pragma once // ===================================================================================================== // Cross-Platform Memory Protection (Used by VTLB, Recompilers and Texture caches) // ===================================================================================================== // Win32 platforms use the SEH model: __try {} __except {} // Linux platforms use the POSIX Signals model: sigaction() // [TODO] OS-X (Darwin) platforms should use the Mach exception model (not implemented) #include "EventSource.h" struct PageFaultInfo { uptr addr; PageFaultInfo( uptr address ) { addr = address; } }; // -------------------------------------------------------------------------------------- // IEventListener_PageFault // -------------------------------------------------------------------------------------- class IEventListener_PageFault : public IEventDispatcher { public: typedef PageFaultInfo EvtParams; public: virtual ~IEventListener_PageFault() throw() {} virtual void DispatchEvent( const PageFaultInfo& evtinfo, bool& handled ) { OnPageFaultEvent( evtinfo, handled ); } virtual void DispatchEvent( const PageFaultInfo& evtinfo ) { pxFailRel( "Don't call me, damnit. Use DispatchException instead." ); } virtual void OnPageFaultEvent( const PageFaultInfo& evtinfo, bool& handled ) {} }; // -------------------------------------------------------------------------------------- // EventListener_PageFault / EventListenerHelper_PageFault // -------------------------------------------------------------------------------------- class EventListener_PageFault : public IEventListener_PageFault { public: EventListener_PageFault(); virtual ~EventListener_PageFault() throw(); }; template< typename TypeToDispatchTo > class EventListenerHelper_PageFault : public EventListener_PageFault { public: TypeToDispatchTo* Owner; public: EventListenerHelper_PageFault( TypeToDispatchTo& dispatchTo ) { Owner = &dispatchTo; } EventListenerHelper_PageFault( TypeToDispatchTo* dispatchTo ) { Owner = dispatchTo; } virtual ~EventListenerHelper_PageFault() throw() {} protected: virtual void OnPageFaultEvent( const PageFaultInfo& info, bool& handled ) { Owner->OnPageFaultEvent( info, handled ); } }; // -------------------------------------------------------------------------------------- // SrcType_PageFault // -------------------------------------------------------------------------------------- class SrcType_PageFault : public EventSource { protected: typedef EventSource _parent; protected: bool m_handled; public: SrcType_PageFault() {} virtual ~SrcType_PageFault() throw() { } bool WasHandled() const { return m_handled; } virtual void Dispatch( const PageFaultInfo& params ); protected: virtual void _DispatchRaw( ListenerIterator iter, const ListenerIterator& iend, const PageFaultInfo& evt ); }; // -------------------------------------------------------------------------------------- // VirtualMemoryReserve // -------------------------------------------------------------------------------------- class VirtualMemoryReserve { DeclareNoncopyableObject( VirtualMemoryReserve ); protected: wxString m_name; // Default size of the reserve, in bytes. Can be specified when the object is constructed. // Is used as the reserve size when Reserve() is called, unless an override is specified // in the Reserve parameters. size_t m_defsize; void* m_baseptr; // reserved memory (in pages). uptr m_pages_reserved; // Records the number of pages committed to memory. // (metric for analysis of buffer usage) uptr m_pages_commited; // Protection mode to be applied to committed blocks. PageProtectionMode m_prot_mode; // Controls write access to the entire reserve. When true (the default), the reserve // operates normally. When set to false, all committed blocks are re-protected with // write disabled, and accesses to uncommitted blocks (read or write) will cause a GPF // as well. bool m_allow_writes; public: VirtualMemoryReserve( const wxString& name=wxEmptyString, size_t size = 0 ); virtual ~VirtualMemoryReserve() throw() { Release(); } virtual void* Reserve( size_t size = 0, uptr base = 0, uptr upper_bounds = 0 ); virtual void* ReserveAt( uptr base = 0, uptr upper_bounds = 0 ) { return Reserve(m_defsize, base, upper_bounds); } virtual void Reset(); virtual void Release(); virtual bool TryResize( uint newsize ); virtual bool Commit(); virtual void ForbidModification(); virtual void AllowModification(); bool IsOk() const { return m_baseptr != NULL; } wxString GetName() const { return m_name; } uptr GetReserveSizeInBytes() const { return m_pages_reserved * __pagesize; } uptr GetReserveSizeInPages() const { return m_pages_reserved; } uint GetCommittedPageCount() const { return m_pages_commited; } uint GetCommittedBytes() const { return m_pages_commited * __pagesize; } u8* GetPtr() { return (u8*)m_baseptr; } const u8* GetPtr() const { return (u8*)m_baseptr; } u8* GetPtrEnd() { return (u8*)m_baseptr + (m_pages_reserved * __pagesize); } const u8* GetPtrEnd() const { return (u8*)m_baseptr + (m_pages_reserved * __pagesize); } VirtualMemoryReserve& SetName( const wxString& newname ); VirtualMemoryReserve& SetBaseAddr( uptr newaddr ); VirtualMemoryReserve& SetPageAccessOnCommit( const PageProtectionMode& mode ); operator void*() { return m_baseptr; } operator const void*() const { return m_baseptr; } operator u8*() { return (u8*)m_baseptr; } operator const u8*() const { return (u8*)m_baseptr; } u8& operator[](uint idx) { pxAssume(idx < (m_pages_reserved * __pagesize)); return *((u8*)m_baseptr + idx); } const u8& operator[](uint idx) const { pxAssume(idx < (m_pages_reserved * __pagesize)); return *((u8*)m_baseptr + idx); } protected: virtual void ReprotectCommittedBlocks( const PageProtectionMode& newmode ); }; // -------------------------------------------------------------------------------------- // BaseVmReserveListener // -------------------------------------------------------------------------------------- class BaseVmReserveListener : public VirtualMemoryReserve { DeclareNoncopyableObject( BaseVmReserveListener ); typedef VirtualMemoryReserve _parent; protected: EventListenerHelper_PageFault m_pagefault_listener; // Incremental size by which the buffer grows (in pages) uptr m_blocksize; public: BaseVmReserveListener( const wxString& name, size_t size = 0 ); virtual ~BaseVmReserveListener() throw() { } operator void*() { return m_baseptr; } operator const void*() const { return m_baseptr; } operator u8*() { return (u8*)m_baseptr; } operator const u8*() const { return (u8*)m_baseptr; } using _parent::operator[]; void OnPageFaultEvent( const PageFaultInfo& info, bool& handled ); virtual uptr SetBlockSize( uptr bytes ) { m_blocksize = (bytes + __pagesize - 1) / __pagesize; return m_blocksize * __pagesize; } protected: // This function is called from OnPageFaultEvent after the address has been translated // and confirmed to apply to this reserved area in question. OnPageFaultEvent contains // a try/catch exception handler, which ensures "reasonable" error response behavior if // this function throws exceptions. // // Important: This method is called from the context of an exception/signal handler. On // Windows this isn't a big deal (most operations are ok). On Linux, however, logging // and other facilities are probably not a good idea. virtual void DoCommitAndProtect( uptr offset )=0; // This function is called for every committed block. virtual void OnCommittedBlock( void* block )=0; virtual void CommitBlocks( uptr page, uint blocks ); }; // -------------------------------------------------------------------------------------- // SpatialArrayReserve // -------------------------------------------------------------------------------------- // A spatial array is one where large areas of the memory reserve will remain unused during // process execution. Only areas put to use will be committed to virtual memory. // // Spatial array efficiency depends heavily on selecting the right parameters for the array's // primary intended use. Memory in a spatial array is arranged by blocks, with each block // containing some number of pages (pages are 4096 bytes each on most platforms). When the // array is accessed, the entire block containing the addressed memory will be committed at // once. Blocks can be a single page in size (4096 bytes), though this is highly discouraged // due to overhead and fragmentation penalties. // // Balancing block sizes: // Larger blocks are good for reducing memory fragmentation and block-tracking overhead, but // can also result in a lot of otherwise unused memory being committed to memory. Smaller // blocks are good for arrays that will tend toward more sequential behavior, as they reduce // the amount of unused memory being committed. However, since every block requires a // tracking entry, assigning small blocks to a very large array can result in quite a bit of // unwanted overhead. Furthermore, if the array is accessed randomly, system physical memory // will become very fragmented, which will also hurt performance. // // By default, the base block size is based on a heuristic that balances the size of the spatial // array reserve against a best-guess performance profile for the target platform. // class SpatialArrayReserve : public BaseVmReserveListener { typedef BaseVmReserveListener _parent; protected: uint m_numblocks; // Array of block bits, each bit indicating if the block has been committed to memory // or not. The array length is typically determined via ((numblocks+7) / 8), though the // actual array size may be larger in order to accommodate 32-bit or 128-bit accelerated // operations. ScopedAlignedAlloc m_blockbits; public: SpatialArrayReserve( const wxString& name ); virtual void* Reserve( size_t size = 0, uptr base = 0, uptr upper_bounds = 0 ); virtual void Reset(); virtual bool TryResize( uint newsize ); void OnCommittedBlock( void* block ); SpatialArrayReserve& SetBlockCount( uint blocks ); SpatialArrayReserve& SetBlockSizeInPages( uint bytes ); uptr SetBlockSize( uptr bytes ); operator void*() { return m_baseptr; } operator const void*() const { return m_baseptr; } operator u8*() { return (u8*)m_baseptr; } operator const u8*() const { return (u8*)m_baseptr; } using _parent::operator[]; protected: void ReprotectCommittedBlocks( const PageProtectionMode& newmode ); void DoCommitAndProtect( uptr page ); uint _calcBlockBitArrayLength() const; }; #ifdef __LINUX__ # define PCSX2_PAGEFAULT_PROTECT # define PCSX2_PAGEFAULT_EXCEPT #elif defined( _WIN32 ) struct _EXCEPTION_POINTERS; extern int SysPageFaultExceptionFilter(struct _EXCEPTION_POINTERS* eps); # define PCSX2_PAGEFAULT_PROTECT __try # define PCSX2_PAGEFAULT_EXCEPT __except(SysPageFaultExceptionFilter(GetExceptionInformation())) {} #else # error PCSX2 - Unsupported operating system platform. #endif extern void pxInstallSignalHandler(); extern void _platform_InstallSignalHandler(); extern SrcType_PageFault* Source_PageFault;