Detect and report VM environment in crossystem
[vboot.git] / host / arch / arm / 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 <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 #include "crossystem_vbnv.h"
28
29 /* Base name for firmware FDT files */
30 #define FDT_BASE_PATH "/proc/device-tree/firmware/chromeos"
31 /* Path to compatible FDT entry */
32 #define FDT_COMPATIBLE_PATH "/proc/device-tree/compatible"
33 /* Path to the chromeos_arm platform device */
34 #define PLATFORM_DEV_PATH "/sys/devices/platform/chromeos_arm"
35 /* Device for NVCTX write */
36 #define NVCTX_PATH "/dev/mmcblk%d"
37 /* Base name for GPIO files */
38 #define GPIO_BASE_PATH "/sys/class/gpio"
39 #define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
40 /* Name of NvStorage type property */
41 #define FDT_NVSTORAGE_TYPE_PROP "nonvolatile-context-storage"
42 /* Errors */
43 #define E_FAIL      -1
44 #define E_FILEOP    -2
45 #define E_MEM       -3
46 /* Common constants */
47 #define FNAME_SIZE  80
48 #define SECTOR_SIZE 512
49 #define MAX_NMMCBLK 9
50
51 static int FindEmmcDev(void) {
52   int mmcblk;
53   unsigned value;
54   char filename[FNAME_SIZE];
55   for (mmcblk = 0; mmcblk < MAX_NMMCBLK; mmcblk++) {
56     /* Get first non-removable mmc block device */
57     snprintf(filename, sizeof(filename), "/sys/block/mmcblk%d/removable",
58               mmcblk);
59     if (ReadFileInt(filename, &value) < 0)
60       continue;
61     if (value == 0)
62       return mmcblk;
63   }
64   /* eMMC not found */
65   return E_FAIL;
66 }
67
68 static int ReadFdtValue(const char *property, int *value) {
69   char filename[FNAME_SIZE];
70   FILE *file;
71   int data = 0;
72
73   snprintf(filename, sizeof(filename), FDT_BASE_PATH "/%s", property);
74   file = fopen(filename, "rb");
75   if (!file) {
76     fprintf(stderr, "Unable to open FDT property %s\n", property);
77     return E_FILEOP;
78   }
79
80   if (fread(&data, 1, sizeof(data), file) != sizeof(data)) {
81     fprintf(stderr, "Unable to read FDT property %s\n", property);
82     return E_FILEOP;
83   }
84   fclose(file);
85
86   if (value)
87     *value = ntohl(data); /* FDT is network byte order */
88
89   return 0;
90 }
91
92 static int ReadFdtInt(const char *property) {
93   int value = 0;
94   if (ReadFdtValue(property, &value))
95     return E_FAIL;
96   return value;
97 }
98
99 static void GetFdtPropertyPath(const char *property, char *path, size_t size) {
100   if (property[0] == '/')
101     StrCopy(path, property, size);
102   else
103     snprintf(path, size, FDT_BASE_PATH "/%s", property);
104 }
105
106 static int FdtPropertyExist(const char *property) {
107   char filename[FNAME_SIZE];
108   struct stat file_status;
109
110   GetFdtPropertyPath(property, filename, sizeof(filename));
111   if (!stat(filename, &file_status))
112     return 1; // It exists!
113   else
114     return 0; // It does not exist or some error happened.
115 }
116
117 static int ReadFdtBlock(const char *property, void **block, size_t *size) {
118   char filename[FNAME_SIZE];
119   FILE *file;
120   size_t property_size;
121   char *data;
122
123   if (!block)
124     return E_FAIL;
125
126   GetFdtPropertyPath(property, filename, sizeof(filename));
127   file = fopen(filename, "rb");
128   if (!file) {
129     fprintf(stderr, "Unable to open FDT property %s\n", property);
130     return E_FILEOP;
131   }
132
133   fseek(file, 0, SEEK_END);
134   property_size = ftell(file);
135   rewind(file);
136
137   data = malloc(property_size +1);
138   if (!data) {
139     fclose(file);
140     return E_MEM;
141   }
142   data[property_size] = 0;
143
144   if (1 != fread(data, property_size, 1, file)) {
145     fprintf(stderr, "Unable to read from property %s\n", property);
146     fclose(file);
147     free(data);
148     return E_FILEOP;
149   }
150
151   fclose(file);
152   *block = data;
153   if (size)
154     *size = property_size;
155
156   return 0;
157 }
158
159 static char * ReadFdtString(const char *property) {
160   void *str = NULL;
161   /* Do not need property size */
162   ReadFdtBlock(property, &str, 0);
163   return (char *)str;
164 }
165
166 static int VbGetPlatformGpioStatus(const char* name) {
167   char gpio_name[FNAME_SIZE];
168   unsigned value;
169
170   snprintf(gpio_name, sizeof(gpio_name), "%s/%s/value",
171            PLATFORM_DEV_PATH, name);
172   if (ReadFileInt(gpio_name, &value) < 0)
173     return -1;
174
175   return (int)value;
176 }
177
178 static int VbGetGpioStatus(unsigned gpio_number) {
179   char gpio_name[FNAME_SIZE];
180   unsigned value;
181
182   snprintf(gpio_name, sizeof(gpio_name), "%s/gpio%d/value",
183            GPIO_BASE_PATH, gpio_number);
184   if (ReadFileInt(gpio_name, &value) < 0) {
185     /* Try exporting the GPIO */
186     FILE* f = fopen(GPIO_EXPORT_PATH, "wt");
187     if (!f)
188       return -1;
189     fprintf(f, "%d", gpio_number);
190     fclose(f);
191
192     /* Try re-reading the GPIO value */
193     if (ReadFileInt(gpio_name, &value) < 0)
194       return -1;
195   }
196
197   return (int)value;
198 }
199
200 static int VbGetVarGpio(const char* name) {
201   int gpio_num;
202   void *pp = NULL;
203   int *prop;
204   size_t proplen = 0;
205   int ret = 0;
206
207   /* TODO: This should at some point in the future use the phandle
208    * to find the gpio chip and thus the base number. Assume 0 now,
209    * which isn't 100% future-proof (i.e. if one of the switches gets
210    * moved to an offchip gpio controller.
211    */
212
213   ret = ReadFdtBlock(name, &pp, &proplen);
214   if (ret || !pp || proplen != 12) {
215     ret = 2;
216     goto out;
217   }
218   prop = pp;
219   gpio_num = ntohl(prop[1]);
220
221   /*
222    * TODO(chrome-os-partner:11296): Use gpio_num == 0 to denote non-exist
223    * GPIO for now, at the risk that one day we might actually want to read
224    * from a GPIO port 0.  We should figure out how to represent "non-exist"
225    * properly.
226    */
227   if (gpio_num)
228     ret = VbGetGpioStatus(gpio_num);
229   else
230     ret = -1;
231 out:
232   if (pp)
233     free(pp);
234
235   return ret;
236 }
237
238
239 static int VbReadNvStorage_disk(VbNvContext* vnc) {
240   int nvctx_fd = -1;
241   uint8_t sector[SECTOR_SIZE];
242   int rv = -1;
243   char nvctx_path[FNAME_SIZE];
244   int emmc_dev;
245   int lba = ReadFdtInt("nonvolatile-context-lba");
246   int offset = ReadFdtInt("nonvolatile-context-offset");
247   int size = ReadFdtInt("nonvolatile-context-size");
248
249   emmc_dev = FindEmmcDev();
250   if (emmc_dev < 0)
251     return E_FAIL;
252   snprintf(nvctx_path, sizeof(nvctx_path), NVCTX_PATH, emmc_dev);
253
254   if (size != sizeof(vnc->raw) || (size + offset > SECTOR_SIZE))
255     return E_FAIL;
256
257   nvctx_fd = open(nvctx_path, O_RDONLY);
258   if (nvctx_fd == -1) {
259     fprintf(stderr, "%s: failed to open %s\n", __FUNCTION__, nvctx_path);
260     goto out;
261   }
262   lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
263
264   rv = read(nvctx_fd, sector, SECTOR_SIZE);
265   if (size <= 0) {
266     fprintf(stderr, "%s: failed to read nvctx from device %s\n",
267             __FUNCTION__, nvctx_path);
268     goto out;
269   }
270   Memcpy(vnc->raw, sector+offset, size);
271   rv = 0;
272
273 out:
274   if (nvctx_fd > 0)
275     close(nvctx_fd);
276
277   return rv;
278 }
279
280 static int VbWriteNvStorage_disk(VbNvContext* vnc) {
281   int nvctx_fd = -1;
282   uint8_t sector[SECTOR_SIZE];
283   int rv = -1;
284   char nvctx_path[FNAME_SIZE];
285   int emmc_dev;
286   int lba = ReadFdtInt("nonvolatile-context-lba");
287   int offset = ReadFdtInt("nonvolatile-context-offset");
288   int size = ReadFdtInt("nonvolatile-context-size");
289
290   emmc_dev = FindEmmcDev();
291   if (emmc_dev < 0)
292     return E_FAIL;
293   snprintf(nvctx_path, sizeof(nvctx_path), NVCTX_PATH, emmc_dev);
294
295   if (size != sizeof(vnc->raw) || (size + offset > SECTOR_SIZE))
296     return E_FAIL;
297
298   do {
299     nvctx_fd = open(nvctx_path, O_RDWR);
300     if (nvctx_fd == -1) {
301       fprintf(stderr, "%s: failed to open %s\n", __FUNCTION__, nvctx_path);
302       break;
303     }
304     lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
305     rv = read(nvctx_fd, sector, SECTOR_SIZE);
306     if (rv <= 0) {
307       fprintf(stderr, "%s: failed to read nvctx from device %s\n",
308               __FUNCTION__, nvctx_path);
309       break;
310     }
311     Memcpy(sector+offset, vnc->raw, size);
312     lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
313     rv = write(nvctx_fd, sector, SECTOR_SIZE);
314     if (rv <= 0) {
315       fprintf(stderr,  "%s: failed to write nvctx to device %s\n",
316               __FUNCTION__, nvctx_path);
317       break;
318     }
319 #ifndef HAVE_MACOS
320     /* Must flush buffer cache here to make sure it goes to disk */
321     rv = ioctl(nvctx_fd, BLKFLSBUF, 0);
322     if (rv < 0) {
323       fprintf(stderr,  "%s: failed to flush nvctx to device %s\n",
324               __FUNCTION__, nvctx_path);
325       break;
326     }
327 #endif
328     rv = 0;
329   } while (0);
330
331   if (nvctx_fd > 0)
332     close(nvctx_fd);
333
334   return rv;
335 }
336
337 int VbReadNvStorage(VbNvContext* vnc) {
338   /* Default to disk for older firmware which does not provide storage type */
339   char *media;
340   if (!FdtPropertyExist(FDT_NVSTORAGE_TYPE_PROP))
341     return VbReadNvStorage_disk(vnc);
342   media = ReadFdtString(FDT_NVSTORAGE_TYPE_PROP);
343   if (!strcmp(media, "disk"))
344     return VbReadNvStorage_disk(vnc);
345   if (!strcmp(media, "cros-ec") || !strcmp(media, "mkbp") ||
346       !strcmp(media, "flash"))
347     return VbReadNvStorage_mosys(vnc);
348   return -1;
349 }
350
351 int VbWriteNvStorage(VbNvContext* vnc) {
352   /* Default to disk for older firmware which does not provide storage type */
353   char *media;
354   if (!FdtPropertyExist(FDT_NVSTORAGE_TYPE_PROP))
355     return VbWriteNvStorage_disk(vnc);
356   media = ReadFdtString(FDT_NVSTORAGE_TYPE_PROP);
357   if (!strcmp(media, "disk"))
358     return VbWriteNvStorage_disk(vnc);
359   if (!strcmp(media, "cros-ec") || !strcmp(media, "mkbp") ||
360       !strcmp(media, "flash"))
361     return VbWriteNvStorage_mosys(vnc);
362   return -1;
363 }
364
365 VbSharedDataHeader *VbSharedDataRead(void) {
366   void *block = NULL;
367   size_t size = 0;
368   if (ReadFdtBlock("vboot-shared-data", &block, &size))
369     return NULL;
370   VbSharedDataHeader *p = (VbSharedDataHeader *)block;
371   if (p->magic != VB_SHARED_DATA_MAGIC) {
372     fprintf(stderr,  "%s: failed to validate magic in "
373             "VbSharedDataHeader (%x != %x)\n",
374             __FUNCTION__, p->magic, VB_SHARED_DATA_MAGIC);
375     return NULL;
376   }
377   return (VbSharedDataHeader *)block;
378 }
379
380 int VbGetArchPropertyInt(const char* name) {
381   if (!strcasecmp(name, "fmap_base")) {
382     return ReadFdtInt("fmap-offset");
383   } else if (!strcasecmp(name, "devsw_cur")) {
384     /* Systems with virtual developer switches return at-boot value */
385     int flags = VbGetSystemPropertyInt("vdat_flags");
386     if ((flags != -1) && (flags & VBSD_HONOR_VIRT_DEV_SWITCH))
387       return VbGetSystemPropertyInt("devsw_boot");
388
389     return VbGetVarGpio("developer-switch");
390   } else if (!strcasecmp(name, "recoverysw_cur")) {
391     int value;
392     value = VbGetPlatformGpioStatus("recovery");
393     if (value != -1)
394       return value;
395
396     return VbGetVarGpio("recovery-switch");
397   } else if (!strcasecmp(name, "wpsw_cur")) {
398     int value;
399     /* Try finding the GPIO through the chromeos_arm platform device first. */
400     value = VbGetPlatformGpioStatus("write-protect");
401     if (value != -1)
402       return value;
403     return VbGetVarGpio("write-protect-switch");
404   } else if (!strcasecmp(name, "recoverysw_ec_boot")) {
405     /* TODO: read correct value using ectool */
406     return 0;
407   } else if (!strcasecmp(name, "inside_vm")) {
408     /* No ARM VMs currently. */
409     return 0;
410   } else {
411     return -1;
412   }
413 }
414
415 const char* VbGetArchPropertyString(const char* name, char* dest,
416                                     size_t size) {
417   char *str = NULL;
418   char *rv = NULL;
419   char *prop = NULL;
420
421   if (!strcasecmp(name,"arch"))
422     return StrCopy(dest, "arm", size);
423
424   /* Properties from fdt */
425   if (!strcasecmp(name, "ro_fwid"))
426     prop = "readonly-firmware-version";
427   else if (!strcasecmp(name, "hwid"))
428     prop = "hardware-id";
429   else if (!strcasecmp(name, "fwid"))
430     prop = "firmware-version";
431   else if (!strcasecmp(name, "mainfw_type"))
432     prop = "firmware-type";
433   else if (!strcasecmp(name, "ecfw_act"))
434     prop = "active-ec-firmware";
435
436   if (prop)
437     str = ReadFdtString(prop);
438
439   if (str) {
440       rv = StrCopy(dest, str, size);
441       free(str);
442       return rv;
443   }
444   return NULL;
445 }
446
447 int VbSetArchPropertyInt(const char* name, int value) {
448   /* All is handled in arch independent fashion */
449   return -1;
450 }
451
452 int VbSetArchPropertyString(const char* name, const char* value) {
453   /* All is handled in arch independent fashion */
454   return -1;
455 }
456
457 int VbArchInit(void)
458 {
459   return 0;
460 }