diff mbox series

[RFC] hw/phb4: Add fake scom to allow reading PHB registers / IODA tables

Message ID 20200402112645.2264-1-oohall@gmail.com
State New
Headers show
Series [RFC] hw/phb4: Add fake scom to allow reading PHB registers / IODA tables | expand

Checks

Context Check Description
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot-dco fail Signed-off-by missing
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot fail Test snowpatch/job/snowpatch-skiboot on branch master
snowpatch_ozlabs/apply_patch success Successfully applied on branch master (ec7be0894c652bfda961418d79dd19838678abfc)

Commit Message

Oliver O'Halloran April 2, 2020, 11:26 a.m. UTC
The motivation for this is largely so that I can have some way to
extracting PHB state in a post-secureboot world. Currently the way
this is done is by wacking the PHB registers directly using /dev/mem,
but  that's pretty jank to begin with and with lockdown we can't do
that anyway.

What I figured we need is an architected way to access a register
space so this patch abuses to XSCOM API to do that. Each of the IODA
tables (and the PHB's HV regs) has a dedicated XSCOM "part id" which
can be used to access those registers from the host. Having a seperate
address space for the IODA tables is necessary since access them needs
to be done indirectly via the IODA_ADDR and IODA_DATA registers.

This setup works well enough for the non-config PHB registers and the
IODA tables since (like SCOMs) each register is 8 bytes. The cracks
start showing when you consider the root port config registers (part
of the HV register set) which only support four byte accesses. Moving
the config regs to a sepeate part ID would fix that, but eh.

The alternative would be something like adding a more generic set of
READ_REG / WRITE_REG OPAL calls which could handle things like varying
register access sizes, etc.

Comments welcome.

Not-Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
 hw/phb4.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 102 insertions(+)
diff mbox series

Patch

diff --git a/hw/phb4.c b/hw/phb4.c
index 60e797cf6e61..733593d40632 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -1999,6 +1999,105 @@  static void __unused phb4_dump_peltv(struct phb4 *p)
 	}
 }
 
+static struct ioda_table {
+	int index;
+	const char *name;
+} ioda_table_list[] = {
+	{ IODA3_TBL_LIST, "list", },
+	{ IODA3_TBL_MIST, "mist", },
+	{ IODA3_TBL_RCAM, "rcam", },
+	{ IODA3_TBL_MRT, "mrt", },
+	{ IODA3_TBL_PESTA, "pesta", },
+	{ IODA3_TBL_PESTB, "pestb", },
+	{ IODA3_TBL_TVT, "tvt", },
+	{ IODA3_TBL_TCAM, "tcam", },
+	{ IODA3_TBL_TDR, "tdr", },
+	{ IODA3_TBL_MBT, "mbt", },
+	{ IODA3_TBL_MDT, "mdt", },
+	{ IODA3_TBL_PEEV, "peev", },
+};
+
+static int64_t phb4_fake_scom_write(struct scom_controller *scom,
+				    uint32_t chipid, uint64_t pcbaddr,
+				    uint64_t val)
+{
+	(void) val;
+	(void) chipid;
+	(void) pcbaddr;
+	(void) scom;
+
+	return 0;
+}
+
+static int64_t phb4_fake_scom_read(struct scom_controller *scom,
+				   uint32_t chipid, uint64_t pcbaddr,
+				   uint64_t *val)
+{
+	struct phb4 *p = scom->private;
+
+	phb_lock(&p->phb);
+
+	if (chipid == p->chip_id) {
+		// FIXME: broken for the RC config space regs
+		*val = phb4_read_reg(p, pcbaddr);
+	} else {
+		/* otherwise it's an IODA reg, so go read that */
+		phb4_ioda_sel(p, chipid & 0x1f, pcbaddr, false);
+		*val = phb4_read_reg(p, PHB_IODA_DATA0);
+	}
+
+	phb_unlock(&p->phb);
+	return OPAL_SUCCESS;
+}
+
+static void phb4_register_fake_scom(struct phb4 *p)
+{
+	struct scom_controller *scom = zalloc(sizeof(*scom));
+	struct dt_node *regs, *n;
+	uint32_t chip_id;
+	int i;
+
+	if (!scom)
+		return;
+
+	chip_id = (0x2 << 28) | (p->phb.opal_id << 8);
+
+	/*
+	 * FIXME: We probably want to have seperate "chip ids" for each
+	 * ioda table so we can give each register range a sensible
+	 * name.
+	 */
+
+	regs = dt_new(p->phb.dt_node, "regs");
+	dt_add_property(regs, "scom-controller", NULL, 0);
+	dt_add_property_cells(regs, "ibm,chip-id", chip_id);
+
+	for (i = 0; i < ARRAY_SIZE(ioda_table_list); i++) {
+		int index = ioda_table_list[i].index;
+		struct scom_controller *scom;
+
+		scom = zalloc(sizeof(*scom));
+		if (!scom)
+			continue;
+
+		scom->private = p;
+		scom->part_id = chip_id | index;
+		scom->read = phb4_fake_scom_read;
+		scom->write = phb4_fake_scom_write;
+
+		if (scom_register(scom)) {
+			PHBERR(p, "Unable to add SCOM for IODA table %s\n",
+			       ioda_table_list[i].name);
+			free(scom);
+			continue;
+		}
+
+		n = dt_new_addr(regs, ioda_table_list[i].name, index);
+		dt_add_property_cells(n, "ibm,chip-id", chip_id | index);
+		dt_add_property(n, "scom-controller", NULL, 0);
+	}
+}
+
 static void __unused phb4_dump_ioda_table(struct phb4 *p, int table)
 {
 	const char *name;
@@ -5814,6 +5913,9 @@  static void phb4_create(struct dt_node *np)
 
 	dt_add_property_string(np, "status", "okay");
 
+	if (nvram_query_eq_dangerous("ioda-scoms", "yes"))
+		phb4_register_fake_scom(p);
+
 	return;
 
  failed: