Пример #1
0
int opb_target_init(struct target *target, const char *name, uint64_t base, struct target *next)
{
	target_init(target, name, base, opb_read, opb_write, NULL, next);

	/* Clear any outstanding errors */
	write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0));
	write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0));

	return 0;
}
Пример #2
0
static uint64_t opb_poll(struct target *target, uint64_t *read_data)
{
	unsigned long retries = MFSI_OPB_MAX_TRIES;
	uint64_t sval;
	uint32_t stat;
	int64_t rc;

	/* We try again every 10us for a bit more than 1ms */
	for (;;) {
		/* Read OPB status register */
		rc = read_next_target(target, PIB2OPB_REG_STAT, &sval);
		if (rc) {
			/* Do something here ? */
			PR_ERROR("XSCOM error %lld read OPB STAT\n", rc);
			return -1;
		}
		PR_DEBUG("  STAT=0x%16llx...\n", sval);

		stat = sval >> 32;

		/* Complete */
		if (!(stat & OPB_STAT_BUSY))
			break;
		if (retries-- == 0) {
			/* This isn't supposed to happen (HW timeout) */
			PR_ERROR("OPB POLL timeout !\n");
			return -1;
		}
		usleep(1);
	}

	/*
	 * TODO: Add the full error analysis that skiboot has. For now
	 * we just reset things so we can continue. Also need to
	 * improve error handling as we expect these occasionally when
	 * probing the system.
	 */
	if (stat & OPB_STAT_ANY_ERR) {
		write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0));
		write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0));
		PR_DEBUG("OPB Error. Status 0x%08x\n", stat);
		rc = -1;
	} else if (read_data) {
		if (!(stat & OPB_STAT_READ_VALID)) {
			PR_DEBUG("Read successful but no data !\n");
			rc = -1;
		}
		*read_data = sval & 0xffffffff;
	}

	return rc;
}
Пример #3
0
static void find_capp_checkstop_reason(int flat_chip_id,
				       struct OpalHMIEvent *hmi_evt,
				       uint64_t *out_flags)
{
	struct capp_info info;
	struct phb *phb;
	uint64_t capp_fir;
	uint64_t capp_fir_mask;
	uint64_t capp_fir_action0;
	uint64_t capp_fir_action1;
	uint64_t reg;
	int64_t rc;

	/* Find the CAPP on the chip associated with the HMI. */
	for_each_phb(phb) {
		/* get the CAPP info */
		rc = capp_get_info(flat_chip_id, phb, &info);
		if (rc == OPAL_PARAMETER)
			continue;

		if (xscom_read(flat_chip_id, info.capp_fir_reg, &capp_fir) ||
		    xscom_read(flat_chip_id, info.capp_fir_mask_reg,
			       &capp_fir_mask) ||
		    xscom_read(flat_chip_id, info.capp_fir_action0_reg,
			       &capp_fir_action0) ||
		    xscom_read(flat_chip_id, info.capp_fir_action1_reg,
			       &capp_fir_action1)) {
			prerror("CAPP: Couldn't read CAPP#%d (PHB:#%x) FIR registers by XSCOM!\n",
				info.capp_index, info.phb_index);
			continue;
		}

		if (!(capp_fir & ~capp_fir_mask))
			continue;

		prlog(PR_DEBUG, "CAPP#%d (PHB:#%x): FIR 0x%016llx mask 0x%016llx\n",
		      info.capp_index, info.phb_index, capp_fir,
		      capp_fir_mask);
		prlog(PR_DEBUG, "CAPP#%d (PHB:#%x): ACTION0 0x%016llx, ACTION1 0x%016llx\n",
		      info.capp_index, info.phb_index, capp_fir_action0,
		      capp_fir_action1);

		/*
		 * If this bit is set (=1) a Recoverable Error has been
		 * detected
		 */
		xscom_read(flat_chip_id, info.capp_err_status_ctrl_reg, &reg);
		if ((reg & PPC_BIT(0)) != 0) {
			phb_lock(phb);
			phb->ops->set_capp_recovery(phb);
			phb_unlock(phb);

			hmi_evt->severity = OpalHMI_SEV_NO_ERROR;
			hmi_evt->type = OpalHMI_ERROR_CAPP_RECOVERY;
			queue_hmi_event(hmi_evt, 1, out_flags);

			return;
		}
	}
}
Пример #4
0
static inline void __tlbie_lpid(unsigned long lpid, unsigned long ric)
{
	unsigned long rb,rs,prs,r;

	rb = PPC_BIT(52); /* IS = 2 */
	rs = lpid;
	prs = 0; /* partition scoped */
	r = 1;   /* radix format */

	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
}
Пример #5
0
static inline void __tlbie_pid(unsigned long pid, unsigned long ric)
{
	unsigned long rb,rs,prs,r;

	rb = PPC_BIT(53); /* IS = 1 */
	rs = pid << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* radix format */

	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	trace_tlbie(0, 0, rb, rs, ric, prs, r);
}
Пример #6
0
static inline void _tlbie_pid(unsigned long pid, unsigned long ric)
{
	unsigned long rb,rs,prs,r;

	rb = PPC_BIT(53); /* IS = 1 */
	rs = pid << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
Пример #7
0
static inline void __tlbiel_pid(unsigned long pid, int set,
				unsigned long ric)
{
	unsigned long rb,rs,prs,r;

	rb = PPC_BIT(53); /* IS = 1 */
	rb |= set << PPC_BITLSHIFT(51);
	rs = ((unsigned long)pid) << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	trace_tlbie(0, 1, rb, rs, ric, prs, r);
}
Пример #8
0
static inline void __tlbiel_lpid_guest(unsigned long lpid, int set,
				unsigned long ric)
{
	unsigned long rb,rs,prs,r;

	rb = PPC_BIT(52); /* IS = 2 */
	rb |= set << PPC_BITLSHIFT(51);
	rs = 0;  /* LPID comes from LPIDR */
	prs = 1; /* process scoped */
	r = 1;   /* radix format */

	asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	trace_tlbie(lpid, 1, rb, rs, ric, prs, r);
}
Пример #9
0
static void decode_malfunction(struct OpalHMIEvent *hmi_evt, uint64_t *out_flags)
{
	int i;
	uint64_t malf_alert, flags;

	flags = 0;

	if (!setup_scom_addresses()) {
		prerror("Failed to setup scom addresses\n");
		/* Send an unknown HMI event. */
		hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_UNKNOWN;
		hmi_evt->u.xstop_error.xstop_reason = 0;
		queue_hmi_event(hmi_evt, false, out_flags);
		return;
	}

	xscom_read(this_cpu()->chip_id, malf_alert_scom, &malf_alert);

	if (!malf_alert)
		return;

	for (i = 0; i < 64; i++) {
		if (malf_alert & PPC_BIT(i)) {
			xscom_write(this_cpu()->chip_id, malf_alert_scom,
								~PPC_BIT(i));
			find_capp_checkstop_reason(i, hmi_evt, &flags);
			find_nx_checkstop_reason(i, hmi_evt, &flags);
			find_npu_checkstop_reason(i, hmi_evt, &flags);
		}
	}

	find_core_checkstop_reason(hmi_evt, &flags);

	/*
	 * If we fail to find checkstop reason, send an unknown HMI event.
	 */
	if (!(flags & OPAL_HMI_FLAGS_NEW_EVENT)) {
		hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_UNKNOWN;
		hmi_evt->u.xstop_error.xstop_reason = 0;
		queue_hmi_event(hmi_evt, false, &flags);
	}
	*out_flags |= flags;
}
Пример #10
0
long hmi_exception_realmode(struct pt_regs *regs)
{
	__this_cpu_inc(irq_stat.hmi_exceptions);

#ifdef CONFIG_PPC_BOOK3S_64
	/* Workaround for P9 vector CI loads (see p9_hmi_special_emu) */
	if (pvr_version_is(PVR_POWER9)) {
		unsigned long hmer = mfspr(SPRN_HMER);

		/* Do we have the debug bit set */
		if (hmer & PPC_BIT(17)) {
			hmer &= ~PPC_BIT(17);
			mtspr(SPRN_HMER, hmer);

			/*
			 * Now to avoid problems with soft-disable we
			 * only do the emulation if we are coming from
			 * user space
			 */
			if (user_mode(regs))
				local_paca->hmi_p9_special_emu = 1;

			/*
			 * Don't bother going to OPAL if that's the
			 * only relevant bit.
			 */
			if (!(hmer & mfspr(SPRN_HMEER)))
				return local_paca->hmi_p9_special_emu;
		}
	}
#endif /* CONFIG_PPC_BOOK3S_64 */

	wait_for_subcore_guest_exit();

	if (ppc_md.hmi_exception_early)
		ppc_md.hmi_exception_early(regs);

	wait_for_tb_resync();

	return 1;
}
Пример #11
0
static void find_nx_checkstop_reason(int flat_chip_id,
				     struct OpalHMIEvent *hmi_evt,
				     uint64_t *out_flags)
{
	uint64_t nx_status;
	uint64_t nx_dma_fir;
	uint64_t nx_pbi_fir_val;
	int i;

	/* Get NX status register value. */
	if (xscom_read(flat_chip_id, nx_status_reg, &nx_status) != 0) {
		prerror("XSCOM error reading NX_STATUS_REG\n");
		return;
	}

	/* Check if NX has driven an HMI interrupt. */
	if (!(nx_status & NX_HMI_ACTIVE))
		return;

	/* Initialize HMI event */
	hmi_evt->severity = OpalHMI_SEV_FATAL;
	hmi_evt->type = OpalHMI_ERROR_MALFUNC_ALERT;
	hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_NX;
	hmi_evt->u.xstop_error.u.chip_id = flat_chip_id;

	/* Get DMA & Engine FIR data register value. */
	if (xscom_read(flat_chip_id, nx_dma_engine_fir, &nx_dma_fir) != 0) {
		prerror("XSCOM error reading NX_DMA_ENGINE_FIR\n");
		return;
	}

	/* Get PowerBus Interface FIR data register value. */
	if (xscom_read(flat_chip_id, nx_pbi_fir, &nx_pbi_fir_val) != 0) {
		prerror("XSCOM error reading NX_PBI_FIR\n");
		return;
	}

	/* Find NX checkstop reason and populate HMI event with error info. */
	for (i = 0; i < ARRAY_SIZE(nx_dma_xstop_bits); i++)
		if (nx_dma_fir & PPC_BIT(nx_dma_xstop_bits[i].bit))
			hmi_evt->u.xstop_error.xstop_reason
						|= nx_dma_xstop_bits[i].reason;

	for (i = 0; i < ARRAY_SIZE(nx_pbi_xstop_bits); i++)
		if (nx_pbi_fir_val & PPC_BIT(nx_pbi_xstop_bits[i].bit))
			hmi_evt->u.xstop_error.xstop_reason
						|= nx_pbi_xstop_bits[i].reason;

	/*
	 * Set NXDMAENGFIR[38] to signal PRD that service action is required.
	 * Without this inject, PRD will not be able to do NX unit checkstop
	 * error analysis. NXDMAENGFIR[38] is a spare bit and used to report
	 * a software initiated attention.
	 *
	 * The behavior of this bit and all FIR bits are documented in
	 * RAS spreadsheet.
	 */
	xscom_write(flat_chip_id, nx_dma_engine_fir, PPC_BIT(38));

	/* Send an HMI event. */
	queue_hmi_event(hmi_evt, 0, out_flags);
}
Пример #12
0
static bool decode_core_fir(struct cpu_thread *cpu,
				struct OpalHMIEvent *hmi_evt)
{
	uint64_t core_fir;
	uint32_t core_id;
	int i, swkup_rc;
	bool found = false;
	int64_t ret;
	const char *loc;

	/* Sanity check */
	if (!cpu || !hmi_evt)
		return false;

	core_id = pir_to_core_id(cpu->pir);

	/* Force the core to wakeup, otherwise reading core_fir is unrealiable
	 * if stop-state 5 is enabled.
	 */
	swkup_rc = dctl_set_special_wakeup(cpu);

	/* Get CORE FIR register value. */
	ret = read_core_fir(cpu->chip_id, core_id, &core_fir);

	if (!swkup_rc)
		dctl_clear_special_wakeup(cpu);


	if (ret == OPAL_WRONG_STATE) {
		/*
		 * CPU is asleep, so it probably didn't cause the checkstop.
		 * If no other HMI cause is found a "catchall" checkstop
		 * will be raised, so if this CPU should've been awake the
		 * error will be handled appropriately.
		 */
		prlog(PR_DEBUG,
		      "FIR read failed, chip %d core %d asleep\n",
		      cpu->chip_id, core_id);
		return false;
	} else if (ret != OPAL_SUCCESS) {
		prerror("XSCOM error reading CORE FIR\n");
		/* If the FIR can't be read, we should checkstop. */
		return true;
	}

	if (!core_fir)
		return false;

	loc = chip_loc_code(cpu->chip_id);
	prlog(PR_INFO, "[Loc: %s]: CHIP ID: %x, CORE ID: %x, FIR: %016llx\n",
			loc ? loc : "Not Available",
			cpu->chip_id, core_id, core_fir);

	/* Check CORE FIR bits and populate HMI event with error info. */
	for (i = 0; i < ARRAY_SIZE(xstop_bits); i++) {
		if (core_fir & PPC_BIT(xstop_bits[i].bit)) {
			found = true;
			hmi_evt->u.xstop_error.xstop_reason
						|= xstop_bits[i].reason;
		}
	}
	return found;
}
Пример #13
0
static void hfscr_pmu_enable(void)
{
	u64 hfscr = mfspr(SPRN_HFSCR);
	hfscr |= PPC_BIT(60);
	mtspr(SPRN_HFSCR, hfscr);
}