And here's the rest of the review, as promised. Please note that I do not have the stgandard at hand, so this is not a correctness review, but a code review.
Am 31.01.2012 06:59 schrieb Stefan Tauner:
--- /dev/null +++ b/sfdp.c @@ -0,0 +1,355 @@
[...]
+static int sfdp_fill_flash(struct flashctx *f, uint8_t *buf, uint16_t len)
*flash instead of *f, please.
+{
- uint32_t tmp32;
- uint8_t tmp8;
- uint32_t total_size; /* in bytes */
- uint32_t bsize;
- uint8_t opcode_4k = 0xFF;
- int dw, j;
- msg_cdbg("Parsing JEDEC SFDP parameter table... ");
... JEDEC flash parameter table...
- if (len != 9 * 4 && len != 4 * 4) {
msg_cerr("%s: len out of spec\n", __func__);
return 1;
- }
- msg_cdbg2("\n");
- /* 1. double word */
- dw = 0;
- tmp32 = buf[(4 * dw) + 0];
- tmp32 |= ((unsigned int)buf[(4 * dw) + 1]) << 8;
- tmp32 |= ((unsigned int)buf[(4 * dw) + 2]) << 16;
- tmp32 |= ((unsigned int)buf[(4 * dw) + 3]) << 24;
- tmp8 = (tmp32 >> 17) & 0x3;
- switch (tmp8) {
- case 0x0:
msg_cdbg2(" 3-Byte only addressing.\n");
break;
- case 0x1:
msg_cdbg2(" 3-Byte (and optionally 4-Byte) addressing.\n");
break;
- case 0x2:
msg_cdbg(" 4-Byte only addressing not supported.\n");
return 1;
- default:
msg_cdbg(" Required addressing mode (0x%x) not supported.\n",
tmp8);
return 1;
- }
- msg_cdbg2(" Writes to the status register have ");
- if (tmp32 & (1 << 3)) {
msg_cdbg2("to be enabled with ");
if (tmp32 & (1 << 4)) {
f->feature_bits = FEATURE_WRSR_WREN;
msg_cdbg2("WREN (0x06).\n");
} else {
f->feature_bits = FEATURE_WRSR_EWSR;
msg_cdbg2("EWSR (0x50).\n");
}
- } else
msg_cdbg2("not to be especially enabled.\n");
The "Writing to Volatile Status Register" part of JESD216 is one of the most confusing wordings I ever saw in a standard. I expect some flash chip vendors to conform to the letter of the spec which will cause pretty explosions: If the status register is nonvolatile and needs EWSR or WREN for writes, the standard explicitly requires the vendor to set bits 3+4 to 0 (EWSR/WREN not needed). I don't think JEDEC understood the implications of that wording.
Suggestion for a standards-conforming code flow: msg_cdbg2(" Status register is "); if (tmp32 & (1 << 3)) { msg_cdbg2("volatile and writes to the status register have to be enabled with "); [your bit 4 code] } else msg_cdbg2("nonvolatile and the standard does not allow vendors to tell us whether EWSR/WREN is needed for status register writes");
- msg_cdbg2(" Write granularity is ");
I know they call it write granularity, but flashrom calls it writechunk size. Please use our terminology here even if the standard calls it differently. You can add a comment if you think that clarifies the code for future developers.
- if (tmp32 & (1 << 2)) {
msg_cdbg2("at least 64 B.\n");
f->write = spi_chip_write_256;
Please insert flash->page_size = 64;
- } else {
msg_cdbg2("1 B only.\n");
f->write = spi_chip_write_1;
flash->page_size = 256; (I know that page_size needs to die, but right now we use it.)
- }
- if ((tmp32 & 0x3) == 0x1) {
opcode_4k = (tmp32 >> 8) & 0xFF; /* will be dealt with later */
- }
What about this instead?
switch (tmp32 & 0x03) { case 0x0: case 0x2: msg_cerr("4k erase is reserved... should not happen"); return 1; break; case 0x1: opcode_4k = (tmp32 >> 8) & 0xFF; /* will be dealt with later */ break; case 0x3: if (((tmp32 >> 8) & 0xFF) != 0xFF) { msg_cdbg("wtf inconsistent 4k erase settings?!?"); return 1; } } Admittedly, the wording could be better.
- /* 2. double word */
- dw = 1;
- tmp32 = buf[(4 * dw) + 0];
- tmp32 |= ((unsigned int)buf[(4 * dw) + 1]) << 8;
- tmp32 |= ((unsigned int)buf[(4 * dw) + 2]) << 16;
- tmp32 |= ((unsigned int)buf[(4 * dw) + 3]) << 24;
- if (tmp32 & (1 << 31)) {
msg_cerr("Flash chip size >= 4 Gb/512 MB not supported.\n");
return 1;
- }
- total_size = ((tmp32 & 0x7FFFFFFF) + 1) / 8;
- f->total_size = total_size / 1024;
- msg_cdbg2(" Flash chip size is %d kB.\n", f->total_size);
- /* FIXME: double words 3-7 contain unused fast read information */
- if (len == 4 * 4) {
msg_cdbg("It seems like this chip supports the preliminary "
"Intel version of SFDP, skipping processing of double "
"words 3-9.\n");
goto proc_4k;
- }
- dw = 8;
- for (j = 0; j < 4; j++) {
/* 8 double words from the start + 2 words for every eraser */
tmp32 = buf[(4 * dw) + (2 * j)];
use tmp8 instead?
if (tmp32 == 0) {
msg_cdbg2(" Block eraser %d is unused.\n", j);
continue;
}
if (tmp32 >= 31) {
msg_cdbg2(" Block size of eraser %d (2^%d) is too big."
"... too big for flashrom."
"\n", j, tmp32);
continue;
}
bsize = 1 << (tmp32); /* bsize = 2 ^ field */
tmp8 = buf[(4 * dw) + (2 * j) + 1];
if(sfdp_add_uniform_eraser(f, tmp8, bsize))
continue;
/* If there is a valid 4k value in the last double words,
* we want to override the value from double word 1, hence force
* skipping its processing: */
if (bsize == 4 * 1024)
opcode_4k = 0xFF;
Not really. What happens if there are multiple valid opcodes for 4k erase? Such chips do exist IIRC. What about if (bsize == 4 * 1024) { if (tmp8 == opcode_4k) opcode_4k == 0xFF; else msg_cdbg("More than one 4kB eraser opcode found: 0x%02x and 0x%02x.", tmp8, opcode_4k); }
- }
+proc_4k:
- if (opcode_4k != 0xFF) {
sfdp_add_uniform_eraser(f, opcode_4k, 4 * 1024);
- }
- msg_cdbg("done.\n");
- return 0;
+}
+static int sfdp_fetch_pt(struct flashctx *flash, uint32_t addr, uint8_t *buf, uint16_t len) +{
- uint16_t i;
- if (spi_sfdp(flash, addr, buf, len)) {
msg_cerr("Receiving SFDP parameter table failed.\n");
return 1;
- }
- msg_cspew(" Parameter table contents:\n");
- for(i = 0; i < len; i++) {
if ((i % 8) == 0) {
msg_cspew(" 0x%03x: ", i);
}
msg_cspew(" 0x%02x", buf[i]);
if ((i % 8) == 7) {
msg_cspew("\n");
continue;
}
if ((i % 8) == 3) {
msg_cspew(" ");
continue;
}
- }
- msg_cspew("\n");
Do we have some generic hexdump() function? I agree that dumping the parameter table contents may make sense, but open-coding your own hexdump is probably not the best idea. Do we want this hexdump functionality at all, and if yes, should it be factored out?
- return 0;
+}
+int probe_spi_sfdp(struct flashctx *flash) +{
- int ret = 0;
- uint8_t buf[8];
- uint32_t tmp32;
- uint8_t nph;
- /* need to limit the table loop by comparing i to uint8_t nph hence: */
- uint16_t i;
- struct sfdp_tbl_hdr *hdrs;
- uint8_t *hbuf;
- uint8_t *tbuf;
- if (spi_sfdp(flash, 0x00, buf, 4)) {
msg_cerr("Receiving SFDP signature failed.\n");
Hmmm... should all boards with IT87/ICH SPI and unknown flash chips see an error message? While I'm often for upgrading error messages to msg_*err, I believe that this case should be rather mag_*dbg.
return 0;
- }
- tmp32 = buf[0];
- tmp32 |= ((unsigned int)buf[1]) << 8;
- tmp32 |= ((unsigned int)buf[2]) << 16;
- tmp32 |= ((unsigned int)buf[3]) << 24;
- msg_cdbg2("SFDP signature = 0x%08x (should be 0x50444653)\n", tmp32);
- if (tmp32 != 0x50444653) {
msg_cdbg("No SFDP signature found.\n");
return 0;
- }
- if (spi_sfdp(flash, 0x04, buf, 3)) {
msg_cerr("Receiving SFDP revision and number of parameter "
"headers (NPH) failed. ");
return 0;
- }
- msg_cdbg2("SFDP revision = %d.%d\n", buf[1], buf[0]);
return 0 if major revision is unknown (i.e. not 0x01). A new major revision may change the meaning of any field and that's why we should abort.
- nph = buf[2];
- msg_cdbg2("SFDP number of parameter headers (NPH) = %d (+ 1 mandatory)"
"\n", nph);
- /* Fetch all parameter headers, even if we don't use them all (yet). */
- hbuf = malloc(sizeof(struct sfdp_tbl_hdr) * (nph + 1));
- hdrs = malloc((nph + 1) * 8);
Why is 8 a magic unexplained constant for hdrs allocation, but sizeof struct sfdp_tbl_hdr (which is 8 as well) is used for hbuf allocation? Did you mix up the two by accident? And why is (nph+1) the first factor in the second malloc and the second factor in the first malloc?
- if (hbuf == NULL || hdrs == NULL ) {
msg_gerr("Out of memory!\n");
insert the following code: ret = 0; goto cleanup_hdrs;
exit(1); /* FIXME: shutdown gracefully */
and kill the exit(1). It would be nice to change the probe interface to return 0 on success... that would allow us to return detailed errors. OTOH, we might want to use the probe interface to return match accuracy, in which case 0 would be nomatch. Comments appreciated.
- }
- if (spi_sfdp(flash, 0x08, hbuf, (nph + 1) * 8)) {
msg_cerr("Receiving SFDP parameter table headers failed.\n");
goto cleanup_hdrs;
- }
- i = 0;
- do {
for (i=0; i <=nph; i++) {
uint16_t len;
hdrs[i].id = hbuf[(8 * i) + 0];
hdrs[i].v_minor = hbuf[(8 * i) + 1];
hdrs[i].v_major = hbuf[(8 * i) + 2];
hdrs[i].len = hbuf[(8 * i) + 3];
hdrs[i].ptp = hbuf[(8 * i) + 4];
hdrs[i].ptp |= ((unsigned int)hbuf[(8 * i) + 5]) << 8;
hdrs[i].ptp |= ((unsigned int)hbuf[(8 * i) + 6]) << 16;
msg_cdbg2("SFDP parameter table header %d/%d:\n", i, nph);
msg_cdbg2(" ID 0x%02x, version %d.%d\n", hdrs[i].id,
hdrs[i].v_major, hdrs[i].v_minor);
len = hdrs[i].len * 4;
tmp32 = hdrs[i].ptp;
msg_cdbg2(" Length %d B, Parameter Table Pointer 0x%06x\n",
len, tmp32);
if (len + tmp32 > UINT16_MAX) {
msg_cerr("SFDP Parameter Table %d supposedly "
"overflows addressable SFDP area. This most\n"
"probably indicates a corrupt SFDP parameter "
"table header. Aborting SFDP probe!\n", i);
ret = 0;
goto cleanup_hdrs;
}
tbuf = malloc(len);
if (tbuf == NULL) {
msg_gerr("Out of memory!\n");
insert the following code: ret = 0; goto cleanup_hdrs;
exit(1); /* FIXME: shutdown gracefully */
and kill the exit(1)
}
if (sfdp_fetch_pt(flash, tmp32, tbuf, len)){
msg_cerr("Fetching SFDP parameter table %d failed.\n",
i);
free(tbuf);
break;
}
if (i == 0) { /* Mandatory JEDEC SFDP parameter table */
if (hdrs[i].id != 0)
msg_cdbg("ID of the mandatory JEDEC SFDP "
msg_cerr, then cleanup and return 0. Maybe even ask user to report?
"parameter table is not 0 as demanded "
"by JESD216 (warning only).\n");
Check hdrs[i].v_major here and do the error dance for unexpected values.
if (len != 9 * 4 && len != 4 * 4) {
msg_cdbg("Length of the mandatory JEDEC SFDP "
msg_cerr, then cleanup and return 0. Maybe even ask user to report?
"parameter table is %d B instead of "
"36 B (i.e. 9 double words) as "
"demanded by JESD216, skipping "
"parsing.\n", len);
} else if (sfdp_fill_flash(flash, tbuf, len) == 0)
ret = 1;
}
free(tbuf);
i++;
- } while(i <= nph);
+cleanup_hdrs:
- free(hdrs);
- free(hbuf);
- return ret;
+}
Cross-checked against the standard, looks pretty good.
Next round will be acked.
Regards, Carl-Daniel