Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04748724 authored by Liam R. Howlett's avatar Liam R. Howlett Committed by David S. Miller
Browse files

sparc64: Handle PIO & MEM non-resumable errors.



User processes trying to access an invalid memory address via PIO will
receive a SIGBUS signal instead of causing a panic.  Memory errors will
receive a SIGKILL since a SIGBUS may result in a coredump which may
attempt to repeat the faulting access.

Signed-off-by: default avatarLiam R. Howlett <Liam.Howlett@Oracle.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7a7dc961
Loading
Loading
Loading
Loading
+73 −0
Original line number Diff line number Diff line
@@ -2051,6 +2051,73 @@ void sun4v_resum_overflow(struct pt_regs *regs)
	atomic_inc(&sun4v_resum_oflow_cnt);
}

/* Given a set of registers, get the virtual addressi that was being accessed
 * by the faulting instructions at tpc.
 */
static unsigned long sun4v_get_vaddr(struct pt_regs *regs)
{
	unsigned int insn;

	if (!copy_from_user(&insn, (void __user *)regs->tpc, 4)) {
		return compute_effective_address(regs, insn,
						 (insn >> 25) & 0x1f);
	}
	return 0;
}

/* Attempt to handle non-resumable errors generated from userspace.
 * Returns true if the signal was handled, false otherwise.
 */
bool sun4v_nonresum_error_user_handled(struct pt_regs *regs,
				  struct sun4v_error_entry *ent) {

	unsigned int attrs = ent->err_attrs;

	if (attrs & SUN4V_ERR_ATTRS_MEMORY) {
		unsigned long addr = ent->err_raddr;
		siginfo_t info;

		if (addr == ~(u64)0) {
			/* This seems highly unlikely to ever occur */
			pr_emerg("SUN4V NON-RECOVERABLE ERROR: Memory error detected in unknown location!\n");
		} else {
			unsigned long page_cnt = DIV_ROUND_UP(ent->err_size,
							      PAGE_SIZE);

			/* Break the unfortunate news. */
			pr_emerg("SUN4V NON-RECOVERABLE ERROR: Memory failed at %016lX\n",
				 addr);
			pr_emerg("SUN4V NON-RECOVERABLE ERROR:   Claiming %lu ages.\n",
				 page_cnt);

			while (page_cnt-- > 0) {
				if (pfn_valid(addr >> PAGE_SHIFT))
					get_page(pfn_to_page(addr >> PAGE_SHIFT));
				addr += PAGE_SIZE;
			}
		}
		info.si_signo = SIGKILL;
		info.si_errno = 0;
		info.si_trapno = 0;
		force_sig_info(info.si_signo, &info, current);

		return true;
	}
	if (attrs & SUN4V_ERR_ATTRS_PIO) {
		siginfo_t info;

		info.si_signo = SIGBUS;
		info.si_code = BUS_ADRERR;
		info.si_addr = (void __user *)sun4v_get_vaddr(regs);
		force_sig_info(info.si_signo, &info, current);

		return true;
	}

	/* Default to doing nothing */
	return false;
}

/* We run with %pil set to PIL_NORMAL_MAX and PSTATE_IE enabled in %pstate.
 * Log the event, clear the first word of the entry, and die.
 */
@@ -2075,6 +2142,12 @@ void sun4v_nonresum_error(struct pt_regs *regs, unsigned long offset)

	put_cpu();

	if (!(regs->tstate & TSTATE_PRIV) &&
	    sun4v_nonresum_error_user_handled(regs, &local_copy)) {
		/* DON'T PANIC: This userspace error was handled. */
		return;
	}

#ifdef CONFIG_PCI
	/* Check for the special PCI poke sequence. */
	if (pci_poke_in_progress && pci_poke_cpu == cpu) {