diff --git a/ldb/arch.h b/ldb/arch.h new file mode 100644 index 0000000000000000000000000000000000000000..3947377261ac7239ac96ca74b4be1b765c25d33d --- /dev/null +++ b/ldb/arch.h @@ -0,0 +1,8 @@ +#ifndef __ARCH_H__ +#define __ARCH_H__ + +extern void arch_init(); +extern void arch_skip_instruction(); +extern os_vm_address_t arch_get_bad_addr(); + +#endif /* __ARCH_H__ */ diff --git a/ldb/fake-mach.c b/ldb/fake-mach.c new file mode 100644 index 0000000000000000000000000000000000000000..422e3e0f736a2a074ce877280d6f2282874fd222 --- /dev/null +++ b/ldb/fake-mach.c @@ -0,0 +1,53 @@ +#include "ldb.h" +#include "os.h" + +#define KERN_SUCCESS 0 +#define KERN_INVALID_ADDRESS 1 +#define KERN_PROTECTION_FAILURE 2 +#define KERN_NO_SPACE 3 +#define KERN_INVALID_ARGUMENT 4 +#define KERN_FAILURE 5 + +int task_self() +{ + return 0; +} + +int thread_reply() +{ + return 0; +} + +int task_notify() +{ + return 0; +} + +int vm_allocate(task,addr_p,len,free) +int task; +os_vm_address_t *addr_p; +os_vm_size_t len; +int free; +{ + *addr_p=os_validate(free ? NULL : *addr_p,len); + return (*addr_p)==NULL ? KERN_FAILURE : KERN_SUCCESS; +} + +int vm_deallocate(task,addr,len) +int task; +os_vm_address_t addr; +os_vm_size_t len; +{ + os_invalidate(addr,len); + return KERN_SUCCESS; +} + +int vm_copy() +{ + return KERN_FAILURE; +} + +int vm_statistics() +{ + return KERN_FAILURE; +} diff --git a/ldb/mach-os.h b/ldb/mach-os.h new file mode 100644 index 0000000000000000000000000000000000000000..d54ffbd053c287da849fb043e1e2256ebe08e9d0 --- /dev/null +++ b/ldb/mach-os.h @@ -0,0 +1,13 @@ +#include <mach.h> +#include "ldb.h" + +typedef vm_address_t os_vm_address_t; +typedef vm_size_t os_vm_size_t; +typedef vm_offset_t os_vm_offset_t; +typedef vm_prot_t os_vm_prot_t; + +#define OS_VM_PROT_READ VM_PROT_READ +#define OS_VM_PROT_WRITE VM_PROT_WRITE +#define OS_VM_PROT_EXECUTE VM_PROT_EXECUTE + +#define OS_VM_DEFAULT_PAGESIZE 4096 diff --git a/ldb/mips-arch.c b/ldb/mips-arch.c new file mode 100644 index 0000000000000000000000000000000000000000..0d997cfcb5ab28ad8648e56bbb758d20389209f6 --- /dev/null +++ b/ldb/mips-arch.c @@ -0,0 +1,134 @@ +#include <mips/cpu.h> + +#include "ldb.h" +#include "lisp.h" +#include "globals.h" +#include "validate.h" +#include "os.h" +#include "arch.h" +#include "lispregs.h" +#include "signal.h" + +void arch_init() +{ +} + +os_vm_address_t arch_get_bad_addr(context) +struct sigcontext *context; +{ + /* Finding the bad address on the mips is easy. */ + return (os_vm_address_t)context->sc_badvaddr; +} + +void arch_skip_instruction(context) +struct sigcontext *context; +{ + /* Skip the offending instruction */ + if (context->sc_cause & CAUSE_BD) + emulate_branch(context, *(unsigned long *)context->sc_pc); + else + context->sc_pc += 4; +} + +static void sigtrap_handler(signal, code, context) + int signal, code; + struct sigcontext *context; +{ + switch (code) { + case trap_PendingInterrupt: + interrupt_handle_pending(context); + break; + + case trap_Halt: + crap_out("%primitive halt called; the party is over.\n"); + + case trap_Error: + case trap_Cerror: + interrupt_internal_error(signal, code, context, code==trap_Cerror); + break; + + case trap_Breakpoint: + sigsetmask(context->sc_mask); + fake_foreign_function_call(context); + handle_breakpoint(signal, code, context); + undo_fake_foreign_function_call(context); + break; + + default: + interrupt_handle_now(signal, code, context); + break; + } +} + +static void sigfpe_handler(signal, code, context) +int signal, code; +struct sigcontext *context; +{ + unsigned long bad_inst; + unsigned int op, rs, rt, rd, funct, dest; + int immed; + long result; + + if (context->sc_cause & CAUSE_BD) + bad_inst = *(unsigned long *)(context->sc_pc + 4); + else + bad_inst = *(unsigned long *)(context->sc_pc); + + op = (bad_inst >> 26) & 0x3f; + rs = (bad_inst >> 21) & 0x1f; + rt = (bad_inst >> 16) & 0x1f; + rd = (bad_inst >> 11) & 0x1f; + funct = bad_inst & 0x3f; + immed = (((int)(bad_inst & 0xffff)) << 16) >> 16; + + switch (op) { + case 0x0: /* SPECIAL */ + switch (funct) { + case 0x20: /* ADD */ + result = FIXNUM_VALUE(context->sc_regs[rs]) + FIXNUM_VALUE(context->sc_regs[rt]); + dest = rd; + break; + + case 0x22: /* SUB */ + result = FIXNUM_VALUE(context->sc_regs[rs]) - FIXNUM_VALUE(context->sc_regs[rt]); + dest = rd; + break; + + default: + dest = 32; + break; + } + break; + + case 0x8: /* ADDI */ + result = FIXNUM_VALUE(context->sc_regs[rs]) + (immed>>2); + dest = rt; + break; + + default: + dest = 32; + break; + } + + if (dest < 32) { + set_global_pointer(saved_global_pointer); + current_dynamic_space_free_pointer = + (lispobj *) context->sc_regs[ALLOC]; + + context->sc_regs[dest] = alloc_number(result); + + context->sc_regs[ALLOC] = + (unsigned long) current_dynamic_space_free_pointer; + + arch_skip_instruction(context); + + } + else + interrupt_handle_now(signal, code, context); +} + +void arch_install_interrupt_handlers() +{ + interrupt_install_low_level_handler(SIGTRAP,sigtrap_handler); + interrupt_install_low_level_handler(SIGEMT,sigemt_handler); +} diff --git a/ldb/mips-lispregs.h b/ldb/mips-lispregs.h new file mode 100644 index 0000000000000000000000000000000000000000..613ce2a0f6d77cc5ec25024f224834560183c244 --- /dev/null +++ b/ldb/mips-lispregs.h @@ -0,0 +1,50 @@ +/* $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/ldb/Attic/mips-lispregs.h,v 1.1 1991/05/24 18:46:15 wlott Exp $ */ + +#ifdef LANGUAGE_ASSEMBLY +#define REG(num) $num +#else +#define REG(num) num +#endif + +#define NREGS (32) + +#define ZERO REG(0) +#define NL3 REG(1) +#define NL4 REG(2) +#define FLAGS REG(3) +#define NL0 REG(4) +#define NL1 REG(5) +#define NL2 REG(6) +#define NARGS REG(7) +#define A0 REG(8) +#define A1 REG(9) +#define A2 REG(10) +#define A3 REG(11) +#define A4 REG(12) +#define A5 REG(13) +#define CNAME REG(14) +#define LEXENV REG(15) +#define NFP REG(16) +#define OCFP REG(17) +#define LRA REG(18) +#define L0 REG(19) +#define NULLREG REG(20) +#define BSP REG(21) +#define CFP REG(22) +#define CSP REG(23) +#define L1 REG(24) +#define ALLOC REG(25) +#define L2 REG(28) +#define NSP REG(29) +#define CODE REG(30) +#define LIP REG(31) + +#define REGNAMES \ + "ZERO", "NL3", "NL4", "FLAGS", \ + "NL0", "NL1", "NL2", "NARGS", \ + "A0", "A1", "A2", "A3", \ + "A4", "A5", "CNAME", "LEXENV", \ + "NFP", "OCFP", "LRA", "L0", \ + "NULL", "BSP", "CFP", "CSP", \ + "L1", "ALLOC", "K0", "K1", \ + "L2", "NSP", "CODE", "LIP" diff --git a/ldb/os-common.c b/ldb/os-common.c new file mode 100644 index 0000000000000000000000000000000000000000..9efd0332882cacef478298f4a340581003b98a76 --- /dev/null +++ b/ldb/os-common.c @@ -0,0 +1,88 @@ +#include <stdio.h> + +#include "ldb.h" +#include "os.h" + +void os_zero(addr, length) +os_vm_address_t addr; +os_vm_size_t length; +{ + os_vm_address_t block_start; + os_vm_size_t block_size; + +#ifdef DEBUG + fprintf(stderr,";;; os_zero: addr: 0x%08x, len: 0x%08x\n",addr,length); +#endif + + block_start=os_round_up_to_page(addr); + + length-=block_start-addr; + block_size=os_trunc_size_to_page(length); + + if(block_start>addr) + bzero((char *)addr,block_start-addr); + if(block_size<length) + bzero((char *)block_start+block_size,length-block_size); + + if (block_size != 0) { + /* Now deallocate and allocate the block so that it */ + /* faults in zero-filled. */ + + os_invalidate(block_start,block_size); + addr=os_validate(block_start,block_size); + + if(addr==NULL || addr!=block_start) + fprintf(stderr,"os_zero: block moved, 0x%08x ==> 0x%08x!\n",block_start,addr); + } +} + +os_vm_address_t os_allocate(len) +os_vm_size_t len; +{ + return os_validate((os_vm_address_t)NULL,len); +} + +void os_deallocate(addr,len) +os_vm_address_t addr; +os_vm_size_t len; +{ + os_invalidate(addr,len); +} + +os_vm_address_t os_reallocate(addr,old_len,len) +os_vm_address_t addr; +os_vm_size_t old_len, len; +{ + addr=os_trunc_to_page(addr); + len=os_round_up_size_to_page(len); + old_len=os_round_up_size_to_page(old_len); + + if(addr==NULL) + return os_allocate(len); + else{ + long len_diff=old_len-len; + + if(len_diff<0) + os_invalidate(addr+len,-len_diff); + else if(len_diff!=0){ + os_vm_address_t new=os_validate(addr+old_len,len_diff); + + if(new==NULL || new!=addr+old_len){ + if(new!=NULL) + /* allocated alright, but in the wrong place */ + os_invalidate(new,len_diff); + + new=os_allocate(len); + + if(new!=NULL){ + bcopy(addr,new,old_len); + os_invalidate(addr,old_len); + } + + addr=new; + } + } + + return addr; + } +} diff --git a/ldb/rt-lispregs.h b/ldb/rt-lispregs.h new file mode 100644 index 0000000000000000000000000000000000000000..c3254700b5336704eb20109a779d16e3d14e3cf6 --- /dev/null +++ b/ldb/rt-lispregs.h @@ -0,0 +1,32 @@ +/* $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/ldb/Attic/rt-lispregs.h,v 1.1 1991/05/24 18:46:09 wlott Exp $ */ + +#ifdef LANGUAGE_ASSEMBLY +#define REG(num) r/**/num +#else +#define REG(num) num +#endif + +#define NREGS (16) + +#define NARGS REG(0) +#define NSP REG(1) +#define NL0 REG(2) +#define OCFP REG(3) +#define NFP REG(4) +#define CSP REG(5) +#define CFP REG(6) +#define CODE REG(7) +#define NULLREG REG(8) +#define CNAME REG(9) +#define LEXENV REG(10) +#define LRA REG(11) +#define A0 REG(12) +#define A1 REG(13) +#define A2 REG(14) +#define LIP REG(15) + +#define REGNAMES \ + "NARGS", "NSP", "NL0", "OCFP", \ + "NFP", "CSP", "CFP", "CODE", \ + "NULL", "CNAME", "LEXENV", "LRA", \ + "A0", "A1", "A2", "LIP" diff --git a/ldb/sparc-arch.c b/ldb/sparc-arch.c new file mode 100644 index 0000000000000000000000000000000000000000..666f8cd50988366a95df2de821ea8d6fe780b568 --- /dev/null +++ b/ldb/sparc-arch.c @@ -0,0 +1,168 @@ +#include <machine/trap.h> + +#include "ldb.h" +#include "lisp.h" +#include "globals.h" +#include "validate.h" +#include "os.h" +#include "arch.h" +#include "lispregs.h" +#include "signal.h" + +void arch_init() +{ +} + +os_vm_address_t arch_get_bad_addr(context) +struct sigcontext *context; +{ + unsigned long badinst; + int rs1; + + /* On the sparc, we have to decode the instruction. */ + + /* Make sure it's not the pc thats bogus, and that it was lisp code */ + /* that caused the fault. */ + if ((context->sc_pc & 3) != 0 || + ((context->sc_pc < READ_ONLY_SPACE_START || + context->sc_pc >= READ_ONLY_SPACE_START+READ_ONLY_SPACE_SIZE) && + ((lispobj *)context->sc_pc < current_dynamic_space && + (lispobj *)context->sc_pc >= + current_dynamic_space + DYNAMIC_SPACE_SIZE))) + return NULL; + + badinst = *(unsigned long *)context->sc_pc; + + if ((badinst >> 30) != 3) + /* All load/store instructions have op = 11 (binary) */ + return NULL; + + rs1 = (badinst>>14)&0x1f; + + if (badinst & (1<<13)) { + /* r[rs1] + simm(13) */ + int simm13 = badinst & 0x1fff; + + if (simm13 & (1<<12)) + simm13 |= -1<<13; + + return (os_vm_address_t)(context->sc_regs[rs1] + simm13); + } + else { + /* r[rs1] + r[rs2] */ + int rs2 = badinst & 0x1f; + + return (os_vm_address_t)(context->sc_regs[rs1] + context->sc_regs[rs2]); + } + +} + +void arch_skip_instruction(context) +struct sigcontext *context; +{ + /* Skip the offending instruction */ + context->sc_pc = context->sc_npc; + context->sc_npc += 4; +} + +static void sigill_handler(signal, code, context) +int signal, code; +struct sigcontext *context; +{ + int badinst; + + if (code == T_UNIMP_INSTR) { + int trap; + + trap = *(unsigned long *)(context->sc_pc) & 0x3fffff; + + switch (trap) { + case trap_PendingInterrupt: + interrupt_handle_pending(context); + break; + + case trap_Halt: + crap_out("%primitive halt called; the party is over.\n"); + + case trap_Error: + case trap_Cerror: + interrupt_internal_error(signal, code, context, trap == trap_Cerror); + break; + + default: + interrupt_handle_now(signal, code, context); + break; + } + } + else if (code >= T_SOFTWARE_TRAP + 16 & code < T_SOFTWARE_TRAP + 32) + interrupt_internal_error(signal, code, context, FALSE); + else + interrupt_handle_now(signal, code, context); +} + +static void sigemt_handler(signal, code, context) + int signal, code; + struct sigcontext *context; +{ + unsigned long badinst; + boolean subtract, immed; + int rd, rs1, op1, rs2, op2, result; + + badinst = *(unsigned long *)context->sc_pc; + if ((badinst >> 30) != 2 || ((badinst >> 20) & 0x1f) != 0x11) { + /* It wasn't a tagged add. Pass the signal into lisp. */ + interrupt_handle_now(signal, code, context); + return; + } + + /* Extract the parts of the inst. */ + subtract = badinst & (1<<19); + rs1 = (badinst>>14) & 0x1f; + op1 = context->sc_regs[rs1]; + if ((op1 & 3) != 0) { + /* The first arg wan't a fixnum. */ + interrupt_internal_error(signal, code, context, FALSE); + return; + } + + if (immed = badinst & (1<<13)) { + op2 = badinst & 0x1fff; + if (op2 & (1<<12)) + op2 |= -1<<13; + } + else { + rs2 = badinst & 0x1f; + op2 = context->sc_regs[rs2]; + } + + if ((op2 & 3) != 0) { + /* The second arg wan't a fixnum. */ + interrupt_internal_error(signal, code, context, FALSE); + return; + } + + rd = (badinst>>25) & 0x1f; + if (rd != 0) { + /* Don't bother computing the result unless we are going to use it. */ + if (subtract) + result = (op1>>2) - (op2>>2); + else + result = (op1>>2) + (op2>>2); + + current_dynamic_space_free_pointer = + (lispobj *) context->sc_regs[ALLOC]; + + context->sc_regs[rd] = alloc_number(result); + + context->sc_regs[ALLOC] = + (unsigned long) current_dynamic_space_free_pointer; + } + + arch_skip_instruction(context); +} + +void arch_install_interrupt_handlers() +{ + interrupt_install_low_level_handler(SIGILL,sigill_handler); + interrupt_install_low_level_handler(SIGEMT,sigemt_handler); +} diff --git a/ldb/sparc-lispregs.h b/ldb/sparc-lispregs.h new file mode 100644 index 0000000000000000000000000000000000000000..dc9bad42308d69052a3650e3adbe21df06cc49fc --- /dev/null +++ b/ldb/sparc-lispregs.h @@ -0,0 +1,64 @@ +/* $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/ldb/Attic/sparc-lispregs.h,v 1.1 1991/05/24 18:45:57 wlott Exp $ */ + +#ifdef LANGUAGE_ASSEMBLY + +#define GREG(num) %g/**/num +#define OREG(num) %o/**/num +#define LREG(num) %l/**/num +#define IREG(num) %i/**/num + +#else + +#define GREG(num) (num) +#define OREG(num) ((num)+8) +#define LREG(num) ((num)+16) +#define IREG(num) ((num)+24) + +#endif + +#define NREGS (32) + +#define ZERO GREG(0) +#define ALLOC GREG(1) +#define NULLREG GREG(2) +#define CSP GREG(3) +#define CFP GREG(4) +#define BSP GREG(5) +#define NFP GREG(6) +#define CFUNC GREG(7) + +#define NL0 OREG(0) +#define NL1 OREG(1) +#define NL2 OREG(2) +#define NL3 OREG(3) +#define NL4 OREG(4) +#define NL5 OREG(5) +#define NSP OREG(6) +#define NARGS OREG(7) + +#define A0 LREG(0) +#define A1 LREG(1) +#define A2 LREG(2) +#define A3 LREG(3) +#define A4 LREG(4) +#define A5 LREG(5) +#define OCFP LREG(6) +#define LRA LREG(7) + +#define CNAME IREG(0) +#define LEXENV IREG(1) +#define L0 IREG(2) +#define L1 IREG(3) +#define L2 IREG(4) +#define CODE IREG(5) +#define LIP IREG(7) + +#define REGNAMES \ + "ZERO", "ALLOC", "NULL", "CSP", \ + "CFP", "BSP", "NFP", "CFUNC", \ + "NL0", "NL1", "NL2", "NL3", \ + "NL4", "NL5", "NSP", "NARGS", \ + "A0", "A1", "A2", "A3", \ + "A4", "A5", "OCFP", "LRA", \ + "CNAME", "LEXENV", "L0", "L1", \ + "L2", "CODE", "???", "LIP" diff --git a/ldb/sunos-os.c b/ldb/sunos-os.c new file mode 100644 index 0000000000000000000000000000000000000000..2cc9bd48e31cb1a8a5826adf6a369d5f6c6a607b --- /dev/null +++ b/ldb/sunos-os.c @@ -0,0 +1,678 @@ +/* + * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/ldb/Attic/sunos-os.c,v 1.1 1991/05/24 18:43:45 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 "ldb.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 + +/* ---------------------------------------------------------------- */ + +#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() +{ + void sunos_segv_handler(); + + zero_fd=open(ZEROFILE,O_RDONLY); + + empty_fd=open(EMPTYFILE,O_RDONLY|O_CREAT); + unlink(EMPTYFILE); + + 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(seg,addr,len) +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 : 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; +os_vm_offset_t 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(context) +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(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(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(context)){ + if(code == SEGV_NOMAP) + fprintf(stderr, "segv_handler: No mapping fault: 0x%08x\n",addr); + 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); + } +} + +void os_install_interrupt_handlers() +{ + interrupt_install_low_level_handler(SIGSEGV,segv_handler); +} diff --git a/ldb/sunos-os.h b/ldb/sunos-os.h new file mode 100644 index 0000000000000000000000000000000000000000..85992579bfa93163c8befaa8d9bbea78b1446eb2 --- /dev/null +++ b/ldb/sunos-os.h @@ -0,0 +1,13 @@ +#include <sys/types.h> +#include <sys/mman.h> + +typedef caddr_t os_vm_address_t; +typedef long os_vm_size_t; +typedef off_t os_vm_offset_t; +typedef int os_vm_prot_t; + +#define OS_VM_PROT_READ PROT_READ +#define OS_VM_PROT_WRITE PROT_WRITE +#define OS_VM_PROT_EXECUTE PROT_EXEC + +#define OS_VM_DEFAULT_PAGESIZE 8192