/* * * OS-dependent routines. This file (along with os.h) exports an * OS-independent interface to the operating system VM facilities. * Suprisingly, this interface looks a lot like the Mach interface * (but simpler in some places). For some operating systems, a subset * of these functions will have to be emulated. * */ /* Assumptions these routines are based on: os_validate: Not called with NULL for an addr. os_invalidate: Never called. os_map: Files are only mapped at the beginning of one of the areas passed to os_validate. os_protect: Only called on an entire region when giving permissions and only called from some point in a segment to the end of the segment when removing them. Only called with all protections or no protections. os_zero: Only ever zeroed from some point in a segment to the end of the segment. os_allocate_at: Calls to here are disjoint from those around it (the others in os-common.c) since it calls os_validate and the others (in os-common.c) use malloc, etc. Note that os_validate does not actually allocate memory until it has to map the particular section in. */ /* #define DEBUG */ #include #include #include #include #include #include "os.h" #include #include "interrupt.h" #include #include #include os_vm_size_t os_vm_page_size = (-1); #define MAX_SEGMENTS 20 #define ALLOC_SIZE 0x10000 static struct segment { os_vm_address_t base; os_vm_size_t len; os_vm_address_t valid; os_vm_address_t protected; } segments[MAX_SEGMENTS]; void os_init0(const char *argv[], const char *envp[]) {} void os_init(const char *argv[], const char *envp[]) { int i; os_vm_page_size = sysconf(_SC_PAGE_SIZE); for (i = 0; i < MAX_SEGMENTS; i++) segments[i].len = 0; } os_vm_address_t os_validate(os_vm_address_t addr, os_vm_size_t len) { int fd, i; caddr_t ret; addr = os_trunc_to_page(addr); len = os_round_up_size_to_page(len); #ifdef DEBUG printf("os_validate: addr: 0x%X, len: 0x%X, end: 0x%X\n", addr, len, addr + len); #endif assert(addr != NULL); assert(len != 0); for (i = 0; i < MAX_SEGMENTS; i++) if (segments[i].len == 0) break; assert(i != MAX_SEGMENTS); segments[i].base = addr; segments[i].len = len; segments[i].valid = addr; segments[i].protected = addr + len; return addr; } void os_invalidate(os_vm_address_t addr, os_vm_size_t len) { assert(FALSE); } os_vm_address_t os_map(int fd, int offset, os_vm_address_t addr, os_vm_size_t len) { int i; addr = os_trunc_to_page(addr); len = os_round_up_size_to_page(len); if (mmap(addr, len, OS_VM_PROT_ALL, MAP_FILE | MAP_FIXED | MAP_PRIVATE, fd, (off_t) offset) == (os_vm_address_t) - 1) { perror("mmap"); return NULL; } for (i = 0; i < MAX_SEGMENTS; i++) if (segments[i].len != 0 && segments[i].base == addr) break; assert(i != MAX_SEGMENTS); assert(segments[i].valid == addr); segments[i].valid = addr + len; #ifdef DEBUG printf("os_map: addr: 0x%X, len: 0x%X, end: 0x%X\n", addr, len, addr + len); #endif return addr; } void os_flush_icache(os_vm_address_t address, os_vm_size_t length) { sanctify_for_execution(address, length); } void os_protect(os_vm_address_t addr, os_vm_size_t len, os_vm_prot_t prot) { int i; addr = os_trunc_to_page(addr); len = os_round_up_size_to_page(len); for (i = 0; i < MAX_SEGMENTS; i++) if (segments[i].base <= addr && addr < segments[i].base + segments[i].len) break; assert(i != MAX_SEGMENTS); if (prot) { assert(segments[i].base == addr && segments[i].len == len); segments[i].protected = addr + len; } else { assert(segments[i].protected == addr + len); segments[i].protected = addr; } if (addr < segments[i].valid) if (mprotect(addr, segments[i].valid - addr, prot) == -1) { perror("mprotect"); printf("segments[i].base: 0x%X\n", segments[i].base); printf("segments[i].len: 0x%X\n", segments[i].len); printf("segments[i].valid: 0x%X\n", segments[i].valid); printf("addr: 0x%X, segments[i].valid-addr: 0x%X\n", addr, segments[i].valid - addr); printf("prot: 0x%X, len 0x%X\n", prot, len); assert(FALSE); } #ifdef DEBUG printf("os_protect: addr: 0x%X, len: 0x%X, end: 0x%X, prot 0x%X\n", addr, len, addr + len, prot); #endif } boolean valid_addr(os_vm_address_t addr) { int i; for (i = 0; i < MAX_SEGMENTS; i++) if (segments[i].base <= addr && addr < segments[i].base + segments[i].len) return TRUE; return FALSE; } void segv_handler(int signal, int code, struct sigcontext *context) { int i; os_vm_address_t addr, nvalid; sigsetmask(BLOCKABLE); addr = (os_vm_address_t) context->sc_sl.sl_ss.ss_cr21; /* verify this!!! */ for (i = 0; i < MAX_SEGMENTS; i++) if (segments[i].len != 0 && segments[i].base <= addr && addr < segments[i].base + segments[i].len) break; if (i == MAX_SEGMENTS || addr < segments[i].valid) interrupt_handle_now(signal, code, context); else if (segments[i].protected <= addr) { if (!interrupt_maybe_gc(signal, code, context)) interrupt_handle_now(signal, code, context); } else { nvalid = ((os_vm_address_t) (((long) (addr + ALLOC_SIZE)) & ~((long) (ALLOC_SIZE - 1)))); if (nvalid > segments[i].protected) nvalid = segments[i].protected; #ifdef DEBUG printf("Mapping: addr: 0x%08x, old: 0x%08x, new: 0x%08x\n", addr, segments[i].valid, nvalid); #endif if (mmap(segments[i].valid, nvalid - segments[i].valid, OS_VM_PROT_ALL, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, -1, 0) == (os_vm_address_t) - 1) { perror("mmap"); printf("segments[i].base: 0x%X\n", segments[i].base); printf("segments[i].len: 0x%X\n", segments[i].len); printf("segments[i].valid: 0x%X\n", segments[i].valid); printf("segments[i].protected: 0x%X\n", segments[i].protected); printf("nvalid: 0x%X\n", nvalid); printf("nvalid-segments[i].valid: 0x%X\n", nvalid - segments[i].valid); assert(FALSE); } segments[i].valid = nvalid; } } void sigbus_handler(int signal, int code, struct sigcontext *context) { #ifdef DEBUG printf("Bus Error at 0x%X\n", context->sc_sl.sl_ss.ss_cr21); #endif if (!interrupt_maybe_gc(signal, code, context)) interrupt_handle_now(signal, code, context); } void os_install_interrupt_handlers(void) { interrupt_install_low_level_handler(SIGSEGV, segv_handler); interrupt_install_low_level_handler(SIGBUS, sigbus_handler); } void os_zero(os_vm_address_t addr, os_vm_size_t length) { os_vm_address_t block_start; os_vm_size_t block_size; int i; #ifdef PRINTNOISE fprintf(stderr, "os_zero: addr: 0x%08x, len: 0x%08x, end: 0x%X\n", addr, length, addr + length); #endif block_start = os_round_up_to_page(addr); length -= block_start - addr; block_size = os_trunc_size_to_page(length); if (block_start > addr) memset((char *) addr, 0, block_start - addr); if (block_size < length) assert(FALSE); if (block_size != 0) { /* Now deallocate and allocate the block so that it */ /* faults in zero-filled. */ for (i = 0; i < MAX_SEGMENTS; i++) if (segments[i].base <= block_start && block_start < segments[i].base + segments[i].len) break; assert(i != MAX_SEGMENTS); assert(block_start + block_size == segments[i].base + segments[i].len); if (segments[i].valid > block_start) { if (munmap(block_start, segments[i].valid - block_start) == -1) { perror("munmap"); return; } segments[i].valid = block_start; } } } os_vm_address_t os_allocate(os_vm_size_t len) { return (os_vm_address_t) malloc(len); } os_vm_address_t os_allocate_at(os_vm_address_t addr, os_vm_size_t len) { return os_validate(addr, len); } void os_deallocate(os_vm_address_t addr, os_vm_size_t len) { free(addr); } os_vm_address_t os_reallocate(os_vm_address_t addr, os_vm_size_t old_len, os_vm_size_t len) { addr = (os_vm_address_t) realloc(addr, len); assert(addr != NULL); return addr; } int getrusage(int who, struct rusage *rusage) { static long ticks_per_sec = 0; static long usec_per_tick = 0; struct tms buf; clock_t uticks, sticks; memset(rusage, 0, sizeof(struct rusage)); if (ticks_per_sec == 0) { ticks_per_sec = sysconf(_SC_CLK_TCK); usec_per_tick = 1000000 / ticks_per_sec; } if (times(&buf) == -1) return -1; if (who == RUSAGE_SELF) { uticks = buf.tms_utime; sticks = buf.tms_stime; } else if (who == RUSAGE_CHILDREN) { uticks = buf.tms_utime; sticks = buf.tms_stime; } else { errno = EINVAL; return -1; } rusage->ru_utime.tv_sec = uticks / ticks_per_sec; rusage->ru_utime.tv_usec = (uticks % ticks_per_sec) * usec_per_tick; rusage->ru_stime.tv_sec = sticks / ticks_per_sec; rusage->ru_stime.tv_usec = (sticks % ticks_per_sec) * usec_per_tick; return 0; } int getdtablesize(void) { struct rlimit rlp; assert(getrlimit(RLIMIT_NOFILE, &rlp) == 0); return rlp.rlim_cur; } unsigned long gethostid(void) { char hostname[256]; struct hostent *hostent; static unsigned long addr = NULL; if (addr) return addr; if (gethostname(hostname, sizeof(hostname)) == -1) { perror("gethostname"); return 0; } hostent = gethostbyname(hostname); if (hostent == NULL) { perror("gethostbyname"); return 0; } addr = ((unsigned char *) (hostent->h_addr))[0] << 24 | ((unsigned char *) (hostent->h_addr))[1] << 16 | ((unsigned char *) (hostent->h_addr))[2] << 8 | ((unsigned char *) (hostent->h_addr))[3]; return addr; } int getpagesize(void) { return os_vm_page_size; }