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