Skip to content
Snippets Groups Projects
Commit c871866f authored by wlott's avatar wlott
Browse files

Initial revision

parent 6e17aece
No related branches found
No related tags found
No related merge requests found
/*
* $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/sunos-os.c,v 1.1 1992/09/08 20:32:23 wlott 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 SunOS version.
* March 1991, Miles Bader <miles@cogsci.ed.ack.uk> & ted <ted@edu.NMSU>
*
*/
/* #define DEBUG */
#include <stdio.h>
#include <signal.h>
#include <sys/file.h>
#include "os.h"
/* 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 MAX_SEGS 64
extern char *getenv();
/* ---------------------------------------------------------------- */
#define ADJ_OFFSET(off,adj) (((off)==OFFSET_NONE) ? OFFSET_NONE : ((off)+(adj)))
long os_vm_page_size=(-1);
static struct segment {
os_vm_address_t start; /* note: start & length are expected to be on page */
os_vm_size_t length; /* boundaries */
long file_offset;
short mapped_fd;
short protection;
} addr_map[MAX_SEGS];
static int n_segments=0;
static int zero_fd=(-1), empty_fd=(-1);
static os_vm_address_t last_fault=0;
static os_vm_size_t real_page_size_difference=0;
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);
unlink(empty_file);
zero_fd=open(ZEROFILE,O_RDONLY);
os_vm_page_size=getpagesize();
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;
}
}
/* ---------------------------------------------------------------- */
void seg_force_resident(struct segment *seg,
os_vm_address_t addr,
os_vm_size_t len)
{
int prot=seg->protection;
if(prot!=0){
os_vm_address_t end=addr+len, touch=addr;
while(touch<end){
int contents=(*(char *)touch);
if(prot&OS_VM_PROT_WRITE)
(*(char *)touch)=contents;
touch=(os_vm_address_t)(((long)touch+SPARSE_BLOCK_SIZE)&~SPARSE_SIZE_MASK);
}
}
}
static struct segment *seg_create_nomerge(addr,len,protection,mapped_fd,file_offset)
os_vm_address_t addr;
os_vm_size_t len;
int protection;
int mapped_fd;
{
int n;
struct segment *seg;
if(len==0)
return NULL;
if(n_segments==MAX_SEGS){
fprintf(stderr,"seg_create_nomerge: Out of segments\n");
return NULL;
}
for(n=n_segments, seg=addr_map; n>0; n--, seg++)
if(addr<seg->start){
seg=(&addr_map[n_segments]);
while(n-->0){
seg[0]=seg[-1];
seg--;
}
break;
}
n_segments++;
seg->start=addr;
seg->length=len;
seg->protection=protection;
seg->mapped_fd=mapped_fd;
seg->file_offset=file_offset;
return seg;
}
/* returns the first segment containing addr */
static struct segment *seg_find(addr)
os_vm_address_t addr;
{
int n;
struct segment *seg;
for(n=n_segments, seg=addr_map; n>0; n--, seg++)
if(seg->start<=addr && seg->start+seg->length>addr)
return seg;
return NULL;
}
/* returns TRUE if the range from addr to addr+len intersects with any segment */
static boolean collides_with_seg_p(addr,len)
os_vm_address_t addr;
os_vm_size_t len;
{
int n;
struct segment *seg;
os_vm_address_t end=addr+len;
for(n=n_segments, seg=addr_map; n>0; n--, seg++)
if(seg->start>=end)
return FALSE;
else if(seg->start+seg->length>addr)
return TRUE;
return FALSE;
}
#define seg_last_p(seg) (((seg)-addr_map)>=n_segments-1)
static void seg_destroy(seg)
struct segment *seg;
{
if(seg!=NULL){
int n;
for(n=seg-addr_map+1; n<n_segments; n++){
seg[0]=seg[1];
seg++;
}
n_segments--;
}
}
static void seg_try_merge_next(seg)
struct segment *seg;
{
struct segment *nseg=seg+1;
if(!seg_last_p(seg)
&& seg->start+seg->length==nseg->start
&& seg->protection==nseg->protection
&& seg->mapped_fd==nseg->mapped_fd
&& ADJ_OFFSET(seg->file_offset,seg->length)==nseg->file_offset)
{
/* can merge with the next segment */
#ifdef DEBUG
fprintf(stderr,
";;; seg_try_merge: Merged 0x%08x[0x%08x] with 0x%08x[0x%08x]\n",
seg->start,seg->length,nseg->start,nseg->length);
#endif
if(((long)nseg->start&SPARSE_SIZE_MASK)!=0){
/*
* if not on a block boundary, we have to ensure both parts
* of a common block are in a known state
*/
seg_force_resident(seg,nseg->start-1,1);
seg_force_resident(nseg,nseg->start,1);
}
seg->length+=nseg->length;
seg_destroy(nseg);
}
}
/*
* Try to merge seg with adjacent segments.
*/
static void seg_try_merge_adjacent(seg)
struct segment *seg;
{
if(!seg_last_p(seg))
seg_try_merge_next(seg);
if(seg>addr_map)
seg_try_merge_next(seg-1);
}
static struct segment *seg_create(addr,len,protection,mapped_fd,file_offset)
os_vm_address_t addr;
os_vm_size_t len;
int protection;
int mapped_fd;
{
struct segment *seg=seg_create_nomerge(addr,len,protection,mapped_fd,file_offset);
if(seg!=NULL)
seg_try_merge_adjacent(seg);
return seg;
}
/*
* Change the attributes of the given range of an existing segment, and return
* a segment corresponding to the new bit.
*/
static struct segment *seg_change_range(seg,addr,len,protection,mapped_fd,file_offset)
struct segment *seg;
os_vm_address_t addr;
os_vm_size_t len;
int protection;
int mapped_fd;
{
os_vm_address_t end=addr+len;
if(len==0)
return NULL;
if(protection!=seg->protection
|| mapped_fd!=seg->mapped_fd
|| file_offset!=ADJ_OFFSET(seg->file_offset,addr-seg->start))
{
os_vm_size_t old_len=seg->length, seg_offset=(addr-seg->start);
if(old_len<len+seg_offset){
struct segment *next=seg+1;
#ifdef DEBUG
fprintf(stderr,
";;; seg_change_range: region 0x%08x[0x%08x] overflows 0x%08x[0x%08x]\n",
addr,len,
seg->start,old_len);
#endif
while(!seg_last_p(seg) && next->start+next->length<=end){
#ifdef DEBUG
fprintf(stderr,
";;; seg_change_range: merging extra segment 0x%08x[0x%08x]\n",
next->start,
next->length);
#endif
seg_destroy(next);
}
if(!seg_last_p(seg) && next->start<end){
next->length-=end-next->start;
next->start=end;
old_len=next->start-seg->start;
}else
old_len=len+seg_offset;
#ifdef DEBUG
fprintf(stderr,
";;; seg_change_range: extended first seg to 0x%08x[0x%08x]\n",
seg->start,
old_len);
#endif
}
if(seg_offset+len<old_len){
/* add second part of old segment */
seg_create_nomerge(end,
old_len-(seg_offset+len),
seg->protection,
seg->mapped_fd,
ADJ_OFFSET(seg->file_offset,seg_offset+len));
#ifdef DEBUG
fprintf(stderr,
";;; seg_change_range: Split off end of 0x%08x[0x%08x]: 0x%08x[0x%08x]\n",
seg->start,old_len,
end,old_len-(seg_offset+len));
#endif
}
if(seg_offset==0){
seg->length=len;
seg->protection=protection;
seg->mapped_fd=mapped_fd;
seg->file_offset=file_offset;
}else{
/* adjust first part of remaining old segment */
seg->length=seg_offset;
#ifdef DEBUG
fprintf(stderr,
";;; seg_change_range: Split off beginning of 0x%08x[0x%08x]: 0x%08x[0x%08x]\n",
seg->start,old_len,
seg->start,seg_offset);
#endif
/* add new middle segment for new protected region */
seg=seg_create_nomerge(addr,len,protection,mapped_fd,file_offset);
}
seg_try_merge_adjacent(seg);
last_fault=0;
}
return seg;
}
/* ---------------------------------------------------------------- */
static os_vm_address_t mapin(addr,len,protection,map_fd,offset,is_readable)
os_vm_address_t addr;
os_vm_size_t len;
int protection;
int map_fd;
long offset;
int is_readable;
{
os_vm_address_t real;
boolean sparse=(len>=SPARSE_BLOCK_SIZE);
if(offset!=OFFSET_NONE
&& (offset<os_vm_page_size || (offset&(os_vm_page_size-1))!=0))
{
fprintf(stderr,
"mapin: file offset (%d) not multiple of pagesize (%d)\n",
offset,
os_vm_page_size);
}
if(addr==NULL)
len+=real_page_size_difference; /* futz around to get an aligned region */
last_fault=0;
real=(os_vm_address_t)
mmap((caddr_t)addr,
(long)len,
sparse ? (is_readable ? PROT_READ|PROT_EXEC : 0) : protection,
(addr==NULL? 0 : MAP_FIXED)|MAP_PRIVATE,
(is_readable || !sparse) ? map_fd : empty_fd,
(off_t)(offset==OFFSET_NONE ? 0 : offset));
if((long)real==-1){
perror("mapin: mmap");
return NULL;
}
if(addr==NULL){
/*
* now play around with what the os gave us to make it align by
* our standards (which is why we overallocated)
*/
os_vm_size_t overflow;
addr=os_round_up_to_page(real);
if(addr!=real)
munmap(real,addr-real);
overflow=real_page_size_difference-(addr-real);
if(overflow!=0)
munmap(addr+len-real_page_size_difference,overflow);
real=addr;
}
return real;
}
static os_vm_address_t map_and_remember(addr,len,protection,map_fd,offset,is_readable)
os_vm_address_t addr;
os_vm_size_t len;
int protection;
int map_fd;
long offset;
int is_readable;
{
os_vm_address_t real=mapin(addr,len,protection,map_fd,offset,is_readable);
if(real!=NULL){
struct segment *seg=seg_find(real);
if(seg!=NULL)
seg=seg_change_range(seg,real,len,protection,map_fd,offset);
else
seg=seg_create(real,len,protection,map_fd,offset);
if(seg==NULL){
munmap(real,len);
return NULL;
}
}
#ifdef DEBUG
fprintf(stderr,";;; map_and_remember: 0x%08x[0x%08x] offset: %d, mapped to: %d\n",
real,len,offset,map_fd);
#endif
return real;
}
/* ---------------------------------------------------------------- */
os_vm_address_t os_validate(addr, len)
os_vm_address_t addr;
os_vm_size_t len;
{
addr=os_trunc_to_page(addr);
len=os_round_up_size_to_page(len);
#ifdef DEBUG
fprintf(stderr, ";;; os_validate: 0x%08x[0x%08x]\n",addr,len);
#endif
if(addr!=NULL && collides_with_seg_p(addr,len))
return NULL;
return map_and_remember(addr,len,PROT_DEFAULT,zero_fd,OFFSET_NONE,FALSE);
}
void os_invalidate(addr, len)
os_vm_address_t addr;
os_vm_size_t len;
{
struct segment *seg=seg_find(addr);
addr=os_trunc_to_page(addr);
len=os_round_up_size_to_page(len);
#ifdef DEBUG
fprintf(stderr, ";;; os_invalidate: 0x%08x[0x%08x]\n",addr,len);
#endif
if(seg==NULL)
fprintf(stderr, "os_invalidate: Unknown segment: 0x%08x[0x%08x]\n",addr,len);
else{
seg=seg_change_range(seg,addr,len,0,0,OFFSET_NONE);
if(seg!=NULL)
seg_destroy(seg);
last_fault=0;
if(munmap(addr,len)!=0)
perror("os_invalidate: munmap");
}
}
os_vm_address_t os_map(fd, offset, addr, len)
int fd;
int offset;
os_vm_address_t addr;
long len;
{
addr=os_trunc_to_page(addr);
len=os_round_up_size_to_page(len);
#ifdef DEBUG
fprintf(stderr, ";;; os_map: 0x%08x[0x%08x]\n",addr,len);
#endif
return map_and_remember(addr,len,PROT_DEFAULT,fd,offset,TRUE);
}
void os_flush_icache(address, length)
os_vm_address_t address;
os_vm_size_t length;
{
#if defined(MACH) && defined(mips)
vm_machine_attribute_val_t flush;
kern_return_t kr;
flush = MATTR_VAL_ICACHE_FLUSH;
kr = vm_machine_attribute(task_self(), address, length,
MATTR_CACHE, &flush);
if (kr != KERN_SUCCESS)
mach_error("Could not flush the instruction cache", kr);
#endif
}
void os_protect(addr, len, prot)
os_vm_address_t addr;
os_vm_size_t len;
int prot;
{
struct segment *seg=seg_find(addr);
addr=os_trunc_to_page(addr);
len=os_round_up_size_to_page(len);
#ifdef DEBUG
fprintf(stderr,";;; os_protect: 0x%08x[0x%08x]\n",addr,len);
#endif
if(seg!=NULL){
int old_prot=seg->protection;
if(prot!=old_prot){
/*
* oooooh, sick: we have to make sure all the pages being protected have
* faulted in, so they're in a known state...
*/
seg_force_resident(seg,addr,len);
seg_change_range(seg,addr,len,prot,seg->mapped_fd,seg->file_offset);
if(mprotect((caddr_t)addr,(long)len,prot)!=0)
perror("os_unprotect: mprotect");
}
}else
fprintf(stderr,"os_protect: Unknown segment: 0x%08x[0x%08x]\n",addr,len);
}
boolean valid_addr(test)
os_vm_address_t test;
{
return seg_find(test)!=NULL;
}
/* ---------------------------------------------------------------- */
static boolean maybe_gc(sig, code, context)
int sig, code;
struct sigcontext *context;
{
/*
* It's necessary to enable recursive SEGVs, since the handle is
* used for multiple things (e.g., both gc-trigger & faulting in pages).
* We check against recursive gc's though...
*/
boolean did_gc;
static already_trying=0;
if(already_trying)
return FALSE;
sigsetmask(context->sc_mask);
already_trying=TRUE;
did_gc=interrupt_maybe_gc(sig, code, context);
already_trying=FALSE;
return did_gc;
}
/*
* The primary point of catching segmentation violations is to allow
* read only memory to be re-mapped with more permissions when a write
* is attempted. this greatly decreases the residency of the program
* in swap space since read only areas don't take up room
*
* Running into the gc trigger page will also end up here...
*/
void segv_handler(sig, code, context, addr)
int sig, code;
struct sigcontext *context;
caddr_t addr;
{
if (code == SEGV_PROT) { /* allow writes to this chunk */
struct segment *seg=seg_find(addr);
if(last_fault==addr){
if(seg!=NULL && maybe_gc(sig, code, context))
/* we just garbage collected */
return;
else{
/* a *real* protection fault */
fprintf(stderr,
"segv_handler: Real protection violation: 0x%08x\n",
addr);
interrupt_handle_now(sig,code,context);
}
}else
last_fault=addr;
if(seg!=NULL){
int err;
/* round down to a page */
os_vm_address_t block=(os_vm_address_t)((long)addr&~SPARSE_SIZE_MASK);
os_vm_size_t length=SPARSE_BLOCK_SIZE;
if(block < seg->start){
length-=(seg->start - block);
block=seg->start;
}
if(block+length > seg->start+seg->length)
length=seg->start+seg->length-block;
#if 0
/* unmap it. probably redundant. */
if(munmap(block,length) == -1)
perror("segv_handler: munmap");
#endif
/* and remap it with more permissions */
err=(int)
mmap(block,
length,
seg->protection,
MAP_PRIVATE|MAP_FIXED,
seg->mapped_fd,
seg->file_offset==OFFSET_NONE
? 0
: seg->file_offset+(block-seg->start));
if (err == -1) {
perror("segv_handler: mmap");
interrupt_handle_now(sig,code,context);
}
}
else{
fprintf(stderr, "segv_handler: 0x%08x not in any segment\n",addr);
interrupt_handle_now(sig,code,context);
}
}
/*
* note that we check for a gc-trigger hit even if it's not a PROT error
*/
else if(!maybe_gc(sig, code, context)){
static int nomap_count=0;
if(code==SEGV_NOMAP){
if(nomap_count==0){
fprintf(stderr,
"segv_handler: No mapping fault: 0x%08x\n",addr);
nomap_count++;
}else{
/*
* There should be higher-level protection against stack
* overflow somewhere, but at least this prevents infinite
* puking of error messages...
*/
fprintf(stderr,
"segv_handler: Recursive no mapping fault (stack overflow?)\n");
exit(-1);
}
}else if(SEGV_CODE(code)==SEGV_OBJERR){
extern int errno;
errno=SEGV_ERRNO(code);
perror("segv_handler: Object error");
}
interrupt_handle_now(sig,code,context);
if(code==SEGV_NOMAP)
nomap_count--;
}
}
void os_install_interrupt_handlers()
{
interrupt_install_low_level_handler(SIGSEGV,segv_handler);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment