kern_return_t projected_buffer_allocate( vm_map_t map, vm_size_t size, int persistence, vm_offset_t *kernel_p, vm_offset_t *user_p, vm_prot_t protection, vm_inherit_t inheritance) /*Currently only VM_INHERIT_NONE supported*/ { vm_object_t object; vm_map_entry_t u_entry, k_entry; vm_offset_t addr; vm_size_t r_size; kern_return_t kr; if (map == VM_MAP_NULL || map == kernel_map) return(KERN_INVALID_ARGUMENT); /* * Allocate a new object. */ size = round_page(size); object = vm_object_allocate(size); vm_map_lock(kernel_map); kr = vm_map_find_entry(kernel_map, &addr, size, (vm_offset_t) 0, VM_OBJECT_NULL, &k_entry); if (kr != KERN_SUCCESS) { vm_map_unlock(kernel_map); vm_object_deallocate(object); return kr; } k_entry->object.vm_object = object; if (!persistence) k_entry->projected_on = (vm_map_entry_t) -1; /*Mark entry so as to automatically deallocate it when last corresponding user entry is deallocated*/ vm_map_unlock(kernel_map); *kernel_p = addr; vm_map_lock(map); kr = vm_map_find_entry(map, &addr, size, (vm_offset_t) 0, VM_OBJECT_NULL, &u_entry); if (kr != KERN_SUCCESS) { vm_map_unlock(map); vm_map_lock(kernel_map); vm_map_entry_delete(kernel_map, k_entry); vm_map_unlock(kernel_map); vm_object_deallocate(object); return kr; } u_entry->object.vm_object = object; vm_object_reference(object); u_entry->projected_on = k_entry; /*Creates coupling with kernel mapping of the buffer, and also guarantees that user cannot directly manipulate buffer VM entry*/ u_entry->protection = protection; u_entry->max_protection = protection; u_entry->inheritance = inheritance; vm_map_unlock(map); *user_p = addr; /* * Allocate wired-down memory in the object, * and enter it in the kernel pmap. */ kmem_alloc_pages(object, 0, *kernel_p, *kernel_p + size, VM_PROT_READ | VM_PROT_WRITE); memset((void*) *kernel_p, 0, size); /*Zero fill*/ /* Set up physical mappings for user pmap */ pmap_pageable(map->pmap, *user_p, *user_p + size, FALSE); for (r_size = 0; r_size < size; r_size += PAGE_SIZE) { addr = pmap_extract(kernel_pmap, *kernel_p + r_size); pmap_enter(map->pmap, *user_p + r_size, addr, protection, TRUE); } return(KERN_SUCCESS); }
kern_return_t kmem_alloc_aligned( vm_map_t map, vm_offset_t *addrp, vm_size_t size) { vm_map_entry_t entry; vm_offset_t offset; vm_offset_t addr; unsigned int attempts; kern_return_t kr; if ((size & (size - 1)) != 0) panic("kmem_alloc_aligned"); /* * Use the kernel object for wired-down kernel pages. * Assume that no region of the kernel object is * referenced more than once. We want vm_map_find_entry * to extend an existing entry if possible. */ size = round_page(size); attempts = 0; retry: vm_map_lock(map); kr = vm_map_find_entry(map, &addr, size, size - 1, kernel_object, &entry); if (kr != KERN_SUCCESS) { vm_map_unlock(map); if (attempts == 0) { attempts++; slab_collect(); goto retry; } printf_once("no more rooom for kmem_alloc_aligned in %p\n", map); return kr; } /* * Since we didn't know where the new region would * start, we couldn't supply the correct offset into * the kernel object. We only initialize the entry * if we aren't extending an existing entry. */ offset = addr - VM_MIN_KERNEL_ADDRESS; if (entry->object.vm_object == VM_OBJECT_NULL) { vm_object_reference(kernel_object); entry->object.vm_object = kernel_object; entry->offset = offset; } /* * Since we have not given out this address yet, * it is safe to unlock the map. */ vm_map_unlock(map); /* * Allocate wired-down memory in the kernel_object, * for this entry, and enter it in the kernel pmap. */ kmem_alloc_pages(kernel_object, offset, addr, addr + size, VM_PROT_DEFAULT); /* * Return the memory, not zeroed. */ *addrp = addr; return KERN_SUCCESS; }
/* * kmem_realloc: * * Reallocate wired-down memory in the kernel's address map * or a submap. Newly allocated pages are not zeroed. * This can only be used on regions allocated with kmem_alloc. * * If successful, the pages in the old region are mapped twice. * The old region is unchanged. Use kmem_free to get rid of it. */ kern_return_t kmem_realloc( vm_map_t map, vm_offset_t oldaddr, vm_size_t oldsize, vm_offset_t *newaddrp, vm_size_t newsize) { vm_offset_t oldmin, oldmax; vm_offset_t newaddr; vm_object_t object; vm_map_entry_t oldentry, newentry; unsigned int attempts; kern_return_t kr; oldmin = trunc_page(oldaddr); oldmax = round_page(oldaddr + oldsize); oldsize = oldmax - oldmin; newsize = round_page(newsize); /* * Find space for the new region. */ attempts = 0; retry: vm_map_lock(map); kr = vm_map_find_entry(map, &newaddr, newsize, (vm_offset_t) 0, VM_OBJECT_NULL, &newentry); if (kr != KERN_SUCCESS) { vm_map_unlock(map); if (attempts == 0) { attempts++; slab_collect(); goto retry; } printf_once("no more room for kmem_realloc in %p\n", map); return kr; } /* * Find the VM object backing the old region. */ if (!vm_map_lookup_entry(map, oldmin, &oldentry)) panic("kmem_realloc"); object = oldentry->object.vm_object; /* * Increase the size of the object and * fill in the new region. */ vm_object_reference(object); vm_object_lock(object); if (object->size != oldsize) panic("kmem_realloc"); object->size = newsize; vm_object_unlock(object); newentry->object.vm_object = object; newentry->offset = 0; /* * Since we have not given out this address yet, * it is safe to unlock the map. We are trusting * that nobody will play with either region. */ vm_map_unlock(map); /* * Remap the pages in the old region and * allocate more pages for the new region. */ kmem_remap_pages(object, 0, newaddr, newaddr + oldsize, VM_PROT_DEFAULT); kmem_alloc_pages(object, oldsize, newaddr + oldsize, newaddr + newsize, VM_PROT_DEFAULT); *newaddrp = newaddr; return KERN_SUCCESS; }
kern_return_t kmem_alloc( vm_map_t map, vm_offset_t *addrp, vm_size_t size) { vm_object_t object; vm_map_entry_t entry; vm_offset_t addr; unsigned int attempts; kern_return_t kr; /* * Allocate a new object. We must do this before locking * the map, lest we risk deadlock with the default pager: * device_read_alloc uses kmem_alloc, * which tries to allocate an object, * which uses kmem_alloc_wired to get memory, * which blocks for pages. * then the default pager needs to read a block * to process a memory_object_data_write, * and device_read_alloc calls kmem_alloc * and deadlocks on the map lock. */ size = round_page(size); object = vm_object_allocate(size); attempts = 0; retry: vm_map_lock(map); kr = vm_map_find_entry(map, &addr, size, (vm_offset_t) 0, VM_OBJECT_NULL, &entry); if (kr != KERN_SUCCESS) { vm_map_unlock(map); if (attempts == 0) { attempts++; slab_collect(); goto retry; } printf_once("no more room for kmem_alloc in %p\n", map); vm_object_deallocate(object); return kr; } entry->object.vm_object = object; entry->offset = 0; /* * Since we have not given out this address yet, * it is safe to unlock the map. */ vm_map_unlock(map); /* * Allocate wired-down memory in the kernel_object, * for this entry, and enter it in the kernel pmap. */ kmem_alloc_pages(object, 0, addr, addr + size, VM_PROT_DEFAULT); /* * Return the memory, not zeroed. */ *addrp = addr; return KERN_SUCCESS; }
kern_return_t projected_buffer_map( vm_map_t map, vm_offset_t kernel_addr, vm_size_t size, vm_offset_t *user_p, vm_prot_t protection, vm_inherit_t inheritance) /*Currently only VM_INHERIT_NONE supported*/ { vm_map_entry_t u_entry, k_entry; vm_offset_t physical_addr, user_addr; vm_size_t r_size; kern_return_t kr; /* * Find entry in kernel map */ size = round_page(size); if (map == VM_MAP_NULL || map == kernel_map || !vm_map_lookup_entry(kernel_map, kernel_addr, &k_entry) || kernel_addr + size > k_entry->vme_end) return(KERN_INVALID_ARGUMENT); /* * Create entry in user task */ vm_map_lock(map); kr = vm_map_find_entry(map, &user_addr, size, (vm_offset_t) 0, VM_OBJECT_NULL, &u_entry); if (kr != KERN_SUCCESS) { vm_map_unlock(map); return kr; } u_entry->object.vm_object = k_entry->object.vm_object; vm_object_reference(k_entry->object.vm_object); u_entry->offset = kernel_addr - k_entry->vme_start + k_entry->offset; u_entry->projected_on = k_entry; /*Creates coupling with kernel mapping of the buffer, and also guarantees that user cannot directly manipulate buffer VM entry*/ u_entry->protection = protection; u_entry->max_protection = protection; u_entry->inheritance = inheritance; u_entry->wired_count = k_entry->wired_count; vm_map_unlock(map); *user_p = user_addr; /* Set up physical mappings for user pmap */ pmap_pageable(map->pmap, user_addr, user_addr + size, !k_entry->wired_count); for (r_size = 0; r_size < size; r_size += PAGE_SIZE) { physical_addr = pmap_extract(kernel_pmap, kernel_addr + r_size); pmap_enter(map->pmap, user_addr + r_size, physical_addr, protection, k_entry->wired_count); } return(KERN_SUCCESS); }
int do_fork(void) { thread_t *td = thread_self(); /* Cannot fork non-user threads. */ assert(td->td_proc); thread_t *newtd = thread_create(td->td_name, NULL, NULL); /* Clone the thread. Since we don't use fork-oriented thread_t layout, we copy all necessary fields one-by one for clarity. The new thread is already on the all_thread list, has name and tid set. Many fields don't require setup as they will be prepared by sched_add. */ assert(td->td_idnest == 0); newtd->td_idnest = 0; /* Copy user context.. */ newtd->td_uctx = td->td_uctx; newtd->td_uctx_fpu = td->td_uctx_fpu; exc_frame_set_retval(&newtd->td_uctx, 0); /* New thread does not need the exception frame just yet. */ newtd->td_kframe = NULL; newtd->td_onfault = 0; /* The new thread already has a new kernel stack allocated. There is no need to copy its contents, it will be discarded anyway. We just prepare the thread's kernel context to a fresh one so that it will continue execution starting from user_exc_leave (which serves as fork_trampoline). */ ctx_init(newtd, (void (*)(void *))user_exc_leave, NULL); newtd->td_sleepqueue = sleepq_alloc(); newtd->td_wchan = NULL; newtd->td_waitpt = NULL; newtd->td_prio = td->td_prio; /* Now, prepare a new process. */ assert(td->td_proc); proc_t *proc = proc_create(); proc->p_parent = td->td_proc; TAILQ_INSERT_TAIL(&td->td_proc->p_children, proc, p_child); proc_populate(proc, newtd); /* Clone the entire process memory space. */ proc->p_uspace = vm_map_clone(td->td_proc->p_uspace); /* Find copied brk segment. */ proc->p_sbrk = vm_map_find_entry(proc->p_uspace, SBRK_START); /* Copy the parent descriptor table. */ /* TODO: Optionally share the descriptor table between processes. */ proc->p_fdtable = fdtab_copy(td->td_proc->p_fdtable); /* Copy signal handler dispatch rules. */ memcpy(proc->p_sigactions, td->td_proc->p_sigactions, sizeof(proc->p_sigactions)); sched_add(newtd); return proc->p_pid; }