diff --git a/BasiliskII/src/Unix/Darwin/lowmem.c b/BasiliskII/src/Unix/Darwin/lowmem.c new file mode 100644 index 00000000..ce84bd3f --- /dev/null +++ b/BasiliskII/src/Unix/Darwin/lowmem.c @@ -0,0 +1,148 @@ +/* + * lowmem.c - enable access to low memory globals on Darwin + * + * Copyright (c) 2003 Michael Z. Sliczniak + * + * Basilisk II (C) 1997-2003 Christian Bauer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const char progname[] = "lowmem"; + +/* + * Under Mach there is very little assumed about the memory map of object + * files. It is the job of the loader to create the initial memory map of an + * executable. In a Mach-O executable there will be numerous loader commands + * that the loader must process. Some of these will create the initial memory + * map used by the executable. Under Darwin the static object file linker, + * ld, automatically adds the __PAGEZERO segment to all executables. The + * default size of this segment is the page size of the target system and + * the initial and maximum permissions are set to allow no access. This is so + * that all programs fault on a NULL pointer dereference. Arguably this is + * incorrect and the maximum permissions shoould be rwx so that programs can + * change this default behavior. Then programs could be written that assume + * a null string at the null address, which was the convention on some + * systems. In our case we need to have 8K mapped at zero for the low memory + * globals and this program modifies the segment load command in the + * basiliskII executable so that it can be used for data. + */ + +int +main(int argc, const char *argv[]) +{ + int fd; + char *addr; + struct mach_header *machhead; + struct segment_command *sc_cmd; + + if (argc != 2) { + (void)fprintf(stderr, "Usage: %s executable\n", progname); + exit(1); + } + + fd = open(argv[1], O_RDWR, 0); + if (fd == -1) { + (void)fprintf(stderr, "%s: could not open %s: %s\n", + progname, argv[1], strerror(errno)); + exit(1); + } + + /* + * Size does not really matter, it will be rounded-up to a multiple + * of the page size automatically. + */ + addr = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0); + if (addr == NULL) { + (void)fprintf(stderr, "%s: could not mmap %s: %s\n", + progname, argv[1], strerror(errno)); + exit(1); + } + + /* + * Check to see if the Mach-O magic bytes are in the header. + * If we cared about cross compiling we would also check against + * MH_CIGAM and then change the endianness with every access, but + * we do not care about that. + */ + machhead = (void *)addr; + if (machhead->magic != MH_MAGIC) { + (void)fprintf(stderr, "%s: %s does not appear to be a Mach-O object file\n", + progname, argv[1]); + exit(1); + } + + if (machhead->filetype != MH_EXECUTE) { + (void)fprintf(stderr, "%s: %s does not appear to be an executable file\n", + progname, argv[1]); + exit(1); + } + + if (machhead->ncmds == 0) { + (void)fprintf(stderr, "%s: %s does not contain any load commands\n", + progname, argv[1]); + exit(1); + } + + sc_cmd = (void *)&machhead[1]; + if (sc_cmd->cmd != LC_SEGMENT){ + (void)fprintf(stderr, "%s: load segment not first command in %s\n", + progname, argv[1]); + exit(1); + } + + if (strncmp(sc_cmd->segname, "__PAGEZERO", + sizeof (*sc_cmd->segname))) { + (void)fprintf(stderr, "%s: zero page not first segment in %s\n", + progname, argv[1]); + exit(1); + } + + /* change the permissions */ + sc_cmd->maxprot = VM_PROT_ALL; + sc_cmd->initprot = VM_PROT_ALL; + + /* + * We do not make __PAGEZERO 8K in this program because then + * all of the offsets would be wrong in the object file after + * this segment. Instead we use the -pagezero_size option + * to link the executable. + */ + if (msync(addr, 0x1000, MS_SYNC) == -1) { + (void)fprintf(stderr, "%s: could not sync %s: %s\n", + progname, argv[1], strerror(errno)); + exit(1); + } + + if (munmap(addr, 0x1000) == -1) { + (void)fprintf(stderr, "%s: could not unmap %s: %s\n", + progname, argv[1], strerror(errno)); + exit(1); + } + + (void)close(fd); + + exit(0); +} diff --git a/BasiliskII/src/Unix/Darwin/pagezero.c b/BasiliskII/src/Unix/Darwin/pagezero.c new file mode 100644 index 00000000..6f3f061e --- /dev/null +++ b/BasiliskII/src/Unix/Darwin/pagezero.c @@ -0,0 +1,31 @@ +/* + * pagezero.c - test to see if low memory globals can be accessed + * + * Copyright (c) 2003 Michael Z. Sliczniak + * + * Basilisk II (C) 1997-2003 Christian Bauer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int +main(int argc, const char *argv[]) +{ + volatile char *pagezero = (void *)0; + + pagezero[0x1234] = pagezero[0x123]; + + return (0); +} diff --git a/BasiliskII/src/Unix/Darwin/testlmem.sh b/BasiliskII/src/Unix/Darwin/testlmem.sh new file mode 100755 index 00000000..19cf073d --- /dev/null +++ b/BasiliskII/src/Unix/Darwin/testlmem.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# testlmem.sh - test whether the Mach-O hack works +# +# Basilisk II (C) 1997-2003 Christian Bauer +# +# testlmem.sh Copyright (C) 2003 Michael Z. Sliczniak +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +PAGEZERO_SIZE=0x2000 +[[ -n "$1" ]] && PAGEZERO_SIZE=$1 +# You want all the output to go to stderr so that configure is quiet but +# config.log is verbose. +{ echo 'building lowmem utility' && \ +make -f /dev/null Darwin/lowmem && \ +echo 'building pagezero test' && \ +make -f /dev/null LDFLAGS="-pagezero_size $PAGEZERO_SIZE" Darwin/pagezero && \ +echo 'enabling low memory globals in pagezero' && \ +Darwin/lowmem Darwin/pagezero && \ +echo 'running pagezero test' && \ +Darwin/pagezero; } 1>&2 diff --git a/BasiliskII/src/Unix/Makefile.in b/BasiliskII/src/Unix/Makefile.in index d07aa289..13a2d255 100644 --- a/BasiliskII/src/Unix/Makefile.in +++ b/BasiliskII/src/Unix/Makefile.in @@ -23,6 +23,7 @@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ SYSSRCS = @SYSSRCS@ CPUSRCS = @CPUSRCS@ +BLESS = @BLESS@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ -s INSTALL_DATA = @INSTALL_DATA@ @@ -61,6 +62,7 @@ VPATH += $(addprefix :, $(subst ,:, $(filter-out $($(subst, :, ,$(VPATH))), $(S $(APP): $(OBJ_DIR) $(OBJS) $(CXX) -o $(APP) $(LDFLAGS) $(OBJS) $(LIBS) + $(BLESS) $(APP) modules: cd Linux/NetDriver; make diff --git a/BasiliskII/src/Unix/configure.ac b/BasiliskII/src/Unix/configure.ac index 69a20d82..b2b7e8e5 100644 --- a/BasiliskII/src/Unix/configure.ac +++ b/BasiliskII/src/Unix/configure.ac @@ -558,6 +558,19 @@ AC_TRANSLATE_DEFINE(HAVE_MMAP_VM, $have_mmap_vm, fi dnl HAVE_MMAP_VM +dnl Check if we can modify the __PAGEZERO segment for use as Low Memory +AC_CACHE_CHECK([whether __PAGEZERO can be Low Memory area 0x0000-0x2000], + ac_cv_pagezero_hack, [ + ac_cv_pagezero_hack=no + if AC_TRY_COMMAND([Darwin/testlmem.sh 0x2000]); then + ac_cv_pagezero_hack=yes + dnl might as well skip the test for mmap-able low memory + ac_cv_can_map_lm=no + fi +]) +AC_TRANSLATE_DEFINE(PAGEZERO_HACK, "$ac_cv_pagezero_hack", + [Define if the __PAGEZERO Mach-O Low Memory Globals hack works on this system.]) + dnl Check if we can mmap 0x2000 bytes from 0x0000 AC_CACHE_CHECK([whether we can map Low Memory area 0x0000-0x2000], ac_cv_can_map_lm, [ @@ -743,6 +756,9 @@ if [[ -n "$sigsegv_recovery" ]]; then CAN_VOSF=yes fi +dnl A dummy program that returns always true +BLESS=/bin/true + dnl Determine the addressing mode to use if [[ "x$WANT_NATIVE_M68K" = "xyes" ]]; then ADDRESSING_MODE="real" @@ -753,10 +769,10 @@ else case $am in real) dnl Requires ability to mmap() Low Memory globals - if [[ "x$ac_cv_can_map_lm" = "xno" ]]; then + if [[ "x$ac_cv_can_map_lm$ac_cv_pagezero_hack" = "xnono" ]]; then continue fi - dnl Requires VOSF screen updates + dnl Requires VOSF screen updates if [[ "x$CAN_VOSF" = "xno" ]]; then continue fi @@ -764,6 +780,10 @@ else ADDRESSING_MODE="real" WANT_VOSF=yes dnl we can use VOSF and we need it actually DEFINES="$DEFINES -DREAL_ADDRESSING" + if [[ "x$ac_cv_pagezero_hack" = "xyes" ]]; then + BLESS=Darwin/lowmem + LDFLAGS="$LDFLAGS -pagezero_size 0x2000" + fi break ;; direct) @@ -1152,6 +1172,7 @@ AC_SUBST(DEFINES) AC_SUBST(SYSSRCS) AC_SUBST(CPUINCLUDES) AC_SUBST(CPUSRCS) +AC_SUBST(BLESS) AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/BasiliskII/src/Unix/main_unix.cpp b/BasiliskII/src/Unix/main_unix.cpp index bcc4826e..b93db866 100644 --- a/BasiliskII/src/Unix/main_unix.cpp +++ b/BasiliskII/src/Unix/main_unix.cpp @@ -413,7 +413,7 @@ int main(int argc, char **argv) // Under Solaris/SPARC and NetBSD/m68k, Basilisk II is known to crash // when trying to map a too big chunk of memory starting at address 0 -#if defined(OS_solaris) || defined(OS_netbsd) +#if defined(OS_solaris) || defined(OS_netbsd) || defined(PAGEZERO_HACK) const bool can_map_all_memory = false; #else const bool can_map_all_memory = true; @@ -425,6 +425,7 @@ int main(int argc, char **argv) memory_mapped_from_zero = true; } +#ifndef PAGEZERO_HACK // Otherwise, just create the Low Memory area (0x0000..0x2000) else if (vm_acquire_fixed(0, 0x2000) == 0) { D(bug("Could allocate the Low Memory globals\n")); @@ -438,6 +439,7 @@ int main(int argc, char **argv) QuitEmulator(); } #endif +#endif /* REAL_ADDRESSING */ // Create areas for Mac RAM and ROM #if REAL_ADDRESSING