git: 27b4a1b7e5b8 - main - isp(4): Add support to read contents of the FLT (flash layout table)

From: Warner Losh <imp_at_FreeBSD.org>
Date: Fri, 07 Jul 2023 21:47:57 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=27b4a1b7e5b871c248dc501aa359c93f7263b074

commit 27b4a1b7e5b871c248dc501aa359c93f7263b074
Author:     Joerg Pulz <Joerg.Pulz@frm2.tum.de>
AuthorDate: 2023-07-07 21:43:34 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2023-07-07 21:45:30 +0000

    isp(4): Add support to read contents of the FLT (flash layout table)
    
    The FLT is like a TOC for the flash area and contains entries for every flash
    region with start/end address, size and flags.
    Start using NVRAM addresses from FLT instead of hardcoded ones for ISP28xx
    based HBAs.
    
    The FLT should be available on earlier HBAs too, probably since ISP24xx based.
    This needs further investigation and testing.
    
    PR: 271062
    Reviewed by: imp, mav
    Sponsored by: Technical University of Munich
    Pull Request: https://github.com/freebsd/freebsd-src/pull/726
---
 sys/dev/isp/isp.c    | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 sys/dev/isp/ispreg.h |  81 ++++++++++++++
 sys/dev/isp/ispvar.h |  22 ++++
 3 files changed, 397 insertions(+), 2 deletions(-)

diff --git a/sys/dev/isp/isp.c b/sys/dev/isp/isp.c
index 46bb40c0fe07..14795198a850 100644
--- a/sys/dev/isp/isp.c
+++ b/sys/dev/isp/isp.c
@@ -123,6 +123,14 @@ static int isp_read_nvram_2400(ispsoftc_t *);
 static void isp_rd_2400_nvram(ispsoftc_t *, uint32_t, uint32_t *);
 static void isp_parse_nvram_2400(ispsoftc_t *, uint8_t *);
 
