2 * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/sunos-os.c,v 1.13 2011/09/01 05:18:26 rtoy Exp $
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.
10 * This is the SunOS version.
11 * March 1991, Miles Bader <miles@cogsci.ed.ack.uk> & ted <ted@edu.NMSU>
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)
29 /* To get dynamic_0_space and friends */
31 /* To get memory map */
32 #include "sparc-validate.h"
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)
38 #define PROT_DEFAULT OS_VM_PROT_ALL
40 #define OFFSET_NONE ((os_vm_offset_t)(~0))
42 #define EMPTYFILE "/tmp/empty"
43 #define ZEROFILE "/dev/zero"
45 #define INITIAL_MAX_SEGS 32
46 #define GROW_MAX_SEGS 16
48 extern char *getenv();
50 /* ---------------------------------------------------------------- */
52 #define ADJ_OFFSET(off,adj) (((off)==OFFSET_NONE) ? OFFSET_NONE : ((off)+(adj)))
54 long os_vm_page_size = (-1);
55 static long os_real_page_size = (-1);
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 */
65 static int n_segments = 0, max_segments = 0;
67 static int zero_fd = (-1), empty_fd = (-1);
69 static os_vm_address_t last_fault = 0;
70 static os_vm_size_t real_page_size_difference = 0;
73 os_init0(const char *argv[], const char *envp[])
82 sprintf(buf, "os_init: %s", arg);
88 os_init0(const char *argv[], const char *envp[])
92 os_init(const char *argv[], const char *envp[])
94 char *empty_file = getenv("CMUCL_EMPTYFILE");
96 if (empty_file == NULL)
97 empty_file = EMPTYFILE;
99 empty_fd = open(empty_file, O_RDONLY | O_CREAT);
101 os_init_bailout(empty_file);
104 zero_fd = open(ZEROFILE, O_RDONLY);
106 os_init_bailout(ZEROFILE);
108 os_vm_page_size = os_real_page_size = getpagesize();
110 max_segments = INITIAL_MAX_SEGS;
111 segments = (struct segment *) malloc(sizeof(struct segment) * max_segments);
113 if (segments == NULL) {
114 fprintf(stderr, "os_init: Couldn't allocate %d segment descriptors\n",
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);
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.
131 real_page_size_difference = OS_VM_DEFAULT_PAGESIZE - os_vm_page_size;
132 os_vm_page_size = OS_VM_DEFAULT_PAGESIZE;
136 /* ---------------------------------------------------------------- */
139 seg_force_resident(struct segment *seg, os_vm_address_t addr, os_vm_size_t len)
141 int prot = seg->protection;
144 os_vm_address_t end = addr + len, touch = addr;
146 while (touch < end) {
147 int contents = (*(char *) touch);
149 if (prot & OS_VM_PROT_WRITE)
150 (*(char *) touch) = contents;
152 (os_vm_address_t) (((long) touch + SPARSE_BLOCK_SIZE) &
158 static struct segment *
159 seg_create_nomerge(addr, len, protection, mapped_fd, file_offset)
160 os_vm_address_t addr;
171 if (n_segments == max_segments) {
172 struct segment *new_segs;
174 max_segments += GROW_MAX_SEGS;
176 new_segs = (struct segment *)
177 realloc(segments, max_segments * sizeof(struct segment));
179 if (new_segs == NULL) {
181 "seg_create_nomerge: Couldn't grow segment descriptor table to %s segments\n",
183 max_segments -= GROW_MAX_SEGS;
190 for (n = n_segments, seg = segments; n > 0; n--, seg++)
191 if (addr < seg->start) {
192 seg = (&segments[n_segments]);
204 seg->protection = protection;
205 seg->mapped_fd = mapped_fd;
206 seg->file_offset = file_offset;
212 /* returns the first segment containing addr */
213 static struct segment *
215 os_vm_address_t addr;
220 for (n = n_segments, seg = segments; n > 0; n--, seg++)
221 if (seg->start <= addr && seg->start + seg->length > addr)
227 /* returns the first segment containing addr */
228 static struct segment *
230 os_vm_address_t addr;
232 /* does a binary search */
233 struct segment *lo = segments, *hi = segments + n_segments;
236 struct segment *mid = lo + ((hi - lo) >> 1);
237 os_vm_address_t start = mid->start;
239 if (addr >= start && addr < start + mid->length)
241 else if (addr < start)
251 /* returns TRUE if the range from addr to addr+len intersects with any segment */
253 collides_with_seg_p(addr, len)
254 os_vm_address_t addr;
259 os_vm_address_t end = addr + len;
261 for (n = n_segments, seg = segments; n > 0; n--, seg++)
262 if (seg->start >= end)
264 else if (seg->start + seg->length > addr)
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) */
274 mem_in_use(addr, len)
275 os_vm_address_t addr;
280 for (p = addr; addr < addr + len; p += os_real_page_size) {
283 if (mincore((caddr_t) p, os_real_page_size, &c) == 0 || errno != ENOMEM)
290 #define seg_last_p(seg) (((seg)-segments)>=n_segments-1)
299 for (n = seg - segments + 1; n < n_segments; n++) {
309 seg_try_merge_next(seg)
312 struct segment *nseg = seg + 1;
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 */
322 ";;; seg_try_merge: Merged 0x%08x[0x%08x] with 0x%08x[0x%08x]\n",
323 seg->start, seg->length, nseg->start, nseg->length);
326 if (((long) nseg->start & SPARSE_SIZE_MASK) != 0) {
328 * if not on a block boundary, we have to ensure both parts
329 * of a common block are in a known state
331 seg_force_resident(seg, nseg->start - 1, 1);
332 seg_force_resident(nseg, nseg->start, 1);
335 seg->length += nseg->length;
342 * Try to merge seg with adjacent segments.
345 seg_try_merge_adjacent(seg)
348 if (!seg_last_p(seg))
349 seg_try_merge_next(seg);
351 seg_try_merge_next(seg - 1);
354 static struct segment *
355 seg_create(addr, len, protection, mapped_fd, file_offset)
356 os_vm_address_t addr;
361 struct segment *seg =
363 seg_create_nomerge(addr, len, protection, mapped_fd, file_offset);
365 seg_try_merge_adjacent(seg);
370 * Change the attributes of the given range of an existing segment, and return
371 * a segment corresponding to the new bit.
373 static struct segment *
374 seg_change_range(seg, addr, len, protection, mapped_fd, file_offset)
376 os_vm_address_t addr;
381 os_vm_address_t end = addr + len;
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);
391 if (old_len < len + seg_offset) {
392 struct segment *next = seg + 1;
396 ";;; seg_change_range: region 0x%08x[0x%08x] overflows 0x%08x[0x%08x]\n",
397 addr, len, seg->start, old_len);
400 while (!seg_last_p(seg) && next->start + next->length <= end) {
403 ";;; seg_change_range: merging extra segment 0x%08x[0x%08x]\n",
404 next->start, next->length);
409 if (!seg_last_p(seg) && next->start < end) {
410 next->length -= end - next->start;
412 old_len = next->start - seg->start;
414 old_len = len + seg_offset;
418 ";;; seg_change_range: extended first seg to 0x%08x[0x%08x]\n",
419 seg->start, old_len);
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),
429 ADJ_OFFSET(seg->file_offset, seg_offset + len));
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));
438 if (seg_offset == 0) {
440 seg->protection = protection;
441 seg->mapped_fd = mapped_fd;
442 seg->file_offset = file_offset;
444 /* adjust first part of remaining old segment */
445 seg->length = seg_offset;
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);
453 /* add new middle segment for new protected region */
455 seg_create_nomerge(addr, len, protection, mapped_fd,
459 seg_try_merge_adjacent(seg);
467 /* ---------------------------------------------------------------- */
469 static os_vm_address_t
470 mapin(addr, len, protection, map_fd, offset, is_readable)
471 os_vm_address_t addr;
478 os_vm_address_t real;
479 boolean sparse = (len >= SPARSE_BLOCK_SIZE);
481 if (offset != OFFSET_NONE
482 && (offset < os_vm_page_size || (offset & (os_vm_page_size - 1)) != 0)) {
484 "mapin: file offset (%d) not multiple of pagesize (%d)\n",
485 offset, os_vm_page_size);
489 len += real_page_size_difference; /* futz around to get an aligned region */
492 real = (os_vm_address_t)
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));
500 if ((long) real == -1) {
501 perror("mapin: mmap");
507 * now play around with what the os gave us to make it align by
508 * our standards (which is why we overallocated)
510 os_vm_size_t overflow;
512 addr = os_round_up_to_page(real);
514 munmap((caddr_t) real, addr - real);
516 overflow = real_page_size_difference - (addr - real);
518 munmap((caddr_t) (addr + len - real_page_size_difference),
528 static os_vm_address_t
529 map_and_remember(addr, len, protection, map_fd, offset, is_readable)
530 os_vm_address_t addr;
537 os_vm_address_t real =
539 mapin(addr, len, protection, map_fd, offset, is_readable);
542 struct segment *seg = seg_find(real);
545 seg = seg_change_range(seg, real, len, protection, map_fd, offset);
547 seg = seg_create(real, len, protection, map_fd, offset);
550 munmap((caddr_t) real, len);
556 ";;; map_and_remember: 0x%08x[0x%08x] offset: %d, mapped to: %d\n",
557 real, len, offset, map_fd);
563 /* ---------------------------------------------------------------- */
566 os_validate(addr, len)
567 os_vm_address_t addr;
570 addr = os_trunc_to_page(addr);
571 len = os_round_up_size_to_page(len);
574 fprintf(stderr, ";;; os_validate: 0x%08x[0x%08x]\n", addr, len);
577 if (addr != NULL && collides_with_seg_p(addr, len))
580 return map_and_remember(addr, len, PROT_DEFAULT, zero_fd, OFFSET_NONE,
585 os_invalidate(addr, len)
586 os_vm_address_t addr;
589 struct segment *seg = seg_find(addr);
591 addr = os_trunc_to_page(addr);
592 len = os_round_up_size_to_page(len);
595 fprintf(stderr, ";;; os_invalidate: 0x%08x[0x%08x]\n", addr, len);
599 fprintf(stderr, "os_invalidate: Unknown segment: 0x%08x[0x%08x]\n",
602 seg = seg_change_range(seg, addr, len, 0, 0, OFFSET_NONE);
607 if (munmap((caddr_t) addr, len) != 0)
608 perror("os_invalidate: munmap");
613 os_map(fd, offset, addr, len)
616 os_vm_address_t addr;
619 addr = os_trunc_to_page(addr);
620 len = os_round_up_size_to_page(len);
623 fprintf(stderr, ";;; os_map: 0x%08x[0x%08x]\n", addr, len);
626 return map_and_remember(addr, len, PROT_DEFAULT, fd, offset, TRUE);
630 os_flush_icache(address, length)
631 os_vm_address_t address;
634 #if defined(MACH) && defined(mips)
635 vm_machine_attribute_val_t flush;
638 flush = MATTR_VAL_ICACHE_FLUSH;
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);
648 os_protect(addr, len, prot)
649 os_vm_address_t addr;
653 struct segment *seg = seg_find(addr);
655 addr = os_trunc_to_page(addr);
656 len = os_round_up_size_to_page(len);
659 fprintf(stderr, ";;; os_protect: 0x%08x[0x%08x]\n", addr, len);
663 int old_prot = seg->protection;
665 if (prot != old_prot) {
667 * oooooh, sick: we have to make sure all the pages being protected have
668 * faulted in, so they're in a known state...
670 seg_force_resident(seg, addr, len);
672 seg_change_range(seg, addr, len, prot, seg->mapped_fd,
675 if (mprotect((caddr_t) addr, (long) len, prot) != 0)
676 perror("os_unprotect: mprotect");
679 fprintf(stderr, "os_protect: Unknown segment: 0x%08x[0x%08x]\n", addr,
685 os_vm_address_t test;
687 return seg_find(test) != NULL;
690 /* ---------------------------------------------------------------- */
693 maybe_gc(HANDLER_ARGS)
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...
702 static already_trying = 0;
709 sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
711 already_trying = TRUE;
712 did_gc = interrupt_maybe_gc(signal, code, context);
713 already_trying = FALSE;
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
724 * Running into the gc trigger page will also end up here...
727 segv_handler(HANDLER_ARGS, caddr_t addr)
731 if (CODE(code) == OS_PROTERR) { /* allow writes to this chunk */
732 struct segment *seg = seg_find(addr);
734 if ((caddr_t) last_fault == addr) {
735 if (seg != NULL && maybe_gc(signal, code, context))
736 /* we just garbage collected */
739 /* a *real* protection fault */
741 "segv_handler: Real protection violation: 0x%08x\n",
743 interrupt_handle_now(signal, code, context);
746 last_fault = (os_vm_address_t) addr;
751 /* round down to a page */
752 os_vm_address_t block =
754 (os_vm_address_t) ((long) addr & ~SPARSE_SIZE_MASK);
755 os_vm_size_t length = SPARSE_BLOCK_SIZE;
757 if (block < seg->start) {
758 length -= (seg->start - block);
761 if (block + length > seg->start + seg->length)
762 length = seg->start + seg->length - block;
765 /* unmap it. probably redundant. */
766 if (munmap((caddr_t) block, length) == -1)
767 perror("segv_handler: munmap");
770 /* and remap it with more permissions */
772 mmap((caddr_t) block,
775 MAP_PRIVATE | MAP_FIXED,
777 seg->file_offset == OFFSET_NONE
778 ? 0 : seg->file_offset + (block - seg->start));
781 perror("segv_handler: mmap");
782 interrupt_handle_now(signal, code, context);
785 fprintf(stderr, "segv_handler: 0x%08x not in any segment\n", addr);
786 interrupt_handle_now(signal, code, context);
790 * note that we check for a gc-trigger hit even if it's not a PROT error
792 else if (!maybe_gc(signal, code, context)) {
793 static int nomap_count = 0;
795 if (CODE(code) == OS_MAPERR) {
796 if (nomap_count == 0) {
798 "segv_handler: No mapping fault: 0x%08x\n", addr);
802 * There should be higher-level protection against stack
803 * overflow somewhere, but at least this prevents infinite
804 * puking of error messages...
807 "segv_handler: Recursive no mapping fault (stack overflow?)\n");
810 } else if (OS_HASERRNO(code)) {
811 errno = OS_ERRNO(code);
812 perror("segv_handler: Object error");
815 interrupt_handle_now(signal, code, context);
817 if (CODE(code) == OS_MAPERR)
823 os_install_interrupt_handlers(void)
825 interrupt_install_low_level_handler(SIGSEGV, segv_handler);
828 os_vm_address_t round_up_sparse_size(os_vm_address_t addr)
830 return (addr + SPARSE_BLOCK_SIZE - 1) & ~SPARSE_SIZE_MASK;
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.
838 static os_vm_address_t spaces[] = {
839 READ_ONLY_SPACE_START, STATIC_SPACE_START,
840 BINDING_STACK_START, CONTROL_STACK_START
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.
850 static unsigned long space_size[] = {
851 READ_ONLY_SPACE_SIZE, STATIC_SPACE_SIZE,
852 BINDING_STACK_SIZE, CONTROL_STACK_SIZE
856 * The size of the hole to make. It should be strictly smaller than
860 #define HOLE_SIZE 0x2000
866 os_vm_address_t hole;
868 /* Make holes of the appropriate size for desired spaces */
870 for (k = 0; k < sizeof(spaces) / sizeof(spaces[0]); ++k) {
872 hole = spaces[k] + space_size[k];
874 if (os_validate(hole, HOLE_SIZE) == NULL) {
876 "ensure_space: Failed to validate hole of %ld bytes at 0x%08X\n",
877 HOLE_SIZE, (unsigned long) hole);
880 /* Make it inaccessible */
881 os_protect(hole, HOLE_SIZE, 0);
884 /* Round up the dynamic_space_size to the nearest SPARSE_BLOCK_SIZE */
885 dynamic_space_size = round_up_sparse_size(dynamic_space_size);
887 /* Now make a hole for the dynamic spaces */
888 hole = dynamic_space_size + (os_vm_address_t) dynamic_0_space;
890 if (os_validate(hole, HOLE_SIZE) == NULL) {
892 "ensure_space: Failed to validate hold of %ld bytes at 0x%08X\n",
893 HOLE_SIZE, (unsigned long) hole);
896 os_protect(hole, HOLE_SIZE, 0);
898 hole = dynamic_space_size + (os_vm_address_t) dynamic_1_space;
899 if (os_validate(hole, HOLE_SIZE) == NULL) {
901 "ensure_space: Failed to validate hole of %ld bytes at 0x%08X\n",
902 HOLE_SIZE, (unsigned long) hole);
905 os_protect(hole, HOLE_SIZE, 0);