void cheetah_deferred_handler()

in kernel/traps_64.c [1572:1757]


void cheetah_deferred_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
{
	struct cheetah_err_info local_snapshot, *p;
	int recoverable, is_memory;

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

		/* Re-enable I-cache/D-cache */
		__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
				     "or %%g1, %1, %%g1\n\t"
				     "stxa %%g1, [%%g0] %0\n\t"
				     "membar #Sync"
				     : /* no outputs */
				     : "i" (ASI_DCU_CONTROL_REG),
				       "i" (DCU_DC | DCU_IC)
				     : "g1");

		/* Re-enable error reporting */
		__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
				     "or %%g1, %1, %%g1\n\t"
				     "stxa %%g1, [%%g0] %0\n\t"
				     "membar #Sync"
				     : /* no outputs */
				     : "i" (ASI_ESTATE_ERROR_EN),
				       "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
				     : "g1");

		(void) cheetah_recheck_errors(NULL);

		pci_poke_faulted = 1;
		regs->tpc += 4;
		regs->tnpc = regs->tpc + 4;
		return;
	}
#endif

	p = cheetah_get_error_log(afsr);
	if (!p) {
		prom_printf("ERROR: Early deferred error afsr[%016lx] afar[%016lx]\n",
			    afsr, afar);
		prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
			    smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
		prom_halt();
	}

	/* Grab snapshot of logged error. */
	memcpy(&local_snapshot, p, sizeof(local_snapshot));

	/* If the current trap snapshot does not match what the
	 * trap handler passed along into our args, big trouble.
	 * In such a case, mark the local copy as invalid.
	 *
	 * Else, it matches and we mark the afsr in the non-local
	 * copy as invalid so we may log new error traps there.
	 */
	if (p->afsr != afsr || p->afar != afar)
		local_snapshot.afsr = CHAFSR_INVALID;
	else
		p->afsr = CHAFSR_INVALID;

	is_memory = cheetah_check_main_memory(afar);

	{
		int flush_all, flush_line;

		flush_all = flush_line = 0;
		if ((afsr & CHAFSR_EDU) != 0UL) {
			if ((afsr & cheetah_afsr_errors) == CHAFSR_EDU)
				flush_line = 1;
			else
				flush_all = 1;
		} else if ((afsr & CHAFSR_BERR) != 0UL) {
			if ((afsr & cheetah_afsr_errors) == CHAFSR_BERR)
				flush_line = 1;
			else
				flush_all = 1;
		}

		cheetah_flush_icache();
		cheetah_flush_dcache();

		/* Re-enable I/D caches */
		__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
				     "or %%g1, %1, %%g1\n\t"
				     "stxa %%g1, [%%g0] %0\n\t"
				     "membar #Sync"
				     : /* no outputs */
				     : "i" (ASI_DCU_CONTROL_REG),
				     "i" (DCU_IC | DCU_DC)
				     : "g1");

		if (flush_all)
			cheetah_flush_ecache();
		else if (flush_line)
			cheetah_flush_ecache_line(afar);
	}

	/* Re-enable error reporting */
	__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
			     "or %%g1, %1, %%g1\n\t"
			     "stxa %%g1, [%%g0] %0\n\t"
			     "membar #Sync"
			     : /* no outputs */
			     : "i" (ASI_ESTATE_ERROR_EN),
			     "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
			     : "g1");

	/* Decide if we can continue after handling this trap and
	 * logging the error.
	 */
	recoverable = 1;
	if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
		recoverable = 0;

	/* Re-check AFSR/AFAR.  What we are looking for here is whether a new
	 * error was logged while we had error reporting traps disabled.
	 */
	if (cheetah_recheck_errors(&local_snapshot)) {
		unsigned long new_afsr = local_snapshot.afsr;

		/* If we got a new asynchronous error, die... */
		if (new_afsr & (CHAFSR_EMU | CHAFSR_EDU |
				CHAFSR_WDU | CHAFSR_CPU |
				CHAFSR_IVU | CHAFSR_UE |
				CHAFSR_BERR | CHAFSR_TO))
			recoverable = 0;
	}

	/* Log errors. */
	cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);

	/* "Recoverable" here means we try to yank the page from ever
	 * being newly used again.  This depends upon a few things:
	 * 1) Must be main memory, and AFAR must be valid.
	 * 2) If we trapped from user, OK.
	 * 3) Else, if we trapped from kernel we must find exception
	 *    table entry (ie. we have to have been accessing user
	 *    space).
	 *
	 * If AFAR is not in main memory, or we trapped from kernel
	 * and cannot find an exception table entry, it is unacceptable
	 * to try and continue.
	 */
	if (recoverable && is_memory) {
		if ((regs->tstate & TSTATE_PRIV) == 0UL) {
			/* OK, usermode access. */
			recoverable = 1;
		} else {
			const struct exception_table_entry *entry;

			entry = search_exception_tables(regs->tpc);
			if (entry) {
				/* OK, kernel access to userspace. */
				recoverable = 1;

			} else {
				/* BAD, privileged state is corrupted. */
				recoverable = 0;
			}

			if (recoverable) {
				if (pfn_valid(afar >> PAGE_SHIFT))
					get_page(pfn_to_page(afar >> PAGE_SHIFT));
				else
					recoverable = 0;

				/* Only perform fixup if we still have a
				 * recoverable condition.
				 */
				if (recoverable) {
					regs->tpc = entry->fixup;
					regs->tnpc = regs->tpc + 4;
				}
			}
		}
	} else {
		recoverable = 0;
	}

	if (!recoverable)
		panic("Irrecoverable deferred error trap.\n");
}