/* * OS-dependent routines. This file (along with os.h) exports an * OS-independent interface to the operating system VM facilities. * Suprisingly, this interface looks a lot like the Mach interface * (but simpler in some places). For some operating systems, a subset * of these functions will have to be emulated. * * This is an experimental Solaris version based on sunos-os.c but * without the VM hack of unmapped pages for the GC trigger which * caused trouble when system calls were passed unmapped pages. * */ #include #include #include #include #include #include #include #include #include #include #include #include #if defined(GENCGC) #include "lisp.h" /* Need struct code defined to get rid of warning from gencgc.h */ #include "internals.h" #include "gencgc.h" #endif #include "os.h" #include "interrupt.h" /* To get dynamic_0_space and friends */ #include "globals.h" /* To get memory map */ #include "sparc-validate.h" /* For type_ListPointer and NIL */ #include "internals.h" #define EMPTYFILE "/tmp/empty" #define ZEROFILE "/dev/zero" /* ---------------------------------------------------------------- */ long os_vm_page_size = (-1); static long os_real_page_size = (-1); static int zero_fd = (-1); static os_vm_size_t real_page_size_difference = 0; static void os_init_bailout(char *arg) { char buf[500]; sprintf(buf, "os_init: %s", arg); perror(buf); exit(1); } void os_init0(const char *argv[], const char *envp[]) {} void os_init(const char *argv[], const char *envp[]) { zero_fd = open(ZEROFILE, O_RDONLY); if (zero_fd < 0) os_init_bailout(ZEROFILE); os_vm_page_size = os_real_page_size = sysconf(_SC_PAGESIZE); if (os_vm_page_size > OS_VM_DEFAULT_PAGESIZE) { fprintf(stderr, "os_init: Pagesize too large (%ld > %d)\n", os_vm_page_size, OS_VM_DEFAULT_PAGESIZE); exit(1); } else { /* * we do this because there are apparently dependencies on * the pagesize being OS_VM_DEFAULT_PAGESIZE somewhere... * but since the OS doesn't know we're using this restriction, * we have to grovel around a bit to enforce it, thus anything * that uses real_page_size_difference. */ real_page_size_difference = OS_VM_DEFAULT_PAGESIZE - os_vm_page_size; os_vm_page_size = OS_VM_DEFAULT_PAGESIZE; } } /* ---------------------------------------------------------------- */ os_vm_address_t os_validate(os_vm_address_t addr, os_vm_size_t len) { int flags = MAP_PRIVATE | MAP_NORESERVE; if (addr) flags |= MAP_FIXED; addr = (os_vm_address_t) mmap((void *) addr, len, OS_VM_PROT_ALL, flags, zero_fd, 0); if (addr == (os_vm_address_t) - 1) { perror("mmap"); addr = NULL; } return addr; } void os_invalidate(os_vm_address_t addr, os_vm_size_t len) { if (munmap((void *) addr, len) == -1) perror("munmap"); } os_vm_address_t os_map(int fd, int offset, os_vm_address_t addr, os_vm_size_t len) { if ( (addr = (os_vm_address_t) mmap((void *) addr, len, OS_VM_PROT_ALL, MAP_PRIVATE | MAP_FIXED, fd, (off_t) offset)) == (os_vm_address_t) - 1) perror("mmap"); return addr; } void os_flush_icache(os_vm_address_t address, os_vm_size_t length) { #ifndef i386 static int flushit = -1; /* * On some systems, iflush needs to be emulated in the kernel * On those systems, it isn't necessary * Call getenv() only once. */ if (flushit == -1) flushit = getenv("CMUCL_NO_SPARC_IFLUSH") == 0; if (flushit) { static int traceit = -1; if (traceit == -1) traceit = getenv("CMUCL_TRACE_SPARC_IFLUSH") != 0; if (traceit) fprintf(stderr, ";;;iflush %p - %lx\n", (void *) address, length); flush_icache((unsigned int *) address, length); } #endif } void os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot) { if (mprotect((void *) address, length, prot) == -1) perror("mprotect"); } static boolean in_range_p(os_vm_address_t a, lispobj sbeg, size_t slen) { char *beg = (char *) sbeg; char *end = (char *) sbeg + slen; char *adr = (char *) a; return (adr >= beg && adr < end); } boolean valid_addr(os_vm_address_t addr) { /* Stolen from Linux-os.c */ os_vm_address_t newaddr; newaddr = os_trunc_to_page(addr); /* Just assume address is valid if it lies within one of the known spaces. (Unlike sunos-os which keeps track of every valid page.) */ return (in_range_p(addr, READ_ONLY_SPACE_START, read_only_space_size) || in_range_p(addr, STATIC_SPACE_START, static_space_size) || in_range_p(addr, DYNAMIC_0_SPACE_START, dynamic_space_size) #ifndef GENCGC || in_range_p(addr, DYNAMIC_1_SPACE_START, dynamic_space_size) #endif || in_range_p(addr, CONTROL_STACK_START, control_stack_size) || in_range_p(addr, BINDING_STACK_START, binding_stack_size)); } /* ---------------------------------------------------------------- */ /* * Running into the gc trigger page will end up here... */ #if defined(GENCGC) void segv_handle_now(HANDLER_ARGS) { interrupt_handle_now(signal, code, context); } void real_segv_handler(HANDLER_ARGS) { segv_handle_now(signal, code, context); } void segv_handler(HANDLER_ARGS) { os_context_t *os_context = (os_context_t *) context; caddr_t addr = code->si_addr; SAVE_CONTEXT(); #ifdef RED_ZONE_HIT if (os_control_stack_overflow(addr, context)) return; #endif if (gc_write_barrier(code->si_addr)) return; /* * Could be a C stack overflow. Let's check */ { struct rlimit rlimit; if (getrlimit(RLIMIT_STACK, &rlimit) == 0) { /* The stack top here is based on the notes in sparc-validate.h */ char *stack_top = (char *) 0xffbf0000; char *stack_bottom; stack_bottom = stack_top - rlimit.rlim_cur; /* * Add a fudge factor. Don't know why, but we get the signal * sometime after the bottom of the stack, as computed above, * has been reached. (It seems to be 8K, so we use something * larger.) */ stack_bottom -= 16384; if ((stack_bottom <= addr) && (addr <= stack_top)) { fprintf(stderr, "\nsegv_handler: C stack overflow. Try increasing stack limit (%ld).\n", rlimit.rlim_cur); segv_handle_now(signal, code, context); } } else { perror("getrlimit"); } } /* a *real* protection fault */ fprintf(stderr, "segv_handler: Real protection violation: %p, PC = %p\n", addr, os_context->uc_mcontext.gregs[1]); real_segv_handler(signal, code, context); } #else void segv_handler(HANDLER_ARGS) { caddr_t addr = code->si_addr; SAVE_CONTEXT(); #ifdef RED_ZONE_HIT if (os_control_stack_overflow(addr, context)) return; #endif if (!interrupt_maybe_gc(signal, code, context)) { /* a *real* protection fault */ fprintf(stderr, "segv_handler: Real protection violation: 0x%08x\n", addr); interrupt_handle_now(signal, code, context); } } #endif void os_install_interrupt_handlers(void) { interrupt_install_low_level_handler(SIGSEGV, segv_handler); } /* function definitions for register lvalues */ #ifndef i386 int * solaris_register_address(struct ucontext *context, int reg) { if (reg == 0) { static int zero; zero = 0; return &zero; } else if (reg < 16) { return &context->uc_mcontext.gregs[reg + 3]; } else if (reg < 32) { int *sp = (int *) context->uc_mcontext.gregs[REG_SP]; return &sp[reg - 16]; } else return 0; } #endif /* function defintions for backward compatibilty and static linking */ /* For now we put in some porting functions */ int sigblock(int mask) { sigset_t old, new; sigemptyset(&new); new.__sigbits[0] = mask; sigprocmask(SIG_BLOCK, &new, &old); return old.__sigbits[0]; } int sigsetmask(int mask) { sigset_t old, new; sigemptyset(&new); new.__sigbits[0] = mask; sigprocmask(SIG_SETMASK, &new, &old); return old.__sigbits[0]; } int openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp) { char *slavename; int masterfd, slavefd; if ((masterfd = open("/dev/ptmx", O_RDWR)) == -1) return -1; if (grantpt(masterfd) == -1) { close(masterfd); return -1; } if (unlockpt(masterfd) == -1) { close(masterfd); return -1; } if ((slavename = ptsname(masterfd)) == NULL) { close(masterfd); return -1; } if ((slavefd = open(slavename, O_RDWR | O_NOCTTY)) == -1) { close(masterfd); return -1; } *amaster = masterfd; *aslave = slavefd; ioctl(*aslave, I_PUSH, "ptem"); ioctl(*aslave, I_PUSH, "ldterm"); ioctl(*aslave, I_PUSH, "ttcompat"); if (name) strcpy(name, slavename); if (termp) tcsetattr(slavefd, TCSAFLUSH, termp); if (winp) ioctl(slavefd, TIOCSWINSZ, (char *) winp); return 0; } os_vm_address_t round_up_sparse_size(os_vm_address_t addr) { return (addr + SPARSE_BLOCK_SIZE - 1) & ~SPARSE_SIZE_MASK; } /* * An array of the start of the spaces which should have holes placed * after them. Must not include the dynamic spaces because the size * of the dynamic space can be controlled from the command line. */ static os_vm_address_t spaces[] = { READ_ONLY_SPACE_START, STATIC_SPACE_START, BINDING_STACK_START, CONTROL_STACK_START }; /* * The corresponding array for the size of each space. Be sure that * the spaces and holes don't overlap! The sizes MUST be on * SPARSE_BLOCK_SIZE boundaries. */ static unsigned long *space_size[] = { &read_only_space_size, &static_space_size, &binding_stack_size, &control_stack_size }; /* * The size of the hole to make. It should be strictly smaller than * SPARSE_BLOCK_SIZE. */ #define HOLE_SIZE 0x2000 void make_holes(void) { int k; os_vm_address_t hole; /* Make holes of the appropriate size for desired spaces */ for (k = 0; k < sizeof(spaces) / sizeof(spaces[0]); ++k) { hole = spaces[k] + *space_size[k]; if (os_validate(hole, HOLE_SIZE) == NULL) { fprintf(stderr, "ensure_space: Failed to validate hole of %d bytes at 0x%08lX\n", HOLE_SIZE, (unsigned long) hole); exit(1); } /* Make it inaccessible */ os_protect(hole, HOLE_SIZE, 0); } /* Round up the dynamic_space_size to the nearest SPARSE_BLOCK_SIZE */ dynamic_space_size = round_up_sparse_size(dynamic_space_size); /* Now make a hole for the dynamic spaces */ hole = dynamic_space_size + (os_vm_address_t) dynamic_0_space; if (os_validate(hole, HOLE_SIZE) == NULL) { fprintf(stderr, "ensure_space: Failed to validate hold of %d bytes at 0x%08lX\n", HOLE_SIZE, (unsigned long) hole); exit(1); } os_protect(hole, HOLE_SIZE, 0); #ifndef GENCGC hole = dynamic_space_size + (os_vm_address_t) dynamic_1_space; if (os_validate(hole, HOLE_SIZE) == NULL) { fprintf(stderr, "ensure_space: Failed to validate hole of %ld bytes at 0x%08X\n", HOLE_SIZE, (unsigned long) hole); exit(1); } os_protect(hole, HOLE_SIZE, 0); #endif } void * os_dlsym(const char *sym_name, lispobj lib_list) { static void *program_handle; void *sym_addr = 0; if (!program_handle) program_handle = dlopen((void *) 0, RTLD_LAZY | RTLD_GLOBAL); if (lib_list != NIL) { lispobj lib_list_head; for (lib_list_head = lib_list; lib_list_head != NIL; lib_list_head = (CONS(lib_list_head))->cdr) { struct cons *lib_cons = CONS(CONS(lib_list_head)->car); struct sap *dlhandle = (struct sap *) PTR(lib_cons->car); sym_addr = dlsym((void *) dlhandle->pointer, sym_name); if (sym_addr) return sym_addr; } } sym_addr = dlsym(program_handle, sym_name); return sym_addr; } #ifdef i386 unsigned long * os_sigcontext_reg(ucontext_t *scp, int index) { #if 0 fprintf(stderr, "os_sigcontext_reg index = %d\n", index); #endif switch (index) { case 0: return (unsigned long *) &scp->uc_mcontext.gregs[EAX]; case 2: return (unsigned long *) &scp->uc_mcontext.gregs[ECX]; case 4: return (unsigned long *) &scp->uc_mcontext.gregs[EDX]; case 6: return (unsigned long *) &scp->uc_mcontext.gregs[EBX]; case 8: return (unsigned long *) &scp->uc_mcontext.gregs[ESP]; case 10: return (unsigned long *) &scp->uc_mcontext.gregs[EBP]; case 12: return (unsigned long *) &scp->uc_mcontext.gregs[ESI]; case 14: return (unsigned long *) &scp->uc_mcontext.gregs[EDI]; } return NULL; } unsigned long * os_sigcontext_pc(ucontext_t *scp) { #if 0 fprintf(stderr, "os_sigcontext_pc = %p\n", scp->uc_mcontext.gregs[EIP]); #endif return (unsigned long *) &scp->uc_mcontext.gregs[EIP]; } unsigned char * os_sigcontext_fpu_reg(ucontext_t *scp, int offset) { fpregset_t *fpregs = &scp->uc_mcontext.fpregs; unsigned char *reg = NULL; if (offset < 8) { unsigned char *fpustate; unsigned char *stregs; /* * Not sure this is right. There is no structure defined for * the x87 fpu state in /usr/include/sys/regset.h */ /* Point to the fpchip_state */ fpustate = (unsigned char*) &fpregs->fp_reg_set.fpchip_state.state[0]; /* Skip to where the x87 fp registers are */ stregs = fpustate + 28; reg = stregs + 10*offset; } #ifdef FEATURE_SSE2 else { reg = (unsigned char*) &fpregs->fp_reg_set.fpchip_state.xmm[offset - 8]; } #endif return reg; } unsigned int os_sigcontext_fpu_modes(ucontext_t *scp) { unsigned int modes; unsigned short cw, sw; fpregset_t *fpr; unsigned int state; fpr = &scp->uc_mcontext.fpregs; cw = fpr->fp_reg_set.fpchip_state.state[0] & 0xffff; sw = fpr->fp_reg_set.fpchip_state.state[1] & 0xffff; modes = ((cw & 0x3f) << 7) | (sw & 0x3f); DPRINTF(0, (stderr, "cw = 0x%04x\n", cw)); DPRINTF(0, (stderr, "sw = 0x%04x\n", sw)); DPRINTF(0, (stderr, "modes = 0x%08x\n", modes)); #ifdef FEATURE_SSE2 /* * Add in the SSE2 part, if we're running the sse2 core. */ if (fpu_mode == SSE2) { unsigned long mxcsr; mxcsr = fpr->fp_reg_set.fpchip_state.mxcsr; DPRINTF(0, (stderr, "SSE2 modes = %08lx\n", mxcsr)); modes |= mxcsr; } #endif modes ^= (0x3f << 7); return modes; } boolean os_support_sse2() { return TRUE; } #endif