Fork SheepShaver vm_alloc/SDL

This commit is contained in:
Seg 2021-06-14 13:26:23 -07:00
parent 256f93b221
commit c756b743d5
7 changed files with 6658 additions and 9 deletions

View File

@ -59,22 +59,23 @@ links:
include/prefs.h include/scsi.h include/serial.h \
include/serial_defs.h include/sony.h include/sys.h \
include/timer.h include/xpram.h \
CrossPlatform/sigsegv.h CrossPlatform/vm_alloc.h CrossPlatform/vm_alloc.cpp \
CrossPlatform/video_vosf.h CrossPlatform/video_blit.h CrossPlatform/video_blit.cpp \
Unix/audio_oss_esd.cpp \
CrossPlatform/sigsegv.h \
CrossPlatform/video_blit.h CrossPlatform/video_blit.cpp \
SDL/SDLMain.h SDL/SDLMain.m SDL/audio_sdl.cpp SDL/keycodes \
SDL/prefs_sdl.cpp SDL/xpram_sdl.cpp \
Unix/vhd_unix.cpp \
Unix/extfs_unix.cpp Unix/serial_unix.cpp \
Unix/sshpty.h Unix/sshpty.c Unix/strlcpy.h Unix/strlcpy.c \
Unix/sys_unix.cpp Unix/timer_unix.cpp Unix/xpram_unix.cpp \
Unix/semaphore.h Unix/posix_sem.cpp Unix/config.sub Unix/config.guess Unix/m4 \
Unix/keycodes Unix/tunconfig Unix/clip_unix.cpp Unix/Irix/audio_irix.cpp \
Unix/keycodes Unix/tunconfig Unix/clip_unix.cpp \
Unix/Linux/scsi_linux.cpp Unix/Linux/NetDriver Unix/ether_unix.cpp \
Unix/rpc.h Unix/rpc_unix.cpp Unix/ldscripts \
Unix/tinyxml2.h Unix/tinyxml2.cpp Unix/disk_unix.h \
Unix/disk_sparsebundle.cpp Unix/Darwin/mkstandalone \
Unix/Darwin/pagezero.c Unix/Darwin/testlmem.sh \
dummy/audio_dummy.cpp dummy/clip_dummy.cpp dummy/serial_dummy.cpp \
dummy/prefs_editor_dummy.cpp dummy/scsi_dummy.cpp SDL slirp \
dummy/prefs_editor_dummy.cpp dummy/scsi_dummy.cpp slirp \
MacOSX/sys_darwin.cpp MacOSX/clip_macosx.cpp MacOSX/clip_macosx64.mm \
MacOSX/macos_util_macosx.h Unix/cpr.sh \
MacOSX/extfs_macosx.cpp Windows/clip_windows.cpp \

View File

@ -1 +0,0 @@
../../../BasiliskII/src/CrossPlatform/video_vosf.h

View File