+static int isp_read_flthdr_28xx(ispsoftc_t *);
+static void isp_rd_28xx_flthdr(ispsoftc_t *, uint32_t, uint32_t *);
+static void isp_parse_flthdr_28xx(ispsoftc_t *, uint8_t *);
+
+static int isp_read_flt_28xx(ispsoftc_t *);
+static void isp_rd_28xx_flt(ispsoftc_t *, uint32_t, uint32_t *);
+static int isp_parse_flt_28xx(ispsoftc_t *, uint8_t *);
+
 static void
 isp_change_fw_state(ispsoftc_t *isp, int chan, int state)
 {
@@ -4334,18 +4342,36 @@ cleanup:
 static int
 isp_read_nvram(ispsoftc_t *isp, int bus)
 {
+	if (IS_28XX(isp)) {
+		fcparam *fcp = FCPARAM(isp, 0);
+		int r = 0;
 
+		fcp->flash_data_addr = ISP28XX_BASE_ADDR;
+		fcp->flt_length = 0;
+		r = isp_read_flthdr_28xx(isp);
+		if (r == 0) {
+			isp_read_flt_28xx(isp);
+		} else {
+			fcp->flt_region_nvram =
+			    (0x300000 + ISP2400_NVRAM_PORT_ADDR(isp->isp_port));
+		}
+	}
 	return (isp_read_nvram_2400(isp));
 }
 
 static int
 isp_read_nvram_2400(ispsoftc_t *isp)
 {
+	fcparam *fcp = FCPARAM(isp, 0);
 	int retval = 0;
 	uint32_t addr, csum, lwrds, *dptr;
 	uint8_t nvram_data[ISP2400_NVRAM_SIZE];
 
-	addr = ISP2400_NVRAM_PORT_ADDR(isp->isp_port);
+	if (IS_28XX(isp)) {
+		addr = fcp->flt_region_nvram;
+	} else {
+		addr = ISP2400_NVRAM_PORT_ADDR(isp->isp_port);
+	}
 	dptr = (uint32_t *) nvram_data;
 	for (lwrds = 0; lwrds < ISP2400_NVRAM_SIZE >> 2; lwrds++) {
 		isp_rd_2400_nvram(isp, addr++, dptr++);
@@ -4381,7 +4407,9 @@ isp_rd_2400_nvram(ispsoftc_t *isp, uint32_t addr, uint32_t *rp)
 	uint32_t tmp = 0;
 
 	if (IS_28XX(isp)) {
-		base = 0x7fad0000;	/* 0x7f7d0000 + 0x300000 */
+		fcparam *fcp = FCPARAM(isp, 0);
+		base = fcp->flash_data_addr + addr;
+		addr = 0;
 	} else if (IS_26XX(isp)) {
 		base = 0x7fe7c000;	/* XXX: Observation, may be wrong. */
 	} else if (IS_25XX(isp)) {
@@ -4449,3 +4477,267 @@ isp_parse_nvram_2400(ispsoftc_t *isp, uint8_t *nvram_data)
 	fcp->isp_xfwoptions = ISP2400_NVRAM_FIRMWARE_OPTIONS2(nvram_data);
 	fcp->isp_zfwoptions = ISP2400_NVRAM_FIRMWARE_OPTIONS3(nvram_data);
 }
+
+static int
+isp_read_flthdr_28xx(ispsoftc_t *isp)
+{
+	int retval = 0;
+	uint32_t addr, lwrds, *dptr;
+	uint16_t csum;
+	uint8_t flthdr_data[FLT_HEADER_SIZE];
+
+	addr = ISP28XX_FLT_ADDR;
+	dptr = (uint32_t *) flthdr_data;
+
+	isp_prt(isp, ISP_LOGDEBUG0,
+	    "FLTL[DEF]: 0x%x", ISP28XX_FLT_ADDR);
+	for (lwrds = 0; lwrds < FLT_HEADER_SIZE >> 2; lwrds++) {
+		isp_rd_28xx_flthdr(isp, addr++, dptr++);
+	}
+	dptr = (uint32_t *) flthdr_data;
+	for (csum = 0, lwrds = 0; lwrds < FLT_HEADER_SIZE >> 4; lwrds++) {
+		uint16_t tmp;
+		ISP_IOXGET_16(isp, &dptr[lwrds], tmp);
+		csum += tmp;
+	}
+	if (csum != 0) {
+		retval = -1;
+		goto out;
+	}
+	isp_parse_flthdr_28xx(isp, flthdr_data);
+out:
+	return (retval);
+}
+
+static void
+isp_rd_28xx_flthdr(ispsoftc_t *isp, uint32_t addr, uint32_t *rp)
+{
+	fcparam *fcp = FCPARAM(isp, 0);
+	int loops = 0;
+	uint32_t base = fcp->flash_data_addr;
+	uint32_t tmp = 0;
+
+	ISP_WRITE(isp, BIU2400_FLASH_ADDR, base | addr);
+	for (loops = 0; loops < 5000; loops++) {
+		ISP_DELAY(10);
+		tmp = ISP_READ(isp, BIU2400_FLASH_ADDR);
+		if ((tmp & (1U << 31)) != 0) {
+			break;
+		}
+	}
+	if (tmp & (1U << 31)) {
+		*rp = ISP_READ(isp, BIU2400_FLASH_DATA);
+		ISP_SWIZZLE_NVRAM_LONG(isp, rp);
+	} else {
+		*rp = 0xffffffff;
+	}
+}
+
+static void
+isp_parse_flthdr_28xx(ispsoftc_t *isp, uint8_t *flthdr_data)
+{
+	fcparam *fcp = FCPARAM(isp, 0);
+	uint16_t ver, csum;
+
+	ver = le16toh((uint16_t) (ISP28XX_FLT_VERSION(flthdr_data)));
+	fcp->flt_length = le16toh((uint16_t) (ISP28XX_FLT_LENGTH(flthdr_data)));
+	csum = le16toh((uint16_t) (ISP28XX_FLT_CSUM(flthdr_data)));
+
+	if ((fcp->flt_length == 0 ) ||
+	    (fcp->flt_length > (FLT_HEADER_SIZE + FLT_REGIONS_SIZE))) {
+		isp_prt(isp, ISP_LOGWARN,
+		    "FLT[DEF]: Invalid length=0x%x(%d)",
+		    fcp->flt_length, fcp->flt_length);
+	}
+	isp_prt(isp, ISP_LOGDEBUG0,
+	    "FLT[DEF]: version=0x%x length=0x%x(%d) checksum=0x%x",
+	    ver, fcp->flt_length, fcp->flt_length, csum);
+}
+
+static int
+isp_read_flt_28xx(ispsoftc_t *isp)
+{
+	fcparam *fcp = FCPARAM(isp, 0);
+	int retval = 0;
+	int len = fcp->flt_length - FLT_HEADER_SIZE;
+	uint32_t addr, lwrds, *dptr;
+	uint8_t flt_data[len];
+	fcp->flt_region_entries = len / FLT_REGION_SIZE;
+
+	addr = ISP28XX_FLT_ADDR + (FLT_HEADER_SIZE >> 2);
+	dptr = (uint32_t *) flt_data;
+	isp_prt(isp, ISP_LOGDEBUG0, "FLT[DEF]: regions=%d",
+	    fcp->flt_region_entries);
+	for (lwrds = 0; lwrds < len >> 2; lwrds++) {
+		isp_rd_28xx_flt(isp, addr++, dptr++);
+	}
+	retval = isp_parse_flt_28xx(isp, flt_data);
+	return (retval);
+}
+
+static void
+isp_rd_28xx_flt(ispsoftc_t *isp, uint32_t addr, uint32_t *rp)
+{
+	fcparam *fcp = FCPARAM(isp, 0);
+	int loops = 0;
+	uint32_t base = fcp->flash_data_addr;
+	uint32_t tmp = 0;
+
+	ISP_WRITE(isp, BIU2400_FLASH_ADDR, base | addr);
+	for (loops = 0; loops < 5000; loops++) {
+		ISP_DELAY(10);
+		tmp = ISP_READ(isp, BIU2400_FLASH_ADDR);
+		if ((tmp & (1U << 31)) != 0) {
+			break;
+		}
+	}
+	if (tmp & (1U << 31)) {
+		*rp = ISP_READ(isp, BIU2400_FLASH_DATA);
+		ISP_SWIZZLE_NVRAM_LONG(isp, rp);
+	} else {
+		*rp = 0xffffffff;
+	}
+}
+
+static int
+isp_parse_flt_28xx(ispsoftc_t *isp, uint8_t *flt_data)
+{
+	fcparam *fcp = FCPARAM(isp, 0);
+	int count;
+	struct flt_region region[fcp->flt_region_entries];
+
+	for (count = 0; count < fcp->flt_region_entries; count++) {
+		region[count].code =
+		    le16toh((uint16_t) (ISP28XX_FLT_REG_CODE(flt_data, count)));
+		region[count].attribute =
+		    (uint8_t) (ISP28XX_FLT_REG_ATTR(flt_data, count));
+		region[count].reserved =
+		    (uint8_t) (ISP28XX_FLT_REG_RES(flt_data, count));
+		region[count].size =
+		    le32toh((uint32_t) (ISP28XX_FLT_REG_SIZE(flt_data, count)) >> 2);
+		region[count].start =
+		    le32toh((uint32_t) (ISP28XX_FLT_REG_START(flt_data, count)) >> 2);
+		region[count].end =
+		    le32toh((uint32_t) (ISP28XX_FLT_REG_END(flt_data, count)) >> 2);
+
+		isp_prt(isp, ISP_LOGDEBUG0,
+		    "FLT[0x%x]: start=0x%x end=0x%x size=0x%x attribute=0x%x",
+		    region[count].code, region[count].start, region[count].end,
+		    region[count].size, region[count].attribute);
+
+		switch(region[count].code) {
+		case FLT_REG_FW:
+			fcp->flt_region_fw = region[count].start;
+			break;
+		case FLT_REG_BOOT_CODE:
+			fcp->flt_region_boot = region[count].start;
+			break;
+		case FLT_REG_VPD_0:
+			fcp->flt_region_vpd_nvram = region[count].start;
+			if (isp->isp_port == 0)
+				fcp->flt_region_vpd = region[count].start;
+			break;
+		case FLT_REG_VPD_1:
+			if (isp->isp_port == 1)
+				fcp->flt_region_vpd = region[count].start;
+			break;
+		case FLT_REG_VPD_2:
+			if (isp->isp_port == 2)
+				fcp->flt_region_vpd = region[count].start;
+			break;
+		case FLT_REG_VPD_3:
+			if (isp->isp_port == 3)
+				fcp->flt_region_vpd = region[count].start;
+			break;
+		case FLT_REG_NVRAM_0:
+			if (isp->isp_port == 0)
+				fcp->flt_region_nvram = region[count].start;
+			break;
+		case FLT_REG_NVRAM_1:
+			if (isp->isp_port == 1)
+				fcp->flt_region_nvram = region[count].start;
+			break;
+		case FLT_REG_NVRAM_2:
+			if (isp->isp_port == 2)
+				fcp->flt_region_nvram = region[count].start;
+			break;
+		case FLT_REG_NVRAM_3:
+			if (isp->isp_port == 3)
+				fcp->flt_region_nvram = region[count].start;
+			break;
+		case FLT_REG_FDT:
+			fcp->flt_region_fdt = region[count].start;
+			break;
+		case FLT_REG_FLT:
+			fcp->flt_region_flt = region[count].start;
+			break;
+		case FLT_REG_NPIV_CONF_0:
+			if (isp->isp_port == 0)
+				fcp->flt_region_npiv_conf = region[count].start;
+			break;
+		case FLT_REG_NPIV_CONF_1:
+			if (isp->isp_port == 1)
+				fcp->flt_region_npiv_conf = region[count].start;
+			break;
+		case FLT_REG_GOLD_FW:
+			fcp->flt_region_gold_fw = region[count].start;
+			break;
+		case FLT_REG_FCP_PRIO_0:
+			if (isp->isp_port == 0)
+				fcp->flt_region_fcp_prio = region[count].start;
+			break;
+		case FLT_REG_FCP_PRIO_1:
+			if (isp->isp_port == 1)
+				fcp->flt_region_fcp_prio = region[count].start;
+			break;
+		case FLT_REG_AUX_IMG_PRI_28XX:
+			fcp->flt_region_aux_img_status_pri = region[count].start;
+			break;
+		case FLT_REG_AUX_IMG_SEC_28XX:
+			fcp->flt_region_aux_img_status_sec = region[count].start;
+			break;
+		case FLT_REG_NVRAM_SEC_28XX_0:
+			if (isp->isp_port == 0)
+				fcp->flt_region_nvram_sec = region[count].start;
+			break;
+		case FLT_REG_NVRAM_SEC_28XX_1:
+			if (isp->isp_port == 1)
+				fcp->flt_region_nvram_sec = region[count].start;
+			break;
+		case FLT_REG_NVRAM_SEC_28XX_2:
+			if (isp->isp_port == 2)
+				fcp->flt_region_nvram_sec = region[count].start;
+			break;
+		case FLT_REG_NVRAM_SEC_28XX_3:
+			if (isp->isp_port == 3)
+				fcp->flt_region_nvram_sec = region[count].start;
+			break;
+		case FLT_REG_VPD_SEC_28XX_0:
+			fcp->flt_region_vpd_nvram_sec = region[count].start;
+			if (isp->isp_port == 0)
+				fcp->flt_region_vpd_sec = region[count].start;
+			break;
+		case FLT_REG_VPD_SEC_28XX_1:
+			if (isp->isp_port == 1)
+				fcp->flt_region_vpd_sec = region[count].start;
+			break;
+		case FLT_REG_VPD_SEC_28XX_2:
+			if (isp->isp_port == 2)
+				fcp->flt_region_vpd_sec = region[count].start;
+			break;
+		case FLT_REG_VPD_SEC_28XX_3:
+			if (isp->isp_port == 3)
+				fcp->flt_region_vpd_sec = region[count].start;
+			break;
+		}
+	}
+	isp_prt(isp, ISP_LOGDEBUG0,
+	    "FLT[FLT]: boot=0x%x fw=0x%x vpd_nvram=0x%x vpd=0x%x nvram 0x%x "
+	    "fdt=0x%x flt=0x%x npiv=0x%x fcp_prif_cfg=0x%x",
+	    fcp->flt_region_boot, fcp->flt_region_fw, fcp->flt_region_vpd_nvram,
+	    fcp->flt_region_vpd, fcp->flt_region_nvram, fcp->flt_region_fdt,
+	    fcp->flt_region_flt, fcp->flt_region_npiv_conf,
+	    fcp->flt_region_fcp_prio);
+
+	return (0);
+}
diff --git a/sys/dev/isp/ispreg.h b/sys/dev/isp/ispreg.h
index 4f7bc9cb37ce..9d2594a97a99 100644
--- a/sys/dev/isp/ispreg.h
+++ b/sys/dev/isp/ispreg.h
@@ -252,4 +252,85 @@ typedef struct {
 #define	ISP2400_NVRAM_FIRMWARE_OPTIONS3(c)	\
 	((c)[52] | ((c)[53] << 8) | ((c)[54] << 16) | ((c)[55] << 24))
 
+/*
+ * Qlogic FLT
+ */
+#define ISP24XX_BASE_ADDR	0x7ff00000
+#define ISP24XX_FLT_ADDR	0x11400
+
+#define ISP25XX_BASE_ADDR	ISP24XX_BASE_ADDR
+#define ISP25XX_FLT_ADDR	0x50400
+
+#define ISP27XX_BASE_ADDR	0x7f800000
+#define ISP27XX_FLT_ADDR	(0x3F1000/4)
+
+#define ISP28XX_BASE_ADDR	0x7f7d0000
+#define ISP28XX_FLT_ADDR	(0x11000/4)
+
+#define FLT_HEADER_SIZE		8
+#define FLT_REGION_SIZE		16
+#define FLT_MAX_REGIONS		0xFF
+#define FLT_REGIONS_SIZE	(FLT_REGION_SIZE * FLT_MAX_REGIONS)
+
+#define ISP28XX_FLT_VERSION(c)		((c)[0] | ((c)[1] << 8))
+#define ISP28XX_FLT_LENGTH(c)		((c)[2] | ((c)[3] << 8))
+#define ISP28XX_FLT_CSUM(c)		((c)[4] | ((c)[5] << 8))
+#define ISP28XX_FLT_REG_CODE(c, o)	\
+	((c)[0 + FLT_REGION_SIZE * o] | ((c)[1 + FLT_REGION_SIZE * o] << 8))
+#define ISP28XX_FLT_REG_ATTR(c, o)	((c)[2 + FLT_REGION_SIZE * o])
+#define ISP28XX_FLT_REG_RES(c, o)	((c)[3 + FLT_REGION_SIZE * o])
+#define ISP28XX_FLT_REG_SIZE(c, o)	(\
+		((uint32_t)(c)[4 + FLT_REGION_SIZE * o] << 0) | \
+		((uint32_t)(c)[5 + FLT_REGION_SIZE * o] << 8) | \
+		((uint32_t)(c)[6 + FLT_REGION_SIZE * o] << 16) | \
+		((uint32_t)(c)[7 + FLT_REGION_SIZE * o] << 24))
+#define ISP28XX_FLT_REG_START(c, o)	(\
+		((uint32_t)(c)[8 + FLT_REGION_SIZE * o] << 0) | \
+		((uint32_t)(c)[9 + FLT_REGION_SIZE * o] << 8) | \
+		((uint32_t)(c)[10 + FLT_REGION_SIZE * o] << 16) | \
+		((uint32_t)(c)[11 + FLT_REGION_SIZE * o] << 24))
+#define ISP28XX_FLT_REG_END(c, o)	(\
+		((uint32_t)(c)[12 + FLT_REGION_SIZE * o] << 0) | \
+		((uint32_t)(c)[13 + FLT_REGION_SIZE * o] << 8) | \
+		((uint32_t)(c)[14 + FLT_REGION_SIZE * o] << 16) | \
+		((uint32_t)(c)[15 + FLT_REGION_SIZE * o] << 24))
+
+struct flt_region {
+	uint16_t  code;
+	uint8_t attribute;
+	uint8_t reserved;
+	uint32_t size;
+	uint32_t start;
+	uint32_t end;
+};
+
+#define FLT_REG_FW		0x01
+#define FLT_REG_BOOT_CODE	0x07
+#define FLT_REG_VPD_0		0x14
+#define FLT_REG_NVRAM_0		0x15
+#define FLT_REG_VPD_1		0x16
+#define FLT_REG_NVRAM_1		0x17
+#define FLT_REG_VPD_2		0xd4
+#define FLT_REG_NVRAM_2		0xd5
+#define FLT_REG_VPD_3		0xd6
+#define FLT_REG_NVRAM_3		0xd7
+#define FLT_REG_FDT		0x1a
+#define FLT_REG_FLT		0x1c
+#define FLT_REG_NPIV_CONF_0	0x29
+#define FLT_REG_NPIV_CONF_1	0x2a
+#define FLT_REG_GOLD_FW		0x2f
+#define FLT_REG_FCP_PRIO_0	0x87
+#define FLT_REG_FCP_PRIO_1	0x88
+
+#define FLT_REG_AUX_IMG_PRI_28XX	0x125
+#define FLT_REG_AUX_IMG_SEC_28XX	0x126
+#define FLT_REG_NVRAM_SEC_28XX_0	0x10d
+#define FLT_REG_NVRAM_SEC_28XX_1	0x10f
+#define FLT_REG_NVRAM_SEC_28XX_2	0x111
+#define FLT_REG_NVRAM_SEC_28XX_3	0x113
+#define FLT_REG_VPD_SEC_28XX_0		0x10c
+#define FLT_REG_VPD_SEC_28XX_1		0x10e
+#define FLT_REG_VPD_SEC_28XX_2		0x110
+#define FLT_REG_VPD_SEC_28XX_3		0x112
+
 #endif	/* _ISPREG_H */
diff --git a/sys/dev/isp/ispvar.h b/sys/dev/isp/ispvar.h
index c597e715fa14..ae693c5c15e8 100644
--- a/sys/dev/isp/ispvar.h
+++ b/sys/dev/isp/ispvar.h
@@ -369,6 +369,28 @@ typedef struct {
 	int			isp_use_gft_id;		/* Use GFT_ID */
 	int			isp_use_gff_id;		/* Use GFF_ID */
 
+	uint32_t		flash_data_addr;
+	/*
+	 * FLT
+	 */
+	uint16_t		flt_length;
+	uint32_t		flt_region_entries;
+	uint32_t		flt_region_aux_img_status_pri;
+	uint32_t		flt_region_aux_img_status_sec;
+	uint32_t		flt_region_boot;
+	uint32_t		flt_region_fcp_prio;
+	uint32_t		flt_region_fdt;
+	uint32_t		flt_region_flt;
+	uint32_t		flt_region_fw;
+	uint32_t		flt_region_gold_fw;
+	uint32_t		flt_region_npiv_conf;
+	uint32_t		flt_region_nvram;
+	uint32_t		flt_region_nvram_sec;
+	uint32_t		flt_region_vpd;
+	uint32_t		flt_region_vpd_nvram;
+	uint32_t		flt_region_vpd_nvram_sec;
+	uint32_t		flt_region_vpd_sec;
+
 	/*
 	 * Current active WWNN/WWPN
 	 */