Newer
Older
* 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.
* $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/Linux-os.c,v 1.32 2007/07/30 07:24:46 cshapiro Exp $
*
*/
#include <stdio.h>
#include <sys/param.h>
#include <sys/file.h>
#include <errno.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>
#include <sys/resource.h>
#include <sys/wait.h>
#include <netdb.h>
#include <fpu_control.h>
#if defined GENCGC
#include "gencgc.h"
#endif
int PVE_stub_errno;
void
update_errno(void)
PVE_stub_errno = errno;
void
os_init(void)
/* We need this for mmap */
if (name.release[0] < '2') {
printf("Linux version must be later then 2.0.0!\n");
exit(2);
}
os_vm_page_size = getpagesize();
setfpucw(0x1272 | 4 | 8 | 16 | 32); /* No interrupts */
/*
* Round to nearest, double-float precision (not extended!), disable
* interrupts for everything except invalid.
*/
setfpucw(_FPU_RC_NEAREST | _FPU_DOUBLE | _FPU_MASK_PM | _FPU_MASK_UM |
_FPU_MASK_OM | _FPU_MASK_ZM | _FPU_MASK_DM);
sc_reg(ucontext_t *context, int offset)
switch (offset) {
case 0:
return &context->uc_mcontext.gregs[REG_EAX];
return &context->uc_mcontext.gregs[REG_ECX];
return &context->uc_mcontext.gregs[REG_EDX];
return &context->uc_mcontext.gregs[REG_EBX];
return &context->uc_mcontext.gregs[REG_ESP];
return &context->uc_mcontext.gregs[REG_EBP];
return &context->uc_mcontext.gregs[REG_ESI];
return &context->uc_mcontext.gregs[REG_EDI];
return (int *) 0;
sc_reg(struct sigcontext *c, int offset)
switch (offset) {
case 0:
return &c->rax;
return &c->rcx;
return &c->rdx;
return &c->rbx;
return &c->rsp;
return &c->rbp;
return &c->rsi;
return &c->rdi;
return &c->r8;
return &c->r9;
return &c->r10;
return &c->r11;
return &c->r12;
return &c->r13;
return &c->r14;
return &c->r15;
return (int *) 0;
os_vm_address_t
os_validate(os_vm_address_t addr, os_vm_size_t len)
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
if (addr)
flags |= MAP_FIXED;
addr = mmap(addr, len, OS_VM_PROT_ALL, flags, -1, 0);
if (addr == (os_vm_address_t) - 1) {
perror("mmap");
return NULL;
return addr;
void
os_invalidate(os_vm_address_t addr, os_vm_size_t len)
DPRINTF(0, (stderr, "os_invalidate %p %d\n", addr, 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)
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)
perror("mmap");
void
os_flush_icache(os_vm_address_t address, os_vm_size_t 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");
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)
os_vm_address_t newaddr;
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)
|| in_range_p(addr, CONTROL_STACK_START, CONTROL_STACK_SIZE)
|| in_range_p(addr, BINDING_STACK_START, BINDING_STACK_SIZE))
return TRUE;
return FALSE;
static void
sigsegv_handle_now(HANDLER_ARGS)
interrupt_handle_now(signal, code, context);
static siginfo_t tramp_code;
static ucontext_t tramp_context;
static void
sigsegv_handler_tramp(void)
sigsegv_handle_now(tramp_signal, &tramp_code, &tramp_context);
void
sigsegv_handler(HANDLER_ARGS)
int fault_addr = context->uc_mcontext.cr2;
int page_index = find_page_index((void *) fault_addr);
if (os_control_stack_overflow((void *) fault_addr, context))
/* 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(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;
DPRINTF(0, (stderr, "sigsegv: rip: %p\n", context->rip));
DPRINTF(0, (stderr, "sigsegv: eip: %x\n", context->uc_mcontext.gregs[REG_EIP]));
{
/* Switch back to the normal stack and invoke the Lisp signal
handler there. Global variables are used to pass the context
to the other stack. */
tramp_signal = signal;
tramp_code = *code;
tramp_context = *context;
SC_PC(context) = sigsegv_handler_tramp;
return;
}
sigsegv_handle_now(signal, code, context);
static void
sigsegv_handler(HANDLER_ARGS)
os_vm_address_t addr;
DPRINTF(0, (stderr, "sigsegv\n"));
interrupt_handle_now(signal, contextstruct);
#define CONTROL_STACK_TOP (((char*) CONTROL_STACK_START) + CONTROL_STACK_SIZE)
addr = arch_get_bad_addr(signal, code, context);
if (addr != NULL && context->sc_regs[reg_ALLOC] & (1 << 63)) {
context->sc_regs[reg_ALLOC] -= (1 << 63);
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 */
while (!(CONTROL_STACK_START <= *current_control_frame_pointer &&
*current_control_frame_pointer <= CONTROL_STACK_TOP))
((char *) current_control_frame_pointer) -= sizeof(lispobj);
ldb_monitor();
} else if (!interrupt_maybe_gc(signal, code, context))
interrupt_handle_now(signal, code, context);
static void
sigbus_handler(HANDLER_ARGS)
DPRINTF(1, (stderr, "sigbus:\n")); /* there is no sigbus in linux??? */
interrupt_handle_now(signal, code, context);
void
os_install_interrupt_handlers(void)
interrupt_install_low_level_handler(SIGSEGV, sigsegv_handler);
interrupt_install_low_level_handler(SIGBUS, sigbus_handler);
/* 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) {
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 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);
if (!sym_addr && dlerror()) {
return dlsym_fallback(program_handle, sym_name);