Skip to content

[WIP] Add support for reading from a raw fru dump #9

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion fru.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,6 @@ fru_field_t * fru_encode_data(int len, const uint8_t *data)
* For binary data use FRU_FIELDDATALEN(field->typelen) to find
* out the size of the returned buffer.
*/
static
unsigned char * fru_decode_data(const fru_field_t *field)
{
unsigned char * out;
Expand Down
1 change: 1 addition & 0 deletions fru.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ fru_chassis_area_t * fru_chassis_info(const fru_exploded_chassis_t *chassis);
fru_board_area_t * fru_board_info(const fru_exploded_board_t *board);
fru_product_area_t * fru_product_info(const fru_exploded_product_t *product);
fru_field_t * fru_encode_data(int len, const uint8_t *data);
unsigned char * fru_decode_data(const fru_field_t *field);
fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size);

#endif // __FRULIB_FRU_H__
167 changes: 166 additions & 1 deletion frugen.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,61 @@ bool json_fill_fru_area_custom(json_object *jso, fru_reclist_t **custom)
}
#endif /* __HAS_JSON__ */

static void safe_read(int fd, void *buffer, size_t length) {
size_t total_bytes_read = 0;
while (total_bytes_read != length) {
ssize_t bytes_read = read(
fd, buffer + total_bytes_read, length - total_bytes_read);
if (bytes_read == -1)
fatal("Error reading file");

total_bytes_read += bytes_read;
}
}

static void fd_read_field(int fd, uint8_t *out) {
uint8_t typelen;
safe_read(fd, &typelen, 1);

size_t length = FRU_FIELDDATALEN(typelen);
fru_field_t *field = calloc(1, FRU_FIELDSIZE(typelen));
if (field == NULL)
fatal("Could not allocate field");
field->typelen = typelen;
safe_read(fd, &(field->data), length);

char *data = fru_decode_data(field);
if (data == NULL)
fatal("Could not decode field");

memcpy(out, data, strnlen(data, 2 * length) + 1);
free(data);
free(field);
}

static void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) {
while (true) {
uint8_t typelen;
safe_read(fd, &typelen, 1);
if (typelen == 0xc1)
break;

fru_reclist_t *custom_field = add_reclist(reclist);
if (custom_field == NULL)
fatal("Error allocating custom field");

size_t length = FRU_FIELDDATALEN(typelen);
uint8_t *data = malloc(length + 1);
if (data == NULL)
fatal("Error allocating custom field");
safe_read(fd, data, length);
data[length] = 0;

custom_field->rec = fru_encode_data(LEN_AUTO, data);
free(data);
}
}

