--- SheepShaver/src/Unix/main_unix.cpp 2002/02/21 15:12:12 1.2 +++ SheepShaver/src/Unix/main_unix.cpp 2011/12/30 17:38:39 1.98 @@ -1,7 +1,7 @@ /* * main_unix.cpp - Emulation core, Unix implementation * - * SheepShaver (C) 1997-2002 Christian Bauer and Marc Hellwig + * SheepShaver (C) Christian Bauer and Marc Hellwig * * 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 @@ -27,12 +27,14 @@ * is slightly different from the SysV ABI used by Linux: * - Stack frames are different (e.g. LR is stored in 8(r1) under * MacOS, but in 4(r1) under Linux) - * - There is no TOC under Linux; r2 is free for the user + * - There is a pointer to Thread Local Storage (TLS) under Linux with + * recent enough glibc. This is r2 in 32-bit mode and r13 in + * 64-bit mode (PowerOpen/AIX ABI) * - r13 is used as a small data pointer under Linux (but appearently * it is not used this way? To be sure, we specify -msdata=none * in the Makefile) - * - As there is no TOC, there are also no TVECTs under Linux; - * function pointers point directly to the function code + * - There are no TVECTs under Linux; function pointers point + * directly to the function code * The Execute*() functions have to account for this. Additionally, we * cannot simply call MacOS functions by getting their TVECT and jumping * to it. Such calls are done via the call_macos*() functions in @@ -65,6 +67,13 @@ * ExecutePPC (or any function that might cause a mode switch). The signal * stack is restored before exiting the SIGUSR2 handler. * + * Note that POSIX standard says you can't modify the alternate + * signal stack while the process is executing on it. There is a + * hackaround though: we install a trampoline SIGUSR2 handler that + * sets up an alternate stack itself and calls the real handler. + * Then, when we call sigaltstack() there, we no longer get an EPERM, + * i.e. it now works. + * * TODO: * check if SIGSEGV handler works for all registers (including FP!) */ @@ -80,6 +89,7 @@ #include #include #include +#include #include #include "sysdeps.h" @@ -93,26 +103,31 @@ #include "xpram.h" #include "timer.h" #include "adb.h" -#include "sony.h" -#include "disk.h" -#include "cdrom.h" -#include "scsi.h" #include "video.h" -#include "audio.h" -#include "ether.h" -#include "serial.h" -#include "clip.h" -#include "extfs.h" #include "sys.h" #include "macos_util.h" #include "rom_patches.h" #include "user_strings.h" +#include "vm_alloc.h" +#include "sigsegv.h" +#include "sigregs.h" +#include "rpc.h" #define DEBUG 0 #include "debug.h" +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifdef USE_SDL +#include +#endif + +#ifndef USE_SDL_VIDEO #include +#endif #ifdef ENABLE_GTK #include @@ -121,7 +136,7 @@ #ifdef ENABLE_XF86_DGA #include #include -#include +#include #endif #ifdef ENABLE_MON @@ -129,6 +144,9 @@ #endif +// Enable emulation of unaligned lmw/stmw? +#define EMULATE_UNALIGNED_LOADSTORE_MULTIPLE 1 + // Enable Execute68k() safety checks? #define SAFE_EXEC_68K 0 @@ -143,115 +161,119 @@ const char ROM_FILE_NAME[] = "ROM"; const char ROM_FILE_NAME2[] = "Mac OS ROM"; -const uint32 ROM_AREA_SIZE = 0x500000; // Size of ROM area -const uint32 ROM_END = ROM_BASE + ROM_SIZE; // End of ROM - -const uint32 KERNEL_DATA_BASE = 0x68ffe000; // Address of Kernel Data -const uint32 KERNEL_DATA2_BASE = 0x5fffe000; // Alternate address of Kernel Data -const uint32 KERNEL_AREA_SIZE = 0x2000; // Size of Kernel Data area - -const uint32 SIG_STACK_SIZE = 0x10000; // Size of signal stack - - -// 68k Emulator Data -struct EmulatorData { - uint32 v[0x400]; -}; - - -// Kernel Data -struct KernelData { - uint32 v[0x400]; - EmulatorData ed; -}; - - -#if !EMULATED_PPC -// Structure in which registers are saved in a signal handler; -// sigcontext->regs points to it -// (see arch/ppc/kernel/signal.c) -typedef struct { - uint32 u[4]; -} __attribute((aligned(16))) vector128; -#include - -struct sigregs { - elf_gregset_t gp_regs; // Identical to pt_regs - double fp_regs[ELF_NFPREG]; // f0..f31 and fpsrc - //more (uninteresting) stuff following here -}; +#if !REAL_ADDRESSING +// FIXME: needs to be >= 0x04000000 +const uintptr RAM_BASE = 0x10000000; // Base address of RAM +#endif +const uintptr ROM_BASE = 0x40800000; // Base address of ROM +#if REAL_ADDRESSING +const uint32 ROM_ALIGNMENT = 0x100000; // ROM must be aligned to a 1MB boundary #endif +const uint32 SIG_STACK_SIZE = 0x10000; // Size of signal stack // Global variables (exported) #if !EMULATED_PPC -void *TOC; // Small data pointer (r13) +void *TOC = NULL; // Pointer to Thread Local Storage (r2) +void *R13 = NULL; // Pointer to .sdata section (r13 under Linux) #endif uint32 RAMBase; // Base address of Mac RAM uint32 RAMSize; // Size of Mac RAM +uint32 ROMBase; // Base address of Mac ROM uint32 KernelDataAddr; // Address of Kernel Data uint32 BootGlobsAddr; // Address of BootGlobs structure at top of Mac RAM +uint32 DRCacheAddr; // Address of DR Cache uint32 PVR; // Theoretical PVR int64 CPUClockSpeed; // Processor clock speed (Hz) int64 BusClockSpeed; // Bus clock speed (Hz) +int64 TimebaseSpeed; // Timebase clock speed (Hz) +uint8 *RAMBaseHost; // Base address of Mac RAM (host address space) +uint8 *ROMBaseHost; // Base address of Mac ROM (host address space) // Global variables -static char *x_display_name = NULL; // X11 display name +#ifndef USE_SDL_VIDEO +char *x_display_name = NULL; // X11 display name Display *x_display = NULL; // X11 display handle +#ifdef X11_LOCK_TYPE +X11_LOCK_TYPE x_display_lock = X11_LOCK_INIT; // X11 display lock +#endif +#endif static int zero_fd = 0; // FD of /dev/zero static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped static int kernel_area = -1; // SHM ID of Kernel Data area static bool rom_area_mapped = false; // Flag: Mac ROM mmap()ped static bool ram_area_mapped = false; // Flag: Mac RAM mmap()ped -static void *mmap_RAMBase = NULL; // Base address of mmap()ed RAM area +static bool dr_cache_area_mapped = false; // Flag: Mac DR Cache mmap()ped +static bool dr_emulator_area_mapped = false;// Flag: Mac DR Emulator mmap()ped static KernelData *kernel_data; // Pointer to Kernel Data static EmulatorData *emulator_data; static uint8 last_xpram[XPRAM_SIZE]; // Buffer for monitoring XPRAM changes static bool nvram_thread_active = false; // Flag: NVRAM watchdog installed +static volatile bool nvram_thread_cancel; // Flag: Cancel NVRAM thread static pthread_t nvram_thread; // NVRAM watchdog static bool tick_thread_active = false; // Flag: MacOS thread installed +static volatile bool tick_thread_cancel; // Flag: Cancel 60Hz thread static pthread_t tick_thread; // 60Hz thread static pthread_t emul_thread; // MacOS thread static bool ready_for_signals = false; // Handler installed, signals can be sent static int64 num_segv = 0; // Number of handled SEGV signals -#if !EMULATED_PPC static struct sigaction sigusr2_action; // Interrupt signal (of emulator thread) +#if EMULATED_PPC +static uintptr sig_stack = 0; // Stack for PowerPC interrupt routine +#else static struct sigaction sigsegv_action; // Data access exception signal (of emulator thread) static struct sigaction sigill_action; // Illegal instruction signal (of emulator thread) -static void *sig_stack = NULL; // Stack for signal handlers -static void *extra_stack = NULL; // Stack for SIGSEGV inside interrupt handler +static stack_t sig_stack; // Stack for signal handlers +static stack_t extra_stack; // Stack for SIGSEGV inside interrupt handler static bool emul_thread_fatal = false; // Flag: MacOS thread crashed, tick thread shall dump debug output static sigregs sigsegv_regs; // Register dump when crashed +static const char *crash_reason = NULL; // Reason of the crash (SIGSEGV, SIGBUS, SIGILL) #endif +static rpc_connection_t *gui_connection = NULL; // RPC connection to the GUI +static const char *gui_connection_path = NULL; // GUI connection identifier + +uint32 SheepMem::page_size; // Size of a native page +uintptr SheepMem::zero_page = 0; // Address of ro page filled in with zeros +uintptr SheepMem::base = 0x60000000; // Address of SheepShaver data +uintptr SheepMem::proc; // Bottom address of SheepShave procedures +uintptr SheepMem::data; // Top of SheepShaver data (stack like storage) + // Prototypes +static bool kernel_data_init(void); +static void kernel_data_exit(void); static void Quit(void); static void *emul_func(void *arg); static void *nvram_func(void *arg); static void *tick_func(void *arg); -#if !EMULATED_PPC -static void sigusr2_handler(int sig, sigcontext_struct *sc); -static void sigsegv_handler(int sig, sigcontext_struct *sc); -static void sigill_handler(int sig, sigcontext_struct *sc); +#if EMULATED_PPC +extern void emul_ppc(uint32 start); +extern void init_emul_ppc(void); +extern void exit_emul_ppc(void); +sigsegv_return_t sigsegv_handler(sigsegv_info_t *sip); +#else +extern "C" void sigusr2_handler_init(int sig, siginfo_t *sip, void *scp); +extern "C" void sigusr2_handler(int sig, siginfo_t *sip, void *scp); +static void sigsegv_handler(int sig, siginfo_t *sip, void *scp); +static void sigill_handler(int sig, siginfo_t *sip, void *scp); #endif // From asm_linux.S -#if EMULATED_PPC -extern int atomic_add(int *var, int v); -extern int atomic_and(int *var, int v); -extern int atomic_or(int *var, int v); -#else -extern "C" void *get_toc(void); +#if !EMULATED_PPC extern "C" void *get_sp(void); -extern "C" void flush_icache_range(void *start, void *end); +extern "C" void *get_r2(void); +extern "C" void set_r2(void *); +extern "C" void *get_r13(void); +extern "C" void set_r13(void *); +extern "C" void flush_icache_range(uint32 start, uint32 end); extern "C" void jump_to_rom(uint32 entry, uint32 context); extern "C" void quit_emulator(void); extern "C" void execute_68k(uint32 pc, M68kRegisters *r); @@ -263,47 +285,74 @@ extern void paranoia_check(void); #endif -// Decode LZSS data -static void decode_lzss(const uint8 *src, uint8 *dest, int size) +#if EMULATED_PPC +/* + * Return signal stack base + */ + +uintptr SignalStackBase(void) +{ + return sig_stack + SIG_STACK_SIZE; +} + + +/* + * Atomic operations + */ + +#if HAVE_SPINLOCKS +static spinlock_t atomic_ops_lock = SPIN_LOCK_UNLOCKED; +#else +#define spin_lock(LOCK) +#define spin_unlock(LOCK) +#endif + +int atomic_add(int *var, int v) +{ + spin_lock(&atomic_ops_lock); + int ret = *var; + *var += v; + spin_unlock(&atomic_ops_lock); + return ret; +} + +int atomic_and(int *var, int v) { - char dict[0x1000]; - int run_mask = 0, dict_idx = 0xfee; - for (;;) { - if (run_mask < 0x100) { - // Start new run - if (--size < 0) - break; - run_mask = *src++ | 0xff00; - } - bool bit = run_mask & 1; - run_mask >>= 1; - if (bit) { - // Verbatim copy - if (--size < 0) - break; - int c = *src++; - dict[dict_idx++] = c; - *dest++ = c; - dict_idx &= 0xfff; - } else { - // Copy from dictionary - if (--size < 0) - break; - int idx = *src++; - if (--size < 0) - break; - int cnt = *src++; - idx |= (cnt << 4) & 0xf00; - cnt = (cnt & 0x0f) + 3; - while (cnt--) { - char c = dict[idx++]; - dict[dict_idx++] = c; - *dest++ = c; - idx &= 0xfff; - dict_idx &= 0xfff; - } - } - } + spin_lock(&atomic_ops_lock); + int ret = *var; + *var &= v; + spin_unlock(&atomic_ops_lock); + return ret; +} + +int atomic_or(int *var, int v) +{ + spin_lock(&atomic_ops_lock); + int ret = *var; + *var |= v; + spin_unlock(&atomic_ops_lock); + return ret; +} +#endif + + +/* + * Memory management helpers + */ + +static inline uint8 *vm_mac_acquire(uint32 size) +{ + return (uint8 *)vm_acquire(size); +} + +static inline int vm_mac_acquire_fixed(uint32 addr, uint32 size) +{ + return vm_acquire_fixed(Mac2HostAddr(addr), size); +} + +static inline int vm_mac_release(uint32 addr, uint32 size) +{ + return vm_release(Mac2HostAddr(addr), size); } @@ -320,22 +369,346 @@ static void usage(const char *prg_name) exit(0); } -int main(int argc, char **argv) +static bool valid_vmdir(const char *path) { - char str[256]; - uint32 *boot_globs; - int16 i16; - int drive, driver; - int rom_fd; + const int suffix_len = sizeof(".sheepvm") - 1; + int len = strlen(path); + if (len && path[len - 1] == '/') // to support both ".sheepvm" and ".sheepvm/" + len--; + if (len > suffix_len && !strncmp(path + len - suffix_len, ".sheepvm", suffix_len)) { + struct stat d; + if (!stat(path, &d) && S_ISDIR(d.st_mode)) { + return true; + } + } + return false; +} + +static void get_system_info(void) +{ +#if !EMULATED_PPC FILE *proc_file; - const char *rom_path; +#endif + + PVR = 0x00040000; // Default: 604 + CPUClockSpeed = 100000000; // Default: 100MHz + BusClockSpeed = 100000000; // Default: 100MHz + TimebaseSpeed = 25000000; // Default: 25MHz + +#if EMULATED_PPC + PVR = 0x000c0000; // Default: 7400 (with AltiVec) +#elif defined(__APPLE__) && defined(__MACH__) + proc_file = popen("ioreg -c IOPlatformDevice", "r"); + if (proc_file) { + char line[256]; + bool powerpc_node = false; + while (fgets(line, sizeof(line) - 1, proc_file)) { + // Read line + int len = strlen(line); + if (len == 0) + continue; + line[len - 1] = 0; + + // Parse line + if (strstr(line, "o PowerPC,")) + powerpc_node = true; + else if (powerpc_node) { + uint32 value; + char head[256]; + if (sscanf(line, "%[ |]\"cpu-version\" = <%x>", head, &value) == 2) + PVR = value; + else if (sscanf(line, "%[ |]\"clock-frequency\" = <%x>", head, &value) == 2) + CPUClockSpeed = value; + else if (sscanf(line, "%[ |]\"bus-frequency\" = <%x>", head, &value) == 2) + BusClockSpeed = value; + else if (sscanf(line, "%[ |]\"timebase-frequency\" = <%x>", head, &value) == 2) + TimebaseSpeed = value; + else if (strchr(line, '}')) + powerpc_node = false; + } + } + fclose(proc_file); + } else { + char str[256]; + sprintf(str, GetString(STR_PROC_CPUINFO_WARN), strerror(errno)); + WarningAlert(str); + } +#else + proc_file = fopen("/proc/cpuinfo", "r"); + if (proc_file) { + // CPU specs from Linux kernel + // TODO: make it more generic with features (e.g. AltiVec) and + // cache information and friends for NameRegistry + static const struct { + uint32 pvr_mask; + uint32 pvr_value; + const char *cpu_name; + } + cpu_specs[] = { + { 0xffff0000, 0x00010000, "601" }, + { 0xffff0000, 0x00030000, "603" }, + { 0xffff0000, 0x00060000, "603e" }, + { 0xffff0000, 0x00070000, "603ev" }, + { 0xffff0000, 0x00040000, "604" }, + { 0xfffff000, 0x00090000, "604e" }, + { 0xffff0000, 0x00090000, "604r" }, + { 0xffff0000, 0x000a0000, "604ev" }, + { 0xffffffff, 0x00084202, "740/750" }, + { 0xfffff000, 0x00083000, "745/755" }, + { 0xfffffff0, 0x00080100, "750CX" }, + { 0xfffffff0, 0x00082200, "750CX" }, + { 0xfffffff0, 0x00082210, "750CXe" }, + { 0xffffff00, 0x70000100, "750FX" }, + { 0xffffffff, 0x70000200, "750FX" }, + { 0xffff0000, 0x70000000, "750FX" }, + { 0xffff0000, 0x70020000, "750GX" }, + { 0xffff0000, 0x00080000, "740/750" }, + { 0xffffffff, 0x000c1101, "7400 (1.1)" }, + { 0xffff0000, 0x000c0000, "7400" }, + { 0xffff0000, 0x800c0000, "7410" }, + { 0xffffffff, 0x80000200, "7450" }, + { 0xffffffff, 0x80000201, "7450" }, + { 0xffff0000, 0x80000000, "7450" }, + { 0xffffff00, 0x80010100, "7455" }, + { 0xffffffff, 0x80010200, "7455" }, + { 0xffff0000, 0x80010000, "7455" }, + { 0xffff0000, 0x80020000, "7457" }, + { 0xffff0000, 0x80030000, "7447A" }, + { 0xffff0000, 0x80040000, "7448" }, + { 0x7fff0000, 0x00810000, "82xx" }, + { 0x7fff0000, 0x00820000, "8280" }, + { 0xffff0000, 0x00400000, "Power3 (630)" }, + { 0xffff0000, 0x00410000, "Power3 (630+)" }, + { 0xffff0000, 0x00360000, "I-star" }, + { 0xffff0000, 0x00370000, "S-star" }, + { 0xffff0000, 0x00350000, "Power4" }, + { 0xffff0000, 0x00390000, "PPC970" }, + { 0xffff0000, 0x003c0000, "PPC970FX" }, + { 0xffff0000, 0x00440000, "PPC970MP" }, + { 0xffff0000, 0x003a0000, "POWER5 (gr)" }, + { 0xffff0000, 0x003b0000, "POWER5+ (gs)" }, + { 0xffff0000, 0x003e0000, "POWER6" }, + { 0xffff0000, 0x00700000, "Cell Broadband Engine" }, + { 0x7fff0000, 0x00900000, "PA6T" }, + { 0, 0, 0 } + }; + + char line[256]; + while(fgets(line, 255, proc_file)) { + // Read line + int len = strlen(line); + if (len == 0) + continue; + line[len-1] = 0; + + // Parse line + int i; + float f; + char value[256]; + if (sscanf(line, "cpu : %[^,]", value) == 1) { + // Search by name + const char *cpu_name = NULL; + for (int i = 0; cpu_specs[i].pvr_mask != 0; i++) { + if (strcmp(cpu_specs[i].cpu_name, value) == 0) { + cpu_name = cpu_specs[i].cpu_name; + PVR = cpu_specs[i].pvr_value; + break; + } + } + if (cpu_name == NULL) + printf("WARNING: Unknown CPU type '%s', assuming 604\n", value); + else + printf("Found a PowerPC %s processor\n", cpu_name); + } + if (sscanf(line, "clock : %fMHz", &f) == 1) + CPUClockSpeed = BusClockSpeed = ((int64)f) * 1000000; + else if (sscanf(line, "clock : %dMHz", &i) == 1) + CPUClockSpeed = BusClockSpeed = i * 1000000; + } + fclose(proc_file); + } else { + sprintf(str, GetString(STR_PROC_CPUINFO_WARN), strerror(errno)); + WarningAlert(str); + } + + // Get actual bus frequency + proc_file = fopen("/proc/device-tree/clock-frequency", "r"); + if (proc_file) { + union { uint8 b[4]; uint32 l; } value; + if (fread(value.b, sizeof(value), 1, proc_file) == 1) + BusClockSpeed = value.l; + fclose(proc_file); + } + + // Get actual timebase frequency + TimebaseSpeed = BusClockSpeed / 4; + DIR *cpus_dir; + if ((cpus_dir = opendir("/proc/device-tree/cpus")) != NULL) { + struct dirent *cpu_entry; + while ((cpu_entry = readdir(cpus_dir)) != NULL) { + if (strstr(cpu_entry->d_name, "PowerPC,") == cpu_entry->d_name) { + char timebase_freq_node[256]; + sprintf(timebase_freq_node, "/proc/device-tree/cpus/%s/timebase-frequency", cpu_entry->d_name); + proc_file = fopen(timebase_freq_node, "r"); + if (proc_file) { + union { uint8 b[4]; uint32 l; } value; + if (fread(value.b, sizeof(value), 1, proc_file) == 1) + TimebaseSpeed = value.l; + fclose(proc_file); + } + } + } + closedir(cpus_dir); + } +#endif + + // Remap any newer G4/G5 processor to plain G4 for compatibility + switch (PVR >> 16) { + case 0x8000: // 7450 + case 0x8001: // 7455 + case 0x8002: // 7457 + case 0x8003: // 7447A + case 0x8004: // 7448 + case 0x0039: // 970 + case 0x003c: // 970FX + case 0x0044: // 970MP + PVR = 0x000c0000; // 7400 + break; + } + D(bug("PVR: %08x (assumed)\n", PVR)); +} + +static bool load_mac_rom(void) +{ uint32 rom_size, actual; uint8 *rom_tmp; - time_t now, expire; + const char *rom_path = PrefsFindString("rom"); + int rom_fd = open(rom_path && *rom_path ? rom_path : ROM_FILE_NAME, O_RDONLY); + if (rom_fd < 0) { + rom_fd = open(ROM_FILE_NAME2, O_RDONLY); + if (rom_fd < 0) { + ErrorAlert(GetString(STR_NO_ROM_FILE_ERR)); + return false; + } + } + printf("%s", GetString(STR_READING_ROM_FILE)); + rom_size = lseek(rom_fd, 0, SEEK_END); + lseek(rom_fd, 0, SEEK_SET); + rom_tmp = new uint8[ROM_SIZE]; + actual = read(rom_fd, (void *)rom_tmp, ROM_SIZE); + close(rom_fd); + + // Decode Mac ROM + if (!DecodeROM(rom_tmp, actual)) { + if (rom_size != 4*1024*1024) { + ErrorAlert(GetString(STR_ROM_SIZE_ERR)); + return false; + } else { + ErrorAlert(GetString(STR_ROM_FILE_READ_ERR)); + return false; + } + } + delete[] rom_tmp; + return true; +} + +static bool install_signal_handlers(void) +{ + char str[256]; +#if !EMULATED_PPC + // Create and install stacks for signal handlers + sig_stack.ss_sp = malloc(SIG_STACK_SIZE); + D(bug("Signal stack at %p\n", sig_stack.ss_sp)); + if (sig_stack.ss_sp == NULL) { + ErrorAlert(GetString(STR_NOT_ENOUGH_MEMORY_ERR)); + return false; + } + sig_stack.ss_flags = 0; + sig_stack.ss_size = SIG_STACK_SIZE; + if (sigaltstack(&sig_stack, NULL) < 0) { + sprintf(str, GetString(STR_SIGALTSTACK_ERR), strerror(errno)); + ErrorAlert(str); + return false; + } + extra_stack.ss_sp = malloc(SIG_STACK_SIZE); + D(bug("Extra stack at %p\n", extra_stack.ss_sp)); + if (extra_stack.ss_sp == NULL) { + ErrorAlert(GetString(STR_NOT_ENOUGH_MEMORY_ERR)); + return false; + } + extra_stack.ss_flags = 0; + extra_stack.ss_size = SIG_STACK_SIZE; + + // Install SIGSEGV and SIGBUS handlers + sigemptyset(&sigsegv_action.sa_mask); // Block interrupts during SEGV handling + sigaddset(&sigsegv_action.sa_mask, SIGUSR2); + sigsegv_action.sa_sigaction = sigsegv_handler; + sigsegv_action.sa_flags = SA_ONSTACK | SA_SIGINFO; +#ifdef HAVE_SIGNAL_SA_RESTORER + sigsegv_action.sa_restorer = NULL; +#endif + if (sigaction(SIGSEGV, &sigsegv_action, NULL) < 0) { + sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGSEGV", strerror(errno)); + ErrorAlert(str); + return false; + } + if (sigaction(SIGBUS, &sigsegv_action, NULL) < 0) { + sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGBUS", strerror(errno)); + ErrorAlert(str); + return false; + } +#else + // Install SIGSEGV handler for CPU emulator + if (!sigsegv_install_handler(sigsegv_handler)) { + sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGSEGV", strerror(errno)); + ErrorAlert(str); + return false; + } +#endif + return true; +} + +static bool init_sdl() +{ + int sdl_flags = 0; +#ifdef USE_SDL_VIDEO + sdl_flags |= SDL_INIT_VIDEO; +#endif +#ifdef USE_SDL_AUDIO + sdl_flags |= SDL_INIT_AUDIO; +#endif + assert(sdl_flags != 0); + +#ifdef USE_SDL_VIDEO + // Don't let SDL block the screensaver + setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", TRUE); + + // Make SDL pass through command-clicks and option-clicks unaltered + setenv("SDL_HAS3BUTTONMOUSE", "1", TRUE); +#endif + + if (SDL_Init(sdl_flags) == -1) { + char str[256]; + sprintf(str, "Could not initialize SDL: %s.\n", SDL_GetError()); + ErrorAlert(str); + return false; + } + atexit(SDL_Quit); + + // Don't let SDL catch SIGINT and SIGTERM signals + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + return true; +} + +int main(int argc, char **argv) +{ + char str[256]; + bool memory_mapped_from_zero, ram_rom_areas_contiguous; + const char *vmdir = NULL; // Initialize variables RAMBase = 0; - mmap_RAMBase = NULL; tzset(); // Print some info @@ -343,33 +716,92 @@ int main(int argc, char **argv) printf(" %s\n", GetString(STR_ABOUT_TEXT2)); #if !EMULATED_PPC +#ifdef SYSTEM_CLOBBERS_R2 // Get TOC pointer - TOC = get_toc(); + TOC = get_r2(); +#endif +#ifdef SYSTEM_CLOBBERS_R13 + // Get r13 register + R13 = get_r13(); #endif - -#ifdef ENABLE_GTK - // Init GTK - gtk_set_locale(); - gtk_init(&argc, &argv); #endif - - // Read preferences - PrefsInit(argc, argv); // Parse command line arguments for (int i=1; i i) { + k -= i; + for (int j=i+k; jed; + KernelDataAddr = KERNEL_DATA_BASE; + D(bug("Kernel Data at %p (%08x)\n", kernel_data, KERNEL_DATA_BASE)); + D(bug("Emulator Data at %p (%08x)\n", emulator_data, KERNEL_DATA_BASE + offsetof(KernelData, ed))); + + // Create area for DR Cache + if (vm_mac_acquire_fixed(DR_EMULATOR_BASE, DR_EMULATOR_SIZE) < 0) { + sprintf(str, GetString(STR_DR_EMULATOR_MMAP_ERR), strerror(errno)); ErrorAlert(str); goto quit; } - if (shmat(kernel_area, (void *)KERNEL_DATA_BASE, 0) < 0) { - sprintf(str, GetString(STR_KD_SHMAT_ERR), strerror(errno)); + dr_emulator_area_mapped = true; + if (vm_mac_acquire_fixed(DR_CACHE_BASE, DR_CACHE_SIZE) < 0) { + sprintf(str, GetString(STR_DR_CACHE_MMAP_ERR), strerror(errno)); ErrorAlert(str); goto quit; } - if (shmat(kernel_area, (void *)KERNEL_DATA2_BASE, 0) < 0) { - sprintf(str, GetString(STR_KD2_SHMAT_ERR), strerror(errno)); + dr_cache_area_mapped = true; +#if !EMULATED_PPC + if (vm_protect((char *)DR_CACHE_BASE, DR_CACHE_SIZE, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) { + sprintf(str, GetString(STR_DR_CACHE_MMAP_ERR), strerror(errno)); ErrorAlert(str); goto quit; } - kernel_data = (KernelData *)0x68ffe000; - emulator_data = &kernel_data->ed; - KernelDataAddr = (uint32)kernel_data; - D(bug("Kernel Data at %p, Emulator Data at %p\n", kernel_data, emulator_data)); +#endif + DRCacheAddr = DR_CACHE_BASE; + D(bug("DR Cache at %p\n", DRCacheAddr)); - // Create area for Mac ROM - if (mmap((char *)ROM_BASE, ROM_AREA_SIZE, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, zero_fd, 0) == (void *)-1) { - sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno)); + // Create area for SheepShaver data + if (!SheepMem::Init()) { + sprintf(str, GetString(STR_SHEEP_MEM_MMAP_ERR), strerror(errno)); ErrorAlert(str); goto quit; } - rom_area_mapped = true; - D(bug("ROM area at %08x\n", ROM_BASE)); - + // Create area for Mac RAM RAMSize = PrefsFindInt32("ramsize"); if (RAMSize < 8*1024*1024) { WarningAlert(GetString(STR_SMALL_RAM_WARN)); RAMSize = 8*1024*1024; } - - mmap_RAMBase = mmap((void *)0x20000000, RAMSize, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, zero_fd, 0); - if (mmap_RAMBase == (void *)-1) { + memory_mapped_from_zero = false; + ram_rom_areas_contiguous = false; +#if REAL_ADDRESSING && HAVE_LINKER_SCRIPT + if (vm_mac_acquire_fixed(0, RAMSize) == 0) { + D(bug("Could allocate RAM from 0x0000\n")); + RAMBase = 0; + RAMBaseHost = Mac2HostAddr(RAMBase); + memory_mapped_from_zero = true; + } +#endif + if (!memory_mapped_from_zero) { +#ifndef PAGEZERO_HACK + // Create Low Memory area (0x0000..0x3000) + if (vm_mac_acquire_fixed(0, 0x3000) < 0) { + sprintf(str, GetString(STR_LOW_MEM_MMAP_ERR), strerror(errno)); + ErrorAlert(str); + goto quit; + } + lm_area_mapped = true; +#endif +#if REAL_ADDRESSING + // Allocate RAM at any address. Since ROM must be higher than RAM, allocate the RAM + // and ROM areas contiguously, plus a little extra to allow for ROM address alignment. + RAMBaseHost = vm_mac_acquire(RAMSize + ROM_AREA_SIZE + ROM_ALIGNMENT); + if (RAMBaseHost == VM_MAP_FAILED) { + sprintf(str, GetString(STR_RAM_ROM_MMAP_ERR), strerror(errno)); + ErrorAlert(str); + goto quit; + } + RAMBase = Host2MacAddr(RAMBaseHost); + ROMBase = (RAMBase + RAMSize + ROM_ALIGNMENT -1) & -ROM_ALIGNMENT; + ROMBaseHost = Mac2HostAddr(ROMBase); + ram_rom_areas_contiguous = true; +#else + if (vm_mac_acquire_fixed(RAM_BASE, RAMSize) < 0) { + sprintf(str, GetString(STR_RAM_MMAP_ERR), strerror(errno)); + ErrorAlert(str); + goto quit; + } + RAMBase = RAM_BASE; + RAMBaseHost = Mac2HostAddr(RAMBase); +#endif + } +#if !EMULATED_PPC + if (vm_protect(RAMBaseHost, RAMSize, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) { sprintf(str, GetString(STR_RAM_MMAP_ERR), strerror(errno)); ErrorAlert(str); goto quit; } - RAMBase = (uint32)mmap_RAMBase; +#endif ram_area_mapped = true; - D(bug("RAM area at %08x\n", RAMBase)); + D(bug("RAM area at %p (%08x)\n", RAMBaseHost, RAMBase)); - if (RAMBase > ROM_BASE) { - ErrorAlert(GetString(STR_RAM_HIGHER_THAN_ROM_ERR)); + if (RAMBase > KernelDataAddr) { + ErrorAlert(GetString(STR_RAM_AREA_TOO_HIGH_ERR)); goto quit; } - - // Load Mac ROM - rom_path = PrefsFindString("rom"); - rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME, O_RDONLY); - if (rom_fd < 0) { - rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME2, O_RDONLY); - if (rom_fd < 0) { - ErrorAlert(GetString(STR_NO_ROM_FILE_ERR)); + + // Create area for Mac ROM + if (!ram_rom_areas_contiguous) { + if (vm_mac_acquire_fixed(ROM_BASE, ROM_AREA_SIZE) < 0) { + sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno)); + ErrorAlert(str); goto quit; } + ROMBase = ROM_BASE; + ROMBaseHost = Mac2HostAddr(ROMBase); } - printf(GetString(STR_READING_ROM_FILE)); - rom_size = lseek(rom_fd, 0, SEEK_END); - lseek(rom_fd, 0, SEEK_SET); - rom_tmp = new uint8[ROM_SIZE]; - actual = read(rom_fd, (void *)rom_tmp, ROM_SIZE); - close(rom_fd); - if (actual == ROM_SIZE) { - // Plain ROM image - memcpy((void *)ROM_BASE, rom_tmp, ROM_SIZE); - delete[] rom_tmp; - } else { - if (strncmp((char *)rom_tmp, "", 11) == 0) { - // CHRP compressed ROM image - D(bug("CHRP ROM image\n")); - uint32 lzss_offset, lzss_size; - - char *s = strstr((char *)rom_tmp, "constant lzss-offset"); - if (s == NULL) { - ErrorAlert(GetString(STR_ROM_SIZE_ERR)); - goto quit; - } - s -= 7; - if (sscanf(s, "%06x", &lzss_offset) != 1) { - ErrorAlert(GetString(STR_ROM_SIZE_ERR)); - goto quit; - } - s = strstr((char *)rom_tmp, "constant lzss-size"); - if (s == NULL) { - ErrorAlert(GetString(STR_ROM_SIZE_ERR)); - goto quit; - } - s -= 7; - if (sscanf(s, "%06x", &lzss_size) != 1) { - ErrorAlert(GetString(STR_ROM_SIZE_ERR)); - goto quit; - } - D(bug("Offset of compressed data: %08x\n", lzss_offset)); - D(bug("Size of compressed data: %08x\n", lzss_size)); - - D(bug("Uncompressing ROM...\n")); - decode_lzss(rom_tmp + lzss_offset, (uint8 *)ROM_BASE, lzss_size); - delete[] rom_tmp; - } else if (rom_size != 4*1024*1024) { - ErrorAlert(GetString(STR_ROM_SIZE_ERR)); - goto quit; - } else { - ErrorAlert(GetString(STR_ROM_FILE_READ_ERR)); - goto quit; - } +#if !EMULATED_PPC + if (vm_protect(ROMBaseHost, ROM_AREA_SIZE, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) { + sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno)); + ErrorAlert(str); + goto quit; } +#endif + rom_area_mapped = true; + D(bug("ROM area at %p (%08x)\n", ROMBaseHost, ROMBase)); - // Load NVRAM - XPRAMInit(); - - // Set boot volume - drive = PrefsFindInt32("bootdrive"); - XPRAM[0x1378] = i16 >> 8; - XPRAM[0x1379] = i16 & 0xff; - driver = PrefsFindInt32("bootdriver"); - XPRAM[0x137a] = i16 >> 8; - XPRAM[0x137b] = i16 & 0xff; - - // Create BootGlobs at top of Mac memory - memset((void *)(RAMBase + RAMSize - 4096), 0, 4096); - BootGlobsAddr = RAMBase + RAMSize - 0x1c; - boot_globs = (uint32 *)BootGlobsAddr; - boot_globs[-5] = htonl(RAMBase + RAMSize); // MemTop - boot_globs[0] = htonl(RAMBase); // First RAM bank - boot_globs[1] = htonl(RAMSize); - boot_globs[2] = htonl((uint32)-1); // End of bank table - - // Init drivers - SonyInit(); - DiskInit(); - CDROMInit(); - SCSIInit(); - - // Init external file system - ExtFSInit(); - - // Init audio - AudioInit(); - - // Init network - EtherInit(); - - // Init serial ports - SerialInit(); - - // Init Time Manager - TimerInit(); - - // Init clipboard - ClipInit(); + if (RAMBase > ROMBase) { + ErrorAlert(GetString(STR_RAM_HIGHER_THAN_ROM_ERR)); + goto quit; + } - // Init video - if (!VideoInit()) + // Load Mac ROM + if (!load_mac_rom()) goto quit; - // Install ROM patches - if (!PatchROM()) { - ErrorAlert(GetString(STR_UNSUPPORTED_ROM_TYPE_ERR)); + // Initialize everything + if (!InitAll(vmdir)) goto quit; - } + D(bug("Initialization complete\n")); // Clear caches (as we loaded and patched code) and write protect ROM #if !EMULATED_PPC - MakeExecutable(0, (void *)ROM_BASE, ROM_AREA_SIZE); -#endif - mprotect((char *)ROM_BASE, ROM_AREA_SIZE, PROT_EXEC | PROT_READ); - - // Initialize Kernel Data - memset(kernel_data, 0, sizeof(KernelData)); - if (ROMType == ROMTYPE_NEWWORLD) { - static uint32 of_dev_tree[4] = {0, 0, 0, 0}; - static uint8 vector_lookup_tbl[128]; - static uint8 vector_mask_tbl[64]; - memset((uint8 *)kernel_data + 0xb80, 0x3d, 0x80); - memset(vector_lookup_tbl, 0, 128); - memset(vector_mask_tbl, 0, 64); - kernel_data->v[0xb80 >> 2] = htonl(ROM_BASE); - kernel_data->v[0xb84 >> 2] = htonl((uint32)of_dev_tree); // OF device tree base - kernel_data->v[0xb90 >> 2] = htonl((uint32)vector_lookup_tbl); - kernel_data->v[0xb94 >> 2] = htonl((uint32)vector_mask_tbl); - kernel_data->v[0xb98 >> 2] = htonl(ROM_BASE); // OpenPIC base - kernel_data->v[0xbb0 >> 2] = htonl(0); // ADB base - kernel_data->v[0xc20 >> 2] = htonl(RAMSize); - kernel_data->v[0xc24 >> 2] = htonl(RAMSize); - kernel_data->v[0xc30 >> 2] = htonl(RAMSize); - kernel_data->v[0xc34 >> 2] = htonl(RAMSize); - kernel_data->v[0xc38 >> 2] = htonl(0x00010020); - kernel_data->v[0xc3c >> 2] = htonl(0x00200001); - kernel_data->v[0xc40 >> 2] = htonl(0x00010000); - kernel_data->v[0xc50 >> 2] = htonl(RAMBase); - kernel_data->v[0xc54 >> 2] = htonl(RAMSize); - kernel_data->v[0xf60 >> 2] = htonl(PVR); - kernel_data->v[0xf64 >> 2] = htonl(CPUClockSpeed); - kernel_data->v[0xf68 >> 2] = htonl(BusClockSpeed); - kernel_data->v[0xf6c >> 2] = htonl(CPUClockSpeed); - } else { - kernel_data->v[0xc80 >> 2] = htonl(RAMSize); - kernel_data->v[0xc84 >> 2] = htonl(RAMSize); - kernel_data->v[0xc90 >> 2] = htonl(RAMSize); - kernel_data->v[0xc94 >> 2] = htonl(RAMSize); - kernel_data->v[0xc98 >> 2] = htonl(0x00010020); - kernel_data->v[0xc9c >> 2] = htonl(0x00200001); - kernel_data->v[0xca0 >> 2] = htonl(0x00010000); - kernel_data->v[0xcb0 >> 2] = htonl(RAMBase); - kernel_data->v[0xcb4 >> 2] = htonl(RAMSize); - kernel_data->v[0xf80 >> 2] = htonl(PVR); - kernel_data->v[0xf84 >> 2] = htonl(CPUClockSpeed); - kernel_data->v[0xf88 >> 2] = htonl(BusClockSpeed); - kernel_data->v[0xf8c >> 2] = htonl(CPUClockSpeed); - } - - // Initialize extra low memory - D(bug("Initializing Low Memory...\n")); - memset(NULL, 0, 0x3000); - WriteMacInt32(XLM_SIGNATURE, FOURCC('B','a','a','h')); // Signature to detect SheepShaver - WriteMacInt32(XLM_KERNEL_DATA, (uint32)kernel_data); // For trap replacement routines - WriteMacInt32(XLM_PVR, PVR); // Theoretical PVR - WriteMacInt32(XLM_BUS_CLOCK, BusClockSpeed); // For DriverServicesLib patch - WriteMacInt16(XLM_EXEC_RETURN_OPCODE, M68K_EXEC_RETURN); // For Execute68k() (RTS from the executed 68k code will jump here and end 68k mode) -#if !EMULATED_PPC - WriteMacInt32(XLM_TOC, (uint32)TOC); // TOC pointer of emulator - WriteMacInt32(XLM_ETHER_INIT, (uint32)InitStreamModule); // DLPI ethernet driver functions - WriteMacInt32(XLM_ETHER_TERM, (uint32)TerminateStreamModule); - WriteMacInt32(XLM_ETHER_OPEN, (uint32)ether_open); - WriteMacInt32(XLM_ETHER_CLOSE, (uint32)ether_close); - WriteMacInt32(XLM_ETHER_WPUT, (uint32)ether_wput); - WriteMacInt32(XLM_ETHER_RSRV, (uint32)ether_rsrv); - WriteMacInt32(XLM_VIDEO_DOIO, (uint32)VideoDoDriverIO); + flush_icache_range(ROMBase, ROMBase + ROM_AREA_SIZE); #endif - D(bug("Low Memory initialized\n")); + vm_protect(ROMBaseHost, ROM_AREA_SIZE, VM_PAGE_READ | VM_PAGE_EXECUTE); // Start 60Hz thread + tick_thread_cancel = false; tick_thread_active = (pthread_create(&tick_thread, NULL, tick_func, NULL) == 0); D(bug("Tick thread installed (%ld)\n", tick_thread)); // Start NVRAM watchdog thread memcpy(last_xpram, XPRAM, XPRAM_SIZE); + nvram_thread_cancel = false; nvram_thread_active = (pthread_create(&nvram_thread, NULL, nvram_func, NULL) == 0); D(bug("NVRAM thread installed (%ld)\n", nvram_thread)); #if !EMULATED_PPC - // Create and install stacks for signal handlers - sig_stack = malloc(SIG_STACK_SIZE); - D(bug("Signal stack at %p\n", sig_stack)); - if (sig_stack == NULL) { - ErrorAlert(GetString(STR_NOT_ENOUGH_MEMORY_ERR)); - goto quit; - } - extra_stack = malloc(SIG_STACK_SIZE); - D(bug("Extra stack at %p\n", extra_stack)); - if (extra_stack == NULL) { - ErrorAlert(GetString(STR_NOT_ENOUGH_MEMORY_ERR)); - goto quit; - } - struct sigaltstack new_stack; - new_stack.ss_sp = sig_stack; - new_stack.ss_flags = 0; - new_stack.ss_size = SIG_STACK_SIZE; - if (sigaltstack(&new_stack, NULL) < 0) { - sprintf(str, GetString(STR_SIGALTSTACK_ERR), strerror(errno)); - ErrorAlert(str); - goto quit; - } -#endif - -#if !EMULATED_PPC - // Install SIGSEGV handler - sigemptyset(&sigsegv_action.sa_mask); // Block interrupts during SEGV handling - sigaddset(&sigsegv_action.sa_mask, SIGUSR2); - sigsegv_action.sa_handler = (__sighandler_t)sigsegv_handler; - sigsegv_action.sa_flags = SA_ONSTACK; - sigsegv_action.sa_restorer = NULL; - if (sigaction(SIGSEGV, &sigsegv_action, NULL) < 0) { - sprintf(str, GetString(STR_SIGSEGV_INSTALL_ERR), strerror(errno)); - ErrorAlert(str); - goto quit; - } - // Install SIGILL handler sigemptyset(&sigill_action.sa_mask); // Block interrupts during ILL handling sigaddset(&sigill_action.sa_mask, SIGUSR2); - sigill_action.sa_handler = (__sighandler_t)sigill_handler; - sigill_action.sa_flags = SA_ONSTACK; + sigill_action.sa_sigaction = sigill_handler; + sigill_action.sa_flags = SA_ONSTACK | SA_SIGINFO; +#ifdef HAVE_SIGNAL_SA_RESTORER sigill_action.sa_restorer = NULL; +#endif if (sigaction(SIGILL, &sigill_action, NULL) < 0) { - sprintf(str, GetString(STR_SIGILL_INSTALL_ERR), strerror(errno)); + sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGILL", strerror(errno)); ErrorAlert(str); goto quit; } +#endif +#if !EMULATED_PPC // Install interrupt signal handler sigemptyset(&sigusr2_action.sa_mask); - sigusr2_action.sa_handler = (__sighandler_t)sigusr2_handler; - sigusr2_action.sa_flags = SA_ONSTACK | SA_RESTART; + sigusr2_action.sa_sigaction = sigusr2_handler_init; + sigusr2_action.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; +#ifdef HAVE_SIGNAL_SA_RESTORER sigusr2_action.sa_restorer = NULL; +#endif if (sigaction(SIGUSR2, &sigusr2_action, NULL) < 0) { - sprintf(str, GetString(STR_SIGUSR2_INSTALL_ERR), strerror(errno)); + sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGUSR2", strerror(errno)); ErrorAlert(str); goto quit; } @@ -797,80 +1055,72 @@ quit: static void Quit(void) { +#if EMULATED_PPC + // Exit PowerPC emulation + exit_emul_ppc(); +#endif + // Stop 60Hz thread if (tick_thread_active) { + tick_thread_cancel = true; pthread_cancel(tick_thread); pthread_join(tick_thread, NULL); } // Stop NVRAM watchdog thread if (nvram_thread_active) { + nvram_thread_cancel = true; pthread_cancel(nvram_thread); pthread_join(nvram_thread, NULL); } #if !EMULATED_PPC - // Uninstall SIGSEGV handler + // Uninstall SIGSEGV and SIGBUS handlers sigemptyset(&sigsegv_action.sa_mask); sigsegv_action.sa_handler = SIG_DFL; sigsegv_action.sa_flags = 0; sigaction(SIGSEGV, &sigsegv_action, NULL); + sigaction(SIGBUS, &sigsegv_action, NULL); // Uninstall SIGILL handler sigemptyset(&sigill_action.sa_mask); sigill_action.sa_handler = SIG_DFL; sigill_action.sa_flags = 0; sigaction(SIGILL, &sigill_action, NULL); -#endif - - // Save NVRAM - XPRAMExit(); - - // Exit clipboard - ClipExit(); - - // Exit Time Manager - TimerExit(); - // Exit serial - SerialExit(); - - // Exit network - EtherExit(); - - // Exit audio - AudioExit(); - - // Exit video - VideoExit(); + // Delete stacks for signal handlers + if (sig_stack.ss_sp) + free(sig_stack.ss_sp); + if (extra_stack.ss_sp) + free(extra_stack.ss_sp); +#endif - // Exit external file system - ExtFSExit(); + // Deinitialize everything + ExitAll(); - // Exit drivers - SCSIExit(); - CDROMExit(); - DiskExit(); - SonyExit(); + // Delete SheepShaver globals + SheepMem::Exit(); // Delete RAM area if (ram_area_mapped) - munmap(mmap_RAMBase, RAMSize); + vm_mac_release(RAMBase, RAMSize); // Delete ROM area if (rom_area_mapped) - munmap((char *)ROM_BASE, ROM_AREA_SIZE); + vm_mac_release(ROMBase, ROM_AREA_SIZE); + + // Delete DR cache areas + if (dr_emulator_area_mapped) + vm_mac_release(DR_EMULATOR_BASE, DR_EMULATOR_SIZE); + if (dr_cache_area_mapped) + vm_mac_release(DR_CACHE_BASE, DR_CACHE_SIZE); // Delete Kernel Data area - if (kernel_area >= 0) { - shmdt((void *)KERNEL_DATA_BASE); - shmdt((void *)KERNEL_DATA2_BASE); - shmctl(kernel_area, IPC_RMID, NULL); - } + kernel_data_exit(); // Delete Low Memory area if (lm_area_mapped) - munmap((char *)0x0000, 0x3000); + vm_mac_release(0, 0x3000); // Close /dev/zero if (zero_fd > 0) @@ -888,20 +1138,71 @@ static void Quit(void) #endif // Close X11 server connection +#ifndef USE_SDL_VIDEO if (x_display) XCloseDisplay(x_display); +#endif + + // Notify GUI we are about to leave + if (gui_connection) { + if (rpc_method_invoke(gui_connection, RPC_METHOD_EXIT, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR) + rpc_method_wait_for_reply(gui_connection, RPC_TYPE_INVALID); + } exit(0); } /* + * Initialize Kernel Data segments + */ + +static bool kernel_data_init(void) +{ + char str[256]; + uint32 kernel_area_size = (KERNEL_AREA_SIZE + SHMLBA - 1) & -SHMLBA; + + kernel_area = shmget(IPC_PRIVATE, kernel_area_size, 0600); + if (kernel_area == -1) { + sprintf(str, GetString(STR_KD_SHMGET_ERR), strerror(errno)); + ErrorAlert(str); + return false; + } + void *kernel_addr = Mac2HostAddr(KERNEL_DATA_BASE & -SHMLBA); + if (shmat(kernel_area, kernel_addr, 0) != kernel_addr) { + sprintf(str, GetString(STR_KD_SHMAT_ERR), strerror(errno)); + ErrorAlert(str); + return false; + } + kernel_addr = Mac2HostAddr(KERNEL_DATA2_BASE & -SHMLBA); + if (shmat(kernel_area, kernel_addr, 0) != kernel_addr) { + sprintf(str, GetString(STR_KD2_SHMAT_ERR), strerror(errno)); + ErrorAlert(str); + return false; + } + return true; +} + + +/* + * Deallocate Kernel Data segments + */ + +static void kernel_data_exit(void) +{ + if (kernel_area >= 0) { + shmdt(Mac2HostAddr(KERNEL_DATA_BASE & -SHMLBA)); + shmdt(Mac2HostAddr(KERNEL_DATA2_BASE & -SHMLBA)); + shmctl(kernel_area, IPC_RMID, NULL); + } +} + + +/* * Jump into Mac ROM, start 680x0 emulator */ #if EMULATED_PPC -extern void emul_ppc(uint32 start); -extern void init_emul_ppc(void); void jump_to_rom(uint32 entry) { init_emul_ppc(); @@ -925,9 +1226,9 @@ static void *emul_func(void *arg) // Jump to ROM boot routine D(bug("Jumping to ROM\n")); #if EMULATED_PPC - jump_to_rom(ROM_BASE + 0x310000); + jump_to_rom(ROMBase + 0x310000); #else - jump_to_rom(ROM_BASE + 0x310000, (uint32)emulator_data); + jump_to_rom(ROMBase + 0x310000, (uint32)emulator_data); #endif D(bug("Returned from ROM\n")); @@ -970,19 +1271,6 @@ void Execute68kTrap(uint16 trap, M68kReg /* - * Execute PPC code from EMUL_OP routine (real mode switch) - */ - -void ExecutePPC(void (*func)()) -{ - uint32 tvect[2] = {(uint32)func, 0}; // Fake TVECT - RoutineDescriptor desc = BUILD_PPC_ROUTINE_DESCRIPTOR(0, tvect); - M68kRegisters r; - Execute68k((uint32)&desc, &r); -} - - -/* * Quit emulator (cause return from jump_to_rom) */ @@ -997,21 +1285,6 @@ void QuitEmulator(void) /* - * Pause/resume emulator - */ - -void PauseEmulator(void) -{ - pthread_kill(emul_thread, SIGSTOP); -} - -void ResumeEmulator(void) -{ - pthread_kill(emul_thread, SIGCONT); -} - - -/* * Dump 68k registers */ @@ -1039,43 +1312,36 @@ void Dump68kRegs(M68kRegisters *r) * Make code executable */ -void MakeExecutable(int dummy, void *start, uint32 length) +void MakeExecutable(int dummy, uint32 start, uint32 length) { -#if !EMULATED_PPC - if (((uint32)start >= ROM_BASE) && ((uint32)start < (ROM_BASE + ROM_SIZE))) + if ((start >= ROMBase) && (start < (ROMBase + ROM_SIZE))) return; - flush_icache_range(start, (void *)((uint32)start + length)); +#if EMULATED_PPC + FlushCodeCache(start, start + length); +#else + flush_icache_range(start, start + length); #endif } /* - * Patch things after system startup (gets called by disk driver accRun routine) + * NVRAM watchdog thread (saves NVRAM every minute) */ -void PatchAfterStartup(void) +static void nvram_watchdog(void) { - ExecutePPC(VideoInstallAccel); - InstallExtFS(); + if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) { + memcpy(last_xpram, XPRAM, XPRAM_SIZE); + SaveXPRAM(); + } } - -/* - * NVRAM watchdog thread (saves NVRAM every minute) - */ - static void *nvram_func(void *arg) { - struct timespec req = {60, 0}; // 1 minute - - for (;;) { - pthread_testcancel(); - nanosleep(&req, NULL); - pthread_testcancel(); - if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) { - memcpy(last_xpram, XPRAM, XPRAM_SIZE); - SaveXPRAM(); - } + while (!nvram_thread_cancel) { + for (int i=0; i<60 && !nvram_thread_cancel; i++) + Delay_usec(999999); // Only wait 1 second so we quit promptly when nvram_thread_cancel becomes true + nvram_watchdog(); } return NULL; } @@ -1088,21 +1354,31 @@ static void *nvram_func(void *arg) static void *tick_func(void *arg) { int tick_counter = 0; - struct timespec req = {0, 16625000}; + uint64 start = GetTicks_usec(); + int64 ticks = 0; + uint64 next = GetTicks_usec(); - for (;;) { + while (!tick_thread_cancel) { // Wait - nanosleep(&req, NULL); + next += 16625; + int64 delay = next - GetTicks_usec(); + if (delay > 0) + Delay_usec(delay); + else if (delay < -16625) + next = GetTicks_usec(); + ticks++; #if !EMULATED_PPC // Did we crash? if (emul_thread_fatal) { // Yes, dump registers - pt_regs *r = (pt_regs *)&sigsegv_regs; + sigregs *r = &sigsegv_regs; char str[256]; - sprintf(str, "SIGSEGV\n" + if (crash_reason == NULL) + crash_reason = "SIGSEGV"; + sprintf(str, "%s\n" " pc %08lx lr %08lx ctr %08lx msr %08lx\n" " xer %08lx cr %08lx \n" " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n" @@ -1113,6 +1389,7 @@ static void *tick_func(void *arg) " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n" " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n" " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n", + crash_reason, r->nip, r->link, r->ctr, r->msr, r->xer, r->ccr, r->gpr[0], r->gpr[1], r->gpr[2], r->gpr[3], @@ -1148,6 +1425,9 @@ static void *tick_func(void *arg) TriggerInterrupt(); } } + + uint64 end = GetTicks_usec(); + D(bug("%lld ticks in %lld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start))); return NULL; } @@ -1158,7 +1438,30 @@ static void *tick_func(void *arg) void Set_pthread_attr(pthread_attr_t *attr, int priority) { - // nothing to do +#ifdef HAVE_PTHREADS + pthread_attr_init(attr); +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) + // Some of these only work for superuser + if (geteuid() == 0) { + pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(attr, SCHED_FIFO); + struct sched_param fifo_param; + fifo_param.sched_priority = ((sched_get_priority_min(SCHED_FIFO) + + sched_get_priority_max(SCHED_FIFO)) / 2 + + priority); + pthread_attr_setschedparam(attr, &fifo_param); + } + if (pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM) != 0) { +#ifdef PTHREAD_SCOPE_BOUND_NP + // If system scope is not available (eg. we're not running + // with CAP_SCHED_MGT capability on an SGI box), try bound + // scope. It exposes pthread scheduling to the kernel, + // without setting realtime priority. + pthread_attr_setscope(attr, PTHREAD_SCOPE_BOUND_NP); +#endif + } +#endif +#endif } @@ -1166,6 +1469,56 @@ void Set_pthread_attr(pthread_attr_t *at * Mutexes */ +#ifdef HAVE_PTHREADS + +struct B2_mutex { + B2_mutex() { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + // Initialize the mutex for priority inheritance -- + // required for accurate timing. +#if defined(HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL) && !defined(__CYGWIN__) + pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); +#endif +#if defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE) && defined(PTHREAD_MUTEX_NORMAL) + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); +#endif +#ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE); +#endif + pthread_mutex_init(&m, &attr); + pthread_mutexattr_destroy(&attr); + } + ~B2_mutex() { + pthread_mutex_trylock(&m); // Make sure it's locked before + pthread_mutex_unlock(&m); // unlocking it. + pthread_mutex_destroy(&m); + } + pthread_mutex_t m; +}; + +B2_mutex *B2_create_mutex(void) +{ + return new B2_mutex; +} + +void B2_lock_mutex(B2_mutex *mutex) +{ + pthread_mutex_lock(&mutex->m); +} + +void B2_unlock_mutex(B2_mutex *mutex) +{ + pthread_mutex_unlock(&mutex->m); +} + +void B2_delete_mutex(B2_mutex *mutex) +{ + delete mutex; +} + +#else + struct B2_mutex { int dummy; }; @@ -1188,24 +1541,22 @@ void B2_delete_mutex(B2_mutex *mutex) delete mutex; } +#endif + /* * Trigger signal USR2 from another thread */ +#if !EMULATED_PPC void TriggerInterrupt(void) { -#if EMULATED_PPC - WriteMacInt32(0x16a, ReadMacInt32(0x16a) + 1); -#else -#if 0 - WriteMacInt32(0x16a, ReadMacInt32(0x16a) + 1); -#else - if (ready_for_signals) + if (ready_for_signals) { + idle_resume(); pthread_kill(emul_thread, SIGUSR2); -#endif -#endif + } } +#endif /* @@ -1231,7 +1582,11 @@ void ClearInterruptFlag(uint32 flag) void DisableInterrupt(void) { +#if EMULATED_PPC + WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) + 1); +#else atomic_add((int *)XLM_IRQ_NEST, 1); +#endif } @@ -1241,18 +1596,36 @@ void DisableInterrupt(void) void EnableInterrupt(void) { +#if EMULATED_PPC + WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) - 1); +#else atomic_add((int *)XLM_IRQ_NEST, -1); +#endif } -#if !EMULATED_PPC /* * USR2 handler */ -static void sigusr2_handler(int sig, sigcontext_struct *sc) +#if !EMULATED_PPC +void sigusr2_handler(int sig, siginfo_t *sip, void *scp) { - pt_regs *r = sc->regs; + machine_regs *r = MACHINE_REGISTERS(scp); + +#ifdef SYSTEM_CLOBBERS_R2 + // Restore pointer to Thread Local Storage + set_r2(TOC); +#endif +#ifdef SYSTEM_CLOBBERS_R13 + // Restore pointer to .sdata section + set_r13(R13); +#endif + +#ifdef USE_SDL_VIDEO + // We must fill in the events queue in the same thread that did call SDL_SetVideoMode() + SDL_PumpEvents(); +#endif // Do nothing if interrupts are disabled if (*(int32 *)XLM_IRQ_NEST > 0) @@ -1266,23 +1639,30 @@ static void sigusr2_handler(int sig, sig case MODE_68K: // 68k emulator active, trigger 68k interrupt level 1 WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1); - r->ccr |= ntohl(kernel_data->v[0x674 >> 2]); + r->cr() |= ntohl(kernel_data->v[0x674 >> 2]); break; #if INTERRUPTS_IN_NATIVE_MODE case MODE_NATIVE: // 68k emulator inactive, in nanokernel? - if (r->gpr[1] != KernelDataAddr) { + if (r->gpr(1) != KernelDataAddr) { + + // Set extra stack for SIGSEGV handler + sigaltstack(&extra_stack, NULL); + // Prepare for 68k interrupt level 1 WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1); WriteMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc, ReadMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc) | ntohl(kernel_data->v[0x674 >> 2])); // Execute nanokernel interrupt routine (this will activate the 68k emulator) - atomic_add((int32 *)XLM_IRQ_NEST, 1); + DisableInterrupt(); if (ROMType == ROMTYPE_NEWWORLD) - ppc_interrupt(ROM_BASE + 0x312b1c, KernelDataAddr); + ppc_interrupt(ROMBase + 0x312b1c, KernelDataAddr); else - ppc_interrupt(ROM_BASE + 0x312a3c, KernelDataAddr); + ppc_interrupt(ROMBase + 0x312a3c, KernelDataAddr); + + // Reset normal stack + sigaltstack(&sig_stack, NULL); } break; #endif @@ -1293,11 +1673,7 @@ static void sigusr2_handler(int sig, sig if ((ReadMacInt32(XLM_68K_R25) & 7) == 0) { // Set extra stack for SIGSEGV handler - struct sigaltstack new_stack; - new_stack.ss_sp = extra_stack; - new_stack.ss_flags = 0; - new_stack.ss_size = SIG_STACK_SIZE; - sigaltstack(&new_stack, NULL); + sigaltstack(&extra_stack, NULL); #if 1 // Execute full 68k interrupt routine M68kRegisters r; @@ -1319,72 +1695,103 @@ static void sigusr2_handler(int sig, sig if (InterruptFlags & INTFLAG_VIA) { ClearInterruptFlag(INTFLAG_VIA); ADBInterrupt(); - ExecutePPC(VideoVBL); + ExecuteNative(NATIVE_VIDEO_VBL); } } #endif - // Reset normal signal stack - new_stack.ss_sp = sig_stack; - new_stack.ss_flags = 0; - new_stack.ss_size = SIG_STACK_SIZE; - sigaltstack(&new_stack, NULL); + // Reset normal stack + sigaltstack(&sig_stack, NULL); } break; #endif - } } +#endif /* * SIGSEGV handler */ -static void sigsegv_handler(int sig, sigcontext_struct *sc) +#if !EMULATED_PPC +static void sigsegv_handler(int sig, siginfo_t *sip, void *scp) { - pt_regs *r = sc->regs; + machine_regs *r = MACHINE_REGISTERS(scp); + + // Get effective address + uint32 addr = r->dar(); + +#ifdef SYSTEM_CLOBBERS_R2 + // Restore pointer to Thread Local Storage + set_r2(TOC); +#endif +#ifdef SYSTEM_CLOBBERS_R13 + // Restore pointer to .sdata section + set_r13(R13); +#endif + +#if ENABLE_VOSF + // Handle screen fault +#if SIGSEGV_CHECK_VERSION(1,0,0) + sigsegv_info_t si; + si.addr = (sigsegv_address_t)addr; + si.pc = (sigsegv_address_t)r->pc(); +#endif + extern bool Screen_fault_handler(sigsegv_info_t *sip); + if (Screen_fault_handler(&si)) + return; +#endif + num_segv++; - // Fault in Mac ROM or RAM? - bool mac_fault = (r->nip >= ROM_BASE) && (r->nip < (ROM_BASE + ROM_AREA_SIZE)) || (r->nip >= RAMBase) && (r->nip < (RAMBase + RAMSize)); + // Fault in Mac ROM or RAM or DR Cache? + bool mac_fault = (r->pc() >= ROMBase) && (r->pc() < (ROMBase + ROM_AREA_SIZE)) || (r->pc() >= RAMBase) && (r->pc() < (RAMBase + RAMSize)) || (r->pc() >= DR_CACHE_BASE && r->pc() < (DR_CACHE_BASE + DR_CACHE_SIZE)); if (mac_fault) { - // Get opcode and divide into fields - uint32 opcode = *((uint32 *)r->nip); - uint32 primop = opcode >> 26; - uint32 exop = (opcode >> 1) & 0x3ff; - uint32 ra = (opcode >> 16) & 0x1f; - uint32 rb = (opcode >> 11) & 0x1f; - uint32 rd = (opcode >> 21) & 0x1f; - int32 imm = (int16)(opcode & 0xffff); - // "VM settings" during MacOS 8 installation - if (r->nip == ROM_BASE + 0x488160 && r->gpr[20] == 0xf8000000) { - r->nip += 4; - r->gpr[8] = 0; + if (r->pc() == ROMBase + 0x488160 && r->gpr(20) == 0xf8000000) { + r->pc() += 4; + r->gpr(8) = 0; return; // MacOS 8.5 installation - } else if (r->nip == ROM_BASE + 0x488140 && r->gpr[16] == 0xf8000000) { - r->nip += 4; - r->gpr[8] = 0; + } else if (r->pc() == ROMBase + 0x488140 && r->gpr(16) == 0xf8000000) { + r->pc() += 4; + r->gpr(8) = 0; return; // MacOS 8 serial drivers on startup - } else if (r->nip == ROM_BASE + 0x48e080 && (r->gpr[8] == 0xf3012002 || r->gpr[8] == 0xf3012000)) { - r->nip += 4; - r->gpr[8] = 0; + } else if (r->pc() == ROMBase + 0x48e080 && (r->gpr(8) == 0xf3012002 || r->gpr(8) == 0xf3012000)) { + r->pc() += 4; + r->gpr(8) = 0; return; // MacOS 8.1 serial drivers on startup - } else if (r->nip == ROM_BASE + 0x48c5e0 && (r->gpr[20] == 0xf3012002 || r->gpr[20] == 0xf3012000)) { - r->nip += 4; + } else if (r->pc() == ROMBase + 0x48c5e0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) { + r->pc() += 4; return; - } else if (r->nip == ROM_BASE + 0x4a10a0 && (r->gpr[20] == 0xf3012002 || r->gpr[20] == 0xf3012000)) { - r->nip += 4; + } else if (r->pc() == ROMBase + 0x4a10a0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) { + r->pc() += 4; + return; + + // MacOS 8.6 serial drivers on startup (with DR Cache and OldWorld ROM) + } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(16) == 0xf3012002 || r->gpr(16) == 0xf3012000)) { + r->pc() += 4; + return; + } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) { + r->pc() += 4; return; } + // Get opcode and divide into fields + uint32 opcode = *((uint32 *)r->pc()); + uint32 primop = opcode >> 26; + uint32 exop = (opcode >> 1) & 0x3ff; + uint32 ra = (opcode >> 16) & 0x1f; + uint32 rb = (opcode >> 11) & 0x1f; + uint32 rd = (opcode >> 21) & 0x1f; + int32 imm = (int16)(opcode & 0xffff); + // Analyze opcode enum { TYPE_UNKNOWN, @@ -1466,45 +1873,52 @@ static void sigsegv_handler(int sig, sig transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; case 45: // sthu transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; - } - - // Calculate effective address - uint32 addr = 0; - switch (addr_mode) { - case MODE_X: - case MODE_UX: - if (ra == 0) - addr = r->gpr[rb]; - else - addr = r->gpr[ra] + r->gpr[rb]; - break; - case MODE_NORM: - case MODE_U: - if (ra == 0) - addr = (int32)(int16)imm; - else - addr = r->gpr[ra] + (int32)(int16)imm; +#if EMULATE_UNALIGNED_LOADSTORE_MULTIPLE + case 46: // lmw + if ((addr % 4) != 0) { + uint32 ea = addr; + D(bug("WARNING: unaligned lmw to EA=%08x from IP=%08x\n", ea, r->pc())); + for (int i = rd; i <= 31; i++) { + r->gpr(i) = ReadMacInt32(ea); + ea += 4; + } + r->pc() += 4; + goto rti; + } break; - default: + case 47: // stmw + if ((addr % 4) != 0) { + uint32 ea = addr; + D(bug("WARNING: unaligned stmw to EA=%08x from IP=%08x\n", ea, r->pc())); + for (int i = rd; i <= 31; i++) { + WriteMacInt32(ea, r->gpr(i)); + ea += 4; + } + r->pc() += 4; + goto rti; + } break; +#endif } - - // Ignore ROM writes - if (transfer_type == TYPE_STORE && addr >= ROM_BASE && addr < ROM_BASE + ROM_SIZE) { -// D(bug("WARNING: %s write access to ROM at %08lx, pc %08lx\n", transfer_size == SIZE_BYTE ? "Byte" : transfer_size == SIZE_HALFWORD ? "Halfword" : "Word", addr, r->nip)); + + // Ignore ROM writes (including to the zero page, which is read-only) + if (transfer_type == TYPE_STORE && + ((addr >= ROMBase && addr < ROMBase + ROM_SIZE) || + (addr >= SheepMem::ZeroPage() && addr < SheepMem::ZeroPage() + SheepMem::PageSize()))) { +// D(bug("WARNING: %s write access to ROM at %08lx, pc %08lx\n", transfer_size == SIZE_BYTE ? "Byte" : transfer_size == SIZE_HALFWORD ? "Halfword" : "Word", addr, r->pc())); if (addr_mode == MODE_U || addr_mode == MODE_UX) - r->gpr[ra] = addr; - r->nip += 4; + r->gpr(ra) = addr; + r->pc() += 4; goto rti; } // Ignore illegal memory accesses? if (PrefsFindBool("ignoresegv")) { if (addr_mode == MODE_U || addr_mode == MODE_UX) - r->gpr[ra] = addr; + r->gpr(ra) = addr; if (transfer_type == TYPE_LOAD) - r->gpr[rd] = 0; - r->nip += 4; + r->gpr(rd) = 0; + r->pc() += 4; goto rti; } @@ -1512,9 +1926,9 @@ static void sigsegv_handler(int sig, sig if (!PrefsFindBool("nogui")) { char str[256]; if (transfer_type == TYPE_LOAD || transfer_type == TYPE_STORE) - sprintf(str, GetString(STR_MEM_ACCESS_ERR), transfer_size == SIZE_BYTE ? "byte" : transfer_size == SIZE_HALFWORD ? "halfword" : "word", transfer_type == TYPE_LOAD ? GetString(STR_MEM_ACCESS_READ) : GetString(STR_MEM_ACCESS_WRITE), addr, r->nip, r->gpr[24], r->gpr[1]); + sprintf(str, GetString(STR_MEM_ACCESS_ERR), transfer_size == SIZE_BYTE ? "byte" : transfer_size == SIZE_HALFWORD ? "halfword" : "word", transfer_type == TYPE_LOAD ? GetString(STR_MEM_ACCESS_READ) : GetString(STR_MEM_ACCESS_WRITE), addr, r->pc(), r->gpr(24), r->gpr(1)); else - sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->nip, r->gpr[24], r->gpr[1], opcode); + sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode); ErrorAlert(str); QuitEmulator(); return; @@ -1522,9 +1936,10 @@ static void sigsegv_handler(int sig, sig } // For all other errors, jump into debugger (sort of...) + crash_reason = (sig == SIGBUS) ? "SIGBUS" : "SIGSEGV"; if (!ready_for_signals) { - printf("SIGSEGV\n"); - printf(" sigcontext %p, pt_regs %p\n", sc, r); + printf("%s\n"); + printf(" sigcontext %p, machine_regs %p\n", scp, r); printf( " pc %08lx lr %08lx ctr %08lx msr %08lx\n" " xer %08lx cr %08lx \n" @@ -1536,22 +1951,23 @@ static void sigsegv_handler(int sig, sig " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n" " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n" " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n", - r->nip, r->link, r->ctr, r->msr, - r->xer, r->ccr, - r->gpr[0], r->gpr[1], r->gpr[2], r->gpr[3], - r->gpr[4], r->gpr[5], r->gpr[6], r->gpr[7], - r->gpr[8], r->gpr[9], r->gpr[10], r->gpr[11], - r->gpr[12], r->gpr[13], r->gpr[14], r->gpr[15], - r->gpr[16], r->gpr[17], r->gpr[18], r->gpr[19], - r->gpr[20], r->gpr[21], r->gpr[22], r->gpr[23], - r->gpr[24], r->gpr[25], r->gpr[26], r->gpr[27], - r->gpr[28], r->gpr[29], r->gpr[30], r->gpr[31]); + crash_reason, + r->pc(), r->lr(), r->ctr(), r->msr(), + r->xer(), r->cr(), + r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3), + r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7), + r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11), + r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15), + r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19), + r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23), + r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27), + r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31)); exit(1); QuitEmulator(); return; } else { // We crashed. Save registers, tell tick thread and loop forever - sigsegv_regs = *(sigregs *)r; + build_sigregs(&sigsegv_regs, r); emul_thread_fatal = true; for (;;) ; } @@ -1563,17 +1979,26 @@ rti:; * SIGILL handler */ -static void sigill_handler(int sig, sigcontext_struct *sc) +static void sigill_handler(int sig, siginfo_t *sip, void *scp) { - pt_regs *r = sc->regs; + machine_regs *r = MACHINE_REGISTERS(scp); char str[256]; +#ifdef SYSTEM_CLOBBERS_R2 + // Restore pointer to Thread Local Storage + set_r2(TOC); +#endif +#ifdef SYSTEM_CLOBBERS_R13 + // Restore pointer to .sdata section + set_r13(R13); +#endif + // Fault in Mac ROM or RAM? - bool mac_fault = (r->nip >= ROM_BASE) && (r->nip < (ROM_BASE + ROM_AREA_SIZE)) || (r->nip >= RAMBase) && (r->nip < (RAMBase + RAMSize)); + bool mac_fault = (r->pc() >= ROMBase) && (r->pc() < (ROMBase + ROM_AREA_SIZE)) || (r->pc() >= RAMBase) && (r->pc() < (RAMBase + RAMSize)); if (mac_fault) { // Get opcode and divide into fields - uint32 opcode = *((uint32 *)r->nip); + uint32 opcode = *((uint32 *)r->pc()); uint32 primop = opcode >> 26; uint32 exop = (opcode >> 1) & 0x3ff; uint32 ra = (opcode >> 16) & 0x1f; @@ -1584,7 +2009,7 @@ static void sigill_handler(int sig, sigc switch (primop) { case 9: // POWER instructions case 22: -power_inst: sprintf(str, GetString(STR_POWER_INSTRUCTION_ERR), r->nip, r->gpr[1], opcode); +power_inst: sprintf(str, GetString(STR_POWER_INSTRUCTION_ERR), r->pc(), r->gpr(1), opcode); ErrorAlert(str); QuitEmulator(); return; @@ -1592,14 +2017,14 @@ power_inst: sprintf(str, GetString(STR_ case 31: switch (exop) { case 83: // mfmsr - r->gpr[rd] = 0xf072; - r->nip += 4; + r->gpr(rd) = 0xf072; + r->pc() += 4; goto rti; case 210: // mtsr case 242: // mtsrin case 306: // tlbie - r->nip += 4; + r->pc() += 4; goto rti; case 339: { // mfspr @@ -1615,15 +2040,15 @@ power_inst: sprintf(str, GetString(STR_ case 957: // PMC3 case 958: // PMC4 case 959: // SDA - r->nip += 4; + r->pc() += 4; goto rti; case 25: // SDR1 - r->gpr[rd] = 0xdead001f; - r->nip += 4; + r->gpr(rd) = 0xdead001f; + r->pc() += 4; goto rti; case 287: // PVR - r->gpr[rd] = PVR; - r->nip += 4; + r->gpr(rd) = PVR; + r->pc() += 4; goto rti; } break; @@ -1659,7 +2084,7 @@ power_inst: sprintf(str, GetString(STR_ case 957: // PMC3 case 958: // PMC4 case 959: // SDA - r->nip += 4; + r->pc() += 4; goto rti; } break; @@ -1678,7 +2103,7 @@ power_inst: sprintf(str, GetString(STR_ // In GUI mode, show error alert if (!PrefsFindBool("nogui")) { - sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->nip, r->gpr[24], r->gpr[1], opcode); + sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode); ErrorAlert(str); QuitEmulator(); return; @@ -1686,9 +2111,10 @@ power_inst: sprintf(str, GetString(STR_ } // For all other errors, jump into debugger (sort of...) + crash_reason = "SIGILL"; if (!ready_for_signals) { - printf("SIGILL\n"); - printf(" sigcontext %p, pt_regs %p\n", sc, r); + printf("%s\n"); + printf(" sigcontext %p, machine_regs %p\n", scp, r); printf( " pc %08lx lr %08lx ctr %08lx msr %08lx\n" " xer %08lx cr %08lx \n" @@ -1700,22 +2126,23 @@ power_inst: sprintf(str, GetString(STR_ " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n" " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n" " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n", - r->nip, r->link, r->ctr, r->msr, - r->xer, r->ccr, - r->gpr[0], r->gpr[1], r->gpr[2], r->gpr[3], - r->gpr[4], r->gpr[5], r->gpr[6], r->gpr[7], - r->gpr[8], r->gpr[9], r->gpr[10], r->gpr[11], - r->gpr[12], r->gpr[13], r->gpr[14], r->gpr[15], - r->gpr[16], r->gpr[17], r->gpr[18], r->gpr[19], - r->gpr[20], r->gpr[21], r->gpr[22], r->gpr[23], - r->gpr[24], r->gpr[25], r->gpr[26], r->gpr[27], - r->gpr[28], r->gpr[29], r->gpr[30], r->gpr[31]); + crash_reason, + r->pc(), r->lr(), r->ctr(), r->msr(), + r->xer(), r->cr(), + r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3), + r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7), + r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11), + r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15), + r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19), + r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23), + r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27), + r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31)); exit(1); QuitEmulator(); return; } else { // We crashed. Save registers, tell tick thread and loop forever - sigsegv_regs = *(sigregs *)r; + build_sigregs(&sigsegv_regs, r); emul_thread_fatal = true; for (;;) ; } @@ -1725,6 +2152,52 @@ rti:; /* + * Helpers to share 32-bit addressable data with MacOS + */ + +bool SheepMem::Init(void) +{ + // Size of a native page + page_size = getpagesize(); + + // Allocate SheepShaver globals + proc = base; + if (vm_mac_acquire_fixed(base, size) < 0) + return false; + + // Allocate page with all bits set to 0, right in the middle + // This is also used to catch undesired overlaps between proc and data areas + zero_page = proc + (size / 2); + Mac_memset(zero_page, 0, page_size); + if (vm_protect(Mac2HostAddr(zero_page), page_size, VM_PAGE_READ) < 0) + return false; + +#if EMULATED_PPC + // Allocate alternate stack for PowerPC interrupt routine + sig_stack = base + size; + if (vm_mac_acquire_fixed(sig_stack, SIG_STACK_SIZE) < 0) + return false; +#endif + + data = base + size; + return true; +} + +void SheepMem::Exit(void) +{ + if (data) { + // Delete SheepShaver globals + vm_mac_release(base, size); + +#if EMULATED_PPC + // Delete alternate stack for PowerPC interrupt routine + vm_mac_release(sig_stack, SIG_STACK_SIZE); +#endif + } +} + + +/* * Display alert */ @@ -1773,7 +2246,12 @@ void display_alert(int title_id, int pre void ErrorAlert(const char *text) { -#ifdef ENABLE_GTK + if (gui_connection) { + if (rpc_method_invoke(gui_connection, RPC_METHOD_ERROR_ALERT, RPC_TYPE_STRING, text, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR && + rpc_method_wait_for_reply(gui_connection, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR) + return; + } +#if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO) if (PrefsFindBool("nogui") || x_display == NULL) { printf(GetString(STR_SHELL_ERROR_PREFIX), text); return; @@ -1792,7 +2270,12 @@ void ErrorAlert(const char *text) void WarningAlert(const char *text) { -#ifdef ENABLE_GTK + if (gui_connection) { + if (rpc_method_invoke(gui_connection, RPC_METHOD_WARNING_ALERT, RPC_TYPE_STRING, text, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR && + rpc_method_wait_for_reply(gui_connection, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR) + return; + } +#if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO) if (PrefsFindBool("nogui") || x_display == NULL) { printf(GetString(STR_SHELL_WARNING_PREFIX), text); return;