copyinsn() fetches a userland instruction through the direct map.
This lets emulation work with execute-only virtual memory mappings.
OK deraadt@
-/* $OpenBSD: cpu.h,v 1.140 2022/11/19 16:23:48 cheloha Exp $ */
+/* $OpenBSD: cpu.h,v 1.141 2023/01/11 03:19:52 visa Exp $ */
/*-
* Copyright (c) 1992, 1993
int exec_md_map(struct proc *, struct exec_package *);
void savectx(struct user *, int);
+int copyinsn(struct proc *, vaddr_t, uint32_t *);
void enable_fpu(struct proc *);
void save_fpu(void);
int fpe_branch_emulate(struct proc *, struct trapframe *, uint32_t,
-/* $OpenBSD: pmap.h,v 1.51 2023/01/01 19:49:17 miod Exp $ */
+/* $OpenBSD: pmap.h,v 1.52 2023/01/11 03:19:52 visa Exp $ */
/*
* Copyright (c) 1987 Carnegie-Mellon University
#define PMAP_PREFER_OFFSET(of) ((of) & pmap_prefer_mask)
void pmap_bootstrap(void);
+int pmap_copyinsn(pmap_t, vaddr_t, uint32_t *);
int pmap_emulate_modify(pmap_t, vaddr_t);
void pmap_page_cache(vm_page_t, u_int);
-/* $OpenBSD: fp_emulate.c,v 1.24 2021/03/11 11:16:59 jsg Exp $ */
+/* $OpenBSD: fp_emulate.c,v 1.25 2023/01/11 03:19:52 visa Exp $ */
/*
* Copyright (c) 2010 Miodrag Vallat.
* if it does, it's probably not your lucky day.
*/
- if (copyin32((const void *)pc, &insn) != 0) {
+ if (copyinsn(p, pc, &insn) != 0) {
sig = SIGBUS;
fault_type = BUS_OBJERR;
sv.sival_ptr = (void *)pc;
inst = *(InstFmt *)&insn;
if (tf->cause & CR_BR_DELAY) {
- if (copyin32((const void *)tf->pc, &branch) != 0) {
+ if (copyinsn(p, tf->pc, &branch) != 0) {
sig = SIGBUS;
fault_type = BUS_OBJERR;
sv.sival_ptr = (void *)tf->pc;
*/
/* inline MipsEmulateBranch(tf, tf->pc, tf->fsr, insn)*/
dest = tf->pc + 4 + ((short)inst.IType.imm << 2);
- if (copyin32((const void *)(tf->pc + 4), &dinsn) != 0) {
+ if (copyinsn(p, tf->pc + 4, &dinsn) != 0) {
sv->sival_ptr = (void *)(tf->pc + 4);
return SIGSEGV;
}
-/* $OpenBSD: pmap.c,v 1.123 2023/01/11 03:17:56 visa Exp $ */
+/* $OpenBSD: pmap.c,v 1.124 2023/01/11 03:19:52 visa Exp $ */
/*
* Copyright (c) 2001-2004 Opsycon AB (www.opsycon.se / www.opsycon.com)
{
Mips_SyncICache(curcpu());
}
+
+/*
+ * Read an instruction from a given virtual memory address.
+ * TLB read-inhibition is bypassed.
+ */
+int
+pmap_copyinsn(pmap_t pmap, vaddr_t uva, uint32_t *insn)
+{
+ pt_entry_t *pte;
+ paddr_t pa;
+ int found = 0;
+
+ if (uva >= VM_MAXUSER_ADDRESS || pmap == pmap_kernel()) {
+ panic("%s(%p, %p): invalid params", __func__,
+ pmap, (void *)uva);
+ }
+
+ /*
+ * Read the instruction through the direct map region.
+ *
+ * The pmap lock prevents other threads from changing the mapping
+ * and repurposing the page frame while this thread accesses the
+ * direct map.
+ */
+ pmap_lock(pmap);
+ pte = pmap_pte_lookup(pmap, uva);
+ if (pte != NULL && (*pte & PG_V) != 0 && (*pte & pg_xi) == 0) {
+ pa = pfn_to_pad(*pte) | (uva & PAGE_MASK);
+ *insn = *(uint32_t *)PHYS_TO_XKPHYS(pa, CCA_CACHED);
+ found = 1;
+ }
+ pmap_unlock(pmap);
+
+ return found;
+}
-/* $OpenBSD: trap.c,v 1.163 2023/01/10 17:04:01 miod Exp $ */
+/* $OpenBSD: trap.c,v 1.164 2023/01/11 03:19:52 visa Exp $ */
/*
* Copyright (c) 1988 University of Utah.
tpc = trapframe->pc; /* Remember if restart */
if (trapframe->cause & CR_BR_DELAY) {
/* Get the branch instruction. */
- if (copyin32((const void *)locr0->pc, &branch) != 0) {
+ if (copyinsn(p, locr0->pc, &branch) != 0) {
signal = SIGBUS;
sicode = BUS_OBJERR;
break;
case T_BREAK+T_USER:
{
struct trapframe *locr0 = p->p_md.md_regs;
- caddr_t va;
+ vaddr_t va;
uint32_t branch = 0;
uint32_t instr;
/* compute address of break instruction */
- va = (caddr_t)trapframe->pc;
+ va = trapframe->pc;
if (trapframe->cause & CR_BR_DELAY) {
va += 4;
/* Read branch instruction. */
- if (copyin32((const void *)trapframe->pc,
- &branch) != 0) {
+ if (copyinsn(p, trapframe->pc, &branch) != 0) {
signal = SIGBUS;
sicode = BUS_OBJERR;
break;
}
/* read break instruction */
- if (copyin32((const void *)va, &instr) != 0) {
+ if (copyinsn(p, va, &instr) != 0) {
signal = SIGBUS;
sicode = BUS_OBJERR;
break;
case T_TRAP+T_USER:
{
struct trapframe *locr0 = p->p_md.md_regs;
- caddr_t va;
+ vaddr_t va;
uint32_t branch = 0;
uint32_t instr;
/* compute address of trap instruction */
- va = (caddr_t)trapframe->pc;
+ va = trapframe->pc;
if (trapframe->cause & CR_BR_DELAY) {
va += 4;
/* Read branch instruction. */
- if (copyin32((const void *)trapframe->pc,
- &branch) != 0) {
+ if (copyinsn(p, trapframe->pc, &branch) != 0) {
signal = SIGBUS;
sicode = BUS_OBJERR;
break;
}
/* read break instruction */
- if (copyin32((const void *)va, &instr) != 0) {
+ if (copyinsn(p, va, &instr) != 0) {
signal = SIGBUS;
sicode = BUS_OBJERR;
break;
case T_RES_INST+T_USER:
{
register_t *regs = (register_t *)trapframe;
- caddr_t va;
+ vaddr_t va;
uint32_t branch = 0;
InstFmt inst;
/* Compute the instruction's address. */
- va = (caddr_t)trapframe->pc;
+ va = trapframe->pc;
if (trapframe->cause & CR_BR_DELAY) {
va += 4;
/* Get the branch instruction. */
- if (copyin32((const void *)trapframe->pc,
- &branch) != 0) {
+ if (copyinsn(p, trapframe->pc, &branch) != 0) {
signal = SIGBUS;
sicode = BUS_OBJERR;
break;
}
/* Get the faulting instruction. */
- if (copyin32((const void *)va, &inst.word) != 0) {
+ if (copyinsn(p, va, &inst.word) != 0) {
signal = SIGBUS;
sicode = BUS_OBJERR;
break;
mi_child_return(p);
}
+int
+copyinsn(struct proc *p, vaddr_t uva, uint32_t *insn)
+{
+ struct vm_map *map = &p->p_vmspace->vm_map;
+ int error = 0;
+
+ if (__predict_false(uva >= VM_MAXUSER_ADDRESS || (uva & 3) != 0))
+ return EFAULT;
+
+ do {
+ if (pmap_copyinsn(map->pmap, uva, insn))
+ break;
+ error = uvm_fault(map, trunc_page(uva), 0, PROT_EXEC);
+ } while (error == 0);
+
+ return error;
+}
+
#if defined(DDB) || defined(DEBUG)
void
trapDump(const char *msg, int (*pr)(const char *, ...))