blob: 5c5dec0d9541cb4304f6da6b0f5b123a960d7a7d [file] [log] [blame]
Randall Spangler54218662011-02-07 11:20:20 -08001/* Copyright (c) 2011 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>
Vadim Bendebury20084232011-03-15 09:29:48 -07008#include <sys/types.h>
9#include <sys/stat.h>
10#include <unistd.h>
11#include <ctype.h>
Randall Spangler54218662011-02-07 11:20:20 -080012
13#include "host_common.h"
14
15#include "crossystem.h"
16#include "utility.h"
17#include "vboot_common.h"
Randall Spanglere73302c2011-02-18 14:53:01 -080018#include "vboot_nvstorage.h"
Randall Spanglerf4ba19d2011-03-17 16:10:21 -070019#include "vboot_struct.h"
Randall Spangler54218662011-02-07 11:20:20 -080020
21/* ACPI constants from Chrome OS Main Processor Firmware Spec */
22/* GPIO signal types */
23#define GPIO_SIGNAL_TYPE_RECOVERY 1
24#define GPIO_SIGNAL_TYPE_DEV 2
25#define GPIO_SIGNAL_TYPE_WP 3
26/* CHSW bitflags */
27#define CHSW_RECOVERY_BOOT 0x00000002
28#define CHSW_RECOVERY_EC_BOOT 0x00000004
29#define CHSW_DEV_BOOT 0x00000020
30#define CHSW_WP_BOOT 0x00000200
Randall Spanglere73302c2011-02-18 14:53:01 -080031/* CMOS reboot field bitflags */
32#define CMOSRF_RECOVERY 0x80
33#define CMOSRF_DEBUG_RESET 0x40
34#define CMOSRF_TRY_B 0x20
Randall Spanglerb47ed5a2011-02-23 13:05:40 -080035/* Boot reasons from BINF.0, from early H2C firmware */
36/* Unknown */
37#define BINF0_UNKNOWN 0
38/* Normal boot to Chrome OS */
39#define BINF0_NORMAL 1
40/* Developer mode boot (developer mode warning displayed) */
41#define BINF0_DEVELOPER 2
42/* Recovery initiated by user, using recovery button */
43#define BINF0_RECOVERY_BUTTON 3
44/* Recovery initiated by user pressing a key at developer mode warning
45 * screen */
46#define BINF0_RECOVERY_DEV_SCREEN_KEY 4
47/* Recovery caused by BIOS failed signature check (neither rewritable
48 * firmware was valid) */
49#define BINF0_RECOVERY_RW_FW_BAD 5
50/* Recovery caused by no OS kernel detected */
51#define BINF0_RECOVERY_NO_OS 6
52/* Recovery caused by OS kernel failed signature check */
53#define BINF0_RECOVERY_BAD_OS 7
54/* Recovery initiated by OS */
55#define BINF0_RECOVERY_OS_INITIATED 8
56/* OS-initiated S3 diagnostic path (debug mode boot) */
57#define BINF0_S3_DIAGNOSTIC_PATH 9
58/* S3 resume failed */
59#define BINF0_S3_RESUME_FAILED 10
60/* Recovery caused by TPM error */
61#define BINF0_RECOVERY_TPM_ERROR 11
Randall Spangler196e1772011-03-10 11:31:06 -080062/* Firmware types from BINF.3 */
63#define BINF3_RECOVERY 0
64#define BINF3_NORMAL 1
65#define BINF3_DEVELOPER 2
Randall Spangler54218662011-02-07 11:20:20 -080066
67/* Base name for ACPI files */
68#define ACPI_BASE_PATH "/sys/devices/platform/chromeos_acpi"
69/* Paths for frequently used ACPI files */
Randall Spanglerb47ed5a2011-02-23 13:05:40 -080070#define ACPI_BINF_PATH ACPI_BASE_PATH "/BINF"
Randall Spangler54218662011-02-07 11:20:20 -080071#define ACPI_CHNV_PATH ACPI_BASE_PATH "/CHNV"
72#define ACPI_CHSW_PATH ACPI_BASE_PATH "/CHSW"
Randall Spangler2b59a072011-02-24 11:17:24 -080073#define ACPI_FMAP_PATH ACPI_BASE_PATH "/FMAP"
Randall Spangler54218662011-02-07 11:20:20 -080074#define ACPI_GPIO_PATH ACPI_BASE_PATH "/GPIO"
Randall Spangler0f8ffb12011-02-25 09:50:54 -080075#define ACPI_VBNV_PATH ACPI_BASE_PATH "/VBNV"
Vadim Bendebury20084232011-03-15 09:29:48 -070076#define ACPI_VDAT_PATH ACPI_BASE_PATH "/VDAT"
Randall Spangler54218662011-02-07 11:20:20 -080077
78/* Base name for GPIO files */
79#define GPIO_BASE_PATH "/sys/class/gpio"
80#define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
81
Randall Spangler196e1772011-03-10 11:31:06 -080082/* Filename for NVRAM file */
Randall Spanglere73302c2011-02-18 14:53:01 -080083#define NVRAM_PATH "/dev/nvram"
84
Randall Spangler196e1772011-03-10 11:31:06 -080085/* Filename for kernel command line */
86#define KERNEL_CMDLINE_PATH "/proc/cmdline"
87
Vadim Bendebury20084232011-03-15 09:29:48 -070088/* A structure to contain buffer data retrieved from the ACPI. */
89typedef struct {
90 int buffer_size;
Randall Spanglerf4ba19d2011-03-17 16:10:21 -070091 uint8_t* buffer;
Vadim Bendebury20084232011-03-15 09:29:48 -070092} AcpiBuffer;
93
Randall Spanglerb47ed5a2011-02-23 13:05:40 -080094
Randall Spanglerf4ba19d2011-03-17 16:10:21 -070095/* Fields that GetVdatString() can get */
96typedef enum VdatStringField {
Randall Spangler71415712011-03-21 11:04:50 -070097 VDAT_STRING_TIMERS = 0, /* Timer values */
98 VDAT_STRING_LOAD_FIRMWARE_DEBUG, /* LoadFirmware() debug information */
99 VDAT_STRING_LOAD_KERNEL_DEBUG /* LoadKernel() debug information */
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700100} VdatStringField;
101
102
103/* Fields that GetVdatInt() can get */
104typedef enum VdatIntField {
Randall Spanglercabe6b32011-03-18 12:44:27 -0700105 VDAT_INT_FLAGS = 0, /* Flags */
106 VDAT_INT_FW_VERSION_TPM, /* Current firmware version in TPM */
107 VDAT_INT_KERNEL_VERSION_TPM, /* Current kernel version in TPM */
108 VDAT_INT_TRIED_FIRMWARE_B, /* Tried firmware B due to fwb_tries */
109 VDAT_INT_KERNEL_KEY_VERIFIED /* Kernel key verified using
110 * signature, not just hash */
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700111} VdatIntField;
112
113
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800114/* Copy up to dest_size-1 characters from src to dest, ensuring null
115 termination (which strncpy() doesn't do). Returns the destination
116 string. */
117char* StrCopy(char* dest, const char* src, int dest_size) {
118 strncpy(dest, src, dest_size);
119 dest[dest_size - 1] = '\0';
120 return dest;
121}
122
123
Randall Spangler54218662011-02-07 11:20:20 -0800124/* Read a string from a file. Passed the destination, dest size, and
125 * filename to read.
126 *
127 * Returns the destination, or NULL if error. */
128char* ReadFileString(char* dest, int size, const char* filename) {
129 char* got;
130 FILE* f;
131
132 f = fopen(filename, "rt");
133 if (!f)
134 return NULL;
135
136 got = fgets(dest, size, f);
137 fclose(f);
138 return got;
139}
140
141
142/* Read an integer from a file.
143 *
144 * Returns the parsed integer, or -1 if error. */
145int ReadFileInt(const char* filename) {
146 char buf[64];
147 int value;
148 char* e = NULL;
149
150 if (!ReadFileString(buf, sizeof(buf), filename))
151 return -1;
152
153 /* Convert to integer. Allow characters after the int ("123 blah"). */
154 value = strtol(buf, &e, 0);
155 if (e == buf)
156 return -1; /* No characters consumed, so conversion failed */
157
158 return value;
159}
160
161
162/* Check if a bit is set in a file which contains an integer.
163 *
164 * Returns 1 if the bit is set, 0 if clear, or -1 if error. */
165int ReadFileBit(const char* filename, int bitmask) {
166 int value = ReadFileInt(filename);
167 if (value == -1)
168 return -1;
169 else return (value & bitmask ? 1 : 0);
170}
171
172
Randall Spanglerc80fe652011-02-17 11:06:47 -0800173/* Return true if the FWID starts with the specified string. */
174static int FwidStartsWith(const char *start) {
175 char fwid[128];
176 if (!VbGetSystemPropertyString("fwid", fwid, sizeof(fwid)))
177 return 0;
178
179 return 0 == strncmp(fwid, start, strlen(start));
180}
181
182
Randall Spangler54218662011-02-07 11:20:20 -0800183/* Read a GPIO of the specified signal type (see ACPI GPIO SignalType).
184 *
185 * Returns 1 if the signal is asserted, 0 if not asserted, or -1 if error. */
186int ReadGpio(int signal_type) {
187 char name[128];
188 int index = 0;
189 int gpio_type;
190 int active_high;
191 int controller_offset;
192 char controller_name[128];
193 int value;
194
195 /* Scan GPIO.* to find a matching signal type */
196 for (index = 0; ; index++) {
197 snprintf(name, sizeof(name), "%s.%d/GPIO.0", ACPI_GPIO_PATH, index);
198 gpio_type = ReadFileInt(name);
199 if (gpio_type == signal_type)
200 break;
201 else if (gpio_type == -1)
202 return -1; /* Ran out of GPIOs before finding a match */
203 }
204
205 /* Read attributes and controller info for the GPIO */
206 snprintf(name, sizeof(name), "%s.%d/GPIO.1", ACPI_GPIO_PATH, index);
207 active_high = ReadFileBit(name, 0x00000001);
208 snprintf(name, sizeof(name), "%s.%d/GPIO.2", ACPI_GPIO_PATH, index);
209 controller_offset = ReadFileInt(name);
210 if (active_high == -1 || controller_offset == -1)
211 return -1; /* Missing needed info */
212
213 /* We only support the NM10 for now */
214 snprintf(name, sizeof(name), "%s.%d/GPIO.3", ACPI_GPIO_PATH, index);
215 if (!ReadFileString(controller_name, sizeof(controller_name), name))
216 return -1;
217 if (0 != strcmp(controller_name, "NM10"))
218 return -1;
219
220 /* Assume the NM10 has offset 192 */
221 /* TODO: should really check gpiochipNNN/label to see if it's the
222 * address we expect for the NM10, and then read the offset from
223 * gpiochipNNN/base. */
224 controller_offset += 192;
225
226 /* Try reading the GPIO value */
227 snprintf(name, sizeof(name), "%s/gpio%d/value",
228 GPIO_BASE_PATH, controller_offset);
229 value = ReadFileInt(name);
230
231 if (value == -1) {
232 /* Try exporting the GPIO */
233 FILE* f = fopen(GPIO_EXPORT_PATH, "wt");
234 if (!f)
235 return -1;
236 fprintf(f, "%d", controller_offset);
237 fclose(f);
238
239 /* Try re-reading the GPIO value */
240 value = ReadFileInt(name);
241 }
242
243 if (value == -1)
244 return -1;
245
246 /* Compare the GPIO value with the active value and return 1 if match. */
247 return (value == active_high ? 1 : 0);
248}
249
250
Randall Spanglere73302c2011-02-18 14:53:01 -0800251/* Read the CMOS reboot field in NVRAM.
252 *
253 * Returns 0 if the mask is clear in the field, 1 if set, or -1 if error. */
254int VbGetCmosRebootField(uint8_t mask) {
255 FILE* f;
256 int chnv, nvbyte;
257
258 /* Get the byte offset from CHNV */
259 chnv = ReadFileInt(ACPI_CHNV_PATH);
260 if (chnv == -1)
261 return -1;
262
263 f = fopen(NVRAM_PATH, "rb");
264 if (!f)
265 return -1;
266
267 if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
268 fclose(f);
269 return -1;
270 }
271
272 fclose(f);
273 return (nvbyte & mask ? 1 : 0);
274}
275
276
277/* Write the CMOS reboot field in NVRAM.
278 *
279 * Sets (value=0) or clears (value!=0) the mask in the byte.
280 *
281 * Returns 0 if success, or -1 if error. */
282int VbSetCmosRebootField(uint8_t mask, int value) {
283 FILE* f;
284 int chnv, nvbyte;
285
286 /* Get the byte offset from CHNV */
287 chnv = ReadFileInt(ACPI_CHNV_PATH);
288 if (chnv == -1)
289 return -1;
290
291 f = fopen(NVRAM_PATH, "w+b");
292 if (!f)
293 return -1;
294
295 /* Read the current value */
296 if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
297 fclose(f);
298 return -1;
299 }
300
301 /* Set/clear the mask */
302 if (value)
303 nvbyte |= mask;
304 else
305 nvbyte &= ~mask;
306
307 /* Write the byte back */
308 if (0 != fseek(f, chnv, SEEK_SET) || EOF == (fputc(nvbyte, f))) {
309 fclose(f);
310 return -1;
311 }
312
313 /* Success */
314 fclose(f);
315 return 0;
316}
317
Vadim Bendebury20084232011-03-15 09:29:48 -0700318/*
319 * Get buffer data from ACPI.
320 *
321 * Buffer data is expected to be represented by a file which is a text dump of
322 * the buffer, representing each byte by two hex numbers, space and newline
323 * separated.
324 *
325 * Input - ACPI file name to get data from.
326 *
327 * Output: a pointer to AcpiBuffer structure containing the binary
328 * representation of the data. The caller is responsible for
329 * deallocating the pointer, this will take care of both the structure
330 * and the buffer. Null in case of error.
331 */
332
333AcpiBuffer* VbGetBuffer(const char* filename)
334{
335 FILE* f = NULL;
336 char* file_buffer = NULL;
337 AcpiBuffer* acpi_buffer = NULL;
338 AcpiBuffer* return_value = NULL;
339
340 do {
341 struct stat fs;
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700342 uint8_t* output_ptr;
Vadim Bendebury20084232011-03-15 09:29:48 -0700343 int rv, i, real_size;
344
345 rv = stat(filename, &fs);
346 if (rv || !S_ISREG(fs.st_mode))
347 break;
348
349 f = fopen(filename, "r");
350 if (!f)
351 break;
352
353 file_buffer = Malloc(fs.st_size + 1);
354 if (!file_buffer)
355 break;
356
Vadim Bendebury20084232011-03-15 09:29:48 -0700357 real_size = fread(file_buffer, 1, fs.st_size, f);
358 if (!real_size)
359 break;
Vadim Bendebury20084232011-03-15 09:29:48 -0700360 file_buffer[real_size] = '\0';
361
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700362 /* Each byte in the output will replace two characters and a space
363 * in the input, so the output size does not exceed input side/3
364 * (a little less if account for newline characters). */
365 acpi_buffer = Malloc(sizeof(AcpiBuffer) + real_size/3);
366 if (!acpi_buffer)
367 break;
368 acpi_buffer->buffer = (uint8_t*)(acpi_buffer + 1);
Vadim Bendebury20084232011-03-15 09:29:48 -0700369 acpi_buffer->buffer_size = 0;
370 output_ptr = acpi_buffer->buffer;
371
372 /* process the file contents */
373 for (i = 0; i < real_size; i++) {
374 char* base, *end;
375
376 base = file_buffer + i;
377
378 if (!isxdigit(*base))
379 continue;
380
381 output_ptr[acpi_buffer->buffer_size++] = strtol(base, &end, 16) & 0xff;
382
383 if ((end - base) != 2)
384 /* Input file format error */
385 break;
386
387 i += 2; /* skip the second character and the following space */
388 }
389
390 if (i == real_size) {
391 /* all is well */
392 return_value = acpi_buffer;
393 acpi_buffer = NULL; /* prevent it from deallocating */
394 }
395 } while(0);
396
397 /* wrap up */
398 if (f)
399 fclose(f);
400
401 if (file_buffer)
402 Free(file_buffer);
403
404 if (acpi_buffer)
405 Free(acpi_buffer);
406
407 return return_value;
408}
Randall Spangler0f8ffb12011-02-25 09:50:54 -0800409
410/* Read an integer property from VbNvStorage.
411 *
412 * Returns the parameter value, or -1 if error. */
413int VbGetNvStorage(VbNvParam param) {
414 FILE* f;
415 VbNvContext vnc;
416 int offs;
417 uint32_t value;
418 int retval;
419
420 /* Get the byte offset from VBNV */
421 offs = ReadFileInt(ACPI_VBNV_PATH ".0");
422 if (offs == -1)
423 return -1;
424 if (VBNV_BLOCK_SIZE > ReadFileInt(ACPI_VBNV_PATH ".1"))
425 return -1; /* NV storage block is too small */
426
427 /* TODO: locking around NV access */
428 f = fopen(NVRAM_PATH, "rb");
429 if (!f)
430 return -1;
431
432 if (0 != fseek(f, offs, SEEK_SET) ||
433 1 != fread(vnc.raw, VBNV_BLOCK_SIZE, 1, f)) {
434 fclose(f);
435 return -1;
436 }
437
438 fclose(f);
439
440 if (0 != VbNvSetup(&vnc))
441 return -1;
442 retval = VbNvGet(&vnc, param, &value);
443 if (0 != VbNvTeardown(&vnc))
444 return -1;
445 if (0 != retval)
446 return -1;
447
448 /* TODO: If vnc.raw_changed, attempt to reopen NVRAM for write and
449 * save the new defaults. If we're able to, log. */
450 /* TODO: release lock */
451
452 return (int)value;
453}
454
455
456/* Write an integer property to VbNvStorage.
457 *
458 * Returns 0 if success, -1 if error. */
459int VbSetNvStorage(VbNvParam param, int value) {
460 FILE* f;
461 VbNvContext vnc;
462 int offs;
463 int retval = -1;
464 int i;
465
466 /* Get the byte offset from VBNV */
467 offs = ReadFileInt(ACPI_VBNV_PATH ".0");
468 if (offs == -1)
469 return -1;
470 if (VBNV_BLOCK_SIZE > ReadFileInt(ACPI_VBNV_PATH ".1"))
471 return -1; /* NV storage block is too small */
472
473 /* TODO: locking around NV access */
474 f = fopen(NVRAM_PATH, "w+b");
475 if (!f)
476 return -1;
477
478 if (0 != fseek(f, offs, SEEK_SET) ||
479 1 != fread(vnc.raw, VBNV_BLOCK_SIZE, 1, f)) {
480 goto VbSetNvCleanup;
481 }
482
483 if (0 != VbNvSetup(&vnc))
484 goto VbSetNvCleanup;
485 i = VbNvSet(&vnc, param, (uint32_t)value);
486 if (0 != VbNvTeardown(&vnc))
487 goto VbSetNvCleanup;
488 if (0 != i)
489 goto VbSetNvCleanup;
490
491 if (vnc.raw_changed) {
492 if (0 != fseek(f, offs, SEEK_SET) ||
493 1 != fwrite(vnc.raw, VBNV_BLOCK_SIZE, 1, f))
494 goto VbSetNvCleanup;
495 }
496
497 /* Success */
498 retval = 0;
499
500VbSetNvCleanup:
501 fclose(f);
502 /* TODO: release lock */
503 return retval;
504}
505
506
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800507/* Read the recovery reason. Returns the reason code or -1 if error. */
508int VbGetRecoveryReason(void) {
509 int value;
510
511 /* Try reading type from BINF.4 */
512 value = ReadFileInt(ACPI_BINF_PATH ".4");
513 if (-1 != value)
514 return value;
515
516 /* Fall back to BINF.0 for legacy systems like Mario. */
517 switch(ReadFileInt(ACPI_BINF_PATH ".0")) {
518 case BINF0_NORMAL:
519 case BINF0_DEVELOPER:
520 return VBNV_RECOVERY_NOT_REQUESTED;
521 case BINF0_RECOVERY_BUTTON:
522 return VBNV_RECOVERY_RO_MANUAL;
523 case BINF0_RECOVERY_DEV_SCREEN_KEY:
524 return VBNV_RECOVERY_RW_DEV_SCREEN;
525 case BINF0_RECOVERY_RW_FW_BAD:
526 case BINF0_RECOVERY_NO_OS:
527 return VBNV_RECOVERY_RW_NO_OS;
528 case BINF0_RECOVERY_BAD_OS:
529 return VBNV_RECOVERY_RW_INVALID_OS;
530 case BINF0_RECOVERY_OS_INITIATED:
531 return VBNV_RECOVERY_LEGACY;
532 default:
533 /* Other values don't map cleanly to firmware type. */
534 return -1;
535 }
536}
537
538
539/* Read the active main firmware type into the destination buffer.
540 * Passed the destination and its size. Returns the destination, or
541 * NULL if error. */
542const char* VbReadMainFwType(char* dest, int size) {
543
544 /* Try reading type from BINF.3 */
545 switch(ReadFileInt(ACPI_BINF_PATH ".3")) {
Randall Spangler196e1772011-03-10 11:31:06 -0800546 case BINF3_RECOVERY:
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800547 return StrCopy(dest, "recovery", size);
Randall Spangler196e1772011-03-10 11:31:06 -0800548 case BINF3_NORMAL:
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800549 return StrCopy(dest, "normal", size);
Randall Spangler196e1772011-03-10 11:31:06 -0800550 case BINF3_DEVELOPER:
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800551 return StrCopy(dest, "developer", size);
552 default:
553 break; /* Fall through to legacy handling */
554 }
555
556 /* Fall back to BINF.0 for legacy systems like Mario. */
557 switch(ReadFileInt(ACPI_BINF_PATH ".0")) {
558 case -1:
559 /* Both BINF.0 and BINF.3 are missing, so this isn't Chrome OS
560 * firmware. */
561 return StrCopy(dest, "nonchrome", size);
562 case BINF0_NORMAL:
563 return StrCopy(dest, "normal", size);
564 case BINF0_DEVELOPER:
565 return StrCopy(dest, "developer", size);
566 case BINF0_RECOVERY_BUTTON:
567 case BINF0_RECOVERY_DEV_SCREEN_KEY:
568 case BINF0_RECOVERY_RW_FW_BAD:
569 case BINF0_RECOVERY_NO_OS:
570 case BINF0_RECOVERY_BAD_OS:
571 case BINF0_RECOVERY_OS_INITIATED:
572 case BINF0_RECOVERY_TPM_ERROR:
573 /* Assorted flavors of recovery boot reason. */
574 return StrCopy(dest, "recovery", size);
575 default:
576 /* Other values don't map cleanly to firmware type. */
577 return NULL;
578 }
579}
580
581
Randall Spangler196e1772011-03-10 11:31:06 -0800582/* Determine whether OS-level debugging should be allowed. Passed the
583 * destination and its size. Returns 1 if yes, 0 if no, -1 if error. */
584int VbGetCrosDebug(void) {
585 FILE* f = NULL;
586 char buf[4096] = "";
587 int binf3;
588 char *t, *saveptr;
589
590 /* Try reading firmware type from BINF.3. */
591 binf3 = ReadFileInt(ACPI_BINF_PATH ".3");
592 if (BINF3_RECOVERY == binf3)
593 return 0; /* Recovery mode never allows debug. */
594 else if (BINF3_DEVELOPER == binf3)
595 return 1; /* Developer firmware always allows debug. */
596
597 /* Normal new firmware, older ChromeOS firmware, or non-Chrome firmware.
Randall Spangler227f7922011-03-11 13:34:56 -0800598 * For all these cases, check /proc/cmdline for cros_[no]debug. */
Randall Spangler196e1772011-03-10 11:31:06 -0800599 f = fopen(KERNEL_CMDLINE_PATH, "rt");
600 if (f) {
601 if (NULL == fgets(buf, sizeof(buf), f))
602 *buf = 0;
603 fclose(f);
604 }
605 for (t = strtok_r(buf, " ", &saveptr); t; t=strtok_r(NULL, " ", &saveptr)) {
606 if (0 == strcmp(t, "cros_debug"))
607 return 1;
Randall Spangler227f7922011-03-11 13:34:56 -0800608 else if (0 == strcmp(t, "cros_nodebug"))
609 return 0;
Randall Spangler196e1772011-03-10 11:31:06 -0800610 }
611
612 /* Normal new firmware or older Chrome OS firmware allows debug if the
613 * dev switch is on. */
614 if (1 == ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT))
615 return 1;
616
617 /* All other cases disallow debug. */
618 return 0;
619}
620
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800621
Randall Spangler71415712011-03-21 11:04:50 -0700622char* GetVdatLoadFirmwareDebug(char* dest, int size,
623 const VbSharedDataHeader* sh) {
624 snprintf(dest, size,
625 "Check A result=%d\n"
626 "Check B result=%d\n"
627 "Firmware index booted=0x%02x\n"
628 "TPM combined version at start=0x%08x\n"
629 "Lowest combined version from firmware=0x%08x\n",
630 sh->check_fw_a_result,
631 sh->check_fw_b_result,
632 sh->firmware_index,
633 sh->fw_version_tpm_start,
634 sh->fw_version_lowest);
635 return dest;
636}
637
638
639#define TRUNCATED "\n(truncated)\n"
640
641char* GetVdatLoadKernelDebug(char* dest, int size,
642 const VbSharedDataHeader* sh) {
643 int used = 0;
644 int first_call_tracked = 0;
645 int call;
646
647 /* Make sure we have space for truncation warning */
648 if (size < strlen(TRUNCATED) + 1)
649 return NULL;
650 size -= strlen(TRUNCATED) + 1;
651
652 used += snprintf(
653 dest + used, size - used,
654 "Calls to LoadKernel()=%d\n",
655 sh->lk_call_count);
656 if (used > size)
657 goto LoadKernelDebugExit;
658
659 /* Report on the last calls */
660 if (sh->lk_call_count > VBSD_MAX_KERNEL_CALLS)
661 first_call_tracked = sh->lk_call_count - VBSD_MAX_KERNEL_CALLS;
662 for (call = first_call_tracked; call < sh->lk_call_count; call++) {
663 const VbSharedDataKernelCall* shc =
664 sh->lk_calls + (call & (VBSD_MAX_KERNEL_CALLS - 1));
665 int first_part_tracked = 0;
666 int part;
667
668 used += snprintf(
669 dest + used, size - used,
670 "Call %d:\n"
671 " Boot flags=0x%02x\n"
672 " Boot mode=%d\n"
673 " Test error=%d\n"
674 " Return code=%d\n"
675 " Debug flags=0x%02x\n"
676 " Drive sectors=%" PRIu64 "\n"
677 " Sector size=%d\n"
678 " Check result=%d\n"
679 " Kernel partitions found=%d\n",
680 call + 1,
681 shc->boot_flags,
682 shc->boot_mode,
683 shc->test_error_num,
684 shc->return_code,
685 shc->flags,
686 shc->sector_count,
687 shc->sector_size,
688 shc->check_result,
689 shc->kernel_parts_found);
690 if (used > size)
691 goto LoadKernelDebugExit;
692
693 /* If we found too many partitions, only prints ones where the
694 * structure has info. */
695 if (shc->kernel_parts_found > VBSD_MAX_KERNEL_PARTS)
696 first_part_tracked = shc->kernel_parts_found - VBSD_MAX_KERNEL_PARTS;
697
698 /* Report on the partitions checked */
699 for (part = first_part_tracked; part < shc->kernel_parts_found; part++) {
700 const VbSharedDataKernelPart* shp =
701 shc->parts + (part & (VBSD_MAX_KERNEL_PARTS - 1));
702
703 used += snprintf(
704 dest + used, size - used,
705 " Kernel %d:\n"
706 " GPT index=%d\n"
707 " Start sector=%" PRIu64 "\n"
708 " Sector count=%" PRIu64 "\n"
709 " Combined version=0x%08x\n"
710 " Check result=%d\n"
711 " Debug flags=0x%02x\n",
712 part + 1,
713 shp->gpt_index,
714 shp->sector_start,
715 shp->sector_count,
716 shp->combined_version,
717 shp->check_result,
718 shp->flags);
719 if (used > size)
720 goto LoadKernelDebugExit;
721 }
722 }
723
724LoadKernelDebugExit:
725
726 /* Warn if data was truncated; we left space for this above. */
727 if (used > size)
728 strcat(dest, TRUNCATED);
729
730 return dest;
731}
732
733
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700734char* GetVdatString(char* dest, int size, VdatStringField field)
735{
736 VbSharedDataHeader* sh;
737 AcpiBuffer* ab = VbGetBuffer(ACPI_VDAT_PATH);
Randall Spangler71415712011-03-21 11:04:50 -0700738 char* value = dest;
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700739 if (!ab)
740 return NULL;
741
742 sh = (VbSharedDataHeader*)ab->buffer;
743
744 switch (field) {
745 case VDAT_STRING_TIMERS:
746 snprintf(dest, size,
747 "LFS=%" PRIu64 ",%" PRIu64
748 " LF=%" PRIu64 ",%" PRIu64
749 " LK=%" PRIu64 ",%" PRIu64,
750 sh->timer_load_firmware_start_enter,
751 sh->timer_load_firmware_start_exit,
752 sh->timer_load_firmware_enter,
753 sh->timer_load_firmware_exit,
754 sh->timer_load_kernel_enter,
755 sh->timer_load_kernel_exit);
756 break;
757
758 case VDAT_STRING_LOAD_FIRMWARE_DEBUG:
Randall Spangler71415712011-03-21 11:04:50 -0700759 value = GetVdatLoadFirmwareDebug(dest, size, sh);
760 break;
761
762 case VDAT_STRING_LOAD_KERNEL_DEBUG:
763 value = GetVdatLoadKernelDebug(dest, size, sh);
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700764 break;
765
766 default:
767 Free(ab);
768 return NULL;
769 }
770
771 Free(ab);
Randall Spangler71415712011-03-21 11:04:50 -0700772 return value;
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700773}
774
775
776int GetVdatInt(VdatIntField field) {
777 VbSharedDataHeader* sh;
778 AcpiBuffer* ab = VbGetBuffer(ACPI_VDAT_PATH);
779 int value = -1;
780
781 if (!ab)
782 return -1;
783
784 sh = (VbSharedDataHeader*)ab->buffer;
785
786 switch (field) {
787 case VDAT_INT_FLAGS:
788 value = (int)sh->flags;
789 break;
Randall Spangler5ac39bf2011-03-17 17:58:56 -0700790 case VDAT_INT_FW_VERSION_TPM:
791 value = (int)sh->fw_version_tpm;
792 break;
793 case VDAT_INT_KERNEL_VERSION_TPM:
794 value = (int)sh->kernel_version_tpm;
795 break;
Randall Spanglercabe6b32011-03-18 12:44:27 -0700796 case VDAT_INT_TRIED_FIRMWARE_B:
797 value = (sh->flags & VBSD_FWB_TRIED ? 1 : 0);
798 break;
799 case VDAT_INT_KERNEL_KEY_VERIFIED:
800 value = (sh->flags & VBSD_KERNEL_KEY_VERIFIED ? 1 : 0);
801 break;
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700802 }
803
804 Free(ab);
805 return value;
806}
807
808
Randall Spangler54218662011-02-07 11:20:20 -0800809/* Read a system property integer.
810 *
811 * Returns the property value, or -1 if error. */
812int VbGetSystemPropertyInt(const char* name) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800813 int value = -1;
Randall Spangler54218662011-02-07 11:20:20 -0800814
Randall Spanglere73302c2011-02-18 14:53:01 -0800815 /* Switch positions */
Randall Spangler54218662011-02-07 11:20:20 -0800816 if (!strcasecmp(name,"devsw_cur")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800817 value = ReadGpio(GPIO_SIGNAL_TYPE_DEV);
Randall Spangler54218662011-02-07 11:20:20 -0800818 } else if (!strcasecmp(name,"devsw_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800819 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT);
Randall Spangler54218662011-02-07 11:20:20 -0800820 } else if (!strcasecmp(name,"recoverysw_cur")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800821 value = ReadGpio(GPIO_SIGNAL_TYPE_RECOVERY);
Randall Spangler54218662011-02-07 11:20:20 -0800822 } else if (!strcasecmp(name,"recoverysw_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800823 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_BOOT);
Randall Spangler54218662011-02-07 11:20:20 -0800824 } else if (!strcasecmp(name,"recoverysw_ec_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800825 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_EC_BOOT);
Randall Spangler54218662011-02-07 11:20:20 -0800826 } else if (!strcasecmp(name,"wpsw_cur")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800827 value = ReadGpio(GPIO_SIGNAL_TYPE_WP);
828 if (-1 != value && FwidStartsWith("Mario."))
829 value = 1 - value; /* Mario reports this backwards */
Randall Spangler54218662011-02-07 11:20:20 -0800830 } else if (!strcasecmp(name,"wpsw_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800831 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_WP_BOOT);
832 if (-1 != value && FwidStartsWith("Mario."))
833 value = 1 - value; /* Mario reports this backwards */
834 }
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800835 /* Saved memory is at a fixed location for all H2C BIOS. If the CHSW
836 * path exists in sysfs, it's a H2C BIOS. */
837 else if (!strcasecmp(name,"savedmem_base")) {
838 return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00F00000);
839 } else if (!strcasecmp(name,"savedmem_size")) {
840 return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00100000);
841 }
Randall Spangler17260282011-02-25 12:06:26 -0800842 /* NV storage values with no defaults for older BIOS. */
Randall Spanglercabe6b32011-03-18 12:44:27 -0700843 else if (!strcasecmp(name,"kern_nv")) {
Randall Spangler618d17d2011-03-01 10:33:11 -0800844 value = VbGetNvStorage(VBNV_KERNEL_FIELD);
Randall Spanglerb4167142011-03-01 13:04:22 -0800845 } else if (!strcasecmp(name,"nvram_cleared")) {
846 value = VbGetNvStorage(VBNV_KERNEL_SETTINGS_RESET);
Randall Spanglerb17e8d32011-03-15 09:50:38 -0700847 } else if (!strcasecmp(name,"vbtest_errfunc")) {
848 value = VbGetNvStorage(VBNV_TEST_ERROR_FUNC);
849 } else if (!strcasecmp(name,"vbtest_errno")) {
850 value = VbGetNvStorage(VBNV_TEST_ERROR_NUM);
Randall Spangler17260282011-02-25 12:06:26 -0800851 }
Randall Spangler0f8ffb12011-02-25 09:50:54 -0800852 /* NV storage values. If unable to get from NV storage, fall back to the
853 * CMOS reboot field used by older BIOS. */
Randall Spanglere73302c2011-02-18 14:53:01 -0800854 else if (!strcasecmp(name,"recovery_request")) {
Randall Spangler0f8ffb12011-02-25 09:50:54 -0800855 value = VbGetNvStorage(VBNV_RECOVERY_REQUEST);
856 if (-1 == value)
857 value = VbGetCmosRebootField(CMOSRF_RECOVERY);
Randall Spanglere73302c2011-02-18 14:53:01 -0800858 } else if (!strcasecmp(name,"dbg_reset")) {
Randall Spangler0f8ffb12011-02-25 09:50:54 -0800859 value = VbGetNvStorage(VBNV_DEBUG_RESET_MODE);
860 if (-1 == value)
861 value = VbGetCmosRebootField(CMOSRF_DEBUG_RESET);
Randall Spanglere73302c2011-02-18 14:53:01 -0800862 } else if (!strcasecmp(name,"fwb_tries")) {
Randall Spangler0f8ffb12011-02-25 09:50:54 -0800863 value = VbGetNvStorage(VBNV_TRY_B_COUNT);
864 if (-1 == value)
865 value = VbGetCmosRebootField(CMOSRF_TRY_B);
Randall Spanglere73302c2011-02-18 14:53:01 -0800866 }
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800867 /* Other parameters */
868 else if (!strcasecmp(name,"recovery_reason")) {
869 return VbGetRecoveryReason();
Randall Spangler2b59a072011-02-24 11:17:24 -0800870 } else if (!strcasecmp(name,"fmap_base")) {
871 value = ReadFileInt(ACPI_FMAP_PATH);
Randall Spangler196e1772011-03-10 11:31:06 -0800872 } else if (!strcasecmp(name,"cros_debug")) {
873 value = VbGetCrosDebug();
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700874 } else if (!strcasecmp(name,"vdat_flags")) {
875 value = GetVdatInt(VDAT_INT_FLAGS);
Randall Spangler5ac39bf2011-03-17 17:58:56 -0700876 } else if (!strcasecmp(name,"tpm_fwver")) {
877 value = GetVdatInt(VDAT_INT_FW_VERSION_TPM);
878 } else if (!strcasecmp(name,"tpm_kernver")) {
879 value = GetVdatInt(VDAT_INT_KERNEL_VERSION_TPM);
Randall Spanglercabe6b32011-03-18 12:44:27 -0700880 } else if (!strcasecmp(name,"tried_fwb")) {
881 value = GetVdatInt(VDAT_INT_TRIED_FIRMWARE_B);
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800882 }
Randall Spangler54218662011-02-07 11:20:20 -0800883
Randall Spanglerc80fe652011-02-17 11:06:47 -0800884 return value;
Randall Spangler54218662011-02-07 11:20:20 -0800885}
886
Randall Spangler54218662011-02-07 11:20:20 -0800887/* Read a system property string into a destination buffer of the specified
888 * size.
889 *
890 * Returns the passed buffer, or NULL if error. */
891const char* VbGetSystemPropertyString(const char* name, char* dest, int size) {
892
893 if (!strcasecmp(name,"hwid")) {
894 return ReadFileString(dest, size, ACPI_BASE_PATH "/HWID");
895 } else if (!strcasecmp(name,"fwid")) {
896 return ReadFileString(dest, size, ACPI_BASE_PATH "/FWID");
897 } else if (!strcasecmp(name,"ro_fwid")) {
898 return ReadFileString(dest, size, ACPI_BASE_PATH "/FRID");
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800899 } else if (!strcasecmp(name,"mainfw_act")) {
900 switch(ReadFileInt(ACPI_BINF_PATH ".1")) {
901 case 0:
902 return StrCopy(dest, "recovery", size);
903 case 1:
904 return StrCopy(dest, "A", size);
905 case 2:
906 return StrCopy(dest, "B", size);
907 default:
908 return NULL;
909 }
910 } else if (!strcasecmp(name,"mainfw_type")) {
911 return VbReadMainFwType(dest, size);
912 } else if (!strcasecmp(name,"ecfw_act")) {
913 switch(ReadFileInt(ACPI_BINF_PATH ".2")) {
914 case 0:
915 return StrCopy(dest, "RO", size);
916 case 1:
917 return StrCopy(dest, "RW", size);
918 default:
919 return NULL;
920 }
Randall Spangler17260282011-02-25 12:06:26 -0800921 } else if (!strcasecmp(name,"kernkey_vfy")) {
Randall Spanglercabe6b32011-03-18 12:44:27 -0700922 switch(GetVdatInt(VDAT_INT_KERNEL_KEY_VERIFIED)) {
Randall Spangler17260282011-02-25 12:06:26 -0800923 case 0:
924 return "hash";
925 case 1:
926 return "sig";
927 default:
928 return NULL;
929 }
Randall Spanglerf4ba19d2011-03-17 16:10:21 -0700930 } else if (!strcasecmp(name, "vdat_timers")) {
931 return GetVdatString(dest, size, VDAT_STRING_TIMERS);
932 } else if (!strcasecmp(name, "vdat_lfdebug")) {
933 return GetVdatString(dest, size, VDAT_STRING_LOAD_FIRMWARE_DEBUG);
Randall Spangler71415712011-03-21 11:04:50 -0700934 } else if (!strcasecmp(name, "vdat_lkdebug")) {
935 return GetVdatString(dest, size, VDAT_STRING_LOAD_KERNEL_DEBUG);
Randall Spangler54218662011-02-07 11:20:20 -0800936 } else
937 return NULL;
Randall Spangler54218662011-02-07 11:20:20 -0800938}
939
940
941/* Set a system property integer.
942 *
943 * Returns 0 if success, -1 if error. */
944int VbSetSystemPropertyInt(const char* name, int value) {
945
Randall Spangler618d17d2011-03-01 10:33:11 -0800946 /* NV storage values with no defaults for older BIOS. */
Randall Spanglerb4167142011-03-01 13:04:22 -0800947 if (!strcasecmp(name,"nvram_cleared")) {
948 /* Can only clear this flag; it's set inside the NV storage library. */
949 return VbSetNvStorage(VBNV_KERNEL_SETTINGS_RESET, 0);
950 } else if (!strcasecmp(name,"kern_nv")) {
Randall Spangler618d17d2011-03-01 10:33:11 -0800951 return VbSetNvStorage(VBNV_KERNEL_FIELD, value);
Randall Spanglerb17e8d32011-03-15 09:50:38 -0700952 } else if (!strcasecmp(name,"vbtest_errfunc")) {
953 return VbSetNvStorage(VBNV_TEST_ERROR_FUNC, value);
954 } else if (!strcasecmp(name,"vbtest_errno")) {
955 return VbSetNvStorage(VBNV_TEST_ERROR_NUM, value);
Randall Spangler618d17d2011-03-01 10:33:11 -0800956 }
Randall Spangler0f8ffb12011-02-25 09:50:54 -0800957 /* NV storage values. If unable to get from NV storage, fall back to the
958 * CMOS reboot field used by older BIOS. */
Randall Spangler618d17d2011-03-01 10:33:11 -0800959 else if (!strcasecmp(name,"recovery_request")) {
Randall Spangler0f8ffb12011-02-25 09:50:54 -0800960 if (0 == VbSetNvStorage(VBNV_RECOVERY_REQUEST, value))
961 return 0;
Randall Spanglere73302c2011-02-18 14:53:01 -0800962 return VbSetCmosRebootField(CMOSRF_RECOVERY, value);
963 } else if (!strcasecmp(name,"dbg_reset")) {
Randall Spangler0f8ffb12011-02-25 09:50:54 -0800964 if (0 == VbSetNvStorage(VBNV_DEBUG_RESET_MODE, value))
965 return 0;
966 return VbSetCmosRebootField(CMOSRF_DEBUG_RESET, value);
Randall Spanglere73302c2011-02-18 14:53:01 -0800967 } else if (!strcasecmp(name,"fwb_tries")) {
Randall Spangler0f8ffb12011-02-25 09:50:54 -0800968 if (0 == VbSetNvStorage(VBNV_TRY_B_COUNT, value))
969 return 0;
Randall Spanglere73302c2011-02-18 14:53:01 -0800970 return VbSetCmosRebootField(CMOSRF_TRY_B, value);
971 }
972
Randall Spangler54218662011-02-07 11:20:20 -0800973 return -1;
974}
975
976
977/* Set a system property string.
978 *
979 * Returns 0 if success, -1 if error. */
980int VbSetSystemPropertyString(const char* name, const char* value) {
981
982 /* TODO: support setting */
983 return -1;
984}