1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
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 <stdio.h>
7 #include <string.h>
8 #include <stddef.h>
9 #include <stdlib.h>
10 #ifndef HAVE_MACOS
11 #include <linux/fs.h>
12 #endif
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/param.h>
16 #include <sys/ioctl.h>
17 #include <sys/wait.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <netinet/in.h>
21
22 #include "vboot_common.h"
23 #include "vboot_nvstorage.h"
24 #include "host_common.h"
25 #include "crossystem.h"
26 #include "crossystem_arch.h"
27
28 #define MOSYS_PATH "/usr/sbin/mosys"
29
30 /* Base name for firmware FDT files */
31 #define FDT_BASE_PATH "/proc/device-tree/firmware/chromeos"
32 /* Path to compatible FDT entry */
33 #define FDT_COMPATIBLE_PATH "/proc/device-tree/compatible"
34 /* Path to the chromeos_arm platform device */
35 #define PLATFORM_DEV_PATH "/sys/devices/platform/chromeos_arm"
36 /* Device for NVCTX write */
37 #define NVCTX_PATH "/dev/mmcblk%d"
38 /* Base name for GPIO files */
39 #define GPIO_BASE_PATH "/sys/class/gpio"
40 #define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
41 /* Name of NvStorage type property */
42 #define FDT_NVSTORAGE_TYPE_PROP "nonvolatile-context-storage"
43 /* Errors */
44 #define E_FAIL -1
45 #define E_FILEOP -2
46 #define E_MEM -3
47 /* Common constants */
48 #define FNAME_SIZE 80
49 #define SECTOR_SIZE 512
50 #define MAX_NMMCBLK 9
51
52 typedef struct PlatformFamily {
53 const char* compatible_string; /* Last string in FDT compatible entry */
54 const char* platform_string; /* String to return */
55 } PlatformFamily;
56
57 /* Array of platform family names, terminated with a NULL entry */
58 const PlatformFamily platform_family_array[] = {
59 {"nvidia,tegra124", "Tegra5"},
60 {"nvidia,tegra250", "Tegra2"},
61 {"nvidia,tegra20", "Tegra2"},
62 {"ti,omap4", "OMAP4"},
63 {"ti,omap3", "OMAP3"},
64 {"samsung,exynos4210", "EXYNOS4"},
65 {"samsung,exynos5250", "EXYNOS5"},
66 {"samsung,exynos5420", "EXYNOS5"},
67 {"qcom,ipq8064", "IPQ8064"},
68 /* Terminate with NULL entry */
69 {NULL, NULL}
70 };
71
FindEmmcDev(void)72 static int FindEmmcDev(void) {
73 int mmcblk;
74 unsigned value;
75 char filename[FNAME_SIZE];
76 for (mmcblk = 0; mmcblk < MAX_NMMCBLK; mmcblk++) {
77 /* Get first non-removable mmc block device */
78 snprintf(filename, sizeof(filename), "/sys/block/mmcblk%d/removable",
79 mmcblk);
80 if (ReadFileInt(filename, &value) < 0)
81 continue;
82 if (value == 0)
83 return mmcblk;
84 }
85 /* eMMC not found */
86 return E_FAIL;
87 }
88
ReadFdtValue(const char * property,int * value)89 static int ReadFdtValue(const char *property, int *value) {
90 char filename[FNAME_SIZE];
91 FILE *file;
92 int data = 0;
93
94 snprintf(filename, sizeof(filename), FDT_BASE_PATH "/%s", property);
95 file = fopen(filename, "rb");
96 if (!file) {
97 fprintf(stderr, "Unable to open FDT property %s\n", property);
98 return E_FILEOP;
99 }
100
101 if (fread(&data, 1, sizeof(data), file) != sizeof(data)) {
102 fprintf(stderr, "Unable to read FDT property %s\n", property);
103 return E_FILEOP;
104 }
105 fclose(file);
106
107 if (value)
108 *value = ntohl(data); /* FDT is network byte order */
109
110 return 0;
111 }
112
ReadFdtInt(const char * property)113 static int ReadFdtInt(const char *property) {
114 int value = 0;
115 if (ReadFdtValue(property, &value))
116 return E_FAIL;
117 return value;
118 }
119
GetFdtPropertyPath(const char * property,char * path,size_t size)120 static void GetFdtPropertyPath(const char *property, char *path, size_t size) {
121 if (property[0] == '/')
122 StrCopy(path, property, size);
123 else
124 snprintf(path, size, FDT_BASE_PATH "/%s", property);
125 }
126
FdtPropertyExist(const char * property)127 static int FdtPropertyExist(const char *property) {
128 char filename[FNAME_SIZE];
129 struct stat file_status;
130
131 GetFdtPropertyPath(property, filename, sizeof(filename));
132 if (!stat(filename, &file_status))
133 return 1; // It exists!
134 else
135 return 0; // It does not exist or some error happened.
136 }
137
ReadFdtBlock(const char * property,void ** block,size_t * size)138 static int ReadFdtBlock(const char *property, void **block, size_t *size) {
139 char filename[FNAME_SIZE];
140 FILE *file;
141 size_t property_size;
142 char *data;
143
144 if (!block)
145 return E_FAIL;
146
147 GetFdtPropertyPath(property, filename, sizeof(filename));
148 file = fopen(filename, "rb");
149 if (!file) {
150 fprintf(stderr, "Unable to open FDT property %s\n", property);
151 return E_FILEOP;
152 }
153
154 fseek(file, 0, SEEK_END);
155 property_size = ftell(file);
156 rewind(file);
157
158 data = malloc(property_size +1);
159 if (!data) {
160 fclose(file);
161 return E_MEM;
162 }
163 data[property_size] = 0;
164
165 if (1 != fread(data, property_size, 1, file)) {
166 fprintf(stderr, "Unable to read from property %s\n", property);
167 fclose(file);
168 free(data);
169 return E_FILEOP;
170 }
171
172 fclose(file);
173 *block = data;
174 if (size)
175 *size = property_size;
176
177 return 0;
178 }
179
ReadFdtString(const char * property)180 static char * ReadFdtString(const char *property) {
181 void *str = NULL;
182 /* Do not need property size */
183 ReadFdtBlock(property, &str, 0);
184 return (char *)str;
185 }
186
ReadFdtPlatformFamily(void)187 static char * ReadFdtPlatformFamily(void) {
188 char *compat = NULL;
189 char *s;
190 const PlatformFamily* p;
191 size_t size = 0;
192 int slen;
193
194 if(ReadFdtBlock(FDT_COMPATIBLE_PATH, (void **)&compat, &size))
195 return NULL;
196
197 if (size > 0)
198 compat[size-1] = 0;
199
200 /* Check each null separated string in compatible against the family array */
201 s = compat;
202 while ((s-compat) < size) {
203 slen = strlen(s);
204 for (p = platform_family_array; p->compatible_string; p++) {
205 if (!strcmp(s, p->compatible_string)) {
206 free(compat);
207 return strdup(p->platform_string);
208 }
209 }
210 s += slen + 1;
211 }
212
213 /* No recognized 'compatible' entry found */
214 free(compat);
215 return NULL;
216 }
217
VbGetPlatformGpioStatus(const char * name)218 static int VbGetPlatformGpioStatus(const char* name) {
219 char gpio_name[FNAME_SIZE];
220 unsigned value;
221
222 snprintf(gpio_name, sizeof(gpio_name), "%s/%s/value",
223 PLATFORM_DEV_PATH, name);
224 if (ReadFileInt(gpio_name, &value) < 0)
225 return -1;
226
227 return (int)value;
228 }
229
VbGetGpioStatus(unsigned gpio_number)230 static int VbGetGpioStatus(unsigned gpio_number) {
231 char gpio_name[FNAME_SIZE];
232 unsigned value;
233
234 snprintf(gpio_name, sizeof(gpio_name), "%s/gpio%d/value",
235 GPIO_BASE_PATH, gpio_number);
236 if (ReadFileInt(gpio_name, &value) < 0) {
237 /* Try exporting the GPIO */
238 FILE* f = fopen(GPIO_EXPORT_PATH, "wt");
239 if (!f)
240 return -1;
241 fprintf(f, "%d", gpio_number);
242 fclose(f);
243
244 /* Try re-reading the GPIO value */
245 if (ReadFileInt(gpio_name, &value) < 0)
246 return -1;
247 }
248
249 return (int)value;
250 }
251
VbGetVarGpio(const char * name)252 static int VbGetVarGpio(const char* name) {
253 int gpio_num;
254 void *pp = NULL;
255 int *prop;
256 size_t proplen = 0;
257 int ret = 0;
258
259 /* TODO: This should at some point in the future use the phandle
260 * to find the gpio chip and thus the base number. Assume 0 now,
261 * which isn't 100% future-proof (i.e. if one of the switches gets
262 * moved to an offchip gpio controller.
263 */
264
265 ret = ReadFdtBlock(name, &pp, &proplen);
266 if (ret || !pp || proplen != 12) {
267 ret = 2;
268 goto out;
269 }
270 prop = pp;
271 gpio_num = ntohl(prop[1]);
272
273 /*
274 * TODO(chrome-os-partner:11296): Use gpio_num == 0 to denote non-exist
275 * GPIO for now, at the risk that one day we might actually want to read
276 * from a GPIO port 0. We should figure out how to represent "non-exist"
277 * properly.
278 */
279 if (gpio_num)
280 ret = VbGetGpioStatus(gpio_num);
281 else
282 ret = -1;
283 out:
284 if (pp)
285 free(pp);
286
287 return ret;
288 }
289
ExecuteMosys(char * const argv[],char * buf,size_t bufsize)290 static int ExecuteMosys(char * const argv[], char *buf, size_t bufsize) {
291 int status, mosys_to_crossystem[2];
292 pid_t pid;
293 ssize_t n;
294
295 if (pipe(mosys_to_crossystem) < 0) {
296 VBDEBUG(("pipe() error\n"));
297 return -1;
298 }
299
300 if ((pid = fork()) < 0) {
301 VBDEBUG(("fork() error\n"));
302 close(mosys_to_crossystem[0]);
303 close(mosys_to_crossystem[1]);
304 return -1;
305 } else if (!pid) { /* Child */
306 close(mosys_to_crossystem[0]);
307 /* Redirect pipe's write-end to mosys' stdout */
308 if (STDOUT_FILENO != mosys_to_crossystem[1]) {
309 if (dup2(mosys_to_crossystem[1], STDOUT_FILENO) != STDOUT_FILENO) {
310 VBDEBUG(("stdout dup2() failed (mosys)\n"));
311 close(mosys_to_crossystem[1]);
312 exit(1);
313 }
314 }
315 /* Execute mosys */
316 execv(MOSYS_PATH, argv);
317 /* We shouldn't be here; exit now! */
318 VBDEBUG(("execv() of mosys failed\n"));
319 close(mosys_to_crossystem[1]);
320 exit(1);
321 } else { /* Parent */
322 close(mosys_to_crossystem[1]);
323 if (bufsize) {
324 bufsize--; /* Reserve 1 byte for '\0' */
325 while ((n = read(mosys_to_crossystem[0], buf, bufsize)) > 0) {
326 buf += n;
327 bufsize -= n;
328 }
329 *buf = '\0';
330 } else {
331 n = 0;
332 }
333 close(mosys_to_crossystem[0]);
334 if (n < 0)
335 VBDEBUG(("read() error while reading output from mosys\n"));
336 if (waitpid(pid, &status, 0) < 0 || status) {
337 VBDEBUG(("waitpid() or mosys error\n"));
338 fprintf(stderr, "waitpid() or mosys error\n");
339 return -1;
340 }
341 if (n < 0)
342 return -1;
343 }
344 return 0;
345 }
346
VbReadNvStorage_mosys(VbNvContext * vnc)347 static int VbReadNvStorage_mosys(VbNvContext* vnc) {
348 char hexstring[VBNV_BLOCK_SIZE * 2 + 32]; /* Reserve extra 32 bytes */
349 char * const argv[] = {
350 MOSYS_PATH, "nvram", "vboot", "read", NULL
351 };
352 char hexdigit[3];
353 int i;
354
355 if (ExecuteMosys(argv, hexstring, sizeof(hexstring)))
356 return -1;
357 hexdigit[2] = '\0';
358 for (i = 0; i < VBNV_BLOCK_SIZE; i++) {
359 hexdigit[0] = hexstring[i * 2];
360 hexdigit[1] = hexstring[i * 2 + 1];
361 vnc->raw[i] = strtol(hexdigit, NULL, 16);
362 }
363 return 0;
364 }
365
VbWriteNvStorage_mosys(VbNvContext * vnc)366 static int VbWriteNvStorage_mosys(VbNvContext* vnc) {
367 char hexstring[VBNV_BLOCK_SIZE * 2 + 1];
368 char * const argv[] = {
369 MOSYS_PATH, "nvram", "vboot", "write", hexstring, NULL
370 };
371 int i;
372
373 for (i = 0; i < VBNV_BLOCK_SIZE; i++)
374 snprintf(hexstring + i * 2, 3, "%02x", vnc->raw[i]);
375 hexstring[sizeof(hexstring) - 1] = '\0';
376 if (ExecuteMosys(argv, NULL, 0))
377 return -1;
378 return 0;
379 }
380
VbReadNvStorage_disk(VbNvContext * vnc)381 static int VbReadNvStorage_disk(VbNvContext* vnc) {
382 int nvctx_fd = -1;
383 uint8_t sector[SECTOR_SIZE];
384 int rv = -1;
385 char nvctx_path[FNAME_SIZE];
386 int emmc_dev;
387 int lba = ReadFdtInt("nonvolatile-context-lba");
388 int offset = ReadFdtInt("nonvolatile-context-offset");
389 int size = ReadFdtInt("nonvolatile-context-size");
390
391 emmc_dev = FindEmmcDev();
392 if (emmc_dev < 0)
393 return E_FAIL;
394 snprintf(nvctx_path, sizeof(nvctx_path), NVCTX_PATH, emmc_dev);
395
396 if (size != sizeof(vnc->raw) || (size + offset > SECTOR_SIZE))
397 return E_FAIL;
398
399 nvctx_fd = open(nvctx_path, O_RDONLY);
400 if (nvctx_fd == -1) {
401 fprintf(stderr, "%s: failed to open %s\n", __FUNCTION__, nvctx_path);
402 goto out;
403 }
404 lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
405
406 rv = read(nvctx_fd, sector, SECTOR_SIZE);
407 if (size <= 0) {
408 fprintf(stderr, "%s: failed to read nvctx from device %s\n",
409 __FUNCTION__, nvctx_path);
410 goto out;
411 }
412 Memcpy(vnc->raw, sector+offset, size);
413 rv = 0;
414
415 out:
416 if (nvctx_fd > 0)
417 close(nvctx_fd);
418
419 return rv;
420 }
421
VbWriteNvStorage_disk(VbNvContext * vnc)422 static int VbWriteNvStorage_disk(VbNvContext* vnc) {
423 int nvctx_fd = -1;
424 uint8_t sector[SECTOR_SIZE];
425 int rv = -1;
426 char nvctx_path[FNAME_SIZE];
427 int emmc_dev;
428 int lba = ReadFdtInt("nonvolatile-context-lba");
429 int offset = ReadFdtInt("nonvolatile-context-offset");
430 int size = ReadFdtInt("nonvolatile-context-size");
431
432 emmc_dev = FindEmmcDev();
433 if (emmc_dev < 0)
434 return E_FAIL;
435 snprintf(nvctx_path, sizeof(nvctx_path), NVCTX_PATH, emmc_dev);
436
437 if (size != sizeof(vnc->raw) || (size + offset > SECTOR_SIZE))
438 return E_FAIL;
439
440 do {
441 nvctx_fd = open(nvctx_path, O_RDWR);
442 if (nvctx_fd == -1) {
443 fprintf(stderr, "%s: failed to open %s\n", __FUNCTION__, nvctx_path);
444 break;
445 }
446 lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
447 rv = read(nvctx_fd, sector, SECTOR_SIZE);
448 if (rv <= 0) {
449 fprintf(stderr, "%s: failed to read nvctx from device %s\n",
450 __FUNCTION__, nvctx_path);
451 break;
452 }
453 Memcpy(sector+offset, vnc->raw, size);
454 lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
455 rv = write(nvctx_fd, sector, SECTOR_SIZE);
456 if (rv <= 0) {
457 fprintf(stderr, "%s: failed to write nvctx to device %s\n",
458 __FUNCTION__, nvctx_path);
459 break;
460 }
461 #ifndef HAVE_MACOS
462 /* Must flush buffer cache here to make sure it goes to disk */
463 rv = ioctl(nvctx_fd, BLKFLSBUF, 0);
464 if (rv < 0) {
465 fprintf(stderr, "%s: failed to flush nvctx to device %s\n",
466 __FUNCTION__, nvctx_path);
467 break;
468 }
469 #endif
470 rv = 0;
471 } while (0);
472
473 if (nvctx_fd > 0)
474 close(nvctx_fd);
475
476 return rv;
477 }
478
VbReadNvStorage(VbNvContext * vnc)479 int VbReadNvStorage(VbNvContext* vnc) {
480 /* Default to disk for older firmware which does not provide storage type */
481 char *media;
482 if (!FdtPropertyExist(FDT_NVSTORAGE_TYPE_PROP))
483 return VbReadNvStorage_disk(vnc);
484 media = ReadFdtString(FDT_NVSTORAGE_TYPE_PROP);
485 if (!strcmp(media, "disk"))
486 return VbReadNvStorage_disk(vnc);
487 if (!strcmp(media, "mkbp") || !strcmp(media, "flash"))
488 return VbReadNvStorage_mosys(vnc);
489 return -1;
490 }
491
VbWriteNvStorage(VbNvContext * vnc)492 int VbWriteNvStorage(VbNvContext* vnc) {
493 /* Default to disk for older firmware which does not provide storage type */
494 char *media;
495 if (!FdtPropertyExist(FDT_NVSTORAGE_TYPE_PROP))
496 return VbWriteNvStorage_disk(vnc);
497 media = ReadFdtString(FDT_NVSTORAGE_TYPE_PROP);
498 if (!strcmp(media, "disk"))
499 return VbWriteNvStorage_disk(vnc);
500 if (!strcmp(media, "mkbp") || !strcmp(media, "flash"))
501 return VbWriteNvStorage_mosys(vnc);
502 return -1;
503 }
504
VbSharedDataRead(void)505 VbSharedDataHeader *VbSharedDataRead(void) {
506 void *block = NULL;
507 size_t size = 0;
508 if (ReadFdtBlock("vboot-shared-data", &block, &size))
509 return NULL;
510 VbSharedDataHeader *p = (VbSharedDataHeader *)block;
511 if (p->magic != VB_SHARED_DATA_MAGIC) {
512 fprintf(stderr, "%s: failed to validate magic in "
513 "VbSharedDataHeader (%x != %x)\n",
514 __FUNCTION__, p->magic, VB_SHARED_DATA_MAGIC);
515 return NULL;
516 }
517 return (VbSharedDataHeader *)block;
518 }
519
VbGetArchPropertyInt(const char * name)520 int VbGetArchPropertyInt(const char* name) {
521 if (!strcasecmp(name, "fmap_base")) {
522 return ReadFdtInt("fmap-offset");
523 } else if (!strcasecmp(name, "devsw_cur")) {
524 /* Systems with virtual developer switches return at-boot value */
525 int flags = VbGetSystemPropertyInt("vdat_flags");
526 if ((flags != -1) && (flags & VBSD_HONOR_VIRT_DEV_SWITCH))
527 return VbGetSystemPropertyInt("devsw_boot");
528
529 return VbGetVarGpio("developer-switch");
530 } else if (!strcasecmp(name, "recoverysw_cur")) {
531 int value;
532 value = VbGetPlatformGpioStatus("recovery");
533 if (value != -1)
534 return value;
535
536 return VbGetVarGpio("recovery-switch");
537 } else if (!strcasecmp(name, "wpsw_cur")) {
538 int value;
539 /* Try finding the GPIO through the chromeos_arm platform device first. */
540 value = VbGetPlatformGpioStatus("write-protect");
541 if (value != -1)
542 return value;
543 return VbGetVarGpio("write-protect-switch");
544 } else if (!strcasecmp(name, "recoverysw_ec_boot"))
545 /* TODO: read correct value using ectool */
546 return 0;
547 else
548 return -1;
549 }
550
VbGetArchPropertyString(const char * name,char * dest,size_t size)551 const char* VbGetArchPropertyString(const char* name, char* dest,
552 size_t size) {
553 char *str = NULL;
554 char *rv = NULL;
555 char *prop = NULL;
556
557 if (!strcasecmp(name,"arch"))
558 return StrCopy(dest, "arm", size);
559
560 /* Properties from fdt */
561 if (!strcasecmp(name, "ro_fwid"))
562 prop = "readonly-firmware-version";
563 else if (!strcasecmp(name, "hwid"))
564 prop = "hardware-id";
565 else if (!strcasecmp(name, "fwid"))
566 prop = "firmware-version";
567 else if (!strcasecmp(name, "mainfw_type"))
568 prop = "firmware-type";
569 else if (!strcasecmp(name, "ecfw_act"))
570 prop = "active-ec-firmware";
571 else if (!strcasecmp(name, "ddr_type"))
572 prop = "ddr-type";
573
574 if (prop)
575 str = ReadFdtString(prop);
576
577 if (!strcasecmp(name, "platform_family"))
578 str = ReadFdtPlatformFamily();
579
580 if (str) {
581 rv = StrCopy(dest, str, size);
582 free(str);
583 return rv;
584 }
585 return NULL;
586 }
587
VbSetArchPropertyInt(const char * name,int value)588 int VbSetArchPropertyInt(const char* name, int value) {
589 /* All is handled in arch independent fashion */
590 return -1;
591 }
592
VbSetArchPropertyString(const char * name,const char * value)593 int VbSetArchPropertyString(const char* name, const char* value) {
594 /* All is handled in arch independent fashion */
595 return -1;
596 }
597
VbArchInit(void)598 int VbArchInit(void)
599 {
600 return 0;
601 }
602