Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F143359736
D34268.id102753.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D34268.id102753.diff
View Options
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, ®))
- 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
Details
Attached
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)
Attached To
Mode
D34268: Re-implement PCI VPD fetch
Attached
Detach File
Event Timeline
Log In to Comment