On 3/9/22 22:14, John David Anglin wrote: > Currently, the parisc kernel does not fully support non-access TLB > fault handling for probe instructions. In the fast path, we set the > target register to zero if it is not a shadowed register. The slow > path is not implemented, so we call do_page_fault. The architecture > indicates that non-access faults should not cause a page fault from > disk. > > This change adds to code to provide non-access fault support for > probe instructions. It also modifies the handling of faults on > userspace so that if the address lies in a valid VMA and the access > type matches that for the VMA, the probe target register is set to > one. Otherwise, the target register is set to zero. > > This was done to make probe instructions more useful for userspace. > Probe instructions are not very useful if they set the target register > to zero whenever a page is not present in memory. Nominally, the > purpose of the probe instruction is determine whether read or write > access to a given address is allowed. > > This fixes a problem in function pointer comparison noticed in the > glibc testsuite (stdio-common/tst-vfprintf-user-type). The same > problem is likely in glibc (_dl_lookup_address). > > V2 adds flush and lpa instruction support to handle_nadtlb_fault. > > Signed-off-by: John David Anglin <dave.anglin@xxxxxxxx> Thanks Dave! I've applied all of your 3 patches to the for-next branch. Helge > --- > > diff --git a/arch/parisc/include/asm/traps.h b/arch/parisc/include/asm/traps.h > index 34619f010c63..0ccdb738a9a3 100644 > --- a/arch/parisc/include/asm/traps.h > +++ b/arch/parisc/include/asm/traps.h > @@ -18,6 +18,7 @@ unsigned long parisc_acctyp(unsigned long code, unsigned int inst); > const char *trap_name(unsigned long code); > void do_page_fault(struct pt_regs *regs, unsigned long code, > unsigned long address); > +int handle_nadtlb_fault(struct pt_regs *regs); > #endif > > #endif > diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c > index b6fdebddc8e9..39576a9245c7 100644 > --- a/arch/parisc/kernel/traps.c > +++ b/arch/parisc/kernel/traps.c > @@ -662,6 +662,8 @@ void notrace handle_interruption(int code, struct pt_regs *regs) > by hand. Technically we need to emulate: > fdc,fdce,pdc,"fic,4f",prober,probeir,probew, probeiw > */ > + if (code == 17 && handle_nadtlb_fault(regs)) > + return; > fault_address = regs->ior; > fault_space = regs->isr; > break; > diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c > index e9eabf8f14d7..39f813dcb008 100644 > --- a/arch/parisc/mm/fault.c > +++ b/arch/parisc/mm/fault.c > @@ -425,3 +425,92 @@ void do_page_fault(struct pt_regs *regs, unsigned long code, > } > pagefault_out_of_memory(); > } > + > +/* Handle non-access data TLB miss faults. > + * > + * For probe instructions, accesses to userspace are considered allowed > + * if they lie in a valid VMA and the access type matches. We are not > + * allowed to handle MM faults here so there may be situations where an > + * actual access would fail even though a probe was successful. > + */ > +int > +handle_nadtlb_fault(struct pt_regs *regs) > +{ > + unsigned long insn = regs->iir; > + int breg, treg, xreg, val = 0; > + struct vm_area_struct *vma, *prev_vma; > + struct task_struct *tsk; > + struct mm_struct *mm; > + unsigned long address; > + unsigned long acc_type; > + > + switch (insn & 0x380) { > + case 0x280: > + /* FDC instruction */ > + fallthrough; > + case 0x380: > + /* PDC and FIC instructions */ > + if(printk_ratelimit()) { > + pr_warn("BUG: nullifying cache flush/purge instruction\n"); > + show_regs(regs); > + } > + if (insn & 0x20) { > + /* Base modification */ > + breg = (insn >> 21) & 0x1f; > + xreg = (insn >> 16) & 0x1f; > + if (breg && xreg) > + regs->gr[breg] += regs->gr[xreg]; > + } > + regs->gr[0] |= PSW_N; > + return 1; > + > + case 0x180: > + /* PROBE instruction */ > + treg = insn & 0x1f; > + if (regs->isr) { > + tsk = current; > + mm = tsk->mm; > + if (mm) { > + /* Search for VMA */ > + address = regs->ior; > + mmap_read_lock(mm); > + vma = find_vma_prev(mm, address, &prev_vma); > + mmap_read_unlock(mm); > + > + /* > + * Check if access to the VMA is okay. > + * We don't allow for stack expansion. > + */ > + acc_type = (insn & 0x40) ? VM_WRITE : VM_READ; > + if (vma > + && address >= vma->vm_start > + && (vma->vm_flags & acc_type) == acc_type) > + val = 1; > + } > + } > + if (treg) > + regs->gr[treg] = val; > + regs->gr[0] |= PSW_N; > + return 1; > + > + case 0x300: > + /* LPA instruction */ > + if (insn & 0x20) { > + /* Base modification */ > + breg = (insn >> 21) & 0x1f; > + xreg = (insn >> 16) & 0x1f; > + if (breg && xreg) > + regs->gr[breg] += regs->gr[xreg]; > + } > + treg = insn & 0x1f; > + if (treg) > + regs->gr[treg] = 0; > + regs->gr[0] |= PSW_N; > + return 1; > + > + default: > + break; > + } > + > + return 0; > +}