Newer
Older
* $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/solaris-os.c,v 1.26 2010/11/12 12:57:32 rtoy 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 <stropts.h>
#include <termios.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;
#ifdef RED_ZONE_HIT
if (os_control_stack_overflow(addr, context))
return;
if (gc_write_barrier(code->si_addr))
return;
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/*
* 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,
context->uc_mcontext.gregs[1]);
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];
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/* 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];
}
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
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;