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
52typedef 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 */
58const 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
72static 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
89static 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
113static int ReadFdtInt(const char *property) {
114  int value = 0;
115  if (ReadFdtValue(property, &value))
116    return E_FAIL;
117  return value;
118}
119
120static 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
127static 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
138static 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
180static 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
187static 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
218static 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
230static 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
252static 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;
283out:
284  if (pp)
285    free(pp);
286
287  return ret;
288}
289
290static 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
347static 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
366static 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
381static 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
415out:
416  if (nvctx_fd > 0)
417    close(nvctx_fd);
418
419  return rv;
420}
421
422static 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
479int 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
492int 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
505VbSharedDataHeader *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
520int 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
551const 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
588int VbSetArchPropertyInt(const char* name, int value) {
589  /* All is handled in arch independent fashion */
590  return -1;
591}
592
593int VbSetArchPropertyString(const char* name, const char* value) {
594  /* All is handled in arch independent fashion */
595  return -1;
596}
597
598int VbArchInit(void)
599{
600  return 0;
601}
602