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