@ -0,0 +1,686 @@
/*
* video_vosf.h - Video/graphics emulation, video on SEGV signals support
*
* Basilisk II (C) 1997-2008 Christian Bauer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef VIDEO_VOSF_H
#define VIDEO_VOSF_H
// Note: this file must be #include'd only in video_x.cpp
#ifdef ENABLE_VOSF
#include "sigsegv.h"
#include "vm_alloc.h"
#ifdef _WIN32
#include "util_windows.h"
#endif
// Import SDL-backend-specific functions
#ifdef USE_SDL_VIDEO
extern void update_sdl_video(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h);
extern void update_sdl_video(SDL_Surface *screen, int numrects, SDL_Rect *rects);
#endif
// Glue for SDL and X11 support
#ifdef TEST_VOSF_PERFORMANCE
#define MONITOR_INIT /* nothing */
#else
#ifdef USE_SDL_VIDEO
#define MONITOR_INIT SDL_monitor_desc &monitor
#define VIDEO_DRV_WIN_INIT driver_base *drv
#define VIDEO_DRV_DGA_INIT driver_base *drv
#define VIDEO_DRV_LOCK_PIXELS SDL_VIDEO_LOCK_SURFACE(drv->s)
#define VIDEO_DRV_UNLOCK_PIXELS SDL_VIDEO_UNLOCK_SURFACE(drv->s)
#define VIDEO_DRV_DEPTH drv->s->format->BitsPerPixel
#define VIDEO_DRV_WIDTH drv->s->w
#define VIDEO_DRV_HEIGHT drv->s->h
#define VIDEO_DRV_ROW_BYTES drv->s->pitch
#else
#ifdef SHEEPSHAVER
#define MONITOR_INIT /* nothing */
#define VIDEO_DRV_WIN_INIT /* nothing */
#define VIDEO_DRV_DGA_INIT /* nothing */
#define VIDEO_DRV_WINDOW the_win
#define VIDEO_DRV_GC the_gc
#define VIDEO_DRV_IMAGE img
#define VIDEO_DRV_HAVE_SHM have_shm
#else
#define MONITOR_INIT X11_monitor_desc &monitor
#define VIDEO_DRV_WIN_INIT driver_window *drv
#define VIDEO_DRV_DGA_INIT driver_dga *drv
#define VIDEO_DRV_WINDOW drv->w
#define VIDEO_DRV_GC drv->gc
#define VIDEO_DRV_IMAGE drv->img
#define VIDEO_DRV_HAVE_SHM drv->have_shm
#endif
#define VIDEO_DRV_LOCK_PIXELS /* nothing */
#define VIDEO_DRV_UNLOCK_PIXELS /* nothing */
#define VIDEO_DRV_DEPTH VIDEO_DRV_IMAGE->depth
#define VIDEO_DRV_WIDTH VIDEO_DRV_IMAGE->width
#define VIDEO_DRV_HEIGHT VIDEO_DRV_IMAGE->height
#define VIDEO_DRV_ROW_BYTES VIDEO_DRV_IMAGE->bytes_per_line
#endif
#endif
// Prototypes
static void vosf_do_set_dirty_area(uintptr first, uintptr last);
static void vosf_set_dirty_area(int x, int y, int w, int h, unsigned screen_width, unsigned screen_height, unsigned bytes_per_row);
// Variables for Video on SEGV support
static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
struct ScreenPageInfo {
unsigned top, bottom; // Mapping between this virtual page and Mac scanlines
};
struct ScreenInfo {
uintptr memStart; // Start address aligned to page boundary
uint32 memLength; // Length of the memory addressed by the screen pages
uintptr pageSize; // Size of a page
int pageBits; // Shift count to get the page number
uint32 pageCount; // Number of pages allocated to the screen
bool dirty; // Flag: set if the frame buffer was touched
bool very_dirty; // Flag: set if the frame buffer was completely modified (e.g. colormap changes)
char * dirtyPages; // Table of flags set if page was altered
ScreenPageInfo * pageInfo; // Table of mappings page -> Mac scanlines
};
static ScreenInfo mainBuffer;
#define PFLAG_SET_VALUE 0x00
#define PFLAG_CLEAR_VALUE 0x01
#define PFLAG_SET_VALUE_4 0x00000000
#define PFLAG_CLEAR_VALUE_4 0x01010101
#define PFLAG_SET(page) mainBuffer.dirtyPages[page] = PFLAG_SET_VALUE
#define PFLAG_CLEAR(page) mainBuffer.dirtyPages[page] = PFLAG_CLEAR_VALUE
#define PFLAG_ISSET(page) (mainBuffer.dirtyPages[page] == PFLAG_SET_VALUE)
#define PFLAG_ISCLEAR(page) (mainBuffer.dirtyPages[page] != PFLAG_SET_VALUE)
#ifdef UNALIGNED_PROFITABLE
# define PFLAG_ISSET_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_SET_VALUE_4)
# define PFLAG_ISCLEAR_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_CLEAR_VALUE_4)
#else
# define PFLAG_ISSET_4(page) \
PFLAG_ISSET(page ) && PFLAG_ISSET(page+1) \
&& PFLAG_ISSET(page+2) && PFLAG_ISSET(page+3)
# define PFLAG_ISCLEAR_4(page) \
PFLAG_ISCLEAR(page ) && PFLAG_ISCLEAR(page+1) \
&& PFLAG_ISCLEAR(page+2) && PFLAG_ISCLEAR(page+3)
#endif
// Set the selected page range [ first_page, last_page [ into the SET state
#define PFLAG_SET_RANGE(first_page, last_page) \
memset(mainBuffer.dirtyPages + (first_page), PFLAG_SET_VALUE, \
(last_page) - (first_page))
// Set the selected page range [ first_page, last_page [ into the CLEAR state
#define PFLAG_CLEAR_RANGE(first_page, last_page) \
memset(mainBuffer.dirtyPages + (first_page), PFLAG_CLEAR_VALUE, \
(last_page) - (first_page))
#define PFLAG_SET_ALL do { \
PFLAG_SET_RANGE(0, mainBuffer.pageCount); \
mainBuffer.dirty = true; \
} while (0)
#define PFLAG_CLEAR_ALL do { \
PFLAG_CLEAR_RANGE(0, mainBuffer.pageCount); \
mainBuffer.dirty = false; \
mainBuffer.very_dirty = false; \
} while (0)
#define PFLAG_SET_VERY_DIRTY do { \
mainBuffer.very_dirty = true; \
} while (0)
// Set the following macro definition to 1 if your system
// provides a really fast strchr() implementation
//#define HAVE_FAST_STRCHR 0
static inline unsigned find_next_page_set(unsigned page)
{
#if HAVE_FAST_STRCHR
char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_SET_VALUE);
return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
#else
while (PFLAG_ISCLEAR_4(page))
page += 4;
while (PFLAG_ISCLEAR(page))
page++;
return page;
#endif
}
static inline unsigned find_next_page_clear(unsigned page)
{
#if HAVE_FAST_STRCHR
char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_CLEAR_VALUE);
return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
#else
while (PFLAG_ISSET_4(page))
page += 4;
while (PFLAG_ISSET(page))
page++;
return page;
#endif
}
#if defined(HAVE_PTHREADS)
static pthread_mutex_t vosf_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer (dirtyPages in fact)
#define LOCK_VOSF pthread_mutex_lock(&vosf_lock);
#define UNLOCK_VOSF pthread_mutex_unlock(&vosf_lock);
#elif defined(_WIN32)
static mutex_t vosf_lock; // Mutex to protect frame buffer (dirtyPages in fact)
#define LOCK_VOSF vosf_lock.lock();
#define UNLOCK_VOSF vosf_lock.unlock();
#elif defined(HAVE_SPINLOCKS)
static spinlock_t vosf_lock = SPIN_LOCK_UNLOCKED; // Mutex to protect frame buffer (dirtyPages in fact)
#define LOCK_VOSF spin_lock(&vosf_lock)
#define UNLOCK_VOSF spin_unlock(&vosf_lock)
#else
#define LOCK_VOSF
#define UNLOCK_VOSF
#endif
static int log_base_2(uint32 x)
{
uint32 mask = 0x80000000;
int l = 31;
while (l >= 0 && (x & mask) == 0) {
mask >>= 1;
l--;
}
return l;
}
// Extend size to page boundary
static uint32 page_extend(uint32 size)
{
const uint32 page_size = vm_get_page_size();
const uint32 page_mask = page_size - 1;
return (size + page_mask) & ~page_mask;
}
/*
* Check if VOSF acceleration is profitable on this platform
*/
#ifndef VOSF_PROFITABLE_TRIES
#define VOSF_PROFITABLE_TRIES VOSF_PROFITABLE_TRIES_DFL
#endif
const int VOSF_PROFITABLE_TRIES_DFL = 3; // Make 3 attempts for full screen update
const int VOSF_PROFITABLE_THRESHOLD = 16667/2; // 60 Hz (half of the quantum)
static bool video_vosf_profitable(uint32 *duration_p = NULL, uint32 *n_page_faults_p = NULL)
{
uint32 duration = 0;
uint32 n_tries = VOSF_PROFITABLE_TRIES;
const uint32 n_page_faults = mainBuffer.pageCount * n_tries;
#ifdef SHEEPSHAVER
const bool accel = PrefsFindBool("gfxaccel");
#else
const bool accel = false;
#endif
for (uint32 i = 0; i < n_tries; i++) {
uint64 start = GetTicks_usec();
for (uint32 p = 0; p < mainBuffer.pageCount; p++) {
uint8 *addr = (uint8 *)(mainBuffer.memStart + (p * mainBuffer.pageSize));
if (accel)
vosf_do_set_dirty_area((uintptr)addr, (uintptr)addr + mainBuffer.pageSize - 1);
else
addr[0] = 0; // Trigger Screen_fault_handler()
}
duration += uint32(GetTicks_usec() - start);
PFLAG_CLEAR_ALL;
mainBuffer.dirty = false;
if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
return false;
}
if (duration_p)
*duration_p = duration;
if (n_page_faults_p)
*n_page_faults_p = n_page_faults;
D(bug("Triggered %d page faults in %ld usec (%.1f usec per fault)\n", n_page_faults, duration, double(duration) / double(n_page_faults)));
return ((duration / n_tries) < (VOSF_PROFITABLE_THRESHOLD * (frame_skip ? frame_skip : 1)));
}
/*
* Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
*/
static bool video_vosf_init(MONITOR_INIT)
{
VIDEO_MODE_INIT_MONITOR;
const uintptr page_size = vm_get_page_size();
const uintptr page_mask = page_size - 1;
// Round up frame buffer base to page boundary
mainBuffer.memStart = (((uintptr) the_buffer) + page_mask) & ~page_mask;
// The frame buffer size shall already be aligned to page boundary (use page_extend)
mainBuffer.memLength = the_buffer_size;
mainBuffer.pageSize = page_size;
mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
// The "2" more bytes requested are a safety net to insure the
// loops in the update routines will terminate.
// See "How can we deal with array overrun conditions ?" hereunder for further details.
mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
if (mainBuffer.dirtyPages == NULL)
return false;
PFLAG_CLEAR_ALL;
PFLAG_CLEAR(mainBuffer.pageCount);
PFLAG_SET(mainBuffer.pageCount+1);
// Allocate and fill in pageInfo with start and end (inclusive) row in number of bytes
mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
if (mainBuffer.pageInfo == NULL)
return false;
uint32 a = 0;
for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
unsigned y1 = a / VIDEO_MODE_ROW_BYTES;
if (y1 >= VIDEO_MODE_Y)
y1 = VIDEO_MODE_Y - 1;
unsigned y2 = (a + mainBuffer.pageSize) / VIDEO_MODE_ROW_BYTES;
if (y2 >= VIDEO_MODE_Y)
y2 = VIDEO_MODE_Y - 1;
mainBuffer.pageInfo[i].top = y1;
mainBuffer.pageInfo[i].bottom = y2;
a += mainBuffer.pageSize;
if (a > mainBuffer.memLength)
a = mainBuffer.memLength;
}
// We can now write-protect the frame buffer
if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
return false;
// The frame buffer is sane, i.e. there is no write to it yet
mainBuffer.dirty = false;
return true;
}
/*
* Deinitialize VOSF system
*/
static void video_vosf_exit(void)
{
if (mainBuffer.pageInfo) {
free(mainBuffer.pageInfo);
mainBuffer.pageInfo = NULL;
}
if (mainBuffer.dirtyPages) {
free(mainBuffer.dirtyPages);
mainBuffer.dirtyPages = NULL;
}
}
/*
* Update VOSF state with specified dirty area
*/
static void vosf_do_set_dirty_area(uintptr first, uintptr last)
{
const int first_page = (first - mainBuffer.memStart) >> mainBuffer.pageBits;
const int last_page = (last - mainBuffer.memStart) >> mainBuffer.pageBits;
uint8 *addr = (uint8 *)(first & ~(mainBuffer.pageSize - 1));
for (int i = first_page; i <= last_page; i++) {
if (PFLAG_ISCLEAR(i)) {
PFLAG_SET(i);
vm_protect(addr, mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
}
addr += mainBuffer.pageSize;
}
}
static void vosf_set_dirty_area(int x, int y, int w, int h, unsigned screen_width, unsigned screen_height, unsigned bytes_per_row)
{
if (x < 0) {
w -= -x;
x = 0;
}
if (y < 0) {
h -= -y;
y = 0;
}
if (w <= 0 || h <= 0)
return;
if (unsigned(x + w) > screen_width)
w -= unsigned(x + w) - screen_width;
if (unsigned(y + h) > screen_height)
h -= unsigned(y + h) - screen_height;
LOCK_VOSF;
if (bytes_per_row >= screen_width) {
const int bytes_per_pixel = bytes_per_row / screen_width;
if (bytes_per_row <= mainBuffer.pageSize) {
const uintptr a0 = mainBuffer.memStart + y * bytes_per_row + x * bytes_per_pixel;
const uintptr a1 = mainBuffer.memStart + (y + h - 1) * bytes_per_row + (x + w - 1) * bytes_per_pixel;
vosf_do_set_dirty_area(a0, a1);
} else {
for (int j = y; j < y + h; j++) {
const uintptr a0 = mainBuffer.memStart + j * bytes_per_row + x * bytes_per_pixel;
const uintptr a1 = a0 + (w - 1) * bytes_per_pixel;
vosf_do_set_dirty_area(a0, a1);
}
}
} else {
const int pixels_per_byte = screen_width / bytes_per_row;
if (bytes_per_row <= mainBuffer.pageSize) {
const uintptr a0 = mainBuffer.memStart + y * bytes_per_row + x / pixels_per_byte;
const uintptr a1 = mainBuffer.memStart + (y + h - 1) * bytes_per_row + (x + w - 1) / pixels_per_byte;
vosf_do_set_dirty_area(a0, a1);
} else {
for (int j = y; j < y + h; j++) {
const uintptr a0 = mainBuffer.memStart + j * bytes_per_row + x / pixels_per_byte;
const uintptr a1 = mainBuffer.memStart + j * bytes_per_row + (x + w - 1) / pixels_per_byte;
vosf_do_set_dirty_area(a0, a1);
}
}
}
mainBuffer.dirty = true;
UNLOCK_VOSF;
}
/*
* Screen fault handler
*/
bool Screen_fault_handler(sigsegv_info_t *sip)
{
const uintptr addr = (uintptr)sigsegv_get_fault_address(sip);
/* Someone attempted to write to the frame buffer. Make it writeable
* now so that the data could actually be written to. It will be made
* read-only back in one of the screen update_*() functions.
*/
if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
LOCK_VOSF;
if (PFLAG_ISCLEAR(page)) {
PFLAG_SET(page);
vm_protect((char *)(addr & ~(mainBuffer.pageSize - 1)), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
}
mainBuffer.dirty = true;
UNLOCK_VOSF;
return true;
}
/* Otherwise, we don't know how to handle the fault, let it crash */
return false;
}
/*
* Update display for Windowed mode and VOSF
*/
/* How can we deal with array overrun conditions ?
The state of the framebuffer pages that have been touched are maintained
in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
Terminology
"Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
"CLEAR Page Guard" refers to the page following the Last Page but is always
in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
Page Guard but is always in the SET state.
Rough process
The update routines must determine which pages have to be blitted to the
screen. This job consists in finding the first_page that was touched.
i.e. find the next page that is SET. Then, finding how many pages were
touched starting from first_page. i.e. find the next page that is CLEAR.
There are two cases to check:
- Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
but it is beyond the valid pageCount value. Therefore, we exit from the
update routine.
- Last Page is SET: first_page equals (pageCount - 1) and
find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
page to the screen. On the next iteration, page equals pageCount and
find_next_page_set() will reach the SET Page Guard. We still safely exit
from the update routine because the SET Page Guard position is greater
than pageCount.
*/
#ifndef TEST_VOSF_PERFORMANCE
static void update_display_window_vosf(VIDEO_DRV_WIN_INIT)
{
VIDEO_MODE_INIT;
unsigned page = 0;
for (;;) {
const unsigned first_page = find_next_page_set(page);
if (first_page >= mainBuffer.pageCount)
break;
page = find_next_page_clear(first_page);
PFLAG_CLEAR_RANGE(first_page, page);
// Make the dirty pages read-only again
const int32 offset = first_page << mainBuffer.pageBits;
const uint32 length = (page - first_page) << mainBuffer.pageBits;
vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
// There is at least one line to update
const int y1 = mainBuffer.pageInfo[first_page].top;
const int y2 = mainBuffer.pageInfo[page - 1].bottom;
const int height = y2 - y1 + 1;
// Update the_host_buffer
VIDEO_DRV_LOCK_PIXELS;
const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
const int dst_bytes_per_row = VIDEO_DRV_ROW_BYTES;
int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
for (j = y1; j <= y2; j++) {
Screen_blit(the_host_buffer + i2, the_buffer + i1, src_bytes_per_row);
i1 += src_bytes_per_row;
i2 += dst_bytes_per_row;
}
VIDEO_DRV_UNLOCK_PIXELS;
#ifdef USE_SDL_VIDEO
update_sdl_video(drv->s, 0, y1, VIDEO_MODE_X, height);
#else
if (VIDEO_DRV_HAVE_SHM)
XShmPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height, 0);
else
XPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height);
#endif
}
mainBuffer.dirty = false;
}
#endif
/*
* Update display for DGA mode and VOSF
* (only in Real or Direct Addressing mode)
*/
#ifndef TEST_VOSF_PERFORMANCE
#if REAL_ADDRESSING || DIRECT_ADDRESSING
static void update_display_dga_vosf(VIDEO_DRV_DGA_INIT)
{
VIDEO_MODE_INIT;
// Compute number of bytes per row, take care to virtual screens
const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
const int dst_bytes_per_row = TrivialBytesPerRow(VIDEO_MODE_X, DepthModeForPixelDepth(VIDEO_DRV_DEPTH));
const int scr_bytes_per_row = VIDEO_DRV_ROW_BYTES;
assert(dst_bytes_per_row <= scr_bytes_per_row);
const int scr_bytes_left = scr_bytes_per_row - dst_bytes_per_row;
// Full screen update requested?
if (mainBuffer.very_dirty) {
PFLAG_CLEAR_ALL;
vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ);
memcpy(the_buffer_copy, the_buffer, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y);
VIDEO_DRV_LOCK_PIXELS;
int i1 = 0, i2 = 0;
for (uint32_t j = 0; j < VIDEO_MODE_Y; j++) {
Screen_blit(the_host_buffer + i2, the_buffer + i1, src_bytes_per_row);
i1 += src_bytes_per_row;
i2 += scr_bytes_per_row;
}
#ifdef USE_SDL_VIDEO
update_sdl_video(drv->s, 0, 0, VIDEO_MODE_X, VIDEO_MODE_Y);
#endif
VIDEO_DRV_UNLOCK_PIXELS;
return;
}
// Setup partial blitter (use 64-pixel wide chunks)
const uint32 n_pixels = 64;
const uint32 n_chunks = VIDEO_MODE_X / n_pixels;
const uint32 n_pixels_left = VIDEO_MODE_X - (n_chunks * n_pixels);
const uint32 src_chunk_size = TrivialBytesPerRow(n_pixels, VIDEO_MODE_DEPTH);
const uint32 dst_chunk_size = TrivialBytesPerRow(n_pixels, DepthModeForPixelDepth(VIDEO_DRV_DEPTH));
assert(src_chunk_size * n_chunks <= src_bytes_per_row);
assert(dst_chunk_size * n_chunks <= dst_bytes_per_row);
const uint32 src_chunk_size_left = src_bytes_per_row - (n_chunks * src_chunk_size);
const uint32 dst_chunk_size_left = dst_bytes_per_row - (n_chunks * dst_chunk_size);
unsigned page = 0;
uint32 last_scanline = uint32(-1);
for (;;) {
const unsigned first_page = find_next_page_set(page);
if (first_page >= mainBuffer.pageCount)
break;
page = find_next_page_clear(first_page);
PFLAG_CLEAR_RANGE(first_page, page);
// Make the dirty pages read-only again
const int32 offset = first_page << mainBuffer.pageBits;
const uint32 length = (page - first_page) << mainBuffer.pageBits;
vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
// Optimized for scanlines, don't process overlapping lines again
uint32 y1 = mainBuffer.pageInfo[first_page].top;
uint32 y2 = mainBuffer.pageInfo[page - 1].bottom;
if (last_scanline != uint32(-1)) {
if (y1 <= last_scanline && ++y1 >= VIDEO_MODE_Y)
continue;
if (y2 <= last_scanline && ++y2 >= VIDEO_MODE_Y)
continue;
}
last_scanline = y2;
// Update the_host_buffer and copy of the_buffer, one line at a time
uint32 i1 = y1 * src_bytes_per_row;
uint32 i2 = y1 * scr_bytes_per_row;
#ifdef USE_SDL_VIDEO
int bbi = 0;
SDL_Rect bb[3] = {
{ Sint16(VIDEO_MODE_X), Sint16(y1), 0, 0 },
{ Sint16(VIDEO_MODE_X), -1, 0, 0 },
{ Sint16(VIDEO_MODE_X), -1, 0, 0 }
};
#endif
VIDEO_DRV_LOCK_PIXELS;
for (uint32 j = y1; j <= y2; j++) {
for (uint32 i = 0; i < n_chunks; i++) {
if (memcmp(the_buffer_copy + i1, the_buffer + i1, src_chunk_size) != 0) {
memcpy(the_buffer_copy + i1, the_buffer + i1, src_chunk_size);
Screen_blit(the_host_buffer + i2, the_buffer + i1, src_chunk_size);
#ifdef USE_SDL_VIDEO
const int x = i * n_pixels;
if (x < bb[bbi].x) {
if (bb[bbi].w)
bb[bbi].w += bb[bbi].x - x;
else
bb[bbi].w = n_pixels;
bb[bbi].x = x;
}
else if (x >= bb[bbi].x + bb[bbi].w)
bb[bbi].w = x + n_pixels - bb[bbi].x;
#endif
}
i1 += src_chunk_size;
i2 += dst_chunk_size;
}
if (src_chunk_size_left && dst_chunk_size_left) {
if (memcmp(the_buffer_copy + i1, the_buffer + i1, src_chunk_size_left) != 0) {
memcpy(the_buffer_copy + i1, the_buffer + i1, src_chunk_size_left);
Screen_blit(the_host_buffer + i2, the_buffer + i1, src_chunk_size_left);
}
#ifdef USE_SDL_VIDEO
const int x = n_chunks * n_pixels;
if (x < bb[bbi].x) {
if (bb[bbi].w)
bb[bbi].w += bb[bbi].x - x;
else
bb[bbi].w = n_pixels_left;
bb[bbi].x = x;
}
else if (x >= bb[bbi].x + bb[bbi].w)
bb[bbi].w = x + n_pixels_left - bb[bbi].x;
#endif
}
i1 += src_chunk_size_left;
i2 += dst_chunk_size_left + scr_bytes_left;
#ifdef USE_SDL_VIDEO
bb[bbi].h++;
if (bb[bbi].w && (j == y1 || j == y2 - 1 || j == y2)) {
bbi++;
assert(bbi <= 3);
if (j != y2)
bb[bbi].y = j + 1;
}
#endif
}
#ifdef USE_SDL_VIDEO
update_sdl_video(drv->s, bbi, bb);
#endif
VIDEO_DRV_UNLOCK_PIXELS;
}
mainBuffer.dirty = false;
}
#endif
#endif
#endif /* ENABLE_VOSF */
#endif /* VIDEO_VOSF_H */

