Newer
Older
* $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/solaris-os.c,v 1.21 2008/09/07 07:07:50 cshapiro Exp $
*
* 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 <stdio.h>
#include <sys/file.h>
#include <unistd.h>
#include <errno.h>
#include <sys/param.h>
#include <dlfcn.h>
#include <sys/resource.h>
#if defined(GENCGC)
#include "lisp.h"
#include "gencgc.h"
#endif
/* 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;
os_init_bailout(char *arg)
sprintf(buf, "os_init: %s", arg);
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);
/*
* 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) {
addr = NULL;
}
}
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)
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);
}
}
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)
|| in_range_p(addr, CONTROL_STACK_START, CONTROL_STACK_SIZE)
|| in_range_p(addr, BINDING_STACK_START, BINDING_STACK_SIZE));
}
/* ---------------------------------------------------------------- */
/*
#if defined(GENCGC)
void
segv_handle_now(HANDLER_ARGS)
interrupt_handle_now(signal, code, context);
void
segv_handler(HANDLER_ARGS)
caddr_t addr = code->si_addr;
int page_index;
SAVE_CONTEXT();
page_index = find_page_index((void *) addr);
#ifdef RED_ZONE_HIT
if (os_control_stack_overflow(addr, context))
return;
/* check if the fault is within the dynamic space. */
if (page_index != -1) {
/* Un-protect the page */
/* The page should have been marked write protected */
if (!PAGE_WRITE_PROTECTED(page_index))
fprintf(stderr,
"*** Sigsegv in page not marked as write protected\n");
os_protect((os_vm_address_t) page_address(page_index), PAGE_SIZE,
OS_VM_PROT_ALL);
page_table[page_index].flags &= ~PAGE_WRITE_PROTECTED_MASK;
page_table[page_index].flags |= PAGE_WRITE_PROTECT_CLEARED_MASK;
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/*
* 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\n", addr);
segv_handle_now(signal, code, context);
caddr_t addr = code->si_addr;
#ifdef RED_ZONE_HIT
if (os_control_stack_overflow(addr, context))
return;
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);
}
os_install_interrupt_handlers(void)
interrupt_install_low_level_handler(SIGSEGV, segv_handler);
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];
int *sp = (int *) context->uc_mcontext.gregs[REG_SP];
return &sp[reg - 16];
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/* 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];
}
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;