/* Copyright 2018 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Cros Board Info utility */ #include #include #include #include #include #include #include #include #include #include #include "cros_board_info.h" #include "crc8.h" #define REQUIRED_MASK_BOARD_VERSION (1 << 0) #define REQUIRED_MASK_OEM_ID (1 << 1) #define REQUIRED_MASK_SKU_ID (1 << 2) #define REQUIRED_MASK_SIZE (1 << 3) #define REQUIRED_MASK_FILENAME (1 << 4) #define REQUIRED_MASK_CREATE (REQUIRED_MASK_BOARD_VERSION | \ REQUIRED_MASK_OEM_ID | \ REQUIRED_MASK_SKU_ID | \ REQUIRED_MASK_SIZE | \ REQUIRED_MASK_FILENAME) #define REQUIRED_MASK_SHOW (REQUIRED_MASK_FILENAME) /* Command line options */ enum { /* mode options */ OPT_MODE_NONE, OPT_MODE_CREATE, OPT_MODE_SHOW, OPT_BOARD_VERSION, OPT_OEM_ID, OPT_SKU_ID, OPT_SIZE, OPT_ERASE_BYTE, OPT_SHOW_ALL, OPT_HELP, }; static const struct option long_opts[] = { {"create", 1, 0, OPT_MODE_CREATE}, {"show", 1, 0, OPT_MODE_SHOW}, {"board_version", 1, 0, OPT_BOARD_VERSION}, {"oem_id", 1, 0, OPT_OEM_ID}, {"sku_id", 1, 0, OPT_SKU_ID}, {"size", 1, 0, OPT_SIZE}, {"erase_byte", 1, 0, OPT_ERASE_BYTE}, {"all", 0, 0, OPT_SHOW_ALL}, {"help", 0, 0, OPT_HELP}, {NULL, 0, 0, 0} }; static int write_file(const char *filename, const char *buf, int size) { FILE *f; int i; /* Write to file */ f = fopen(filename, "wb"); if (!f) { perror("Error opening output file"); return -1; } i = fwrite(buf, 1, size, f); fclose(f); if (i != size) { perror("Error writing to file"); return -1; } return 0; } static uint8_t *read_file(const char *filename, uint32_t *size_ptr) { FILE *f; uint8_t *buf; long size; *size_ptr = 0; f = fopen(filename, "rb"); if (!f) { fprintf(stderr, "Unable to open file %s\n", filename); return NULL; } fseek(f, 0, SEEK_END); size = ftell(f); rewind(f); if (size < 0 || size > UINT32_MAX) { fclose(f); return NULL; } buf = malloc(size); if (!buf) { fclose(f); return NULL; } if (1 != fread(buf, size, 1, f)) { fprintf(stderr, "Unable to read file %s\n", filename); fclose(f); free(buf); return NULL; } fclose(f); *size_ptr = size; return buf; } static int cbi_crc8(const struct board_info *bi) { return crc8((uint8_t *)&bi->head.crc + 1, bi->head.total_size - 4); } /* * Create a CBI blob */ static int do_create(const char *cbi_filename, uint32_t size, uint8_t erase, struct board_info *bi) { int rv; uint8_t *buf; buf = malloc(size); if (!buf) { fprintf(stderr, "Failed to allocate memory\n"); return -1; } memset(buf, erase, size); memcpy(bi->head.magic, cbi_magic, sizeof(bi->head.magic)); bi->head.major_version = CBI_VERSION_MAJOR; bi->head.minor_version = CBI_VERSION_MINOR; bi->head.total_size = sizeof(*bi); bi->head.crc = cbi_crc8(bi); memcpy(buf, bi, sizeof(*bi)); /* Output blob */ rv = write_file(cbi_filename, buf, size); if (rv) { fprintf(stderr, "Unable to write CBI blob\n"); return rv; } fprintf(stderr, "CBI blob is created successfully\n"); return 0; } static int do_show(const char *cbi_filename, int show_all) { uint8_t *buf; uint32_t size; struct board_info *bi; if (!cbi_filename) { fprintf(stderr, "Missing arguments\n"); return -1; } buf = read_file(cbi_filename, &size); if (!buf) { fprintf(stderr, "Unable to read CBI blob\n"); return -1; } bi = (struct board_info *)buf; printf("CBI blob: %s\n", cbi_filename); printf(" BOARD_VERSION: %d.%d (0x%02x.%02x)\n", bi->major_version, bi->minor_version, bi->major_version, bi->minor_version); printf(" OEM_ID: %d (0x%02x)\n", bi->oem_id, bi->oem_id); printf(" SKU_ID: %d (0x%02x)\n", bi->sku_id, bi->sku_id); if (memcmp(bi->head.magic, cbi_magic, sizeof(cbi_magic))) { fprintf(stderr, "Invalid Magic\n"); return -1; } if (cbi_crc8(bi) != bi->head.crc) { fprintf(stderr, "Invalid CRC\n"); return -1; } printf("Data validated successfully\n"); return 0; } /* Print help and return error */ static void print_help(int argc, char *argv[]) { printf("\nUsage: cbi %s <--create|--show>\n" "\n" "Utility for managing Cros Board Info (CBIs).\n" "\n" "For '--create [OPTIONS]', required OPTIONS are:\n" " --board_version Board version\n" " --oem_id OEM ID\n" " --sku_id SKU ID\n" " --size Size of output file\n" "Optional OPTIONS are:\n" " --erase_byte Byte used for empty space\n" " --format_version Data format version\n" "\n" "For '--show [OPTIONS]', OPTIONS are:\n" " --all Dump all information\n" " It also validates the contents against the checksum and\n" " returns non-zero if validation fails.\n" "\n", argv[0]); } int main(int argc, char **argv) { int mode = OPT_MODE_NONE; const char *cbi_filename = NULL; struct board_info bi; uint32_t size = 0; uint8_t erase = 0xff; int show_all = 0; int parse_error = 0; uint32_t required_mask = 0; uint32_t set_mask = 0; uint64_t val; char *e; int i; memset(&bi, 0, sizeof(bi)); while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { switch (i) { case '?': /* Unhandled option */ fprintf(stderr, "Unknown option or missing value\n"); parse_error = 1; break; case OPT_HELP: print_help(argc, argv); return !!parse_error; case OPT_MODE_CREATE: mode = i; cbi_filename = optarg; required_mask = REQUIRED_MASK_CREATE; set_mask |= REQUIRED_MASK_FILENAME; break; case OPT_MODE_SHOW: mode = i; cbi_filename = optarg; required_mask = REQUIRED_MASK_SHOW; set_mask |= REQUIRED_MASK_FILENAME; break; case OPT_BOARD_VERSION: val = strtoul(optarg, &e, 0); if (val > USHRT_MAX || !*optarg || (e && *e)) { fprintf(stderr, "Invalid --board_version\n"); parse_error = 1; } bi.version = val; set_mask |= REQUIRED_MASK_BOARD_VERSION; break; case OPT_OEM_ID: val = strtoul(optarg, &e, 0); if (val > UCHAR_MAX || !*optarg || (e && *e)) { fprintf(stderr, "Invalid --oem_id\n"); parse_error = 1; } bi.oem_id = val; set_mask |= REQUIRED_MASK_OEM_ID; break; case OPT_SKU_ID: val = strtoul(optarg, &e, 0); if (val > UCHAR_MAX || !*optarg || (e && *e)) { fprintf(stderr, "Invalid --sku_id\n"); parse_error = 1; } bi.sku_id = val; set_mask |= REQUIRED_MASK_SKU_ID; break; case OPT_SIZE: val = strtoul(optarg, &e, 0); if (val > USHRT_MAX || !*optarg || (e && *e)) { fprintf(stderr, "Invalid --size\n"); parse_error = 1; } size = val; set_mask |= REQUIRED_MASK_SIZE; break; case OPT_ERASE_BYTE: erase = strtoul(optarg, &e, 0); if (!*optarg || (e && *e)) { fprintf(stderr, "Invalid --erase_byte\n"); parse_error = 1; } break; case OPT_SHOW_ALL: show_all = 1; break; } } if (parse_error) { print_help(argc, argv); return 1; } if (set_mask != required_mask) { fprintf(stderr, "Missing required arguments\n"); print_help(argc, argv); return 1; } switch (mode) { case OPT_MODE_CREATE: return do_create(cbi_filename, size, erase, &bi); case OPT_MODE_SHOW: return do_show(cbi_filename, show_all); case OPT_MODE_NONE: default: fprintf(stderr, "Must specify a mode.\n"); print_help(argc, argv); return 1; } }