ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/main_unix.cpp
Revision: 1.86
Committed: 2009-08-18T18:26:10Z (14 years, 9 months ago) by asvitkine
Branch: MAIN
Changes since 1.85: +80 -47 lines
Log Message:
[Michael Schmitt]
Attached is a patch to SheepShaver to fix memory allocation problems when OS X 10.5 is the host. It also relaxes the 512 MB RAM limit on OS X hosts.


Problem
-------
Some users have been unable to run SheepShaver on OS X 10.5 (Leopard) hosts. The symptom is error "ERROR: Cannot map RAM: File already exists".

SheepShaver allocates RAM at fixed addresses. If it is running in "Real" addressing mode, and can't allocate at address 0, then it was hard-coded to allocate the RAM area at 0x20000000. The ROM area as allocated at 0x40800000.

The normal configuration is for SheepShaver to run under SDL, which is a Cocoa wrapper. By the time SheepShaver does its memory allocations, the Cocoa application has already started. The result is the SheepShaver memory address space already contains libraries, fonts, Input Managers, and IOKit areas.

On Leopard hosts these areas can land on the same addresses SheepShaver needs, so SheepShaver's memory allocation fails.


Solution
--------
The approach is to change SheepShaver (on Unix & OS X hosts) to allocate the RAM area anywhere it can find the space, rather than at a fixed address.

This could result in the RAM allocated higher than the ROM area, which causes a crash. To prevent this from occurring, the RAM and ROM areas are allocated contiguously.

Previously the ROM starting address was a constant ROM_BASE, which was used throughout the source files. The ROM start address is now a variable ROMBase. ROMBase is allocated and set by main_*.cpp just like RAMBase.

A side-effect of this change is that it lifts the 512 MB RAM limit for OS X hosts. The limit was because the fixed RAM and ROM addresses were such that the RAM could only be 512 MB before it overlapped the ROM area.


Impact
------
The change to make ROMBase a variable is throughout all hosts & addressing modes.

The RAM and ROM areas will only shift when run on Unix & OS X hosts, otherwise the same fixed allocation address is used as before.

This change is limited to "Real" addressing mode. Unlike Basilisk II, SheepShaver *pre-calculates* the offset for "Direct" addressing mode; the offset is compiled into the program. If the RAM address were allowed to shift, it could result in the RAM area wrapping around address 0.


Changes to main_unix.cpp
------------------------
1. Real addressing mode no longer defines a RAM_BASE constant.

2. The base address of the Mac ROM (ROMBase) is defined and exported by this program.

3. Memory management helper vm_mac_acquire is renamed to vm_mac_acquire_fixed. Added a new memory management helper vm_mac_acquire, which allocates memory at any address.

4. Changed and rearranged the allocation of RAM and ROM areas.

Before it worked like this:

  - Allocate ROM area
  - If can, attempt to allocate RAM at address zero
  - If RAM not allocated at 0, allocate at fixed address

We still want to try allocating the RAM at zero, and if using DIRECT addressing we're still going to use the fixed addresses. So we don't know where the ROM should be until after we do the RAM. The new logic is:

  - If can, attempt to allocate RAM at address zero
  - If RAM not allocated at 0
      if REAL addressing
         allocate RAM and ROM together. The ROM address is aligned to a 1 MB boundary
      else (direct addressing)
         allocate RAM at fixed address
  - If ROM hasn't been allocated yet, allocate at fixed address

5. Calculate ROMBase and ROMBaseHost based on where the ROM was loaded.

6. There is a crash if the RAM is allocated too high. To try and catch this, check if it was allocated higher than the kernel data address.

7. Change subsequent code from using constant ROM_BASE to variable ROMBase.


Changes to Other Programs
-------------------------
emul_op.cpp, main.cpp, name_registery.cpp, rom_patches.cpp, rsrc_patches.cpp, emul_ppc.cpp, sheepshaver_glue.cpp, ppc-translate-cpp:
Change from constant ROM_BASE to variable ROMBase.

ppc_asm.S: It was setting register to a hard-coded literal address: 0x40b0d000. Changed to set it to ROMBase + 0x30d000.

ppc_asm.tmpl: It defined a macro ASM_LO16 but it assumed that the macro would always be used with operands that included a register specification. This is not true. Moved the register specification from the macro to the macro invocations.

main_beos.cpp, main_windows.cpp: Since the subprograms are all expecting a variable ROMBase, all the main_*.cpp pgrams have to define and export it. The ROM_BASE constant is moved here for consistency. The mains for beos and windows just allocate the ROM at the same fixed address as before, set ROMBaseHost and ROMBase to that address, and then use ROMBase for the subsequent code.

cpu_emulation.h: removed ROM_BASE constant. This value is moved to the main_*.cpp modules, to be consistent with RAM_BASE.

user_strings_unix.cpp, user_strings_unix.h: Added new error messages related to errors that occur when the RAM and ROM are allocated anywhere.

File Contents

