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