Back to index

plt-scheme  4.2.1
vm_osx.c
Go to the documentation of this file.
00001 /* 
00002    Provides:
00003       Mach-based allocator (uses alloc_cache.c)
00004       macosx_init_exception_handler() --- installs fault handler
00005       size_type -- the type of the heap size
00006       determine_max_heap_size()
00007    Requires:
00008       TEST = 0
00009       GENERATIONS --- zero or non-zero
00010       designate_modified --- when GENERATIONS is non-zero
00011    Optional:
00012       DONT_NEED_MAX_HEAP_SIZE --- to disable a provide
00013 */
00014 
00015 #include <sys/time.h>
00016 #include <sys/resource.h>
00017 #include <unistd.h>
00018 #include <mach/mach.h>
00019 #include <mach/mach_error.h>
00020 #if defined(__POWERPC__) && 0
00021 # define PPC_HAND_ROLLED_THREAD
00022 #endif
00023 #ifdef PPC_HAND_ROLLED_THREAD
00024 # include <architecture/ppc/cframe.h>
00025 #else
00026 # include <pthread.h>
00027 #endif
00028 
00029 # if GENERATIONS
00030 static int designate_modified(void *p);
00031 # endif
00032 # define TEST 0
00033 #ifndef TEST
00034 # define TEST 1
00035 int designate_modified(void *p);
00036 #endif
00037 
00038 #ifdef __POWERPC__
00039 # define ARCH_thread_state_t ppc_thread_state_t
00040 # define ARCH_THREAD_STATE PPC_THREAD_STATE
00041 # define ARCH_THREAD_STATE_COUNT PPC_THREAD_STATE_COUNT
00042 #else
00043 # define ARCH_thread_state_t i386_thread_state_t
00044 # define ARCH_THREAD_STATE i386_THREAD_STATE
00045 # define ARCH_THREAD_STATE_COUNT i386_THREAD_STATE_COUNT
00046 #endif
00047 
00048 /* the structure of an exception msg and its reply */
00049 typedef struct rep_msg {
00050   mach_msg_header_t head;
00051   NDR_record_t NDR;
00052   kern_return_t ret_code;
00053 } mach_reply_msg_t;
00054 
00055 typedef struct exc_msg {
00056   mach_msg_header_t head;
00057   /* start of the kernel processed data */
00058   mach_msg_body_t msgh_body;
00059   mach_msg_port_descriptor_t thread;
00060   mach_msg_port_descriptor_t task;
00061   /* end of the kernel processed data */
00062   NDR_record_t NDR;
00063   exception_type_t exception;
00064   mach_msg_type_number_t code_cnt;
00065   exception_data_t code;
00066   /* some padding */
00067   char pad[512];
00068 } mach_exc_msg_t;
00069 
00070 /* this is a neat little mach callback */
00071 extern boolean_t exc_server(mach_msg_header_t *in, mach_msg_header_t *out);
00072 
00073 /* these are the globals everyone needs */
00074 #define page_size vm_page_size
00075 static mach_port_t task_self = 0;
00076 static mach_port_t exc_port = 0;
00077 
00078 /* the VM subsystem as defined by the GC files */
00079 static void *os_vm_alloc_pages(size_t len)
00080 {
00081   kern_return_t retval;
00082   void *r;
00083 
00084   if(!task_self) task_self = mach_task_self();
00085 
00086   /* round up to the nearest page: */
00087   if(len & (page_size - 1))
00088     len += page_size - (len & (page_size - 1));
00089 
00090   retval = vm_allocate(task_self, (vm_address_t*)&r, len, TRUE);
00091   if(retval != KERN_SUCCESS) {
00092     GCPRINT(GCOUTF, "Couldn't allocate memory: %s\n", mach_error_string(retval));
00093     abort();
00094   }
00095 
00096   return r;
00097 }
00098 
00099 static void os_vm_free_pages(void *p, size_t len)
00100 {
00101   kern_return_t retval;
00102 
00103   retval = vm_deallocate(task_self, (vm_address_t)p, len);
00104   if(retval != KERN_SUCCESS) {
00105     GCPRINT(GCOUTF, "WARNING: couldn't deallocate page %p: %s\n", p,
00106           mach_error_string(retval));
00107   }
00108 }
00109 
00110 static void vm_protect_pages(void *p, size_t len, int writeable)
00111 {
00112   kern_return_t retval;
00113 
00114   if(len & (page_size - 1)) {
00115     len += page_size - (len & (page_size - 1));
00116   }
00117 
00118   retval = vm_protect(task_self, (vm_address_t)p, len, FALSE,
00119                     writeable ? VM_PROT_ALL 
00120                     : (VM_PROT_READ | VM_PROT_EXECUTE));
00121   if(retval != KERN_SUCCESS) {
00122     GCPRINT(GCOUTF, "WARNING: couldn't protect %li bytes of page %p%s\n",
00123           len, p, mach_error_string(retval));
00124   }
00125 }
00126 
00127 #include "alloc_cache.c"
00128 
00129 #ifndef DONT_NEED_MAX_HEAP_SIZE
00130 
00131 static unsigned long determine_max_heap_size()
00132 {
00133   struct rlimit rlim;
00134 
00135   getrlimit(RLIMIT_RSS, &rlim);
00136   return (rlim.rlim_cur == RLIM_INFINITY) ? (unsigned long)-1 : rlim.rlim_cur;
00137 }
00138 #endif
00139 
00140 /* The catch_exception_raise() functions are treated specially by the
00141    linker, and Mach looks them up at run time. We provide
00142    GC_... variants due to linker confusion when the implementaiton of
00143    these are in a framework instead of the main binary, so that the
00144    main binary needs to define them and jump to the implemenations
00145    here. (This linker problem seems to occur when we use
00146    -mmacosx-version-min.) */
00147 
00148 kern_return_t GC_catch_exception_raise_state(mach_port_t port,
00149                                              exception_type_t exception_type,
00150                                              exception_data_t exception_data,
00151                                              mach_msg_type_number_t data_cnt,
00152                                              thread_state_flavor_t *flavor,
00153                                              thread_state_t in_state,
00154                                              mach_msg_type_number_t is_cnt,
00155                                              thread_state_t out_state,
00156                                              mach_msg_type_number_t os_cnt)
00157 {
00158   return KERN_FAILURE;
00159 }
00160 
00161 kern_return_t catch_exception_raise_state(mach_port_t port,
00162                                      exception_type_t exception_type,
00163                                      exception_data_t exception_data,
00164                                      mach_msg_type_number_t data_cnt,
00165                                      thread_state_flavor_t *flavor,
00166                                      thread_state_t in_state,
00167                                      mach_msg_type_number_t is_cnt,
00168                                      thread_state_t out_state,
00169                                      mach_msg_type_number_t os_cnt)
00170 {
00171   return GC_catch_exception_raise_state(port, exception_type, exception_data,
00172                                         data_cnt, flavor,
00173                                         in_state, is_cnt,
00174                                         out_state, os_cnt);
00175 }
00176 
00177 kern_return_t GC_catch_exception_raise_state_identitity
00178   (mach_port_t port,  mach_port_t thread_port, mach_port_t task_port,
00179    exception_type_t exception_type, exception_data_t exception_data,
00180    mach_msg_type_number_t data_count, thread_state_flavor_t *state_flavor,
00181    thread_state_t in_state, mach_msg_type_number_t in_state_count,
00182    thread_state_t out_state, mach_msg_type_number_t out_state_count)
00183 {
00184   return KERN_FAILURE;
00185 }
00186 
00187 kern_return_t catch_exception_raise_state_identitity
00188   (mach_port_t port,  mach_port_t thread_port, mach_port_t task_port,
00189    exception_type_t exception_type, exception_data_t exception_data,
00190    mach_msg_type_number_t data_count, thread_state_flavor_t *state_flavor,
00191    thread_state_t in_state, mach_msg_type_number_t in_state_count,
00192    thread_state_t out_state, mach_msg_type_number_t out_state_count)
00193 {
00194   return GC_catch_exception_raise_state_identitity(port, thread_port, task_port,
00195                                                    exception_type, exception_data,
00196                                                    data_count, state_flavor,
00197                                                    in_state, in_state_count,
00198                                                    out_state, out_state_count);
00199 }
00200 
00201 kern_return_t GC_catch_exception_raise(mach_port_t port,
00202                                        mach_port_t thread_port,
00203                                        mach_port_t task_port,
00204                                        exception_type_t exception_type,
00205                                        exception_data_t exception_data,
00206                                        mach_msg_type_number_t data_count)
00207 {
00208 #if GENERATIONS
00209   /* kernel return value is in exception_data[0], faulting address in
00210      exception_data[1] */
00211   if(exception_data[0] == KERN_PROTECTION_FAILURE) {
00212     if (designate_modified((void*)exception_data[1]))
00213       return KERN_SUCCESS;
00214     else
00215       return KERN_FAILURE;
00216   } else 
00217 #endif
00218     return KERN_FAILURE;
00219 }
00220 
00221 kern_return_t catch_exception_raise(mach_port_t port,
00222                                 mach_port_t thread_port,
00223                                 mach_port_t task_port,
00224                                 exception_type_t exception_type,
00225                                 exception_data_t exception_data,
00226                                 mach_msg_type_number_t data_count)
00227 {
00228   return GC_catch_exception_raise(port, thread_port, task_port,
00229                                   exception_type, exception_data, data_count);
00230 }
00231 
00232 /* this is the thread which forwards of exceptions read from the exception
00233    server off to our exception catchers and then back out to the other
00234    thread */
00235 void exception_thread(void)
00236 {
00237   mach_msg_header_t *message;
00238   mach_msg_header_t *reply;
00239   kern_return_t retval;
00240   
00241   /* allocate the space for the message and reply */
00242   message = (mach_msg_header_t*)malloc(sizeof(mach_exc_msg_t));
00243   reply = (mach_msg_header_t*)malloc(sizeof(mach_reply_msg_t));
00244   /* do this loop forever */
00245   while(1) {
00246     /* block until we get an exception message */
00247     retval = mach_msg(message, MACH_RCV_MSG, 0, sizeof(mach_exc_msg_t), 
00248                     exc_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
00249     /* forward off the handling of this message */
00250     if(!exc_server(message, reply)) {
00251       GCPRINT(GCOUTF, "INTERNAL ERROR: exc_server() didn't like something\n");
00252       abort();
00253     }
00254     /* send the message back out to the thread */
00255     retval = mach_msg(reply, MACH_SEND_MSG, sizeof(mach_reply_msg_t), 0, 
00256                     MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
00257   }
00258 }
00259 
00260 /* this initializes the subsystem (sets the exception port, starts the
00261    exception handling thread, etc) */
00262 static void macosx_init_exception_handler() 
00263 {
00264   mach_port_t thread_self, exc_port_s;
00265   mach_msg_type_name_t type;
00266   kern_return_t retval;
00267 
00268   /* get ids for ourself */
00269   if(!task_self) task_self = mach_task_self();
00270   thread_self = mach_thread_self();
00271 
00272   /* allocate the port we're going to get exceptions on */
00273   retval = mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE, &exc_port);
00274   if(retval != KERN_SUCCESS) {
00275     GCPRINT(GCOUTF, "Couldn't allocate exception port: %s\n", 
00276           mach_error_string(retval));
00277     abort();
00278   }
00279 
00280   /* extract out the send rights for that port, which the OS needs */
00281   retval = mach_port_extract_right(task_self, exc_port, MACH_MSG_TYPE_MAKE_SEND,
00282                                &exc_port_s, &type);
00283   if(retval != KERN_SUCCESS) {
00284     GCPRINT(GCOUTF, "Couldn't extract send rights: %s\n", mach_error_string(retval));
00285     abort();
00286   }
00287 
00288   /* set the exception ports for this thread to the above */
00289   retval = thread_set_exception_ports(thread_self, EXC_MASK_BAD_ACCESS, 
00290                                   exc_port_s, EXCEPTION_DEFAULT, 
00291                                   ARCH_THREAD_STATE);
00292   if(retval != KERN_SUCCESS) {
00293     GCPRINT(GCOUTF, "Couldn't set exception ports: %s\n", mach_error_string(retval));
00294     abort();
00295   }
00296 
00297 #ifdef PPC_HAND_ROLLED_THREAD 
00298   /* Old hand-rolled thread creation. pthread_create is fine for our
00299      purposes. */
00300  {
00301    /* set up the subthread */
00302    mach_port_t exc_thread;
00303    ARCH_thread_state_t *exc_thread_state;
00304    void *subthread_stack;
00305 
00306    retval = thread_create(task_self, &exc_thread);
00307    if(retval != KERN_SUCCESS) {
00308      GCPRINT(GCOUTF, "Couldn't create exception thread: %s\n", mach_error_string(retval));
00309      abort();
00310    }
00311    subthread_stack = (void*)malloc(page_size);
00312    subthread_stack += (page_size - C_ARGSAVE_LEN - C_RED_ZONE);
00313    exc_thread_state = (ARCH_thread_state_t*)malloc(sizeof(ARCH_thread_state_t));
00314    exc_thread_state->srr0 = (unsigned int)exception_thread;
00315    exc_thread_state->r1 = (unsigned int)subthread_stack;
00316    retval = thread_set_state(exc_thread, ARCH_THREAD_STATE,
00317                           (thread_state_t)exc_thread_state,
00318                           ARCH_THREAD_STATE_COUNT);
00319    if(retval != KERN_SUCCESS) {
00320      GCPRINT(GCOUTF, "Couldn't set subthread state: %s\n", mach_error_string(retval));
00321      abort();
00322    }
00323    retval = thread_resume(exc_thread);
00324    if(retval != KERN_SUCCESS) {
00325      GCPRINT(GCOUTF, "Couldn't resume subthread: %s\n", mach_error_string(retval));
00326      abort();
00327    }
00328  }
00329 #else
00330  {
00331    pthread_t th;
00332    pthread_create(&th, NULL, (void *(*)(void *))exception_thread, NULL);
00333  }
00334 #endif
00335 }
00336 
00337 #if TEST
00338 #define MPAGE_SIZE 16384
00339 #define BPAGE_SIZE 20034
00340 
00341 char *normal_page = NULL;
00342 char *big_page = NULL;
00343 
00344 int designate_modified(void *p)
00345 {
00346   if((p >= normal_page) && (p < (normal_page + MPAGE_SIZE))) {
00347     vm_protect_pages(p, MPAGE_SIZE, 1);
00348     return 1;
00349   }
00350   if((p >= big_page) && (p < (big_page + BPAGE_SIZE))) {
00351     vm_protect_pages(p, BPAGE_SIZE, 1);
00352     return 1;
00353   }
00354   printf("Unrecognized write: %p\n", p);
00355   return 0;
00356 }
00357 
00358 int main(int argc, char **argv)
00359 {
00360   macosx_init_exception_handler();
00361   printf("Allocating test pages:\n");
00362   normal_page = vm_malloc_pages(MPAGE_SIZE, MPAGE_SIZE,0);
00363   printf("  ... normal page at %p\n", normal_page);
00364   big_page = vm_malloc_pages(BPAGE_SIZE, MPAGE_SIZE,0);
00365   printf("  ... big page at %p\n", big_page);
00366   printf("Setting protection on test pages\n");
00367   vm_protect_pages(normal_page, MPAGE_SIZE, 0);
00368   printf("  ... normal page %p set\n", normal_page);
00369   vm_protect_pages(big_page, MPAGE_SIZE, 0);
00370   printf("  ... big page %p set\n", big_page);
00371   printf("Writing to test pages\n");
00372   normal_page[2] = 'A';
00373   big_page[2] = 'A';
00374   printf("Reading from test pages:\n");
00375   printf("  ... normal_page %p's second byte is %c\n", normal_page, normal_page[2]);
00376   printf("  ... big_page %p's second byte is %c\n", big_page, big_page[2]);
00377   printf("Freeing test pages:\n");
00378   vm_free_pages(normal_page, MPAGE_SIZE);
00379   printf("  ... freed normal page\n");
00380   vm_free_pages(big_page, MPAGE_SIZE);
00381   printf("  ... freed big page\n");
00382 }
00383 #endif