• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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