View File

@ -1 +0,0 @@
../../../BasiliskII/src/CrossPlatform/vm_alloc.cpp

View File

@ -0,0 +1,619 @@
/*
* vm_alloc.cpp - Wrapper to various virtual memory allocation schemes
* (supports mmap, vm_allocate or fallbacks to malloc)
*
* Basilisk II (C) 1997-2008 Christian Bauer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_WIN32_VM
#define WIN32_LEAN_AND_MEAN /* avoid including junk */
#include <windows.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "vm_alloc.h"
#if defined(__APPLE__) && defined(__MACH__)
#include <sys/utsname.h>
#endif
#ifdef HAVE_MACH_VM
#ifndef HAVE_MACH_TASK_SELF
#ifdef HAVE_TASK_SELF
#define mach_task_self task_self
#else
#error "No task_self(), you lose."
#endif
#endif
#endif
#ifdef HAVE_WIN32_VM
/* Windows is either ILP32 or LLP64 */
typedef UINT_PTR vm_uintptr_t;
#else
/* Other systems are sane as they are either ILP32 or LP64 */
typedef unsigned long vm_uintptr_t;
#endif
/* We want MAP_32BIT, if available, for SheepShaver and BasiliskII
because the emulated target is 32-bit and this helps to allocate
memory so that branches could be resolved more easily (32-bit
displacement to code in .text), on AMD64 for example. */
#if defined(__hpux)
#define MAP_32BIT MAP_ADDR32
#endif
#ifndef MAP_32BIT
#define MAP_32BIT 0
#endif
#ifdef __FreeBSD__
#define FORCE_MAP_32BIT MAP_FIXED
#else
#define FORCE_MAP_32BIT MAP_32BIT
#endif
#ifndef MAP_ANON
#define MAP_ANON 0
#endif
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS 0
#endif
/* NOTE: on linux MAP_32BIT is only implemented on AMD64
it is a null op on all other architectures
thus the MAP_BASE setting below is the only thing
ensuring low addresses on aarch64 for example */
#define MAP_EXTRA_FLAGS (MAP_32BIT)
#ifdef HAVE_MMAP_VM
#if (defined(__linux__) && defined(__i386__)) || defined(__FreeBSD__) || HAVE_LINKER_SCRIPT
/* Force a reasonnable address below 0x80000000 on x86 so that we
don't get addresses above when the program is run on AMD64.
NOTE: this is empirically determined on Linux/x86. */
#define MAP_BASE 0x10000000
#elif DIRECT_ADDRESSING
/* linux does not implement any useful fallback behavior
such as allocating the next available address
and the first 4k-64k of address space is marked unavailable
for security reasons (see https://wiki.debian.org/mmap_min_addr)
so we must start requesting after the first page
or we get a high 64bit address that will crash direct addressing
leaving NULL unmapped is a good idea anyway for debugging reasons */
#define MAP_BASE 0x00010000
#else
#define MAP_BASE 0x00000000
#endif
static char * next_address = (char *)MAP_BASE;
#ifdef HAVE_MMAP_ANON
#define map_flags (MAP_ANON | MAP_EXTRA_FLAGS)
#define zero_fd -1
#else
#ifdef HAVE_MMAP_ANONYMOUS
#define map_flags (MAP_ANONYMOUS | MAP_EXTRA_FLAGS)
#define zero_fd -1
#else
#define map_flags (MAP_EXTRA_FLAGS)
static int zero_fd = -1;
#endif
#endif
#endif
/* Translate generic VM map flags to host values. */
#ifdef HAVE_MMAP_VM
static int translate_map_flags(int vm_flags)
{
int flags = 0;
if (vm_flags & VM_MAP_SHARED)
flags |= MAP_SHARED;
if (vm_flags & VM_MAP_PRIVATE)
flags |= MAP_PRIVATE;
if (vm_flags & VM_MAP_FIXED)
flags |= MAP_FIXED;
if (vm_flags & VM_MAP_32BIT)
flags |= FORCE_MAP_32BIT;
return flags;
}
#endif
/* Align ADDR and SIZE to 64K boundaries. */
#ifdef HAVE_WIN32_VM
static inline LPVOID align_addr_segment(LPVOID addr)
{
return LPVOID(vm_uintptr_t(addr) & ~vm_uintptr_t(0xFFFF));
}
static inline DWORD align_size_segment(LPVOID addr, DWORD size)
{
return size + ((vm_uintptr_t)addr - (vm_uintptr_t)align_addr_segment(addr));
}
#endif
/* Translate generic VM prot flags to host values. */
#ifdef HAVE_WIN32_VM
static int translate_prot_flags(int prot_flags)
{
int prot = PAGE_READWRITE;
if (prot_flags == (VM_PAGE_EXECUTE | VM_PAGE_READ | VM_PAGE_WRITE))
prot = PAGE_EXECUTE_READWRITE;
else if (prot_flags == (VM_PAGE_EXECUTE | VM_PAGE_READ))
prot = PAGE_EXECUTE_READ;
else if (prot_flags == (VM_PAGE_READ | VM_PAGE_WRITE))
prot = PAGE_READWRITE;
else if (prot_flags == VM_PAGE_READ)
prot = PAGE_READONLY;
else if (prot_flags == 0)
prot = PAGE_NOACCESS;
return prot;
}
#endif
/* Translate Mach return codes to POSIX errno values. */
#ifdef HAVE_MACH_VM
static int vm_error(kern_return_t ret_code)
{
switch (ret_code) {
case KERN_SUCCESS:
return 0;
case KERN_INVALID_ADDRESS:
case KERN_NO_SPACE:
return ENOMEM;
case KERN_PROTECTION_FAILURE:
return EACCES;
default:
return EINVAL;
}
}
#endif
/* Initialize the VM system. Returns 0 if successful, -1 for errors. */
int vm_init(void)
{
#ifdef HAVE_MMAP_VM
#ifndef zero_fd
zero_fd = open("/dev/zero", O_RDWR);
if (zero_fd < 0)
return -1;
#endif
#endif
// On 10.4 and earlier, reset CrashReporter's task signal handler to
// avoid having it show up for signals that get handled.
#if defined(__APPLE__) && defined(__MACH__)
struct utsname info;
if (!uname(&info) && atoi(info.release) <= 8) {
task_set_exception_ports(mach_task_self(),
EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC,
MACH_PORT_NULL,
EXCEPTION_STATE_IDENTITY,
MACHINE_THREAD_STATE);
}
#endif
return 0;
}
/* Deallocate all internal data used to wrap virtual memory allocators. */
void vm_exit(void)
{
#ifdef HAVE_MMAP_VM
#ifndef zero_fd
if (zero_fd != -1) {
close(zero_fd);
zero_fd = -1;
}
#endif
#endif
}
static void *reserved_buf;
static const size_t RESERVED_SIZE = 64 * 1024 * 1024; // for 5K Retina
void *vm_acquire_reserved(size_t size) {
return reserved_buf && size <= RESERVED_SIZE ? reserved_buf : VM_MAP_FAILED;
}
int vm_init_reserved(void *hostAddress) {
int result = vm_acquire_fixed(hostAddress, RESERVED_SIZE);
if (result >= 0)
reserved_buf = hostAddress;
return result;
}
/* Allocate zero-filled memory of SIZE bytes. The mapping is private
and default protection bits are read / write. The return value
is the actual mapping address chosen or VM_MAP_FAILED for errors. */
void * vm_acquire(size_t size, int options)
{
void * addr;
errno = 0;
// VM_MAP_FIXED are to be used with vm_acquire_fixed() only
if (options & VM_MAP_FIXED)
return VM_MAP_FAILED;
#ifndef HAVE_VM_WRITE_WATCH
if (options & VM_MAP_WRITE_WATCH)
return VM_MAP_FAILED;
#endif
#if defined(HAVE_MACH_VM)
// vm_allocate() returns a zero-filled memory region
kern_return_t ret_code = vm_allocate(mach_task_self(), (vm_address_t *)&addr, reserved_buf ? size : size + RESERVED_SIZE, TRUE);
if (ret_code != KERN_SUCCESS) {
errno = vm_error(ret_code);
return VM_MAP_FAILED;
}
if (!reserved_buf)
reserved_buf = (char *)addr + size;
#elif defined(HAVE_MMAP_VM)
int fd = zero_fd;
int the_map_flags = translate_map_flags(options) | map_flags;
if ((addr = mmap((caddr_t)next_address, size, VM_PAGE_DEFAULT, the_map_flags, fd, 0)) == (void *)MAP_FAILED)
return VM_MAP_FAILED;
#if DIRECT_ADDRESSING
// If MAP_32BIT and MAP_BASE fail to ensure
// a 32-bit address crash now instead of later.
// FIXME: make everything 64-bit clean and tear this all out.
if(sizeof(void *) > 4 && (options & VM_MAP_32BIT))
assert((size_t)addr<0xffffffffL);
#endif
next_address = (char *)addr + size;
#elif defined(HAVE_WIN32_VM)
int alloc_type = MEM_RESERVE | MEM_COMMIT;
if (options & VM_MAP_WRITE_WATCH)
alloc_type |= MEM_WRITE_WATCH;
if ((addr = VirtualAlloc(NULL, size, alloc_type, PAGE_EXECUTE_READWRITE)) == NULL)
return VM_MAP_FAILED;
#else
if ((addr = calloc(size, 1)) == 0)
return VM_MAP_FAILED;
// Omit changes for protections because they are not supported in this mode
return addr;
#endif
// Explicitely protect the newly mapped region here because on some systems,
// say MacOS X, mmap() doesn't honour the requested protection flags.
if (vm_protect(addr, size, VM_PAGE_DEFAULT) != 0)
return VM_MAP_FAILED;
return addr;
}
/* Allocate zero-filled memory at exactly ADDR (which must be page-aligned).
Retuns 0 if successful, -1 on errors. */
int vm_acquire_fixed(void * addr, size_t size, int options)
{
errno = 0;
// Fixed mappings are required to be private
if (options & VM_MAP_SHARED)
return -1;
#ifndef HAVE_VM_WRITE_WATCH
if (options & VM_MAP_WRITE_WATCH)
return -1;
#endif
#if defined(HAVE_MACH_VM)
// vm_allocate() returns a zero-filled memory region
kern_return_t ret_code = vm_allocate(mach_task_self(), (vm_address_t *)&addr, size, 0);
if (ret_code != KERN_SUCCESS) {
errno = vm_error(ret_code);
return -1;
}
#elif defined(HAVE_MMAP_VM)
int fd = zero_fd;
int the_map_flags = translate_map_flags(options) | map_flags | MAP_FIXED;
if (mmap((caddr_t)addr, size, VM_PAGE_DEFAULT, the_map_flags, fd, 0) == (void *)MAP_FAILED)
return -1;
#elif defined(HAVE_WIN32_VM)
// Windows cannot allocate Low Memory
if (addr == NULL)
return -1;
int alloc_type = MEM_RESERVE | MEM_COMMIT;
if (options & VM_MAP_WRITE_WATCH)
alloc_type |= MEM_WRITE_WATCH;
// Allocate a possibly offset region to align on 64K boundaries
LPVOID req_addr = align_addr_segment(addr);
DWORD req_size = align_size_segment(addr, size);
LPVOID ret_addr = VirtualAlloc(req_addr, req_size, alloc_type, PAGE_EXECUTE_READWRITE);
if (ret_addr != req_addr)
return -1;
#else
// Unsupported
return -1;
#endif
// Explicitely protect the newly mapped region here because on some systems,
// say MacOS X, mmap() doesn't honour the requested protection flags.
if (vm_protect(addr, size, VM_PAGE_DEFAULT) != 0)
return -1;
return 0;
}
/* Deallocate any mapping for the region starting at ADDR and extending
LEN bytes. Returns 0 if successful, -1 on errors. */
int vm_release(void * addr, size_t size)
{
// Safety check: don't try to release memory that was not allocated
if (addr == VM_MAP_FAILED)
return 0;
#ifdef HAVE_MACH_VM
if (vm_deallocate(mach_task_self(), (vm_address_t)addr, size) != KERN_SUCCESS)
return -1;
#else
#ifdef HAVE_MMAP_VM
if (munmap((caddr_t)addr, size) != 0)
return -1;
#else
#ifdef HAVE_WIN32_VM
if (VirtualFree(align_addr_segment(addr), 0, MEM_RELEASE) == 0)
return -1;
#else
free(addr);
#endif
#endif
#endif
return 0;
}
/* Change the memory protection of the region starting at ADDR and
extending LEN bytes to PROT. Returns 0 if successful, -1 for errors. */
int vm_protect(void * addr, size_t size, int prot)
{
#ifdef HAVE_MACH_VM
int ret_code = vm_protect(mach_task_self(), (vm_address_t)addr, size, 0, prot);
return ret_code == KERN_SUCCESS ? 0 : -1;
#else
#ifdef HAVE_MMAP_VM
int ret_code = mprotect((caddr_t)addr, size, prot);
return ret_code == 0 ? 0 : -1;
#else
#ifdef HAVE_WIN32_VM
DWORD old_prot;
int ret_code = VirtualProtect(addr, size, translate_prot_flags(prot), &old_prot);
return ret_code != 0 ? 0 : -1;
#else
// Unsupported
return -1;
#endif
#endif
#endif
}
/* Return the addresses of the pages that got modified in the
specified range [ ADDR, ADDR + SIZE [ since the last reset of the watch
bits. Returns 0 if successful, -1 for errors. */
int vm_get_write_watch(void * addr, size_t size,
void ** pages, unsigned int * n_pages,
int options)
{
#ifdef HAVE_VM_WRITE_WATCH
#ifdef HAVE_WIN32_VM
DWORD flags = 0;
if (options & VM_WRITE_WATCH_RESET)
flags |= WRITE_WATCH_FLAG_RESET;
ULONG page_size;
ULONG_PTR count = *n_pages;
int ret_code = GetWriteWatch(flags, addr, size, pages, &count, &page_size);
if (ret_code != 0)
return -1;
*n_pages = count;
return 0;
#endif
#endif
// Unsupported
return -1;
}
/* Reset the write-tracking state for the specified range [ ADDR, ADDR
+ SIZE [. Returns 0 if successful, -1 for errors. */
int vm_reset_write_watch(void * addr, size_t size)
{
#ifdef HAVE_VM_WRITE_WATCH
#ifdef HAVE_WIN32_VM
int ret_code = ResetWriteWatch(addr, size);
return ret_code == 0 ? 0 : -1;
#endif
#endif
// Unsupported
return -1;
}
/* Returns the size of a page. */
int vm_get_page_size(void)
{
#ifdef HAVE_WIN32_VM
static vm_uintptr_t page_size = 0;
if (page_size == 0) {
SYSTEM_INFO si;
GetSystemInfo(&si);
page_size = si.dwAllocationGranularity;
}
return page_size;
#else
return getpagesize();
#endif
}
#ifdef CONFIGURE_TEST_VM_WRITE_WATCH
int main(void)
{
int i, j;
vm_init();
vm_uintptr_t page_size = vm_get_page_size();
char *area;
const int n_pages = 7;
const int area_size = n_pages * page_size;
const int map_options = VM_MAP_DEFAULT | VM_MAP_WRITE_WATCH;
if ((area = (char *)vm_acquire(area_size, map_options)) == VM_MAP_FAILED)
return 1;
unsigned int n_modified_pages_expected = 0;
static const int touch_page[n_pages] = { 0, 1, 1, 0, 1, 0, 1 };
for (i = 0; i < n_pages; i++) {
if (touch_page[i]) {
area[i * page_size] = 1;
++n_modified_pages_expected;
}
}
char *modified_pages[n_pages];
unsigned int n_modified_pages = n_pages;
if (vm_get_write_watch(area, area_size, (void **)modified_pages, &n_modified_pages) < 0)
return 2;
if (n_modified_pages != n_modified_pages_expected)
return 3;
for (i = 0, j = 0; i < n_pages; i++) {
char v = area[i * page_size];
if ((touch_page[i] && !v) || (!touch_page[i] && v))
return 4;
if (!touch_page[i])
continue;
if (modified_pages[j] != (area + i * page_size))
return 5;
++j;
}
vm_release(area, area_size);
return 0;
}
#endif
#ifdef CONFIGURE_TEST_VM_MAP
#include <stdlib.h>
#include <signal.h>
static void fault_handler(int sig)
{
exit(1);
}
/* Tests covered here:
- TEST_VM_PROT_* program slices actually succeeds when a crash occurs
- TEST_VM_MAP_ANON* program slices succeeds when it could be compiled
*/
int main(void)
{
vm_init();
signal(SIGSEGV, fault_handler);
#ifdef SIGBUS
signal(SIGBUS, fault_handler);
#endif
#define page_align(address) ((char *)((vm_uintptr_t)(address) & -page_size))
vm_uintptr_t page_size = vm_get_page_size();
const int area_size = 6 * page_size;
volatile char * area = (volatile char *) vm_acquire(area_size);
volatile char * fault_address = area + (page_size * 7) / 2;
#if defined(TEST_VM_MMAP_ANON) || defined(TEST_VM_MMAP_ANONYMOUS)
if (area == VM_MAP_FAILED)
return 1;
if (vm_release((char *)area, area_size) < 0)
return 1;
return 0;
#endif
#if defined(TEST_VM_PROT_NONE_READ) || defined(TEST_VM_PROT_NONE_WRITE)
if (area == VM_MAP_FAILED)
return 0;
if (vm_protect(page_align(fault_address), page_size, VM_PAGE_NOACCESS) < 0)
return 0;
#endif
#if defined(TEST_VM_PROT_RDWR_WRITE)
if (area == VM_MAP_FAILED)
return 1;
if (vm_protect(page_align(fault_address), page_size, VM_PAGE_READ) < 0)
return 1;
if (vm_protect(page_align(fault_address), page_size, VM_PAGE_READ | VM_PAGE_WRITE) < 0)
return 1;
#endif
#if defined(TEST_VM_PROT_READ_WRITE)
if (vm_protect(page_align(fault_address), page_size, VM_PAGE_READ) < 0)
return 0;
#endif
#if defined(TEST_VM_PROT_NONE_READ)
// this should cause a core dump
char foo = *fault_address;
return 0;
#endif
#if defined(TEST_VM_PROT_NONE_WRITE) || defined(TEST_VM_PROT_READ_WRITE)
// this should cause a core dump
*fault_address = 'z';
return 0;
#endif
#if defined(TEST_VM_PROT_RDWR_WRITE)
// this should not cause a core dump
*fault_address = 'z';
return 0;
#endif
}
#endif