# Content
1 /*
2 * main_unix.cpp - Emulation core, Unix implementation
3 *
4 * SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * NOTES:
23 *
24 * See main_beos.cpp for a description of the three operating modes.
25 *
26 * In addition to that, we have to handle the fact that the MacOS ABI
27 * is slightly different from the SysV ABI used by Linux:
28 * - Stack frames are different (e.g. LR is stored in 8(r1) under
29 * MacOS, but in 4(r1) under Linux)
30 * - There is a pointer to Thread Local Storage (TLS) under Linux with
31 * recent enough glibc. This is r2 in 32-bit mode and r13 in
32 * 64-bit mode (PowerOpen/AIX ABI)
33 * - r13 is used as a small data pointer under Linux (but appearently
34 * it is not used this way? To be sure, we specify -msdata=none
35 * in the Makefile)
36 * - There are no TVECTs under Linux; function pointers point
37 * directly to the function code
38 * The Execute*() functions have to account for this. Additionally, we
39 * cannot simply call MacOS functions by getting their TVECT and jumping
40 * to it. Such calls are done via the call_macos*() functions in
41 * asm_linux.S that create a MacOS stack frame, load the TOC pointer
42 * and put the arguments into the right registers.
43 *
44 * As on the BeOS, we have to specify an alternate signal stack because
45 * interrupts (and, under Linux, Low Memory accesses) may occur when r1
46 * is pointing to the Kernel Data or to Low Memory. There is one
47 * problem, however, due to the alternate signal stack being global to
48 * all signal handlers. Consider the following scenario:
49 * - The main thread is executing some native PPC MacOS code in
50 * MODE_NATIVE, running on the MacOS stack (somewhere in the Mac RAM).
51 * - A SIGUSR2 interrupt occurs. The kernel switches to the signal
52 * stack and starts executing the SIGUSR2 signal handler.
53 * - The signal handler sees the MODE_NATIVE and calls ppc_interrupt()
54 * to handle a native interrupt.
55 * - ppc_interrupt() sets r1 to point to the Kernel Data and jumps to
56 * the nanokernel.
57 * - The nanokernel accesses a Low Memory global (most likely one of
58 * the XLMs), a SIGSEGV occurs.
59 * - The kernel sees that r1 does not point to the signal stack and
60 * switches to the signal stack again, thus overwriting the data that
61 * the SIGUSR2 handler put there.
62 * The same problem arises when calling ExecutePPC() inside the MODE_EMUL_OP
63 * interrupt handler.
64 *
65 * The solution is to set the signal stack to a second, "extra" stack
66 * inside the SIGUSR2 handler before entering the Nanokernel or calling
67 * ExecutePPC (or any function that might cause a mode switch). The signal
68 * stack is restored before exiting the SIGUSR2 handler.
69 *
70 * Note that POSIX standard says you can't modify the alternate
71 * signal stack while the process is executing on it. There is a
72 * hackaround though: we install a trampoline SIGUSR2 handler that
73 * sets up an alternate stack itself and calls the real handler.
74 * Then, when we call sigaltstack() there, we no longer get an EPERM,
75 * i.e. it now works.
76 *
77 * TODO:
78 * check if SIGSEGV handler works for all registers (including FP!)
79 */
80
81 #include <unistd.h>
82 #include <fcntl.h>
83 #include <time.h>
84 #include <errno.h>
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <string.h>
88 #include <pthread.h>
89 #include <sys/mman.h>
90 #include <sys/ipc.h>
91 #include <sys/shm.h>
92 #include <sys/stat.h>
93 #include <signal.h>
94
95 #include "sysdeps.h"
96 #include "main.h"
97 #include "version.h"
98 #include "prefs.h"
99 #include "prefs_editor.h"
100 #include "cpu_emulation.h"
101 #include "emul_op.h"
102 #include "xlowmem.h"
103 #include "xpram.h"
104 #include "timer.h"
105 #include "adb.h"
106 #include "video.h"
107 #include "sys.h"
108 #include "macos_util.h"
109 #include "rom_patches.h"
110 #include "user_strings.h"
111 #include "vm_alloc.h"
112 #include "sigsegv.h"
113 #include "sigregs.h"
114 #include "rpc.h"
115
116 #define DEBUG 0
117 #include "debug.h"
118
119
120 #ifdef HAVE_DIRENT_H
121 #include <dirent.h>
122 #endif
123
124 #ifdef USE_SDL
125 #include <SDL.h>
126 #endif
127
128 #ifndef USE_SDL_VIDEO
129 #include <X11/Xlib.h>
130 #endif
131
132 #ifdef ENABLE_GTK
133 #include <gtk/gtk.h>
134 #endif
135
136 #ifdef ENABLE_XF86_DGA
137 #include <X11/Xlib.h>
138 #include <X11/Xutil.h>
139 #include <X11/extensions/xf86dga.h>
140 #endif
141
142 #ifdef ENABLE_MON
143 #include "mon.h"
144 #endif
145
146
147 // Enable emulation of unaligned lmw/stmw?
148 #define EMULATE_UNALIGNED_LOADSTORE_MULTIPLE 1
149
150 // Enable Execute68k() safety checks?
151 #define SAFE_EXEC_68K 0
152
153 // Interrupts in EMUL_OP mode?
154 #define INTERRUPTS_IN_EMUL_OP_MODE 1
155
156 // Interrupts in native mode?
157 #define INTERRUPTS_IN_NATIVE_MODE 1
158
159
160 // Constants
161 const char ROM_FILE_NAME[] = "ROM";
162 const char ROM_FILE_NAME2[] = "Mac OS ROM";
163
164 #if !REAL_ADDRESSING
165 // FIXME: needs to be >= 0x04000000
166 const uintptr RAM_BASE = 0x10000000; // Base address of RAM
167 #endif
168 const uintptr ROM_BASE = 0x40800000; // Base address of ROM
169 #if REAL_ADDRESSING
170 const uint32 ROM_ALIGNMENT = 0x100000; // ROM must be aligned to a 1MB boundary
171 #endif
172 const uint32 SIG_STACK_SIZE = 0x10000; // Size of signal stack
173
174
175 // Global variables (exported)
176 #if !EMULATED_PPC
177 void *TOC = NULL; // Pointer to Thread Local Storage (r2)
178 void *R13 = NULL; // Pointer to .sdata section (r13 under Linux)
179 #endif
180 uint32 RAMBase; // Base address of Mac RAM
181 uint32 RAMSize; // Size of Mac RAM
182 uint32 ROMBase; // Base address of Mac ROM
183 uint32 KernelDataAddr; // Address of Kernel Data
184 uint32 BootGlobsAddr; // Address of BootGlobs structure at top of Mac RAM
185 uint32 DRCacheAddr; // Address of DR Cache
186 uint32 PVR; // Theoretical PVR
187 int64 CPUClockSpeed; // Processor clock speed (Hz)
188 int64 BusClockSpeed; // Bus clock speed (Hz)
189 int64 TimebaseSpeed; // Timebase clock speed (Hz)
190 uint8 *RAMBaseHost; // Base address of Mac RAM (host address space)
191 uint8 *ROMBaseHost; // Base address of Mac ROM (host address space)
192
193
194 // Global variables
195 #ifndef USE_SDL_VIDEO
196 char *x_display_name = NULL; // X11 display name
197 Display *x_display = NULL; // X11 display handle
198 #ifdef X11_LOCK_TYPE
199 X11_LOCK_TYPE x_display_lock = X11_LOCK_INIT; // X11 display lock
200 #endif
201 #endif
202
203 static int zero_fd = 0; // FD of /dev/zero
204 static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped
205 static int kernel_area = -1; // SHM ID of Kernel Data area
206 static bool rom_area_mapped = false; // Flag: Mac ROM mmap()ped
207 static bool ram_area_mapped = false; // Flag: Mac RAM mmap()ped
208 static bool dr_cache_area_mapped = false; // Flag: Mac DR Cache mmap()ped
209 static bool dr_emulator_area_mapped = false;// Flag: Mac DR Emulator mmap()ped
210 static KernelData *kernel_data; // Pointer to Kernel Data
211 static EmulatorData *emulator_data;
212
213 static uint8 last_xpram[XPRAM_SIZE]; // Buffer for monitoring XPRAM changes
214
215 static bool nvram_thread_active = false; // Flag: NVRAM watchdog installed
216 static volatile bool nvram_thread_cancel; // Flag: Cancel NVRAM thread
217 static pthread_t nvram_thread; // NVRAM watchdog
218 static bool tick_thread_active = false; // Flag: MacOS thread installed
219 static volatile bool tick_thread_cancel; // Flag: Cancel 60Hz thread
220 static pthread_t tick_thread; // 60Hz thread
221 static pthread_t emul_thread; // MacOS thread
222
223 static bool ready_for_signals = false; // Handler installed, signals can be sent
224 static int64 num_segv = 0; // Number of handled SEGV signals
225
226 static struct sigaction sigusr2_action; // Interrupt signal (of emulator thread)
227 #if EMULATED_PPC
228 static uintptr sig_stack = 0; // Stack for PowerPC interrupt routine
229 #else
230 static struct sigaction sigsegv_action; // Data access exception signal (of emulator thread)
231 static struct sigaction sigill_action; // Illegal instruction signal (of emulator thread)
232 static stack_t sig_stack; // Stack for signal handlers
233 static stack_t extra_stack; // Stack for SIGSEGV inside interrupt handler
234 static bool emul_thread_fatal = false; // Flag: MacOS thread crashed, tick thread shall dump debug output
235 static sigregs sigsegv_regs; // Register dump when crashed
236 static const char *crash_reason = NULL; // Reason of the crash (SIGSEGV, SIGBUS, SIGILL)
237 #endif
238
239 static rpc_connection_t *gui_connection = NULL; // RPC connection to the GUI
240 static const char *gui_connection_path = NULL; // GUI connection identifier
241
242 uint32 SheepMem::page_size; // Size of a native page
243 uintptr SheepMem::zero_page = 0; // Address of ro page filled in with zeros
244 uintptr SheepMem::base = 0x60000000; // Address of SheepShaver data
245 uintptr SheepMem::proc; // Bottom address of SheepShave procedures
246 uintptr SheepMem::data; // Top of SheepShaver data (stack like storage)
247
248
249 // Prototypes
250 static bool kernel_data_init(void);
251 static void kernel_data_exit(void);
252 static void Quit(void);
253 static void *emul_func(void *arg);
254 static void *nvram_func(void *arg);
255 static void *tick_func(void *arg);
256 #if EMULATED_PPC
257 extern void emul_ppc(uint32 start);
258 extern void init_emul_ppc(void);
259 extern void exit_emul_ppc(void);
260 sigsegv_return_t sigsegv_handler(sigsegv_info_t *sip);
261 #else
262 extern "C" void sigusr2_handler_init(int sig, siginfo_t *sip, void *scp);
263 extern "C" void sigusr2_handler(int sig, siginfo_t *sip, void *scp);
264 static void sigsegv_handler(int sig, siginfo_t *sip, void *scp);
265 static void sigill_handler(int sig, siginfo_t *sip, void *scp);
266 #endif
267
268
269 // From asm_linux.S
270 #if !EMULATED_PPC
271 extern "C" void *get_sp(void);
272 extern "C" void *get_r2(void);
273 extern "C" void set_r2(void *);
274 extern "C" void *get_r13(void);
275 extern "C" void set_r13(void *);
276 extern "C" void flush_icache_range(uint32 start, uint32 end);
277 extern "C" void jump_to_rom(uint32 entry, uint32 context);
278 extern "C" void quit_emulator(void);
279 extern "C" void execute_68k(uint32 pc, M68kRegisters *r);
280 extern "C" void ppc_interrupt(uint32 entry, uint32 kernel_data);
281 extern "C" int atomic_add(int *var, int v);
282 extern "C" int atomic_and(int *var, int v);
283 extern "C" int atomic_or(int *var, int v);
284 extern void paranoia_check(void);
285 #endif
286
287
288 #if EMULATED_PPC
289 /*
290 * Return signal stack base
291 */
292
293 uintptr SignalStackBase(void)
294 {
295 return sig_stack + SIG_STACK_SIZE;
296 }
297
298
299 /*
300 * Atomic operations
301 */
302
303 #if HAVE_SPINLOCKS
304 static spinlock_t atomic_ops_lock = SPIN_LOCK_UNLOCKED;
305 #else
306 #define spin_lock(LOCK)
307 #define spin_unlock(LOCK)
308 #endif
309
310 int atomic_add(int *var, int v)
311 {
312 spin_lock(&atomic_ops_lock);
313 int ret = *var;
314 *var += v;
315 spin_unlock(&atomic_ops_lock);
316 return ret;
317 }
318
319 int atomic_and(int *var, int v)
320 {
321 spin_lock(&atomic_ops_lock);
322 int ret = *var;
323 *var &= v;
324 spin_unlock(&atomic_ops_lock);
325 return ret;
326 }
327
328 int atomic_or(int *var, int v)
329 {
330 spin_lock(&atomic_ops_lock);
331 int ret = *var;
332 *var |= v;
333 spin_unlock(&atomic_ops_lock);
334 return ret;
335 }
336 #endif
337
338
339 /*
340 * Memory management helpers
341 */
342
343 static inline uint8 *vm_mac_acquire(uint32 size)
344 {
345 return (uint8 *)vm_acquire(size);
346 }
347
348 static inline int vm_mac_acquire_fixed(uint32 addr, uint32 size)
349 {
350 return vm_acquire_fixed(Mac2HostAddr(addr), size);
351 }
352
353 static inline int vm_mac_release(uint32 addr, uint32 size)
354 {
355 return vm_release(Mac2HostAddr(addr), size);
356 }
357
358
359 /*
360 * Main program
361 */
362
363 static void usage(const char *prg_name)
364 {
365 printf("Usage: %s [OPTION...]\n", prg_name);
366 printf("\nUnix options:\n");
367 printf(" --display STRING\n X display to use\n");
368 PrefsPrintUsage();
369 exit(0);
370 }
371
372 static bool valid_vmdir(const char *path)
373 {
374 const int suffix_len = sizeof(".sheepvm") - 1;
375 int len = strlen(path);
376 if (len && path[len - 1] == '/') // to support both ".sheepvm" and ".sheepvm/"
377 len--;
378 if (len > suffix_len && !strncmp(path + len - suffix_len, ".sheepvm", suffix_len)) {
379 struct stat d;
380 if (!stat(path, &d) && S_ISDIR(d.st_mode)) {
381 return true;
382 }
383 }
384 return false;
385 }
386
387 int main(int argc, char **argv)
388 {
389 char str[256];
390 int rom_fd;
391 FILE *proc_file;
392 const char *rom_path;
393 uint32 rom_size, actual;
394 uint8 *rom_tmp;
395 time_t now, expire;
396 bool memory_mapped_from_zero, ram_rom_areas_contiguous;
397 const char *vmdir = NULL;
398
399 #ifdef USE_SDL_VIDEO
400 // Don't let SDL block the screensaver
401 putenv("SDL_VIDEO_ALLOW_SCREENSAVER=1");
402
403 // Make SDL pass through command-clicks and option-clicks unaltered
404 putenv("SDL_HAS3BUTTONMOUSE=1");
405 #endif
406
407 // Initialize variables
408 RAMBase = 0;
409 tzset();
410
411 // Print some info
412 printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR);
413 printf(" %s\n", GetString(STR_ABOUT_TEXT2));
414
415 #if !EMULATED_PPC
416 #ifdef SYSTEM_CLOBBERS_R2
417 // Get TOC pointer
418 TOC = get_r2();
419 #endif
420 #ifdef SYSTEM_CLOBBERS_R13
421 // Get r13 register
422 R13 = get_r13();
423 #endif
424 #endif
425
426 // Parse command line arguments
427 for (int i=1; i<argc; i++) {
428 if (strcmp(argv[i], "--help") == 0) {
429 usage(argv[0]);
430 #ifndef USE_SDL_VIDEO
431 } else if (strcmp(argv[i], "--display") == 0) {
432 i++;
433 if (i < argc)
434 x_display_name = strdup(argv[i]);
435 #endif
436 } else if (strcmp(argv[i], "--gui-connection") == 0) {
437 argv[i++] = NULL;
438 if (i < argc) {
439 gui_connection_path = argv[i];
440 argv[i] = NULL;
441 }
442 } else if (valid_vmdir(argv[i])) {
443 vmdir = argv[i];
444 argv[i] = NULL;
445 printf("Using %s as vmdir.\n", vmdir);
446 if (chdir(vmdir)) {
447 printf("Failed to chdir to %s. Good bye.", vmdir);
448 exit(1);
449 }
450 break;
451 }
452 }
453
454 // Remove processed arguments
455 for (int i=1; i<argc; i++) {
456 int k;
457 for (k=i; k<argc; k++)
458 if (argv[k] != NULL)
459 break;
460 if (k > i) {
461 k -= i;
462 for (int j=i+k; j<argc; j++)
463 argv[j-k] = argv[j];
464 argc -= k;
465 }
466 }
467
468 // Connect to the external GUI
469 if (gui_connection_path) {
470 if ((gui_connection = rpc_init_client(gui_connection_path)) == NULL) {
471 fprintf(stderr, "Failed to initialize RPC client connection to the GUI\n");
472 return 1;
473 }
474 }
475
476 #ifdef ENABLE_GTK
477 if (!gui_connection) {
478 // Init GTK
479 gtk_set_locale();
480 gtk_init(&argc, &argv);
481 }
482 #endif
483
484 // Read preferences
485 PrefsInit(vmdir, argc, argv);
486
487 // Any command line arguments left?
488 for (int i=1; i<argc; i++) {
489 if (argv[i][0] == '-') {
490 fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
491 usage(argv[0]);
492 }
493 }
494
495 #ifdef USE_SDL
496 // Initialize SDL system
497 int sdl_flags = 0;
498 #ifdef USE_SDL_VIDEO
499 sdl_flags |= SDL_INIT_VIDEO;
500 #endif
501 #ifdef USE_SDL_AUDIO
502 sdl_flags |= SDL_INIT_AUDIO;
503 #endif
504 assert(sdl_flags != 0);
505 if (SDL_Init(sdl_flags) == -1) {
506 char str[256];
507 sprintf(str, "Could not initialize SDL: %s.\n", SDL_GetError());
508 ErrorAlert(str);
509 goto quit;
510 }
511 atexit(SDL_Quit);
512
513 // Don't let SDL catch SIGINT and SIGTERM signals
514 signal(SIGINT, SIG_DFL);
515 signal(SIGTERM, SIG_DFL);
516 #endif
517
518 #ifndef USE_SDL_VIDEO
519 // Open display
520 x_display = XOpenDisplay(x_display_name);
521 if (x_display == NULL) {
522 char str[256];
523 sprintf(str, GetString(STR_NO_XSERVER_ERR), XDisplayName(x_display_name));
524 ErrorAlert(str);
525 goto quit;
526 }
527
528 #if defined(ENABLE_XF86_DGA) && !defined(ENABLE_MON)
529 // Fork out, so we can return from fullscreen mode when things get ugly
530 XF86DGAForkApp(DefaultScreen(x_display));
531 #endif
532 #endif
533
534 #ifdef ENABLE_MON
535 // Initialize mon
536 mon_init();
537 #endif
538
539 #if !EMULATED_PPC
540 // Create and install stacks for signal handlers
541 sig_stack.ss_sp = malloc(SIG_STACK_SIZE);
542 D(bug("Signal stack at %p\n", sig_stack.ss_sp));
543 if (sig_stack.ss_sp == NULL) {
544 ErrorAlert(GetString(STR_NOT_ENOUGH_MEMORY_ERR));
545 goto quit;
546 }
547 sig_stack.ss_flags = 0;
548 sig_stack.ss_size = SIG_STACK_SIZE;
549 if (sigaltstack(&sig_stack, NULL) < 0) {
550 sprintf(str, GetString(STR_SIGALTSTACK_ERR), strerror(errno));
551 ErrorAlert(str);
552 goto quit;
553 }
554 extra_stack.ss_sp = malloc(SIG_STACK_SIZE);
555 D(bug("Extra stack at %p\n", extra_stack.ss_sp));
556 if (extra_stack.ss_sp == NULL) {
557 ErrorAlert(GetString(STR_NOT_ENOUGH_MEMORY_ERR));
558 goto quit;
559 }
560 extra_stack.ss_flags = 0;
561 extra_stack.ss_size = SIG_STACK_SIZE;
562 #endif
563
564 #if !EMULATED_PPC
565 // Install SIGSEGV and SIGBUS handlers
566 sigemptyset(&sigsegv_action.sa_mask); // Block interrupts during SEGV handling
567 sigaddset(&sigsegv_action.sa_mask, SIGUSR2);
568 sigsegv_action.sa_sigaction = sigsegv_handler;
569 sigsegv_action.sa_flags = SA_ONSTACK | SA_SIGINFO;
570 #ifdef HAVE_SIGNAL_SA_RESTORER
571 sigsegv_action.sa_restorer = NULL;
572 #endif
573 if (sigaction(SIGSEGV, &sigsegv_action, NULL) < 0) {
574 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGSEGV", strerror(errno));
575 ErrorAlert(str);
576 goto quit;
577 }
578 if (sigaction(SIGBUS, &sigsegv_action, NULL) < 0) {
579 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGBUS", strerror(errno));
580 ErrorAlert(str);
581 goto quit;
582 }
583 #else
584 // Install SIGSEGV handler for CPU emulator
585 if (!sigsegv_install_handler(sigsegv_handler)) {
586 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGSEGV", strerror(errno));
587 ErrorAlert(str);
588 goto quit;
589 }
590 #endif
591
592 // Initialize VM system
593 vm_init();
594
595 // Get system info
596 PVR = 0x00040000; // Default: 604
597 CPUClockSpeed = 100000000; // Default: 100MHz
598 BusClockSpeed = 100000000; // Default: 100MHz
599 TimebaseSpeed = 25000000; // Default: 25MHz
600 #if EMULATED_PPC
601 PVR = 0x000c0000; // Default: 7400 (with AltiVec)
602 #elif defined(__APPLE__) && defined(__MACH__)
603 proc_file = popen("ioreg -c IOPlatformDevice", "r");
604 if (proc_file) {
605 char line[256];
606 bool powerpc_node = false;
607 while (fgets(line, sizeof(line) - 1, proc_file)) {
608 // Read line
609 int len = strlen(line);
610 if (len == 0)
611 continue;
612 line[len - 1] = 0;
613
614 // Parse line
615 if (strstr(line, "o PowerPC,"))
616 powerpc_node = true;
617 else if (powerpc_node) {
618 uint32 value;
619 char head[256];
620 if (sscanf(line, "%[ |]\"cpu-version\" = <%x>", head, &value) == 2)
621 PVR = value;
622 else if (sscanf(line, "%[ |]\"clock-frequency\" = <%x>", head, &value) == 2)
623 CPUClockSpeed = value;
624 else if (sscanf(line, "%[ |]\"bus-frequency\" = <%x>", head, &value) == 2)
625 BusClockSpeed = value;
626 else if (sscanf(line, "%[ |]\"timebase-frequency\" = <%x>", head, &value) == 2)
627 TimebaseSpeed = value;
628 else if (strchr(line, '}'))
629 powerpc_node = false;
630 }
631 }
632 fclose(proc_file);
633 } else {
634 sprintf(str, GetString(STR_PROC_CPUINFO_WARN), strerror(errno));
635 WarningAlert(str);
636 }
637 #else
638 proc_file = fopen("/proc/cpuinfo", "r");
639 if (proc_file) {
640 // CPU specs from Linux kernel
641 // TODO: make it more generic with features (e.g. AltiVec) and
642 // cache information and friends for NameRegistry
643 static const struct {
644 uint32 pvr_mask;
645 uint32 pvr_value;
646 const char *cpu_name;
647 }
648 cpu_specs[] = {
649 { 0xffff0000, 0x00010000, "601" },
650 { 0xffff0000, 0x00030000, "603" },
651 { 0xffff0000, 0x00060000, "603e" },
652 { 0xffff0000, 0x00070000, "603ev" },
653 { 0xffff0000, 0x00040000, "604" },
654 { 0xfffff000, 0x00090000, "604e" },
655 { 0xffff0000, 0x00090000, "604r" },
656 { 0xffff0000, 0x000a0000, "604ev" },
657 { 0xffffffff, 0x00084202, "740/750" },
658 { 0xfffff000, 0x00083000, "745/755" },
659 { 0xfffffff0, 0x00080100, "750CX" },
660 { 0xfffffff0, 0x00082200, "750CX" },
661 { 0xfffffff0, 0x00082210, "750CXe" },
662 { 0xffffff00, 0x70000100, "750FX" },
663 { 0xffffffff, 0x70000200, "750FX" },
664 { 0xffff0000, 0x70000000, "750FX" },
665 { 0xffff0000, 0x70020000, "750GX" },
666 { 0xffff0000, 0x00080000, "740/750" },
667 { 0xffffffff, 0x000c1101, "7400 (1.1)" },
668 { 0xffff0000, 0x000c0000, "7400" },
669 { 0xffff0000, 0x800c0000, "7410" },
670 { 0xffffffff, 0x80000200, "7450" },
671 { 0xffffffff, 0x80000201, "7450" },
672 { 0xffff0000, 0x80000000, "7450" },
673 { 0xffffff00, 0x80010100, "7455" },
674 { 0xffffffff, 0x80010200, "7455" },
675 { 0xffff0000, 0x80010000, "7455" },
676 { 0xffff0000, 0x80020000, "7457" },
677 { 0xffff0000, 0x80030000, "7447A" },
678 { 0xffff0000, 0x80040000, "7448" },
679 { 0x7fff0000, 0x00810000, "82xx" },
680 { 0x7fff0000, 0x00820000, "8280" },
681 { 0xffff0000, 0x00400000, "Power3 (630)" },
682 { 0xffff0000, 0x00410000, "Power3 (630+)" },
683 { 0xffff0000, 0x00360000, "I-star" },
684 { 0xffff0000, 0x00370000, "S-star" },
685 { 0xffff0000, 0x00350000, "Power4" },
686 { 0xffff0000, 0x00390000, "PPC970" },
687 { 0xffff0000, 0x003c0000, "PPC970FX" },
688 { 0xffff0000, 0x003a0000, "POWER5 (gr)" },
689 { 0xffff0000, 0x003b0000, "POWER5+ (gs)" },
690 { 0xffff0000, 0x003e0000, "POWER6" },
691 { 0xffff0000, 0x00700000, "Cell Broadband Engine" },
692 { 0x7fff0000, 0x00900000, "PA6T" },
693 { 0, 0, 0 }
694 };
695
696 char line[256];
697 while(fgets(line, 255, proc_file)) {
698 // Read line
699 int len = strlen(line);
700 if (len == 0)
701 continue;
702 line[len-1] = 0;
703
704 // Parse line
705 int i;
706 float f;
707 char value[256];
708 if (sscanf(line, "cpu : %[^,]", value) == 1) {
709 // Search by name
710 const char *cpu_name = NULL;
711 for (int i = 0; cpu_specs[i].pvr_mask != 0; i++) {
712 if (strcmp(cpu_specs[i].cpu_name, value) == 0) {
713 cpu_name = cpu_specs[i].cpu_name;
714 PVR = cpu_specs[i].pvr_value;
715 break;
716 }
717 }
718 if (cpu_name == NULL)
719 printf("WARNING: Unknown CPU type '%s', assuming 604\n", value);
720 else
721 printf("Found a PowerPC %s processor\n", cpu_name);
722 }
723 if (sscanf(line, "clock : %fMHz", &f) == 1)
724 CPUClockSpeed = BusClockSpeed = ((int64)f) * 1000000;
725 else if (sscanf(line, "clock : %dMHz", &i) == 1)
726 CPUClockSpeed = BusClockSpeed = i * 1000000;
727 }
728 fclose(proc_file);
729 } else {
730 sprintf(str, GetString(STR_PROC_CPUINFO_WARN), strerror(errno));
731 WarningAlert(str);
732 }
733
734 // Get actual bus frequency
735 proc_file = fopen("/proc/device-tree/clock-frequency", "r");
736 if (proc_file) {
737 union { uint8 b[4]; uint32 l; } value;
738 if (fread(value.b, sizeof(value), 1, proc_file) == 1)
739 BusClockSpeed = value.l;
740 fclose(proc_file);
741 }
742
743 // Get actual timebase frequency
744 TimebaseSpeed = BusClockSpeed / 4;
745 DIR *cpus_dir;
746 if ((cpus_dir = opendir("/proc/device-tree/cpus")) != NULL) {
747 struct dirent *cpu_entry;
748 while ((cpu_entry = readdir(cpus_dir)) != NULL) {
749 if (strstr(cpu_entry->d_name, "PowerPC,") == cpu_entry->d_name) {
750 char timebase_freq_node[256];
751 sprintf(timebase_freq_node, "/proc/device-tree/cpus/%s/timebase-frequency", cpu_entry->d_name);
752 proc_file = fopen(timebase_freq_node, "r");
753 if (proc_file) {
754 union { uint8 b[4]; uint32 l; } value;
755 if (fread(value.b, sizeof(value), 1, proc_file) == 1)
756 TimebaseSpeed = value.l;
757 fclose(proc_file);
758 }
759 }
760 }
761 closedir(cpus_dir);
762 }
763 #endif
764 // Remap any newer G4/G5 processor to plain G4 for compatibility
765 switch (PVR >> 16) {
766 case 0x8000: // 7450
767 case 0x8001: // 7455
768 case 0x8002: // 7457
769 case 0x8003: // 7447A
770 case 0x8004: // 7448
771 case 0x0039: // 970
772 case 0x003c: // 970FX
773 PVR = 0x000c0000; // 7400
774 break;
775 }
776 D(bug("PVR: %08x (assumed)\n", PVR));
777
778 // Init system routines
779 SysInit();
780
781 // Show preferences editor
782 if (!PrefsFindBool("nogui"))
783 if (!PrefsEditor())
784 goto quit;
785
786 #if !EMULATED_PPC
787 // Check some things
788 paranoia_check();
789 #endif
790
791 // Open /dev/zero
792 zero_fd = open("/dev/zero", O_RDWR);
793 if (zero_fd < 0) {
794 sprintf(str, GetString(STR_NO_DEV_ZERO_ERR), strerror(errno));
795 ErrorAlert(str);
796 goto quit;
797 }
798
799 // Create areas for Kernel Data
800 if (!kernel_data_init())
801 goto quit;
802 kernel_data = (KernelData *)Mac2HostAddr(KERNEL_DATA_BASE);
803 emulator_data = &kernel_data->ed;
804 KernelDataAddr = KERNEL_DATA_BASE;
805 D(bug("Kernel Data at %p (%08x)\n", kernel_data, KERNEL_DATA_BASE));
806 D(bug("Emulator Data at %p (%08x)\n", emulator_data, KERNEL_DATA_BASE + offsetof(KernelData, ed)));
807
808 // Create area for DR Cache
809 if (vm_mac_acquire_fixed(DR_EMULATOR_BASE, DR_EMULATOR_SIZE) < 0) {
810 sprintf(str, GetString(STR_DR_EMULATOR_MMAP_ERR), strerror(errno));
811 ErrorAlert(str);
812 goto quit;
813 }
814 dr_emulator_area_mapped = true;
815 if (vm_mac_acquire_fixed(DR_CACHE_BASE, DR_CACHE_SIZE) < 0) {
816 sprintf(str, GetString(STR_DR_CACHE_MMAP_ERR), strerror(errno));
817 ErrorAlert(str);
818 goto quit;
819 }
820 dr_cache_area_mapped = true;
821 #if !EMULATED_PPC
822 if (vm_protect((char *)DR_CACHE_BASE, DR_CACHE_SIZE, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
823 sprintf(str, GetString(STR_DR_CACHE_MMAP_ERR), strerror(errno));
824 ErrorAlert(str);
825 goto quit;
826 }
827 #endif
828 DRCacheAddr = DR_CACHE_BASE;
829 D(bug("DR Cache at %p\n", DRCacheAddr));
830
831 // Create area for SheepShaver data
832 if (!SheepMem::Init()) {
833 sprintf(str, GetString(STR_SHEEP_MEM_MMAP_ERR), strerror(errno));
834 ErrorAlert(str);
835 goto quit;
836 }
837
838 // Create area for Mac RAM
839 RAMSize = PrefsFindInt32("ramsize");
840 if (RAMSize < 8*1024*1024) {
841 WarningAlert(GetString(STR_SMALL_RAM_WARN));
842 RAMSize = 8*1024*1024;
843 }
844 memory_mapped_from_zero = false;
845 ram_rom_areas_contiguous = false;
846 #if REAL_ADDRESSING && HAVE_LINKER_SCRIPT
847 if (vm_mac_acquire_fixed(0, RAMSize) == 0) {
848 D(bug("Could allocate RAM from 0x0000\n"));
849 RAMBase = 0;
850 RAMBaseHost = Mac2HostAddr(RAMBase);
851 memory_mapped_from_zero = true;
852 }
853 #endif
854 if (!memory_mapped_from_zero) {
855 #ifndef PAGEZERO_HACK
856 // Create Low Memory area (0x0000..0x3000)
857 if (vm_mac_acquire_fixed(0, 0x3000) < 0) {
858 sprintf(str, GetString(STR_LOW_MEM_MMAP_ERR), strerror(errno));
859 ErrorAlert(str);
860 goto quit;
861 }
862 lm_area_mapped = true;
863 #endif
864 #if REAL_ADDRESSING
865 // Allocate RAM at any address. Since ROM must be higher than RAM, allocate the RAM
866 // and ROM areas contiguously, plus a little extra to allow for ROM address alignment.
867 RAMBaseHost = vm_mac_acquire(RAMSize + ROM_AREA_SIZE + ROM_ALIGNMENT);
868 if (RAMBaseHost == VM_MAP_FAILED) {
869 sprintf(str, GetString(STR_RAM_ROM_MMAP_ERR), strerror(errno));
870 ErrorAlert(str);
871 goto quit;
872 }
873 RAMBase = Host2MacAddr(RAMBaseHost);
874 ROMBase = (RAMBase + RAMSize + ROM_ALIGNMENT -1) & -ROM_ALIGNMENT;
875 ROMBaseHost = Mac2HostAddr(ROMBase);
876 ram_rom_areas_contiguous = true;
877 #else
878 if (vm_mac_acquire_fixed(RAM_BASE, RAMSize) < 0) {
879 sprintf(str, GetString(STR_RAM_MMAP_ERR), strerror(errno));
880 ErrorAlert(str);
881 goto quit;
882 }
883 RAMBase = RAM_BASE;
884 RAMBaseHost = Mac2HostAddr(RAMBase);
885 #endif
886 }
887 #if !EMULATED_PPC
888 if (vm_protect(RAMBaseHost, RAMSize, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
889 sprintf(str, GetString(STR_RAM_MMAP_ERR), strerror(errno));
890 ErrorAlert(str);
891 goto quit;
892 }
893 #endif
894 ram_area_mapped = true;
895 D(bug("RAM area at %p (%08x)\n", RAMBaseHost, RAMBase));
896
897 if (RAMBase > KernelDataAddr) {
898 ErrorAlert(GetString(STR_RAM_AREA_TOO_HIGH_ERR));
899 goto quit;
900 }
901
902 // Create area for Mac ROM
903 if (!ram_rom_areas_contiguous) {
904 if (vm_mac_acquire_fixed(ROM_BASE, ROM_AREA_SIZE) < 0) {
905 sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno));
906 ErrorAlert(str);
907 goto quit;
908 }
909 ROMBase = ROM_BASE;
910 ROMBaseHost = Mac2HostAddr(ROMBase);
911 }
912 #if !EMULATED_PPC
913 if (vm_protect(ROMBaseHost, ROM_AREA_SIZE, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
914 sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno));
915 ErrorAlert(str);
916 goto quit;
917 }
918 #endif
919 rom_area_mapped = true;
920 D(bug("ROM area at %p (%08x)\n", ROMBaseHost, ROMBase));
921
922 if (RAMBase > ROMBase) {
923 ErrorAlert(GetString(STR_RAM_HIGHER_THAN_ROM_ERR));
924 goto quit;
925 }
926
927 // Load Mac ROM
928 rom_path = PrefsFindString("rom");
929 rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME, O_RDONLY);
930 if (rom_fd < 0) {
931 rom_fd = open(rom_path ? rom_path : ROM_FILE_NAME2, O_RDONLY);
932 if (rom_fd < 0) {
933 ErrorAlert(GetString(STR_NO_ROM_FILE_ERR));
934 goto quit;
935 }
936 }
937 printf(GetString(STR_READING_ROM_FILE));
938 rom_size = lseek(rom_fd, 0, SEEK_END);
939 lseek(rom_fd, 0, SEEK_SET);
940 rom_tmp = new uint8[ROM_SIZE];
941 actual = read(rom_fd, (void *)rom_tmp, ROM_SIZE);
942 close(rom_fd);
943
944 // Decode Mac ROM
945 if (!DecodeROM(rom_tmp, actual)) {
946 if (rom_size != 4*1024*1024) {
947 ErrorAlert(GetString(STR_ROM_SIZE_ERR));
948 goto quit;
949 } else {
950 ErrorAlert(GetString(STR_ROM_FILE_READ_ERR));
951 goto quit;
952 }
953 }
954 delete[] rom_tmp;
955
956 // Initialize everything
957 if (!InitAll(vmdir))
958 goto quit;
959 D(bug("Initialization complete\n"));
960
961 // Clear caches (as we loaded and patched code) and write protect ROM
962 #if !EMULATED_PPC
963 flush_icache_range(ROMBase, ROMBase + ROM_AREA_SIZE);
964 #endif
965 vm_protect(ROMBaseHost, ROM_AREA_SIZE, VM_PAGE_READ | VM_PAGE_EXECUTE);
966
967 // Start 60Hz thread
968 tick_thread_cancel = false;
969 tick_thread_active = (pthread_create(&tick_thread, NULL, tick_func, NULL) == 0);
970 D(bug("Tick thread installed (%ld)\n", tick_thread));
971
972 // Start NVRAM watchdog thread
973 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
974 nvram_thread_cancel = false;
975 nvram_thread_active = (pthread_create(&nvram_thread, NULL, nvram_func, NULL) == 0);
976 D(bug("NVRAM thread installed (%ld)\n", nvram_thread));
977
978 #if !EMULATED_PPC
979 // Install SIGILL handler
980 sigemptyset(&sigill_action.sa_mask); // Block interrupts during ILL handling
981 sigaddset(&sigill_action.sa_mask, SIGUSR2);
982 sigill_action.sa_sigaction = sigill_handler;
983 sigill_action.sa_flags = SA_ONSTACK | SA_SIGINFO;
984 #ifdef HAVE_SIGNAL_SA_RESTORER
985 sigill_action.sa_restorer = NULL;
986 #endif
987 if (sigaction(SIGILL, &sigill_action, NULL) < 0) {
988 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGILL", strerror(errno));
989 ErrorAlert(str);
990 goto quit;
991 }
992 #endif
993
994 #if !EMULATED_PPC
995 // Install interrupt signal handler
996 sigemptyset(&sigusr2_action.sa_mask);
997 sigusr2_action.sa_sigaction = sigusr2_handler_init;
998 sigusr2_action.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
999 #ifdef HAVE_SIGNAL_SA_RESTORER
1000 sigusr2_action.sa_restorer = NULL;
1001 #endif
1002 if (sigaction(SIGUSR2, &sigusr2_action, NULL) < 0) {
1003 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGUSR2", strerror(errno));
1004 ErrorAlert(str);
1005 goto quit;
1006 }
1007 #endif
1008
1009 // Get my thread ID and execute MacOS thread function
1010 emul_thread = pthread_self();
1011 D(bug("MacOS thread is %ld\n", emul_thread));
1012 emul_func(NULL);
1013
1014 quit:
1015 Quit();
1016 return 0;
1017 }
1018
1019
1020 /*
1021 * Cleanup and quit
1022 */
1023
1024 static void Quit(void)
1025 {
1026 #if EMULATED_PPC
1027 // Exit PowerPC emulation
1028 exit_emul_ppc();
1029 #endif
1030
1031 // Stop 60Hz thread
1032 if (tick_thread_active) {
1033 tick_thread_cancel = true;
1034 pthread_cancel(tick_thread);
1035 pthread_join(tick_thread, NULL);
1036 }
1037
1038 // Stop NVRAM watchdog thread
1039 if (nvram_thread_active) {
1040 nvram_thread_cancel = true;
1041 pthread_cancel(nvram_thread);
1042 pthread_join(nvram_thread, NULL);
1043 }
1044
1045 #if !EMULATED_PPC
1046 // Uninstall SIGSEGV and SIGBUS handlers
1047 sigemptyset(&sigsegv_action.sa_mask);
1048 sigsegv_action.sa_handler = SIG_DFL;
1049 sigsegv_action.sa_flags = 0;
1050 sigaction(SIGSEGV, &sigsegv_action, NULL);
1051 sigaction(SIGBUS, &sigsegv_action, NULL);
1052
1053 // Uninstall SIGILL handler
1054 sigemptyset(&sigill_action.sa_mask);
1055 sigill_action.sa_handler = SIG_DFL;
1056 sigill_action.sa_flags = 0;
1057 sigaction(SIGILL, &sigill_action, NULL);
1058
1059 // Delete stacks for signal handlers
1060 if (sig_stack.ss_sp)
1061 free(sig_stack.ss_sp);
1062 if (extra_stack.ss_sp)
1063 free(extra_stack.ss_sp);
1064 #endif
1065
1066 // Deinitialize everything
1067 ExitAll();
1068
1069 // Delete SheepShaver globals
1070 SheepMem::Exit();
1071
1072 // Delete RAM area
1073 if (ram_area_mapped)
1074 vm_mac_release(RAMBase, RAMSize);
1075
1076 // Delete ROM area
1077 if (rom_area_mapped)
1078 vm_mac_release(ROMBase, ROM_AREA_SIZE);
1079
1080 // Delete DR cache areas
1081 if (dr_emulator_area_mapped)
1082 vm_mac_release(DR_EMULATOR_BASE, DR_EMULATOR_SIZE);
1083 if (dr_cache_area_mapped)
1084 vm_mac_release(DR_CACHE_BASE, DR_CACHE_SIZE);
1085
1086 // Delete Kernel Data area
1087 kernel_data_exit();
1088
1089 // Delete Low Memory area
1090 if (lm_area_mapped)
1091 vm_mac_release(0, 0x3000);
1092
1093 // Close /dev/zero
1094 if (zero_fd > 0)
1095 close(zero_fd);
1096
1097 // Exit system routines
1098 SysExit();
1099
1100 // Exit preferences
1101 PrefsExit();
1102
1103 #ifdef ENABLE_MON
1104 // Exit mon
1105 mon_exit();
1106 #endif
1107
1108 // Close X11 server connection
1109 #ifndef USE_SDL_VIDEO
1110 if (x_display)
1111 XCloseDisplay(x_display);
1112 #endif
1113
1114 // Notify GUI we are about to leave
1115 if (gui_connection) {
1116 if (rpc_method_invoke(gui_connection, RPC_METHOD_EXIT, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR)
1117 rpc_method_wait_for_reply(gui_connection, RPC_TYPE_INVALID);
1118 }
1119
1120 exit(0);
1121 }
1122
1123
1124 /*
1125 * Initialize Kernel Data segments
1126 */
1127
1128 static bool kernel_data_init(void)
1129 {
1130 char str[256];
1131 uint32 kernel_area_size = (KERNEL_AREA_SIZE + SHMLBA - 1) & -SHMLBA;
1132
1133 kernel_area = shmget(IPC_PRIVATE, kernel_area_size, 0600);
1134 if (kernel_area == -1) {
1135 sprintf(str, GetString(STR_KD_SHMGET_ERR), strerror(errno));
1136 ErrorAlert(str);
1137 return false;
1138 }
1139 void *kernel_addr = Mac2HostAddr(KERNEL_DATA_BASE & -SHMLBA);
1140 if (shmat(kernel_area, kernel_addr, 0) != kernel_addr) {
1141 sprintf(str, GetString(STR_KD_SHMAT_ERR), strerror(errno));
1142 ErrorAlert(str);
1143 return false;
1144 }
1145 kernel_addr = Mac2HostAddr(KERNEL_DATA2_BASE & -SHMLBA);
1146 if (shmat(kernel_area, kernel_addr, 0) != kernel_addr) {
1147 sprintf(str, GetString(STR_KD2_SHMAT_ERR), strerror(errno));
1148 ErrorAlert(str);
1149 return false;
1150 }
1151 return true;
1152 }
1153
1154
1155 /*
1156 * Deallocate Kernel Data segments
1157 */
1158
1159 static void kernel_data_exit(void)
1160 {
1161 if (kernel_area >= 0) {
1162 shmdt(Mac2HostAddr(KERNEL_DATA_BASE & -SHMLBA));
1163 shmdt(Mac2HostAddr(KERNEL_DATA2_BASE & -SHMLBA));
1164 shmctl(kernel_area, IPC_RMID, NULL);
1165 }
1166 }
1167
1168
1169 /*
1170 * Jump into Mac ROM, start 680x0 emulator
1171 */
1172
1173 #if EMULATED_PPC
1174 void jump_to_rom(uint32 entry)
1175 {
1176 init_emul_ppc();
1177 emul_ppc(entry);
1178 }
1179 #endif
1180
1181
1182 /*
1183 * Emulator thread function
1184 */
1185
1186 static void *emul_func(void *arg)
1187 {
1188 // We're now ready to receive signals
1189 ready_for_signals = true;
1190
1191 // Decrease priority, so more time-critical things like audio will work better
1192 nice(1);
1193
1194 // Jump to ROM boot routine
1195 D(bug("Jumping to ROM\n"));
1196 #if EMULATED_PPC
1197 jump_to_rom(ROMBase + 0x310000);
1198 #else
1199 jump_to_rom(ROMBase + 0x310000, (uint32)emulator_data);
1200 #endif
1201 D(bug("Returned from ROM\n"));
1202
1203 // We're no longer ready to receive signals
1204 ready_for_signals = false;
1205 return NULL;
1206 }
1207
1208
1209 #if !EMULATED_PPC
1210 /*
1211 * Execute 68k subroutine (must be ended with RTS)
1212 * This must only be called by the emul_thread when in EMUL_OP mode
1213 * r->a[7] is unused, the routine runs on the caller's stack
1214 */
1215
1216 void Execute68k(uint32 pc, M68kRegisters *r)
1217 {
1218 #if SAFE_EXEC_68K
1219 if (ReadMacInt32(XLM_RUN_MODE) != MODE_EMUL_OP)
1220 printf("FATAL: Execute68k() not called from EMUL_OP mode\n");
1221 if (!pthread_equal(pthread_self(), emul_thread))
1222 printf("FATAL: Execute68k() not called from emul_thread\n");
1223 #endif
1224 execute_68k(pc, r);
1225 }
1226
1227
1228 /*
1229 * Execute 68k A-Trap from EMUL_OP routine
1230 * r->a[7] is unused, the routine runs on the caller's stack
1231 */
1232
1233 void Execute68kTrap(uint16 trap, M68kRegisters *r)
1234 {
1235 uint16 proc[2] = {trap, M68K_RTS};
1236 Execute68k((uint32)proc, r);
1237 }
1238 #endif
1239
1240
1241 /*
1242 * Quit emulator (cause return from jump_to_rom)
1243 */
1244
1245 void QuitEmulator(void)
1246 {
1247 #if EMULATED_PPC
1248 Quit();
1249 #else
1250 quit_emulator();
1251 #endif
1252 }
1253
1254
1255 /*
1256 * Dump 68k registers
1257 */
1258
1259 void Dump68kRegs(M68kRegisters *r)
1260 {
1261 // Display 68k registers
1262 for (int i=0; i<8; i++) {
1263 printf("d%d: %08x", i, r->d[i]);
1264 if (i == 3 || i == 7)
1265 printf("\n");
1266 else
1267 printf(", ");
1268 }
1269 for (int i=0; i<8; i++) {
1270 printf("a%d: %08x", i, r->a[i]);
1271 if (i == 3 || i == 7)
1272 printf("\n");
1273 else
1274 printf(", ");
1275 }
1276 }
1277
1278
1279 /*
1280 * Make code executable
1281 */
1282
1283 void MakeExecutable(int dummy, uint32 start, uint32 length)
1284 {
1285 if ((start >= ROMBase) && (start < (ROMBase + ROM_SIZE)))
1286 return;
1287 #if EMULATED_PPC
1288 FlushCodeCache(start, start + length);
1289 #else
1290 flush_icache_range(start, start + length);
1291 #endif
1292 }
1293
1294
1295 /*
1296 * NVRAM watchdog thread (saves NVRAM every minute)
1297 */
1298
1299 static void nvram_watchdog(void)
1300 {
1301 if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) {
1302 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
1303 SaveXPRAM();
1304 }
1305 }
1306
1307 static void *nvram_func(void *arg)
1308 {
1309 while (!nvram_thread_cancel) {
1310 for (int i=0; i<60 && !nvram_thread_cancel; i++)
1311 Delay_usec(999999); // Only wait 1 second so we quit promptly when nvram_thread_cancel becomes true
1312 nvram_watchdog();
1313 }
1314 return NULL;
1315 }
1316
1317
1318 /*
1319 * 60Hz thread (really 60.15Hz)
1320 */
1321
1322 static void *tick_func(void *arg)
1323 {
1324 int tick_counter = 0;
1325 uint64 start = GetTicks_usec();
1326 int64 ticks = 0;
1327 uint64 next = GetTicks_usec();
1328
1329 while (!tick_thread_cancel) {
1330
1331 // Wait
1332 next += 16625;
1333 int64 delay = next - GetTicks_usec();
1334 if (delay > 0)
1335 Delay_usec(delay);
1336 else if (delay < -16625)
1337 next = GetTicks_usec();
1338 ticks++;
1339
1340 #if !EMULATED_PPC
1341 // Did we crash?
1342 if (emul_thread_fatal) {
1343
1344 // Yes, dump registers
1345 sigregs *r = &sigsegv_regs;
1346 char str[256];
1347 if (crash_reason == NULL)
1348 crash_reason = "SIGSEGV";
1349 sprintf(str, "%s\n"
1350 " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
1351 " xer %08lx cr %08lx \n"
1352 " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
1353 " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
1354 " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
1355 " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
1356 " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
1357 " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
1358 " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
1359 " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
1360 crash_reason,
1361 r->nip, r->link, r->ctr, r->msr,
1362 r->xer, r->ccr,
1363 r->gpr[0], r->gpr[1], r->gpr[2], r->gpr[3],
1364 r->gpr[4], r->gpr[5], r->gpr[6], r->gpr[7],
1365 r->gpr[8], r->gpr[9], r->gpr[10], r->gpr[11],
1366 r->gpr[12], r->gpr[13], r->gpr[14], r->gpr[15],
1367 r->gpr[16], r->gpr[17], r->gpr[18], r->gpr[19],
1368 r->gpr[20], r->gpr[21], r->gpr[22], r->gpr[23],
1369 r->gpr[24], r->gpr[25], r->gpr[26], r->gpr[27],
1370 r->gpr[28], r->gpr[29], r->gpr[30], r->gpr[31]);
1371 printf(str);
1372 VideoQuitFullScreen();
1373
1374 #ifdef ENABLE_MON
1375 // Start up mon in real-mode
1376 printf("Welcome to the sheep factory.\n");
1377 char *arg[4] = {"mon", "-m", "-r", NULL};
1378 mon(3, arg);
1379 #endif
1380 return NULL;
1381 }
1382 #endif
1383
1384 // Pseudo Mac 1Hz interrupt, update local time
1385 if (++tick_counter > 60) {
1386 tick_counter = 0;
1387 WriteMacInt32(0x20c, TimerDateTime());
1388 }
1389
1390 // Trigger 60Hz interrupt
1391 if (ReadMacInt32(XLM_IRQ_NEST) == 0) {
1392 SetInterruptFlag(INTFLAG_VIA);
1393 TriggerInterrupt();
1394 }
1395 }
1396
1397 uint64 end = GetTicks_usec();
1398 D(bug("%lld ticks in %lld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
1399 return NULL;
1400 }
1401
1402
1403 /*
1404 * Pthread configuration
1405 */
1406
1407 void Set_pthread_attr(pthread_attr_t *attr, int priority)
1408 {
1409 #ifdef HAVE_PTHREADS
1410 pthread_attr_init(attr);
1411 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
1412 // Some of these only work for superuser
1413 if (geteuid() == 0) {
1414 pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
1415 pthread_attr_setschedpolicy(attr, SCHED_FIFO);
1416 struct sched_param fifo_param;
1417 fifo_param.sched_priority = ((sched_get_priority_min(SCHED_FIFO) +
1418 sched_get_priority_max(SCHED_FIFO)) / 2 +
1419 priority);
1420 pthread_attr_setschedparam(attr, &fifo_param);
1421 }
1422 if (pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM) != 0) {
1423 #ifdef PTHREAD_SCOPE_BOUND_NP
1424 // If system scope is not available (eg. we're not running
1425 // with CAP_SCHED_MGT capability on an SGI box), try bound
1426 // scope. It exposes pthread scheduling to the kernel,
1427 // without setting realtime priority.
1428 pthread_attr_setscope(attr, PTHREAD_SCOPE_BOUND_NP);
1429 #endif
1430 }
1431 #endif
1432 #endif
1433 }
1434
1435
1436 /*
1437 * Mutexes
1438 */
1439
1440 #ifdef HAVE_PTHREADS
1441
1442 struct B2_mutex {
1443 B2_mutex() {
1444 pthread_mutexattr_t attr;
1445 pthread_mutexattr_init(&attr);
1446 // Initialize the mutex for priority inheritance --
1447 // required for accurate timing.
1448 #if defined(HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL) && !defined(__CYGWIN__)
1449 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
1450 #endif
1451 #if defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE) && defined(PTHREAD_MUTEX_NORMAL)
1452 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
1453 #endif
1454 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED
1455 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
1456 #endif
1457 pthread_mutex_init(&m, &attr);
1458 pthread_mutexattr_destroy(&attr);
1459 }
1460 ~B2_mutex() {
1461 pthread_mutex_trylock(&m); // Make sure it's locked before
1462 pthread_mutex_unlock(&m); // unlocking it.
1463 pthread_mutex_destroy(&m);
1464 }
1465 pthread_mutex_t m;
1466 };
1467
1468 B2_mutex *B2_create_mutex(void)
1469 {
1470 return new B2_mutex;
1471 }
1472
1473 void B2_lock_mutex(B2_mutex *mutex)
1474 {
1475 pthread_mutex_lock(&mutex->m);
1476 }
1477
1478 void B2_unlock_mutex(B2_mutex *mutex)
1479 {
1480 pthread_mutex_unlock(&mutex->m);
1481 }
1482
1483 void B2_delete_mutex(B2_mutex *mutex)
1484 {
1485 delete mutex;
1486 }
1487
1488 #else
1489
1490 struct B2_mutex {
1491 int dummy;
1492 };
1493
1494 B2_mutex *B2_create_mutex(void)
1495 {
1496 return new B2_mutex;
1497 }
1498
1499 void B2_lock_mutex(B2_mutex *mutex)
1500 {
1501 }
1502
1503 void B2_unlock_mutex(B2_mutex *mutex)
1504 {
1505 }
1506
1507 void B2_delete_mutex(B2_mutex *mutex)
1508 {
1509 delete mutex;
1510 }
1511
1512 #endif
1513
1514
1515 /*
1516 * Trigger signal USR2 from another thread
1517 */
1518
1519 #if !EMULATED_PPC
1520 void TriggerInterrupt(void)
1521 {
1522 if (ready_for_signals) {
1523 idle_resume();
1524 pthread_kill(emul_thread, SIGUSR2);
1525 }
1526 }
1527 #endif
1528
1529
1530 /*
1531 * Interrupt flags (must be handled atomically!)
1532 */
1533
1534 volatile uint32 InterruptFlags = 0;
1535
1536 void SetInterruptFlag(uint32 flag)
1537 {
1538 atomic_or((int *)&InterruptFlags, flag);
1539 }
1540
1541 void ClearInterruptFlag(uint32 flag)
1542 {
1543 atomic_and((int *)&InterruptFlags, ~flag);
1544 }
1545
1546
1547 /*
1548 * Disable interrupts
1549 */
1550
1551 void DisableInterrupt(void)
1552 {
1553 #if EMULATED_PPC
1554 WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) + 1);
1555 #else
1556 atomic_add((int *)XLM_IRQ_NEST, 1);
1557 #endif
1558 }
1559
1560
1561 /*
1562 * Enable interrupts
1563 */
1564
1565 void EnableInterrupt(void)
1566 {
1567 #if EMULATED_PPC
1568 WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) - 1);
1569 #else
1570 atomic_add((int *)XLM_IRQ_NEST, -1);
1571 #endif
1572 }
1573
1574
1575 /*
1576 * USR2 handler
1577 */
1578
1579 #if !EMULATED_PPC
1580 void sigusr2_handler(int sig, siginfo_t *sip, void *scp)
1581 {
1582 machine_regs *r = MACHINE_REGISTERS(scp);
1583
1584 #ifdef SYSTEM_CLOBBERS_R2
1585 // Restore pointer to Thread Local Storage
1586 set_r2(TOC);
1587 #endif
1588 #ifdef SYSTEM_CLOBBERS_R13
1589 // Restore pointer to .sdata section
1590 set_r13(R13);
1591 #endif
1592
1593 #ifdef USE_SDL_VIDEO
1594 // We must fill in the events queue in the same thread that did call SDL_SetVideoMode()
1595 SDL_PumpEvents();
1596 #endif
1597
1598 // Do nothing if interrupts are disabled
1599 if (*(int32 *)XLM_IRQ_NEST > 0)
1600 return;
1601
1602 // Disable MacOS stack sniffer
1603 WriteMacInt32(0x110, 0);
1604
1605 // Interrupt action depends on current run mode
1606 switch (ReadMacInt32(XLM_RUN_MODE)) {
1607 case MODE_68K:
1608 // 68k emulator active, trigger 68k interrupt level 1
1609 WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1);
1610 r->cr() |= ntohl(kernel_data->v[0x674 >> 2]);
1611 break;
1612
1613 #if INTERRUPTS_IN_NATIVE_MODE
1614 case MODE_NATIVE:
1615 // 68k emulator inactive, in nanokernel?
1616 if (r->gpr(1) != KernelDataAddr) {
1617
1618 // Set extra stack for SIGSEGV handler
1619 sigaltstack(&extra_stack, NULL);
1620
1621 // Prepare for 68k interrupt level 1
1622 WriteMacInt16(ntohl(kernel_data->v[0x67c >> 2]), 1);
1623 WriteMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc, ReadMacInt32(ntohl(kernel_data->v[0x658 >> 2]) + 0xdc) | ntohl(kernel_data->v[0x674 >> 2]));
1624
1625 // Execute nanokernel interrupt routine (this will activate the 68k emulator)
1626 DisableInterrupt();
1627 if (ROMType == ROMTYPE_NEWWORLD)
1628 ppc_interrupt(ROMBase + 0x312b1c, KernelDataAddr);
1629 else
1630 ppc_interrupt(ROMBase + 0x312a3c, KernelDataAddr);
1631
1632 // Reset normal stack
1633 sigaltstack(&sig_stack, NULL);
1634 }
1635 break;
1636 #endif
1637
1638 #if INTERRUPTS_IN_EMUL_OP_MODE
1639 case MODE_EMUL_OP:
1640 // 68k emulator active, within EMUL_OP routine, execute 68k interrupt routine directly when interrupt level is 0
1641 if ((ReadMacInt32(XLM_68K_R25) & 7) == 0) {
1642
1643 // Set extra stack for SIGSEGV handler
1644 sigaltstack(&extra_stack, NULL);
1645 #if 1
1646 // Execute full 68k interrupt routine
1647 M68kRegisters r;
1648 uint32 old_r25 = ReadMacInt32(XLM_68K_R25); // Save interrupt level
1649 WriteMacInt32(XLM_68K_R25, 0x21); // Execute with interrupt level 1
1650 static const uint16 proc[] = {
1651 0x3f3c, 0x0000, // move.w #$0000,-(sp) (fake format word)
1652 0x487a, 0x000a, // pea @1(pc) (return address)
1653 0x40e7, // move sr,-(sp) (saved SR)
1654 0x2078, 0x0064, // move.l $64,a0
1655 0x4ed0, // jmp (a0)
1656 M68K_RTS // @1
1657 };
1658 Execute68k((uint32)proc, &r);
1659 WriteMacInt32(XLM_68K_R25, old_r25); // Restore interrupt level
1660 #else
1661 // Only update cursor
1662 if (HasMacStarted()) {
1663 if (InterruptFlags & INTFLAG_VIA) {
1664 ClearInterruptFlag(INTFLAG_VIA);
1665 ADBInterrupt();
1666 ExecuteNative(NATIVE_VIDEO_VBL);
1667 }
1668 }
1669 #endif
1670 // Reset normal stack
1671 sigaltstack(&sig_stack, NULL);
1672 }
1673 break;
1674 #endif
1675 }
1676 }
1677 #endif
1678
1679
1680 /*
1681 * SIGSEGV handler
1682 */
1683
1684 #if !EMULATED_PPC
1685 static void sigsegv_handler(int sig, siginfo_t *sip, void *scp)
1686 {
1687 machine_regs *r = MACHINE_REGISTERS(scp);
1688
1689 // Get effective address
1690 uint32 addr = r->dar();
1691
1692 #ifdef SYSTEM_CLOBBERS_R2
1693 // Restore pointer to Thread Local Storage
1694 set_r2(TOC);
1695 #endif
1696 #ifdef SYSTEM_CLOBBERS_R13
1697 // Restore pointer to .sdata section
1698 set_r13(R13);
1699 #endif
1700
1701 #if ENABLE_VOSF
1702 // Handle screen fault
1703 #if SIGSEGV_CHECK_VERSION(1,0,0)
1704 sigsegv_info_t si;
1705 si.addr = (sigsegv_address_t)addr;
1706 si.pc = (sigsegv_address_t)r->pc();
1707 #endif
1708 extern bool Screen_fault_handler(sigsegv_info_t *sip);
1709 if (Screen_fault_handler(&si))
1710 return;
1711 #endif
1712
1713 num_segv++;
1714
1715 // Fault in Mac ROM or RAM or DR Cache?
1716 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));
1717 if (mac_fault) {
1718
1719 // "VM settings" during MacOS 8 installation
1720 if (r->pc() == ROMBase + 0x488160 && r->gpr(20) == 0xf8000000) {
1721 r->pc() += 4;
1722 r->gpr(8) = 0;
1723 return;
1724
1725 // MacOS 8.5 installation
1726 } else if (r->pc() == ROMBase + 0x488140 && r->gpr(16) == 0xf8000000) {
1727 r->pc() += 4;
1728 r->gpr(8) = 0;
1729 return;
1730
1731 // MacOS 8 serial drivers on startup
1732 } else if (r->pc() == ROMBase + 0x48e080 && (r->gpr(8) == 0xf3012002 || r->gpr(8) == 0xf3012000)) {
1733 r->pc() += 4;
1734 r->gpr(8) = 0;
1735 return;
1736
1737 // MacOS 8.1 serial drivers on startup
1738 } else if (r->pc() == ROMBase + 0x48c5e0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1739 r->pc() += 4;
1740 return;
1741 } else if (r->pc() == ROMBase + 0x4a10a0 && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1742 r->pc() += 4;
1743 return;
1744
1745 // MacOS 8.6 serial drivers on startup (with DR Cache and OldWorld ROM)
1746 } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(16) == 0xf3012002 || r->gpr(16) == 0xf3012000)) {
1747 r->pc() += 4;
1748 return;
1749 } else if ((r->pc() - DR_CACHE_BASE) < DR_CACHE_SIZE && (r->gpr(20) == 0xf3012002 || r->gpr(20) == 0xf3012000)) {
1750 r->pc() += 4;
1751 return;
1752 }
1753
1754 // Get opcode and divide into fields
1755 uint32 opcode = *((uint32 *)r->pc());
1756 uint32 primop = opcode >> 26;
1757 uint32 exop = (opcode >> 1) & 0x3ff;
1758 uint32 ra = (opcode >> 16) & 0x1f;
1759 uint32 rb = (opcode >> 11) & 0x1f;
1760 uint32 rd = (opcode >> 21) & 0x1f;
1761 int32 imm = (int16)(opcode & 0xffff);
1762
1763 // Analyze opcode
1764 enum {
1765 TYPE_UNKNOWN,
1766 TYPE_LOAD,
1767 TYPE_STORE
1768 } transfer_type = TYPE_UNKNOWN;
1769 enum {
1770 SIZE_UNKNOWN,
1771 SIZE_BYTE,
1772 SIZE_HALFWORD,
1773 SIZE_WORD
1774 } transfer_size = SIZE_UNKNOWN;
1775 enum {
1776 MODE_UNKNOWN,
1777 MODE_NORM,
1778 MODE_U,
1779 MODE_X,
1780 MODE_UX
1781 } addr_mode = MODE_UNKNOWN;
1782 switch (primop) {
1783 case 31:
1784 switch (exop) {
1785 case 23: // lwzx
1786 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1787 case 55: // lwzux
1788 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1789 case 87: // lbzx
1790 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1791 case 119: // lbzux
1792 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1793 case 151: // stwx
1794 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
1795 case 183: // stwux
1796 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
1797 case 215: // stbx
1798 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
1799 case 247: // stbux
1800 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
1801 case 279: // lhzx
1802 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1803 case 311: // lhzux
1804 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1805 case 343: // lhax
1806 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1807 case 375: // lhaux
1808 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1809 case 407: // sthx
1810 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
1811 case 439: // sthux
1812 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
1813 }
1814 break;
1815
1816 case 32: // lwz
1817 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
1818 case 33: // lwzu
1819 transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
1820 case 34: // lbz
1821 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
1822 case 35: // lbzu
1823 transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
1824 case 36: // stw
1825 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
1826 case 37: // stwu
1827 transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
1828 case 38: // stb
1829 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
1830 case 39: // stbu
1831 transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
1832 case 40: // lhz
1833 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1834 case 41: // lhzu
1835 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1836 case 42: // lha
1837 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1838 case 43: // lhau
1839 transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1840 case 44: // sth
1841 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
1842 case 45: // sthu
1843 transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
1844 #if EMULATE_UNALIGNED_LOADSTORE_MULTIPLE
1845 case 46: // lmw
1846 if ((addr % 4) != 0) {
1847 uint32 ea = addr;
1848 D(bug("WARNING: unaligned lmw to EA=%08x from IP=%08x\n", ea, r->pc()));
1849 for (int i = rd; i <= 31; i++) {
1850 r->gpr(i) = ReadMacInt32(ea);
1851 ea += 4;
1852 }
1853 r->pc() += 4;
1854 goto rti;
1855 }
1856 break;
1857 case 47: // stmw
1858 if ((addr % 4) != 0) {
1859 uint32 ea = addr;
1860 D(bug("WARNING: unaligned stmw to EA=%08x from IP=%08x\n", ea, r->pc()));
1861 for (int i = rd; i <= 31; i++) {
1862 WriteMacInt32(ea, r->gpr(i));
1863 ea += 4;
1864 }
1865 r->pc() += 4;
1866 goto rti;
1867 }
1868 break;
1869 #endif
1870 }
1871
1872 // Ignore ROM writes (including to the zero page, which is read-only)
1873 if (transfer_type == TYPE_STORE &&
1874 ((addr >= ROMBase && addr < ROMBase + ROM_SIZE) ||
1875 (addr >= SheepMem::ZeroPage() && addr < SheepMem::ZeroPage() + SheepMem::PageSize()))) {
1876 // 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()));
1877 if (addr_mode == MODE_U || addr_mode == MODE_UX)
1878 r->gpr(ra) = addr;
1879 r->pc() += 4;
1880 goto rti;
1881 }
1882
1883 // Ignore illegal memory accesses?
1884 if (PrefsFindBool("ignoresegv")) {
1885 if (addr_mode == MODE_U || addr_mode == MODE_UX)
1886 r->gpr(ra) = addr;
1887 if (transfer_type == TYPE_LOAD)
1888 r->gpr(rd) = 0;
1889 r->pc() += 4;
1890 goto rti;
1891 }
1892
1893 // In GUI mode, show error alert
1894 if (!PrefsFindBool("nogui")) {
1895 char str[256];
1896 if (transfer_type == TYPE_LOAD || transfer_type == TYPE_STORE)
1897 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));
1898 else
1899 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode);
1900 ErrorAlert(str);
1901 QuitEmulator();
1902 return;
1903 }
1904 }
1905
1906 // For all other errors, jump into debugger (sort of...)
1907 crash_reason = (sig == SIGBUS) ? "SIGBUS" : "SIGSEGV";
1908 if (!ready_for_signals) {
1909 printf("%s\n");
1910 printf(" sigcontext %p, machine_regs %p\n", scp, r);
1911 printf(
1912 " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
1913 " xer %08lx cr %08lx \n"
1914 " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
1915 " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
1916 " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
1917 " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
1918 " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
1919 " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
1920 " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
1921 " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
1922 crash_reason,
1923 r->pc(), r->lr(), r->ctr(), r->msr(),
1924 r->xer(), r->cr(),
1925 r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3),
1926 r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7),
1927 r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11),
1928 r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15),
1929 r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19),
1930 r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23),
1931 r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27),
1932 r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31));
1933 exit(1);
1934 QuitEmulator();
1935 return;
1936 } else {
1937 // We crashed. Save registers, tell tick thread and loop forever
1938 build_sigregs(&sigsegv_regs, r);
1939 emul_thread_fatal = true;
1940 for (;;) ;
1941 }
1942 rti:;
1943 }
1944
1945
1946 /*
1947 * SIGILL handler
1948 */
1949
1950 static void sigill_handler(int sig, siginfo_t *sip, void *scp)
1951 {
1952 machine_regs *r = MACHINE_REGISTERS(scp);
1953 char str[256];
1954
1955 #ifdef SYSTEM_CLOBBERS_R2
1956 // Restore pointer to Thread Local Storage
1957 set_r2(TOC);
1958 #endif
1959 #ifdef SYSTEM_CLOBBERS_R13
1960 // Restore pointer to .sdata section
1961 set_r13(R13);
1962 #endif
1963
1964 // Fault in Mac ROM or RAM?
1965 bool mac_fault = (r->pc() >= ROMBase) && (r->pc() < (ROMBase + ROM_AREA_SIZE)) || (r->pc() >= RAMBase) && (r->pc() < (RAMBase + RAMSize));
1966 if (mac_fault) {
1967
1968 // Get opcode and divide into fields
1969 uint32 opcode = *((uint32 *)r->pc());
1970 uint32 primop = opcode >> 26;
1971 uint32 exop = (opcode >> 1) & 0x3ff;
1972 uint32 ra = (opcode >> 16) & 0x1f;
1973 uint32 rb = (opcode >> 11) & 0x1f;
1974 uint32 rd = (opcode >> 21) & 0x1f;
1975 int32 imm = (int16)(opcode & 0xffff);
1976
1977 switch (primop) {
1978 case 9: // POWER instructions
1979 case 22:
1980 power_inst: sprintf(str, GetString(STR_POWER_INSTRUCTION_ERR), r->pc(), r->gpr(1), opcode);
1981 ErrorAlert(str);
1982 QuitEmulator();
1983 return;
1984
1985 case 31:
1986 switch (exop) {
1987 case 83: // mfmsr
1988 r->gpr(rd) = 0xf072;
1989 r->pc() += 4;
1990 goto rti;
1991
1992 case 210: // mtsr
1993 case 242: // mtsrin
1994 case 306: // tlbie
1995 r->pc() += 4;
1996 goto rti;
1997
1998 case 339: { // mfspr
1999 int spr = ra | (rb << 5);
2000 switch (spr) {
2001 case 0: // MQ
2002 case 22: // DEC
2003 case 952: // MMCR0
2004 case 953: // PMC1
2005 case 954: // PMC2
2006 case 955: // SIA
2007 case 956: // MMCR1
2008 case 957: // PMC3
2009 case 958: // PMC4
2010 case 959: // SDA
2011 r->pc() += 4;
2012 goto rti;
2013 case 25: // SDR1
2014 r->gpr(rd) = 0xdead001f;
2015 r->pc() += 4;
2016 goto rti;
2017 case 287: // PVR
2018 r->gpr(rd) = PVR;
2019 r->pc() += 4;
2020 goto rti;
2021 }
2022 break;
2023 }
2024
2025 case 467: { // mtspr
2026 int spr = ra | (rb << 5);
2027 switch (spr) {
2028 case 0: // MQ
2029 case 22: // DEC
2030 case 275: // SPRG3
2031 case 528: // IBAT0U
2032 case 529: // IBAT0L
2033 case 530: // IBAT1U
2034 case 531: // IBAT1L
2035 case 532: // IBAT2U
2036 case 533: // IBAT2L
2037 case 534: // IBAT3U
2038 case 535: // IBAT3L
2039 case 536: // DBAT0U
2040 case 537: // DBAT0L
2041 case 538: // DBAT1U
2042 case 539: // DBAT1L
2043 case 540: // DBAT2U
2044 case 541: // DBAT2L
2045 case 542: // DBAT3U
2046 case 543: // DBAT3L
2047 case 952: // MMCR0
2048 case 953: // PMC1
2049 case 954: // PMC2
2050 case 955: // SIA
2051 case 956: // MMCR1
2052 case 957: // PMC3
2053 case 958: // PMC4
2054 case 959: // SDA
2055 r->pc() += 4;
2056 goto rti;
2057 }
2058 break;
2059 }
2060
2061 case 29: case 107: case 152: case 153: // POWER instructions
2062 case 184: case 216: case 217: case 248:
2063 case 264: case 277: case 331: case 360:
2064 case 363: case 488: case 531: case 537:
2065 case 541: case 664: case 665: case 696:
2066 case 728: case 729: case 760: case 920:
2067 case 921: case 952:
2068 goto power_inst;
2069 }
2070 }
2071
2072 // In GUI mode, show error alert
2073 if (!PrefsFindBool("nogui")) {
2074 sprintf(str, GetString(STR_UNKNOWN_SEGV_ERR), r->pc(), r->gpr(24), r->gpr(1), opcode);
2075 ErrorAlert(str);
2076 QuitEmulator();
2077 return;
2078 }
2079 }
2080
2081 // For all other errors, jump into debugger (sort of...)
2082 crash_reason = "SIGILL";
2083 if (!ready_for_signals) {
2084 printf("%s\n");
2085 printf(" sigcontext %p, machine_regs %p\n", scp, r);
2086 printf(
2087 " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
2088 " xer %08lx cr %08lx \n"
2089 " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
2090 " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
2091 " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
2092 " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
2093 " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
2094 " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
2095 " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
2096 " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
2097 crash_reason,
2098 r->pc(), r->lr(), r->ctr(), r->msr(),
2099 r->xer(), r->cr(),
2100 r->gpr(0), r->gpr(1), r->gpr(2), r->gpr(3),
2101 r->gpr(4), r->gpr(5), r->gpr(6), r->gpr(7),
2102 r->gpr(8), r->gpr(9), r->gpr(10), r->gpr(11),
2103 r->gpr(12), r->gpr(13), r->gpr(14), r->gpr(15),
2104 r->gpr(16), r->gpr(17), r->gpr(18), r->gpr(19),
2105 r->gpr(20), r->gpr(21), r->gpr(22), r->gpr(23),
2106 r->gpr(24), r->gpr(25), r->gpr(26), r->gpr(27),
2107 r->gpr(28), r->gpr(29), r->gpr(30), r->gpr(31));
2108 exit(1);
2109 QuitEmulator();
2110 return;
2111 } else {
2112 // We crashed. Save registers, tell tick thread and loop forever
2113 build_sigregs(&sigsegv_regs, r);
2114 emul_thread_fatal = true;
2115 for (;;) ;
2116 }
2117 rti:;
2118 }
2119 #endif
2120
2121
2122 /*
2123 * Helpers to share 32-bit addressable data with MacOS
2124 */
2125
2126 bool SheepMem::Init(void)
2127 {
2128 // Size of a native page
2129 page_size = getpagesize();
2130
2131 // Allocate SheepShaver globals
2132 proc = base;
2133 if (vm_mac_acquire_fixed(base, size) < 0)
2134 return false;
2135
2136 // Allocate page with all bits set to 0, right in the middle
2137 // This is also used to catch undesired overlaps between proc and data areas
2138 zero_page = proc + (size / 2);
2139 Mac_memset(zero_page, 0, page_size);
2140 if (vm_protect(Mac2HostAddr(zero_page), page_size, VM_PAGE_READ) < 0)
2141 return false;
2142
2143 #if EMULATED_PPC
2144 // Allocate alternate stack for PowerPC interrupt routine
2145 sig_stack = base + size;
2146 if (vm_mac_acquire_fixed(sig_stack, SIG_STACK_SIZE) < 0)
2147 return false;
2148 #endif
2149
2150 data = base + size;
2151 return true;
2152 }
2153
2154 void SheepMem::Exit(void)
2155 {
2156 if (data) {
2157 // Delete SheepShaver globals
2158 vm_mac_release(base, size);
2159
2160 #if EMULATED_PPC
2161 // Delete alternate stack for PowerPC interrupt routine
2162 vm_mac_release(sig_stack, SIG_STACK_SIZE);
2163 #endif
2164 }
2165 }
2166
2167
2168 /*
2169 * Display alert
2170 */
2171
2172 #ifdef ENABLE_GTK
2173 static void dl_destroyed(void)
2174 {
2175 gtk_main_quit();
2176 }
2177
2178 static void dl_quit(GtkWidget *dialog)
2179 {
2180 gtk_widget_destroy(dialog);
2181 }
2182
2183 void display_alert(int title_id, int prefix_id, int button_id, const char *text)
2184 {
2185 char str[256];
2186 sprintf(str, GetString(prefix_id), text);
2187
2188 GtkWidget *dialog = gtk_dialog_new();
2189 gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
2190 gtk_container_border_width(GTK_CONTAINER(dialog), 5);
2191 gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
2192 gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
2193
2194 GtkWidget *label = gtk_label_new(str);
2195 gtk_widget_show(label);
2196 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
2197
2198 GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
2199 gtk_widget_show(button);
2200 gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
2201 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
2202 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2203 gtk_widget_grab_default(button);
2204 gtk_widget_show(dialog);
2205
2206 gtk_main();
2207 }
2208 #endif
2209
2210
2211 /*
2212 * Display error alert
2213 */
2214
2215 void ErrorAlert(const char *text)
2216 {
2217 if (gui_connection) {
2218 if (rpc_method_invoke(gui_connection, RPC_METHOD_ERROR_ALERT, RPC_TYPE_STRING, text, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR &&
2219 rpc_method_wait_for_reply(gui_connection, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR)
2220 return;
2221 }
2222 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
2223 if (PrefsFindBool("nogui") || x_display == NULL) {
2224 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
2225 return;
2226 }
2227 VideoQuitFullScreen();
2228 display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
2229 #else
2230 printf(GetString(STR_SHELL_ERROR_PREFIX), text);
2231 #endif
2232 }
2233
2234
2235 /*
2236 * Display warning alert
2237 */
2238
2239 void WarningAlert(const char *text)
2240 {
2241 if (gui_connection) {
2242 if (rpc_method_invoke(gui_connection, RPC_METHOD_WARNING_ALERT, RPC_TYPE_STRING, text, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR &&
2243 rpc_method_wait_for_reply(gui_connection, RPC_TYPE_INVALID) == RPC_ERROR_NO_ERROR)
2244 return;
2245 }
2246 #if defined(ENABLE_GTK) && !defined(USE_SDL_VIDEO)
2247 if (PrefsFindBool("nogui") || x_display == NULL) {
2248 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2249 return;
2250 }
2251 display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
2252 #else
2253 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2254 #endif
2255 }
2256
2257
2258 /*
2259 * Display choice alert
2260 */
2261
2262 bool ChoiceAlert(const char *text, const char *pos, const char *neg)
2263 {
2264 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
2265 return false; //!!
2266 }