Clean up RCS ids
[projects/cmucl/cmucl.git] / src / lisp / sunos-os.c
CommitLineData
c871866f 1/*
c871866f 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
571df509 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)
24extern int errno;
571df509 25
c871866f 26#include "os.h"
3d7012f9 27/* To get dynamic_0_space and friends */
28#include "globals.h"
29/* To get memory map */
30#include "sparc-validate.h"
c871866f 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
e802325d 43#define INITIAL_MAX_SEGS 32
44#define GROW_MAX_SEGS 16
c871866f 45
46extern char *getenv();
47
48/* ---------------------------------------------------------------- */
49
50#define ADJ_OFFSET(off,adj) (((off)==OFFSET_NONE) ? OFFSET_NONE : ((off)+(adj)))
51
9a8c1c2f 52long os_vm_page_size = (-1);
53static long os_real_page_size = (-1);
c871866f 54
55static struct segment {
56 os_vm_address_t start; /* note: start & length are expected to be on page */
9a8c1c2f 57 os_vm_size_t length; /* boundaries */
c871866f 58 long file_offset;
59 short mapped_fd;
60 short protection;
e802325d 61} *segments;
c871866f 62
9a8c1c2f 63static int n_segments = 0, max_segments = 0;
c871866f 64
9a8c1c2f 65static int zero_fd = (-1), empty_fd = (-1);
c871866f 66
9a8c1c2f 67static os_vm_address_t last_fault = 0;
68static os_vm_size_t real_page_size_difference = 0;
c871866f 69
44a8f0c7
RT
70void
71os_init0(const char *argv[], const char *envp[])
72{}
73
9a8c1c2f 74static void
75os_init_bailout(arg)
76 char *arg;
e802325d 77{
78 char buf[500];
9a8c1c2f 79
80 sprintf(buf, "os_init: %s", arg);
e802325d 81 perror(buf);
82 exit(1);
83}
84
9a8c1c2f 85void
dafb9e03 86os_init0(const char *argv[], const char *envp[])
87{}
88
89void
0f0aed07 90os_init(const char *argv[], const char *envp[])
c871866f 91{
9a8c1c2f 92 char *empty_file = getenv("CMUCL_EMPTYFILE");
c871866f 93
9a8c1c2f 94 if (empty_file == NULL)
95 empty_file = EMPTYFILE;
c871866f 96
9a8c1c2f 97 empty_fd = open(empty_file, O_RDONLY | O_CREAT);
98 if (empty_fd < 0)
e802325d 99 os_init_bailout(empty_file);
c871866f 100 unlink(empty_file);
101
9a8c1c2f 102 zero_fd = open(ZEROFILE, O_RDONLY);
103 if (zero_fd < 0)
e802325d 104 os_init_bailout(ZEROFILE);
c871866f 105
571df509 106 os_vm_page_size = os_real_page_size = getpagesize();
c871866f 107
9a8c1c2f 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",
e802325d 113 max_segments);
114 exit(1);
115 }
116
9a8c1c2f 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);
c871866f 120 exit(1);
9a8c1c2f 121 } else {
c871866f 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 */
9a8c1c2f 129 real_page_size_difference = OS_VM_DEFAULT_PAGESIZE - os_vm_page_size;
130 os_vm_page_size = OS_VM_DEFAULT_PAGESIZE;
c871866f 131 }
132}
133
134/* ---------------------------------------------------------------- */
135
9a8c1c2f 136void
137seg_force_resident(struct segment *seg, os_vm_address_t addr, os_vm_size_t len)
c871866f 138{
9a8c1c2f 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);
c871866f 152 }
153 }
154}
155
9a8c1c2f 156static struct segment *
157seg_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;
c871866f 162{
163 int n;
164 struct segment *seg;
165
9a8c1c2f 166 if (len == 0)
c871866f 167 return NULL;
168
9a8c1c2f 169 if (n_segments == max_segments) {
e802325d 170 struct segment *new_segs;
171
9a8c1c2f 172 max_segments += GROW_MAX_SEGS;
e802325d 173
9a8c1c2f 174 new_segs = (struct segment *)
175 realloc(segments, max_segments * sizeof(struct segment));
e802325d 176
9a8c1c2f 177 if (new_segs == NULL) {
e802325d 178 fprintf(stderr,
179 "seg_create_nomerge: Couldn't grow segment descriptor table to %s segments\n",
180 max_segments);
9a8c1c2f 181 max_segments -= GROW_MAX_SEGS;
e802325d 182 return NULL;
183 }
9a8c1c2f 184
185 segments = new_segs;
c871866f 186 }
187
9a8c1c2f 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];
c871866f 193 seg--;
194 }
195 break;
196 }
197
198 n_segments++;
199
9a8c1c2f 200 seg->start = addr;
201 seg->length = len;
202 seg->protection = protection;
203 seg->mapped_fd = mapped_fd;
204 seg->file_offset = file_offset;
c871866f 205
206 return seg;
207}
208
e802325d 209#if 1
c871866f 210/* returns the first segment containing addr */
9a8c1c2f 211static struct segment *
212seg_find(addr)
213 os_vm_address_t addr;
c871866f 214{
215 int n;
216 struct segment *seg;
217
9a8c1c2f 218 for (n = n_segments, seg = segments; n > 0; n--, seg++)
219 if (seg->start <= addr && seg->start + seg->length > addr)
c871866f 220 return seg;
221
222 return NULL;
223}
e802325d 224#else
225/* returns the first segment containing addr */
9a8c1c2f 226static struct segment *
227seg_find(addr)
228 os_vm_address_t addr;
e802325d 229{
230 /* does a binary search */
9a8c1c2f 231 struct segment *lo = segments, *hi = segments + n_segments;
e802325d 232
9a8c1c2f 233 while (hi > lo) {
234 struct segment *mid = lo + ((hi - lo) >> 1);
235 os_vm_address_t start = mid->start;
e802325d 236
9a8c1c2f 237 if (addr >= start && addr < start + mid->length)
e802325d 238 return mid;
9a8c1c2f 239 else if (addr < start)
240 hi = mid;
e802325d 241 else
9a8c1c2f 242 lo = mid + 1;
e802325d 243 }
244
245 return NULL;
246}
247#endif
c871866f 248
249/* returns TRUE if the range from addr to addr+len intersects with any segment */
9a8c1c2f 250static boolean
251collides_with_seg_p(addr, len)
252 os_vm_address_t addr;
253 os_vm_size_t len;
c871866f 254{
255 int n;
256 struct segment *seg;
9a8c1c2f 257 os_vm_address_t end = addr + len;
c871866f 258
9a8c1c2f 259 for (n = n_segments, seg = segments; n > 0; n--, seg++)
260 if (seg->start >= end)
c871866f 261 return FALSE;
9a8c1c2f 262 else if (seg->start + seg->length > addr)
c871866f 263 return TRUE;
264
265 return FALSE;
266}
267
9a8c1c2f 268#if 0 /* WAY to SLOW */
571df509 269/* returns TRUE if the range from addr to addr+len is a valid mapping
270 * (that we don't know about) */
9a8c1c2f 271static boolean
272mem_in_use(addr, len)
273 os_vm_address_t addr;
274 os_vm_size_t len;
571df509 275{
276 os_vm_address_t p;
277
278 for (p = addr; addr < addr + len; p += os_real_page_size) {
279 char c;
280
9a8c1c2f 281 if (mincore((caddr_t) p, os_real_page_size, &c) == 0 || errno != ENOMEM)
571df509 282 return TRUE;
283 }
284 return FALSE;
285}
286#endif
287
e802325d 288#define seg_last_p(seg) (((seg)-segments)>=n_segments-1)
c871866f 289
9a8c1c2f 290static void
291seg_destroy(seg)
292 struct segment *seg;
c871866f 293{
9a8c1c2f 294 if (seg != NULL) {
c871866f 295 int n;
296
9a8c1c2f 297 for (n = seg - segments + 1; n < n_segments; n++) {
298 seg[0] = seg[1];
c871866f 299 seg++;
300 }
301
302 n_segments--;
303 }
304}
305
9a8c1c2f 306static void
307seg_try_merge_next(seg)
308 struct segment *seg;
c871866f 309{
9a8c1c2f 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) {
c871866f 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",
9a8c1c2f 321 seg->start, seg->length, nseg->start, nseg->length);
c871866f 322#endif
323
9a8c1c2f 324 if (((long) nseg->start & SPARSE_SIZE_MASK) != 0) {
c871866f 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 */
9a8c1c2f 329 seg_force_resident(seg, nseg->start - 1, 1);
330 seg_force_resident(nseg, nseg->start, 1);
c871866f 331 }
332
9a8c1c2f 333 seg->length += nseg->length;
c871866f 334 seg_destroy(nseg);
335 }
336}
337
338
339/*
340 * Try to merge seg with adjacent segments.
341 */
9a8c1c2f 342static void
343seg_try_merge_adjacent(seg)
344 struct segment *seg;
c871866f 345{
9a8c1c2f 346 if (!seg_last_p(seg))
c871866f 347 seg_try_merge_next(seg);
9a8c1c2f 348 if (seg > segments)
349 seg_try_merge_next(seg - 1);
c871866f 350}
351
9a8c1c2f 352static struct segment *
353seg_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;
c871866f 358{
9a8c1c2f 359 struct segment *seg =
360
361 seg_create_nomerge(addr, len, protection, mapped_fd, file_offset);
362 if (seg != NULL)
c871866f 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 */
9a8c1c2f 371static struct segment *
372seg_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;
c871866f 378{
9a8c1c2f 379 os_vm_address_t end = addr + len;
c871866f 380
9a8c1c2f 381 if (len == 0)
c871866f 382 return NULL;
383
9a8c1c2f 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
c871866f 392#ifdef DEBUG
393 fprintf(stderr,
394 ";;; seg_change_range: region 0x%08x[0x%08x] overflows 0x%08x[0x%08x]\n",
9a8c1c2f 395 addr, len, seg->start, old_len);
c871866f 396#endif
9a8c1c2f 397
398 while (!seg_last_p(seg) && next->start + next->length <= end) {
c871866f 399#ifdef DEBUG
400 fprintf(stderr,
401 ";;; seg_change_range: merging extra segment 0x%08x[0x%08x]\n",
9a8c1c2f 402 next->start, next->length);
c871866f 403#endif
404 seg_destroy(next);
405 }
9a8c1c2f 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
c871866f 414#ifdef DEBUG
415 fprintf(stderr,
416 ";;; seg_change_range: extended first seg to 0x%08x[0x%08x]\n",
9a8c1c2f 417 seg->start, old_len);
c871866f 418#endif
419 }
420
9a8c1c2f 421 if (seg_offset + len < old_len) {
c871866f 422 /* add second part of old segment */
423 seg_create_nomerge(end,
9a8c1c2f 424 old_len - (seg_offset + len),
c871866f 425 seg->protection,
426 seg->mapped_fd,
9a8c1c2f 427 ADJ_OFFSET(seg->file_offset, seg_offset + len));
c871866f 428
429#ifdef DEBUG
430 fprintf(stderr,
431 ";;; seg_change_range: Split off end of 0x%08x[0x%08x]: 0x%08x[0x%08x]\n",
9a8c1c2f 432 seg->start, old_len, end, old_len - (seg_offset + len));
c871866f 433#endif
434 }
435
9a8c1c2f 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 {
c871866f 442 /* adjust first part of remaining old segment */
9a8c1c2f 443 seg->length = seg_offset;
c871866f 444
445#ifdef DEBUG
446 fprintf(stderr,
447 ";;; seg_change_range: Split off beginning of 0x%08x[0x%08x]: 0x%08x[0x%08x]\n",
9a8c1c2f 448 seg->start, old_len, seg->start, seg_offset);
c871866f 449#endif
450
451 /* add new middle segment for new protected region */
9a8c1c2f 452 seg =
453 seg_create_nomerge(addr, len, protection, mapped_fd,
454 file_offset);
c871866f 455 }
456
457 seg_try_merge_adjacent(seg);
458
9a8c1c2f 459 last_fault = 0;
c871866f 460 }
461
462 return seg;
463}
464
465/* ---------------------------------------------------------------- */
466
9a8c1c2f 467static os_vm_address_t
468mapin(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;
c871866f 475{
476 os_vm_address_t real;
9a8c1c2f 477 boolean sparse = (len >= SPARSE_BLOCK_SIZE);
c871866f 478
9a8c1c2f 479 if (offset != OFFSET_NONE
480 && (offset < os_vm_page_size || (offset & (os_vm_page_size - 1)) != 0)) {
c871866f 481 fprintf(stderr,
482 "mapin: file offset (%d) not multiple of pagesize (%d)\n",
9a8c1c2f 483 offset, os_vm_page_size);
c871866f 484 }
485
9a8c1c2f 486 if (addr == NULL)
487 len += real_page_size_difference; /* futz around to get an aligned region */
c871866f 488
9a8c1c2f 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,
c871866f 495 (is_readable || !sparse) ? map_fd : empty_fd,
9a8c1c2f 496 (off_t) (offset == OFFSET_NONE ? 0 : offset));
c871866f 497
9a8c1c2f 498 if ((long) real == -1) {
c871866f 499 perror("mapin: mmap");
500 return NULL;
501 }
502
9a8c1c2f 503 if (addr == NULL) {
c871866f 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
9a8c1c2f 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);
c871866f 518
9a8c1c2f 519 real = addr;
c871866f 520 }
521
522
523 return real;
524}
525
9a8c1c2f 526static os_vm_address_t
527map_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;
c871866f 534{
9a8c1c2f 535 os_vm_address_t real =
c871866f 536
9a8c1c2f 537 mapin(addr, len, protection, map_fd, offset, is_readable);
c871866f 538
9a8c1c2f 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);
c871866f 544 else
9a8c1c2f 545 seg = seg_create(real, len, protection, map_fd, offset);
c871866f 546
9a8c1c2f 547 if (seg == NULL) {
548 munmap((caddr_t) real, len);
c871866f 549 return NULL;
550 }
551 }
c871866f 552#ifdef DEBUG
9a8c1c2f 553 fprintf(stderr,
554 ";;; map_and_remember: 0x%08x[0x%08x] offset: %d, mapped to: %d\n",
555 real, len, offset, map_fd);
c871866f 556#endif
557
558 return real;
559}
560
561/* ---------------------------------------------------------------- */
562
9a8c1c2f 563os_vm_address_t
564os_validate(addr, len)
565 os_vm_address_t addr;
566 os_vm_size_t len;
c871866f 567{
9a8c1c2f 568 addr = os_trunc_to_page(addr);
569 len = os_round_up_size_to_page(len);
c871866f 570
571#ifdef DEBUG
9a8c1c2f 572 fprintf(stderr, ";;; os_validate: 0x%08x[0x%08x]\n", addr, len);
c871866f 573#endif
574
9a8c1c2f 575 if (addr != NULL && collides_with_seg_p(addr, len))
c871866f 576 return NULL;
577
9a8c1c2f 578 return map_and_remember(addr, len, PROT_DEFAULT, zero_fd, OFFSET_NONE,
579 FALSE);
c871866f 580}
581
9a8c1c2f 582void
583os_invalidate(addr, len)
584 os_vm_address_t addr;
585 os_vm_size_t len;
c871866f 586{
9a8c1c2f 587 struct segment *seg = seg_find(addr);
c871866f 588
9a8c1c2f 589 addr = os_trunc_to_page(addr);
590 len = os_round_up_size_to_page(len);
c871866f 591
592#ifdef DEBUG
9a8c1c2f 593 fprintf(stderr, ";;; os_invalidate: 0x%08x[0x%08x]\n", addr, len);
c871866f 594#endif
595
9a8c1c2f 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)
c871866f 602 seg_destroy(seg);
603
9a8c1c2f 604 last_fault = 0;
605 if (munmap((caddr_t) addr, len) != 0)
c871866f 606 perror("os_invalidate: munmap");
607 }
608}
609
9a8c1c2f 610os_vm_address_t
611os_map(fd, offset, addr, len)
612 int fd;
613 int offset;
614 os_vm_address_t addr;
615 long len;
c871866f 616{
9a8c1c2f 617 addr = os_trunc_to_page(addr);
618 len = os_round_up_size_to_page(len);
c871866f 619
620#ifdef DEBUG
9a8c1c2f 621 fprintf(stderr, ";;; os_map: 0x%08x[0x%08x]\n", addr, len);
c871866f 622#endif
623
9a8c1c2f 624 return map_and_remember(addr, len, PROT_DEFAULT, fd, offset, TRUE);
c871866f 625}
626
9a8c1c2f 627void
628os_flush_icache(address, length)
629 os_vm_address_t address;
630 os_vm_size_t length;
c871866f 631{
632#if defined(MACH) && defined(mips)
9a8c1c2f 633 vm_machine_attribute_val_t flush;
634 kern_return_t kr;
c871866f 635
9a8c1c2f 636 flush = MATTR_VAL_ICACHE_FLUSH;
c871866f 637
9a8c1c2f 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);
c871866f 642#endif
643}
644
9a8c1c2f 645void
646os_protect(addr, len, prot)
647 os_vm_address_t addr;
648 os_vm_size_t len;
649 int prot;
c871866f 650{
9a8c1c2f 651 struct segment *seg = seg_find(addr);
c871866f 652
9a8c1c2f 653 addr = os_trunc_to_page(addr);
654 len = os_round_up_size_to_page(len);
c871866f 655
656#ifdef DEBUG
9a8c1c2f 657 fprintf(stderr, ";;; os_protect: 0x%08x[0x%08x]\n", addr, len);
c871866f 658#endif
659
9a8c1c2f 660 if (seg != NULL) {
661 int old_prot = seg->protection;
c871866f 662
9a8c1c2f 663 if (prot != old_prot) {
c871866f 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 */
9a8c1c2f 668 seg_force_resident(seg, addr, len);
c871866f 669
9a8c1c2f 670 seg_change_range(seg, addr, len, prot, seg->mapped_fd,
671 seg->file_offset);
c871866f 672
9a8c1c2f 673 if (mprotect((caddr_t) addr, (long) len, prot) != 0)
c871866f 674 perror("os_unprotect: mprotect");
675 }
9a8c1c2f 676 } else
677 fprintf(stderr, "os_protect: Unknown segment: 0x%08x[0x%08x]\n", addr,
678 len);
c871866f 679}
680
9a8c1c2f 681boolean
682valid_addr(test)
683 os_vm_address_t test;
c871866f 684{
9a8c1c2f 685 return seg_find(test) != NULL;
c871866f 686}
687
688/* ---------------------------------------------------------------- */
689
9a8c1c2f 690static boolean
691maybe_gc(HANDLER_ARGS)
c871866f 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;
9a8c1c2f 700 static already_trying = 0;
c871866f 701
9a8c1c2f 702 if (already_trying)
c871866f 703 return FALSE;
704
571df509 705 SAVE_CONTEXT();
706
9a8c1c2f 707 sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
c871866f 708
9a8c1c2f 709 already_trying = TRUE;
710 did_gc = interrupt_maybe_gc(signal, code, context);
711 already_trying = FALSE;
c871866f 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 */
9a8c1c2f 724void
725segv_handler(HANDLER_ARGS, caddr_t addr)
c871866f 726{
571df509 727 SAVE_CONTEXT();
728
729 if (CODE(code) == OS_PROTERR) { /* allow writes to this chunk */
9a8c1c2f 730 struct segment *seg = seg_find(addr);
c871866f 731
9a8c1c2f 732 if ((caddr_t) last_fault == addr) {
733 if (seg != NULL && maybe_gc(signal, code, context))
c871866f 734 /* we just garbage collected */
735 return;
9a8c1c2f 736 else {
c871866f 737 /* a *real* protection fault */
738 fprintf(stderr,
739 "segv_handler: Real protection violation: 0x%08x\n",
740 addr);
9a8c1c2f 741 interrupt_handle_now(signal, code, context);
c871866f 742 }
9a8c1c2f 743 } else
744 last_fault = (os_vm_address_t) addr;
c871866f 745
9a8c1c2f 746 if (seg != NULL) {
c871866f 747 int err;
9a8c1c2f 748
c871866f 749 /* round down to a page */
9a8c1c2f 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;
c871866f 754
9a8c1c2f 755 if (block < seg->start) {
756 length -= (seg->start - block);
757 block = seg->start;
c871866f 758 }
9a8c1c2f 759 if (block + length > seg->start + seg->length)
760 length = seg->start + seg->length - block;
c871866f 761
762#if 0
763 /* unmap it. probably redundant. */
9a8c1c2f 764 if (munmap((caddr_t) block, length) == -1)
c871866f 765 perror("segv_handler: munmap");
766#endif
767
768 /* and remap it with more permissions */
9a8c1c2f 769 err = (int)
770 mmap((caddr_t) block,
c871866f 771 length,
772 seg->protection,
9a8c1c2f 773 MAP_PRIVATE | MAP_FIXED,
c871866f 774 seg->mapped_fd,
9a8c1c2f 775 seg->file_offset == OFFSET_NONE
776 ? 0 : seg->file_offset + (block - seg->start));
c871866f 777
778 if (err == -1) {
779 perror("segv_handler: mmap");
9a8c1c2f 780 interrupt_handle_now(signal, code, context);
c871866f 781 }
9a8c1c2f 782 } else {
783 fprintf(stderr, "segv_handler: 0x%08x not in any segment\n", addr);
784 interrupt_handle_now(signal, code, context);
c871866f 785 }
786 }
787 /*
788 * note that we check for a gc-trigger hit even if it's not a PROT error
789 */
9a8c1c2f 790 else if (!maybe_gc(signal, code, context)) {
791 static int nomap_count = 0;
c871866f 792
9a8c1c2f 793 if (CODE(code) == OS_MAPERR) {
794 if (nomap_count == 0) {
c871866f 795 fprintf(stderr,
9a8c1c2f 796 "segv_handler: No mapping fault: 0x%08x\n", addr);
c871866f 797 nomap_count++;
9a8c1c2f 798 } else {
c871866f 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 }
9a8c1c2f 808 } else if (OS_HASERRNO(code)) {
809 errno = OS_ERRNO(code);
c871866f 810 perror("segv_handler: Object error");
811 }
812
9a8c1c2f 813 interrupt_handle_now(signal, code, context);
c871866f 814
9a8c1c2f 815 if (CODE(code) == OS_MAPERR)
c871866f 816 nomap_count--;
817 }
818}
819
9a8c1c2f 820void
b8d0dfaf 821os_install_interrupt_handlers(void)
c871866f 822{
9a8c1c2f 823 interrupt_install_low_level_handler(SIGSEGV, segv_handler);
c871866f 824}
571df509 825
9a8c1c2f 826os_vm_address_t round_up_sparse_size(os_vm_address_t addr)
3d7012f9 827{
9a8c1c2f 828 return (addr + SPARSE_BLOCK_SIZE - 1) & ~SPARSE_SIZE_MASK;
3d7012f9 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 */
9a8c1c2f 836static os_vm_address_t spaces[] = {
837 READ_ONLY_SPACE_START, STATIC_SPACE_START,
838 BINDING_STACK_START, CONTROL_STACK_START
3d7012f9 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 */
9a8c1c2f 848static unsigned long space_size[] = {
849 READ_ONLY_SPACE_SIZE, STATIC_SPACE_SIZE,
850 BINDING_STACK_SIZE, CONTROL_STACK_SIZE
3d7012f9 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
9a8c1c2f 860void
861make_holes(void)
3d7012f9 862{
9a8c1c2f 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);
3d7012f9 880 }
881
9a8c1c2f 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);
3d7012f9 893 }
9a8c1c2f 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);
3d7012f9 902 }
9a8c1c2f 903 os_protect(hole, HOLE_SIZE, 0);
3d7012f9 904}