ccbabd820b43dbd1bb0f0b5ae7024b0d9cd629e7
[projects/cmucl/cmucl.git] / src / lisp / sunos-os.c
1 /*
2  * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/sunos-os.c,v 1.13 2011/09/01 05:18:26 rtoy Exp $
3  *
4  * OS-dependent routines.  This file (along with os.h) exports an
5  * OS-independent interface to the operating system VM facilities.
6  * Suprisingly, this interface looks a lot like the Mach interface
7  * (but simpler in some places).  For some operating systems, a subset
8  * of these functions will have to be emulated.
9  *
10  * This is the SunOS version.
11  * March 1991, Miles Bader <miles@cogsci.ed.ack.uk> & ted <ted@edu.NMSU>
12  *
13  */
14
15 /* #define DEBUG */
16
17 #include <stdio.h>
18
19 #include <signal.h>
20 #include <sys/file.h>
21
22 #define OS_PROTERR              SEGV_PROT
23 #define OS_MAPERR               SEGV_NOMAP
24 #define OS_HASERRNO(code)       (SEGV_CODE(code)==SEGV_OBJERR)
25 #define OS_ERRNO(code)          SEGV_ERRNO(code)
26 extern int errno;
27
28 #include "os.h"
29 /* To get dynamic_0_space and friends */
30 #include "globals.h"
31 /* To get memory map */
32 #include "sparc-validate.h"
33
34 /* block size must be larger than the system page size */
35 #define SPARSE_BLOCK_SIZE (1<<15)
36 #define SPARSE_SIZE_MASK (SPARSE_BLOCK_SIZE-1)
37
38 #define PROT_DEFAULT OS_VM_PROT_ALL
39
40 #define OFFSET_NONE ((os_vm_offset_t)(~0))
41
42 #define EMPTYFILE "/tmp/empty"
43 #define ZEROFILE "/dev/zero"
44
45 #define INITIAL_MAX_SEGS 32
46 #define GROW_MAX_SEGS 16
47
48 extern char *getenv();
49
50 /* ---------------------------------------------------------------- */
51
52 #define ADJ_OFFSET(off,adj) (((off)==OFFSET_NONE) ? OFFSET_NONE : ((off)+(adj)))
53
54 long os_vm_page_size = (-1);
55 static long os_real_page_size = (-1);
56
57 static struct segment {
58     os_vm_address_t start;      /* note: start & length are expected to be on page */
59     os_vm_size_t length;        /*       boundaries */
60     long file_offset;
61     short mapped_fd;
62     short protection;
63 } *segments;
64
65 static int n_segments = 0, max_segments = 0;
66
67 static int zero_fd = (-1), empty_fd = (-1);
68
69 static os_vm_address_t last_fault = 0;
70 static os_vm_size_t real_page_size_difference = 0;
71
72 void
73 os_init0(const char *argv[], const char *envp[])
74 {}
75
76 static void
77 os_init_bailout(arg)
78      char *arg;
79 {
80     char buf[500];
81
82     sprintf(buf, "os_init: %s", arg);
83     perror(buf);
84     exit(1);
85 }
86
87 void
88 os_init0(const char *argv[], const char *envp[])
89 {}
90
91 void
92 os_init(const char *argv[], const char *envp[])
93 {
94     char *empty_file = getenv("CMUCL_EMPTYFILE");
95
96     if (empty_file == NULL)
97         empty_file = EMPTYFILE;
98
99     empty_fd = open(empty_file, O_RDONLY | O_CREAT);
100     if (empty_fd < 0)
101         os_init_bailout(empty_file);
102     unlink(empty_file);
103
104     zero_fd = open(ZEROFILE, O_RDONLY);
105     if (zero_fd < 0)
106         os_init_bailout(ZEROFILE);
107
108     os_vm_page_size = os_real_page_size = getpagesize();
109
110     max_segments = INITIAL_MAX_SEGS;
111     segments = (struct segment *) malloc(sizeof(struct segment) * max_segments);
112
113     if (segments == NULL) {
114         fprintf(stderr, "os_init: Couldn't allocate %d segment descriptors\n",
115                 max_segments);
116         exit(1);
117     }
118
119     if (os_vm_page_size > OS_VM_DEFAULT_PAGESIZE) {
120         fprintf(stderr, "os_init: Pagesize too large (%d > %d)\n",
121                 os_vm_page_size, OS_VM_DEFAULT_PAGESIZE);
122         exit(1);
123     } else {
124         /*
125          * we do this because there are apparently dependencies on
126          * the pagesize being OS_VM_DEFAULT_PAGESIZE somewhere...
127          * but since the OS doesn't know we're using this restriction,
128          * we have to grovel around a bit to enforce it, thus anything
129          * that uses real_page_size_difference.
130          */
131         real_page_size_difference = OS_VM_DEFAULT_PAGESIZE - os_vm_page_size;
132         os_vm_page_size = OS_VM_DEFAULT_PAGESIZE;
133     }
134 }
135
136 /* ---------------------------------------------------------------- */
137
138 void
139 seg_force_resident(struct segment *seg, os_vm_address_t addr, os_vm_size_t len)
140 {
141     int prot = seg->protection;
142
143     if (prot != 0) {
144         os_vm_address_t end = addr + len, touch = addr;
145
146         while (touch < end) {
147             int contents = (*(char *) touch);
148
149             if (prot & OS_VM_PROT_WRITE)
150                 (*(char *) touch) = contents;
151             touch =
152                 (os_vm_address_t) (((long) touch + SPARSE_BLOCK_SIZE) &
153                                    ~SPARSE_SIZE_MASK);
154         }
155     }
156 }
157
158 static struct segment *
159 seg_create_nomerge(addr, len, protection, mapped_fd, file_offset)
160      os_vm_address_t addr;
161      os_vm_size_t len;
162      int protection;
163      int mapped_fd;
164 {
165     int n;
166     struct segment *seg;
167
168     if (len == 0)
169         return NULL;
170
171     if (n_segments == max_segments) {
172         struct segment *new_segs;
173
174         max_segments += GROW_MAX_SEGS;
175
176         new_segs = (struct segment *)
177             realloc(segments, max_segments * sizeof(struct segment));
178
179         if (new_segs == NULL) {
180             fprintf(stderr,
181                     "seg_create_nomerge: Couldn't grow segment descriptor table to %s segments\n",
182                     max_segments);
183             max_segments -= GROW_MAX_SEGS;
184             return NULL;
185         }
186
187         segments = new_segs;
188     }
189
190     for (n = n_segments, seg = segments; n > 0; n--, seg++)
191         if (addr < seg->start) {
192             seg = (&segments[n_segments]);
193             while (n-- > 0) {
194                 seg[0] = seg[-1];
195                 seg--;
196             }
197             break;
198         }
199
200     n_segments++;
201
202     seg->start = addr;
203     seg->length = len;
204     seg->protection = protection;
205     seg->mapped_fd = mapped_fd;
206     seg->file_offset = file_offset;
207
208     return seg;
209 }
210
211 #if 1
212 /* returns the first segment containing addr */
213 static struct segment *
214 seg_find(addr)
215      os_vm_address_t addr;
216 {
217     int n;
218     struct segment *seg;
219
220     for (n = n_segments, seg = segments; n > 0; n--, seg++)
221         if (seg->start <= addr && seg->start + seg->length > addr)
222             return seg;
223
224     return NULL;
225 }
226 #else
227 /* returns the first segment containing addr */
228 static struct segment *
229 seg_find(addr)
230      os_vm_address_t addr;
231 {
232     /* does a binary search */
233     struct segment *lo = segments, *hi = segments + n_segments;
234
235     while (hi > lo) {
236         struct segment *mid = lo + ((hi - lo) >> 1);
237         os_vm_address_t start = mid->start;
238
239         if (addr >= start && addr < start + mid->length)
240             return mid;
241         else if (addr < start)
242             hi = mid;
243         else
244             lo = mid + 1;
245     }
246
247     return NULL;
248 }
249 #endif
250
251 /* returns TRUE if the range from addr to addr+len intersects with any segment */
252 static boolean
253 collides_with_seg_p(addr, len)
254      os_vm_address_t addr;
255      os_vm_size_t len;
256 {
257     int n;
258     struct segment *seg;
259     os_vm_address_t end = addr + len;
260
261     for (n = n_segments, seg = segments; n > 0; n--, seg++)
262         if (seg->start >= end)
263             return FALSE;
264         else if (seg->start + seg->length > addr)
265             return TRUE;
266
267     return FALSE;
268 }
269
270 #if 0                           /* WAY to SLOW */
271 /* returns TRUE if the range from addr to addr+len is a valid mapping
272  * (that we don't know about) */
273 static boolean
274 mem_in_use(addr, len)
275      os_vm_address_t addr;
276      os_vm_size_t len;
277 {
278     os_vm_address_t p;
279
280     for (p = addr; addr < addr + len; p += os_real_page_size) {
281         char c;
282
283         if (mincore((caddr_t) p, os_real_page_size, &c) == 0 || errno != ENOMEM)
284             return TRUE;
285     }
286     return FALSE;
287 }
288 #endif
289
290 #define seg_last_p(seg) (((seg)-segments)>=n_segments-1)
291
292 static void
293 seg_destroy(seg)
294      struct segment *seg;
295 {
296     if (seg != NULL) {
297         int n;
298
299         for (n = seg - segments + 1; n < n_segments; n++) {
300             seg[0] = seg[1];
301             seg++;
302         }
303
304         n_segments--;
305     }
306 }
307
308 static void
309 seg_try_merge_next(seg)
310      struct segment *seg;
311 {
312     struct segment *nseg = seg + 1;
313
314     if (!seg_last_p(seg)
315         && seg->start + seg->length == nseg->start
316         && seg->protection == nseg->protection
317         && seg->mapped_fd == nseg->mapped_fd
318         && ADJ_OFFSET(seg->file_offset, seg->length) == nseg->file_offset) {
319         /* can merge with the next segment */
320 #ifdef DEBUG
321         fprintf(stderr,
322                 ";;; seg_try_merge: Merged 0x%08x[0x%08x] with 0x%08x[0x%08x]\n",
323                 seg->start, seg->length, nseg->start, nseg->length);
324 #endif
325
326         if (((long) nseg->start & SPARSE_SIZE_MASK) != 0) {
327             /*
328              * if not on a block boundary, we have to ensure both parts
329              * of a common block are in a known state
330              */
331             seg_force_resident(seg, nseg->start - 1, 1);
332             seg_force_resident(nseg, nseg->start, 1);
333         }
334
335         seg->length += nseg->length;
336         seg_destroy(nseg);
337     }
338 }
339
340
341 /*
342  * Try to merge seg with adjacent segments.
343  */
344 static void
345 seg_try_merge_adjacent(seg)
346      struct segment *seg;
347 {
348     if (!seg_last_p(seg))
349         seg_try_merge_next(seg);
350     if (seg > segments)
351         seg_try_merge_next(seg - 1);
352 }
353
354 static struct segment *
355 seg_create(addr, len, protection, mapped_fd, file_offset)
356      os_vm_address_t addr;
357      os_vm_size_t len;
358      int protection;
359      int mapped_fd;
360 {
361     struct segment *seg =
362
363         seg_create_nomerge(addr, len, protection, mapped_fd, file_offset);
364     if (seg != NULL)
365         seg_try_merge_adjacent(seg);
366     return seg;
367 }
368
369 /* 
370  * Change the attributes of the given range of an existing segment, and return
371  * a segment corresponding to the new bit.
372  */
373 static struct segment *
374 seg_change_range(seg, addr, len, protection, mapped_fd, file_offset)
375      struct segment *seg;
376      os_vm_address_t addr;
377      os_vm_size_t len;
378      int protection;
379      int mapped_fd;
380 {
381     os_vm_address_t end = addr + len;
382
383     if (len == 0)
384         return NULL;
385
386     if (protection != seg->protection
387         || mapped_fd != seg->mapped_fd
388         || file_offset != ADJ_OFFSET(seg->file_offset, addr - seg->start)) {
389         os_vm_size_t old_len = seg->length, seg_offset = (addr - seg->start);
390
391         if (old_len < len + seg_offset) {
392             struct segment *next = seg + 1;
393
394 #ifdef DEBUG
395             fprintf(stderr,
396                     ";;; seg_change_range: region 0x%08x[0x%08x] overflows 0x%08x[0x%08x]\n",
397                     addr, len, seg->start, old_len);
398 #endif
399
400             while (!seg_last_p(seg) && next->start + next->length <= end) {
401 #ifdef DEBUG
402                 fprintf(stderr,
403                         ";;; seg_change_range: merging extra segment 0x%08x[0x%08x]\n",
404                         next->start, next->length);
405 #endif
406                 seg_destroy(next);
407             }
408
409             if (!seg_last_p(seg) && next->start < end) {
410                 next->length -= end - next->start;
411                 next->start = end;
412                 old_len = next->start - seg->start;
413             } else
414                 old_len = len + seg_offset;
415
416 #ifdef DEBUG
417             fprintf(stderr,
418                     ";;; seg_change_range: extended first seg to 0x%08x[0x%08x]\n",
419                     seg->start, old_len);
420 #endif
421         }
422
423         if (seg_offset + len < old_len) {
424             /* add second part of old segment */
425             seg_create_nomerge(end,
426                                old_len - (seg_offset + len),
427                                seg->protection,
428                                seg->mapped_fd,
429                                ADJ_OFFSET(seg->file_offset, seg_offset + len));
430
431 #ifdef DEBUG
432             fprintf(stderr,
433                     ";;; seg_change_range: Split off end of 0x%08x[0x%08x]: 0x%08x[0x%08x]\n",
434                     seg->start, old_len, end, old_len - (seg_offset + len));
435 #endif
436         }
437
438         if (seg_offset == 0) {
439             seg->length = len;
440             seg->protection = protection;
441             seg->mapped_fd = mapped_fd;
442             seg->file_offset = file_offset;
443         } else {
444             /* adjust first part of remaining old segment */
445             seg->length = seg_offset;
446
447 #ifdef DEBUG
448             fprintf(stderr,
449                     ";;; seg_change_range: Split off beginning of 0x%08x[0x%08x]: 0x%08x[0x%08x]\n",
450                     seg->start, old_len, seg->start, seg_offset);
451 #endif
452
453             /* add new middle segment for new protected region */
454             seg =
455                 seg_create_nomerge(addr, len, protection, mapped_fd,
456                                    file_offset);
457         }
458
459         seg_try_merge_adjacent(seg);
460
461         last_fault = 0;
462     }
463
464     return seg;
465 }
466
467 /* ---------------------------------------------------------------- */
468
469 static os_vm_address_t
470 mapin(addr, len, protection, map_fd, offset, is_readable)
471      os_vm_address_t addr;
472      os_vm_size_t len;
473      int protection;
474      int map_fd;
475      long offset;
476      int is_readable;
477 {
478     os_vm_address_t real;
479     boolean sparse = (len >= SPARSE_BLOCK_SIZE);
480
481     if (offset != OFFSET_NONE
482         && (offset < os_vm_page_size || (offset & (os_vm_page_size - 1)) != 0)) {
483         fprintf(stderr,
484                 "mapin: file offset (%d) not multiple of pagesize (%d)\n",
485                 offset, os_vm_page_size);
486     }
487
488     if (addr == NULL)
489         len += real_page_size_difference;       /* futz around to get an aligned region */
490
491     last_fault = 0;
492     real = (os_vm_address_t)
493         mmap((caddr_t) addr,
494              (long) len,
495              sparse ? (is_readable ? PROT_READ | PROT_EXEC : 0) : protection,
496              (addr == NULL ? 0 : MAP_FIXED) | MAP_PRIVATE,
497              (is_readable || !sparse) ? map_fd : empty_fd,
498              (off_t) (offset == OFFSET_NONE ? 0 : offset));
499
500     if ((long) real == -1) {
501         perror("mapin: mmap");
502         return NULL;
503     }
504
505     if (addr == NULL) {
506         /*
507          * now play around with what the os gave us to make it align by
508          * our standards (which is why we overallocated)
509          */
510         os_vm_size_t overflow;
511
512         addr = os_round_up_to_page(real);
513         if (addr != real)
514             munmap((caddr_t) real, addr - real);
515
516         overflow = real_page_size_difference - (addr - real);
517         if (overflow != 0)
518             munmap((caddr_t) (addr + len - real_page_size_difference),
519                    overflow);
520
521         real = addr;
522     }
523
524
525     return real;
526 }
527
528 static os_vm_address_t
529 map_and_remember(addr, len, protection, map_fd, offset, is_readable)
530      os_vm_address_t addr;
531      os_vm_size_t len;
532      int protection;
533      int map_fd;
534      long offset;
535      int is_readable;
536 {
537     os_vm_address_t real =
538
539         mapin(addr, len, protection, map_fd, offset, is_readable);
540
541     if (real != NULL) {
542         struct segment *seg = seg_find(real);
543
544         if (seg != NULL)
545             seg = seg_change_range(seg, real, len, protection, map_fd, offset);
546         else
547             seg = seg_create(real, len, protection, map_fd, offset);
548
549         if (seg == NULL) {
550             munmap((caddr_t) real, len);
551             return NULL;
552         }
553     }
554 #ifdef DEBUG
555     fprintf(stderr,
556             ";;; map_and_remember: 0x%08x[0x%08x] offset: %d, mapped to: %d\n",
557             real, len, offset, map_fd);
558 #endif
559
560     return real;
561 }
562
563 /* ---------------------------------------------------------------- */
564
565 os_vm_address_t
566 os_validate(addr, len)
567      os_vm_address_t addr;
568      os_vm_size_t len;
569 {
570     addr = os_trunc_to_page(addr);
571     len = os_round_up_size_to_page(len);
572
573 #ifdef DEBUG
574     fprintf(stderr, ";;; os_validate: 0x%08x[0x%08x]\n", addr, len);
575 #endif
576
577     if (addr != NULL && collides_with_seg_p(addr, len))
578         return NULL;
579
580     return map_and_remember(addr, len, PROT_DEFAULT, zero_fd, OFFSET_NONE,
581                             FALSE);
582 }
583
584 void
585 os_invalidate(addr, len)
586      os_vm_address_t addr;
587      os_vm_size_t len;
588 {
589     struct segment *seg = seg_find(addr);
590
591     addr = os_trunc_to_page(addr);
592     len = os_round_up_size_to_page(len);
593
594 #ifdef DEBUG
595     fprintf(stderr, ";;; os_invalidate: 0x%08x[0x%08x]\n", addr, len);
596 #endif
597
598     if (seg == NULL)
599         fprintf(stderr, "os_invalidate: Unknown segment: 0x%08x[0x%08x]\n",
600                 addr, len);
601     else {
602         seg = seg_change_range(seg, addr, len, 0, 0, OFFSET_NONE);
603         if (seg != NULL)
604             seg_destroy(seg);
605
606         last_fault = 0;
607         if (munmap((caddr_t) addr, len) != 0)
608             perror("os_invalidate: munmap");
609     }
610 }
611
612 os_vm_address_t
613 os_map(fd, offset, addr, len)
614      int fd;
615      int offset;
616      os_vm_address_t addr;
617      long len;
618 {
619     addr = os_trunc_to_page(addr);
620     len = os_round_up_size_to_page(len);
621
622 #ifdef DEBUG
623     fprintf(stderr, ";;; os_map: 0x%08x[0x%08x]\n", addr, len);
624 #endif
625
626     return map_and_remember(addr, len, PROT_DEFAULT, fd, offset, TRUE);
627 }
628
629 void
630 os_flush_icache(address, length)
631      os_vm_address_t address;
632      os_vm_size_t length;
633 {
634 #if defined(MACH) && defined(mips)
635     vm_machine_attribute_val_t flush;
636     kern_return_t kr;
637
638     flush = MATTR_VAL_ICACHE_FLUSH;
639
640     kr = vm_machine_attribute(task_self(), address, length,
641                               MATTR_CACHE, &flush);
642     if (kr != KERN_SUCCESS)
643         mach_error("Could not flush the instruction cache", kr);
644 #endif
645 }
646
647 void
648 os_protect(addr, len, prot)
649      os_vm_address_t addr;
650      os_vm_size_t len;
651      int prot;
652 {
653     struct segment *seg = seg_find(addr);
654
655     addr = os_trunc_to_page(addr);
656     len = os_round_up_size_to_page(len);
657
658 #ifdef DEBUG
659     fprintf(stderr, ";;; os_protect: 0x%08x[0x%08x]\n", addr, len);
660 #endif
661
662     if (seg != NULL) {
663         int old_prot = seg->protection;
664
665         if (prot != old_prot) {
666             /*
667              * oooooh, sick: we have to make sure all the pages being protected have
668              * faulted in, so they're in a known state...
669              */
670             seg_force_resident(seg, addr, len);
671
672             seg_change_range(seg, addr, len, prot, seg->mapped_fd,
673                              seg->file_offset);
674
675             if (mprotect((caddr_t) addr, (long) len, prot) != 0)
676                 perror("os_unprotect: mprotect");
677         }
678     } else
679         fprintf(stderr, "os_protect: Unknown segment: 0x%08x[0x%08x]\n", addr,
680                 len);
681 }
682
683 boolean
684 valid_addr(test)
685      os_vm_address_t test;
686 {
687     return seg_find(test) != NULL;
688 }
689
690 /* ---------------------------------------------------------------- */
691
692 static boolean
693 maybe_gc(HANDLER_ARGS)
694 {
695     /*
696      * It's necessary to enable recursive SEGVs, since the handle is
697      * used for multiple things (e.g., both gc-trigger & faulting in pages).
698      * We check against recursive gc's though...
699      */
700
701     boolean did_gc;
702     static already_trying = 0;
703
704     if (already_trying)
705         return FALSE;
706
707     SAVE_CONTEXT();
708
709     sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
710
711     already_trying = TRUE;
712     did_gc = interrupt_maybe_gc(signal, code, context);
713     already_trying = FALSE;
714
715     return did_gc;
716 }
717
718 /*
719  * The primary point of catching segmentation violations is to allow 
720  * read only memory to be re-mapped with more permissions when a write
721  * is attempted.  this greatly decreases the residency of the program
722  * in swap space since read only areas don't take up room
723  *
724  * Running into the gc trigger page will also end up here...
725  */
726 void
727 segv_handler(HANDLER_ARGS, caddr_t addr)
728 {
729     SAVE_CONTEXT();
730
731     if (CODE(code) == OS_PROTERR) {     /* allow writes to this chunk */
732         struct segment *seg = seg_find(addr);
733
734         if ((caddr_t) last_fault == addr) {
735             if (seg != NULL && maybe_gc(signal, code, context))
736                 /* we just garbage collected */
737                 return;
738             else {
739                 /* a *real* protection fault */
740                 fprintf(stderr,
741                         "segv_handler: Real protection violation: 0x%08x\n",
742                         addr);
743                 interrupt_handle_now(signal, code, context);
744             }
745         } else
746             last_fault = (os_vm_address_t) addr;
747
748         if (seg != NULL) {
749             int err;
750
751             /* round down to a page */
752             os_vm_address_t block =
753
754                 (os_vm_address_t) ((long) addr & ~SPARSE_SIZE_MASK);
755             os_vm_size_t length = SPARSE_BLOCK_SIZE;
756
757             if (block < seg->start) {
758                 length -= (seg->start - block);
759                 block = seg->start;
760             }
761             if (block + length > seg->start + seg->length)
762                 length = seg->start + seg->length - block;
763
764 #if 0
765             /* unmap it.  probably redundant. */
766             if (munmap((caddr_t) block, length) == -1)
767                 perror("segv_handler: munmap");
768 #endif
769
770             /* and remap it with more permissions */
771             err = (int)
772                 mmap((caddr_t) block,
773                      length,
774                      seg->protection,
775                      MAP_PRIVATE | MAP_FIXED,
776                      seg->mapped_fd,
777                      seg->file_offset == OFFSET_NONE
778                      ? 0 : seg->file_offset + (block - seg->start));
779
780             if (err == -1) {
781                 perror("segv_handler: mmap");
782                 interrupt_handle_now(signal, code, context);
783             }
784         } else {
785             fprintf(stderr, "segv_handler: 0x%08x not in any segment\n", addr);
786             interrupt_handle_now(signal, code, context);
787         }
788     }
789     /*
790      * note that we check for a gc-trigger hit even if it's not a PROT error
791      */
792     else if (!maybe_gc(signal, code, context)) {
793         static int nomap_count = 0;
794
795         if (CODE(code) == OS_MAPERR) {
796             if (nomap_count == 0) {
797                 fprintf(stderr,
798                         "segv_handler: No mapping fault: 0x%08x\n", addr);
799                 nomap_count++;
800             } else {
801                 /*
802                  * There should be higher-level protection against stack
803                  * overflow somewhere, but at least this prevents infinite
804                  * puking of error messages...
805                  */
806                 fprintf(stderr,
807                         "segv_handler: Recursive no mapping fault (stack overflow?)\n");
808                 exit(-1);
809             }
810         } else if (OS_HASERRNO(code)) {
811             errno = OS_ERRNO(code);
812             perror("segv_handler: Object error");
813         }
814
815         interrupt_handle_now(signal, code, context);
816
817         if (CODE(code) == OS_MAPERR)
818             nomap_count--;
819     }
820 }
821
822 void
823 os_install_interrupt_handlers(void)
824 {
825     interrupt_install_low_level_handler(SIGSEGV, segv_handler);
826 }
827
828 os_vm_address_t round_up_sparse_size(os_vm_address_t addr)
829 {
830     return (addr + SPARSE_BLOCK_SIZE - 1) & ~SPARSE_SIZE_MASK;
831 }
832
833 /*
834  * An array of the start of the spaces which should have holes placed
835  * after them.  Must not include the dynamic spaces because the size
836  * of the dynamic space can be controlled from the command line.
837  */
838 static os_vm_address_t spaces[] = {
839     READ_ONLY_SPACE_START, STATIC_SPACE_START,
840     BINDING_STACK_START, CONTROL_STACK_START
841 };
842
843 /*
844   
845  * The corresponding array for the size of each space.  Be sure that
846  * the spaces and holes don't overlap!  The sizes MUST be on
847  * SPARSE_BLOCK_SIZE boundaries.
848  
849  */
850 static unsigned long space_size[] = {
851     READ_ONLY_SPACE_SIZE, STATIC_SPACE_SIZE,
852     BINDING_STACK_SIZE, CONTROL_STACK_SIZE
853 };
854
855 /*
856  * The size of the hole to make.  It should be strictly smaller than
857  * SPARSE_BLOCK_SIZE.
858  */
859
860 #define HOLE_SIZE 0x2000
861
862 void
863 make_holes(void)
864 {
865     int k;
866     os_vm_address_t hole;
867
868     /* Make holes of the appropriate size for desired spaces */
869
870     for (k = 0; k < sizeof(spaces) / sizeof(spaces[0]); ++k) {
871
872         hole = spaces[k] + space_size[k];
873
874         if (os_validate(hole, HOLE_SIZE) == NULL) {
875             fprintf(stderr,
876                     "ensure_space: Failed to validate hole of %ld bytes at 0x%08X\n",
877                     HOLE_SIZE, (unsigned long) hole);
878             exit(1);
879         }
880         /* Make it inaccessible */
881         os_protect(hole, HOLE_SIZE, 0);
882     }
883
884     /* Round up the dynamic_space_size to the nearest SPARSE_BLOCK_SIZE */
885     dynamic_space_size = round_up_sparse_size(dynamic_space_size);
886
887     /* Now make a hole for the dynamic spaces */
888     hole = dynamic_space_size + (os_vm_address_t) dynamic_0_space;
889
890     if (os_validate(hole, HOLE_SIZE) == NULL) {
891         fprintf(stderr,
892                 "ensure_space: Failed to validate hold of %ld bytes at 0x%08X\n",
893                 HOLE_SIZE, (unsigned long) hole);
894         exit(1);
895     }
896     os_protect(hole, HOLE_SIZE, 0);
897
898     hole = dynamic_space_size + (os_vm_address_t) dynamic_1_space;
899     if (os_validate(hole, HOLE_SIZE) == NULL) {
900         fprintf(stderr,
901                 "ensure_space: Failed to validate hole of %ld bytes at 0x%08X\n",
902                 HOLE_SIZE, (unsigned long) hole);
903         exit(1);
904     }
905     os_protect(hole, HOLE_SIZE, 0);
906 }