Home | History | Annotate | Download | only in lib
      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    588 int VbSetArchPropertyInt(const char* name, int value) {
    589   /* All is handled in arch independent fashion */
    590   return -1;
    591 }
    592 
    593 int VbSetArchPropertyString(const char* name, const char* value) {
    594   /* All is handled in arch independent fashion */
    595   return -1;
    596 }
    597 
    598 int VbArchInit(void)
    599 {
    600   return 0;
    601 }
    602