Page MenuHomeFreeBSD

D34268.id102753.diff
No OneTemporary

D34268.id102753.diff

diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -1008,6 +1008,54 @@
* PCI Vital Product Data
*/
+#define VPD_DUMMY_DATA
+#ifdef VPD_DUMMY_DATA
+
+char VPD_TEST_DATA[] =
+ "\x82" "\x21\x00"
+ "ABCD Super-Fast Widget Controller"
+
+#ifdef INV_RO_LEN
+ "\x90" "\x5b\x00" // 0x4a + 5 * 3 => 0x59
+#else
+ "\x90" "\x59\x00" // 0x4a + 5 * 3 => 0x59
+#endif
+ "PN" "\x08" "6181682A"
+ "EC" "\x0a" "4950262536"
+ "SN" "\x08" "00000194"
+ "MN" "\x04" "1037"
+#ifdef INV_CKSUM
+ "RV" "\x2c" "\x4e*******************************************"
+#else
+ "RV" "\x2c" "\x50*******************************************"
+#endif
+
+#ifdef INV_WR_LEN
+ "\x91" "\x7e\x00" // 0x75 + 3 * 3 => 0x7e
+#else
+ "\x91" "\x7c\x00" // 0x75 + 3 * 3 => 0x7e
+#endif
+ "V1" "\x05" "65A01"
+ "Y1" "\x0d" "Error Code 26"
+ "RW" "\x61" "*************************************************************************************************"
+
+ "\x78"
+ "\x78"
+ "\x78"
+ "\x78";
+
+static int
+pci_read_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg, uint32_t *data)
+{
+ if ((reg & 3) != 0 || reg < 0 || reg + 4 > sizeof(VPD_TEST_DATA))
+ return (-1);
+ memcpy(data, VPD_TEST_DATA + reg, 4);
+// pci_printf(cfg, "TESTDATA(%d/%lu): %#x\n", reg, sizeof(VPD_TEST_DATA), *data);
+ return (0);
+}
+
+#else
+
#define PCI_VPD_TIMEOUT 1000000
static int
@@ -1051,6 +1099,8 @@
#undef PCI_VPD_TIMEOUT
+#endif
+
struct vpd_readstate {
device_t pcib;
pcicfgregs *cfg;
@@ -1068,7 +1118,7 @@
if (vrs->bytesinval == 0) {
if (pci_read_vpd_reg(vrs->pcib, vrs->cfg, vrs->off, &reg))
- return (ENXIO);
+ return (-1);
vrs->val = le32toh(reg);
vrs->off += 4;
byte = vrs->val & 0xff;
@@ -1084,277 +1134,282 @@
return (0);
}
+static int
+vpd_expectbyte(struct vpd_readstate *vrs, uint8_t expected)
+{
+ uint8_t data;
+
+ if (vpd_nextbyte(vrs, &data) != 0)
+ return (-1);
+
+ if (data == expected)
+ return (0);
+
+ vrs->cksum -= data;
+ vrs->val = (vrs->val << 8) + data;
+ vrs->bytesinval++;
+ return (-1);
+}
+
+static int
+vpd_read_tag_size(struct vpd_readstate *vrs, uint8_t vpd_tag)
+{
+ uint8_t byte1, byte2;
+
+ if (vpd_expectbyte(vrs, vpd_tag) != 0)
+ return (0);
+
+ if ((vpd_tag & 0x80) == 0)
+ return (vpd_tag & 0x07);
+
+ if (vpd_nextbyte(vrs, &byte1) != 0)
+ return (-1);
+ if (vpd_nextbyte(vrs, &byte2) != 0)
+ return (-1);
+
+ return ((byte2 << 8) + byte1);
+}
+
+static void*
+alloc_buffer(void* buffer, size_t element_size, int needed)
+{
+ int alloc, new_alloc;
+
+ alloc = roundup2(needed, 8);
+ new_alloc = roundup2(needed + 1, 8);
+ if (alloc != new_alloc) {
+ buffer = reallocf(buffer,
+ new_alloc * element_size, M_DEVBUF, M_WAITOK | M_ZERO);
+ }
+
+ return (buffer);
+}
+
+static int
+vpd_read_elem_head(struct vpd_readstate *vrs, char keyword[2])
+{
+ uint8_t data;
+
+ if (vpd_nextbyte(vrs, &keyword[0]) != 0)
+ return (-1);
+ if (vpd_nextbyte(vrs, &keyword[1]) != 0)
+ return (-1);
+ if (vpd_nextbyte(vrs, &data) != 0)
+ return (-1);
+
+ return (data);
+}
+
+static char*
+vpd_read_value(struct vpd_readstate *vrs, int size)
+{
+ int i;
+ char char1;
+ char *value;
+
+ if (size < 0 || size > 255) // maximum element size supported is 255
+ return (NULL);
+ value = malloc(size + 1, M_DEVBUF, M_WAITOK);
+ if (value == NULL)
+ return (NULL);
+ for (i = 0; i < size; i++) {
+ if (vpd_nextbyte(vrs, &char1) != 0) {
+ free(value, M_DEVBUF);
+ return (NULL);
+ }
+ value[i] = char1;
+ }
+ value[size] = '\0';
+
+ return (value);
+}
+
+static int
+vpd_read_elem_data(struct vpd_readstate *vrs, char keyword[2], char **value, int maxlen)
+{
+ int len;
+
+ len = vpd_read_elem_head(vrs, keyword);
+ if (len > maxlen)
+ return (-1);
+ *value = vpd_read_value(vrs, len);
+
+ return (len);
+}
+
static void
-pci_read_vpd(device_t pcib, pcicfgregs *cfg)
+vpd_fixup_cksum(struct vpd_readstate *vrs, char *rvstring, int len)
{
- struct vpd_readstate vrs;
- int state;
- int name;
- int remain;
int i;
- int alloc, off; /* alloc/off for RO/W arrays */
- int cksumvalid;
- int dflen;
- uint8_t byte;
- uint8_t byte2;
+ uint8_t fixup;
- /* init vpd reader */
- vrs.bytesinval = 0;
- vrs.off = 0;
- vrs.pcib = pcib;
- vrs.cfg = cfg;
- vrs.cksum = 0;
+ fixup = 0;
+ for (i = 1; i < len; i++)
+ fixup += rvstring[i];
+ vrs->cksum -= fixup;
+}
- state = 0;
- name = remain = i = 0; /* shut up stupid gcc */
- alloc = off = 0; /* shut up stupid gcc */
- dflen = 0; /* shut up stupid gcc */
- cksumvalid = -1;
- while (state >= 0) {
- if (vpd_nextbyte(&vrs, &byte)) {
- state = -2;
- break;
+static size_t
+next_vpd_ro_elem(struct vpd_readstate *vrs, int maxsize)
+{
+ struct pcicfg_vpd *vpd;
+ pcicfgregs *cfg;
+ struct vpd_readonly *vpd_ros;
+ int len;
+
+ cfg = vrs->cfg;
+ vpd = &cfg->vpd;
+
+ if (maxsize < 3)
+ return (-1);
+ vpd->vpd_ros = alloc_buffer(vpd->vpd_ros, sizeof(*vpd->vpd_ros), vpd->vpd_rocnt);
+ if (vpd->vpd_ros == NULL) {
+ pci_printf(cfg, "out of memory");
+ return (-1);
+ }
+ vpd_ros = &vpd->vpd_ros[vpd->vpd_rocnt];
+ maxsize -= 3;
+ len = vpd_read_elem_data(vrs, vpd_ros->keyword, &vpd_ros->value, maxsize);
+ if (vpd_ros->value == NULL)
+ return (-1);
+ vpd_ros->len = len;
+ if (vpd_ros->keyword[0] == 'R' && vpd_ros->keyword[1] == 'V') {
+ vpd_fixup_cksum(vrs, vpd_ros->value, len);
+ if (vrs->cksum != 0) {
+ pci_printf(cfg,
+ "invalid VPD checksum %#hhx\n", vrs->cksum);
+ return (-1);
}
-#if 0
- printf("vpd: val: %#x, off: %d, bytesinval: %d, byte: %#hhx, " \
- "state: %d, remain: %d, name: %#x, i: %d\n", vrs.val,
- vrs.off, vrs.bytesinval, byte, state, remain, name, i);
-#endif
- switch (state) {
- case 0: /* item name */
- if (byte & 0x80) {
- if (vpd_nextbyte(&vrs, &byte2)) {
- state = -2;
- break;
- }
- remain = byte2;
- if (vpd_nextbyte(&vrs, &byte2)) {
- state = -2;
- break;
- }
- remain |= byte2 << 8;
- name = byte & 0x7f;
- } else {
- remain = byte & 0x7;
- name = (byte >> 3) & 0xf;
- }
- if (vrs.off + remain - vrs.bytesinval > 0x8000) {
- pci_printf(cfg,
- "VPD data overflow, remain %#x\n", remain);
- state = -1;
- break;
- }
- switch (name) {
- case 0x2: /* String */
- cfg->vpd.vpd_ident = malloc(remain + 1,
- M_DEVBUF, M_WAITOK);
- i = 0;
- state = 1;
- break;
- case 0xf: /* End */
- state = -1;
- break;
- case 0x10: /* VPD-R */
- alloc = 8;
- off = 0;
- cfg->vpd.vpd_ros = malloc(alloc *
- sizeof(*cfg->vpd.vpd_ros), M_DEVBUF,
- M_WAITOK | M_ZERO);
- state = 2;
- break;
- case 0x11: /* VPD-W */
- alloc = 8;
- off = 0;
- cfg->vpd.vpd_w = malloc(alloc *
- sizeof(*cfg->vpd.vpd_w), M_DEVBUF,
- M_WAITOK | M_ZERO);
- state = 5;
- break;
- default: /* Invalid data, abort */
- state = -1;
- break;
- }
- break;
+ }
+ vpd->vpd_rocnt++;
- case 1: /* Identifier String */
- cfg->vpd.vpd_ident[i++] = byte;
- remain--;
- if (remain == 0) {
- cfg->vpd.vpd_ident[i] = '\0';
- state = 0;
- }
- break;
+ return (vpd_ros->len + 3);
+}
- case 2: /* VPD-R Keyword Header */
- if (off == alloc) {
- cfg->vpd.vpd_ros = reallocf(cfg->vpd.vpd_ros,
- (alloc *= 2) * sizeof(*cfg->vpd.vpd_ros),
- M_DEVBUF, M_WAITOK | M_ZERO);
- }
- cfg->vpd.vpd_ros[off].keyword[0] = byte;
- if (vpd_nextbyte(&vrs, &byte2)) {
- state = -2;
- break;
- }
- cfg->vpd.vpd_ros[off].keyword[1] = byte2;
- if (vpd_nextbyte(&vrs, &byte2)) {
- state = -2;
- break;
- }
- cfg->vpd.vpd_ros[off].len = dflen = byte2;
- if (dflen == 0 &&
- strncmp(cfg->vpd.vpd_ros[off].keyword, "RV",
- 2) == 0) {
- /*
- * if this happens, we can't trust the rest
- * of the VPD.
- */
- pci_printf(cfg, "bad keyword length: %d\n",
- dflen);
- cksumvalid = 0;
- state = -1;
- break;
- } else if (dflen == 0) {
- cfg->vpd.vpd_ros[off].value = malloc(1 *
- sizeof(*cfg->vpd.vpd_ros[off].value),
- M_DEVBUF, M_WAITOK);
- cfg->vpd.vpd_ros[off].value[0] = '\x00';
- } else
- cfg->vpd.vpd_ros[off].value = malloc(
- (dflen + 1) *
- sizeof(*cfg->vpd.vpd_ros[off].value),
- M_DEVBUF, M_WAITOK);
- remain -= 3;
- i = 0;
- /* keep in sync w/ state 3's transistions */
- if (dflen == 0 && remain == 0)
- state = 0;
- else if (dflen == 0)
- state = 2;
- else
- state = 3;
- break;
+static size_t
+next_vpd_rw_elem(struct vpd_readstate *vrs, int maxsize)
+{
+ struct pcicfg_vpd *vpd;
+ pcicfgregs *cfg;
+ struct vpd_write *vpd_w;
+ int len;
- case 3: /* VPD-R Keyword Value */
- cfg->vpd.vpd_ros[off].value[i++] = byte;
- if (strncmp(cfg->vpd.vpd_ros[off].keyword,
- "RV", 2) == 0 && cksumvalid == -1) {
- if (vrs.cksum == 0)
- cksumvalid = 1;
- else {
- if (bootverbose)
- pci_printf(cfg,
- "bad VPD cksum, remain %hhu\n",
- vrs.cksum);
- cksumvalid = 0;
- state = -1;
- break;
- }
- }
- dflen--;
- remain--;
- /* keep in sync w/ state 2's transistions */
- if (dflen == 0)
- cfg->vpd.vpd_ros[off++].value[i++] = '\0';
- if (dflen == 0 && remain == 0) {
- cfg->vpd.vpd_rocnt = off;
- cfg->vpd.vpd_ros = reallocf(cfg->vpd.vpd_ros,
- off * sizeof(*cfg->vpd.vpd_ros),
- M_DEVBUF, M_WAITOK | M_ZERO);
- state = 0;
- } else if (dflen == 0)
- state = 2;
- break;
+ cfg = vrs->cfg;
+ vpd = &cfg->vpd;
- case 4:
- remain--;
- if (remain == 0)
- state = 0;
- break;
+ if (maxsize < 3)
+ return (-1);
+ vpd->vpd_w = alloc_buffer(vpd->vpd_w, sizeof(*vpd->vpd_w), vpd->vpd_wcnt);
+ if (vpd->vpd_w == NULL) {
+ pci_printf(cfg, "out of memory");
+ return (-1);
+ }
+ vpd_w = &vpd->vpd_w[vpd->vpd_wcnt];
+ maxsize -= 3;
+ vpd_w->start = vrs->off + 3 - vrs->bytesinval;
+ len = vpd_read_elem_data(vrs, vpd_w->keyword, &vpd_w->value, maxsize);
+ if (vpd_w->value == NULL)
+ return (-1);
+ vpd_w->len = len;
+ vpd->vpd_wcnt++;
- case 5: /* VPD-W Keyword Header */
- if (off == alloc) {
- cfg->vpd.vpd_w = reallocf(cfg->vpd.vpd_w,
- (alloc *= 2) * sizeof(*cfg->vpd.vpd_w),
- M_DEVBUF, M_WAITOK | M_ZERO);
- }
- cfg->vpd.vpd_w[off].keyword[0] = byte;
- if (vpd_nextbyte(&vrs, &byte2)) {
- state = -2;
- break;
- }
- cfg->vpd.vpd_w[off].keyword[1] = byte2;
- if (vpd_nextbyte(&vrs, &byte2)) {
- state = -2;
- break;
- }
- cfg->vpd.vpd_w[off].len = dflen = byte2;
- cfg->vpd.vpd_w[off].start = vrs.off - vrs.bytesinval;
- cfg->vpd.vpd_w[off].value = malloc((dflen + 1) *
- sizeof(*cfg->vpd.vpd_w[off].value),
- M_DEVBUF, M_WAITOK);
- remain -= 3;
- i = 0;
- /* keep in sync w/ state 6's transistions */
- if (dflen == 0 && remain == 0)
- state = 0;
- else if (dflen == 0)
- state = 5;
- else
- state = 6;
- break;
+ return (vpd_w->len + 3);
+}
- case 6: /* VPD-W Keyword Value */
- cfg->vpd.vpd_w[off].value[i++] = byte;
- dflen--;
- remain--;
- /* keep in sync w/ state 5's transistions */
- if (dflen == 0)
- cfg->vpd.vpd_w[off++].value[i++] = '\0';
- if (dflen == 0 && remain == 0) {
- cfg->vpd.vpd_wcnt = off;
- cfg->vpd.vpd_w = reallocf(cfg->vpd.vpd_w,
- off * sizeof(*cfg->vpd.vpd_w),
- M_DEVBUF, M_WAITOK | M_ZERO);
- state = 0;
- } else if (dflen == 0)
- state = 5;
- break;
+static void
+vpd_free(struct pcicfg_vpd *vpd)
+{
+ int i;
- default:
- pci_printf(cfg, "invalid state: %d\n", state);
- state = -1;
- break;
- }
+ free(vpd->vpd_ident, M_DEVBUF);
+ for (i = 0; i < vpd->vpd_rocnt; i++)
+ free(vpd->vpd_ros[i].value, M_DEVBUF);
+ free(vpd->vpd_ros, M_DEVBUF);
+ vpd->vpd_rocnt = 0;
+ for (i = 0; i < vpd->vpd_wcnt; i++)
+ free(vpd->vpd_w[i].value, M_DEVBUF);
+ free(vpd->vpd_w, M_DEVBUF);
+ vpd->vpd_wcnt = 0;
+}
+
+#define VPD_TAG_END ((0x0f << 3) | 0) /* small tag, len == 0 */
+#define VPD_TAG_IDENT (0x02 | 0x80) /* large tag */
+#define VPD_TAG_RO (0x10 | 0x80) /* large tag */
+#define VPD_TAG_RW (0x11 | 0x80) /* large tag */
+
+static int
+pci_parse_vpd(device_t pcib, pcicfgregs *cfg)
+{
+ struct vpd_readstate vrs;
+ int cksumvalid;
+ int size, elem_size;
+
+ /* init vpd reader */
+ vrs.bytesinval = 0;
+ vrs.off = 0;
+ vrs.pcib = pcib;
+ vrs.cfg = cfg;
+ vrs.cksum = 0;
+
+ /* read VPD ident element - mandatory */
+ size = vpd_read_tag_size(&vrs, VPD_TAG_IDENT);
+ cfg->vpd.vpd_ident = vpd_read_value(&vrs, size);
+ if (cfg->vpd.vpd_ident == NULL) {
+ pci_printf(cfg, "No VPD ident found\n");
+ return (0);
}
- if (cksumvalid == 0 || state < -1) {
- /* read-only data bad, clean up */
- if (cfg->vpd.vpd_ros != NULL) {
- for (off = 0; cfg->vpd.vpd_ros[off].value; off++)
- free(cfg->vpd.vpd_ros[off].value, M_DEVBUF);
- free(cfg->vpd.vpd_ros, M_DEVBUF);
- cfg->vpd.vpd_ros = NULL;
- }
+ /* read VPD RO elements - mandatory */
+ size = vpd_read_tag_size(&vrs, VPD_TAG_RO);
+ if (size <= 0) {
+ pci_printf(cfg, "no read-only VPD data found\n");
+ return (0);
}
- if (state < -1) {
- /* I/O error, clean up */
- pci_printf(cfg, "failed to read VPD data.\n");
- if (cfg->vpd.vpd_ident != NULL) {
- free(cfg->vpd.vpd_ident, M_DEVBUF);
- cfg->vpd.vpd_ident = NULL;
+ while (size > 0) {
+ elem_size = next_vpd_ro_elem(&vrs, size);
+ if (elem_size < 0) {
+ pci_printf(cfg, "error accessing read-only VPD data\n");
+ return (-1);
}
- if (cfg->vpd.vpd_w != NULL) {
- for (off = 0; cfg->vpd.vpd_w[off].value; off++)
- free(cfg->vpd.vpd_w[off].value, M_DEVBUF);
- free(cfg->vpd.vpd_w, M_DEVBUF);
- cfg->vpd.vpd_w = NULL;
+ size -= elem_size;
+ }
+ cksumvalid = (vrs.cksum == 0);
+ if (!cksumvalid)
+ return (-1);
+
+ /* read VPD RW elements - optional */
+ size = vpd_read_tag_size(&vrs, VPD_TAG_RW);
+ if (size < 0)
+ return (-1);
+ while (size > 0) {
+ elem_size = next_vpd_rw_elem(&vrs, size);
+ if (elem_size < 0) {
+ pci_printf(cfg, "error accessing writeable VPD data\n");
+ return (-1);
}
+ size -= elem_size;
+ }
+
+ /* read empty END tag - mandatory */
+ size = vpd_read_tag_size(&vrs, VPD_TAG_END);
+ if (size != 0) {
+ pci_printf(cfg, "No valid VPD end tag found\n");
}
+ return (0);
+}
+
+static void
+pci_read_vpd(device_t pcib, pcicfgregs *cfg)
+{
+ int status;
+
+ status = pci_parse_vpd(pcib, cfg);
+ if (status < 0)
+ vpd_free(&cfg->vpd);
cfg->vpd.vpd_cached = 1;
+}
#undef REG
#undef WREG
-}
int
pci_get_vpd_ident_method(device_t dev, device_t child, const char **identptr)
@@ -2744,19 +2799,12 @@
{
struct devlist *devlist_head;
struct pci_map *pm, *next;
- int i;
devlist_head = &pci_devq;
- if (dinfo->cfg.vpd.vpd_reg) {
- free(dinfo->cfg.vpd.vpd_ident, M_DEVBUF);
- for (i = 0; i < dinfo->cfg.vpd.vpd_rocnt; i++)
- free(dinfo->cfg.vpd.vpd_ros[i].value, M_DEVBUF);
- free(dinfo->cfg.vpd.vpd_ros, M_DEVBUF);
- for (i = 0; i < dinfo->cfg.vpd.vpd_wcnt; i++)
- free(dinfo->cfg.vpd.vpd_w[i].value, M_DEVBUF);
- free(dinfo->cfg.vpd.vpd_w, M_DEVBUF);
- }
+ if (dinfo->cfg.vpd.vpd_reg)
+ vpd_free(&dinfo->cfg.vpd);
+
STAILQ_FOREACH_SAFE(pm, &dinfo->cfg.maps, pm_link, next) {
free(pm, M_DEVBUF);
}
diff --git a/sys/dev/pci/pci_user.c b/sys/dev/pci/pci_user.c
--- a/sys/dev/pci/pci_user.c
+++ b/sys/dev/pci/pci_user.c
@@ -562,7 +562,7 @@
{
struct pci_vpd_element vpd_element, *vpd_user;
struct pcicfg_vpd *vpd;
- size_t len;
+ size_t len, datalen;
int error, i;
vpd = pci_fetch_vpd_list(dev);
@@ -593,16 +593,17 @@
* Copyout the identifier string followed by each keyword and
* value.
*/
+ datalen = strlen(vpd->vpd_ident);
+ KASSERT(datalen <= 255, ("invalid VPD ident length"));
vpd_user = lvio->plvi_data;
vpd_element.pve_keyword[0] = '\0';
vpd_element.pve_keyword[1] = '\0';
vpd_element.pve_flags = PVE_FLAG_IDENT;
- vpd_element.pve_datalen = strlen(vpd->vpd_ident);
+ vpd_element.pve_datalen = datalen;
error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
if (error)
return (error);
- error = copyout(vpd->vpd_ident, vpd_user->pve_data,
- strlen(vpd->vpd_ident));
+ error = copyout(vpd->vpd_ident, vpd_user->pve_data, datalen);
if (error)
return (error);
vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen);

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 30, 9:45 AM (15 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28098367
Default Alt Text
D34268.id102753.diff (15 KB)

Event Timeline