Message ID | 20220729215457.2697339-3-quic_jaehyoo@quicinc.com |
---|---|
State | Superseded |
Delegated to: | Michal Simek |
Headers | show |
Series | cmd/fru: move FRU handling support to common region | expand |
On 7/29/22 23:54, Jae Hyun Yoo wrote: > Add product info area parsing support. Custom product info field parsing > function 'fru_parse_product_custom' can be replaced with a board specific > implementation. > > Signed-off-by: Jae Hyun Yoo <quic_jaehyoo@quicinc.com> > --- > Changes from RFC: > * Added manufacturer custom product info fields parsing flow. Please, provide a unit test for the new functions. Best regards Heinrich > > common/fru_ops.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++- > include/fru.h | 22 +++++++ > 2 files changed, 182 insertions(+), 1 deletion(-) > > diff --git a/common/fru_ops.c b/common/fru_ops.c > index c03eeffbddc6..9f350f875035 100644 > --- a/common/fru_ops.c > +++ b/common/fru_ops.c > @@ -264,6 +264,91 @@ static int fru_parse_board(unsigned long addr) > return ret; > } > > +__weak int fru_parse_product_custom(unsigned long addr) > +{ > + int len; > + u8 type; > + > + do { > + len = fru_check_type_len(*(u8 *)addr, fru_data.prd.lang_code, > + &type); > + if (len == -EINVAL) > + break; > + > + addr += 1; > + > + /* Skip empty field */ > + if (!len) > + continue; > + > + if (DEBUG_PARSE_CUSTOM_FIELDS) > + print_hex_dump_bytes("Product Custom Field: ", > + DUMP_PREFIX_NONE, (u8 *)addr, len); > + > + addr += len; > + } while (true); > + > + return 0; > +} > + > +static int fru_parse_product(unsigned long addr) > +{ > + u8 i, type; > + int len, ret = 0; > + u8 *data, *term, *limit; > + > + memcpy(&fru_data.prd.ver, (void *)addr, 6); > + addr += 3; > + data = (u8 *)&fru_data.prd.manufacturer_type_len; > + > + /* Record max structure limit not to write data over allocated space */ > + limit = (u8 *)&fru_data.prd + sizeof(struct fru_product_data); > + > + for (i = 0; i < FRU_PRODUCT_AREA_TOTAL_FIELDS; > + i++, data += FRU_BOARD_MAX_LEN) { > + len = fru_check_type_len(*(u8 *)addr, fru_data.prd.lang_code, > + &type); > + /* > + * Stop cature if it end of fields > + */ > + if (len == -EINVAL) > + break; > + > + /* Stop when amount of chars is more then fields to record */ > + if (data + len > limit) > + break; > + /* This record type/len field */ > + *data++ = *(u8 *)addr; > + > + /* Add offset to match data */ > + addr += 1; > + > + /* If len is 0 it means empty field that's why skip writing */ > + if (!len) > + continue; > + > + /* Record data field */ > + memcpy(data, (u8 *)addr, len); > + term = data + (u8)len; > + *term = 0; > + addr += len; > + } > + > + if (i < FRU_PRODUCT_AREA_TOTAL_FIELDS) { > + printf("Product area require minimum %d fields\n", > + FRU_PRODUCT_AREA_TOTAL_FIELDS); > + return -EINVAL; > + } > + > + len = fru_check_type_len(*(u8 *)addr, fru_data.prd.lang_code, &type); > + > + /* If it has custom fields, do custom parsing */ > + if (len != -EINVAL) > + ret = fru_parse_product_custom(addr); > + > + return ret; > +} > + > __weak int fru_parse_multirec_oem(unsigned long addr) > { > struct fru_multirec_hdr *mrc = (struct fru_multirec_hdr *)addr; > @@ -319,6 +404,9 @@ int fru_capture(unsigned long addr) > if (hdr->off_board) > fru_parse_board(addr + fru_cal_area_len(hdr->off_board)); > > + if (hdr->off_product) > + fru_parse_product(addr + fru_cal_area_len(hdr->off_product)); > + > if (hdr->off_multirec) > fru_parse_multirec(addr + fru_cal_area_len(hdr->off_multirec)); > > @@ -397,6 +485,71 @@ static int fru_display_board(struct fru_board_data *brd, int verbose) > return 0; > } > > +static int fru_display_product(struct fru_product_data *prd, int verbose) > +{ > + u8 type; > + int len; > + u8 *data; > + static const char * const productinfo[] = { > + "Manufacturer Name", > + "Product Name", > + "Part Number", > + "Version Number", > + "Serial No", > + "Asset Number", > + "File ID", > + }; > + > + if (verbose) { > + printf("*****PRODUCT INFO*****\n"); > + printf("Version:%d\n", fru_version(prd->ver)); > + printf("Product Area Length:%d\n", fru_cal_area_len(prd->len)); > + } > + > + if (fru_check_language(prd->lang_code)) > + return -EINVAL; > + > + data = (u8 *)&prd->manufacturer_type_len; > + > + for (u8 i = 0; i < (sizeof(productinfo) / sizeof(*productinfo)); i++) { > + len = fru_check_type_len(*data++, prd->lang_code, > + &type); > + if (len == -EINVAL) { > + printf("**** EOF for Product Area ****\n"); > + break; > + } > + > + if (type <= FRU_TYPELEN_TYPE_ASCII8 && > + (prd->lang_code == FRU_LANG_CODE_ENGLISH || > + prd->lang_code == FRU_LANG_CODE_ENGLISH_1)) > + debug("Type code: %s\n", fru_typecode_str[type]); > + else > + debug("Type code: %s\n", fru_typecode_str[type + 1]); > + > + if (!len) { > + debug("%s not found\n", productinfo[i]); > + continue; > + } > + > + switch (type) { > + case FRU_TYPELEN_TYPE_BINARY: > + debug("Length: %d\n", len); > + printf(" %s: 0x%x\n", productinfo[i], *data); > + break; > + case FRU_TYPELEN_TYPE_ASCII8: > + debug("Length: %d\n", len); > + printf(" %s: %s\n", productinfo[i], data); > + break; > + default: > + debug("Unsupported type %x\n", type); > + } > + > + data += FRU_BOARD_MAX_LEN; > + } > + > + return 0; > +} > + > static void fru_display_common_hdr(struct fru_common_hdr *hdr, int verbose) > { > if (!verbose) > @@ -437,6 +590,8 @@ static void fru_display_common_hdr(struct fru_common_hdr *hdr, int verbose) > > int fru_display(int verbose) > { > + int ret; > + > if (!fru_data.captured) { > printf("FRU data not available please run fru parse\n"); > return -EINVAL; > @@ -444,7 +599,11 @@ int fru_display(int verbose) > > fru_display_common_hdr(&fru_data.hdr, verbose); > > - return fru_display_board(&fru_data.brd, verbose); > + ret = fru_display_board(&fru_data.brd, verbose); > + if (ret) > + return ret; > + > + return fru_display_product(&fru_data.prd, verbose); > } > > const struct fru_table *fru_get_fru_data(void) > diff --git a/include/fru.h b/include/fru.h > index f64fe1cca5e6..14643fd9616c 100644 > --- a/include/fru.h > +++ b/include/fru.h > @@ -49,6 +49,26 @@ struct fru_board_data { > u8 file_id[FRU_BOARD_MAX_LEN]; > }; > > +struct fru_product_data { > + u8 ver; > + u8 len; > + u8 lang_code; > + u8 manufacturer_type_len; > + u8 manufacturer_name[FRU_BOARD_MAX_LEN]; > + u8 product_name_type_len; > + u8 product_name[FRU_BOARD_MAX_LEN]; > + u8 part_number_type_len; > + u8 part_number[FRU_BOARD_MAX_LEN]; > + u8 version_number_type_len; > + u8 version_number[FRU_BOARD_MAX_LEN]; > + u8 serial_number_type_len; > + u8 serial_number[FRU_BOARD_MAX_LEN]; > + u8 asset_number_type_len; > + u8 asset_number[FRU_BOARD_MAX_LEN]; > + u8 file_id_type_len; > + u8 file_id[FRU_BOARD_MAX_LEN]; > +}; > + > struct fru_multirec_hdr { > u8 rec_type; > u8 type; > @@ -60,6 +80,7 @@ struct fru_multirec_hdr { > struct fru_table { > struct fru_common_hdr hdr; > struct fru_board_data brd; > + struct fru_product_data prd; > bool captured; > }; > > @@ -74,6 +95,7 @@ struct fru_table { > > /* This should be minimum of fields */ > #define FRU_BOARD_AREA_TOTAL_FIELDS 5 > +#define FRU_PRODUCT_AREA_TOTAL_FIELDS 7 > #define FRU_TYPELEN_TYPE_SHIFT 6 > #define FRU_TYPELEN_TYPE_BINARY 0 > #define FRU_TYPELEN_TYPE_ASCII8 3
On 8/1/2022 5:37 AM, Heinrich Schuchardt wrote: > On 7/29/22 23:54, Jae Hyun Yoo wrote: >> Add product info area parsing support. Custom product info field parsing >> function 'fru_parse_product_custom' can be replaced with a board specific >> implementation. >> >> Signed-off-by: Jae Hyun Yoo <quic_jaehyoo@quicinc.com> >> --- >> Changes from RFC: >> * Added manufacturer custom product info fields parsing flow. > > Please, provide a unit test for the new functions. I'll add a unit test in the next spin. Best Regards, Jae
diff --git a/common/fru_ops.c b/common/fru_ops.c index c03eeffbddc6..9f350f875035 100644 --- a/common/fru_ops.c +++ b/common/fru_ops.c @@ -264,6 +264,91 @@ static int fru_parse_board(unsigned long addr) return ret; } +__weak int fru_parse_product_custom(unsigned long addr) +{ + int len; + u8 type; + + do { + len = fru_check_type_len(*(u8 *)addr, fru_data.prd.lang_code, + &type); + if (len == -EINVAL) + break; + + addr += 1; + + /* Skip empty field */ + if (!len) + continue; + + if (DEBUG_PARSE_CUSTOM_FIELDS) + print_hex_dump_bytes("Product Custom Field: ", + DUMP_PREFIX_NONE, (u8 *)addr, len); + + addr += len; + } while (true); + + return 0; +} + +static int fru_parse_product(unsigned long addr) +{ + u8 i, type; + int len, ret = 0; + u8 *data, *term, *limit; + + memcpy(&fru_data.prd.ver, (void *)addr, 6); + addr += 3; + data = (u8 *)&fru_data.prd.manufacturer_type_len; + + /* Record max structure limit not to write data over allocated space */ + limit = (u8 *)&fru_data.prd + sizeof(struct fru_product_data); + + for (i = 0; i < FRU_PRODUCT_AREA_TOTAL_FIELDS; + i++, data += FRU_BOARD_MAX_LEN) { + len = fru_check_type_len(*(u8 *)addr, fru_data.prd.lang_code, + &type); + /* + * Stop cature if it end of fields + */ + if (len == -EINVAL) + break; + + /* Stop when amount of chars is more then fields to record */ + if (data + len > limit) + break; + /* This record type/len field */ + *data++ = *(u8 *)addr; + + /* Add offset to match data */ + addr += 1; + + /* If len is 0 it means empty field that's why skip writing */ + if (!len) + continue; + + /* Record data field */ + memcpy(data, (u8 *)addr, len); + term = data + (u8)len; + *term = 0; + addr += len; + } + + if (i < FRU_PRODUCT_AREA_TOTAL_FIELDS) { + printf("Product area require minimum %d fields\n", + FRU_PRODUCT_AREA_TOTAL_FIELDS); + return -EINVAL; + } + + len = fru_check_type_len(*(u8 *)addr, fru_data.prd.lang_code, &type); + + /* If it has custom fields, do custom parsing */ + if (len != -EINVAL) + ret = fru_parse_product_custom(addr); + + return ret; +} + __weak int fru_parse_multirec_oem(unsigned long addr) { struct fru_multirec_hdr *mrc = (struct fru_multirec_hdr *)addr; @@ -319,6 +404,9 @@ int fru_capture(unsigned long addr) if (hdr->off_board) fru_parse_board(addr + fru_cal_area_len(hdr->off_board)); + if (hdr->off_product) + fru_parse_product(addr + fru_cal_area_len(hdr->off_product)); + if (hdr->off_multirec) fru_parse_multirec(addr + fru_cal_area_len(hdr->off_multirec)); @@ -397,6 +485,71 @@ static int fru_display_board(struct fru_board_data *brd, int verbose) return 0; } +static int fru_display_product(struct fru_product_data *prd, int verbose) +{ + u8 type; + int len; + u8 *data; + static const char * const productinfo[] = { + "Manufacturer Name", + "Product Name", + "Part Number", + "Version Number", + "Serial No", + "Asset Number", + "File ID", + }; + + if (verbose) { + printf("*****PRODUCT INFO*****\n"); + printf("Version:%d\n", fru_version(prd->ver)); + printf("Product Area Length:%d\n", fru_cal_area_len(prd->len)); + } + + if (fru_check_language(prd->lang_code)) + return -EINVAL; + + data = (u8 *)&prd->manufacturer_type_len; + + for (u8 i = 0; i < (sizeof(productinfo) / sizeof(*productinfo)); i++) { + len = fru_check_type_len(*data++, prd->lang_code, + &type); + if (len == -EINVAL) { + printf("**** EOF for Product Area ****\n"); + break; + } + + if (type <= FRU_TYPELEN_TYPE_ASCII8 && + (prd->lang_code == FRU_LANG_CODE_ENGLISH || + prd->lang_code == FRU_LANG_CODE_ENGLISH_1)) + debug("Type code: %s\n", fru_typecode_str[type]); + else + debug("Type code: %s\n", fru_typecode_str[type + 1]); + + if (!len) { + debug("%s not found\n", productinfo[i]); + continue; + } + + switch (type) { + case FRU_TYPELEN_TYPE_BINARY: + debug("Length: %d\n", len); + printf(" %s: 0x%x\n", productinfo[i], *data); + break; + case FRU_TYPELEN_TYPE_ASCII8: + debug("Length: %d\n", len); + printf(" %s: %s\n", productinfo[i], data); + break; + default: + debug("Unsupported type %x\n", type); + } + + data += FRU_BOARD_MAX_LEN; + } + + return 0; +} + static void fru_display_common_hdr(struct fru_common_hdr *hdr, int verbose) { if (!verbose) @@ -437,6 +590,8 @@ static void fru_display_common_hdr(struct fru_common_hdr *hdr, int verbose) int fru_display(int verbose) { + int ret; + if (!fru_data.captured) { printf("FRU data not available please run fru parse\n"); return -EINVAL; @@ -444,7 +599,11 @@ int fru_display(int verbose) fru_display_common_hdr(&fru_data.hdr, verbose); - return fru_display_board(&fru_data.brd, verbose); + ret = fru_display_board(&fru_data.brd, verbose); + if (ret) + return ret; + + return fru_display_product(&fru_data.prd, verbose); } const struct fru_table *fru_get_fru_data(void) diff --git a/include/fru.h b/include/fru.h index f64fe1cca5e6..14643fd9616c 100644 --- a/include/fru.h +++ b/include/fru.h @@ -49,6 +49,26 @@ struct fru_board_data { u8 file_id[FRU_BOARD_MAX_LEN]; }; +struct fru_product_data { + u8 ver; + u8 len; + u8 lang_code; + u8 manufacturer_type_len; + u8 manufacturer_name[FRU_BOARD_MAX_LEN]; + u8 product_name_type_len; + u8 product_name[FRU_BOARD_MAX_LEN]; + u8 part_number_type_len; + u8 part_number[FRU_BOARD_MAX_LEN]; + u8 version_number_type_len; + u8 version_number[FRU_BOARD_MAX_LEN]; + u8 serial_number_type_len; + u8 serial_number[FRU_BOARD_MAX_LEN]; + u8 asset_number_type_len; + u8 asset_number[FRU_BOARD_MAX_LEN]; + u8 file_id_type_len; + u8 file_id[FRU_BOARD_MAX_LEN]; +}; + struct fru_multirec_hdr { u8 rec_type; u8 type; @@ -60,6 +80,7 @@ struct fru_multirec_hdr { struct fru_table { struct fru_common_hdr hdr; struct fru_board_data brd; + struct fru_product_data prd; bool captured; }; @@ -74,6 +95,7 @@ struct fru_table { /* This should be minimum of fields */ #define FRU_BOARD_AREA_TOTAL_FIELDS 5 +#define FRU_PRODUCT_AREA_TOTAL_FIELDS 7 #define FRU_TYPELEN_TYPE_SHIFT 6 #define FRU_TYPELEN_TYPE_BINARY 0 #define FRU_TYPELEN_TYPE_ASCII8 3
Add product info area parsing support. Custom product info field parsing function 'fru_parse_product_custom' can be replaced with a board specific implementation. Signed-off-by: Jae Hyun Yoo <quic_jaehyoo@quicinc.com> --- Changes from RFC: * Added manufacturer custom product info fields parsing flow. common/fru_ops.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++- include/fru.h | 22 +++++++ 2 files changed, 182 insertions(+), 1 deletion(-)