xref: /aosp_15_r20/external/vboot_reference/host/arch/arm/lib/crossystem_arch.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2012 The ChromiumOS Authors
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <stdbool.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <stddef.h>
10 #include <stdlib.h>
11 #ifndef HAVE_MACOS
12 #include <linux/fs.h>
13 #include <linux/gpio.h>
14 #endif
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/param.h>
18 #include <sys/ioctl.h>
19 #include <sys/wait.h>
20 #include <dirent.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <netinet/in.h>
25 
26 #include "crossystem_arch.h"
27 #include "crossystem.h"
28 #include "crossystem_vbnv.h"
29 #include "host_common.h"
30 
31 /* Base name for firmware FDT files */
32 #define FDT_BASE_PATH "/proc/device-tree"
33 /* The /firmware/chromeos path in FDT */
34 #define FDT_CHROMEOS "firmware/chromeos/"
35 /* Path to compatible FDT entry */
36 #define FDT_COMPATIBLE_PATH "/proc/device-tree/compatible"
37 /* Path to the chromeos_arm platform device (deprecated) */
38 #define PLATFORM_DEV_PATH "/sys/devices/platform/chromeos_arm"
39 /* These should match the Linux GPIO name (i.e., 'gpio-line-names'). */
40 #define GPIO_NAME_RECOVERY_SW_L "RECOVERY_SW_L"
41 #define GPIO_NAME_RECOVERY_SW "RECOVERY_SW"
42 #define GPIO_NAME_WP_L "AP_FLASH_WP_L"
43 #define GPIO_NAME_WP "AP_FLASH_WP"
44 /* Device for NVCTX write */
45 #define NVCTX_PATH "/dev/mmcblk%d"
46 /* Base name for GPIO files */
47 #define GPIO_BASE_PATH "/sys/class/gpio"
48 #define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
49 /* Name of NvStorage type property */
50 #define FDT_NVSTORAGE_TYPE_PROP FDT_CHROMEOS "nonvolatile-context-storage"
51 /* Errors */
52 #define E_FAIL      -1
53 #define E_FILEOP    -2
54 #define E_MEM       -3
55 /* Common constants */
56 #define FNAME_SIZE  80
57 #define SECTOR_SIZE 512
58 #define MAX_NMMCBLK 9
59 
FindEmmcDev(void)60 static int FindEmmcDev(void)
61 {
62 	int mmcblk;
63 	unsigned value;
64 	char filename[FNAME_SIZE];
65 	for (mmcblk = 0; mmcblk < MAX_NMMCBLK; mmcblk++) {
66 		/* Get first non-removable mmc block device */
67 		snprintf(filename, sizeof(filename),
68 			 "/sys/block/mmcblk%d/removable", mmcblk);
69 		if (ReadFileInt(filename, &value) < 0)
70 			continue;
71 		if (value == 0)
72 			return mmcblk;
73 	}
74 	/* eMMC not found */
75 	return E_FAIL;
76 }
77 
ReadFdtValue(const char * property,int * value)78 static int ReadFdtValue(const char *property, int *value)
79 {
80 	char filename[FNAME_SIZE];
81 	FILE *file;
82 	int data = 0;
83 
84 	snprintf(filename, sizeof(filename), FDT_BASE_PATH "/%s", property);
85 	file = fopen(filename, "rb");
86 	if (!file) {
87 		fprintf(stderr, "Unable to open FDT property %s\n", property);
88 		return E_FILEOP;
89 	}
90 
91 	if (fread(&data, 1, sizeof(data), file) != sizeof(data)) {
92 		fprintf(stderr, "Unable to read FDT property %s\n", property);
93 		return E_FILEOP;
94 	}
95 	fclose(file);
96 
97 	if (value)
98 		*value = ntohl(data); /* FDT is network byte order */
99 
100 	return 0;
101 }
102 
ReadFdtInt(const char * property)103 static int ReadFdtInt(const char *property)
104 {
105 	int value = 0;
106 	if (ReadFdtValue(property, &value))
107 		return E_FAIL;
108 	return value;
109 }
110 
GetFdtPropertyPath(const char * property,char * path,size_t size)111 static void GetFdtPropertyPath(const char *property, char *path, size_t size)
112 {
113 	if (property[0] == '/')
114 		StrCopy(path, property, size);
115 	else
116 		snprintf(path, size, FDT_BASE_PATH "/%s", property);
117 }
118 
FdtPropertyExist(const char * property)119 static int FdtPropertyExist(const char *property)
120 {
121 	char filename[FNAME_SIZE];
122 	struct stat file_status;
123 
124 	GetFdtPropertyPath(property, filename, sizeof(filename));
125 	if (!stat(filename, &file_status))
126 		return 1; // It exists!
127 	else
128 		return 0; // It does not exist or some error happened.
129 }
130 
ReadFdtBlock(const char * property,void ** block,size_t * size)131 static int ReadFdtBlock(const char *property, void **block, size_t *size)
132 {
133 	char filename[FNAME_SIZE];
134 	FILE *file;
135 	size_t property_size;
136 	char *data;
137 
138 	if (!block)
139 		return E_FAIL;
140 
141 	GetFdtPropertyPath(property, filename, sizeof(filename));
142 	file = fopen(filename, "rb");
143 	if (!file) {
144 		fprintf(stderr, "Unable to open FDT property %s\n", property);
145 		return E_FILEOP;
146 	}
147 
148 	fseek(file, 0, SEEK_END);
149 	property_size = ftell(file);
150 	rewind(file);
151 
152 	data = malloc(property_size +1);
153 	if (!data) {
154 		fclose(file);
155 		return E_MEM;
156 	}
157 	data[property_size] = 0;
158 
159 	if (1 != fread(data, property_size, 1, file)) {
160 		fprintf(stderr, "Unable to read from property %s\n", property);
161 		fclose(file);
162 		free(data);
163 		return E_FILEOP;
164 	}
165 
166 	fclose(file);
167 	*block = data;
168 	if (size)
169 		*size = property_size;
170 
171 	return 0;
172 }
173 
ReadFdtString(const char * property)174 static char * ReadFdtString(const char *property)
175 {
176 	void *str = NULL;
177 	/* Do not need property size */
178 	ReadFdtBlock(property, &str, 0);
179 	return (char *)str;
180 }
181 
VbGetPlatformGpioStatus(const char * name)182 static int VbGetPlatformGpioStatus(const char* name)
183 {
184 	char gpio_name[FNAME_SIZE];
185 	unsigned value;
186 
187 	snprintf(gpio_name, sizeof(gpio_name), "%s/%s/value",
188 		 PLATFORM_DEV_PATH, name);
189 	if (ReadFileInt(gpio_name, &value) < 0)
190 		return -1;
191 
192 	return (int)value;
193 }
194 
195 #ifndef HAVE_MACOS
gpioline_read_value(int chip_fd,int idx,bool active_low)196 static int gpioline_read_value(int chip_fd, int idx, bool active_low)
197 {
198 	struct gpiohandle_request request = {
199 		.lineoffsets = { idx },
200 		.flags = GPIOHANDLE_REQUEST_INPUT | \
201 			 (active_low ? GPIOHANDLE_REQUEST_ACTIVE_LOW : 0),
202 		.lines = 1,
203 	};
204 	struct gpiohandle_data data;
205 	int trynum;
206 	int ret;
207 
208 	/*
209 	 * If two callers try to read the same GPIO at the same time then
210 	 * one of the two will get back EBUSY. There's no great way to
211 	 * solve this, so we'll just retry a bunch with a small sleep in
212 	 * between.
213 	 */
214 	for (trynum = 0; true; trynum++) {
215 		ret = ioctl(chip_fd, GPIO_GET_LINEHANDLE_IOCTL, &request);
216 
217 		/*
218 		 * Not part of the loop condition so usleep doesn't clobber
219 		 * errno (implicitly used by perror).
220 		 */
221 		if (ret >= 0 || errno != EBUSY || trynum >= 50)
222 			break;
223 
224 		usleep(trynum * 1000);
225 	}
226 
227 	if (ret < 0) {
228 		perror("GPIO_GET_LINEHANDLE_IOCTL");
229 		return -1;
230 	}
231 
232 	if (request.fd < 0) {
233 		fprintf(stderr, "bad LINEHANDLE fd %d\n", request.fd);
234 		return -1;
235 	}
236 
237 	ret = ioctl(request.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
238 	if (ret < 0) {
239 		perror("GPIOHANDLE_GET_LINE_VALUES_IOCTL");
240 		close(request.fd);
241 		return -1;
242 	}
243 	close(request.fd);
244 	return data.values[0];
245 }
246 
247 /* Return 1 if @idx line matches name; 0 if no match; negative if error. */
gpioline_name_match(int chip_fd,int idx,const char * name)248 static int gpioline_name_match(int chip_fd, int idx, const char *name)
249 {
250 	struct gpioline_info info = {
251 		.line_offset = idx,
252 	};
253 	int ret;
254 
255 	ret = ioctl(chip_fd, GPIO_GET_LINEINFO_IOCTL, &info);
256 	if (ret < 0) {
257 		perror("GPIO_GET_LINEINFO_IOCTL");
258 		return -1;
259 	}
260 
261 	return strncmp(info.name, name, sizeof(info.name)) == 0;
262 }
263 
264 /* Return value of gpio, if found. Negative for error codes. */
gpiochip_read_value(int chip_fd,const char * name,bool active_low)265 static int gpiochip_read_value(int chip_fd, const char *name, bool active_low)
266 {
267 	struct gpiochip_info info;
268 	int i, ret;
269 
270 	ret = ioctl(chip_fd, GPIO_GET_CHIPINFO_IOCTL, &info);
271 	if (ret < 0) {
272 		perror("GPIO_GET_CHIPINFO_IOCTL");
273 		return -1;
274 	}
275 
276 	for (i = 0; i < info.lines; i++) {
277 		if (gpioline_name_match(chip_fd, i, name) != 1)
278 			continue;
279 		return gpioline_read_value(chip_fd, i, active_low);
280 	}
281 
282 	return -1;
283 }
284 
285 /* Return nonzero for entries with a 'gpiochip'-prefixed name. */
gpiochip_scan_filter(const struct dirent * d)286 static int gpiochip_scan_filter(const struct dirent *d)
287 {
288 	const char prefix[] = "gpiochip";
289 	return !strncmp(prefix, d->d_name, strlen(prefix));
290 }
291 
292 /*
293  * Read a named GPIO via the Linux /dev/gpiochip* API, supported in recent
294  * kernels (e.g., ChromeOS kernel 4.14+). This method is preferred over the
295  * downstream chromeos_arm driver.
296  *
297  * Returns -1 for errors (e.g., API not supported, or @name not found); 1 for
298  * active; 0 for inactive.
299  */
gpiod_read(const char * name,bool active_low)300 static int gpiod_read(const char *name, bool active_low)
301 {
302 	struct dirent **list;
303 	int i, max, ret;
304 
305 	ret = scandir("/dev", &list, gpiochip_scan_filter, alphasort);
306 	if (ret < 0) {
307 		perror("scandir");
308 		return -1;
309 	}
310 	max = ret;
311 	/* No /dev/gpiochip* -- API not supported. */
312 	if (!max)
313 		return -1;
314 
315 	for (i = 0; i < max; i++) {
316 		char buf[5 + NAME_MAX + 1];
317 		int fd;
318 
319 		snprintf(buf, sizeof(buf), "/dev/%s", list[i]->d_name);
320 		ret = open(buf, O_RDWR);
321 		if (ret < 0) {
322 			perror("open");
323 			break;
324 		}
325 		fd = ret;
326 
327 		ret = gpiochip_read_value(fd, name, active_low);
328 		close(fd);
329 		if (ret >= 0)
330 			break;
331 	}
332 
333 	for (i = 0; i < max; i++)
334 		free(list[i]);
335 	free(list);
336 
337 	return ret >= 0 ? ret : -1;
338 }
339 #else
gpiod_read(const char * name,bool active_low)340 static int gpiod_read(const char *name, bool active_low)
341 {
342 	return -1;
343 }
344 #endif /* HAVE_MACOS */
345 
vb2_read_nv_storage_disk(struct vb2_context * ctx)346 static int vb2_read_nv_storage_disk(struct vb2_context *ctx)
347 {
348 	int nvctx_fd = -1;
349 	uint8_t sector[SECTOR_SIZE];
350 	int rv = -1;
351 	char nvctx_path[FNAME_SIZE];
352 	int emmc_dev;
353 	int lba = ReadFdtInt(FDT_CHROMEOS "nonvolatile-context-lba");
354 	int offset = ReadFdtInt(FDT_CHROMEOS "nonvolatile-context-offset");
355 	int size = ReadFdtInt(FDT_CHROMEOS "nonvolatile-context-size");
356 
357 	emmc_dev = FindEmmcDev();
358 	if (emmc_dev < 0)
359 		return E_FAIL;
360 	snprintf(nvctx_path, sizeof(nvctx_path), NVCTX_PATH, emmc_dev);
361 
362 	if (size != vb2_nv_get_size(ctx) || (size + offset > SECTOR_SIZE))
363 		return E_FAIL;
364 
365 	nvctx_fd = open(nvctx_path, O_RDONLY);
366 	if (nvctx_fd == -1) {
367 		fprintf(stderr, "%s: failed to open %s\n", __FUNCTION__,
368 			nvctx_path);
369 		goto out;
370 	}
371 	lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
372 
373 	rv = read(nvctx_fd, sector, SECTOR_SIZE);
374 	if (size <= 0) {
375 		fprintf(stderr, "%s: failed to read nvctx from device %s\n",
376 			__FUNCTION__, nvctx_path);
377 		goto out;
378 	}
379 	memcpy(ctx->nvdata, sector+offset, size);
380 	rv = 0;
381 
382 out:
383 	if (nvctx_fd > 0)
384 		close(nvctx_fd);
385 
386 	return rv;
387 }
388 
vb2_write_nv_storage_disk(struct vb2_context * ctx)389 static int vb2_write_nv_storage_disk(struct vb2_context *ctx)
390 {
391 	int nvctx_fd = -1;
392 	uint8_t sector[SECTOR_SIZE];
393 	int rv = -1;
394 	char nvctx_path[FNAME_SIZE];
395 	int emmc_dev;
396 	int lba = ReadFdtInt(FDT_CHROMEOS "nonvolatile-context-lba");
397 	int offset = ReadFdtInt(FDT_CHROMEOS "nonvolatile-context-offset");
398 	int size = ReadFdtInt(FDT_CHROMEOS "nonvolatile-context-size");
399 
400 	emmc_dev = FindEmmcDev();
401 	if (emmc_dev < 0)
402 		return E_FAIL;
403 	snprintf(nvctx_path, sizeof(nvctx_path), NVCTX_PATH, emmc_dev);
404 
405 	if (size != vb2_nv_get_size(ctx) || (size + offset > SECTOR_SIZE))
406 		return E_FAIL;
407 
408 	do {
409 		nvctx_fd = open(nvctx_path, O_RDWR);
410 		if (nvctx_fd == -1) {
411 			fprintf(stderr, "%s: failed to open %s\n",
412 				__FUNCTION__, nvctx_path);
413 			break;
414 		}
415 		lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
416 		rv = read(nvctx_fd, sector, SECTOR_SIZE);
417 		if (rv <= 0) {
418 			fprintf(stderr,
419 				"%s: failed to read nvctx from device %s\n",
420 				__FUNCTION__, nvctx_path);
421 			break;
422 		}
423 		memcpy(sector+offset, ctx->nvdata, size);
424 		lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
425 		rv = write(nvctx_fd, sector, SECTOR_SIZE);
426 		if (rv <= 0) {
427 			fprintf(stderr,
428 				"%s: failed to write nvctx to device %s\n",
429 				__FUNCTION__, nvctx_path);
430 			break;
431 		}
432 #ifndef HAVE_MACOS
433 		/* Must flush buffer cache here to make sure it goes to disk */
434 		rv = ioctl(nvctx_fd, BLKFLSBUF, 0);
435 		if (rv < 0) {
436 			fprintf(stderr,
437 				"%s: failed to flush nvctx to device %s\n",
438 				__FUNCTION__, nvctx_path);
439 			break;
440 		}
441 #endif
442 		rv = 0;
443 	} while (0);
444 
445 	if (nvctx_fd > 0)
446 		close(nvctx_fd);
447 
448 	return rv;
449 }
450 
vb2_read_nv_storage(struct vb2_context * ctx)451 int vb2_read_nv_storage(struct vb2_context *ctx)
452 {
453 	/* Default to disk for older firmware which does not provide storage
454 	 * type */
455 	char *media;
456 	if (!FdtPropertyExist(FDT_NVSTORAGE_TYPE_PROP))
457 		return vb2_read_nv_storage_disk(ctx);
458 	media = ReadFdtString(FDT_NVSTORAGE_TYPE_PROP);
459 	if (!strcmp(media, "disk"))
460 		return vb2_read_nv_storage_disk(ctx);
461 	if (!strcmp(media, "flash"))
462 		return vb2_read_nv_storage_flashrom(ctx);
463 	return -1;
464 }
465 
vb2_write_nv_storage(struct vb2_context * ctx)466 int vb2_write_nv_storage(struct vb2_context *ctx)
467 {
468 	/* Default to disk for older firmware which does not provide storage
469 	 * type */
470 	char *media;
471 	if (!FdtPropertyExist(FDT_NVSTORAGE_TYPE_PROP))
472 		return vb2_write_nv_storage_disk(ctx);
473 	media = ReadFdtString(FDT_NVSTORAGE_TYPE_PROP);
474 	if (!strcmp(media, "disk"))
475 		return vb2_write_nv_storage_disk(ctx);
476 	if (!strcmp(media, "flash"))
477 		return vb2_write_nv_storage_flashrom(ctx);
478 	return -1;
479 }
480 
VbSharedDataRead(void)481 VbSharedDataHeader *VbSharedDataRead(void)
482 {
483 	void *block = NULL;
484 	size_t size = 0;
485 	if (ReadFdtBlock(FDT_CHROMEOS "vboot-shared-data", &block, &size))
486 		return NULL;
487 	VbSharedDataHeader *p = (VbSharedDataHeader *)block;
488 	if (p->magic != VB_SHARED_DATA_MAGIC) {
489 		fprintf(stderr,  "%s: failed to validate magic in "
490 			"VbSharedDataHeader (%x != %x)\n",
491 			__FUNCTION__, p->magic, VB_SHARED_DATA_MAGIC);
492 		return NULL;
493 	}
494 	return (VbSharedDataHeader *)block;
495 }
496 
VbGetArchPropertyInt(const char * name)497 int VbGetArchPropertyInt(const char* name)
498 {
499 	if (!strcasecmp(name, "devsw_cur")) {
500 		/* Systems with virtual developer switches return at-boot
501 		 * value */
502 		return VbGetSystemPropertyInt("devsw_boot");
503 	} else if (!strcasecmp(name, "recoverysw_cur")) {
504 		int value;
505 		/* Try GPIO chardev API first. */
506 		value = gpiod_read(GPIO_NAME_RECOVERY_SW_L, true);
507 		if (value != -1)
508 			return value;
509 		value = gpiod_read(GPIO_NAME_RECOVERY_SW, false);
510 		if (value != -1)
511 			return value;
512 		/* Try the deprecated chromeos_arm platform device next. */
513 		return VbGetPlatformGpioStatus("recovery");
514 	} else if (!strcasecmp(name, "wpsw_cur")) {
515 		int value;
516 		/* Try GPIO chardev API first. */
517 		value = gpiod_read(GPIO_NAME_WP_L, true);
518 		if (value != -1)
519 			return value;
520 		value = gpiod_read(GPIO_NAME_WP, false);
521 		if (value != -1)
522 			return value;
523 		/* Try the deprecated chromeos_arm platform device next. */
524 		return VbGetPlatformGpioStatus("write-protect");
525 	} else if (!strcasecmp(name, "recoverysw_ec_boot")) {
526 		/* TODO: read correct value using ectool */
527 		return 0;
528 	} else if (!strcasecmp(name, "board_id")) {
529 		return ReadFdtInt("firmware/coreboot/board-id");
530 	} else {
531 		return -1;
532 	}
533 }
534 
VbGetArchPropertyString(const char * name,char * dest,size_t size)535 const char* VbGetArchPropertyString(const char* name, char* dest,
536                                     size_t size)
537 {
538 	char *str = NULL;
539 	char *rv = NULL;
540 	const char *prop = NULL;
541 
542 	if (!strcasecmp(name,"arch"))
543 		return StrCopy(dest, "arm", size);
544 
545 	/* Properties from fdt */
546 	if (!strcasecmp(name, "ro_fwid"))
547 		prop = FDT_CHROMEOS "readonly-firmware-version";
548 	else if (!strcasecmp(name, "hwid"))
549 		prop = FDT_CHROMEOS "hardware-id";
550 	else if (!strcasecmp(name, "fwid"))
551 		prop = FDT_CHROMEOS "firmware-version";
552 	else if (!strcasecmp(name, "mainfw_type"))
553 		prop = FDT_CHROMEOS "firmware-type";
554 	else if (!strcasecmp(name, "ecfw_act"))
555 		prop = FDT_CHROMEOS "active-ec-firmware";
556 	if (prop)
557 		str = ReadFdtString(prop);
558 
559 	if (str) {
560 		rv = StrCopy(dest, str, size);
561 		free(str);
562 		return rv;
563 	}
564 	return NULL;
565 }
566 
VbSetArchPropertyInt(const char * name,int value)567 int VbSetArchPropertyInt(const char* name, int value)
568 {
569 	/* All is handled in arch independent fashion */
570 	return -1;
571 }
572 
VbSetArchPropertyString(const char * name,const char * value)573 int VbSetArchPropertyString(const char* name, const char* value)
574 {
575 	/* All is handled in arch independent fashion */
576 	return -1;
577 }
578