Skip to content
Snippets Groups Projects
Linux-os.c 8.96 KiB
Newer Older
ram's avatar
ram committed
/*
 * Linux-os.c. 
pw's avatar
pw committed
 * From FreeBSD-os.c
ram's avatar
ram committed
 * From osf1-os.c,v 1.1 94/03/27 15:30:51 hallgren 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 the OSF1 version.  By Sean Hallgren.
 * Much hacked by Paul Werkowski
 * Morfed from the FreeBSD file by Peter Van Eynde (July 1996)
 * GENCGC support by Douglas Crosher, 1996, 1997.
dtc's avatar
dtc committed
 * Alpha support by Julian Dolby, 1999.
ram's avatar
ram committed
 *
 * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/Linux-os.c,v 1.20 2004/07/08 04:10:09 rtoy Exp $
ram's avatar
ram committed
 *
 */

#include <stdio.h>
#include <sys/param.h>
#include <sys/file.h>
#include <errno.h>
#include "./signal.h"
#include "os.h"
#include "arch.h"
#include "globals.h"
#include "interrupt.h"
#include "lispregs.h"
#include "internals.h"
#include <sys/socket.h>
#include <sys/utsname.h>

#include <sys/types.h>
#include <signal.h>
/* #include <sys/sysinfo.h> */
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
pw's avatar
pw committed
#include <sys/resource.h>
#include <sys/wait.h>
#include <netdb.h>
moore's avatar
 
moore committed
#include <link.h>
#include <dlfcn.h>
ram's avatar
ram committed

pw's avatar
pw committed
#include "validate.h"
ram's avatar
ram committed
size_t os_vm_page_size;

dtc's avatar
dtc committed
#define DPRINTF(t,a) {if (t) fprintf a;}
ram's avatar
ram committed

#if defined GENCGC
#include "gencgc.h"
#endif

#if ((LINUX_VERSION_CODE >= linuxversion(2,1,0)) || (__GNU_LIBRARY__ >= 6))
int PVE_stub_errno;
#endif

#if ((LINUX_VERSION_CODE >= linuxversion(2,1,0)) || (__GNU_LIBRARY__ >= 6))
void update_errno (void)
{
  PVE_stub_errno = errno;
}
#endif

ram's avatar
ram committed

dtc's avatar
dtc committed
void os_init(void)
ram's avatar
ram committed
{
  struct utsname name;

  uname(&name);

dtc's avatar
dtc committed
  /* We need this for mmap */
ram's avatar
ram committed

dtc's avatar
dtc committed
  if (name.release[0] < '2')
ram's avatar
ram committed
   {
    printf("Linux version must be later then 2.0.0!\n");
    exit(2);
  }

dtc's avatar
dtc committed
  os_vm_page_size = getpagesize();

cwang's avatar
cwang committed
#if defined(i386) || defined(__x86_64)
dtc's avatar
dtc committed
  setfpucw(0x1372|4|8|16|32); /* No interrupts */
pw's avatar
pw committed
#endif
ram's avatar
ram committed
}

dtc's avatar
dtc committed
#ifdef i386
#if (LINUX_VERSION_CODE >= linuxversion(2,1,0)) || (__GNU_LIBRARY__ >= 6)
dtc's avatar
dtc committed
int sc_reg(struct sigcontext *c, int offset)
pw's avatar
pw committed
#else
dtc's avatar
dtc committed
int sc_reg(struct sigcontext_struct *c, int offset)
pw's avatar
pw committed
#endif
ram's avatar
ram committed
{
  switch(offset)
    {
    case  0: return c->eax;
    case  2: return c->ecx;
    case  4: return c->edx;
    case  6: return c->ebx;
    case  8: return c->esp;
    case 10: return c->ebp;
    case 12: return c->esi;
    case 14: return c->edi;
    }
  return 0;
}
pw's avatar
pw committed
#endif
ram's avatar
ram committed

cwang's avatar
cwang committed
#ifdef __x86_64
#if (LINUX_VERSION_CODE >= linuxversion(2,1,0)) || (__GNU_LIBRARY__ >= 6)
int sc_reg(struct sigcontext *c, int offset)
#else
int sc_reg(struct sigcontext_struct *c, int offset)
#endif
{
  switch(offset)
    {
    case  0: return c->rax;
    case  2: return c->rcx;
    case  4: return c->rdx;
    case  6: return c->rbx;
    case  8: return c->rsp;
    case 10: return c->rbp;
    case 12: return c->rsi;
    case 14: return c->rdi;
    }
  return 0;
}
#endif

dtc's avatar
dtc committed
void os_save_context(void)
ram's avatar
ram committed
{
dtc's avatar
dtc committed
  /*
   * Called from interrupt handlers so C stuff knows things set in Lisp.
ram's avatar
ram committed
   */
}

