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.26 2006/03/17 02:56:45 rtoy Exp $
*
*/
#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>
#include <sys/resource.h>
#include <sys/wait.h>
#include <netdb.h>
#include <fpu_control.h>
#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;
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);
#if (LINUX_VERSION_CODE >= linuxversion(2,1,0)) || (__GNU_LIBRARY__ >= 6)
int
sc_reg(struct sigcontext *c, int offset)
int
sc_reg(struct sigcontext_struct *c, int offset)
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;
int
sc_reg(struct sigcontext *c, int offset)
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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;
case 16:
return c->r8;
case 18:
return c->r9;
case 20:
return c->r10;
case 22:
return c->r11;
case 24:
return c->r12;
case 26:
return c->r13;
case 28:
return c->r14;
case 30:
return c->r15;
void
os_save_context(void)
/*
* Called from interrupt handlers so C stuff knows things set in Lisp.
*/
void
os_set_context(void)
os_vm_address_t
os_validate(os_vm_address_t addr, os_vm_size_t len)
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
os_vm_address_t out_addr;
DPRINTF(0, (stderr, "os_validate %p %d => ", addr, len));
out_addr = mmap(addr, len, OS_VM_PROT_ALL, flags, -1, 0);
if (out_addr == (os_vm_address_t) - 1) {
perror("mmap");
return NULL;
if (addr && (addr != out_addr)) {
fprintf(stderr,
"Couldn't mmap at %p, len %d; got mapping at %p instead", addr,
len, out_addr);
munmap(addr, len);
return NULL;
DPRINTF(0, (stderr, "%p\n", out_addr));
return out_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, contextstruct);
static int tramp_signal;
static struct sigcontext tramp_contextstruct;
static void
sigsegv_handler_tramp(void)
sigsegv_handle_now(tramp_signal, tramp_contextstruct);
assert(0);
void
sigsegv_handler(HANDLER_ARGS)
#if (LINUX_VERSION_CODE >= linuxversion(2,1,0)) || (__GNU_LIBRARY__ >= 6)
int fault_addr = ((struct sigcontext *) (&contextstruct))->cr2;
int fault_addr = ((struct sigcontext_struct *) (&contextstruct))->cr2;
int page_index = find_page_index((void *) fault_addr);
if (os_control_stack_overflow((void *) fault_addr, &contextstruct))
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(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: %lx\n", context->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_contextstruct = contextstruct;
SC_PC(context) = sigsegv_handler_tramp;
return;
}
sigsegv_handle_now(signal, contextstruct);
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, contextstruct);
interrupt_handle_now(signal, code, context);
#endif
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);