#define _SYSTEM 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "glo.h" #include "vm.h" #include "proto.h" #include "util.h" #include "sanitycheck.h" #include "region.h" #include "memory.h" /*===========================================================================* * do_fork * *===========================================================================*/ int do_fork(message *msg) { int r, proc, childproc, fullvm; struct vmproc *vmp, *vmc; pt_t origpt; vir_bytes msgaddr; SANITYCHECK(SCL_FUNCTIONS); if(vm_isokendpt(msg->VMF_ENDPOINT, &proc) != OK) { printf("VM: bogus endpoint VM_FORK %d\n", msg->VMF_ENDPOINT); SANITYCHECK(SCL_FUNCTIONS); return EINVAL; } childproc = msg->VMF_SLOTNO; if(childproc < 0 || childproc >= NR_PROCS) { printf("VM: bogus slotno VM_FORK %d\n", msg->VMF_SLOTNO); SANITYCHECK(SCL_FUNCTIONS); return EINVAL; } vmp = &vmproc[proc]; /* parent */ vmc = &vmproc[childproc]; /* child */ assert(vmc->vm_slot == childproc); if(vmp->vm_flags & VMF_HAS_DMA) { printf("VM: %d has DMA memory and may not fork\n", msg->VMF_ENDPOINT); return EINVAL; } fullvm = vmp->vm_flags & VMF_HASPT; /* The child is basically a copy of the parent. */ origpt = vmc->vm_pt; *vmc = *vmp; vmc->vm_slot = childproc; region_init(&vmc->vm_regions_avl); vmc->vm_endpoint = NONE; /* In case someone tries to use it. */ vmc->vm_pt = origpt; vmc->vm_flags &= ~VMF_HASPT; #if VMSTATS vmc->vm_bytecopies = 0; #endif if(pt_new(&vmc->vm_pt) != OK) { printf("VM: fork: pt_new failed\n"); return ENOMEM; } vmc->vm_flags |= VMF_HASPT; if(fullvm) { SANITYCHECK(SCL_DETAIL); if(map_proc_copy(vmc, vmp) != OK) { printf("VM: fork: map_proc_copy failed\n"); pt_free(&vmc->vm_pt); return(ENOMEM); } if(vmp->vm_heap) { vmc->vm_heap = map_region_lookup_tag(vmc, VRT_HEAP); assert(vmc->vm_heap); } SANITYCHECK(SCL_DETAIL); } else { vir_bytes sp; struct vir_region *heap, *stack; vir_bytes text_bytes, data_bytes, stack_bytes, parent_gap_bytes, child_gap_bytes; vir_bytes text_addr, data_addr; int is_elf = 0; /* Get SP of new process (using parent). */ if(get_stack_ptr(vmp->vm_endpoint, &sp) != OK) { printf("VM: fork: get_stack_ptr failed for %d\n", vmp->vm_endpoint); return ENOMEM; } /* Update size of stack segment using current SP. */ if(adjust(vmp, vmp->vm_arch.vm_seg[D].mem_len, sp) != OK) { printf("VM: fork: adjust failed for %d\n", vmp->vm_endpoint); return ENOMEM; } /* Copy newly adjust()ed stack segment size to child. */ vmc->vm_arch.vm_seg[S] = vmp->vm_arch.vm_seg[S]; text_addr = CLICK2ABS(vmc->vm_arch.vm_seg[T].mem_vir); text_bytes = CLICK2ABS(vmc->vm_arch.vm_seg[T].mem_len); data_addr = CLICK2ABS(vmc->vm_arch.vm_seg[D].mem_vir); data_bytes = CLICK2ABS(vmc->vm_arch.vm_seg[D].mem_len); stack_bytes = CLICK2ABS(vmc->vm_arch.vm_seg[S].mem_len); /* how much space after break and before lower end (which is the * logical top) of stack for the parent */ parent_gap_bytes = CLICK2ABS(vmc->vm_arch.vm_seg[S].mem_vir - vmc->vm_arch.vm_seg[D].mem_len - vmc->vm_arch.vm_seg[D].mem_vir); /* how much space can the child stack grow downwards, below * the current SP? The rest of the gap is available for the * heap to grow upwards. */ child_gap_bytes = VM_PAGE_SIZE; #if defined(__ELF__) is_elf = 1; #endif if((r=proc_new(vmc, VM_PROCSTART, text_addr, text_bytes, data_addr, data_bytes, stack_bytes, child_gap_bytes, 0, 0, CLICK2ABS(vmc->vm_arch.vm_seg[S].mem_vir + vmc->vm_arch.vm_seg[S].mem_len), 1, is_elf, 0)) != OK) { printf("VM: fork: proc_new failed\n"); return r; } if(!(heap = map_region_lookup_tag(vmc, VRT_HEAP))) panic("couldn't lookup heap"); assert(heap->phys); if(!(stack = map_region_lookup_tag(vmc, VRT_STACK))) panic("couldn't lookup stack"); assert(stack->phys); /* Now copy the memory regions. */ if(vmc->vm_arch.vm_seg[T].mem_len > 0) { struct vir_region *text; if(!(text = map_region_lookup_tag(vmc, VRT_TEXT))) panic("couldn't lookup text"); assert(text->phys); if(copy_abs2region(CLICK2ABS(vmp->vm_arch.vm_seg[T].mem_phys), text, 0, text_bytes) != OK) panic("couldn't copy text"); } if(copy_abs2region(CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys), heap, 0, data_bytes) != OK) panic("couldn't copy heap"); if(copy_abs2region(CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys + vmc->vm_arch.vm_seg[D].mem_len) + parent_gap_bytes, stack, child_gap_bytes, stack_bytes) != OK) panic("couldn't copy stack"); } /* Only inherit these flags. */ vmc->vm_flags &= (VMF_INUSE|VMF_SEPARATE|VMF_HASPT); /* inherit the priv call bitmaps */ memcpy(&vmc->vm_call_mask, &vmp->vm_call_mask, sizeof(vmc->vm_call_mask)); /* Tell kernel about the (now successful) FORK. */ if((r=sys_fork(vmp->vm_endpoint, childproc, &vmc->vm_endpoint, vmc->vm_arch.vm_seg, PFF_VMINHIBIT, &msgaddr)) != OK) { panic("do_fork can't sys_fork: %d", r); } if((r=pt_bind(&vmc->vm_pt, vmc)) != OK) panic("fork can't pt_bind: %d", r); if(fullvm) { vir_bytes vir; /* making these messages writable is an optimisation * and its return value needn't be checked. */ vir = arch_vir2map(vmc, msgaddr); if (handle_memory(vmc, vir, sizeof(message), 1) != OK) panic("do_fork: handle_memory for child failed\n"); vir = arch_vir2map(vmp, msgaddr); if (handle_memory(vmp, vir, sizeof(message), 1) != OK) panic("do_fork: handle_memory for parent failed\n"); } /* Inform caller of new child endpoint. */ msg->VMF_CHILD_ENDPOINT = vmc->vm_endpoint; SANITYCHECK(SCL_FUNCTIONS); return OK; }