[Skiboot] [PATCH 07/24] phb4: Mask out write-1-to-clear registers in RC cfg

Benjamin Herrenschmidt benh at kernel.crashing.org
Fri Jun 23 02:22:08 AEST 2017


From: Russell Currey <ruscur at russell.cc>

The root complex config space only supports 4-byte accesses. Thus, when
the client requests a smaller size write, we do a read-modify-write to
the register.

However, some register have bits defined as "write 1 to clear".

If we do a RMW cycles on such a register and such bits are 1 in the
part that the client doesn't intend to modify, we will accidentally
write back those 1's and clear the corresponding bit.

This avoids it by masking out those magic bits from the "old" value
read from the register.

Signed-off-by: Russell Currey <ruscur at russell.cc>
---
 hw/phb4.c | 44 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 39 insertions(+), 5 deletions(-)

diff --git a/hw/phb4.c b/hw/phb4.c
index 3ee618e..7e8ba3b 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -264,20 +264,54 @@ static int64_t phb4_rc_write(struct phb4 *p, uint32_t offset, uint8_t sz,
 			     uint32_t val)
 {
 	uint32_t reg = offset & ~3;
-	uint32_t old, mask, shift;
+	uint32_t old, mask, shift, oldold;
 	int64_t rc;
 
 	if (reg > PHB_RC_CONFIG_SIZE)
 		return OPAL_SUCCESS;
 
-	/* If size isn't 4-bytes, do a RMW cycle
-	 *
-	 * XXX TODO: Filter out registers that do write-1-to-clear !!!
-	 */
+	/* If size isn't 4-bytes, do a RMW cycle */
 	if (sz < 4) {
 		rc = phb4_rc_read(p, reg, 4, &old);
 		if (rc != OPAL_SUCCESS)
 			return rc;
+
+		/*
+		 * Since we have to Read-Modify-Write here, we need to filter
+		 * out registers that have write-1-to-clear bits to prevent
+		 * clearing stuff we shouldn't be.  So for any register this
+		 * applies to, mask out those bits.
+		 */
+		oldold = old;
+		switch(reg) {
+		case 0x1C: /* Secondary status */
+			old &= 0x00ffffff; /* mask out 24-31 */
+			break;
+		case 0x50: /* EC - Device status */
+			old &= 0xfff0ffff; /* mask out 16-19 */
+			break;
+		case 0x58: /* EC - Link status */
+			old &= 0x3fffffff; /* mask out 30-31 */
+			break;
+		case 0x78: /* EC - Link status 2 */
+			old &= 0xf000ffff; /* mask out 16-27 */
+			break;
+		/* These registers *only* have write-1-to-clear bits */
+		case 0x104: /* AER - Uncorr. error status */
+		case 0x110: /* AER - Corr. error status */
+		case 0x130: /* AER - Root error status */
+		case 0x180: /* P16 - status */
+		case 0x184: /* P16 - LDPM status */
+		case 0x188: /* P16 - FRDPM status */
+		case 0x18C: /* P16 - SRDPM status */
+			old &= 0x00000000;
+			break;
+		}
+
+		if (old != oldold)
+			PHBDBG(p, "Rewrote %x to %x for reg %x for W1C\n",
+			       oldold, old, reg);
+
 		if (sz == 1) {
 			shift = (offset & 3) << 3;
 			mask = 0xff << shift;
-- 
2.9.4



More information about the Skiboot mailing list