dtc's avatar
dtc committed
void os_set_context(void)
ram's avatar
ram committed
{
}

dtc's avatar
dtc committed
os_vm_address_t os_validate(os_vm_address_t addr, os_vm_size_t len)
ram's avatar
ram committed
{
  int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
ram's avatar
ram committed

dtc's avatar
dtc committed
  if (addr)
    flags |= MAP_FIXED;
  else
    flags |= MAP_VARIABLE;
ram's avatar
ram committed

  DPRINTF(0, (stderr, "os_validate %p %d => ", addr, len));

  addr = mmap(addr, len, OS_VM_PROT_ALL, flags, -1, 0);

  if(addr == (os_vm_address_t) -1)
ram's avatar
ram committed
    {
      perror("mmap");
      return NULL;
ram's avatar
ram committed
    }
  DPRINTF(0, (stderr, "%p\n", addr));
ram's avatar
ram committed
}

dtc's avatar
dtc committed
void os_invalidate(os_vm_address_t addr, os_vm_size_t len)
ram's avatar
ram committed
{
  DPRINTF(0, (stderr, "os_invalidate %p %d\n", addr, len));
dtc's avatar
dtc committed

  if (munmap(addr, len) == -1)
ram's avatar
ram committed
    perror("munmap");
}

dtc's avatar
dtc committed
os_vm_address_t os_map(int fd, int offset, os_vm_address_t addr,
		       os_vm_size_t len)
ram's avatar
ram committed
{
dtc's avatar
dtc committed
  addr = mmap(addr, len,
	      OS_VM_PROT_ALL,
	      MAP_PRIVATE | MAP_FILE | MAP_FIXED,
	      fd, (off_t) offset);

  if (addr == (os_vm_address_t) -1)
ram's avatar
ram committed
    perror("mmap");
dtc's avatar
dtc committed

ram's avatar
ram committed
  return addr;
}

dtc's avatar
dtc committed
void os_flush_icache(os_vm_address_t address, os_vm_size_t length)
ram's avatar
ram committed
{
}

dtc's avatar
dtc committed
void os_protect(os_vm_address_t address, os_vm_size_t length,
		os_vm_prot_t prot)
ram's avatar
ram committed
{
dtc's avatar
dtc committed
  if (mprotect(address, length, prot) == -1)
ram's avatar
ram committed
    perror("mprotect");
}
dtc's avatar
dtc committed

ram's avatar
ram committed

dtc's avatar
dtc committed

static boolean in_range_p(os_vm_address_t a, lispobj sbeg, size_t slen)
ram's avatar
ram committed
{
dtc's avatar
dtc committed
  char* beg = (char*) sbeg;
  char* end = (char*) sbeg + slen;
  char* adr = (char*) a;
ram's avatar
ram committed
  return (adr >= beg && adr < end);
}
dtc's avatar
dtc committed

boolean valid_addr(os_vm_address_t addr)
ram's avatar
ram committed
{
  os_vm_address_t newaddr;
dtc's avatar
dtc committed
  newaddr = os_trunc_to_page(addr);

  if (   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  )
      || in_range_p(addr, DYNAMIC_1_SPACE_START, dynamic_space_size  )
dtc's avatar
dtc committed
      || in_range_p(addr, CONTROL_STACK_START  , CONTROL_STACK_SIZE  )
      || in_range_p(addr, BINDING_STACK_START  , BINDING_STACK_SIZE  ))
ram's avatar
ram committed
    return TRUE;
  return FALSE;
}


static void sigsegv_handle_now(HANDLER_ARGS)
{
  interrupt_handle_now(signal, contextstruct);
}