View File

@ -1 +0,0 @@
../../../BasiliskII/src/CrossPlatform/vm_alloc.h

View File

@ -0,0 +1,138 @@
/*
* vm_alloc.h - Wrapper to various virtual memory allocation schemes
* (supports mmap, vm_allocate or fallbacks to malloc)
*
* Basilisk II (C) 1997-2008 Christian Bauer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef VM_ALLOC_H
#define VM_ALLOC_H
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef HAVE_MACH_VM
extern "C" {
#include <mach/mach.h>
#include <mach/task.h>
}
#endif
#include <cassert>
/* Return value of `vm_acquire' in case of an error. */
#ifdef HAVE_MACH_VM
#define VM_MAP_FAILED ((void *)-1)
#else
#ifdef HAVE_MMAP_VM
#define VM_MAP_FAILED ((void *)-1)
#else
#define VM_MAP_FAILED 0
#endif
#endif
/* Option to vm_get_write_watch() to reset the write-tracking state
once it was retrieved. Otherwise, you have to manually call
vm_reset_write_watch() and potentially lose some info. */
#define VM_WRITE_WATCH_RESET 0x01
/* Mapping options. */
#define VM_MAP_SHARED 0x01
#define VM_MAP_PRIVATE 0x02
#define VM_MAP_FIXED 0x04
#define VM_MAP_32BIT 0x08
#define VM_MAP_WRITE_WATCH 0x10
/* Default mapping options. */
#define VM_MAP_DEFAULT (VM_MAP_PRIVATE)
/* Protection bits. */
#ifdef HAVE_MACH_VM
#define VM_PAGE_NOACCESS VM_PROT_NONE
#define VM_PAGE_READ VM_PROT_READ
#define VM_PAGE_WRITE VM_PROT_WRITE
#define VM_PAGE_EXECUTE VM_PROT_EXECUTE
#else
#ifdef HAVE_MMAP_VM
#define VM_PAGE_NOACCESS PROT_NONE
#define VM_PAGE_READ PROT_READ
#define VM_PAGE_WRITE PROT_WRITE
#define VM_PAGE_EXECUTE PROT_EXEC
#else
#define VM_PAGE_NOACCESS 0x0
#define VM_PAGE_READ 0x1
#define VM_PAGE_WRITE 0x2
#define VM_PAGE_EXECUTE 0x4
#endif
#endif
/* Default protection bits. */
#define VM_PAGE_DEFAULT (VM_PAGE_READ | VM_PAGE_WRITE)
/* Initialize the VM system. Returns 0 if successful, -1 for errors. */
extern int vm_init(void);
/* Deallocate all internal data used to wrap virtual memory allocators. */
extern void vm_exit(void);
/* Allocate zero-filled memory of SIZE bytes. The mapping is private
and default protection bits are read / write. The return value
is the actual mapping address chosen or VM_MAP_FAILED for errors. */
extern void * vm_acquire(size_t size, int options = VM_MAP_DEFAULT);
extern void * vm_acquire_reserved(size_t size);
/* Allocate zero-filled memory at exactly ADDR (which must be page-aligned).
Returns 0 if successful, -1 on errors. */
extern int vm_acquire_fixed(void * addr, size_t size, int options = VM_MAP_DEFAULT);
/* Deallocate any mapping for the region starting at ADDR and extending
LEN bytes. Returns 0 if successful, -1 on errors. */
extern int vm_release(void * addr, size_t size);
/* Change the memory protection of the region starting at ADDR and
extending SIZE bytes to PROT. Returns 0 if successful, -1 for errors. */
extern int vm_protect(void * addr, size_t size, int prot);
/* Return the addresses of the pages that got modified since the last
reset of the write-tracking state for the specified range [ ADDR,
ADDR + SIZE [. Returns 0 if successful, -1 for errors. */
extern int vm_get_write_watch(void * addr, size_t size,
void ** pages, unsigned int * n_pages,
int options = 0);
/* Reset the write-tracking state for the specified range [ ADDR, ADDR
+ SIZE [. Returns 0 if successful, -1 for errors. */
extern int vm_reset_write_watch(void * addr, size_t size);
/* Returns the size of a page. */
extern int vm_get_page_size(void);
#endif /* VM_ALLOC_H */

View File

@ -1 +0,0 @@
../../BasiliskII/src/SDL

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff