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.
8 * This is the SunOS version.
9 * March 1991, Miles Bader <miles@cogsci.ed.ack.uk> & ted <ted@edu.NMSU>
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)
27 /* To get dynamic_0_space and friends */
29 /* To get memory map */
30 #include "sparc-validate.h"
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)
36 #define PROT_DEFAULT OS_VM_PROT_ALL
38 #define OFFSET_NONE ((os_vm_offset_t)(~0))
40 #define EMPTYFILE "/tmp/empty"
41 #define ZEROFILE "/dev/zero"
43 #define INITIAL_MAX_SEGS 32
44 #define GROW_MAX_SEGS 16
46 extern char *getenv();
48 /* ---------------------------------------------------------------- */
50 #define ADJ_OFFSET(off,adj) (((off)==OFFSET_NONE) ? OFFSET_NONE : ((off)+(adj)))
52 long os_vm_page_size = (-1);
53 static long os_real_page_size = (-1);
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 */
63 static int n_segments = 0, max_segments = 0;
65 static int zero_fd = (-1), empty_fd = (-1);
67 static os_vm_address_t last_fault = 0;
68 static os_vm_size_t real_page_size_difference = 0;
71 os_init0(const char *argv[], const char *envp[])
80 sprintf(buf, "os_init: %s", arg);
86 os_init0(const char *argv[], const char *envp[])
90 os_init(const char *argv[], const char *envp[])
92 char *empty_file = getenv("CMUCL_EMPTYFILE");
94 if (empty_file == NULL)
95 empty_file = EMPTYFILE;
97 empty_fd = open(empty_file, O_RDONLY | O_CREAT);
99 os_init_bailout(empty_file);
102 zero_fd = open(ZEROFILE, O_RDONLY);
104 os_init_bailout(ZEROFILE);
106 os_vm_page_size = os_real_page_size = getpagesize();
108 max_segments = INITIAL_MAX_SEGS;
109 segments = (struct segment *) malloc(sizeof(struct segment) * max_segments);
111 if (segments == NULL) {
112 fprintf(stderr, "os_init: Couldn't allocate %d segment descriptors\n",
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);
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.
129 real_page_size_difference = OS_VM_DEFAULT_PAGESIZE - os_vm_page_size;
130 os_vm_page_size = OS_VM_DEFAULT_PAGESIZE;
134 /* ---------------------------------------------------------------- */
137 seg_force_resident(struct segment *seg, os_vm_address_t addr, os_vm_size_t len)
139 int prot = seg->protection;
142 os_vm_address_t end = addr + len, touch = addr;
144 while (touch < end) {
145 int contents = (*(char *) touch);
147 if (prot & OS_VM_PROT_WRITE)
148 (*(char *) touch) = contents;
150 (os_vm_address_t) (((long) touch + SPARSE_BLOCK_SIZE) &
156 static struct segment *
157 seg_create_nomerge(addr, len, protection, mapped_fd, file_offset)
158 os_vm_address_t addr;
169 if (n_segments == max_segments) {
170 struct segment *new_segs;
172 max_segments += GROW_MAX_SEGS;
174 new_segs = (struct segment *)
175 realloc(segments, max_segments * sizeof(struct segment));
177 if (new_segs == NULL) {
179 "seg_create_nomerge: Couldn't grow segment descriptor table to %s segments\n",
181 max_segments -= GROW_MAX_SEGS;
188 for (n = n_segments, seg = segments; n > 0; n--, seg++)
189 if (addr < seg->start) {
190 seg = (&segments[n_segments]);
202 seg->protection = protection;
203 seg->mapped_fd = mapped_fd;
204 seg->file_offset = file_offset;
210 /* returns the first segment containing addr */
211 static struct segment *
213 os_vm_address_t addr;
218 for (n = n_segments, seg = segments; n > 0; n--, seg++)
219 if (seg->start <= addr && seg->start + seg->length > addr)
225 /* returns the first segment containing addr */
226 static struct segment *
228 os_vm_address_t addr;
230 /* does a binary search */
231 struct segment *lo = segments, *hi = segments + n_segments;
234 struct segment *mid = lo + ((hi - lo) >> 1);
235 os_vm_address_t start = mid->start;
237 if (addr >= start && addr < start + mid->length)
239 else if (addr < start)
249 /* returns TRUE if the range from addr to addr+len intersects with any segment */
251 collides_with_seg_p(addr, len)
252 os_vm_address_t addr;
257 os_vm_address_t end = addr + len;
259 for (n = n_segments, seg = segments; n > 0; n--, seg++)
260 if (seg->start >= end)
262 else if (seg->start + seg->length > addr)
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) */
272 mem_in_use(addr, len)
273 os_vm_address_t addr;
278 for (p = addr; addr < addr + len; p += os_real_page_size) {
281 if (mincore((caddr_t) p, os_real_page_size, &c) == 0 || errno != ENOMEM)
288 #define seg_last_p(seg) (((seg)-segments)>=n_segments-1)
297 for (n = seg - segments + 1; n < n_segments; n++) {
307 seg_try_merge_next(seg)
310 struct segment *nseg = seg + 1;
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 */
320 ";;; seg_try_merge: Merged 0x%08x[0x%08x] with 0x%08x[0x%08x]\n",
321 seg->start, seg->length, nseg->start, nseg->length);
324 if (((long) nseg->start & SPARSE_SIZE_MASK) != 0) {
326 * if not on a block boundary, we have to ensure both parts
327 * of a common block are in a known state
329 seg_force_resident(seg, nseg->start - 1, 1);
330 seg_force_resident(nseg, nseg->start, 1);
333 seg->length += nseg->length;
340 * Try to merge seg with adjacent segments.
343 seg_try_merge_adjacent(seg)
346 if (!seg_last_p(seg))
347 seg_try_merge_next(seg);
349 seg_try_merge_next(seg - 1);
352 static struct segment *
353 seg_create(addr, len, protection, mapped_fd, file_offset)
354 os_vm_address_t addr;
359 struct segment *seg =
361 seg_create_nomerge(addr, len, protection, mapped_fd, file_offset);
363 seg_try_merge_adjacent(seg);
368 * Change the attributes of the given range of an existing segment, and return
369 * a segment corresponding to the new bit.
371 static struct segment *
372 seg_change_range(seg, addr, len, protection, mapped_fd, file_offset)
374 os_vm_address_t addr;
379 os_vm_address_t end = addr + len;
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);
389 if (old_len < len + seg_offset) {
390 struct segment *next = seg + 1;
394 ";;; seg_change_range: region 0x%08x[0x%08x] overflows 0x%08x[0x%08x]\n",
395 addr, len, seg->start, old_len);
398 while (!seg_last_p(seg) && next->start + next->length <= end) {
401 ";;; seg_change_range: merging extra segment 0x%08x[0x%08x]\n",
402 next->start, next->length);
407 if (!seg_last_p(seg) && next->start < end) {
408 next->length -= end - next->start;
410 old_len = next->start - seg->start;
412 old_len = len + seg_offset;
416 ";;; seg_change_range: extended first seg to 0x%08x[0x%08x]\n",
417 seg->start, old_len);
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),
427 ADJ_OFFSET(seg->file_offset, seg_offset + len));
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));
436 if (seg_offset == 0) {
438 seg->protection = protection;
439 seg->mapped_fd = mapped_fd;
440 seg->file_offset = file_offset;
442 /* adjust first part of remaining old segment */
443 seg->length = seg_offset;
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);
451 /* add new middle segment for new protected region */
453 seg_create_nomerge(addr, len, protection, mapped_fd,
457 seg_try_merge_adjacent(seg);
465 /* ---------------------------------------------------------------- */
467 static os_vm_address_t
468 mapin(addr, len, protection, map_fd, offset, is_readable)
469 os_vm_address_t addr;
476 os_vm_address_t real;
477 boolean sparse = (len >= SPARSE_BLOCK_SIZE);
479 if (offset != OFFSET_NONE
480 && (offset < os_vm_page_size || (offset & (os_vm_page_size - 1)) != 0)) {
482 "mapin: file offset (%d) not multiple of pagesize (%d)\n",
483 offset, os_vm_page_size);
487 len += real_page_size_difference; /* futz around to get an aligned region */
490 real = (os_vm_address_t)
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));
498 if ((long) real == -1) {
499 perror("mapin: mmap");
505 * now play around with what the os gave us to make it align by
506 * our standards (which is why we overallocated)
508 os_vm_size_t overflow;
510 addr = os_round_up_to_page(real);
512 munmap((caddr_t) real, addr - real);
514 overflow = real_page_size_difference - (addr - real);
516 munmap((caddr_t) (addr + len - real_page_size_difference),
526 static os_vm_address_t
527 map_and_remember(addr, len, protection, map_fd, offset, is_readable)
528 os_vm_address_t addr;
535 os_vm_address_t real =
537 mapin(addr, len, protection, map_fd, offset, is_readable);
540 struct segment *seg = seg_find(real);
543 seg = seg_change_range(seg, real, len, protection, map_fd, offset);
545 seg = seg_create(real, len, protection, map_fd, offset);
548 munmap((caddr_t) real, len);
554 ";;; map_and_remember: 0x%08x[0x%08x] offset: %d, mapped to: %d\n",
555 real, len, offset, map_fd);
561 /* ---------------------------------------------------------------- */
564 os_validate(addr, len)
565 os_vm_address_t addr;
568 addr = os_trunc_to_page(addr);
569 len = os_round_up_size_to_page(len);
572 fprintf(stderr, ";;; os_validate: 0x%08x[0x%08x]\n", addr, len);
575 if (addr != NULL && collides_with_seg_p(addr, len))
578 return map_and_remember(addr, len, PROT_DEFAULT, zero_fd, OFFSET_NONE,
583 os_invalidate(addr, len)
584 os_vm_address_t addr;
587 struct segment *seg = seg_find(addr);
589 addr = os_trunc_to_page(addr);
590 len = os_round_up_size_to_page(len);
593 fprintf(stderr, ";;; os_invalidate: 0x%08x[0x%08x]\n", addr, len);
597 fprintf(stderr, "os_invalidate: Unknown segment: 0x%08x[0x%08x]\n",
600 seg = seg_change_range(seg, addr, len, 0, 0, OFFSET_NONE);
605 if (munmap((caddr_t) addr, len) != 0)
606 perror("os_invalidate: munmap");
611 os_map(fd, offset, addr, len)
614 os_vm_address_t addr;
617 addr = os_trunc_to_page(addr);
618 len = os_round_up_size_to_page(len);
621 fprintf(stderr, ";;; os_map: 0x%08x[0x%08x]\n", addr, len);
624 return map_and_remember(addr, len, PROT_DEFAULT, fd, offset, TRUE);
628 os_flush_icache(address, length)
629 os_vm_address_t address;
632 #if defined(MACH) && defined(mips)
633 vm_machine_attribute_val_t flush;
636 flush = MATTR_VAL_ICACHE_FLUSH;
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);
646 os_protect(addr, len, prot)
647 os_vm_address_t addr;
651 struct segment *seg = seg_find(addr);
653 addr = os_trunc_to_page(addr);
654 len = os_round_up_size_to_page(len);
657 fprintf(stderr, ";;; os_protect: 0x%08x[0x%08x]\n", addr, len);
661 int old_prot = seg->protection;
663 if (prot != old_prot) {
665 * oooooh, sick: we have to make sure all the pages being protected have
666 * faulted in, so they're in a known state...
668 seg_force_resident(seg, addr, len);
670 seg_change_range(seg, addr, len, prot, seg->mapped_fd,
673 if (mprotect((caddr_t) addr, (long) len, prot) != 0)
674 perror("os_unprotect: mprotect");
677 fprintf(stderr, "os_protect: Unknown segment: 0x%08x[0x%08x]\n", addr,
683 os_vm_address_t test;
685 return seg_find(test) != NULL;
688 /* ---------------------------------------------------------------- */
691 maybe_gc(HANDLER_ARGS)
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...
700 static already_trying = 0;
707 sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
709 already_trying = TRUE;
710 did_gc = interrupt_maybe_gc(signal, code, context);
711 already_trying = FALSE;
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
722 * Running into the gc trigger page will also end up here...
725 segv_handler(HANDLER_ARGS, caddr_t addr)
729 if (CODE(code) == OS_PROTERR) { /* allow writes to this chunk */
730 struct segment *seg = seg_find(addr);
732 if ((caddr_t) last_fault == addr) {
733 if (seg != NULL && maybe_gc(signal, code, context))
734 /* we just garbage collected */
737 /* a *real* protection fault */
739 "segv_handler: Real protection violation: 0x%08x\n",
741 interrupt_handle_now(signal, code, context);
744 last_fault = (os_vm_address_t) addr;
749 /* round down to a page */
750 os_vm_address_t block =
752 (os_vm_address_t) ((long) addr & ~SPARSE_SIZE_MASK);
753 os_vm_size_t length = SPARSE_BLOCK_SIZE;
755 if (block < seg->start) {
756 length -= (seg->start - block);
759 if (block + length > seg->start + seg->length)
760 length = seg->start + seg->length - block;
763 /* unmap it. probably redundant. */
764 if (munmap((caddr_t) block, length) == -1)
765 perror("segv_handler: munmap");
768 /* and remap it with more permissions */
770 mmap((caddr_t) block,
773 MAP_PRIVATE | MAP_FIXED,
775 seg->file_offset == OFFSET_NONE
776 ? 0 : seg->file_offset + (block - seg->start));
779 perror("segv_handler: mmap");
780 interrupt_handle_now(signal, code, context);
783 fprintf(stderr, "segv_handler: 0x%08x not in any segment\n", addr);
784 interrupt_handle_now(signal, code, context);
788 * note that we check for a gc-trigger hit even if it's not a PROT error
790 else if (!maybe_gc(signal, code, context)) {
791 static int nomap_count = 0;
793 if (CODE(code) == OS_MAPERR) {
794 if (nomap_count == 0) {
796 "segv_handler: No mapping fault: 0x%08x\n", addr);
800 * There should be higher-level protection against stack
801 * overflow somewhere, but at least this prevents infinite
802 * puking of error messages...
805 "segv_handler: Recursive no mapping fault (stack overflow?)\n");
808 } else if (OS_HASERRNO(code)) {
809 errno = OS_ERRNO(code);
810 perror("segv_handler: Object error");
813 interrupt_handle_now(signal, code, context);
815 if (CODE(code) == OS_MAPERR)
821 os_install_interrupt_handlers(void)
823 interrupt_install_low_level_handler(SIGSEGV, segv_handler);
826 os_vm_address_t round_up_sparse_size(os_vm_address_t addr)
828 return (addr + SPARSE_BLOCK_SIZE - 1) & ~SPARSE_SIZE_MASK;
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.
836 static os_vm_address_t spaces[] = {
837 READ_ONLY_SPACE_START, STATIC_SPACE_START,
838 BINDING_STACK_START, CONTROL_STACK_START
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.
848 static unsigned long space_size[] = {
849 READ_ONLY_SPACE_SIZE, STATIC_SPACE_SIZE,
850 BINDING_STACK_SIZE, CONTROL_STACK_SIZE
854 * The size of the hole to make. It should be strictly smaller than
858 #define HOLE_SIZE 0x2000
864 os_vm_address_t hole;
866 /* Make holes of the appropriate size for desired spaces */
868 for (k = 0; k < sizeof(spaces) / sizeof(spaces[0]); ++k) {
870 hole = spaces[k] + space_size[k];
872 if (os_validate(hole, HOLE_SIZE) == NULL) {
874 "ensure_space: Failed to validate hole of %ld bytes at 0x%08X\n",
875 HOLE_SIZE, (unsigned long) hole);
878 /* Make it inaccessible */
879 os_protect(hole, HOLE_SIZE, 0);
882 /* Round up the dynamic_space_size to the nearest SPARSE_BLOCK_SIZE */
883 dynamic_space_size = round_up_sparse_size(dynamic_space_size);
885 /* Now make a hole for the dynamic spaces */
886 hole = dynamic_space_size + (os_vm_address_t) dynamic_0_space;
888 if (os_validate(hole, HOLE_SIZE) == NULL) {
890 "ensure_space: Failed to validate hold of %ld bytes at 0x%08X\n",
891 HOLE_SIZE, (unsigned long) hole);
894 os_protect(hole, HOLE_SIZE, 0);
896 hole = dynamic_space_size + (os_vm_address_t) dynamic_1_space;
897 if (os_validate(hole, HOLE_SIZE) == NULL) {
899 "ensure_space: Failed to validate hole of %ld bytes at 0x%08X\n",
900 HOLE_SIZE, (unsigned long) hole);
903 os_protect(hole, HOLE_SIZE, 0);