Index: vpd.c |
diff --git a/vpd.c b/vpd.c |
index c1397ae5eddde520bf88755dfb3a32f024d3b190..45fbc1d66d319feb170e26c6ff25190a28961725 100644 |
--- a/vpd.c |
+++ b/vpd.c |
@@ -18,11 +18,12 @@ |
*/ |
#include <assert.h> |
#include <errno.h> |
+#include <getopt.h> |
#include <inttypes.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
-#include <getopt.h> |
+#include <uuid/uuid.h> |
#include "lib/flashrom.h" |
#include "lib/fmap.h" |
#include "lib/lib_vpd.h" |
@@ -52,16 +53,43 @@ unsigned char buf[BUF_LEN]; |
int buf_len = 0; |
int max_buf_len = sizeof(buf); |
-/* The EPS base address is used to fill the EPS table entry. */ |
-uint32_t eps_base = GOOGLE_VPD_2_0_EPS_BASE; |
+/* The EPS base address used to fill the EPS table entry. |
+ * If the VPD partition can be found in fmap, this points to the starting |
+ * offset of VPD partition. If not found, this is used to be the base address |
+ * to increase SPD and VPD 2.0 offset fields. |
+ * |
+ * User can overwrite this by -E argument. |
+ */ |
+uint32_t eps_base = GOOGLE_EPS_BASE; |
/* the fmap name of VPD. */ |
uint8_t fmap_vpd_area_name[FMAP_STRLEN] = "RO VPD"; |
+ |
/* If found_vpd, replace the VPD partition when saveFile(). |
* If not found, always create new file when saveFlie(). */ |
int found_vpd = 0; |
-/* The VPD offset and size in buf[] */ |
-off_t vpd_offset = 0, vpd_size = 0; |
+ |
+/* The VPD partition offset and size in buf[]. The whole partition includes: |
+ * |
+ * SMBIOS EPS |
+ * SMBIOS tables[] |
+ * SPD |
+ * VPD 2.0 data |
+ * |
+ * For those offset values below, ABSENT means not presented in buffer. |
+ */ |
+off_t vpd_offset = 0, vpd_size; /* The whole partition */ |
+/* Below offset are related to vpd_offset and assume positive. |
+ * Those are used in saveFile() to write back data. */ |
+off_t eps_offset = 0; /* EPS's starting address. Tables[] is following. */ |
+off_t spd_offset = GOOGLE_SPD_OFFSET; /* SPD address .*/ |
+off_t vpd_2_0_offset = GOOGLE_VPD_2_0_OFFSET; /* VPD 2.0 data address. */ |
+ |
+/* This points to the SPD data if it is availiable when loadFile(). |
+ * The memory is allocated in loadFile(), will be used in saveFile(), |
+ * and freed at end of main(). */ |
+uint8_t *spd_data = NULL; |
+int32_t spd_len = 256; /* max value for DDR3 */ |
/* for debug purpose */ |
void dumpBuf() { |
@@ -77,7 +105,6 @@ void dumpBuf() { |
* the size of blob, the is function generates an SMBIOS ESP. |
*/ |
int buildEpsAndTables( |
- const int offset_blob, |
const int size_blob, |
const int max_buf_len, |
unsigned char *buf, |
@@ -93,10 +120,24 @@ int buildEpsAndTables( |
buf += *generated; |
- /* Generate type 241 */ |
+ /* Generate type 241 - SPD data */ |
table_len = vpd_append_type241(0, &table, table_len, |
+ GOOGLE_SPD_UUID, |
+ eps_base + GOOGLE_SPD_OFFSET, |
+ spd_len, /* Max length for DDR3 */ |
+ GOOGLE_SPD_VENDOR, |
+ GOOGLE_SPD_DESCRIPTION, |
+ GOOGLE_SPD_VARIANT); |
+ if (table_len < 0) { |
+ retval = VPD_FAIL; |
+ goto error_1; |
+ } |
+ num_structures++; |
+ |
+ /* Generate type 241 - VPD 2.0 */ |
+ table_len = vpd_append_type241(1, &table, table_len, |
GOOGLE_VPD_2_0_UUID, |
- eps_base + offset_blob, |
+ eps_base + GOOGLE_VPD_2_0_OFFSET, |
size_blob, |
GOOGLE_VPD_2_0_VENDOR, |
GOOGLE_VPD_2_0_DESCRIPTION, |
@@ -108,7 +149,7 @@ int buildEpsAndTables( |
num_structures++; |
/* Generate type 127 */ |
- table_len = vpd_append_type127(1, &table, table_len); |
+ table_len = vpd_append_type127(2, &table, table_len); |
if (table_len < 0) { |
retval = VPD_FAIL; |
goto error_1; |
@@ -178,11 +219,14 @@ int loadFile(const char *filename, struct PairContainer *container, |
struct vpd_entry *eps; |
struct vpd_header *header; |
struct vpd_table_binary_blob_pointer *data; |
+ uint8_t spd_uuid[16], vpd_2_0_uuid[16]; |
+ int expected_handle = 0; |
+ int table_len; |
int index; |
int retval = 0; |
if (!(fp = fopen(filename, "r"))) { |
- fprintf(stderr, "* File [%s] cannot be opened for read. Ignored.\n", |
+ fprintf(stderr, "[WARN] File [%s] cannot be opened for read. Ignored.\n", |
filename); |
return 0; |
} |
@@ -204,7 +248,7 @@ int loadFile(const char *filename, struct PairContainer *container, |
/* scan the file and find out the VPD partition. */ |
sig_offset = fmapFind(read_buf, file_size); |
if (-1 == sig_offset) { |
- /* FMAP signature is not found, assume it is pure VPD content. */ |
+ /* FMAP signature is not found, assume it is pure VPD partition. */ |
vpd_buf = read_buf; |
eps = (struct vpd_entry *)vpd_buf; |
@@ -223,16 +267,16 @@ int loadFile(const char *filename, struct PairContainer *container, |
if (FMAP_OK == fmapGetArea(fmap_vpd_area_name, fmap, |
&vpd_offset, &vpd_size)) { |
- fprintf(stderr, |
- "FMAP and area are found, eps_base changed from 0x%x to 0x%lx\n", |
- eps_base, vpd_offset); |
+ found_vpd = 1; /* Mark found here then saveFile() knows where to |
+ * write back (vpd_offset, vpd_size). */ |
eps_base = vpd_offset; |
vpd_buf = &read_buf[vpd_offset]; |
eps = (struct vpd_entry *)vpd_buf; |
/* In overwrite mode, the VPD content is erased before reading. */ |
if (overwrite_it) { |
- memset(vpd_buf, 0xff, vpd_size); |
+ retval = 0; |
+ goto teardown; |
} |
} else { |
fprintf(stderr, "The VPD partition [%s] is not found.\n", |
@@ -240,37 +284,122 @@ int loadFile(const char *filename, struct PairContainer *container, |
retval = 1; |
goto teardown; |
} |
- |
- /* jump if the VPD partition is not recognized. */ |
- if (memcmp(VPD_ENTRY_MAGIC, eps, sizeof(VPD_ENTRY_MAGIC) - 1)) { |
- /* But OKAY if the VPD partition is all-FF, which is un-used. */ |
- if (memcmp("\xff\xff\xff\xff", eps, sizeof(VPD_ENTRY_MAGIC) - 1)) { |
- fprintf(stderr, "FMAP found, but SMBIOS signature is not matched.\n"); |
- fprintf(stderr, "You may use -O to overwrite the data.\n"); |
- retval = 1; |
- goto teardown; |
- } |
+ } |
+ /* Now, vpd_buf points to the VPD partition in buffer. |
+ * eps points to the EPS structure, which is usually equal to vpd_buf. */ |
+ eps_offset = (uint8_t*)eps - vpd_buf; |
+ |
+ /* jump if the VPD partition is not recognized. */ |
+ if (memcmp(VPD_ENTRY_MAGIC, eps, sizeof(VPD_ENTRY_MAGIC) - 1)) { |
+ /* But OKAY if the VPD partition is all-FF, which is un-used. */ |
+ if (memcmp("\xff\xff\xff\xff", eps, sizeof(VPD_ENTRY_MAGIC) - 1)) { |
+ fprintf(stderr, "SMBIOS signature is not matched.\n"); |
+ fprintf(stderr, "You may use -O to overwrite the data.\n"); |
+ retval = 1; |
+ goto teardown; |
} |
- |
- /* mark the VPD is found, so that saveFile() knows where to overwrite |
- * (vpd_offset, vpd_size). */ |
- found_vpd = 1; |
+ /* TODO(yjlou): need more EPS sanity checks here. */ |
} |
- |
- /* Get type 241 blob */ |
+ /* EPS is done above. Parse structure tables below. */ |
+ /* Get the first type 241 blob, at the tail of EPS. */ |
header = (struct vpd_header*)(((uint8_t*)eps) + eps->entry_length); |
data = (struct vpd_table_binary_blob_pointer *) |
((uint8_t *)header + sizeof(*header)); |
- /* iterate all pairs */ |
- for (index = data->offset - eps_base; /* skip the EPS */ |
- vpd_buf[index] != VPD_TYPE_TERMINATOR && |
- vpd_buf[index] != VPD_TYPE_IMPLICIT_TERMINATOR;) { |
- if (VPD_OK != decodeVpdString(file_size, vpd_buf, container, &index)) { |
- fprintf(stderr, "decodeVpdString() error.\n"); |
+ /* TODO(yjlou): Re-factor the parsing code to support more SMBIOS entries. |
+ * The current code only supports 2 combinations: |
+ * 1. Type 241 (SPD) + Type 241 (VPD 2.0) + Type 127 |
+ * 2. Type 241 (VPD 2.0) + Type 127 |
+ */ |
+ |
+ /* Now header points to the first SMBIOS entry, and data points to the |
+ * first BBP entry. The first entry could be SPD data. We don't care. */ |
+ if (header->handle != expected_handle) { |
+ fprintf(stderr, "The first handle value must be 0, but is %d.\n", |
+ header->handle); |
+ retval = 1; |
+ goto teardown; |
+ } |
+ if (header->type != VPD_TYPE_BINARY_BLOB_POINTER) { |
+ fprintf(stderr, "Expect first entry is type Binary Blob Pointer (241)," |
+ " but actually is %d\n", header->type); |
+ fprintf(stderr, "You may use -O to overwrite the data.\n"); |
+ retval = 1; |
+ goto teardown; |
+ } |
+ uuid_parse(GOOGLE_SPD_UUID, spd_uuid); |
+ if (!memcmp(data->uuid, spd_uuid, sizeof(data->uuid))) { |
+ ++expected_handle; |
+ spd_offset = data->offset - eps_base; |
+ spd_len = data->size; |
+ if (vpd_offset + spd_offset + spd_len >= file_size) { |
+ fprintf(stderr, "[ERROR] SPD offset in BBP is not correct.\n" |
+ " vpd=0x%x spd=0x%x len=0x%x file_size=0x%x\n" |
+ " If this file is VPD partition only, try to\n" |
+ " use -E to adjust offset values.\n", |
+ (uint32_t)vpd_offset, (uint32_t)spd_offset, |
+ spd_len, file_size); |
retval = 1; |
goto teardown; |
} |
+ if (!(spd_data = malloc(spd_len))) { |
+ fprintf(stderr, "spd_data: malloc(%d bytes) failed.\n", spd_len); |
+ retval = 1; |
+ goto teardown; |
+ } |
+ memcpy(spd_data, &read_buf[vpd_offset + spd_offset], spd_len); |
+ |
+ /* move to next table */ |
+ if ((table_len = vpd_type241_size(header)) < 0) { |
+ fprintf(stderr, "[ERROR] Cannot get type 241 structure table length.\n"); |
+ retval = 1; |
+ goto teardown; |
+ } |
+ header = (struct vpd_header*)((uint8_t*)header + table_len); |
+ data = (struct vpd_table_binary_blob_pointer *) |
+ ((uint8_t *)header + sizeof(*header)); |
+ } |
+ |
+ /* The 2nd could be VPD 2.0 data or End Of Table. */ |
+ if (header->handle != expected_handle) { |
+ fprintf(stderr, "The second handle value must be 1, but is %d.\n", |
+ header->handle); |
+ retval = 1; |
+ goto teardown; |
+ } |
+ uuid_parse(GOOGLE_VPD_2_0_UUID, vpd_2_0_uuid); |
+ if (header->type == VPD_TYPE_BINARY_BLOB_POINTER && |
+ !memcmp(data->uuid, vpd_2_0_uuid, sizeof(data->uuid))) { |
+ ++expected_handle; |
+ |
+ /* iterate all pairs */ |
+ for (index = data->offset - eps_base; /* skip the EPS */ |
+ vpd_buf[index] != VPD_TYPE_TERMINATOR && |
+ vpd_buf[index] != VPD_TYPE_IMPLICIT_TERMINATOR;) { |
+ if (VPD_OK != decodeVpdString(file_size, vpd_buf, container, &index)) { |
+ fprintf(stderr, "decodeVpdString() error.\n"); |
+ retval = 1; |
+ goto teardown; |
+ } |
+ } |
+ |
+ /* move to next table */ |
+ if ((table_len = vpd_type241_size(header)) < 0) { |
+ fprintf(stderr, "[ERROR] Cannot get type 241 structure table length.\n"); |
+ retval = 1; |
+ goto teardown; |
+ } |
+ header = (struct vpd_header*)((uint8_t*)header + table_len); |
+ data = (struct vpd_table_binary_blob_pointer *) |
+ ((uint8_t *)header + sizeof(*header)); |
+ } else { |
+ fprintf(stderr, "[WARN] no VPD 2.0 BBP is found, ignored.\n"); |
+ retval = 0; |
+ goto teardown; |
+ } |
+ |
+ if (header->type != VPD_TYPE_END) { |
+ fprintf(stderr, "[WARN] we expect the last one is type 127. Ignored.\n"); |
} |
teardown: |
@@ -286,6 +415,8 @@ int saveFile(const struct PairContainer *container, const char *filename) { |
int eps_len = 0; |
int retval = 0; |
+ memset(eps, 0, sizeof(eps)); |
+ |
/* encode into buffer */ |
if (VPD_OK != encodeContainer(&file, max_buf_len, buf, &buf_len)) { |
fprintf(stderr, "encodeContainer() error.\n"); |
@@ -298,29 +429,26 @@ int saveFile(const struct PairContainer *container, const char *filename) { |
goto teardown; |
} |
- if (VPD_OK != buildEpsAndTables(GOOGLE_VPD_2_0_OFFSET, buf_len, |
- sizeof(eps), eps, &eps_len)) { |
+ if (VPD_OK != buildEpsAndTables(buf_len, sizeof(eps), eps, &eps_len)) { |
fprintf(stderr, "Cannot build EPS.\n"); |
retval = 1; |
goto teardown; |
} |
- assert(eps_len <= GOOGLE_VPD_2_0_OFFSET); |
- eps_len = GOOGLE_VPD_2_0_OFFSET; |
+ assert(eps_len <= GOOGLE_SPD_OFFSET); |
+ /* Write data in the following order: |
+ * 1. EPS |
+ * 2. SPD |
+ * 3. VPD 2.0 |
+ */ |
if (found_vpd) { |
- /* We found VPD in file, which means file is existed. |
+ /* We found VPD partition in -f file, which means file is existed. |
* Instead of truncating the whole file, open to write partial. */ |
if (!(fp = fopen(filename, "r+"))) { |
fprintf(stderr, "File [%s] cannot be opened for write.\n", filename); |
retval = 1; |
goto teardown; |
} |
- /* Move file cursor to VPD section */ |
- if (fseek(fp, vpd_offset, SEEK_SET)) { |
- fprintf(stderr,"fseek(0x%lx) error: %s\n", vpd_offset, strerror(errno)); |
- retval = 1; |
- goto teardown; |
- } |
} else { |
/* VPD is not found, which means the file is pure VPD data. |
* Always creates the new file and overwrites the original content. */ |
@@ -330,13 +458,29 @@ int saveFile(const struct PairContainer *container, const char *filename) { |
goto teardown; |
} |
} |
+ |
+ /* write EPS */ |
+ fseek(fp, vpd_offset + eps_offset, SEEK_SET); |
if (fwrite(eps, eps_len, 1, fp) != 1) { |
fprintf(stderr, "fwrite(EPS) error (%s)\n", strerror(errno)); |
retval = 1; |
goto teardown; |
} |
+ |
+ /* write SPD */ |
+ if (spd_data) { |
+ fseek(fp, vpd_offset + spd_offset, SEEK_SET); |
+ if (fwrite(spd_data, spd_len, 1, fp) != 1) { |
+ fprintf(stderr, "fwrite(SPD) error (%s)\n", strerror(errno)); |
+ retval = 1; |
+ goto teardown; |
+ } |
+ } |
+ |
+ /* write VPD 2.0 */ |
+ fseek(fp, vpd_offset + vpd_2_0_offset, SEEK_SET); |
if (fwrite(buf, buf_len, 1, fp) != 1) { |
- fprintf(stderr, "fwrite(VPD) error (%s)\n", strerror(errno)); |
+ fprintf(stderr, "fwrite(VPD 2.0) error (%s)\n", strerror(errno)); |
retval = 1; |
goto teardown; |
} |
@@ -362,7 +506,7 @@ static void usage(const char *progname) { |
printf(" -p <pad length> Pad if length is shorter.\n"); |
printf(" -i <partition> Specify VPD partition name in fmap.\n"); |
printf(" -l List content in the file.\n"); |
- printf(" -O Overwrite current VPD partition.\n"); |
+ printf(" -O Overwrite and re-format VPD partition.\n"); |
printf("\n"); |
} |
@@ -520,6 +664,7 @@ int main(int argc, char *argv[]) { |
} |
teardown: |
+ if (spd_data) free(spd_data); |
if (filename) free(filename); |
destroyContainer(&file); |
destroyContainer(&argument); |