static long zerofill(unsigned long start, unsigned long end, int prot) { unsigned long pg_start = PAGE_NEXT(start), pg_end = PAGE_NEXT(end), padd_len = pg_start-start; if (start > end) return -1; if (prot & PROT_WRITE) memset((void *)start, 0, padd_len); return do_mmap2(pg_start, pg_end-pg_start, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); }
static unsigned long get_mmap_base(elf_bin_t *elf) { unsigned long addr, pg_start, pg_end, mmap_min = ULONG_MAX, mmap_max = 0; int i; for (i=0; i<elf->hdr.e_phnum; i++) if ( elf->phdr[i].p_type == PT_LOAD) { pg_start = PAGE_BASE(elf->phdr[i].p_vaddr); pg_end = PAGE_NEXT(elf->phdr[i].p_vaddr + elf->phdr[i].p_memsz); if (pg_start < mmap_min) mmap_min = pg_start; if (pg_end > mmap_max) mmap_max = pg_end; } if (mmap_min > mmap_max) mmap_min = mmap_max; addr = do_mmap2(mmap_min, mmap_max-mmap_min, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if (addr & PG_MASK) /* not on page boundary -> error code */ return addr; sys_munmap(addr, mmap_max-mmap_min); return addr-mmap_min; }
/** ユーザ空間へのアクセス許可があることを確認する(実装部) @param[in] as 検査対象の仮想アドレス空間 @param[in] start 検査開始アドレス @param[in] count 検査範囲のアドレス長(単位:バイト) @param[in] prot 検査するアクセス権(仮想メモリ領域の保護属性) @retval true 対象範囲に物理ページが存在し, 必要なアクセス権がある @retval false 対象範囲に物理ページが存在しないか, 必要なアクセス権がない */ static bool user_area_can_access_nolock(vm *as, void *start, size_t count, vma_prot prot) { int rc; void *pg_start; void *pg_end; vma *vmap; kassert( as != NULL ); pg_start = (void *)PAGE_START((uintptr_t)start); pg_end = ( PAGE_ALIGNED( (uintptr_t)(start + count) ) ? ( start + count ) : ( (void *)PAGE_NEXT( (uintptr_t)(start + count) ) ) ); rc = _vm_find_vma_nolock(as, pg_start, &vmap); if ( rc != 0 ) goto can_not_access; if ( vmap->end < pg_end ) goto can_not_access; if ( !( vmap->prot & prot ) ) goto can_not_access; return true; can_not_access: return false; }
static char unmap_page(char *previous, char *mem, char *next) { if (!has_one_page_available()) return (0); if (previous) *PAGE_NEXT(previous) = next; else get_page_list(0, next); munmap((void *)mem, *PAGE_SIZE(mem)); return (0); }
/** sbrkシステムコールの実処理 @param[in] sbrk sbrkメッセージ @param[in] src 呼出元エンドポイント @retval 0 正常に更新した @retval -ENOMEM メモリ不足により更新に失敗した */ static int handle_sbrk(vm_sys_sbrk *sbrk,endpoint src) { int rc; thread *thr; void *cur_end; void *new_end; intrflags flags; acquire_all_thread_lock( &flags ); thr = thr_find_thread_by_tid_nolock(src); if ( thr == NULL ) { rc = -ENOENT; goto unlock_out; } rc = proc_expand_heap(thr->p, NULL, &cur_end); if ( rc != 0 ) goto unlock_out; if ( sbrk->inc == 0 ) { rc = 0; goto success_out; } new_end = ( PAGE_ALIGNED( (uintptr_t)(cur_end + sbrk->inc) ) ) ? ( (void *)( cur_end + sbrk->inc ) ) : ( (void *)PAGE_NEXT( (uintptr_t)(cur_end + sbrk->inc) ) ); if ( new_end < thr->p->heap->start ) { rc = -EINVAL; goto unlock_out; } rc = proc_expand_heap(thr->p, new_end, &cur_end); if ( rc != 0 ) goto unlock_out; success_out: release_all_thread_lock(&flags); if ( rc == 0 ) sbrk->old_heap_end = cur_end; return 0; unlock_out: release_all_thread_lock(&flags); return rc; }
static unsigned long set_brk(elf_bin_t *bin) { unsigned long new_brk = 0, v_end, i; for (i=0; i<bin->hdr.e_phnum; i++) if ( bin->phdr[i].p_type == PT_LOAD) { v_end = PAGE_NEXT(bin->phdr[i].p_vaddr+bin->phdr[i].p_memsz); if ( (bin->phdr[i].p_type == PT_LOAD) && (new_brk < v_end) ) new_brk = v_end; } return (new_brk == set_brk_min(new_brk)) ? 0 : -1; }
static char has_one_page_available() { char *mem; char av; av = 0; mem = get_page_list(0, (void *)-1); while (mem && av != 3) { if (*PAGE_SIZE(mem) == get_alloc_size(TINY)) av |= 1; else if (*PAGE_SIZE(mem) == get_alloc_size(SMALL)) av |= 2; mem = *PAGE_NEXT(mem); } return (av == 3); }
static char delete_ptrs_page(char *ptr) { char *mem; char *previous; char *next; previous = NULL; mem = get_page_list(0, (void *)-1); while (mem) { next = *PAGE_NEXT(mem); if (ptr >= mem + PAGE_META && ptr < mem + *PAGE_SIZE(mem)) return (evaluate_freeable(ptr, previous, mem, next)); previous = mem; mem = next; } return (0); }
static long mmap_prog_section(elf_bin_t *elf, Elf32_Phdr *p) { unsigned long base = elf->base, brk_, bss, addr, pg_off, size; int prot = 0; if (p->p_flags & PF_R) prot |= PROT_READ; if (p->p_flags & PF_W) prot |= PROT_WRITE; if (p->p_flags & PF_X) prot |= PROT_EXEC; addr = PAGE_BASE(base+p->p_vaddr); size = PAGE_NEXT(base+p->p_vaddr + p->p_filesz) - addr; pg_off = p->p_offset/PG_SIZE; bss = base + p->p_vaddr + p->p_filesz; brk_ = base + p->p_vaddr + p->p_memsz; if ( ((p->p_vaddr-p->p_offset) & PG_MASK) || (bss > brk_) ) return -1; addr = do_mmap2(addr, size, prot, MAP_PRIVATE|MAP_FIXED, elf->fd, pg_off); if (addr & PG_MASK) /* not on page boundary -> error code */ return addr; if (elf->brk < brk_) elf->brk = brk_; if (elf->bss < bss) elf->bss = bss; /* linux does not fill in bss sections * between load segments for interpreters; * makes no difference to the standard ld.so */ addr = zerofill(bss, brk_, prot); if (addr & PG_MASK) /* not on page boundary -> error code */ return addr; return 0; }
/* relocate the stack */ static long get_stack_random_shift(long *auxv) { return 0x1000000 - (PAGE_NEXT((long)get_aux(auxv, AT_EXECFN)) & 0xfff000); }
/** プロセス間でデータをコピーする @param[in] dest_as コピー先の仮想アドレス空間 @param[in] src_as コピー元の仮想アドレス空間 @param[in] dest コピー先のアドレス @param[in] src コピー元のアドレス @param[in] count コピーするバイト数 @return コピーしたバイト数 @return -ENOMEM バッファページが獲得できない @return -EFAULT ページが存在しない @note カーネルストレートマップ領域間でメモリコピーを行うことで アドレス空間の切り替えを行わないようにする また、自プロセスの空間のmutexを取ると互いの空間のロック待ちで デッドロックするため, プリエンプションを禁止にして自プロセス内の 他のスレッドが割り込まないようにし, 自プロセスのアドレス空間の mutexを取らないようにする。 */ static int inter_user_copy(vm *dest_as, vm *src_as, void *dest, const void *src, size_t count) { int rc; size_t len; uintptr_t src_kvaddr; vma_prot src_prot; uintptr_t dest_kvaddr; vma_prot dest_prot; void *saddr; void *daddr; size_t src_len; size_t dest_len; size_t cpy_len; void *new_page; vma *vmap; kassert( src_as != NULL ); kassert( src_as->p != NULL ); kassert( dest_as != NULL ); kassert( dest_as->p != NULL ); /* アドレス空間とアドレスの範囲がユーザ空間に収まることを確認 */ if ( ( (uintptr_t)src >= KERN_VMA_BASE ) || ( (uintptr_t)dest >= KERN_VMA_BASE ) ) return -EFAULT; if ( ( dest_as->p == hal_refer_kernel_proc() ) || ( src_as->p == hal_refer_kernel_proc() ) ) return -EFAULT; /* 自CPUの同一プロセス内のスレッドがアドレス空間を操作 * しないことを保証するためにプリエンプションを抑止 */ ti_disable_dispatch(); /* 相手の空間のロック(mutex)を取る */ if ( ¤t->p->vm == dest_as ) mutex_lock( &src_as->asmtx ); else mutex_lock( &dest_as->asmtx ); saddr = (void *)src; daddr = dest; for( len = count; len > 0; ) { /* * コピー元/コピー先ページを取得する */ if ( src_as->p == hal_refer_kernel_proc() ) src_kvaddr = PAGE_START(saddr); else { rc = hal_translate_user_page(src_as, (uintptr_t)saddr, &src_kvaddr, &src_prot); if ( rc != 0 ) { /* * ページ未割り当て時はページを割当てる */ rc = _vm_find_vma_nolock(src_as, saddr, &vmap); if ( rc != 0 ) goto unlock_out; if ( vmap->prot == VMA_PROT_NONE ) goto unlock_out; rc = get_free_page(&new_page); if ( rc != 0 ) goto unlock_out; memset(new_page, 0, PAGE_SIZE); rc = hal_map_user_page(src_as, (uintptr_t)saddr, (uintptr_t)new_page, vmap->prot ); if ( rc != 0 ) { free_page(new_page); goto unlock_out; } } } if ( dest_as->p == hal_refer_kernel_proc() ) dest_kvaddr = PAGE_START(daddr); else { rc = hal_translate_user_page(dest_as, (uintptr_t)daddr, &dest_kvaddr, &dest_prot); if ( rc != 0 ) { /* * ページ未割り当て時はページを割当てる */ rc = _vm_find_vma_nolock(dest_as, daddr, &vmap); if ( rc != 0 ) goto unlock_out; if ( vmap->prot == VMA_PROT_NONE ) goto unlock_out; rc = get_free_page(&new_page); if ( rc != 0 ) goto unlock_out; memset(new_page, 0, PAGE_SIZE); rc = hal_map_user_page(dest_as, (uintptr_t)daddr, (uintptr_t)new_page, vmap->prot ); if ( rc != 0 ) { free_page(new_page); goto unlock_out; } } } /* * src/destともページ内に収まる分だけコピーする */ src_len = PAGE_NEXT(saddr) - (uintptr_t)saddr; dest_len = PAGE_NEXT(daddr) - (uintptr_t)daddr; cpy_len = ( src_len <= dest_len ) ? ( src_len ) : ( dest_len ); if ( cpy_len > len ) cpy_len = len; len -= cpy_len; while( cpy_len > 0 ) { *(char *)(dest_kvaddr + ( (uintptr_t)daddr - PAGE_START(daddr) ) ) = *(char *)(src_kvaddr + ( (uintptr_t)saddr - PAGE_START(saddr) ) ); --cpy_len; ++saddr; ++daddr; } } rc = count - len; unlock_out: if ( ¤t->p->vm == dest_as ) mutex_unlock( &src_as->asmtx ); else mutex_unlock( &dest_as->asmtx ); ti_enable_dispatch(); return rc; }