Re: [PATCH, V2] parisc: Fix handling off probe non-access faults

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
> +}





[Index of Archives]     [Linux SoC]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux