diff --git a/BasiliskII/src/Unix/main_unix.cpp b/BasiliskII/src/Unix/main_unix.cpp index f380fc55..b9bc8f1d 100644 --- a/BasiliskII/src/Unix/main_unix.cpp +++ b/BasiliskII/src/Unix/main_unix.cpp @@ -209,6 +209,32 @@ char *strdup(const char *s) } +/* + * SIGSEGV handler + */ + +static sigsegv_return_t sigsegv_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction) +{ +#if ENABLE_VOSF + // Handle screen fault + extern bool Screen_fault_handler(sigsegv_address_t, sigsegv_address_t); + if (Screen_fault_handler(fault_address, fault_instruction)) + return SIGSEGV_RETURN_SUCCESS; +#endif + +#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION + // Ignore writes to ROM + if (((uintptr)fault_address - (uintptr)ROMBaseHost) < ROMSize) + return SIGSEGV_RETURN_SKIP_INSTRUCTION; + + // Ignore all other faults, if requested + if (PrefsFindBool("ignoresegv")) + return SIGSEGV_RETURN_SKIP_INSTRUCTION; +#endif + + return SIGSEGV_RETURN_FAILURE; +} + /* * Dump state when everything went wrong after a SEGV */ @@ -357,12 +383,10 @@ int main(int argc, char **argv) if (!PrefsEditor()) QuitEmulator(); - // Register request to ignore all segmentation faults -#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION - if (PrefsFindBool("ignoresegv")) - sigsegv_add_ignore_range(0, ~(0UL), SIGSEGV_TRANSFER_LOAD | SIGSEGV_TRANSFER_STORE); -#endif - + // Install the handler for SIGSEGV + if (!sigsegv_install_handler(sigsegv_handler)) + return false; + // Register dump state function when we got mad after a segfault sigsegv_set_dump_state(sigsegv_dump_state); diff --git a/BasiliskII/src/Unix/sigsegv.cpp b/BasiliskII/src/Unix/sigsegv.cpp index 7209ba9a..d3629af4 100644 --- a/BasiliskII/src/Unix/sigsegv.cpp +++ b/BasiliskII/src/Unix/sigsegv.cpp @@ -45,16 +45,6 @@ using std::list; // Type of the system signal handler typedef RETSIGTYPE (*signal_handler)(int); -// Ignore range chain -struct ignore_range_t { - sigsegv_address_t start; - unsigned long length; - int transfer_type; -}; - -typedef list ignore_range_list_t; -ignore_range_list_t sigsegv_ignore_ranges; - // User's SIGSEGV handler static sigsegv_fault_handler_t sigsegv_fault_handler = 0; @@ -64,16 +54,6 @@ static sigsegv_state_dumper_t sigsegv_state_dumper = 0; // Actual SIGSEGV handler installer static bool sigsegv_do_install_handler(int sig); -// Find ignore range matching address -static inline ignore_range_list_t::iterator sigsegv_find_ignore_range(sigsegv_address_t address) -{ - ignore_range_list_t::iterator it; - for (it = sigsegv_ignore_ranges.begin(); it != sigsegv_ignore_ranges.end(); it++) - if (address >= it->start && address < it->start + it->length) - break; - return it; -} - /* * Instruction decoding aids @@ -610,12 +590,6 @@ static bool powerpc_skip_instruction(unsigned int * nip_p, unsigned int * regs) return false; } - ignore_range_list_t::iterator it = sigsegv_find_ignore_range((sigsegv_address_t)instr.addr); - if (it == sigsegv_ignore_ranges.end() || ((it->transfer_type & instr.transfer_type) != instr.transfer_type)) { - // Address doesn't fall into ignore ranges list, let it crash. - return false; - } - #if DEBUG printf("%08x: %s %s access", *nip_p, instr.transfer_size == SIZE_BYTE ? "byte" : instr.transfer_size == SIZE_WORD ? "word" : "long", @@ -661,22 +635,24 @@ static void sigsegv_handler(SIGSEGV_FAULT_HANDLER_ARGLIST) bool fault_recovered = false; // Call user's handler and reinstall the global handler, if required - if (sigsegv_fault_handler(fault_address, fault_instruction)) { + switch (sigsegv_fault_handler(fault_address, fault_instruction)) { + case SIGSEGV_RETURN_SUCCESS: #if (defined(HAVE_SIGACTION) ? defined(SIGACTION_NEED_REINSTALL) : defined(SIGNAL_NEED_REINSTALL)) sigsegv_do_install_handler(sig); #endif fault_recovered = true; - } + break; #if HAVE_SIGSEGV_SKIP_INSTRUCTION - else if (sigsegv_ignore_ranges.size() > 0) { + case SIGSEGV_RETURN_SKIP_INSTRUCTION: // Call the instruction skipper with the register file available if (SIGSEGV_SKIP_INSTRUCTION(SIGSEGV_REGISTER_FILE)) fault_recovered = true; - } + break; #endif + } if (!fault_recovered) { - // FAIL: reinstall default handler for "safe" crash + // Failure: reinstall default handler for "safe" crash #define FAULT_HANDLER(sig) signal(sig, SIG_DFL); SIGSEGV_ALL_SIGNALS #undef FAULT_HANDLER @@ -760,43 +736,6 @@ void sigsegv_deinstall_handler(void) } -/* - * Add SIGSEGV ignore range - */ - -void sigsegv_add_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type) -{ - ignore_range_t ignore_range; - ignore_range.start = address; - ignore_range.length = length; - ignore_range.transfer_type = transfer_type; - sigsegv_ignore_ranges.push_front(ignore_range); -} - - -/* - * Remove SIGSEGV ignore range. Range must match installed one, otherwise FALSE is returned. - */ - -bool sigsegv_remove_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type) -{ - ignore_range_list_t::iterator it; - for (it = sigsegv_ignore_ranges.begin(); it != sigsegv_ignore_ranges.end(); it++) - if (it->start == address && it->length == length && ((it->transfer_type & transfer_type) == transfer_type)) - break; - - if (it != sigsegv_ignore_ranges.end()) { - if (it->transfer_type != transfer_type) - it->transfer_type &= ~transfer_type; - else - sigsegv_ignore_ranges.erase(it); - return true; - } - - return false; -} - - /* * Set callback function when we cannot handle the fault */ @@ -822,20 +761,22 @@ static int page_size; static volatile char * page = 0; static volatile int handler_called = 0; -static bool sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) +static sigsegv_return_t sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) { handler_called++; if ((fault_address - 123) != page) exit(1); if (vm_protect((char *)((unsigned long)fault_address & -page_size), page_size, VM_PAGE_READ | VM_PAGE_WRITE) != 0) exit(1); - return true; + return SIGSEGV_RETURN_SUCCESS; } #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION -static bool sigsegv_insn_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) +static sigsegv_return_t sigsegv_insn_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) { - return false; + if (((unsigned long)fault_address - (unsigned long)page) < page_size) + return SIGSEGV_SRETURN_KIP_INSTRUCTION; + return SIGSEGV_RETURN_FAILURE; } #endif @@ -873,8 +814,6 @@ int main(void) if (vm_protect((char *)page, page_size, VM_PAGE_NOACCESS) < 0) return 1; - sigsegv_add_ignore_range((char *)page, page_size, SIGSEGV_TRANSFER_LOAD | SIGSEGV_TRANSFER_STORE); - #define TEST_SKIP_INSTRUCTION(TYPE) do { \ const unsigned int TAG = 0x12345678; \ TYPE data = *((TYPE *)(page + sizeof(TYPE))); \ diff --git a/BasiliskII/src/Unix/sigsegv.h b/BasiliskII/src/Unix/sigsegv.h index bc04f30a..a038961e 100644 --- a/BasiliskII/src/Unix/sigsegv.h +++ b/BasiliskII/src/Unix/sigsegv.h @@ -34,8 +34,15 @@ enum sigsegv_transfer_type_t { SIGSEGV_TRANSFER_STORE = 2, }; +// SIGSEGV handler return state +enum sigsegv_return_t { + SIGSEGV_RETURN_SUCCESS, + SIGSEGV_RETURN_FAILURE, + SIGSEGV_RETURN_SKIP_INSTRUCTION, +}; + // Type of a SIGSEGV handler. Returns boolean expressing successful operation -typedef bool (*sigsegv_fault_handler_t)(sigsegv_address_t fault_address, sigsegv_address_t instruction_address); +typedef sigsegv_return_t (*sigsegv_fault_handler_t)(sigsegv_address_t fault_address, sigsegv_address_t instruction_address); // Type of a SIGSEGV state dump function typedef void (*sigsegv_state_dumper_t)(sigsegv_address_t fault_address, sigsegv_address_t instruction_address); @@ -46,12 +53,6 @@ extern bool sigsegv_install_handler(sigsegv_fault_handler_t handler); // Remove the user SIGSEGV handler, revert to default behavior extern void sigsegv_uninstall_handler(void); -// Add SIGSEGV ignore range -extern void sigsegv_add_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type); - -// Remove SIGSEGV ignore range. Range must match installed one, otherwise FALSE is returned. -extern bool sigsegv_remove_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type); - // Set callback function when we cannot handle the fault extern void sigsegv_set_dump_state(sigsegv_state_dumper_t handler); diff --git a/BasiliskII/src/Unix/video_vosf.h b/BasiliskII/src/Unix/video_vosf.h index 0a3fb83f..a01b5f07 100644 --- a/BasiliskII/src/Unix/video_vosf.h +++ b/BasiliskII/src/Unix/video_vosf.h @@ -21,7 +21,7 @@ #ifndef VIDEO_VOSF_H #define VIDEO_VOSF_H -// Note: this file is #include'd in video_x.cpp +// Note: this file must be #include'd only in video_x.cpp #ifdef ENABLE_VOSF #include @@ -200,11 +200,6 @@ static uint32 page_extend(uint32 size) * Initialize the VOSF system (mainBuffer structure, SIGSEGV handler) */ -#if !EMULATED_PPC && !POWERPC_ROM -static -#endif -bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction); - static bool video_vosf_init(X11_MONITOR_INIT) { VIDEO_MODE_INIT; @@ -260,12 +255,6 @@ static bool video_vosf_init(X11_MONITOR_INIT) if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0) return false; -#if !EMULATED_PPC && !POWERPC_ROM - // Initialize the handler for SIGSEGV - if (!sigsegv_install_handler(Screen_fault_handler)) - return false; -#endif - // The frame buffer is sane, i.e. there is no write to it yet mainBuffer.dirty = false; return true;