2010-11-06 16:25:40 +00:00
|
|
|
/* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "PrecompiledHeader.h"
|
|
|
|
#include "PageFaultSource.h"
|
|
|
|
|
|
|
|
#ifndef __WXMSW__
|
2010-11-06 05:21:06 +00:00
|
|
|
#include <wx/thread.h>
|
|
|
|
#endif
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2010-11-17 03:18:36 +00:00
|
|
|
#include "EventSource.inl"
|
|
|
|
#include "MemsetFast.inl"
|
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
template class EventSource<IEventListener_PageFault>;
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
SrcType_PageFault *Source_PageFault = NULL;
|
|
|
|
Threading::Mutex PageFault_Mutex;
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2010-11-15 14:05:02 +00:00
|
|
|
void pxInstallSignalHandler()
|
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!Source_PageFault) {
|
|
|
|
Source_PageFault = new SrcType_PageFault();
|
|
|
|
}
|
2010-11-15 14:05:02 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
_platform_InstallSignalHandler();
|
2010-11-15 14:05:02 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
// NOP on Win32 systems -- we use __try{} __except{} instead.
|
2010-11-15 14:05:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// EventListener_PageFault (implementations)
|
|
|
|
// --------------------------------------------------------------------------------------
|
2010-11-06 16:25:40 +00:00
|
|
|
EventListener_PageFault::EventListener_PageFault()
|
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
pxAssert(Source_PageFault);
|
|
|
|
Source_PageFault->Add(*this);
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EventListener_PageFault::~EventListener_PageFault() throw()
|
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
if (Source_PageFault)
|
|
|
|
Source_PageFault->Remove(*this);
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
void SrcType_PageFault::Dispatch(const PageFaultInfo ¶ms)
|
2010-11-06 16:25:40 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
m_handled = false;
|
|
|
|
_parent::Dispatch(params);
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
void SrcType_PageFault::_DispatchRaw(ListenerIterator iter, const ListenerIterator &iend, const PageFaultInfo &evt)
|
2010-11-06 16:25:40 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
do {
|
|
|
|
(*iter)->DispatchEvent(evt, m_handled);
|
|
|
|
} while ((++iter != iend) && !m_handled);
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
2010-11-15 14:05:02 +00:00
|
|
|
// VirtualMemoryReserve (implementations)
|
2010-11-06 16:25:40 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
2016-11-12 15:28:37 +00:00
|
|
|
VirtualMemoryReserve::VirtualMemoryReserve(const wxString &name, size_t size)
|
|
|
|
: m_name(name)
|
2010-11-06 16:25:40 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
m_defsize = size;
|
2010-11-15 14:05:02 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
m_pages_commited = 0;
|
|
|
|
m_pages_reserved = 0;
|
|
|
|
m_baseptr = NULL;
|
|
|
|
m_prot_mode = PageAccess_None();
|
|
|
|
m_allow_writes = true;
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
VirtualMemoryReserve &VirtualMemoryReserve::SetName(const wxString &newname)
|
2010-11-16 15:58:42 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
m_name = newname;
|
|
|
|
return *this;
|
2010-11-16 15:58:42 +00:00
|
|
|
}
|
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
VirtualMemoryReserve &VirtualMemoryReserve::SetBaseAddr(uptr newaddr)
|
2010-11-15 14:05:02 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!pxAssertDev(!m_pages_reserved, "Invalid object state: you must release the virtual memory reserve prior to changing its base address!"))
|
|
|
|
return *this;
|
2010-11-17 03:18:36 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
m_baseptr = (void *)newaddr;
|
|
|
|
return *this;
|
2010-11-15 14:05:02 +00:00
|
|
|
}
|
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
VirtualMemoryReserve &VirtualMemoryReserve::SetPageAccessOnCommit(const PageProtectionMode &mode)
|
2010-11-15 14:05:02 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
m_prot_mode = mode;
|
|
|
|
return *this;
|
2010-11-15 14:05:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Notes:
|
|
|
|
// * This method should be called if the object is already in an released (unreserved) state.
|
|
|
|
// Subsequent calls will be ignored, and the existing reserve will be returned.
|
|
|
|
//
|
2010-11-06 16:25:40 +00:00
|
|
|
// Parameters:
|
2010-11-15 14:05:02 +00:00
|
|
|
// size - size of the reserve, in bytes. (optional)
|
|
|
|
// If not specified (or zero), then the default size specified in the constructor for the
|
|
|
|
// object instance is used.
|
|
|
|
//
|
2010-11-06 16:25:40 +00:00
|
|
|
// upper_bounds - criteria that must be met for the allocation to be valid.
|
|
|
|
// If the OS refuses to allocate the memory below the specified address, the
|
|
|
|
// object will fail to initialize and an exception will be thrown.
|
2016-11-12 15:28:37 +00:00
|
|
|
void *VirtualMemoryReserve::Reserve(size_t size, uptr base, uptr upper_bounds)
|
2010-11-06 16:25:40 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!pxAssertDev(m_baseptr == NULL, "(VirtualMemoryReserve) Invalid object state; object has already been reserved."))
|
|
|
|
return m_baseptr;
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!size)
|
|
|
|
size = m_defsize;
|
|
|
|
if (!size)
|
|
|
|
return NULL;
|
2010-11-15 14:05:02 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
m_pages_reserved = (size + __pagesize - 4) / __pagesize;
|
|
|
|
uptr reserved_bytes = m_pages_reserved * __pagesize;
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
m_baseptr = (void *)HostSys::MmapReserve(base, reserved_bytes);
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!m_baseptr || (upper_bounds != 0 && (((uptr)m_baseptr + reserved_bytes) > upper_bounds))) {
|
|
|
|
DevCon.Warning(L"%s: host memory @ %ls -> %ls is unavailable; attempting to map elsewhere...",
|
|
|
|
WX_STR(m_name), pxsPtr(base), pxsPtr(base + size));
|
2010-11-15 14:05:02 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
SafeSysMunmap(m_baseptr, reserved_bytes);
|
2010-11-15 14:05:02 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
if (base) {
|
|
|
|
// Let's try again at an OS-picked memory area, and then hope it meets needed
|
|
|
|
// boundschecking criteria below.
|
|
|
|
m_baseptr = HostSys::MmapReserve(0, reserved_bytes);
|
|
|
|
}
|
|
|
|
}
|
2010-11-17 03:18:36 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
if ((upper_bounds != 0) && (((uptr)m_baseptr + reserved_bytes) > upper_bounds)) {
|
|
|
|
SafeSysMunmap(m_baseptr, reserved_bytes);
|
|
|
|
// returns null, caller should throw an exception or handle appropriately.
|
|
|
|
}
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!m_baseptr)
|
|
|
|
return NULL;
|
2010-11-16 15:58:42 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
FastFormatUnicode mbkb;
|
|
|
|
uint mbytes = reserved_bytes / _1mb;
|
|
|
|
if (mbytes)
|
|
|
|
mbkb.Write("[%umb]", mbytes);
|
|
|
|
else
|
|
|
|
mbkb.Write("[%ukb]", reserved_bytes / 1024);
|
2010-11-16 15:58:42 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
DevCon.WriteLn(Color_Gray, L"%-32s @ %ls -> %ls %ls", WX_STR(m_name),
|
|
|
|
pxsPtr(m_baseptr), pxsPtr((uptr)m_baseptr + reserved_bytes), mbkb.c_str());
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
return m_baseptr;
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
void VirtualMemoryReserve::ReprotectCommittedBlocks(const PageProtectionMode &newmode)
|
2010-12-30 06:21:07 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!m_pages_commited)
|
|
|
|
return;
|
|
|
|
HostSys::MemProtect(m_baseptr, m_pages_commited * __pagesize, newmode);
|
2010-12-30 06:21:07 +00:00
|
|
|
}
|
|
|
|
|
2010-11-06 16:25:40 +00:00
|
|
|
// Clears all committed blocks, restoring the allocation to a reserve only.
|
2010-11-15 14:05:02 +00:00
|
|
|
void VirtualMemoryReserve::Reset()
|
2010-11-06 16:25:40 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!m_pages_commited)
|
|
|
|
return;
|
2010-11-15 14:05:02 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
ReprotectCommittedBlocks(PageAccess_None());
|
|
|
|
HostSys::MmapResetPtr(m_baseptr, m_pages_commited * __pagesize);
|
|
|
|
m_pages_commited = 0;
|
2010-11-15 14:05:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VirtualMemoryReserve::Release()
|
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
SafeSysMunmap(m_baseptr, m_pages_reserved * __pagesize);
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
|
|
|
|
2010-11-15 14:05:02 +00:00
|
|
|
bool VirtualMemoryReserve::Commit()
|
2010-11-06 16:25:40 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!m_pages_reserved)
|
|
|
|
return false;
|
|
|
|
if (!pxAssert(!m_pages_commited))
|
|
|
|
return true;
|
2010-11-15 14:05:02 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
m_pages_commited = m_pages_reserved;
|
|
|
|
return HostSys::MmapCommitPtr(m_baseptr, m_pages_reserved * __pagesize, m_prot_mode);
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
|
|
|
|
2010-12-30 06:21:07 +00:00
|
|
|
void VirtualMemoryReserve::AllowModification()
|
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
m_allow_writes = true;
|
|
|
|
HostSys::MemProtect(m_baseptr, m_pages_commited * __pagesize, m_prot_mode);
|
2010-12-30 06:21:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VirtualMemoryReserve::ForbidModification()
|
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
m_allow_writes = false;
|
|
|
|
HostSys::MemProtect(m_baseptr, m_pages_commited * __pagesize, PageProtectionMode(m_prot_mode).Write(false));
|
2010-12-30 06:21:07 +00:00
|
|
|
}
|
|
|
|
|
2010-11-15 14:05:02 +00:00
|
|
|
|
2010-11-06 16:25:40 +00:00
|
|
|
// If growing the array, or if shrinking the array to some point that's still *greater* than the
|
|
|
|
// committed memory range, then attempt a passive "on-the-fly" resize that maps/unmaps some portion
|
|
|
|
// of the reserve.
|
|
|
|
//
|
|
|
|
// If the above conditions are not met, or if the map/unmap fails, this method returns false.
|
|
|
|
// The caller will be responsible for manually resetting the reserve.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// newsize - new size of the reserved buffer, in bytes.
|
2016-11-12 15:28:37 +00:00
|
|
|
bool VirtualMemoryReserve::TryResize(uint newsize)
|
2010-11-06 16:25:40 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
uint newPages = (newsize + __pagesize - 1) / __pagesize;
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
if (newPages > m_pages_reserved) {
|
|
|
|
uint toReservePages = newPages - m_pages_reserved;
|
|
|
|
uint toReserveBytes = toReservePages * __pagesize;
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
DevCon.WriteLn(L"%-32s is being expanded by %u pages.", WX_STR(m_name), toReservePages);
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
m_baseptr = (void *)HostSys::MmapReserve((uptr)GetPtrEnd(), toReserveBytes);
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
if (!m_baseptr) {
|
|
|
|
Console.Warning("%-32s could not be passively resized due to virtual memory conflict!");
|
|
|
|
Console.Indent().Warning("(attempted to map memory @ %08p -> %08p)", m_baseptr, (uptr)m_baseptr + toReserveBytes);
|
|
|
|
}
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
DevCon.WriteLn(Color_Gray, L"%-32s @ %08p -> %08p [%umb]", WX_STR(m_name),
|
|
|
|
m_baseptr, (uptr)m_baseptr + toReserveBytes, toReserveBytes / _1mb);
|
|
|
|
} else if (newPages < m_pages_reserved) {
|
|
|
|
if (m_pages_commited > newsize)
|
|
|
|
return false;
|
2010-11-17 03:18:36 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
uint toRemovePages = m_pages_reserved - newPages;
|
|
|
|
uint toRemoveBytes = toRemovePages * __pagesize;
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
DevCon.WriteLn(L"%-32s is being shrunk by %u pages.", WX_STR(m_name), toRemovePages);
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
HostSys::MmapResetPtr(GetPtrEnd(), toRemoveBytes);
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
DevCon.WriteLn(Color_Gray, L"%-32s @ %08p -> %08p [%umb]", WX_STR(m_name),
|
|
|
|
m_baseptr, (uptr)m_baseptr + toRemoveBytes, toRemoveBytes / _1mb);
|
|
|
|
}
|
2010-11-17 03:18:36 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
return true;
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// PageProtectionMode (implementations)
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
wxString PageProtectionMode::ToString() const
|
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
wxString modeStr;
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
if (m_read)
|
|
|
|
modeStr += L"Read";
|
|
|
|
if (m_write)
|
|
|
|
modeStr += L"Write";
|
|
|
|
if (m_exec)
|
|
|
|
modeStr += L"Exec";
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
if (modeStr.IsEmpty())
|
|
|
|
return L"NoAccess";
|
|
|
|
if (modeStr.Length() <= 5)
|
|
|
|
modeStr += L"Only";
|
2010-11-06 16:25:40 +00:00
|
|
|
|
2016-11-12 15:28:37 +00:00
|
|
|
return modeStr;
|
2010-11-06 16:25:40 +00:00
|
|
|
}
|
2015-06-05 19:18:21 +00:00
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// Common HostSys implementation
|
|
|
|
// --------------------------------------------------------------------------------------
|
2016-11-12 15:28:37 +00:00
|
|
|
void HostSys::Munmap(void *base, size_t size)
|
2015-06-05 19:18:21 +00:00
|
|
|
{
|
2016-11-12 15:28:37 +00:00
|
|
|
Munmap((uptr)base, size);
|
2015-06-05 19:18:21 +00:00
|
|
|
}
|