/
PageFaultSource.h
356 lines (276 loc) · 11.9 KB
/
PageFaultSource.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/* 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/>.
*/
// [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<PageFaultInfo>
{
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<IEventListener_PageFault>
{
protected:
typedef EventSource<IEventListener_PageFault> _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)
{
pxAssert(idx < (m_pages_reserved * __pagesize));
return *((u8*)m_baseptr + idx);
}
const u8& operator[](uint idx) const
{
pxAssert(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<BaseVmReserveListener> 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<u8,16> 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;