| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <getopt.h> |
| #include <errno.h> |
| #include <stdarg.h> |
| |
| /* Place the following struct somewhere in the ROM: */ |
| struct SMC_BIOS_Update { |
| uint8_t magic0[4]; // always 0xaa00b1ed |
| char magic1[4]; // always $FID |
| uint8_t magic2[2]; // always 0x7804 |
| uint8_t space0; // always zero |
| // SMCinfotool doesn't care for the first letter |
| // The BMC webinterface does. |
| char boardid[9]; // "100000000" |
| uint8_t space1[15]; // unknown data |
| uint8_t space2; // always 0x1f |
| char ukn_majorVer[2];// unknown |
| uint8_t space3; // always zero |
| char ukn_minorVer[2];// unknown |
| uint8_t space4; // always zero |
| char majorVer[3]; // BIOS major version |
| char minorVer[2]; // BIOS minor version |
| uint8_t space5; // always zero |
| uint16_t year; // year |
| uint8_t month; // month |
| uint8_t day; // day |
| uint32_t space6; // unknown data |
| uint8_t space7; // all ones |
| char str[15]; // "SUPERMSMCI--MB1" |
| uint8_t space8[3]; // always zero |
| uint64_t space9[6]; // all ones |
| } __packed; |
| |
| static const char *optstring = "b:i:o:h"; |
| |
| static struct option long_options[] = { |
| {"boardid", required_argument, 0, 'b' }, |
| {"input", required_argument, 0, 'i' }, |
| {"output", required_argument, 0, 'o' }, |
| {"help", no_argument, 0, 'h' }, |
| }; |
| |
| static void usage(void) |
| { |
| printf("smcbiosinfo: Create BIOSInfo for BMC BIOS updates\n"); |
| printf("Usage: smcbiosinfo [options] -i build.h -b <boardid> -o <filename>\n"); |
| printf("-b | --boardid <ID> The board ID assigned by SMC\n"); |
| printf("-i | --input <FILE> The build.h file to parse\n"); |
| printf("-o | --output <FILE> The file to generate\n"); |
| printf("-h | --help Print this help\n"); |
| } |
| |
| static int bcd2int(int hex) |
| { |
| if (hex > 0xff) |
| return -1; |
| return ((hex & 0xF0) >> 4) * 10 + (hex & 0x0F); |
| } |
| |
| static char *get_line(char *fn, char *match) |
| { |
| ssize_t read; |
| char *line = NULL; |
| char *ret = NULL; |
| size_t len = 0; |
| |
| FILE *fp = fopen(fn, "r"); |
| if (fp == NULL) { |
| fprintf(stderr, "E: Couldn't open file '%s'\n", fn); |
| return NULL; |
| } |
| |
| while ((read = getline(&line, &len, fp)) != -1) { |
| if (strstr(line, match) != NULL) { |
| ret = strdup(strstr(line, match) + strlen(match)); |
| break; |
| } |
| } |
| |
| if (!ret) |
| fprintf(stderr, "E: %s not found in %s\n", match, fn); |
| |
| fclose(fp); |
| return ret; |
| } |
| |
| static int get_line_as_int(char *fn, char *match, int bcd) |
| { |
| int ret = -1; |
| char *s = get_line(fn, match); |
| if (s && strlen(s) > 0) { |
| char *endptr; |
| ret = strtol(s, &endptr, 0); |
| if (*endptr != '\0' && *endptr != '\n') { |
| fprintf(stderr, "E: Couldn't parse number for key '%s'\n", match); |
| return -1; |
| } |
| if (bcd) |
| ret = bcd2int(ret); |
| free(s); |
| } else { |
| fprintf(stderr, "E: Got invalid line for key '%s'\n", match); |
| } |
| |
| return ret; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int c; |
| int ret = 1; |
| char *filename = NULL; |
| char *inputfilename = NULL; |
| char *boardid = NULL; |
| int num; |
| |
| while (1) { |
| int optindex = 0; |
| |
| c = getopt_long(argc, argv, optstring, long_options, &optindex); |
| |
| if (c == -1) |
| break; |
| |
| switch (c) { |
| case 'b': |
| boardid = strdup(optarg); |
| break; |
| case 'i': |
| inputfilename = strdup(optarg); |
| break; |
| case 'o': |
| filename = strdup(optarg); |
| break; |
| case 'h': |
| ret = 0; /* fallthrough */ |
| case '?': |
| usage(); |
| goto out; |
| default: |
| break; |
| } |
| } |
| |
| if (!inputfilename) { |
| fprintf(stderr, "E: Must specify build.h filename\n"); |
| goto out; |
| } |
| if (!filename) { |
| fprintf(stderr, "E: Must specify a destination filename\n"); |
| goto out; |
| } |
| |
| if (!boardid || strlen(boardid) == 0) { |
| fprintf(stderr, "E: Board ID must be set\n"); |
| goto out; |
| } |
| if (strlen(boardid) > 8) { |
| fprintf(stderr, "E: Board ID must be less than 8 characters\n"); |
| goto out; |
| } |
| |
| // generate the table |
| |
| struct SMC_BIOS_Update sbu = { |
| {0xed, 0xb1, 0x00, 0xaa}, |
| "$FID", |
| {0x04, 0x78}, |
| 0, // space |
| "100000000", // boardid |
| {}, // unknown data |
| 0x1f, // space |
| "05", // unknown data |
| 0, // zero |
| "06", // unknown data |
| 0, // zero |
| "000", // major |
| "00", // minor |
| 0, // zero |
| 0, // year |
| 0, // month |
| 0, //day |
| 0, // unknown data |
| 0xff, // space |
| "SUPERMSMCI--MB1", |
| {0, 0, 0}, // all zero |
| {~0, ~0, ~0, ~0, ~0, ~0}, // all ones |
| }; |
| |
| num = get_line_as_int(inputfilename, "COREBOOT_MAJOR_VERSION", 0); |
| if (num < 0) |
| goto out; |
| |
| if (num < 999) { |
| char tmp[4]; |
| snprintf(tmp, sizeof(tmp), "%03d", num); |
| memcpy(&sbu.majorVer, &tmp, sizeof(sbu.majorVer)); |
| } else { |
| fprintf(stderr, "E: Unsupported coreboot major version\n"); |
| goto out; |
| } |
| |
| num = get_line_as_int(inputfilename, "COREBOOT_MINOR_VERSION", 0); |
| if (num < 0) |
| goto out; |
| |
| if (num < 99) { |
| char tmp[3]; |
| snprintf(tmp, sizeof(tmp), "%02d", num); |
| memcpy(&sbu.minorVer, &tmp, sizeof(sbu.minorVer)); |
| } else { |
| fprintf(stderr, "E: Unsupported coreboot minor version\n"); |
| goto out; |
| } |
| |
| num = get_line_as_int(inputfilename, "COREBOOT_BUILD_YEAR_BCD", 1); |
| if (num < 0) |
| goto out; |
| sbu.year = 2000 + num; |
| |
| num = get_line_as_int(inputfilename, "COREBOOT_BUILD_MONTH_BCD", 1); |
| if (num < 0) |
| goto out; |
| sbu.month = num; |
| |
| num = get_line_as_int(inputfilename, "COREBOOT_BUILD_DAY_BCD", 1); |
| if (num < 0) |
| goto out; |
| sbu.day = num; |
| |
| memcpy(&sbu.boardid[1], boardid, strlen(boardid)); |
| |
| // write the table |
| FILE *fd = fopen(filename, "wb"); |
| if (!fd) { |
| fprintf(stderr, "E: %s open failed: %s\n", filename, strerror(errno)); |
| goto out; |
| } |
| |
| if (fwrite(&sbu, 1, sizeof(sbu), fd) != sizeof(sbu)) { |
| fprintf(stderr, "E: %s write failed: %s\n", filename, strerror(errno)); |
| fclose(fd); |
| goto out; |
| } |
| |
| if (fclose(fd)) { |
| fprintf(stderr, "E: %s close failed: %s\n", filename, strerror(errno)); |
| goto out; |
| } |
| |
| ret = 0; |
| out: |
| if (ret > 0) |
| fprintf(stderr, "E: Error creating '%s'\n", filename); |
| |
| free(filename); |
| |
| exit(ret); |
| |
| return 0; |
| } |