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 <ctype.h>
7#include <dirent.h>
8#include <errno.h>
9#include <linux/nvram.h>
10#include <stddef.h>
11#include <stdint.h>
12#include <stdio.h>
13#include <string.h>
14#include <sys/ioctl.h>
15#include <sys/stat.h>
16#include <sys/types.h>
17#include <unistd.h>
18
19#include "crossystem.h"
20#include "crossystem_arch.h"
21#include "host_common.h"
22#include "utility.h"
23#include "vboot_common.h"
24#include "vboot_nvstorage.h"
25#include "vboot_struct.h"
26
27
28/* ACPI constants from Chrome OS Main Processor Firmware Spec */
29/* Boot reasons from BINF.0, from early H2C firmware */
30/* Unknown */
31#define BINF0_UNKNOWN                  0
32/* Normal boot to Chrome OS */
33#define BINF0_NORMAL                   1
34/* Developer mode boot (developer mode warning displayed) */
35#define BINF0_DEVELOPER                2
36/* Recovery initiated by user, using recovery button */
37#define BINF0_RECOVERY_BUTTON          3
38/* Recovery initiated by user pressing a key at developer mode warning
39 * screen */
40#define BINF0_RECOVERY_DEV_SCREEN_KEY  4
41/* Recovery caused by BIOS failed signature check (neither rewritable
42 * firmware was valid) */
43#define BINF0_RECOVERY_RW_FW_BAD       5
44/* Recovery caused by no OS kernel detected */
45#define BINF0_RECOVERY_NO_OS           6
46/* Recovery caused by OS kernel failed signature check */
47#define BINF0_RECOVERY_BAD_OS          7
48/* Recovery initiated by OS */
49#define BINF0_RECOVERY_OS_INITIATED    8
50/* OS-initiated S3 diagnostic path (debug mode boot) */
51#define BINF0_S3_DIAGNOSTIC_PATH       9
52/* S3 resume failed */
53#define BINF0_S3_RESUME_FAILED        10
54/* Recovery caused by TPM error */
55#define BINF0_RECOVERY_TPM_ERROR      11
56/* CHSW bitflags */
57#define CHSW_RECOVERY_BOOT     0x00000002
58#define CHSW_RECOVERY_EC_BOOT  0x00000004
59#define CHSW_DEV_BOOT          0x00000020
60#define CHSW_WP_BOOT           0x00000200
61/* CMOS reboot field bitflags */
62#define CMOSRF_RECOVERY        0x80
63#define CMOSRF_DEBUG_RESET     0x40
64#define CMOSRF_TRY_B           0x20
65/* GPIO signal types */
66#define GPIO_SIGNAL_TYPE_RECOVERY 1
67#define GPIO_SIGNAL_TYPE_DEV 2
68#define GPIO_SIGNAL_TYPE_WP 3
69
70/* Base name for ACPI files */
71#define ACPI_BASE_PATH "/sys/devices/platform/chromeos_acpi"
72/* Paths for frequently used ACPI files */
73#define ACPI_BINF_PATH ACPI_BASE_PATH "/BINF"
74#define ACPI_CHNV_PATH ACPI_BASE_PATH "/CHNV"
75#define ACPI_CHSW_PATH ACPI_BASE_PATH "/CHSW"
76#define ACPI_FMAP_PATH ACPI_BASE_PATH "/FMAP"
77#define ACPI_GPIO_PATH ACPI_BASE_PATH "/GPIO"
78#define ACPI_VBNV_PATH ACPI_BASE_PATH "/VBNV"
79#define ACPI_VDAT_PATH ACPI_BASE_PATH "/VDAT"
80
81/* Base name for GPIO files */
82#define GPIO_BASE_PATH "/sys/class/gpio"
83#define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
84
85/* Filename for NVRAM file */
86#define NVRAM_PATH "/dev/nvram"
87
88/* Filename for legacy firmware update tries */
89#define NEED_FWUPDATE_PATH "/mnt/stateful_partition/.need_firmware_update"
90
91/* Filenames for PCI Vendor and Device IDs */
92#define PCI_VENDOR_ID_PATH "/sys/bus/pci/devices/0000:00:00.0/vendor"
93#define PCI_DEVICE_ID_PATH "/sys/bus/pci/devices/0000:00:00.0/device"
94
95typedef struct PlatformFamily {
96  unsigned int vendor;          /* Vendor id value */
97  unsigned int device;          /* Device id value */
98  const char* platform_string; /* String to return */
99} PlatformFamily;
100
101/* Array of platform family names, terminated with a NULL entry */
102const PlatformFamily platform_family_array[] = {
103  {0x8086, 0xA010, "PineTrail"},
104  {0x8086, 0x3406, "Westmere"},
105  {0x8086, 0x0104, "SandyBridge"}, /* mobile */
106  {0x8086, 0x0100, "SandyBridge"}, /* desktop */
107  {0x8086, 0x0154, "IvyBridge"},   /* mobile */
108  {0x8086, 0x0150, "IvyBridge"},   /* desktop */
109  {0x8086, 0x0a04, "Haswell"},     /* ult */
110  {0x8086, 0x0c04, "Haswell"},     /* mobile */
111  {0x8086, 0x0f00, "BayTrail"},    /* mobile */
112  {0x8086, 0x1604, "Broadwell"},   /* ult */
113  /* Terminate with NULL entry */
114  {0, 0, 0}
115};
116
117static void VbFixCmosChecksum(FILE* file) {
118  int fd = fileno(file);
119  ioctl(fd, NVRAM_SETCKS);
120}
121
122
123static int VbCmosRead(unsigned offs, size_t size, void *ptr) {
124  size_t res;
125  FILE* f;
126
127  f = fopen(NVRAM_PATH, "rb");
128  if (!f)
129    return -1;
130
131  if (0 != fseek(f, offs, SEEK_SET)) {
132    fclose(f);
133    return -1;
134  }
135
136  res = fread(ptr, size, 1, f);
137  if (1 != res && errno == EIO && ferror(f)) {
138    VbFixCmosChecksum(f);
139    res = fread(ptr, size, 1, f);
140  }
141
142  fclose(f);
143  return (1 == res) ? 0 : -1;
144}
145
146
147static int VbCmosWrite(unsigned offs, size_t size, const void *ptr) {
148  size_t res;
149  FILE* f;
150
151  f = fopen(NVRAM_PATH, "w+b");
152  if (!f)
153    return -1;
154
155  if (0 != fseek(f, offs, SEEK_SET)) {
156    fclose(f);
157    return -1;
158  }
159
160  res = fwrite(ptr, size, 1, f);
161  if (1 != res && errno == EIO && ferror(f)) {
162    VbFixCmosChecksum(f);
163    res = fwrite(ptr, size, 1, f);
164  }
165
166  fclose(f);
167  return (1 == res) ? 0 : -1;
168}
169
170
171int VbReadNvStorage(VbNvContext* vnc) {
172  unsigned offs, blksz;
173
174  /* Get the byte offset from VBNV */
175  if (ReadFileInt(ACPI_VBNV_PATH ".0", &offs) < 0)
176    return -1;
177  if (ReadFileInt(ACPI_VBNV_PATH ".1", &blksz) < 0)
178    return -1;
179  if (VBNV_BLOCK_SIZE > blksz)
180    return -1;  /* NV storage block is too small */
181
182  if (0 != VbCmosRead(offs, VBNV_BLOCK_SIZE, vnc->raw))
183    return -1;
184
185  return 0;
186}
187
188
189int VbWriteNvStorage(VbNvContext* vnc) {
190  unsigned offs, blksz;
191
192  if (!vnc->raw_changed)
193    return 0;  /* Nothing changed, so no need to write */
194
195  /* Get the byte offset from VBNV */
196  if (ReadFileInt(ACPI_VBNV_PATH ".0", &offs) < 0)
197    return -1;
198  if (ReadFileInt(ACPI_VBNV_PATH ".1", &blksz) < 0)
199    return -1;
200  if (VBNV_BLOCK_SIZE > blksz)
201    return -1;  /* NV storage block is too small */
202
203  if (0 != VbCmosWrite(offs, VBNV_BLOCK_SIZE, vnc->raw))
204    return -1;
205
206  return 0;
207}
208
209
210/*
211 * Get buffer data from ACPI.
212 *
213 * Buffer data is expected to be represented by a file which is a text dump of
214 * the buffer, representing each byte by two hex numbers, space and newline
215 * separated.
216 *
217 * On success, stores the amount of data read in bytes to *buffer_size; on
218 * erros, sets *buffer_size=0.
219 *
220 * Input - ACPI file name to get data from.
221 *
222 * Output: a pointer to AcpiBuffer structure containing the binary
223 *         representation of the data. The caller is responsible for
224 *         deallocating the pointer, this will take care of both the structure
225 *         and the buffer. Null in case of error.
226 */
227static uint8_t* VbGetBuffer(const char* filename, int* buffer_size) {
228  FILE* f = NULL;
229  char* file_buffer = NULL;
230  uint8_t* output_buffer = NULL;
231  uint8_t* return_value = NULL;
232
233  /* Assume error until proven otherwise */
234  if (buffer_size)
235    *buffer_size = 0;
236
237  do {
238    struct stat fs;
239    uint8_t* output_ptr;
240    int rv, i, real_size;
241    int parsed_size = 0;
242
243    rv = stat(filename, &fs);
244    if (rv || !S_ISREG(fs.st_mode))
245      break;
246
247    f = fopen(filename, "r");
248    if (!f)
249      break;
250
251    file_buffer = malloc(fs.st_size + 1);
252    if (!file_buffer)
253      break;
254
255    real_size = fread(file_buffer, 1, fs.st_size, f);
256    if (!real_size)
257      break;
258    file_buffer[real_size] = '\0';
259
260    /* Each byte in the output will replace two characters and a space
261     * in the input, so the output size does not exceed input side/3
262     * (a little less if account for newline characters). */
263    output_buffer = malloc(real_size/3);
264    if (!output_buffer)
265      break;
266    output_ptr = output_buffer;
267
268    /* process the file contents */
269    for (i = 0; i < real_size; i++) {
270      char* base, *end;
271
272      base = file_buffer + i;
273
274      if (!isxdigit(*base))
275        continue;
276
277      output_ptr[parsed_size++] = strtol(base, &end, 16) & 0xff;
278
279      if ((end - base) != 2)
280        /* Input file format error */
281        break;
282
283      i += 2; /* skip the second character and the following space */
284    }
285
286    if (i == real_size) {
287      /* all is well */
288      return_value = output_buffer;
289      output_buffer = NULL; /* prevent it from deallocating */
290      if (buffer_size)
291        *buffer_size = parsed_size;
292    }
293  } while(0);
294
295  /* wrap up */
296  if (f)
297    fclose(f);
298
299  if (file_buffer)
300    free(file_buffer);
301
302  if (output_buffer)
303    free(output_buffer);
304
305  return return_value;
306}
307
308
309VbSharedDataHeader* VbSharedDataRead(void) {
310  VbSharedDataHeader* sh;
311  int got_size = 0;
312  int expect_size;
313
314  sh = (VbSharedDataHeader*)VbGetBuffer(ACPI_VDAT_PATH, &got_size);
315  if (!sh)
316    return NULL;
317
318  /* Make sure the size is sufficient for the struct version we got.
319   * Check supported old versions first. */
320  if (1 == sh->struct_version)
321    expect_size = VB_SHARED_DATA_HEADER_SIZE_V1;
322  else {
323    /* There'd better be enough data for the current header size. */
324    expect_size = sizeof(VbSharedDataHeader);
325  }
326
327  if (got_size < expect_size) {
328    free(sh);
329    return NULL;
330  }
331  if (sh->data_size > got_size)
332    sh->data_size = got_size;  /* Truncated read */
333
334  return sh;
335}
336
337
338/* Read the CMOS reboot field in NVRAM.
339 *
340 * Returns 0 if the mask is clear in the field, 1 if set, or -1 if error. */
341static int VbGetCmosRebootField(uint8_t mask) {
342  unsigned chnv;
343  uint8_t nvbyte;
344
345  /* Get the byte offset from CHNV */
346  if (ReadFileInt(ACPI_CHNV_PATH, &chnv) < 0)
347    return -1;
348
349  if (0 != VbCmosRead(chnv, 1, &nvbyte))
350    return -1;
351
352  return (nvbyte & mask ? 1 : 0);
353}
354
355
356/* Write the CMOS reboot field in NVRAM.
357 *
358 * Sets (value=0) or clears (value!=0) the mask in the byte.
359 *
360 * Returns 0 if success, or -1 if error. */
361static int VbSetCmosRebootField(uint8_t mask, int value) {
362  unsigned chnv;
363  uint8_t nvbyte;
364
365  /* Get the byte offset from CHNV */
366  if (ReadFileInt(ACPI_CHNV_PATH, &chnv) < 0)
367    return -1;
368
369  if (0 != VbCmosRead(chnv, 1, &nvbyte))
370    return -1;
371
372  /* Set/clear the mask */
373  if (value)
374    nvbyte |= mask;
375  else
376    nvbyte &= ~mask;
377
378  /* Write the byte back */
379  if (0 != VbCmosWrite(chnv, 1, &nvbyte))
380    return -1;
381
382  /* Success */
383  return 0;
384}
385
386
387/* Read the active main firmware type into the destination buffer.
388 * Passed the destination and its size.  Returns the destination, or
389 * NULL if error. */
390static const char* VbReadMainFwType(char* dest, int size) {
391  unsigned value;
392
393  /* Try reading type from BINF.3 */
394  if (ReadFileInt(ACPI_BINF_PATH ".3", &value) == 0) {
395    switch(value) {
396      case BINF3_NETBOOT:
397        return StrCopy(dest, "netboot", size);
398      case BINF3_RECOVERY:
399        return StrCopy(dest, "recovery", size);
400      case BINF3_NORMAL:
401        return StrCopy(dest, "normal", size);
402      case BINF3_DEVELOPER:
403        return StrCopy(dest, "developer", size);
404      default:
405        break;  /* Fall through to legacy handling */
406    }
407  }
408
409  /* Fall back to BINF.0 for legacy systems like Mario. */
410  if (ReadFileInt(ACPI_BINF_PATH ".0", &value) < 0)
411    /* Both BINF.0 and BINF.3 are missing, so this isn't Chrome OS
412     * firmware. */
413    return StrCopy(dest, "nonchrome", size);
414
415  switch(value) {
416    case BINF0_NORMAL:
417      return StrCopy(dest, "normal", size);
418    case BINF0_DEVELOPER:
419      return StrCopy(dest, "developer", size);
420    case BINF0_RECOVERY_BUTTON:
421    case BINF0_RECOVERY_DEV_SCREEN_KEY:
422    case BINF0_RECOVERY_RW_FW_BAD:
423    case BINF0_RECOVERY_NO_OS:
424    case BINF0_RECOVERY_BAD_OS:
425    case BINF0_RECOVERY_OS_INITIATED:
426    case BINF0_RECOVERY_TPM_ERROR:
427      /* Assorted flavors of recovery boot reason. */
428      return StrCopy(dest, "recovery", size);
429    default:
430      /* Other values don't map cleanly to firmware type. */
431      return NULL;
432  }
433}
434
435
436/* Read the recovery reason.  Returns the reason code or -1 if error. */
437static int VbGetRecoveryReason(void) {
438  unsigned value;
439
440  /* Try reading type from BINF.4 */
441  if (ReadFileInt(ACPI_BINF_PATH ".4", &value) == 0)
442    return value;
443
444  /* Fall back to BINF.0 for legacy systems like Mario. */
445  if (ReadFileInt(ACPI_BINF_PATH ".0", &value) < 0)
446    return -1;
447  switch(value) {
448    case BINF0_NORMAL:
449    case BINF0_DEVELOPER:
450      return VBNV_RECOVERY_NOT_REQUESTED;
451    case BINF0_RECOVERY_BUTTON:
452      return VBNV_RECOVERY_RO_MANUAL;
453    case BINF0_RECOVERY_DEV_SCREEN_KEY:
454      return VBNV_RECOVERY_RW_DEV_SCREEN;
455    case BINF0_RECOVERY_RW_FW_BAD:
456      return VBNV_RECOVERY_RO_INVALID_RW;
457    case BINF0_RECOVERY_NO_OS:
458      return VBNV_RECOVERY_RW_NO_OS;
459    case BINF0_RECOVERY_BAD_OS:
460      return VBNV_RECOVERY_RW_INVALID_OS;
461    case BINF0_RECOVERY_OS_INITIATED:
462      return VBNV_RECOVERY_LEGACY;
463    default:
464      /* Other values don't map cleanly to firmware type. */
465      return -1;
466  }
467}
468
469/* Determine the platform family and return it in the dest string.
470 * This uses the PCI Bus 0, Device 0, Function 0 vendor and device id values
471 * taken from sysfs to determine the platform family. This assumes there will
472 * be a unique pair of values here for any given platform.
473 */
474static char* ReadPlatformFamilyString(char* dest, int size) {
475  FILE* f;
476  const PlatformFamily* p;
477  unsigned int v = 0xFFFF;
478  unsigned int d = 0xFFFF;
479
480  f = fopen(PCI_VENDOR_ID_PATH, "rt");
481  if (!f)
482    return NULL;
483  if(fscanf(f, "0x%4x", &v) != 1)
484    return NULL;
485  fclose(f);
486
487  f = fopen(PCI_DEVICE_ID_PATH, "rt");
488  if (!f)
489    return NULL;
490  if(fscanf(f, "0x%4x", &d) != 1)
491    return NULL;
492  fclose(f);
493
494  for (p = platform_family_array; p->vendor; p++) {
495    if((v == p->vendor) && (d == p->device))
496      return StrCopy(dest, p->platform_string, size);
497  }
498
499  /* No recognized platform family was found */
500  return NULL;
501}
502
503/* Physical GPIO number <N> may be accessed through /sys/class/gpio/gpio<M>/,
504 * but <N> and <M> may differ by some offset <O>. To determine that constant,
505 * we look for a directory named /sys/class/gpio/gpiochip<O>/. If there's not
506 * exactly one match for that, we're SOL.
507 */
508static int FindGpioChipOffset(unsigned *gpio_num, unsigned *offset,
509                              const char *name) {
510  DIR *dir;
511  struct dirent *ent;
512  int match = 0;
513
514  dir = opendir(GPIO_BASE_PATH);
515  if (!dir) {
516    return 0;
517  }
518
519  while(0 != (ent = readdir(dir))) {
520    if (1 == sscanf(ent->d_name, "gpiochip%u", offset)) {
521      match++;
522    }
523  }
524
525  closedir(dir);
526  return (1 == match);
527}
528
529/* Physical GPIO number <N> may be accessed through /sys/class/gpio/gpio<M>/,
530 * but <N> and <M> may differ by some offset <O>. To determine that constant,
531 * we look for a directory named /sys/class/gpio/gpiochip<O>/ and check for
532 * a 'label' file inside of it to find the expected the controller name.
533 */
534static int FindGpioChipOffsetByLabel(unsigned *gpio_num, unsigned *offset,
535                                     const char *name) {
536  DIR *dir;
537  struct dirent *ent;
538  char filename[128];
539  char chiplabel[128];
540  int match = 0;
541
542  dir = opendir(GPIO_BASE_PATH);
543  if (!dir) {
544    return 0;
545  }
546
547  while(0 != (ent = readdir(dir))) {
548    if (1 == sscanf(ent->d_name, "gpiochip%u", offset)) {
549      /*
550       * Read the file at gpiochip<O>/label to get the identifier
551       * for this bank of GPIOs.
552       */
553      snprintf(filename, sizeof(filename), "%s/gpiochip%u/label",
554               GPIO_BASE_PATH, *offset);
555      if (ReadFileString(chiplabel, sizeof(chiplabel), filename)) {
556        if (!strncasecmp(chiplabel, name, strlen(name)))
557          match++;
558      }
559    }
560  }
561
562  closedir(dir);
563  return (1 == match);
564}
565
566/* BayTrail has 3 sets of GPIO banks. It is expected the firmware exposes
567 * each bank of gpios using a UID in ACPI. Furthermore the gpio number exposed
568 * is relative to the bank. e.g. gpio 6 in the bank specified by UID 3 would
569 * be encoded as 0x2006.
570 *  UID | Bank Offset
571 *  ----+------------
572 *   1  | 0x0000
573 *   2  | 0x1000
574 *   3  | 0x2000
575 */
576static int BayTrailFindGpioChipOffset(unsigned *gpio_num, unsigned *offset,
577                                      const char *name) {
578  DIR *dir;
579  struct dirent *ent;
580  unsigned expected_uid;
581  int match = 0;
582
583  /* Obtain relative GPIO number. */
584  if (*gpio_num >= 0x2000) {
585    *gpio_num -= 0x2000;
586    expected_uid = 3;
587  } else if (*gpio_num >= 0x1000) {
588    *gpio_num -= 0x1000;
589    expected_uid = 2;
590  } else if (*gpio_num >= 0x0000) {
591    *gpio_num -= 0x0000;
592    expected_uid = 1;
593  } else {
594    return 0;
595  }
596
597  dir = opendir(GPIO_BASE_PATH);
598  if (!dir) {
599    return 0;
600  }
601
602  while(0 != (ent = readdir(dir))) {
603    /* For every gpiochip entry determine uid. */
604    if (1 == sscanf(ent->d_name, "gpiochip%u", offset)) {
605      char uid_file[128];
606      unsigned uid_value;
607      snprintf(uid_file, sizeof(uid_file),
608               "%s/gpiochip%u/device/firmware_node/uid", GPIO_BASE_PATH,
609               *offset);
610      if (ReadFileInt(uid_file, &uid_value) < 0)
611        continue;
612      if (expected_uid == uid_value) {
613        match++;
614        break;
615      }
616    }
617  }
618
619  closedir(dir);
620  return (1 == match);
621}
622
623
624struct GpioChipset {
625  const char *name;
626  int (*ChipOffsetAndGpioNumber)(unsigned *gpio_num, unsigned *chip_offset,
627                                 const char *name);
628};
629
630static const struct GpioChipset chipsets_supported[] = {
631  { "NM10", FindGpioChipOffset },
632  { "CougarPoint", FindGpioChipOffset },
633  { "PantherPoint", FindGpioChipOffset },
634  { "LynxPoint", FindGpioChipOffset },
635  { "PCH-LP", FindGpioChipOffset },
636  { "INT3437:00", FindGpioChipOffsetByLabel },
637  { "BayTrail", BayTrailFindGpioChipOffset },
638  { NULL },
639};
640
641static const struct GpioChipset *FindChipset(const char *name) {
642  const struct GpioChipset *chipset = &chipsets_supported[0];
643
644  while (chipset->name != NULL) {
645    if (!strcmp(name, chipset->name))
646      return chipset;
647    chipset++;
648  }
649  return NULL;
650}
651
652/* Read a GPIO of the specified signal type (see ACPI GPIO SignalType).
653 *
654 * Returns 1 if the signal is asserted, 0 if not asserted, or -1 if error. */
655static int ReadGpio(unsigned signal_type) {
656  char name[128];
657  int index = 0;
658  unsigned gpio_type;
659  unsigned active_high;
660  unsigned controller_num;
661  unsigned controller_offset = 0;
662  char controller_name[128];
663  unsigned value;
664  const struct GpioChipset *chipset;
665
666  /* Scan GPIO.* to find a matching signal type */
667  for (index = 0; ; index++) {
668    snprintf(name, sizeof(name), "%s.%d/GPIO.0", ACPI_GPIO_PATH, index);
669    if (ReadFileInt(name, &gpio_type) < 0)
670      return -1;                  /* Ran out of GPIOs before finding a match */
671    if (gpio_type == signal_type)
672      break;
673  }
674
675  /* Read attributes and controller info for the GPIO */
676  snprintf(name, sizeof(name), "%s.%d/GPIO.1", ACPI_GPIO_PATH, index);
677  if (ReadFileInt(name, &active_high) < 0)
678    return -1;
679  snprintf(name, sizeof(name), "%s.%d/GPIO.2", ACPI_GPIO_PATH, index);
680  if (ReadFileInt(name, &controller_num) < 0)
681    return -1;
682  /* Do not attempt to read GPIO that is set to -1 in ACPI */
683  if (controller_num == 0xFFFFFFFF)
684    return -1;
685
686  /* Check for chipsets we recognize. */
687  snprintf(name, sizeof(name), "%s.%d/GPIO.3", ACPI_GPIO_PATH, index);
688  if (!ReadFileString(controller_name, sizeof(controller_name), name))
689    return -1;
690  chipset = FindChipset(controller_name);
691  if (chipset == NULL)
692    return -1;
693
694  /* Modify GPIO number by driver's offset */
695  if (!chipset->ChipOffsetAndGpioNumber(&controller_num, &controller_offset,
696                                        chipset->name))
697    return -1;
698  controller_offset += controller_num;
699
700  /* Try reading the GPIO value */
701  snprintf(name, sizeof(name), "%s/gpio%d/value",
702           GPIO_BASE_PATH, controller_offset);
703  if (ReadFileInt(name, &value) < 0) {
704    /* Try exporting the GPIO */
705    FILE* f = fopen(GPIO_EXPORT_PATH, "wt");
706    if (!f)
707      return -1;
708    fprintf(f, "%u", controller_offset);
709    fclose(f);
710
711    /* Try re-reading the GPIO value */
712    if (ReadFileInt(name, &value) < 0)
713      return -1;
714  }
715
716  /* Normalize the value read from the kernel in case it is not always 1. */
717  value = value ? 1 : 0;
718
719  /* Compare the GPIO value with the active value and return 1 if match. */
720  return (value == active_high ? 1 : 0);
721}
722
723
724int VbGetArchPropertyInt(const char* name) {
725  int value = -1;
726
727  /* Values from ACPI */
728  if (!strcasecmp(name,"fmap_base")) {
729    unsigned fmap_base;
730    if (ReadFileInt(ACPI_FMAP_PATH, &fmap_base) < 0)
731      return -1;
732    else
733      value = (int)fmap_base;
734  }
735
736  /* Switch positions */
737  if (!strcasecmp(name,"devsw_cur")) {
738    /* Systems with virtual developer switches return at-boot value */
739    int flags = VbGetSystemPropertyInt("vdat_flags");
740    if ((flags != -1) && (flags & VBSD_HONOR_VIRT_DEV_SWITCH))
741      value = VbGetSystemPropertyInt("devsw_boot");
742    else
743      value = ReadGpio(GPIO_SIGNAL_TYPE_DEV);
744  } else if (!strcasecmp(name,"recoverysw_cur")) {
745    value = ReadGpio(GPIO_SIGNAL_TYPE_RECOVERY);
746  } else if (!strcasecmp(name,"wpsw_cur")) {
747    value = ReadGpio(GPIO_SIGNAL_TYPE_WP);
748    if (-1 != value && FwidStartsWith("Mario."))
749      value = 1 - value;  /* Mario reports this backwards */
750  } else if (!strcasecmp(name,"recoverysw_ec_boot")) {
751    value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_EC_BOOT);
752  }
753
754  /* Fields for old systems which don't have VbSharedData */
755  if (VbSharedDataVersion() < 2) {
756    if (!strcasecmp(name,"recovery_reason")) {
757      value = VbGetRecoveryReason();
758    } else if (!strcasecmp(name,"devsw_boot")) {
759      value = ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT);
760    } else if (!strcasecmp(name,"recoverysw_boot")) {
761      value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_BOOT);
762    } else if (!strcasecmp(name,"wpsw_boot")) {
763      value = ReadFileBit(ACPI_CHSW_PATH, CHSW_WP_BOOT);
764      if (-1 != value && FwidStartsWith("Mario."))
765        value = 1 - value;  /* Mario reports this backwards */
766    }
767  }
768
769  /* Saved memory is at a fixed location for all H2C BIOS.  If the CHSW
770   * path exists in sysfs, it's a H2C BIOS. */
771  if (!strcasecmp(name,"savedmem_base")) {
772    unsigned savedmem_base;
773    if (ReadFileInt(ACPI_CHSW_PATH, &savedmem_base) < 0)
774      return -1;
775    else
776      return 0x00F00000;
777  } else if (!strcasecmp(name,"savedmem_size")) {
778    unsigned savedmem_size;
779    if (ReadFileInt(ACPI_CHSW_PATH, &savedmem_size) < 0)
780      return -1;
781    else
782      return 0x00100000;
783  }
784
785  /* NV storage values.  If unable to get from NV storage, fall back to the
786   * CMOS reboot field used by older BIOS (e.g. Mario). */
787  if (!strcasecmp(name,"recovery_request")) {
788    value = VbGetNvStorage(VBNV_RECOVERY_REQUEST);
789    if (-1 == value)
790      value = VbGetCmosRebootField(CMOSRF_RECOVERY);
791  } else if (!strcasecmp(name,"dbg_reset")) {
792    value = VbGetNvStorage(VBNV_DEBUG_RESET_MODE);
793    if (-1 == value)
794      value = VbGetCmosRebootField(CMOSRF_DEBUG_RESET);
795  } else if (!strcasecmp(name,"fwb_tries")) {
796    value = VbGetNvStorage(VBNV_TRY_B_COUNT);
797    if (-1 == value)
798      value = VbGetCmosRebootField(CMOSRF_TRY_B);
799  }
800
801  /* Firmware update tries is now stored in the kernel field.  On
802   * older systems where it's not, it was stored in a file in the
803   * stateful partition. */
804  if (!strcasecmp(name,"fwupdate_tries")) {
805    unsigned fwupdate_value;
806    if (-1 != VbGetNvStorage(VBNV_KERNEL_FIELD))
807      return -1;  /* NvStorage supported; fail through arch-specific
808                   * implementation to normal implementation. */
809    /* Read value from file; missing file means value=0. */
810    if (ReadFileInt(NEED_FWUPDATE_PATH, &fwupdate_value) < 0)
811      value = 0;
812    else
813      value = (int)fwupdate_value;
814  }
815
816  return value;
817}
818
819
820const char* VbGetArchPropertyString(const char* name, char* dest,
821                                    size_t size) {
822  unsigned value;
823
824  if (!strcasecmp(name,"arch")) {
825    return StrCopy(dest, "x86", size);
826  } else if (!strcasecmp(name,"hwid")) {
827    return ReadFileString(dest, size, ACPI_BASE_PATH "/HWID");
828  } else if (!strcasecmp(name,"fwid")) {
829    return ReadFileString(dest, size, ACPI_BASE_PATH "/FWID");
830  } else if (!strcasecmp(name,"ro_fwid")) {
831    return ReadFileString(dest, size, ACPI_BASE_PATH "/FRID");
832  } else if (!strcasecmp(name,"mainfw_act")) {
833    if (ReadFileInt(ACPI_BINF_PATH ".1", &value) < 0)
834      return NULL;
835    switch(value) {
836      case 0:
837        return StrCopy(dest, "recovery", size);
838      case 1:
839        return StrCopy(dest, "A", size);
840      case 2:
841        return StrCopy(dest, "B", size);
842      default:
843        return NULL;
844    }
845  } else if (!strcasecmp(name,"mainfw_type")) {
846    return VbReadMainFwType(dest, size);
847  } else if (!strcasecmp(name,"ecfw_act")) {
848    if (ReadFileInt(ACPI_BINF_PATH ".2", &value) < 0)
849      return NULL;
850    switch(value) {
851      case 0:
852        return StrCopy(dest, "RO", size);
853      case 1:
854        return StrCopy(dest, "RW", size);
855      default:
856        return NULL;
857    }
858  } else if (!strcasecmp(name,"platform_family")) {
859    return ReadPlatformFamilyString(dest, size);
860  }
861
862  return NULL;
863}
864
865
866int VbSetArchPropertyInt(const char* name, int value) {
867  /* NV storage values.  If unable to get from NV storage, fall back to the
868   * CMOS reboot field used by older BIOS. */
869  if (!strcasecmp(name,"recovery_request")) {
870    if (0 == VbSetNvStorage(VBNV_RECOVERY_REQUEST, value))
871      return 0;
872    return VbSetCmosRebootField(CMOSRF_RECOVERY, value);
873  } else if (!strcasecmp(name,"dbg_reset")) {
874    if (0 == VbSetNvStorage(VBNV_DEBUG_RESET_MODE, value))
875      return 0;
876    return  VbSetCmosRebootField(CMOSRF_DEBUG_RESET, value);
877  } else if (!strcasecmp(name,"fwb_tries")) {
878    if (0 == VbSetNvStorage(VBNV_TRY_B_COUNT, value))
879      return 0;
880    return VbSetCmosRebootField(CMOSRF_TRY_B, value);
881  }
882  /* Firmware update tries is now stored in the kernel field.  On
883   * older systems where it's not, it was stored in a file in the
884   * stateful partition. */
885  else if (!strcasecmp(name,"fwupdate_tries")) {
886    if (-1 != VbGetNvStorage(VBNV_KERNEL_FIELD))
887      return -1;  /* NvStorage supported; fail through arch-specific
888                   * implementation to normal implementation */
889
890    if (value) {
891      char buf[32];
892      snprintf(buf, sizeof(buf), "%d", value);
893      return WriteFile(NEED_FWUPDATE_PATH, buf, strlen(buf));
894    } else {
895      /* No update tries, so remove file if it exists. */
896      unlink(NEED_FWUPDATE_PATH);
897      return 0;
898    }
899  }
900
901  return -1;
902}
903
904
905int VbSetArchPropertyString(const char* name, const char* value) {
906  /* If there were settable architecture-dependent string properties,
907   * they'd be here. */
908  return -1;
909}
910