int vmcmd_readvn(struct lwp *l, struct exec_vmcmd *cmd) { struct proc *p = l->l_proc; int error; vm_prot_t prot, maxprot; error = vn_rdwr(UIO_READ, cmd->ev_vp, (void *)cmd->ev_addr, cmd->ev_len, cmd->ev_offset, UIO_USERSPACE, IO_UNIT, l->l_cred, NULL, l); if (error) return error; prot = cmd->ev_prot; maxprot = VM_PROT_ALL; #ifdef PAX_MPROTECT pax_mprotect(l, &prot, &maxprot); #endif /* PAX_MPROTECT */ #ifdef PMAP_NEED_PROCWR /* * we had to write the process, make sure the pages are synched * with the instruction cache. */ if (prot & VM_PROT_EXECUTE) pmap_procwr(p, cmd->ev_addr, cmd->ev_len); #endif /* * we had to map in the area at PROT_ALL so that vn_rdwr() * could write to it. however, the caller seems to want * it mapped read-only, so now we are going to have to call * uvm_map_protect() to fix up the protection. ICK. */ if (maxprot != VM_PROT_ALL) { error = uvm_map_protect(&p->p_vmspace->vm_map, trunc_page(cmd->ev_addr), round_page(cmd->ev_addr + cmd->ev_len), maxprot, true); if (error) return (error); } if (prot != maxprot) { error = uvm_map_protect(&p->p_vmspace->vm_map, trunc_page(cmd->ev_addr), round_page(cmd->ev_addr + cmd->ev_len), prot, false); if (error) return (error); } return 0; }
int sys_mprotect(struct proc *p, void *v, register_t *retval) { struct sys_mprotect_args /* { syscallarg(void *) addr; syscallarg(size_t) len; syscallarg(int) prot; } */ *uap = v; vaddr_t addr; vsize_t size, pageoff; vm_prot_t prot; /* * extract syscall args from uap */ addr = (vaddr_t)SCARG(uap, addr); size = (vsize_t)SCARG(uap, len); prot = SCARG(uap, prot); if ((prot & VM_PROT_ALL) != prot) return (EINVAL); /* * align the address to a page boundary, and adjust the size accordingly */ ALIGN_ADDR(addr, size, pageoff); if (addr > SIZE_MAX - size) return (EINVAL); /* disallow wrap-around. */ return (uvm_map_protect(&p->p_vmspace->vm_map, addr, addr+size, prot, FALSE)); }
int vmcmd_map_readvn(struct proc *p, struct exec_vmcmd *cmd) { int error; vm_prot_t prot; if (cmd->ev_len == 0) return (0); prot = cmd->ev_prot; cmd->ev_addr = trunc_page(cmd->ev_addr); /* required by uvm_map */ error = uvm_map(&p->p_vmspace->vm_map, &cmd->ev_addr, round_page(cmd->ev_len), NULL, UVM_UNKNOWN_OFFSET, 0, UVM_MAPFLAG(prot | UVM_PROT_WRITE, UVM_PROT_ALL, UVM_INH_COPY, UVM_ADV_NORMAL, UVM_FLAG_FIXED|UVM_FLAG_OVERLAY|UVM_FLAG_COPYONW)); if (error) return (error); error = vn_rdwr(UIO_READ, cmd->ev_vp, (caddr_t)cmd->ev_addr, cmd->ev_len, cmd->ev_offset, UIO_USERSPACE, IO_UNIT, p->p_ucred, NULL, p); if (error) return (error); if (cmd->ev_prot != (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE)) { /* * we had to map in the area at PROT_ALL so that vn_rdwr() * could write to it. however, the caller seems to want * it mapped read-only, so now we are going to have to call * uvm_map_protect() to fix up the protection. ICK. */ return (uvm_map_protect(&p->p_vmspace->vm_map, trunc_page(cmd->ev_addr), round_page(cmd->ev_addr + cmd->ev_len), prot, FALSE)); } return (0); }
int sys_mprotect(struct lwp *l, const struct sys_mprotect_args *uap, register_t *retval) { /* { syscallarg(void *) addr; syscallarg(size_t) len; syscallarg(int) prot; } */ struct proc *p = l->l_proc; vaddr_t addr; vsize_t size, pageoff; vm_prot_t prot; int error; /* * extract syscall args from uap */ addr = (vaddr_t)SCARG(uap, addr); size = (vsize_t)SCARG(uap, len); prot = SCARG(uap, prot) & VM_PROT_ALL; /* * align the address to a page boundary and adjust the size accordingly. */ pageoff = (addr & PAGE_MASK); addr -= pageoff; size += pageoff; size = round_page(size); error = range_test(&p->p_vmspace->vm_map, addr, size, false); if (error) return EINVAL; error = uvm_map_protect(&p->p_vmspace->vm_map, addr, addr + size, prot, false); return error; }
int sti_rom_setup(struct sti_rom *rom, bus_space_tag_t iot, bus_space_tag_t memt, bus_space_handle_t romh, bus_addr_t *bases, u_int codebase) { struct sti_dd *dd; int error, size, i; KASSERT(rom != NULL); STI_ENABLE_ROM(rom->rom_softc); rom->iot = iot; rom->memt = memt; rom->romh = romh; rom->bases = bases; /* * Get ROM header and code function pointers. */ dd = &rom->rom_dd; rom->rom_devtype = bus_space_read_1(memt, romh, 3); if (rom->rom_devtype == STI_DEVTYPE1) { dd->dd_type = bus_space_read_1(memt, romh, 0x03); dd->dd_nmon = bus_space_read_1(memt, romh, 0x07); dd->dd_grrev = bus_space_read_1(memt, romh, 0x0b); dd->dd_lrrev = bus_space_read_1(memt, romh, 0x0f); dd->dd_grid[0] = parseword(0x10); dd->dd_grid[1] = parseword(0x20); dd->dd_fntaddr = parseword(0x30) & ~3; dd->dd_maxst = parseword(0x40); dd->dd_romend = parseword(0x50) & ~3; dd->dd_reglst = parseword(0x60) & ~3; dd->dd_maxreent= parseshort(0x70); dd->dd_maxtimo = parseshort(0x78); dd->dd_montbl = parseword(0x80) & ~3; dd->dd_udaddr = parseword(0x90) & ~3; dd->dd_stimemreq=parseword(0xa0); dd->dd_udsize = parseword(0xb0); dd->dd_pwruse = parseshort(0xc0); dd->dd_bussup = bus_space_read_1(memt, romh, 0xcb); dd->dd_ebussup = bus_space_read_1(memt, romh, 0xcf); dd->dd_altcodet= bus_space_read_1(memt, romh, 0xd3); dd->dd_eddst[0]= bus_space_read_1(memt, romh, 0xd7); dd->dd_eddst[1]= bus_space_read_1(memt, romh, 0xdb); dd->dd_eddst[2]= bus_space_read_1(memt, romh, 0xdf); dd->dd_cfbaddr = parseword(0xe0) & ~3; codebase <<= 2; dd->dd_pacode[0x0] = parseword(codebase + 0x00) & ~3; dd->dd_pacode[0x1] = parseword(codebase + 0x10) & ~3; dd->dd_pacode[0x2] = parseword(codebase + 0x20) & ~3; dd->dd_pacode[0x3] = parseword(codebase + 0x30) & ~3; dd->dd_pacode[0x4] = parseword(codebase + 0x40) & ~3; dd->dd_pacode[0x5] = parseword(codebase + 0x50) & ~3; dd->dd_pacode[0x6] = parseword(codebase + 0x60) & ~3; dd->dd_pacode[0x7] = parseword(codebase + 0x70) & ~3; dd->dd_pacode[0x8] = parseword(codebase + 0x80) & ~3; dd->dd_pacode[0x9] = parseword(codebase + 0x90) & ~3; dd->dd_pacode[0xa] = parseword(codebase + 0xa0) & ~3; dd->dd_pacode[0xb] = parseword(codebase + 0xb0) & ~3; dd->dd_pacode[0xc] = parseword(codebase + 0xc0) & ~3; dd->dd_pacode[0xd] = parseword(codebase + 0xd0) & ~3; dd->dd_pacode[0xe] = parseword(codebase + 0xe0) & ~3; dd->dd_pacode[0xf] = parseword(codebase + 0xf0) & ~3; } else { /* STI_DEVTYPE4 */ bus_space_read_region_stream_4(memt, romh, 0, (uint32_t *)dd, sizeof(*dd) / 4); /* fix pacode... */ bus_space_read_region_stream_4(memt, romh, codebase, (uint32_t *)dd->dd_pacode, sizeof(dd->dd_pacode) / 4); } STI_DISABLE_ROM(rom->rom_softc); DPRINTF(("dd:\n" "devtype=%x, rev=%x;%d, altt=%x, gid=%08x%08x, font=%x, mss=%x\n" "end=%x, regions=%x, msto=%x, timo=%d, mont=%x, user=%x[%x]\n" "memrq=%x, pwr=%d, bus=%x, ebus=%x, cfb=%x\n" "code=", dd->dd_type & 0xff, dd->dd_grrev, dd->dd_lrrev, dd->dd_altcodet, dd->dd_grid[0], dd->dd_grid[1], dd->dd_fntaddr, dd->dd_maxst, dd->dd_romend, dd->dd_reglst, dd->dd_maxreent, dd->dd_maxtimo, dd->dd_montbl, dd->dd_udaddr, dd->dd_udsize, dd->dd_stimemreq, dd->dd_pwruse, dd->dd_bussup, dd->dd_ebussup, dd->dd_cfbaddr)); DPRINTF(("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", dd->dd_pacode[0x0], dd->dd_pacode[0x1], dd->dd_pacode[0x2], dd->dd_pacode[0x3], dd->dd_pacode[0x4], dd->dd_pacode[0x5], dd->dd_pacode[0x6], dd->dd_pacode[0x7], dd->dd_pacode[0x8], dd->dd_pacode[0x9], dd->dd_pacode[0xa], dd->dd_pacode[0xb], dd->dd_pacode[0xc], dd->dd_pacode[0xd], dd->dd_pacode[0xe], dd->dd_pacode[0xf])); /* * Figure out how many bytes we need for the STI code. * Note there could be fewer than STI_END pointer entries * populated, especially on older devices. */ for (i = STI_END; !dd->dd_pacode[i]; i--) ; size = dd->dd_pacode[i] - dd->dd_pacode[STI_BEGIN]; if (rom->rom_devtype == STI_DEVTYPE1) size = (size + 3) / 4; if (size == 0) { aprint_error(": no code for the requested platform\n"); return EINVAL; } DPRINTF(("code size %x/%x\n", size, round_page(size))); if (!(rom->rom_code = uvm_km_alloc(kernel_map, round_page(size), 0, UVM_KMF_WIRED))) { aprint_error(": cannot allocate %u bytes for code\n", size); return ENOMEM; } /* * Copy code into memory and make it executable. */ STI_ENABLE_ROM(rom->rom_softc); if (rom->rom_devtype == STI_DEVTYPE1) { uint8_t *p; uint32_t addr, eaddr; p = (uint8_t *)rom->rom_code; for (addr = dd->dd_pacode[STI_BEGIN], eaddr = addr + size * 4; addr < eaddr; addr += 4 ) { *p++ = bus_space_read_4(memt, romh, addr) & 0xff; } } else { /* STI_DEVTYPE4 */ bus_space_read_region_stream_4(memt, romh, dd->dd_pacode[STI_BEGIN], (uint32_t *)rom->rom_code, size / 4); } STI_DISABLE_ROM(rom->rom_softc); if ((error = uvm_map_protect(kernel_map, rom->rom_code, rom->rom_code + round_page(size), UVM_PROT_RX, FALSE))) { aprint_error(": uvm_map_protect failed (%d)\n", error); uvm_km_free(kernel_map, rom->rom_code, round_page(size), UVM_KMF_WIRED); return error; } /* * Setup code function pointers. */ #define O(i) \ (dd->dd_pacode[(i)] == 0 ? 0 : \ (rom->rom_code + (dd->dd_pacode[(i)] - dd->dd_pacode[0]) / \ (rom->rom_devtype == STI_DEVTYPE1 ? 4 : 1))) rom->init = (sti_init_t) O(STI_INIT_GRAPH); rom->mgmt = (sti_mgmt_t) O(STI_STATE_MGMT); rom->unpmv = (sti_unpmv_t) O(STI_FONT_UNPMV); rom->blkmv = (sti_blkmv_t) O(STI_BLOCK_MOVE); rom->test = (sti_test_t) O(STI_SELF_TEST); rom->exhdl = (sti_exhdl_t) O(STI_EXCEP_HDLR); rom->inqconf = (sti_inqconf_t)O(STI_INQ_CONF); rom->scment = (sti_scment_t)O(STI_SCM_ENT); rom->dmac = (sti_dmac_t) O(STI_DMA_CTRL); rom->flowc = (sti_flowc_t) O(STI_FLOW_CTRL); rom->utiming = (sti_utiming_t)O(STI_UTIMING); rom->pmgr = (sti_pmgr_t) O(STI_PROC_MGR); rom->util = (sti_util_t) O(STI_UTIL); #undef O /* * Set colormap entry is not implemented until 8.04, so force * a NULL pointer here. */ if (dd->dd_grrev < STI_REVISION(8, 4)) { rom->scment = NULL; } return 0; }
/* ARGSUSED */ int sys_execve(struct proc *p, void *v, register_t *retval) { struct sys_execve_args /* { syscallarg(const char *) path; syscallarg(char *const *) argp; syscallarg(char *const *) envp; } */ *uap = v; int error; struct exec_package pack; struct nameidata nid; struct vattr attr; struct ucred *cred = p->p_ucred; char *argp; char * const *cpp, *dp, *sp; #ifdef KTRACE char *env_start; #endif struct process *pr = p->p_p; long argc, envc; size_t len, sgap; #ifdef MACHINE_STACK_GROWS_UP size_t slen; #endif char *stack; struct ps_strings arginfo; struct vmspace *vm = pr->ps_vmspace; char **tmpfap; extern struct emul emul_native; #if NSYSTRACE > 0 int wassugid = ISSET(pr->ps_flags, PS_SUGID | PS_SUGIDEXEC); size_t pathbuflen; #endif char *pathbuf = NULL; struct vnode *otvp; /* get other threads to stop */ if ((error = single_thread_set(p, SINGLE_UNWIND, 1))) return (error); /* * Cheap solution to complicated problems. * Mark this process as "leave me alone, I'm execing". */ atomic_setbits_int(&pr->ps_flags, PS_INEXEC); #if NSYSTRACE > 0 if (ISSET(p->p_flag, P_SYSTRACE)) { systrace_execve0(p); pathbuf = pool_get(&namei_pool, PR_WAITOK); error = copyinstr(SCARG(uap, path), pathbuf, MAXPATHLEN, &pathbuflen); if (error != 0) goto clrflag; } #endif if (pathbuf != NULL) { NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_SYSSPACE, pathbuf, p); } else { NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); } /* * initialize the fields of the exec package. */ if (pathbuf != NULL) pack.ep_name = pathbuf; else pack.ep_name = (char *)SCARG(uap, path); pack.ep_hdr = malloc(exec_maxhdrsz, M_EXEC, M_WAITOK); pack.ep_hdrlen = exec_maxhdrsz; pack.ep_hdrvalid = 0; pack.ep_ndp = &nid; pack.ep_interp = NULL; pack.ep_emul_arg = NULL; VMCMDSET_INIT(&pack.ep_vmcmds); pack.ep_vap = &attr; pack.ep_emul = &emul_native; pack.ep_flags = 0; /* see if we can run it. */ if ((error = check_exec(p, &pack)) != 0) { goto freehdr; } /* XXX -- THE FOLLOWING SECTION NEEDS MAJOR CLEANUP */ /* allocate an argument buffer */ argp = km_alloc(NCARGS, &kv_exec, &kp_pageable, &kd_waitok); #ifdef DIAGNOSTIC if (argp == NULL) panic("execve: argp == NULL"); #endif dp = argp; argc = 0; /* copy the fake args list, if there's one, freeing it as we go */ if (pack.ep_flags & EXEC_HASARGL) { tmpfap = pack.ep_fa; while (*tmpfap != NULL) { char *cp; cp = *tmpfap; while (*cp) *dp++ = *cp++; *dp++ = '\0'; free(*tmpfap, M_EXEC, 0); tmpfap++; argc++; } free(pack.ep_fa, M_EXEC, 0); pack.ep_flags &= ~EXEC_HASARGL; } /* Now get argv & environment */ if (!(cpp = SCARG(uap, argp))) { error = EFAULT; goto bad; } if (pack.ep_flags & EXEC_SKIPARG) cpp++; while (1) { len = argp + ARG_MAX - dp; if ((error = copyin(cpp, &sp, sizeof(sp))) != 0) goto bad; if (!sp) break; if ((error = copyinstr(sp, dp, len, &len)) != 0) { if (error == ENAMETOOLONG) error = E2BIG; goto bad; } dp += len; cpp++; argc++; } /* must have at least one argument */ if (argc == 0) { error = EINVAL; goto bad; } #ifdef KTRACE if (KTRPOINT(p, KTR_EXECARGS)) ktrexec(p, KTR_EXECARGS, argp, dp - argp); #endif envc = 0; /* environment does not need to be there */ if ((cpp = SCARG(uap, envp)) != NULL ) { #ifdef KTRACE env_start = dp; #endif while (1) { len = argp + ARG_MAX - dp; if ((error = copyin(cpp, &sp, sizeof(sp))) != 0) goto bad; if (!sp) break; if ((error = copyinstr(sp, dp, len, &len)) != 0) { if (error == ENAMETOOLONG) error = E2BIG; goto bad; } dp += len; cpp++; envc++; } #ifdef KTRACE if (KTRPOINT(p, KTR_EXECENV)) ktrexec(p, KTR_EXECENV, env_start, dp - env_start); #endif } dp = (char *)(((long)dp + _STACKALIGNBYTES) & ~_STACKALIGNBYTES); sgap = STACKGAPLEN; /* * If we have enabled random stackgap, the stack itself has already * been moved from a random location, but is still aligned to a page * boundary. Provide the lower bits of random placement now. */ if (stackgap_random != 0) { sgap += arc4random() & PAGE_MASK; sgap = (sgap + _STACKALIGNBYTES) & ~_STACKALIGNBYTES; } /* Now check if args & environ fit into new stack */ len = ((argc + envc + 2 + pack.ep_emul->e_arglen) * sizeof(char *) + sizeof(long) + dp + sgap + sizeof(struct ps_strings)) - argp; len = (len + _STACKALIGNBYTES) &~ _STACKALIGNBYTES; if (len > pack.ep_ssize) { /* in effect, compare to initial limit */ error = ENOMEM; goto bad; } /* adjust "active stack depth" for process VSZ */ pack.ep_ssize = len; /* maybe should go elsewhere, but... */ /* * we're committed: any further errors will kill the process, so * kill the other threads now. */ single_thread_set(p, SINGLE_EXIT, 0); /* * Prepare vmspace for remapping. Note that uvmspace_exec can replace * pr_vmspace! */ uvmspace_exec(p, VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS); vm = pr->ps_vmspace; /* Now map address space */ vm->vm_taddr = (char *)trunc_page(pack.ep_taddr); vm->vm_tsize = atop(round_page(pack.ep_taddr + pack.ep_tsize) - trunc_page(pack.ep_taddr)); vm->vm_daddr = (char *)trunc_page(pack.ep_daddr); vm->vm_dsize = atop(round_page(pack.ep_daddr + pack.ep_dsize) - trunc_page(pack.ep_daddr)); vm->vm_dused = 0; vm->vm_ssize = atop(round_page(pack.ep_ssize)); vm->vm_maxsaddr = (char *)pack.ep_maxsaddr; vm->vm_minsaddr = (char *)pack.ep_minsaddr; /* create the new process's VM space by running the vmcmds */ #ifdef DIAGNOSTIC if (pack.ep_vmcmds.evs_used == 0) panic("execve: no vmcmds"); #endif error = exec_process_vmcmds(p, &pack); /* if an error happened, deallocate and punt */ if (error) goto exec_abort; /* old "stackgap" is gone now */ pr->ps_stackgap = 0; #ifdef MACHINE_STACK_GROWS_UP pr->ps_strings = (vaddr_t)vm->vm_maxsaddr + sgap; if (uvm_map_protect(&vm->vm_map, (vaddr_t)vm->vm_maxsaddr, trunc_page(pr->ps_strings), PROT_NONE, TRUE)) goto exec_abort; #else pr->ps_strings = (vaddr_t)vm->vm_minsaddr - sizeof(arginfo) - sgap; if (uvm_map_protect(&vm->vm_map, round_page(pr->ps_strings + sizeof(arginfo)), (vaddr_t)vm->vm_minsaddr, PROT_NONE, TRUE)) goto exec_abort; #endif /* remember information about the process */ arginfo.ps_nargvstr = argc; arginfo.ps_nenvstr = envc; #ifdef MACHINE_STACK_GROWS_UP stack = (char *)vm->vm_maxsaddr + sizeof(arginfo) + sgap; slen = len - sizeof(arginfo) - sgap; #else stack = (char *)(vm->vm_minsaddr - len); #endif /* Now copy argc, args & environ to new stack */ if (!(*pack.ep_emul->e_copyargs)(&pack, &arginfo, stack, argp)) goto exec_abort; /* copy out the process's ps_strings structure */ if (copyout(&arginfo, (char *)pr->ps_strings, sizeof(arginfo))) goto exec_abort; stopprofclock(pr); /* stop profiling */ fdcloseexec(p); /* handle close on exec */ execsigs(p); /* reset caught signals */ TCB_SET(p, NULL); /* reset the TCB address */ pr->ps_kbind_addr = 0; /* reset the kbind bits */ pr->ps_kbind_cookie = 0; /* set command name & other accounting info */ memset(p->p_comm, 0, sizeof(p->p_comm)); len = min(nid.ni_cnd.cn_namelen, MAXCOMLEN); memcpy(p->p_comm, nid.ni_cnd.cn_nameptr, len); pr->ps_acflag &= ~AFORK; /* record proc's vnode, for use by sysctl */ otvp = pr->ps_textvp; vref(pack.ep_vp); pr->ps_textvp = pack.ep_vp; if (otvp) vrele(otvp); atomic_setbits_int(&pr->ps_flags, PS_EXEC); if (pr->ps_flags & PS_PPWAIT) { atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT); atomic_clearbits_int(&pr->ps_pptr->ps_flags, PS_ISPWAIT); wakeup(pr->ps_pptr); } /* * If process does execve() while it has a mismatched real, * effective, or saved uid/gid, we set PS_SUGIDEXEC. */ if (cred->cr_uid != cred->cr_ruid || cred->cr_uid != cred->cr_svuid || cred->cr_gid != cred->cr_rgid || cred->cr_gid != cred->cr_svgid) atomic_setbits_int(&pr->ps_flags, PS_SUGIDEXEC); else atomic_clearbits_int(&pr->ps_flags, PS_SUGIDEXEC); atomic_clearbits_int(&pr->ps_flags, PS_TAMED); tame_dropwpaths(pr); /* * deal with set[ug]id. * MNT_NOEXEC has already been used to disable s[ug]id. */ if ((attr.va_mode & (VSUID | VSGID)) && proc_cansugid(p)) { int i; atomic_setbits_int(&pr->ps_flags, PS_SUGID|PS_SUGIDEXEC); #ifdef KTRACE /* * If process is being ktraced, turn off - unless * root set it. */ if (pr->ps_tracevp && !(pr->ps_traceflag & KTRFAC_ROOT)) ktrcleartrace(pr); #endif p->p_ucred = cred = crcopy(cred); if (attr.va_mode & VSUID) cred->cr_uid = attr.va_uid; if (attr.va_mode & VSGID) cred->cr_gid = attr.va_gid; /* * For set[ug]id processes, a few caveats apply to * stdin, stdout, and stderr. */ error = 0; fdplock(p->p_fd); for (i = 0; i < 3; i++) { struct file *fp = NULL; /* * NOTE - This will never return NULL because of * immature fds. The file descriptor table is not * shared because we're suid. */ fp = fd_getfile(p->p_fd, i); /* * Ensure that stdin, stdout, and stderr are already * allocated. We do not want userland to accidentally * allocate descriptors in this range which has implied * meaning to libc. */ if (fp == NULL) { short flags = FREAD | (i == 0 ? 0 : FWRITE); struct vnode *vp; int indx; if ((error = falloc(p, &fp, &indx)) != 0) break; #ifdef DIAGNOSTIC if (indx != i) panic("sys_execve: falloc indx != i"); #endif if ((error = cdevvp(getnulldev(), &vp)) != 0) { fdremove(p->p_fd, indx); closef(fp, p); break; } if ((error = VOP_OPEN(vp, flags, cred, p)) != 0) { fdremove(p->p_fd, indx); closef(fp, p); vrele(vp); break; } if (flags & FWRITE) vp->v_writecount++; fp->f_flag = flags; fp->f_type = DTYPE_VNODE; fp->f_ops = &vnops; fp->f_data = (caddr_t)vp; FILE_SET_MATURE(fp, p); } } fdpunlock(p->p_fd); if (error) goto exec_abort; } else atomic_clearbits_int(&pr->ps_flags, PS_SUGID); /* * Reset the saved ugids and update the process's copy of the * creds if the creds have been changed */ if (cred->cr_uid != cred->cr_svuid || cred->cr_gid != cred->cr_svgid) { /* make sure we have unshared ucreds */ p->p_ucred = cred = crcopy(cred); cred->cr_svuid = cred->cr_uid; cred->cr_svgid = cred->cr_gid; } if (pr->ps_ucred != cred) { struct ucred *ocred; ocred = pr->ps_ucred; crhold(cred); pr->ps_ucred = cred; crfree(ocred); } if (pr->ps_flags & PS_SUGIDEXEC) { int i, s = splclock(); timeout_del(&pr->ps_realit_to); for (i = 0; i < nitems(pr->ps_timer); i++) { timerclear(&pr->ps_timer[i].it_interval); timerclear(&pr->ps_timer[i].it_value); } splx(s); } /* reset CPU time usage for the thread, but not the process */ timespecclear(&p->p_tu.tu_runtime); p->p_tu.tu_uticks = p->p_tu.tu_sticks = p->p_tu.tu_iticks = 0; km_free(argp, NCARGS, &kv_exec, &kp_pageable); pool_put(&namei_pool, nid.ni_cnd.cn_pnbuf); vn_close(pack.ep_vp, FREAD, cred, p); /* * notify others that we exec'd */ KNOTE(&pr->ps_klist, NOTE_EXEC); /* setup new registers and do misc. setup. */ if (pack.ep_emul->e_fixup != NULL) { if ((*pack.ep_emul->e_fixup)(p, &pack) != 0) goto free_pack_abort; } #ifdef MACHINE_STACK_GROWS_UP (*pack.ep_emul->e_setregs)(p, &pack, (u_long)stack + slen, retval); #else (*pack.ep_emul->e_setregs)(p, &pack, (u_long)stack, retval); #endif /* map the process's signal trampoline code */ if (exec_sigcode_map(pr, pack.ep_emul)) goto free_pack_abort; #ifdef __HAVE_EXEC_MD_MAP /* perform md specific mappings that process might need */ if (exec_md_map(p, &pack)) goto free_pack_abort; #endif if (pr->ps_flags & PS_TRACED) psignal(p, SIGTRAP); free(pack.ep_hdr, M_EXEC, pack.ep_hdrlen); /* * Call emulation specific exec hook. This can setup per-process * p->p_emuldata or do any other per-process stuff an emulation needs. * * If we are executing process of different emulation than the * original forked process, call e_proc_exit() of the old emulation * first, then e_proc_exec() of new emulation. If the emulation is * same, the exec hook code should deallocate any old emulation * resources held previously by this process. */ if (pr->ps_emul && pr->ps_emul->e_proc_exit && pr->ps_emul != pack.ep_emul) (*pr->ps_emul->e_proc_exit)(p); p->p_descfd = 255; if ((pack.ep_flags & EXEC_HASFD) && pack.ep_fd < 255) p->p_descfd = pack.ep_fd; /* * Call exec hook. Emulation code may NOT store reference to anything * from &pack. */ if (pack.ep_emul->e_proc_exec) (*pack.ep_emul->e_proc_exec)(p, &pack); #if defined(KTRACE) && defined(COMPAT_LINUX) /* update ps_emul, but don't ktrace it if native-execing-native */ if (pr->ps_emul != pack.ep_emul || pack.ep_emul != &emul_native) { pr->ps_emul = pack.ep_emul; if (KTRPOINT(p, KTR_EMUL)) ktremul(p); } #else /* update ps_emul, the old value is no longer needed */ pr->ps_emul = pack.ep_emul; #endif atomic_clearbits_int(&pr->ps_flags, PS_INEXEC); single_thread_clear(p, P_SUSPSIG); #if NSYSTRACE > 0 if (ISSET(p->p_flag, P_SYSTRACE) && wassugid && !ISSET(pr->ps_flags, PS_SUGID | PS_SUGIDEXEC)) systrace_execve1(pathbuf, p); #endif if (pathbuf != NULL) pool_put(&namei_pool, pathbuf); return (0); bad: /* free the vmspace-creation commands, and release their references */ kill_vmcmds(&pack.ep_vmcmds); /* kill any opened file descriptor, if necessary */ if (pack.ep_flags & EXEC_HASFD) { pack.ep_flags &= ~EXEC_HASFD; fdplock(p->p_fd); (void) fdrelease(p, pack.ep_fd); fdpunlock(p->p_fd); } if (pack.ep_interp != NULL) pool_put(&namei_pool, pack.ep_interp); if (pack.ep_emul_arg != NULL) free(pack.ep_emul_arg, M_TEMP, pack.ep_emul_argsize); /* close and put the exec'd file */ vn_close(pack.ep_vp, FREAD, cred, p); pool_put(&namei_pool, nid.ni_cnd.cn_pnbuf); km_free(argp, NCARGS, &kv_exec, &kp_pageable); freehdr: free(pack.ep_hdr, M_EXEC, pack.ep_hdrlen); #if NSYSTRACE > 0 clrflag: #endif atomic_clearbits_int(&pr->ps_flags, PS_INEXEC); single_thread_clear(p, P_SUSPSIG); if (pathbuf != NULL) pool_put(&namei_pool, pathbuf); return (error); exec_abort: /* * the old process doesn't exist anymore. exit gracefully. * get rid of the (new) address space we have created, if any, get rid * of our namei data and vnode, and exit noting failure */ uvm_deallocate(&vm->vm_map, VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS); if (pack.ep_interp != NULL) pool_put(&namei_pool, pack.ep_interp); if (pack.ep_emul_arg != NULL) free(pack.ep_emul_arg, M_TEMP, pack.ep_emul_argsize); pool_put(&namei_pool, nid.ni_cnd.cn_pnbuf); vn_close(pack.ep_vp, FREAD, cred, p); km_free(argp, NCARGS, &kv_exec, &kp_pageable); free_pack_abort: free(pack.ep_hdr, M_EXEC, pack.ep_hdrlen); if (pathbuf != NULL) pool_put(&namei_pool, pathbuf); exit1(p, W_EXITCODE(0, SIGABRT), EXIT_NORMAL); /* NOTREACHED */ atomic_clearbits_int(&pr->ps_flags, PS_INEXEC); return (0); }
/* * Handle a single exception. */ void itsa(struct trap_frame *trapframe, struct cpu_info *ci, struct proc *p, int type) { int i; unsigned ucode = 0; vm_prot_t ftype; extern vaddr_t onfault_table[]; int onfault; int typ = 0; union sigval sv; struct pcb *pcb; switch (type) { case T_TLB_MOD: /* check for kernel address */ if (trapframe->badvaddr < 0) { pt_entry_t *pte, entry; paddr_t pa; vm_page_t pg; pte = kvtopte(trapframe->badvaddr); entry = *pte; #ifdef DIAGNOSTIC if (!(entry & PG_V) || (entry & PG_M)) panic("trap: ktlbmod: invalid pte"); #endif if (pmap_is_page_ro(pmap_kernel(), trunc_page(trapframe->badvaddr), entry)) { /* write to read only page in the kernel */ ftype = VM_PROT_WRITE; pcb = &p->p_addr->u_pcb; goto kernel_fault; } entry |= PG_M; *pte = entry; KERNEL_LOCK(); pmap_update_kernel_page(trapframe->badvaddr & ~PGOFSET, entry); pa = pfn_to_pad(entry); pg = PHYS_TO_VM_PAGE(pa); if (pg == NULL) panic("trap: ktlbmod: unmanaged page"); pmap_set_modify(pg); KERNEL_UNLOCK(); return; } /* FALLTHROUGH */ case T_TLB_MOD+T_USER: { pt_entry_t *pte, entry; paddr_t pa; vm_page_t pg; pmap_t pmap = p->p_vmspace->vm_map.pmap; if (!(pte = pmap_segmap(pmap, trapframe->badvaddr))) panic("trap: utlbmod: invalid segmap"); pte += uvtopte(trapframe->badvaddr); entry = *pte; #ifdef DIAGNOSTIC if (!(entry & PG_V) || (entry & PG_M)) panic("trap: utlbmod: invalid pte"); #endif if (pmap_is_page_ro(pmap, trunc_page(trapframe->badvaddr), entry)) { /* write to read only page */ ftype = VM_PROT_WRITE; pcb = &p->p_addr->u_pcb; goto fault_common_no_miss; } entry |= PG_M; *pte = entry; KERNEL_LOCK(); pmap_update_user_page(pmap, (trapframe->badvaddr & ~PGOFSET), entry); pa = pfn_to_pad(entry); pg = PHYS_TO_VM_PAGE(pa); if (pg == NULL) panic("trap: utlbmod: unmanaged page"); pmap_set_modify(pg); KERNEL_UNLOCK(); return; } case T_TLB_LD_MISS: case T_TLB_ST_MISS: ftype = (type == T_TLB_ST_MISS) ? VM_PROT_WRITE : VM_PROT_READ; pcb = &p->p_addr->u_pcb; /* check for kernel address */ if (trapframe->badvaddr < 0) { vaddr_t va; int rv; kernel_fault: va = trunc_page((vaddr_t)trapframe->badvaddr); onfault = pcb->pcb_onfault; pcb->pcb_onfault = 0; KERNEL_LOCK(); rv = uvm_fault(kernel_map, trunc_page(va), 0, ftype); KERNEL_UNLOCK(); pcb->pcb_onfault = onfault; if (rv == 0) return; if (onfault != 0) { pcb->pcb_onfault = 0; trapframe->pc = onfault_table[onfault]; return; } goto err; } /* * It is an error for the kernel to access user space except * through the copyin/copyout routines. */ if (pcb->pcb_onfault != 0) { /* * We want to resolve the TLB fault before invoking * pcb_onfault if necessary. */ goto fault_common; } else { goto err; } case T_TLB_LD_MISS+T_USER: ftype = VM_PROT_READ; pcb = &p->p_addr->u_pcb; goto fault_common; case T_TLB_ST_MISS+T_USER: ftype = VM_PROT_WRITE; pcb = &p->p_addr->u_pcb; fault_common: #ifdef CPU_R4000 if (r4000_errata != 0) { if (eop_tlb_miss_handler(trapframe, ci, p) != 0) return; } #endif fault_common_no_miss: #ifdef CPU_R4000 if (r4000_errata != 0) { eop_cleanup(trapframe, p); } #endif { vaddr_t va; struct vmspace *vm; vm_map_t map; int rv; vm = p->p_vmspace; map = &vm->vm_map; va = trunc_page((vaddr_t)trapframe->badvaddr); onfault = pcb->pcb_onfault; pcb->pcb_onfault = 0; KERNEL_LOCK(); rv = uvm_fault(map, va, 0, ftype); pcb->pcb_onfault = onfault; /* * If this was a stack access we keep track of the maximum * accessed stack size. Also, if vm_fault gets a protection * failure it is due to accessing the stack region outside * the current limit and we need to reflect that as an access * error. */ if ((caddr_t)va >= vm->vm_maxsaddr) { if (rv == 0) uvm_grow(p, va); else if (rv == EACCES) rv = EFAULT; } KERNEL_UNLOCK(); if (rv == 0) return; if (!USERMODE(trapframe->sr)) { if (onfault != 0) { pcb->pcb_onfault = 0; trapframe->pc = onfault_table[onfault]; return; } goto err; } ucode = ftype; i = SIGSEGV; typ = SEGV_MAPERR; break; } case T_ADDR_ERR_LD+T_USER: /* misaligned or kseg access */ case T_ADDR_ERR_ST+T_USER: /* misaligned or kseg access */ ucode = 0; /* XXX should be VM_PROT_something */ i = SIGBUS; typ = BUS_ADRALN; break; case T_BUS_ERR_IFETCH+T_USER: /* BERR asserted to cpu */ case T_BUS_ERR_LD_ST+T_USER: /* BERR asserted to cpu */ ucode = 0; /* XXX should be VM_PROT_something */ i = SIGBUS; typ = BUS_OBJERR; break; case T_SYSCALL+T_USER: { struct trap_frame *locr0 = p->p_md.md_regs; struct sysent *callp; unsigned int code; register_t tpc; int numsys, error; struct args { register_t i[8]; } args; register_t rval[2]; atomic_add_int(&uvmexp.syscalls, 1); /* compute next PC after syscall instruction */ tpc = trapframe->pc; /* Remember if restart */ if (trapframe->cause & CR_BR_DELAY) locr0->pc = MipsEmulateBranch(locr0, trapframe->pc, 0, 0); else locr0->pc += 4; callp = p->p_p->ps_emul->e_sysent; numsys = p->p_p->ps_emul->e_nsysent; code = locr0->v0; switch (code) { case SYS_syscall: case SYS___syscall: /* * Code is first argument, followed by actual args. * __syscall provides the code as a quad to maintain * proper alignment of 64-bit arguments on 32-bit * platforms, which doesn't change anything here. */ code = locr0->a0; if (code >= numsys) callp += p->p_p->ps_emul->e_nosys; /* (illegal) */ else callp += code; i = callp->sy_argsize / sizeof(register_t); args.i[0] = locr0->a1; args.i[1] = locr0->a2; args.i[2] = locr0->a3; if (i > 3) { args.i[3] = locr0->a4; args.i[4] = locr0->a5; args.i[5] = locr0->a6; args.i[6] = locr0->a7; if (i > 7) if ((error = copyin((void *)locr0->sp, &args.i[7], sizeof(register_t)))) goto bad; } break; default: if (code >= numsys) callp += p->p_p->ps_emul->e_nosys; /* (illegal) */ else callp += code; i = callp->sy_narg; args.i[0] = locr0->a0; args.i[1] = locr0->a1; args.i[2] = locr0->a2; args.i[3] = locr0->a3; if (i > 4) { args.i[4] = locr0->a4; args.i[5] = locr0->a5; args.i[6] = locr0->a6; args.i[7] = locr0->a7; } } rval[0] = 0; rval[1] = locr0->v1; #if defined(DDB) || defined(DEBUG) trapdebug[TRAPSIZE * ci->ci_cpuid + (trppos[ci->ci_cpuid] == 0 ? TRAPSIZE : trppos[ci->ci_cpuid]) - 1].code = code; #endif error = mi_syscall(p, code, callp, args.i, rval); switch (error) { case 0: locr0->v0 = rval[0]; locr0->v1 = rval[1]; locr0->a3 = 0; break; case ERESTART: locr0->pc = tpc; break; case EJUSTRETURN: break; /* nothing to do */ default: bad: locr0->v0 = error; locr0->a3 = 1; } mi_syscall_return(p, code, error, rval); return; } case T_BREAK: #ifdef DDB kdb_trap(type, trapframe); #endif /* Reenable interrupts if necessary */ if (trapframe->sr & SR_INT_ENAB) { enableintr(); } return; case T_BREAK+T_USER: { caddr_t va; u_int32_t instr; struct trap_frame *locr0 = p->p_md.md_regs; /* compute address of break instruction */ va = (caddr_t)trapframe->pc; if (trapframe->cause & CR_BR_DELAY) va += 4; /* read break instruction */ copyin(va, &instr, sizeof(int32_t)); switch ((instr & BREAK_VAL_MASK) >> BREAK_VAL_SHIFT) { case 6: /* gcc range error */ i = SIGFPE; typ = FPE_FLTSUB; /* skip instruction */ if (trapframe->cause & CR_BR_DELAY) locr0->pc = MipsEmulateBranch(locr0, trapframe->pc, 0, 0); else locr0->pc += 4; break; case 7: /* gcc3 divide by zero */ i = SIGFPE; typ = FPE_INTDIV; /* skip instruction */ if (trapframe->cause & CR_BR_DELAY) locr0->pc = MipsEmulateBranch(locr0, trapframe->pc, 0, 0); else locr0->pc += 4; break; #ifdef PTRACE case BREAK_SSTEP_VAL: if (p->p_md.md_ss_addr == (long)va) { #ifdef DEBUG printf("trap: %s (%d): breakpoint at %p " "(insn %08x)\n", p->p_comm, p->p_pid, (void *)p->p_md.md_ss_addr, p->p_md.md_ss_instr); #endif /* Restore original instruction and clear BP */ process_sstep(p, 0); typ = TRAP_BRKPT; } else { typ = TRAP_TRACE; } i = SIGTRAP; break; #endif #ifdef FPUEMUL case BREAK_FPUEMUL_VAL: /* * If this is a genuine FP emulation break, * resume execution to our branch destination. */ if ((p->p_md.md_flags & MDP_FPUSED) != 0 && p->p_md.md_fppgva + 4 == (vaddr_t)va) { struct vm_map *map = &p->p_vmspace->vm_map; p->p_md.md_flags &= ~MDP_FPUSED; locr0->pc = p->p_md.md_fpbranchva; /* * Prevent access to the relocation page. * XXX needs to be fixed to work with rthreads */ uvm_fault_unwire(map, p->p_md.md_fppgva, p->p_md.md_fppgva + PAGE_SIZE); (void)uvm_map_protect(map, p->p_md.md_fppgva, p->p_md.md_fppgva + PAGE_SIZE, UVM_PROT_NONE, FALSE); return; } /* FALLTHROUGH */ #endif default: typ = TRAP_TRACE; i = SIGTRAP; break; } break; } case T_IWATCH+T_USER: case T_DWATCH+T_USER: { caddr_t va; /* compute address of trapped instruction */ va = (caddr_t)trapframe->pc; if (trapframe->cause & CR_BR_DELAY) va += 4; printf("watch exception @ %p\n", va); #ifdef RM7K_PERFCNTR if (rm7k_watchintr(trapframe)) { /* Return to user, don't add any more overhead */ return; } #endif i = SIGTRAP; typ = TRAP_BRKPT; break; } case T_TRAP+T_USER: { caddr_t va; u_int32_t instr; struct trap_frame *locr0 = p->p_md.md_regs; /* compute address of trap instruction */ va = (caddr_t)trapframe->pc; if (trapframe->cause & CR_BR_DELAY) va += 4; /* read break instruction */ copyin(va, &instr, sizeof(int32_t)); if (trapframe->cause & CR_BR_DELAY) locr0->pc = MipsEmulateBranch(locr0, trapframe->pc, 0, 0); else locr0->pc += 4; #ifdef RM7K_PERFCNTR if (instr == 0x040c0000) { /* Performance cntr trap */ int result; result = rm7k_perfcntr(trapframe->a0, trapframe->a1, trapframe->a2, trapframe->a3); locr0->v0 = -result; /* Return to user, don't add any more overhead */ return; } else #endif /* * GCC 4 uses teq with code 7 to signal divide by * zero at runtime. This is one instruction shorter * than the BEQ + BREAK combination used by gcc 3. */ if ((instr & 0xfc00003f) == 0x00000034 /* teq */ && (instr & 0x001fffc0) == ((ZERO << 16) | (7 << 6))) { i = SIGFPE; typ = FPE_INTDIV; } else { i = SIGEMT; /* Stuff it with something for now */ typ = 0; } break; } case T_RES_INST+T_USER: i = SIGILL; typ = ILL_ILLOPC; break; case T_COP_UNUSABLE+T_USER: /* * Note MIPS IV COP1X instructions issued with FPU * disabled correctly report coprocessor 1 as the * unusable coprocessor number. */ if ((trapframe->cause & CR_COP_ERR) != CR_COP1_ERR) { i = SIGILL; /* only FPU instructions allowed */ typ = ILL_ILLOPC; break; } #ifdef FPUEMUL MipsFPTrap(trapframe); #else enable_fpu(p); #endif return; case T_FPE: printf("FPU Trap: PC %lx CR %lx SR %lx\n", trapframe->pc, trapframe->cause, trapframe->sr); goto err; case T_FPE+T_USER: MipsFPTrap(trapframe); return; case T_OVFLOW+T_USER: i = SIGFPE; typ = FPE_FLTOVF; break; case T_ADDR_ERR_LD: /* misaligned access */ case T_ADDR_ERR_ST: /* misaligned access */ case T_BUS_ERR_LD_ST: /* BERR asserted to cpu */ pcb = &p->p_addr->u_pcb; if ((onfault = pcb->pcb_onfault) != 0) { pcb->pcb_onfault = 0; trapframe->pc = onfault_table[onfault]; return; } goto err; default: err: disableintr(); #if !defined(DDB) && defined(DEBUG) trapDump("trap", printf); #endif printf("\nTrap cause = %d Frame %p\n", type, trapframe); printf("Trap PC %p RA %p fault %p\n", (void *)trapframe->pc, (void *)trapframe->ra, (void *)trapframe->badvaddr); #ifdef DDB stacktrace(!USERMODE(trapframe->sr) ? trapframe : p->p_md.md_regs); kdb_trap(type, trapframe); #endif panic("trap"); } #ifdef FPUEMUL /* * If a relocated delay slot causes an exception, blame the * original delay slot address - userland is not supposed to * know anything about emulation bowels. */ if ((p->p_md.md_flags & MDP_FPUSED) != 0 && trapframe->badvaddr == p->p_md.md_fppgva) trapframe->badvaddr = p->p_md.md_fpslotva; #endif p->p_md.md_regs->pc = trapframe->pc; p->p_md.md_regs->cause = trapframe->cause; p->p_md.md_regs->badvaddr = trapframe->badvaddr; sv.sival_ptr = (void *)trapframe->badvaddr; KERNEL_LOCK(); trapsignal(p, i, ucode, typ, sv); KERNEL_UNLOCK(); }
/* * Trap is called from locore to handle most types of processor traps. */ void trap(unsigned int status, unsigned int cause, vaddr_t vaddr, vaddr_t opc, struct trapframe *frame) { int type; struct lwp *l = curlwp; struct proc *p = curproc; vm_prot_t ftype; ksiginfo_t ksi; struct frame *fp; extern void fswintrberr(void); KSI_INIT_TRAP(&ksi); uvmexp.traps++; if ((type = TRAPTYPE(cause)) >= LENGTH(trap_type)) panic("trap: unknown trap type %d", type); if (USERMODE(status)) { type |= T_USER; LWP_CACHE_CREDS(l, p); } /* Enable interrupts just at it was before the trap. */ _splset(status & AVR32_STATUS_IMx); switch (type) { default: dopanic: (void)splhigh(); printf("trap: %s in %s mode\n", trap_type[TRAPTYPE(cause)], USERMODE(status) ? "user" : "kernel"); printf("status=0x%x, cause=0x%x, epc=%#lx, vaddr=%#lx\n", status, cause, opc, vaddr); if (curlwp != NULL) { fp = (struct frame *)l->l_md.md_regs; printf("pid=%d cmd=%s usp=0x%x ", p->p_pid, p->p_comm, (int)fp->f_regs[_R_SP]); } else printf("curlwp == NULL "); printf("ksp=%p\n", &status); #if defined(DDB) kdb_trap(type, (mips_reg_t *) frame); /* XXX force halt XXX */ #elif defined(KGDB) { struct frame *f = (struct frame *)&ddb_regs; extern mips_reg_t kgdb_cause, kgdb_vaddr; kgdb_cause = cause; kgdb_vaddr = vaddr; /* * init global ddb_regs, used in db_interface.c routines * shared between ddb and gdb. Send ddb_regs to gdb so * that db_machdep.h macros will work with it, and * allow gdb to alter the PC. */ db_set_ddb_regs(type, (mips_reg_t *) frame); PC_BREAK_ADVANCE(f); if (kgdb_trap(type, &ddb_regs)) { ((mips_reg_t *)frame)[21] = f->f_regs[_R_PC]; return; } } #else panic("trap: dopanic: notyet"); #endif /*NOTREACHED*/ case T_TLB_MOD: panic("trap: T_TLB_MOD: notyet"); #if notyet if (KERNLAND(vaddr)) { pt_entry_t *pte; unsigned entry; paddr_t pa; pte = kvtopte(vaddr); entry = pte->pt_entry; if (!avr32_pte_v(entry) /*|| (entry & mips_pg_m_bit())*/) { panic("ktlbmod: invalid pte"); } if (entry & avr32_pte_ropage_bit()) { /* write to read only page in the kernel */ ftype = VM_PROT_WRITE; goto kernelfault; } entry |= mips_pg_m_bit(); /* XXXAVR32 Do it on tlbarlo/ tlbarhi? */ pte->pt_entry = entry; vaddr &= ~PGOFSET; MachTLBUpdate(vaddr, entry); pa = avr32_tlbpfn_to_paddr(entry); if (!IS_VM_PHYSADDR(pa)) { printf("ktlbmod: va %#lx pa %#llx\n", vaddr, (long long)pa); panic("ktlbmod: unmanaged page"); } pmap_set_modified(pa); return; /* KERN */ } /*FALLTHROUGH*/ #endif case T_TLB_MOD+T_USER: panic("trap: T_TLB_MOD+T_USER: notyet"); #if notyet { pt_entry_t *pte; unsigned entry; paddr_t pa; pmap_t pmap; pmap = p->p_vmspace->vm_map.pmap; if (!(pte = pmap_segmap(pmap, vaddr))) panic("utlbmod: invalid segmap"); pte += (vaddr >> PGSHIFT) & (NPTEPG - 1); entry = pte->pt_entry; if (!avr32_pte_v(entry)) panic("utlbmod: invalid pte"); if (entry & avr32_pte_ropage_bit()) { /* write to read only page */ ftype = VM_PROT_WRITE; goto pagefault; } /* entry |= mips_pg_m_bit(); XXXAVR32 Do it on tlbarlo/ tlbarhi? */ pte->pt_entry = entry; vaddr = (vaddr & ~PGOFSET) | (pmap->pm_asid << AVR32_TLB_PID_SHIFT); MachTLBUpdate(vaddr, entry); pa = avr32_tlbpfn_to_paddr(entry); if (!IS_VM_PHYSADDR(pa)) { printf("utlbmod: va %#lx pa %#llx\n", vaddr, (long long)pa); panic("utlbmod: unmanaged page"); } pmap_set_modified(pa); if (type & T_USER) userret(l); return; /* GEN */ } #endif case T_TLB_LD_MISS: panic("trap: T_TLB_LD_MISS: notyet"); case T_TLB_ST_MISS: ftype = (type == T_TLB_LD_MISS) ? VM_PROT_READ : VM_PROT_WRITE; if (KERNLAND(vaddr)) goto kernelfault; panic("trap: T_TLB_ST_MISS: notyet"); #if notyet /* * It is an error for the kernel to access user space except * through the copyin/copyout routines. */ if (l == NULL || l->l_addr->u_pcb.pcb_onfault == NULL) goto dopanic; /* check for fuswintr() or suswintr() getting a page fault */ if (l->l_addr->u_pcb.pcb_onfault == (void *)fswintrberr) { frame->tf_regs[TF_EPC] = (int)fswintrberr; return; /* KERN */ } goto pagefault; #endif case T_TLB_LD_MISS+T_USER: panic("trap: T_TLB_LD_MISS+T_USER: notyet"); #if notyet ftype = VM_PROT_READ; goto pagefault; #endif case T_TLB_ST_MISS+T_USER: panic("trap: T_TLB_ST_MISS+T_USER: notyet"); #if notyet ftype = VM_PROT_WRITE; #endif pagefault: ; { vaddr_t va; struct vmspace *vm; struct vm_map *map; int rv; vm = p->p_vmspace; map = &vm->vm_map; va = trunc_page(vaddr); if ((l->l_flag & LW_SA) && (~l->l_pflag & LP_SA_NOBLOCK)) { l->l_savp->savp_faultaddr = (vaddr_t)vaddr; l->l_pflag |= LP_SA_PAGEFAULT; } if (p->p_emul->e_fault) rv = (*p->p_emul->e_fault)(p, va, ftype); else rv = uvm_fault(map, va, ftype); #ifdef VMFAULT_TRACE printf( "uvm_fault(%p (pmap %p), %lx (0x%x), %d) -> %d at pc %p\n", map, vm->vm_map.pmap, va, vaddr, ftype, rv, (void*)opc); #endif /* * If this was a stack access we keep track of the maximum * accessed stack size. Also, if vm_fault gets a protection * failure it is due to accessing the stack region outside * the current limit and we need to reflect that as an access * error. */ if ((void *)va >= vm->vm_maxsaddr) { if (rv == 0){ uvm_grow(p, va); } else if (rv == EACCES) rv = EFAULT; } l->l_pflag &= ~LP_SA_PAGEFAULT; if (rv == 0) { if (type & T_USER) { userret(l); } return; /* GEN */ } if ((type & T_USER) == 0) goto copyfault; if (rv == ENOMEM) { printf("UVM: pid %d (%s), uid %d killed: out of swap\n", p->p_pid, p->p_comm, l->l_cred ? kauth_cred_geteuid(l->l_cred) : (uid_t) -1); ksi.ksi_signo = SIGKILL; ksi.ksi_code = 0; } else { if (rv == EACCES) { ksi.ksi_signo = SIGBUS; ksi.ksi_code = BUS_OBJERR; } else { ksi.ksi_signo = SIGSEGV; ksi.ksi_code = SEGV_MAPERR; } } ksi.ksi_trap = type & ~T_USER; ksi.ksi_addr = (void *)vaddr; break; /* SIGNAL */ } kernelfault: ; { vaddr_t va; int rv; va = trunc_page(vaddr); rv = uvm_fault(kernel_map, va, ftype); if (rv == 0) return; /* KERN */ /*FALLTHROUGH*/ } case T_ADDR_ERR_LD: /* misaligned access */ case T_ADDR_ERR_ST: /* misaligned access */ case T_BUS_ERR_LD_ST: /* BERR asserted to CPU */ copyfault: panic("trap: copyfault: notyet"); #if notyet if (l == NULL || l->l_addr->u_pcb.pcb_onfault == NULL) goto dopanic; frame->tf_regs[TF_EPC] = (intptr_t)l->l_addr->u_pcb.pcb_onfault; return; /* KERN */ #endif #if notyet case T_ADDR_ERR_LD+T_USER: /* misaligned or kseg access */ case T_ADDR_ERR_ST+T_USER: /* misaligned or kseg access */ case T_BUS_ERR_IFETCH+T_USER: /* BERR asserted to CPU */ case T_BUS_ERR_LD_ST+T_USER: /* BERR asserted to CPU */ ksi.ksi_trap = type & ~T_USER; ksi.ksi_signo = SIGSEGV; /* XXX */ ksi.ksi_addr = (void *)vaddr; ksi.ksi_code = SEGV_MAPERR; /* XXX */ break; /* SIGNAL */ case T_BREAK: panic("trap: T_BREAK: notyet"); #if defined(DDB) kdb_trap(type, (avr32_reg_t *) frame); return; /* KERN */ #elif defined(KGDB) { struct frame *f = (struct frame *)&ddb_regs; extern avr32_reg_t kgdb_cause, kgdb_vaddr; kgdb_cause = cause; kgdb_vaddr = vaddr; /* * init global ddb_regs, used in db_interface.c routines * shared between ddb and gdb. Send ddb_regs to gdb so * that db_machdep.h macros will work with it, and * allow gdb to alter the PC. */ db_set_ddb_regs(type, (avr32_reg_t *) frame); PC_BREAK_ADVANCE(f); if (!kgdb_trap(type, &ddb_regs)) printf("kgdb: ignored %s\n", trap_type[TRAPTYPE(cause)]); else ((avr32_reg_t *)frame)[21] = f->f_regs[_R_PC]; return; } #else goto dopanic; #endif case T_BREAK+T_USER: { vaddr_t va; uint32_t instr; int rv; /* compute address of break instruction */ va = (DELAYBRANCH(cause)) ? opc + sizeof(int) : opc; /* read break instruction */ instr = fuiword((void *)va); if (l->l_md.md_ss_addr != va || instr != MIPS_BREAK_SSTEP) { ksi.ksi_trap = type & ~T_USER; ksi.ksi_signo = SIGTRAP; ksi.ksi_addr = (void *)va; ksi.ksi_code = TRAP_TRACE; break; } /* * Restore original instruction and clear BP */ rv = suiword((void *)va, l->l_md.md_ss_instr); if (rv < 0) { vaddr_t sa, ea; sa = trunc_page(va); ea = round_page(va + sizeof(int) - 1); rv = uvm_map_protect(&p->p_vmspace->vm_map, sa, ea, VM_PROT_ALL, false); if (rv == 0) { rv = suiword((void *)va, l->l_md.md_ss_instr); (void)uvm_map_protect(&p->p_vmspace->vm_map, sa, ea, VM_PROT_READ|VM_PROT_EXECUTE, false); } } mips_icache_sync_all(); /* XXXJRT -- necessary? */ mips_dcache_wbinv_all(); /* XXXJRT -- necessary? */ if (rv < 0) printf("Warning: can't restore instruction at 0x%lx: 0x%x\n", l->l_md.md_ss_addr, l->l_md.md_ss_instr); l->l_md.md_ss_addr = 0; ksi.ksi_trap = type & ~T_USER; ksi.ksi_signo = SIGTRAP; ksi.ksi_addr = (void *)va; ksi.ksi_code = TRAP_BRKPT; break; /* SIGNAL */ } case T_RES_INST+T_USER: case T_COP_UNUSABLE+T_USER: #if !defined(SOFTFLOAT) && !defined(NOFPU) if ((cause & MIPS_CR_COP_ERR) == 0x10000000) { struct frame *f; f = (struct frame *)l->l_md.md_regs; savefpregs(fpcurlwp); /* yield FPA */ loadfpregs(l); /* load FPA */ fpcurlwp = l; l->l_md.md_flags |= MDP_FPUSED; f->f_regs[_R_SR] |= MIPS_SR_COP_1_BIT; } else #endif { MachEmulateInst(status, cause, opc, l->l_md.md_regs); } userret(l); return; /* GEN */ case T_FPE+T_USER: panic ("trap: T_FPE+T_USER: notyet"); #if defined(SOFTFLOAT) MachEmulateInst(status, cause, opc, l->l_md.md_regs); #elif !defined(NOFPU) MachFPTrap(status, cause, opc, l->l_md.md_regs); #endif userret(l); return; /* GEN */ case T_OVFLOW+T_USER: case T_TRAP+T_USER: ksi.ksi_trap = type & ~T_USER; ksi.ksi_signo = SIGFPE; fp = (struct frame *)l->l_md.md_regs; ksi.ksi_addr = (void *)fp->f_regs[_R_PC]; ksi.ksi_code = FPE_FLTOVF; /* XXX */ break; /* SIGNAL */ #endif } panic("trap: post-switch: notyet"); #if notyet fp = (struct frame *)l->l_md.md_regs; fp->f_regs[_R_CAUSE] = cause; fp->f_regs[_R_BADVADDR] = vaddr; (*p->p_emul->e_trapsignal)(l, &ksi); if ((type & T_USER) == 0) panic("trapsignal"); userret(l); #endif return; }
/* * cpu_startup: allocate memory for variable-sized tables, * initialize cpu, and do autoconfiguration. */ void cpu_startup() { extern char *kernel_text, *etext; unsigned i; caddr_t v; int base, residual; u_quad_t vmememsize; vaddr_t minaddr, maxaddr; vsize_t size; #ifdef DEBUG extern int pmapdebug; int opmapdebug = pmapdebug; pmapdebug = 0; #endif /* * Initialize the kernel crash dump header. */ cpu_init_kcore_hdr(); /* * Good {morning,afternoon,evening,night}. */ printf(version); identifycpu(); printf("real mem = %d", ctob(physmem)); for (vmememsize = 0, i = 1; i < mem_cluster_cnt; i++) vmememsize += mem_clusters[i].size; if (vmememsize != 0) printf(" (%qu on-board, %qu VMEbus)", mem_clusters[0].size, vmememsize); printf("\n"); /* * Find out how much space we need, allocate it, * and then give everything true virtual addresses. */ size = (vsize_t)allocsys((caddr_t)0); if ((v = (caddr_t)uvm_km_zalloc(kernel_map, round_page(size))) == 0) panic("startup: no room for tables"); if ((allocsys(v) - v) != size) panic("startup: talbe size inconsistency"); /* * Now allocate buffers proper. They are different than the above * in that they usually occupy more virtual memory than physical. */ size = MAXBSIZE * nbuf; if (uvm_map(kernel_map, (vaddr_t *) &buffers, round_page(size), NULL, UVM_UNKNOWN_OFFSET, UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_NONE, UVM_INH_NONE, UVM_ADV_NORMAL, 0)) != KERN_SUCCESS) panic("startup: cannot allocate VM for buffers"); minaddr = (vaddr_t)buffers; base = bufpages / nbuf; residual = bufpages % nbuf; for (i = 0; i < nbuf; i++) { vsize_t curbufsize; vaddr_t curbuf; struct vm_page *pg; /* * Each buffer has MAXBSIZE bytes of VM space allocated. Of * that MAXBSIZE space, we allocate and map (base+1) pages * for the first "residual" buffers, and then we allocate * "base" pages for the rest. */ curbuf = (vaddr_t) buffers + (i * MAXBSIZE); curbufsize = CLBYTES * ((i < residual) ? (base+1) : base); while (curbufsize) { pg = uvm_pagealloc(NULL, 0, NULL, 0); if (pg == NULL) panic("cpu_startup: not enough memory for " "buffer cache"); #ifdef PMAP_NEW pmap_kenter_pgs(curbuf, &pg, 1); #else pmap_enter(kernel_map->pmap, curbuf, VM_PAGE_TO_PHYS(pg), VM_PROT_READ|VM_PROT_WRITE, TRUE, VM_PROT_READ|VM_PROT_WRITE); #endif curbuf += PAGE_SIZE; curbufsize -= PAGE_SIZE; } } /* * Allocate a submap for exec arguments. This map effectively * limits the number of processes exec'ing at any time. */ exec_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, 16*NCARGS, TRUE, FALSE, NULL); /* * Allocate a submap for physio */ phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, VM_PHYS_SIZE, TRUE, FALSE, NULL); /* * Finally, allocate mbuf cluster submap. */ mb_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, VM_MBUF_SIZE, FALSE, FALSE, NULL); /* * Initialize callouts */ callfree = callout; for (i = 1; i < ncallout; i++) callout[i-1].c_next = &callout[i]; callout[i-1].c_next = NULL; #ifdef DEBUG pmapdebug = opmapdebug; #endif printf("avail mem = %ld\n", ptoa(uvmexp.free)); printf("using %d buffers containing %d bytes of memory\n", nbuf, bufpages * CLBYTES); /* * Tell the VM system that the area before the text segment * is invalid. * * XXX Should just change KERNBASE and VM_MIN_KERNEL_ADDRESS, * XXX but not right now. */ if (uvm_map_protect(kernel_map, 0, round_page(&kernel_text), UVM_PROT_NONE, TRUE) != KERN_SUCCESS) panic("can't mark pre-text pages off-limits"); /* * Tell the VM system that writing to the kernel text isn't allowed. * If we don't, we might end up COW'ing the text segment! */ if (uvm_map_protect(kernel_map, trunc_page(&kernel_text), round_page(&etext), UVM_PROT_READ|UVM_PROT_EXEC, TRUE) != KERN_SUCCESS) panic("can't protect kernel text"); /* * Set up CPU-specific registers, cache, etc. */ initcpu(); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); }