From 8d733ed691c283ba0aa3da8a4790676e6e2e56f0 Mon Sep 17 00:00:00 2001 From: gbeauche <> Date: Sat, 7 Jul 2001 09:14:47 +0000 Subject: [PATCH] - added video_vosf_init()/video_vosf_exit() for initialization and destruction of the internal structures used for the VOSF system - use vm_acquire()/vm_release() for VOSF buffers and hope the_buffer is allocated above RAM address space (temporary workaround for 64-bit addressing systems) - don't free() screen buffers in driver_base dtor - don't free() memory mapped buffers in driver_base dtor --- BasiliskII/src/Unix/video_vosf.h | 149 +++++++++++++++++-------------- BasiliskII/src/Unix/video_x.cpp | 94 ++++++++++++++----- 2 files changed, 150 insertions(+), 93 deletions(-) diff --git a/BasiliskII/src/Unix/video_vosf.h b/BasiliskII/src/Unix/video_vosf.h index c61d65f7..bfaf775d 100644 --- a/BasiliskII/src/Unix/video_vosf.h +++ b/BasiliskII/src/Unix/video_vosf.h @@ -35,19 +35,16 @@ // Variables for Video on SEGV support static uint8 *the_host_buffer; // Host frame buffer in VOSF mode -static uint32 the_buffer_size; // Size of allocated the_buffer struct ScreenPageInfo { int top, bottom; // Mapping between this virtual page and Mac scanlines }; struct ScreenInfo { - uintptr memBase; // Real start address uintptr memStart; // Start address aligned to page boundary - uintptr memEnd; // Address of one-past-the-end of the screen uint32 memLength; // Length of the memory addressed by the screen pages - uint32 pageSize; // Size of a page + uintptr pageSize; // Size of a page int pageBits; // Shift count to get the page number uint32 pageCount; // Number of pages allocated to the screen @@ -161,104 +158,118 @@ static uint32 page_extend(uint32 size) /* - * Initialize mainBuffer structure + * Initialize the VOSF system (mainBuffer structure, SIGSEGV handler) */ -static bool video_init_buffer(void) +static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction); + +static bool video_vosf_init(void) { - if (use_vosf) { - const uint32 page_size = getpagesize(); - const uint32 page_mask = page_size - 1; + const uintptr page_size = getpagesize(); + 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 *) vm_acquire(mainBuffer.pageCount + 2); + if (mainBuffer.dirtyPages == VM_MAP_FAILED) + return false; - mainBuffer.memBase = (uintptr) the_buffer; - // Round up frame buffer base to page boundary - mainBuffer.memStart = (uintptr)((((unsigned long) the_buffer) + page_mask) & ~page_mask); - mainBuffer.memLength = the_buffer_size; - mainBuffer.memEnd = mainBuffer.memStart + mainBuffer.memLength; + 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 *) vm_acquire(mainBuffer.pageCount * sizeof(ScreenPageInfo)); + if (mainBuffer.pageInfo == VM_MAP_FAILED) + return false; + + uint32 a = 0; + for (int i = 0; i < mainBuffer.pageCount; i++) { + int y1 = a / VideoMonitor.mode.bytes_per_row; + if (y1 >= VideoMonitor.mode.y) + y1 = VideoMonitor.mode.y - 1; - mainBuffer.pageSize = page_size; - mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize; - mainBuffer.pageBits = log_base_2(mainBuffer.pageSize); + int y2 = (a + mainBuffer.pageSize) / VideoMonitor.mode.bytes_per_row; + if (y2 >= VideoMonitor.mode.y) + y2 = VideoMonitor.mode.y - 1; - if (mainBuffer.dirtyPages) { - free(mainBuffer.dirtyPages); - mainBuffer.dirtyPages = NULL; - } + mainBuffer.pageInfo[i].top = y1; + mainBuffer.pageInfo[i].bottom = y2; - mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2); - - if (mainBuffer.pageInfo) { - free(mainBuffer.pageInfo); - mainBuffer.pageInfo = NULL; - } - - mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo)); - - if ((mainBuffer.dirtyPages == NULL) || (mainBuffer.pageInfo == NULL)) - return false; - - mainBuffer.dirty = false; - - PFLAG_CLEAR_ALL; - // 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 - PFLAG_CLEAR(mainBuffer.pageCount); - PFLAG_SET(mainBuffer.pageCount+1); - - uint32 a = 0; - for (int i = 0; i < mainBuffer.pageCount; i++) { - int y1 = a / VideoMonitor.mode.bytes_per_row; - if (y1 >= VideoMonitor.mode.y) - y1 = VideoMonitor.mode.y - 1; - - int y2 = (a + mainBuffer.pageSize) / VideoMonitor.mode.bytes_per_row; - if (y2 >= VideoMonitor.mode.y) - y2 = VideoMonitor.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; + 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; + + // Initialize the handler for SIGSEGV + if (!sigsegv_install_handler(screen_fault_handler)) + 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 != VM_MAP_FAILED) { + vm_release(mainBuffer.pageInfo, mainBuffer.pageCount * sizeof(ScreenPageInfo)); + mainBuffer.pageInfo = (ScreenPageInfo *) VM_MAP_FAILED; + } + if (mainBuffer.dirtyPages != VM_MAP_FAILED) { + vm_release(mainBuffer.dirtyPages, mainBuffer.pageCount + 2); + mainBuffer.dirtyPages = (char *) VM_MAP_FAILED; + } +} + + /* * Screen fault handler */ static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction) { -// D(bug("screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", fault_address, fault_instruction)); +// D(bug("screen_fault_handler: ADDR=%p from IP=%p\n", fault_address, fault_instruction)); const uintptr addr = (uintptr)fault_address; /* 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 ((addr >= mainBuffer.memStart) && (addr < mainBuffer.memEnd)) { - const int page = (addr - mainBuffer.memStart) >> mainBuffer.pageBits; - caddr_t page_ad = (caddr_t)(addr & -mainBuffer.pageSize); + if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) { + const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits; LOCK_VOSF; PFLAG_SET(page); - vm_protect((char *)page_ad, mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE); + vm_protect((char *)(addr & -mainBuffer.pageSize), 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 */ - fprintf(stderr, "do_handle_screen_fault: unhandled address 0x%08X", addr); + fprintf(stderr, "do_handle_screen_fault: unhandled address %p", fault_address); if (fault_instruction != SIGSEGV_INVALID_PC) - fprintf(stderr, " [IP=0x%08X]", fault_instruction); + fprintf(stderr, " [IP=%p]", fault_instruction); fprintf(stderr, "\n"); #if EMULATED_68K uaecptr nextpc; diff --git a/BasiliskII/src/Unix/video_x.cpp b/BasiliskII/src/Unix/video_x.cpp index 9efdb911..d6b29388 100644 --- a/BasiliskII/src/Unix/video_x.cpp +++ b/BasiliskII/src/Unix/video_x.cpp @@ -93,6 +93,7 @@ static int display_type = DISPLAY_WINDOW; // See enum above static bool local_X11; // Flag: X server running on local machine? static uint8 *the_buffer = NULL; // Mac frame buffer (where MacOS draws into) static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer (for refreshed modes) +static uint32 the_buffer_size; // Size of allocated the_buffer static bool redraw_thread_active = false; // Flag: Redraw thread installed #ifdef HAVE_PTHREADS @@ -509,12 +510,12 @@ driver_base::~driver_base() free(the_host_buffer); the_host_buffer = NULL; } - if (the_buffer != (uint8 *)VM_MAP_FAILED) { - vm_release(the_buffer, the_buffer_size); + if (the_buffer) { + free(the_buffer); the_buffer = NULL; } - if (the_buffer_copy != (uint8 *)VM_MAP_FAILED) { - vm_release(the_buffer_copy, the_buffer_size); + if (the_buffer_copy) { + free(the_buffer_copy); the_buffer_copy = NULL; } } @@ -629,6 +630,7 @@ driver_window::driver_window(const video_mode &mode) } #ifdef ENABLE_VOSF + use_vosf = true; // Allocate memory for frame buffer (SIZE is extended to page-boundary) the_host_buffer = the_buffer_copy; the_buffer_size = page_extend((aligned_height + 2) * img->bytes_per_line); @@ -682,6 +684,19 @@ driver_window::~driver_window() the_buffer_copy = NULL; // don't free() in driver_base dtor #endif } +#ifdef ENABLE_VOSF + if (use_vosf) { + // don't free() memory mapped buffers in driver_base dtor + if (the_buffer != VM_MAP_FAILED) { + vm_release(the_buffer, the_buffer_size); + the_buffer = NULL; + } + if (the_buffer_copy != VM_MAP_FAILED) { + vm_release(the_buffer_copy, the_buffer_size); + the_buffer_copy = NULL; + } + } +#endif if (img) XDestroyImage(img); if (have_shm) { @@ -1001,8 +1016,9 @@ driver_fbdev::driver_fbdev(const video_mode &mode) int bytes_per_row = TrivialBytesPerRow(mode.x, mode.depth); // Map frame buffer - if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) { - if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) { + the_buffer_size = height * bytes_per_row; + if ((the_buffer = (uint8 *) mmap(NULL, the_buffer_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) { + if ((the_buffer = (uint8 *) mmap(NULL, the_buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) { char str[256]; sprintf(str, GetString(STR_FBDEV_MMAP_ERR), strerror(errno)); ErrorAlert(str); @@ -1041,6 +1057,30 @@ driver_fbdev::driver_fbdev(const video_mode &mode) // Close display driver_fbdev::~driver_fbdev() { + if (!use_vosf) { + if (the_buffer != MAP_FAILED) { + // don't free() the screen buffer in driver_base dtor + munmap(the_buffer, the_buffer_size); + the_buffer = NULL; + } + } +#ifdef ENABLE_VOSF + else { + if (the_host_buffer != MAP_FAILED) { + // don't free() the screen buffer in driver_base dtor + munmap(the_host_buffer, the_buffer_size); + the_host_buffer = NULL; + } + if (the_buffer_copy != VM_MAP_FAILED) { + vm_release(the_buffer_copy, the_buffer_size); + the_buffer_copy = NULL; + } + if (the_buffer != VM_MAP_FAILED) { + vm_release(the_buffer, the_buffer_size); + the_buffer = NULL; + } + } +#endif } #endif @@ -1126,7 +1166,7 @@ driver_xf86dga::driver_xf86dga(const video_mode &mode) // Init blitting routines int bytes_per_row = TrivialBytesPerRow((v_width + 7) & ~7, mode.depth); -#ifdef VIDEO_VOSF +#if VIDEO_VOSF #if REAL_ADDRESSING || DIRECT_ADDRESSING // Screen_blitter_init() returns TRUE if VOSF is mandatory // i.e. the framebuffer update function is not Blit_Copy_Raw @@ -1157,6 +1197,25 @@ driver_xf86dga::driver_xf86dga(const video_mode &mode) driver_xf86dga::~driver_xf86dga() { XF86DGADirectVideo(x_display, screen, 0); + if (!use_vosf) { + // don't free() the screen buffer in driver_base dtor + the_buffer = NULL; + } +#ifdef ENABLE_VOSF + else { + // don't free() the screen buffer in driver_base dtor + the_host_buffer = NULL; + + if (the_buffer_copy != VM_MAP_FAILED) { + vm_release(the_buffer_copy, the_buffer_size); + the_buffer_copy = NULL; + } + if (the_buffer != VM_MAP_FAILED) { + vm_release(the_buffer, the_buffer_size); + the_buffer = NULL; + } + } +#endif #ifdef ENABLE_XF86_VIDMODE if (has_vidmode) XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]); @@ -1343,17 +1402,11 @@ static bool video_open(const video_mode &mode) #ifdef ENABLE_VOSF if (use_vosf) { - // Initialize the mainBuffer structure - if (!video_init_buffer()) { + // Initialize the VOSF system + if (!video_vosf_init()) { ErrorAlert(STR_VOSF_INIT_ERR); return false; } - - // Initialize the handler for SIGSEGV - if (!sigsegv_install_handler(screen_fault_handler)) { - ErrorAlert("Could not initialize Video on SEGV signals"); - return false; - } } #endif @@ -1569,16 +1622,9 @@ static void video_close(void) XSync(x_display, false); #ifdef ENABLE_VOSF - // Deinitialize VOSF if (use_vosf) { - if (mainBuffer.pageInfo) { - free(mainBuffer.pageInfo); - mainBuffer.pageInfo = NULL; - } - if (mainBuffer.dirtyPages) { - free(mainBuffer.dirtyPages); - mainBuffer.dirtyPages = NULL; - } + // Deinitialize VOSF + video_vosf_exit(); } #endif