blob: 692eb612b100df17a10b747a1453d029abd5976a [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>
8
9#include "host_common.h"
10
11#include "crossystem.h"
12#include "utility.h"
13#include "vboot_common.h"
Randall Spanglere73302c2011-02-18 14:53:01 -080014#include "vboot_nvstorage.h"
Randall Spangler54218662011-02-07 11:20:20 -080015
16/* ACPI constants from Chrome OS Main Processor Firmware Spec */
17/* GPIO signal types */
18#define GPIO_SIGNAL_TYPE_RECOVERY 1
19#define GPIO_SIGNAL_TYPE_DEV 2
20#define GPIO_SIGNAL_TYPE_WP 3
21/* CHSW bitflags */
22#define CHSW_RECOVERY_BOOT 0x00000002
23#define CHSW_RECOVERY_EC_BOOT 0x00000004
24#define CHSW_DEV_BOOT 0x00000020
25#define CHSW_WP_BOOT 0x00000200
Randall Spanglere73302c2011-02-18 14:53:01 -080026/* CMOS reboot field bitflags */
27#define CMOSRF_RECOVERY 0x80
28#define CMOSRF_DEBUG_RESET 0x40
29#define CMOSRF_TRY_B 0x20
Randall Spanglerb47ed5a2011-02-23 13:05:40 -080030/* 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
Randall Spangler54218662011-02-07 11:20:20 -080057
58/* Base name for ACPI files */
59#define ACPI_BASE_PATH "/sys/devices/platform/chromeos_acpi"
60/* Paths for frequently used ACPI files */
Randall Spanglerb47ed5a2011-02-23 13:05:40 -080061#define ACPI_BINF_PATH ACPI_BASE_PATH "/BINF"
Randall Spangler54218662011-02-07 11:20:20 -080062#define ACPI_CHNV_PATH ACPI_BASE_PATH "/CHNV"
63#define ACPI_CHSW_PATH ACPI_BASE_PATH "/CHSW"
64#define ACPI_GPIO_PATH ACPI_BASE_PATH "/GPIO"
65
66/* Base name for GPIO files */
67#define GPIO_BASE_PATH "/sys/class/gpio"
68#define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
69
Randall Spanglere73302c2011-02-18 14:53:01 -080070/* Base name for NVRAM file */
71#define NVRAM_PATH "/dev/nvram"
72
Randall Spanglerb47ed5a2011-02-23 13:05:40 -080073
74/* Copy up to dest_size-1 characters from src to dest, ensuring null
75 termination (which strncpy() doesn't do). Returns the destination
76 string. */
77char* StrCopy(char* dest, const char* src, int dest_size) {
78 strncpy(dest, src, dest_size);
79 dest[dest_size - 1] = '\0';
80 return dest;
81}
82
83
Randall Spangler54218662011-02-07 11:20:20 -080084/* Read a string from a file. Passed the destination, dest size, and
85 * filename to read.
86 *
87 * Returns the destination, or NULL if error. */
88char* ReadFileString(char* dest, int size, const char* filename) {
89 char* got;
90 FILE* f;
91
92 f = fopen(filename, "rt");
93 if (!f)
94 return NULL;
95
96 got = fgets(dest, size, f);
97 fclose(f);
98 return got;
99}
100
101
102/* Read an integer from a file.
103 *
104 * Returns the parsed integer, or -1 if error. */
105int ReadFileInt(const char* filename) {
106 char buf[64];
107 int value;
108 char* e = NULL;
109
110 if (!ReadFileString(buf, sizeof(buf), filename))
111 return -1;
112
113 /* Convert to integer. Allow characters after the int ("123 blah"). */
114 value = strtol(buf, &e, 0);
115 if (e == buf)
116 return -1; /* No characters consumed, so conversion failed */
117
118 return value;
119}
120
121
122/* Check if a bit is set in a file which contains an integer.
123 *
124 * Returns 1 if the bit is set, 0 if clear, or -1 if error. */
125int ReadFileBit(const char* filename, int bitmask) {
126 int value = ReadFileInt(filename);
127 if (value == -1)
128 return -1;
129 else return (value & bitmask ? 1 : 0);
130}
131
132
Randall Spanglerc80fe652011-02-17 11:06:47 -0800133/* Return true if the FWID starts with the specified string. */
134static int FwidStartsWith(const char *start) {
135 char fwid[128];
136 if (!VbGetSystemPropertyString("fwid", fwid, sizeof(fwid)))
137 return 0;
138
139 return 0 == strncmp(fwid, start, strlen(start));
140}
141
142
Randall Spangler54218662011-02-07 11:20:20 -0800143/* Read a GPIO of the specified signal type (see ACPI GPIO SignalType).
144 *
145 * Returns 1 if the signal is asserted, 0 if not asserted, or -1 if error. */
146int ReadGpio(int signal_type) {
147 char name[128];
148 int index = 0;
149 int gpio_type;
150 int active_high;
151 int controller_offset;
152 char controller_name[128];
153 int value;
154
155 /* Scan GPIO.* to find a matching signal type */
156 for (index = 0; ; index++) {
157 snprintf(name, sizeof(name), "%s.%d/GPIO.0", ACPI_GPIO_PATH, index);
158 gpio_type = ReadFileInt(name);
159 if (gpio_type == signal_type)
160 break;
161 else if (gpio_type == -1)
162 return -1; /* Ran out of GPIOs before finding a match */
163 }
164
165 /* Read attributes and controller info for the GPIO */
166 snprintf(name, sizeof(name), "%s.%d/GPIO.1", ACPI_GPIO_PATH, index);
167 active_high = ReadFileBit(name, 0x00000001);
168 snprintf(name, sizeof(name), "%s.%d/GPIO.2", ACPI_GPIO_PATH, index);
169 controller_offset = ReadFileInt(name);
170 if (active_high == -1 || controller_offset == -1)
171 return -1; /* Missing needed info */
172
173 /* We only support the NM10 for now */
174 snprintf(name, sizeof(name), "%s.%d/GPIO.3", ACPI_GPIO_PATH, index);
175 if (!ReadFileString(controller_name, sizeof(controller_name), name))
176 return -1;
177 if (0 != strcmp(controller_name, "NM10"))
178 return -1;
179
180 /* Assume the NM10 has offset 192 */
181 /* TODO: should really check gpiochipNNN/label to see if it's the
182 * address we expect for the NM10, and then read the offset from
183 * gpiochipNNN/base. */
184 controller_offset += 192;
185
186 /* Try reading the GPIO value */
187 snprintf(name, sizeof(name), "%s/gpio%d/value",
188 GPIO_BASE_PATH, controller_offset);
189 value = ReadFileInt(name);
190
191 if (value == -1) {
192 /* Try exporting the GPIO */
193 FILE* f = fopen(GPIO_EXPORT_PATH, "wt");
194 if (!f)
195 return -1;
196 fprintf(f, "%d", controller_offset);
197 fclose(f);
198
199 /* Try re-reading the GPIO value */
200 value = ReadFileInt(name);
201 }
202
203 if (value == -1)
204 return -1;
205
206 /* Compare the GPIO value with the active value and return 1 if match. */
207 return (value == active_high ? 1 : 0);
208}
209
210
Randall Spanglere73302c2011-02-18 14:53:01 -0800211/* Read the CMOS reboot field in NVRAM.
212 *
213 * Returns 0 if the mask is clear in the field, 1 if set, or -1 if error. */
214int VbGetCmosRebootField(uint8_t mask) {
215 FILE* f;
216 int chnv, nvbyte;
217
218 /* Get the byte offset from CHNV */
219 chnv = ReadFileInt(ACPI_CHNV_PATH);
220 if (chnv == -1)
221 return -1;
222
223 f = fopen(NVRAM_PATH, "rb");
224 if (!f)
225 return -1;
226
227 if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
228 fclose(f);
229 return -1;
230 }
231
232 fclose(f);
233 return (nvbyte & mask ? 1 : 0);
234}
235
236
237/* Write the CMOS reboot field in NVRAM.
238 *
239 * Sets (value=0) or clears (value!=0) the mask in the byte.
240 *
241 * Returns 0 if success, or -1 if error. */
242int VbSetCmosRebootField(uint8_t mask, int value) {
243 FILE* f;
244 int chnv, nvbyte;
245
246 /* Get the byte offset from CHNV */
247 chnv = ReadFileInt(ACPI_CHNV_PATH);
248 if (chnv == -1)
249 return -1;
250
251 f = fopen(NVRAM_PATH, "w+b");
252 if (!f)
253 return -1;
254
255 /* Read the current value */
256 if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
257 fclose(f);
258 return -1;
259 }
260
261 /* Set/clear the mask */
262 if (value)
263 nvbyte |= mask;
264 else
265 nvbyte &= ~mask;
266
267 /* Write the byte back */
268 if (0 != fseek(f, chnv, SEEK_SET) || EOF == (fputc(nvbyte, f))) {
269 fclose(f);
270 return -1;
271 }
272
273 /* Success */
274 fclose(f);
275 return 0;
276}
277
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800278/* Read the recovery reason. Returns the reason code or -1 if error. */
279int VbGetRecoveryReason(void) {
280 int value;
281
282 /* Try reading type from BINF.4 */
283 value = ReadFileInt(ACPI_BINF_PATH ".4");
284 if (-1 != value)
285 return value;
286
287 /* Fall back to BINF.0 for legacy systems like Mario. */
288 switch(ReadFileInt(ACPI_BINF_PATH ".0")) {
289 case BINF0_NORMAL:
290 case BINF0_DEVELOPER:
291 return VBNV_RECOVERY_NOT_REQUESTED;
292 case BINF0_RECOVERY_BUTTON:
293 return VBNV_RECOVERY_RO_MANUAL;
294 case BINF0_RECOVERY_DEV_SCREEN_KEY:
295 return VBNV_RECOVERY_RW_DEV_SCREEN;
296 case BINF0_RECOVERY_RW_FW_BAD:
297 case BINF0_RECOVERY_NO_OS:
298 return VBNV_RECOVERY_RW_NO_OS;
299 case BINF0_RECOVERY_BAD_OS:
300 return VBNV_RECOVERY_RW_INVALID_OS;
301 case BINF0_RECOVERY_OS_INITIATED:
302 return VBNV_RECOVERY_LEGACY;
303 default:
304 /* Other values don't map cleanly to firmware type. */
305 return -1;
306 }
307}
308
309
310/* Read the active main firmware type into the destination buffer.
311 * Passed the destination and its size. Returns the destination, or
312 * NULL if error. */
313const char* VbReadMainFwType(char* dest, int size) {
314
315 /* Try reading type from BINF.3 */
316 switch(ReadFileInt(ACPI_BINF_PATH ".3")) {
317 case 0:
318 return StrCopy(dest, "recovery", size);
319 case 1:
320 return StrCopy(dest, "normal", size);
321 case 2:
322 return StrCopy(dest, "developer", size);
323 default:
324 break; /* Fall through to legacy handling */
325 }
326
327 /* Fall back to BINF.0 for legacy systems like Mario. */
328 switch(ReadFileInt(ACPI_BINF_PATH ".0")) {
329 case -1:
330 /* Both BINF.0 and BINF.3 are missing, so this isn't Chrome OS
331 * firmware. */
332 return StrCopy(dest, "nonchrome", size);
333 case BINF0_NORMAL:
334 return StrCopy(dest, "normal", size);
335 case BINF0_DEVELOPER:
336 return StrCopy(dest, "developer", size);
337 case BINF0_RECOVERY_BUTTON:
338 case BINF0_RECOVERY_DEV_SCREEN_KEY:
339 case BINF0_RECOVERY_RW_FW_BAD:
340 case BINF0_RECOVERY_NO_OS:
341 case BINF0_RECOVERY_BAD_OS:
342 case BINF0_RECOVERY_OS_INITIATED:
343 case BINF0_RECOVERY_TPM_ERROR:
344 /* Assorted flavors of recovery boot reason. */
345 return StrCopy(dest, "recovery", size);
346 default:
347 /* Other values don't map cleanly to firmware type. */
348 return NULL;
349 }
350}
351
352
353
Randall Spangler54218662011-02-07 11:20:20 -0800354/* Read a system property integer.
355 *
356 * Returns the property value, or -1 if error. */
357int VbGetSystemPropertyInt(const char* name) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800358 int value = -1;
Randall Spangler54218662011-02-07 11:20:20 -0800359
Randall Spanglere73302c2011-02-18 14:53:01 -0800360 /* Switch positions */
Randall Spangler54218662011-02-07 11:20:20 -0800361 if (!strcasecmp(name,"devsw_cur")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800362 value = ReadGpio(GPIO_SIGNAL_TYPE_DEV);
Randall Spangler54218662011-02-07 11:20:20 -0800363 } else if (!strcasecmp(name,"devsw_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800364 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT);
Randall Spangler54218662011-02-07 11:20:20 -0800365 } else if (!strcasecmp(name,"recoverysw_cur")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800366 value = ReadGpio(GPIO_SIGNAL_TYPE_RECOVERY);
Randall Spangler54218662011-02-07 11:20:20 -0800367 } else if (!strcasecmp(name,"recoverysw_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800368 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_BOOT);
Randall Spangler54218662011-02-07 11:20:20 -0800369 } else if (!strcasecmp(name,"recoverysw_ec_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800370 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_EC_BOOT);
Randall Spangler54218662011-02-07 11:20:20 -0800371 } else if (!strcasecmp(name,"wpsw_cur")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800372 value = ReadGpio(GPIO_SIGNAL_TYPE_WP);
373 if (-1 != value && FwidStartsWith("Mario."))
374 value = 1 - value; /* Mario reports this backwards */
Randall Spangler54218662011-02-07 11:20:20 -0800375 } else if (!strcasecmp(name,"wpsw_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800376 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_WP_BOOT);
377 if (-1 != value && FwidStartsWith("Mario."))
378 value = 1 - value; /* Mario reports this backwards */
379 }
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800380 /* Saved memory is at a fixed location for all H2C BIOS. If the CHSW
381 * path exists in sysfs, it's a H2C BIOS. */
382 else if (!strcasecmp(name,"savedmem_base")) {
383 return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00F00000);
384 } else if (!strcasecmp(name,"savedmem_size")) {
385 return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00100000);
386 }
Randall Spanglere73302c2011-02-18 14:53:01 -0800387 /* NV storage values for older H2C BIOS */
388 else if (!strcasecmp(name,"recovery_request")) {
389 value = VbGetCmosRebootField(CMOSRF_RECOVERY);
390 } else if (!strcasecmp(name,"dbg_reset")) {
391 value = VbGetCmosRebootField(CMOSRF_DEBUG_RESET);
392 } else if (!strcasecmp(name,"fwb_tries")) {
393 value = VbGetCmosRebootField(CMOSRF_TRY_B);
394 }
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800395 /* Other parameters */
396 else if (!strcasecmp(name,"recovery_reason")) {
397 return VbGetRecoveryReason();
398 }
Randall Spangler54218662011-02-07 11:20:20 -0800399
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800400 /* TODO: implement the following properties:
401 * nvram_cleared
402 */
Randall Spanglerc80fe652011-02-17 11:06:47 -0800403
404 return value;
Randall Spangler54218662011-02-07 11:20:20 -0800405}
406
407
408/* Read a system property string into a destination buffer of the specified
409 * size.
410 *
411 * Returns the passed buffer, or NULL if error. */
412const char* VbGetSystemPropertyString(const char* name, char* dest, int size) {
413
414 if (!strcasecmp(name,"hwid")) {
415 return ReadFileString(dest, size, ACPI_BASE_PATH "/HWID");
416 } else if (!strcasecmp(name,"fwid")) {
417 return ReadFileString(dest, size, ACPI_BASE_PATH "/FWID");
418 } else if (!strcasecmp(name,"ro_fwid")) {
419 return ReadFileString(dest, size, ACPI_BASE_PATH "/FRID");
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800420 } else if (!strcasecmp(name,"mainfw_act")) {
421 switch(ReadFileInt(ACPI_BINF_PATH ".1")) {
422 case 0:
423 return StrCopy(dest, "recovery", size);
424 case 1:
425 return StrCopy(dest, "A", size);
426 case 2:
427 return StrCopy(dest, "B", size);
428 default:
429 return NULL;
430 }
431 } else if (!strcasecmp(name,"mainfw_type")) {
432 return VbReadMainFwType(dest, size);
433 } else if (!strcasecmp(name,"ecfw_act")) {
434 switch(ReadFileInt(ACPI_BINF_PATH ".2")) {
435 case 0:
436 return StrCopy(dest, "RO", size);
437 case 1:
438 return StrCopy(dest, "RW", size);
439 default:
440 return NULL;
441 }
Randall Spangler54218662011-02-07 11:20:20 -0800442 } else
443 return NULL;
Randall Spangler54218662011-02-07 11:20:20 -0800444}
445
446
447/* Set a system property integer.
448 *
449 * Returns 0 if success, -1 if error. */
450int VbSetSystemPropertyInt(const char* name, int value) {
451
Randall Spanglere73302c2011-02-18 14:53:01 -0800452 /* NV storage values for older H2C BIOS */
453 if (!strcasecmp(name,"recovery_request")) {
454 return VbSetCmosRebootField(CMOSRF_RECOVERY, value);
455 } else if (!strcasecmp(name,"dbg_reset")) {
456 return VbSetCmosRebootField(CMOSRF_DEBUG_RESET, value);
457 } else if (!strcasecmp(name,"fwb_tries")) {
458 return VbSetCmosRebootField(CMOSRF_TRY_B, value);
459 }
460
Randall Spanglerb47ed5a2011-02-23 13:05:40 -0800461 /* TODO: implement the following:
462 * nvram_cleared
463 */
464
Randall Spangler54218662011-02-07 11:20:20 -0800465 return -1;
466}
467
468
469/* Set a system property string.
470 *
471 * Returns 0 if success, -1 if error. */
472int VbSetSystemPropertyString(const char* name, const char* value) {
473
474 /* TODO: support setting */
475 return -1;
476}