xref: /aosp_15_r20/external/coreboot/util/supermicro/smcbiosinfo/smcbiosinfo.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <unistd.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <getopt.h>
11 #include <errno.h>
12 #include <stdarg.h>
13 
14 /* Place the following struct somewhere in the ROM: */
15 struct SMC_BIOS_Update {
16 	uint8_t		magic0[4];	// always 0xaa00b1ed
17 	char		magic1[4];	// always $FID
18 	uint8_t		magic2[2];	// always 0x7804
19 	uint8_t		space0;		// always zero
20 	// SMCinfotool doesn't care for the first letter
21 	// The BMC webinterface does.
22 	char		boardid[9];	// "100000000"
23 	uint8_t		space1[15];	// unknown data
24 	uint8_t		space2;		// always 0x1f
25 	char		ukn_majorVer[2];// unknown
26 	uint8_t		space3;		// always zero
27 	char		ukn_minorVer[2];// unknown
28 	uint8_t		space4;		// always zero
29 	char		majorVer[3];	// BIOS major version
30 	char		minorVer[2];	// BIOS minor version
31 	uint8_t		space5;		// always zero
32 	uint16_t	year;		// year
33 	uint8_t		month;		// month
34 	uint8_t		day;		// day
35 	uint32_t	space6;		// unknown data
36 	uint8_t		space7;		// all ones
37 	char		str[15];	// "SUPERMSMCI--MB1"
38 	uint8_t		space8[3];	// always zero
39 	uint64_t	space9[6];	// all ones
40 } __packed;
41 
42 static const char *optstring  = "b:i:o:h";
43 
44 static struct option long_options[] = {
45 	{"boardid",            required_argument, 0, 'b' },
46 	{"input",              required_argument, 0, 'i' },
47 	{"output",             required_argument, 0, 'o' },
48 	{"help",                     no_argument, 0, 'h' },
49 };
50 
usage(void)51 static void usage(void)
52 {
53 	printf("smcbiosinfo: Create BIOSInfo for BMC BIOS updates\n");
54 	printf("Usage: smcbiosinfo [options] -i build.h -b <boardid> -o <filename>\n");
55 	printf("-b | --boardid <ID>              The board ID assigned by SMC\n");
56 	printf("-i | --input <FILE>              The build.h file to parse\n");
57 	printf("-o | --output <FILE>             The file to generate\n");
58 	printf("-h | --help                      Print this help\n");
59 }
60 
bcd2int(int hex)61 static int bcd2int(int hex)
62 {
63 	if (hex > 0xff)
64 		return -1;
65 	return ((hex & 0xF0) >> 4) * 10 + (hex & 0x0F);
66 }
67 
get_line(char * fn,char * match)68 static char *get_line(char *fn, char *match)
69 {
70 	ssize_t read;
71 	char *line = NULL;
72 	char *ret = NULL;
73 	size_t len = 0;
74 
75 	FILE *fp = fopen(fn, "r");
76 	if (fp == NULL) {
77 		fprintf(stderr, "E: Couldn't open file '%s'\n", fn);
78 		return NULL;
79 	}
80 
81 	while ((read = getline(&line, &len, fp)) != -1) {
82 		if (strstr(line, match) != NULL) {
83 			ret = strdup(strstr(line, match) + strlen(match));
84 			break;
85 		}
86 	}
87 
88 	if (!ret)
89 		fprintf(stderr, "E: %s not found in %s\n", match, fn);
90 
91 	fclose(fp);
92 	return ret;
93 }
94 
get_line_as_int(char * fn,char * match,int bcd)95 static int get_line_as_int(char *fn, char *match, int bcd)
96 {
97 	int ret = -1;
98 	char *s = get_line(fn, match);
99 	if (s && strlen(s) > 0) {
100 		char *endptr;
101 		ret = strtol(s, &endptr, 0);
102 		if (*endptr != '\0' && *endptr != '\n') {
103 			fprintf(stderr, "E: Couldn't parse number for key '%s'\n", match);
104 			return -1;
105 		}
106 		if (bcd)
107 			ret = bcd2int(ret);
108 		free(s);
109 	} else {
110 		fprintf(stderr, "E: Got invalid line for key '%s'\n", match);
111 	}
112 
113 	return ret;
114 }
115 
main(int argc,char ** argv)116 int main(int argc, char **argv)
117 {
118 	int c;
119 	int ret = 1;
120 	char *filename = NULL;
121 	char *inputfilename = NULL;
122 	char *boardid = NULL;
123 	int num;
124 
125 	while (1) {
126 		int optindex = 0;
127 
128 		c = getopt_long(argc, argv, optstring, long_options, &optindex);
129 
130 		if (c == -1)
131 			break;
132 
133 		switch (c) {
134 		case 'b':
135 			boardid = strdup(optarg);
136 			break;
137 		case 'i':
138 			inputfilename = strdup(optarg);
139 			break;
140 		case 'o':
141 			filename = strdup(optarg);
142 			break;
143 		case 'h':
144 			ret = 0; /* fallthrough */
145 		case '?':
146 			usage();
147 			goto out;
148 		default:
149 			break;
150 		}
151 	}
152 
153 	if (!inputfilename) {
154 		fprintf(stderr, "E: Must specify build.h filename\n");
155 		goto out;
156 	}
157 	if (!filename) {
158 		fprintf(stderr, "E: Must specify a destination filename\n");
159 		goto out;
160 	}
161 
162 	if (!boardid || strlen(boardid) == 0) {
163 		fprintf(stderr, "E: Board ID must be set\n");
164 		goto out;
165 	}
166 	if (strlen(boardid) > 8) {
167 		fprintf(stderr, "E: Board ID must be less than 8 characters\n");
168 		goto out;
169 	}
170 
171 	// generate the table
172 
173 	struct SMC_BIOS_Update sbu = {
174 		{0xed, 0xb1, 0x00, 0xaa},
175 		"$FID",
176 		{0x04, 0x78},
177 		0, // space
178 		"100000000", // boardid
179 		{}, // unknown data
180 		0x1f, // space
181 		"05", // unknown data
182 		0, // zero
183 		"06", // unknown data
184 		0, // zero
185 		"000", // major
186 		"00", // minor
187 		0, // zero
188 		0, // year
189 		0, // month
190 		0, //day
191 		0, // unknown data
192 		0xff, // space
193 		"SUPERMSMCI--MB1",
194 		{0, 0, 0}, // all zero
195 		{~0, ~0, ~0, ~0, ~0, ~0}, // all ones
196 	};
197 
198 	num = get_line_as_int(inputfilename, "COREBOOT_MAJOR_VERSION", 0);
199 	if (num < 0)
200 		goto out;
201 
202 	if (num < 999) {
203 		char tmp[4];
204 		snprintf(tmp, sizeof(tmp), "%03d", num);
205 		memcpy(&sbu.majorVer, &tmp, sizeof(sbu.majorVer));
206 	} else {
207 		fprintf(stderr, "E: Unsupported coreboot major version\n");
208 		goto out;
209 	}
210 
211 	num = get_line_as_int(inputfilename, "COREBOOT_MINOR_VERSION", 0);
212 	if (num < 0)
213 		goto out;
214 
215 	if (num < 99) {
216 		char tmp[3];
217 		snprintf(tmp, sizeof(tmp), "%02d", num);
218 		memcpy(&sbu.minorVer, &tmp, sizeof(sbu.minorVer));
219 	} else {
220 		fprintf(stderr, "E: Unsupported coreboot minor version\n");
221 		goto out;
222 	}
223 
224 	num = get_line_as_int(inputfilename, "COREBOOT_BUILD_YEAR_BCD", 1);
225 	if (num < 0)
226 		goto out;
227 	sbu.year = 2000 + num;
228 
229 	num = get_line_as_int(inputfilename, "COREBOOT_BUILD_MONTH_BCD", 1);
230 	if (num < 0)
231 		goto out;
232 	sbu.month = num;
233 
234 	num = get_line_as_int(inputfilename, "COREBOOT_BUILD_DAY_BCD", 1);
235 	if (num < 0)
236 		goto out;
237 	sbu.day = num;
238 
239 	memcpy(&sbu.boardid[1], boardid, strlen(boardid));
240 
241 	// write the table
242 	FILE *fd = fopen(filename, "wb");
243 	if (!fd) {
244 		fprintf(stderr, "E: %s open failed: %s\n", filename, strerror(errno));
245 		goto out;
246 	}
247 
248 	if (fwrite(&sbu, 1, sizeof(sbu), fd) != sizeof(sbu)) {
249 		fprintf(stderr, "E: %s write failed: %s\n", filename, strerror(errno));
250 		fclose(fd);
251 		goto out;
252 	}
253 
254 	if (fclose(fd)) {
255 		fprintf(stderr, "E: %s close failed: %s\n", filename, strerror(errno));
256 		goto out;
257 	}
258 
259 	ret = 0;
260 out:
261 	if (ret > 0)
262 		fprintf(stderr, "E: Error creating '%s'\n", filename);
263 
264 	free(filename);
265 
266 	exit(ret);
267 
268 	return 0;
269 }
270