void sigsegv_handler(HANDLER_ARGS)
ram's avatar
ram committed
{
  GET_CONTEXT

#if (LINUX_VERSION_CODE >= linuxversion(2,1,0)) || (__GNU_LIBRARY__ >= 6)
  int  fault_addr = ((struct sigcontext *) (&contextstruct))->cr2;
#else
dtc's avatar
dtc committed
  int  fault_addr = ((struct sigcontext_struct *) (&contextstruct))->cr2;
dtc's avatar
dtc committed
  int  page_index = find_page_index((void *) fault_addr);
  if (os_control_stack_overflow ((void *) fault_addr, &contextstruct))
    return;
#endif

  /* check if the fault is within the dynamic space. */
dtc's avatar
dtc committed
  if (page_index != -1) {
    /* Un-protect the page */
dtc's avatar
dtc committed

    /* The page should have been marked write protected */
    if (!PAGE_WRITE_PROTECTED(page_index))
dtc's avatar
dtc committed
      fprintf(stderr, "*** Sigsegv in page not marked as write protected\n");
    os_protect(page_address(page_index), 4096, OS_VM_PROT_ALL);
    page_table[page_index].flags &= ~PAGE_WRITE_PROTECTED_MASK;
    page_table[page_index].flags |= PAGE_WRITE_PROTECT_CLEARED_MASK;
dtc's avatar
dtc committed

cwang's avatar
cwang committed
#if defined(__x86_64)
  DPRINTF(0,(stderr,"sigsegv: rip: %p\n",context->rip));
#else
  DPRINTF(0,(stderr,"sigsegv: eip: %lx\n",context->eip));
cwang's avatar
cwang committed
#endif
  sigsegv_handle_now(signal, contextstruct);
ram's avatar
ram committed
}
dtc's avatar
dtc committed
static void sigsegv_handler(HANDLER_ARGS)
ram's avatar
ram committed
{
pw's avatar
pw committed
  os_vm_address_t addr;

#ifdef i386
ram's avatar
ram committed
  GET_CONTEXT
pw's avatar
pw committed
#endif
ram's avatar
ram committed

dtc's avatar
dtc committed
  DPRINTF(0, (stderr, "sigsegv\n"));
pw's avatar
pw committed
#ifdef i386
dtc's avatar
dtc committed
  interrupt_handle_now(signal, contextstruct);
pw's avatar
pw committed
#else
dtc's avatar
dtc committed
#define CONTROL_STACK_TOP (((char*) CONTROL_STACK_START) + CONTROL_STACK_SIZE)
pw's avatar
pw committed

  addr = arch_get_bad_addr(signal,code,context);

dtc's avatar
dtc committed
  if (addr != NULL && context->sc_regs[reg_ALLOC] & (1 << 63)) {
    context->sc_regs[reg_ALLOC] -= (1 << 63);
pw's avatar
pw committed
    interrupt_handle_pending(context);
  } else if (addr > CONTROL_STACK_TOP && addr < BINDING_STACK_START) {
    fprintf(stderr, "Possible stack overflow at 0x%08lX!\n", addr);
    /* try to fix control frame pointer */
dtc's avatar
dtc committed
    while (!(CONTROL_STACK_START <= *current_control_frame_pointer &&
	     *current_control_frame_pointer <= CONTROL_STACK_TOP))
      ((char*) current_control_frame_pointer) -= sizeof(lispobj);
pw's avatar
pw committed
    ldb_monitor();
  } else if (!interrupt_maybe_gc(signal, code, context))
    interrupt_handle_now(signal, code, context);
#endif
ram's avatar
ram committed
#endif
dtc's avatar
dtc committed
static void sigbus_handler(HANDLER_ARGS)
cwang's avatar
cwang committed
#if defined(i386) || defined(__x86_64)
pw's avatar
pw committed
#endif
dtc's avatar
dtc committed
  DPRINTF(1, (stderr, "sigbus:\n")); /* there is no sigbus in linux??? */
cwang's avatar
cwang committed
#if defined(i386) || defined(__x86_64)
dtc's avatar
dtc committed
  interrupt_handle_now(signal, contextstruct);
pw's avatar
pw committed
#else
dtc's avatar
dtc committed
  interrupt_handle_now(signal, code, context);
pw's avatar
pw committed
#endif  
ram's avatar
ram committed
}

dtc's avatar
dtc committed
void os_install_interrupt_handlers(void)
ram's avatar
ram committed
{
dtc's avatar
dtc committed
  interrupt_install_low_level_handler(SIGSEGV, sigsegv_handler);
  interrupt_install_low_level_handler(SIGBUS, sigbus_handler);
ram's avatar
ram committed
}
moore's avatar
 
moore committed

/* Some symbols, most notably stat and lstat, don't appear at all in
   the glibc .so files as a result of preprocessor and linker magic /
   braindamage.  So, try falling back to a stub in linux-stubs.S that
   will call the proper function if it's one of those. */

static void *dlsym_fallback(void *handle, const char *name)
{
    char newsym[1024];
    void *sym_addr;

    strcpy(newsym, "PVE_stub_");
    strcat(newsym, name);
    sym_addr = dlsym(handle, newsym);
#ifdef DEBUG
    if (sym_addr == 0) {
moore's avatar
 
moore committed
	fputs(dlerror(), stderr);
    }
moore's avatar
 
moore committed
    return sym_addr;
}

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) {
moore's avatar
 
moore committed
	    struct cons *lib_cons = CONS(CONS(lib_list_head)->car);
	    struct sap *dlhandle = (struct sap *)PTR(lib_cons->car);
moore's avatar
 
moore committed

	    sym_addr = dlsym((void *)dlhandle->pointer, sym_name);
	    if (sym_addr)
		return sym_addr;
	}
    }
    sym_addr = dlsym(program_handle, sym_name);
    if (!sym_addr && dlerror()) {
	return dlsym_fallback(program_handle,sym_name);
    } else {
	return sym_addr;
    }
}