int main(int argc, char *argv[])
{
int i;
Expand Down Expand Up @@ -264,6 +319,9 @@ int main(int argc, char *argv[])
/* Set input file format to JSON */
{ .name = "json", .val = 'j', .has_arg = false },

/* Set input file format to binary */
{ .name = "binaryformat", .val = 'x', .has_arg = false },
aneeshdurg marked this conversation as resolved.
Show resolved Hide resolved

/* Set file to load the data from */
{ .name = "from", .val = 'z', .has_arg = true },

Expand Down Expand Up @@ -302,6 +360,7 @@ int main(int argc, char *argv[])
"\n\t\t"
"There must be an even number of characters in a 'binary' argument",
['j'] = "Set input text file format to JSON (default). Specify before '--from'",
['x'] = "Set input file format to binary. Specify before '--from'",
['z'] = "Load FRU information from a text file",
/* Chassis info area related options */
['t'] = "Set chassis type (hex). Defaults to 0x02 ('Unknown')",
Expand Down Expand Up @@ -336,7 +395,8 @@ int main(int argc, char *argv[])
has_internal = false,
has_multirec = false;

bool use_json = true; /* TODO: Add more input formats, consider libconfig */
bool use_json = false; /* TODO: Add more input formats, consider libconfig */
bool use_binary = false;

do {
fru_reclist_t **custom = NULL;
Expand Down Expand Up @@ -376,6 +436,16 @@ int main(int argc, char *argv[])

case 'j': // json
use_json = true;
if (use_binary) {
fatal("Can't specify --json and --binaryformat together");
}
break;

case 'x': // binary
use_binary = true;
if (use_json) {
fatal("Can't specify --json and --binaryformat together");
}
break;

case 'z': // from
Expand Down Expand Up @@ -460,6 +530,101 @@ int main(int argc, char *argv[])
fatal("JSON support was disabled at compile time");
#endif
}
else if (use_binary) {
int fd = open(optarg, O_RDONLY);
if (fd < 0) {
perror("");
aneeshdurg marked this conversation as resolved.
Show resolved Hide resolved
fatal("Failed to open file");
}

char common_header[8];
aneeshdurg marked this conversation as resolved.
Show resolved Hide resolved
safe_read(fd, common_header, 8);

// Common Header Format Version = common_header[0]
// Internal Use Area Starting Offset = common_header[1]
uint16_t chassis_start_offset = 8 * common_header[2];
uint16_t board_start_offset = 8 * common_header[3];
uint16_t product_start_offset = 8 * common_header[4];
// MultiRecord Area Starting Offset = 8 * common_header[5]
// Padding = common_header[6] = 0
// Checksum = common_header[7]

// For the above offsets, an offset of 0 is valid and implies
// that the field is not present.

bool data_has_chassis = chassis_start_offset != 0;
bool data_has_board = board_start_offset != 0;
bool data_has_product = product_start_offset != 0;

if (data_has_chassis) {
aneeshdurg marked this conversation as resolved.
Show resolved Hide resolved
lseek(fd, chassis_start_offset, SEEK_SET);

uint8_t chassis_header[3];
safe_read(fd, chassis_header, 3);
if (chassis_header[0] != 1)
fatal("Unsupported Chassis Info Area Format Version");
// Chassis Info Area Length = 8 * chassis_header[1]
chassis.type = chassis_header[2];
fd_read_field(fd, chassis.pn);
fd_read_field(fd, chassis.serial);
fd_fill_custom_fields(fd, &chassis.cust);

has_chassis = true;
}
if (data_has_board) {
lseek(fd, board_start_offset, SEEK_SET);

uint8_t board_header[3];
safe_read(fd, board_header, 3);
if (board_header[0] != 1)
fatal("Unsupported Board Info Area Format Version");
// Board Info Area Length = 8 * board_header[1]
board.lang = board_header[2];

uint32_t min_since_1996 = 0;
safe_read(fd, &min_since_1996, 3);
struct tm tm_1996 = {
.tm_year = 96,
.tm_mon = 0,
.tm_mday = 1
};
// The argument to mktime is zoneless
board.tv.tv_sec = mktime(&tm_1996) + 60 * min_since_1996;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a very similar piece of code in fru.c, please think about some function that would allow to get rid of repetitions.


fd_read_field(fd, board.mfg);
fd_read_field(fd, board.pname);
fd_read_field(fd, board.serial);
fd_read_field(fd, board.pn);
fd_read_field(fd, board.file);
fd_fill_custom_fields(fd, &board.cust);

has_board = true;
has_bdate = true;
}
if (data_has_product) {
lseek(fd, product_start_offset, SEEK_SET);

uint8_t product_header[3];
safe_read(fd, product_header, 3);
if (product_header[0] != 1)
fatal("Unsupported Board Info Area Format Version");
// Product Info Area Length = 8 * product_header[1]
product.lang = product_header[2];

fd_read_field(fd, product.mfg);
fd_read_field(fd, product.pname);
fd_read_field(fd, product.pn);
fd_read_field(fd, product.ver);
fd_read_field(fd, product.serial);
fd_read_field(fd, product.atag);
fd_read_field(fd, product.file);
fd_fill_custom_fields(fd, &product.cust);

has_product = true;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you're losing the "internal use" and "multi-record" areas here. The output file won't have them even if the original raw file did. That may defeat the purpose of your change as I understand it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need those right now. If it's alright, I'd like to omit support for multirec and internal in the output for now. The internal area is pretty straightforward, but the multirec area seems to require some parsing, which I'd prefer to not have to implement for this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's ok because libfru initially didn't support creating those areas, but please specify in the help that users may lose their pre-existing areas when using --raw, and also please update the README.md with the information about the new feature and its limitations.


close(fd);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, that code block is large enough for a separate function.

else {
fatal("The requested input file format is not supported");
}
Expand Down