Skip to content
solaris-os.c 9.91 KiB
Newer Older
dtc's avatar
dtc committed
/*
 * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/solaris-os.c,v 1.3 2000/11/03 18:13:45 dtc Exp $
dtc's avatar
dtc committed
 *
 * 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.
 *
 */

#define DEBUG

#include <stdio.h>

#include <signal.h>
#include <sys/file.h>

#include <unistd.h>
#include <errno.h>
#include <sys/param.h>
#define OS_PROTERR		SEGV_ACCERR
#define OS_MAPERR		SEGV_MAPERR
#define OS_HASERRNO(code)	((code)->si_errno != 0)
#define OS_ERRNO(code)		((code)->si_errno)

#include "os.h"

/* To get dynamic_0_space and friends */
#include "globals.h"
/* To get memory map */
#include "sparc-validate.h"

dtc's avatar
dtc committed
/* block size must be larger than the system page size */
#define SPARSE_BLOCK_SIZE (1<<15)
#define SPARSE_SIZE_MASK (SPARSE_BLOCK_SIZE-1)

#define PROT_DEFAULT OS_VM_PROT_ALL

#define OFFSET_NONE ((os_vm_offset_t)(~0))

#define EMPTYFILE "/tmp/empty"
#define ZEROFILE "/dev/zero"

#define INITIAL_MAX_SEGS 32
#define GROW_MAX_SEGS 16

extern char *getenv();

/* ---------------------------------------------------------------- */

#define ADJ_OFFSET(off,adj) (((off)==OFFSET_NONE) ? OFFSET_NONE : ((off)+(adj)))

long os_vm_page_size=(-1);
static long os_real_page_size=(-1);

static int zero_fd=(-1), empty_fd=(-1);

static os_vm_size_t real_page_size_difference=0;

static void os_init_bailout(arg)
char *arg;
{
    char buf[500];
    sprintf(buf,"os_init: %s",arg);
    perror(buf);
    exit(1);
}

void os_init()
{
    char *empty_file=getenv("CMUCL_EMPTYFILE");

    if(empty_file==NULL)
	empty_file=EMPTYFILE;

    empty_fd=open(empty_file,O_RDONLY|O_CREAT);
    if(empty_fd<0)
	os_init_bailout(empty_file);
    unlink(empty_file);

    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 (%d > %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;

  if((addr=mmap(addr,len,OS_VM_PROT_ALL,flags,zero_fd,0)) == (os_vm_address_t) -1)
    perror("mmap");

  return addr;
}

void
os_invalidate(os_vm_address_t addr, os_vm_size_t len)
{
  if(munmap(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=mmap(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(address, length)
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 - %x\n", address,length);
    flush_icache(address,length);
  }
}

void
os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot)
{
  if(mprotect(address, length, prot) == -1)
    perror("mprotect");
}

boolean valid_addr(test)
os_vm_address_t test;
{
  /* XXX needs implementing */
  return 1;
}

/* ---------------------------------------------------------------- */

/*
dtc's avatar
dtc committed
 * Running into the gc trigger page will end up here...
dtc's avatar
dtc committed
 */
void segv_handler(HANDLER_ARGS)
{
dtc's avatar
dtc committed
  caddr_t addr = code->si_addr;

  SAVE_CONTEXT();
  
  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);
  }
dtc's avatar
dtc committed
}

void os_install_interrupt_handlers()
{
    interrupt_install_low_level_handler(SIGSEGV,segv_handler);
}


/* function defintions for register lvalues */

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;
}
/* function defintions for backward compatibilty and static linking */

/* For now we put in some porting functions */

#ifndef SOLARIS25
int
getdtablesize(void)
{
    return sysconf(_SC_OPEN_MAX);
}

char *
getwd(char *path)
{
    return getcwd(path, MAXPATHLEN);
}

int
getpagesize(void)
{
    return sysconf(_SC_PAGESIZE);
}


#include <sys/procfs.h>
/* Old rusage definition */
struct  rusage {
        struct timeval ru_utime;        /* user time used */
        struct timeval ru_stime;        /* system time used */
        long    ru_maxrss;
#define ru_first        ru_ixrss
        long    ru_ixrss;               /* XXX: 0 */
        long    ru_idrss;               /* XXX: sum of rm_asrss */
        long    ru_isrss;               /* XXX: 0 */
        long    ru_minflt;              /* any page faults not requiring I/O */
        long    ru_majflt;              /* any page faults requiring I/O */
        long    ru_nswap;               /* swaps */
        long    ru_inblock;             /* block input operations */
        long    ru_oublock;             /* block output operations */
        long    ru_msgsnd;              /* messages sent */
        long    ru_msgrcv;              /* messages received */
        long    ru_nsignals;            /* signals received */
        long    ru_nvcsw;               /* voluntary context switches */
        long    ru_nivcsw;              /* involuntary " */
#define ru_last         ru_nivcsw
};


int
getrusage(int who, struct rusage *rp)
{
    memset(rp, 0, sizeof(struct rusage));
    return 0;
}

int
setreuid()
{
    fprintf(stderr,"setreuid unimplemented\n");
    errno = ENOSYS;
    return -1;
}

int
setregid()
{
    fprintf(stderr,"setregid unimplemented\n");
    errno = ENOSYS;
    return -1;
}

int
gethostid()
{
    fprintf(stderr,"gethostid unimplemented\n");
    errno = ENOSYS;
    return -1;
}

int
killpg(int pgrp, int sig)
{
    if (pgrp < 0) {
	errno = ESRCH;
	return -1;
    }
    return kill(-pgrp, sig);
}
#endif

int
sigblock(int mask)
{
    sigset_t old, new;

    sigemptyset(&new);
    new.__sigbits[0] = mask;

    sigprocmask(SIG_BLOCK, &new, &old);

    return old.__sigbits[0];
}

#ifndef SOLARIS25
int
wait3(int *status, int options, struct rusage *rp)
{
    if (rp)
	memset(rp, 0, sizeof(struct rusage));
    return waitpid(-1, status, options);
}
#endif

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 %ld bytes at 0x%08X\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 %ld bytes at 0x%08X\n",
              HOLE_SIZE,
              (unsigned long)hole);
      exit(1);
    }
  os_protect(hole, HOLE_SIZE, 0);

  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);
}