blob: 5f94d711a002453f6f28010d37fcfd27a625700e [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 Spangler54218662011-02-07 11:20:20 -080030
31/* Base name for ACPI files */
32#define ACPI_BASE_PATH "/sys/devices/platform/chromeos_acpi"
33/* Paths for frequently used ACPI files */
34#define ACPI_CHNV_PATH ACPI_BASE_PATH "/CHNV"
35#define ACPI_CHSW_PATH ACPI_BASE_PATH "/CHSW"
36#define ACPI_GPIO_PATH ACPI_BASE_PATH "/GPIO"
37
38/* Base name for GPIO files */
39#define GPIO_BASE_PATH "/sys/class/gpio"
40#define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
41
Randall Spanglere73302c2011-02-18 14:53:01 -080042/* Base name for NVRAM file */
43#define NVRAM_PATH "/dev/nvram"
44
Randall Spangler54218662011-02-07 11:20:20 -080045/* Read a string from a file. Passed the destination, dest size, and
46 * filename to read.
47 *
48 * Returns the destination, or NULL if error. */
49char* ReadFileString(char* dest, int size, const char* filename) {
50 char* got;
51 FILE* f;
52
53 f = fopen(filename, "rt");
54 if (!f)
55 return NULL;
56
57 got = fgets(dest, size, f);
58 fclose(f);
59 return got;
60}
61
62
63/* Read an integer from a file.
64 *
65 * Returns the parsed integer, or -1 if error. */
66int ReadFileInt(const char* filename) {
67 char buf[64];
68 int value;
69 char* e = NULL;
70
71 if (!ReadFileString(buf, sizeof(buf), filename))
72 return -1;
73
74 /* Convert to integer. Allow characters after the int ("123 blah"). */
75 value = strtol(buf, &e, 0);
76 if (e == buf)
77 return -1; /* No characters consumed, so conversion failed */
78
79 return value;
80}
81
82
83/* Check if a bit is set in a file which contains an integer.
84 *
85 * Returns 1 if the bit is set, 0 if clear, or -1 if error. */
86int ReadFileBit(const char* filename, int bitmask) {
87 int value = ReadFileInt(filename);
88 if (value == -1)
89 return -1;
90 else return (value & bitmask ? 1 : 0);
91}
92
93
Randall Spanglerc80fe652011-02-17 11:06:47 -080094/* Return true if the FWID starts with the specified string. */
95static int FwidStartsWith(const char *start) {
96 char fwid[128];
97 if (!VbGetSystemPropertyString("fwid", fwid, sizeof(fwid)))
98 return 0;
99
100 return 0 == strncmp(fwid, start, strlen(start));
101}
102
103
Randall Spangler54218662011-02-07 11:20:20 -0800104/* Read a GPIO of the specified signal type (see ACPI GPIO SignalType).
105 *
106 * Returns 1 if the signal is asserted, 0 if not asserted, or -1 if error. */
107int ReadGpio(int signal_type) {
108 char name[128];
109 int index = 0;
110 int gpio_type;
111 int active_high;
112 int controller_offset;
113 char controller_name[128];
114 int value;
115
116 /* Scan GPIO.* to find a matching signal type */
117 for (index = 0; ; index++) {
118 snprintf(name, sizeof(name), "%s.%d/GPIO.0", ACPI_GPIO_PATH, index);
119 gpio_type = ReadFileInt(name);
120 if (gpio_type == signal_type)
121 break;
122 else if (gpio_type == -1)
123 return -1; /* Ran out of GPIOs before finding a match */
124 }
125
126 /* Read attributes and controller info for the GPIO */
127 snprintf(name, sizeof(name), "%s.%d/GPIO.1", ACPI_GPIO_PATH, index);
128 active_high = ReadFileBit(name, 0x00000001);
129 snprintf(name, sizeof(name), "%s.%d/GPIO.2", ACPI_GPIO_PATH, index);
130 controller_offset = ReadFileInt(name);
131 if (active_high == -1 || controller_offset == -1)
132 return -1; /* Missing needed info */
133
134 /* We only support the NM10 for now */
135 snprintf(name, sizeof(name), "%s.%d/GPIO.3", ACPI_GPIO_PATH, index);
136 if (!ReadFileString(controller_name, sizeof(controller_name), name))
137 return -1;
138 if (0 != strcmp(controller_name, "NM10"))
139 return -1;
140
141 /* Assume the NM10 has offset 192 */
142 /* TODO: should really check gpiochipNNN/label to see if it's the
143 * address we expect for the NM10, and then read the offset from
144 * gpiochipNNN/base. */
145 controller_offset += 192;
146
147 /* Try reading the GPIO value */
148 snprintf(name, sizeof(name), "%s/gpio%d/value",
149 GPIO_BASE_PATH, controller_offset);
150 value = ReadFileInt(name);
151
152 if (value == -1) {
153 /* Try exporting the GPIO */
154 FILE* f = fopen(GPIO_EXPORT_PATH, "wt");
155 if (!f)
156 return -1;
157 fprintf(f, "%d", controller_offset);
158 fclose(f);
159
160 /* Try re-reading the GPIO value */
161 value = ReadFileInt(name);
162 }
163
164 if (value == -1)
165 return -1;
166
167 /* Compare the GPIO value with the active value and return 1 if match. */
168 return (value == active_high ? 1 : 0);
169}
170
171
Randall Spanglere73302c2011-02-18 14:53:01 -0800172/* Read the CMOS reboot field in NVRAM.
173 *
174 * Returns 0 if the mask is clear in the field, 1 if set, or -1 if error. */
175int VbGetCmosRebootField(uint8_t mask) {
176 FILE* f;
177 int chnv, nvbyte;
178
179 /* Get the byte offset from CHNV */
180 chnv = ReadFileInt(ACPI_CHNV_PATH);
181 if (chnv == -1)
182 return -1;
183
184 f = fopen(NVRAM_PATH, "rb");
185 if (!f)
186 return -1;
187
188 if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
189 fclose(f);
190 return -1;
191 }
192
193 fclose(f);
194 return (nvbyte & mask ? 1 : 0);
195}
196
197
198/* Write the CMOS reboot field in NVRAM.
199 *
200 * Sets (value=0) or clears (value!=0) the mask in the byte.
201 *
202 * Returns 0 if success, or -1 if error. */
203int VbSetCmosRebootField(uint8_t mask, int value) {
204 FILE* f;
205 int chnv, nvbyte;
206
207 /* Get the byte offset from CHNV */
208 chnv = ReadFileInt(ACPI_CHNV_PATH);
209 if (chnv == -1)
210 return -1;
211
212 f = fopen(NVRAM_PATH, "w+b");
213 if (!f)
214 return -1;
215
216 /* Read the current value */
217 if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
218 fclose(f);
219 return -1;
220 }
221
222 /* Set/clear the mask */
223 if (value)
224 nvbyte |= mask;
225 else
226 nvbyte &= ~mask;
227
228 /* Write the byte back */
229 if (0 != fseek(f, chnv, SEEK_SET) || EOF == (fputc(nvbyte, f))) {
230 fclose(f);
231 return -1;
232 }
233
234 /* Success */
235 fclose(f);
236 return 0;
237}
238
Randall Spangler54218662011-02-07 11:20:20 -0800239/* Read a system property integer.
240 *
241 * Returns the property value, or -1 if error. */
242int VbGetSystemPropertyInt(const char* name) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800243 int value = -1;
Randall Spangler54218662011-02-07 11:20:20 -0800244
Randall Spanglere73302c2011-02-18 14:53:01 -0800245 /* Switch positions */
Randall Spangler54218662011-02-07 11:20:20 -0800246 if (!strcasecmp(name,"devsw_cur")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800247 value = ReadGpio(GPIO_SIGNAL_TYPE_DEV);
Randall Spangler54218662011-02-07 11:20:20 -0800248 } else if (!strcasecmp(name,"devsw_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800249 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT);
Randall Spangler54218662011-02-07 11:20:20 -0800250 } else if (!strcasecmp(name,"recoverysw_cur")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800251 value = ReadGpio(GPIO_SIGNAL_TYPE_RECOVERY);
Randall Spangler54218662011-02-07 11:20:20 -0800252 } else if (!strcasecmp(name,"recoverysw_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800253 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_BOOT);
Randall Spangler54218662011-02-07 11:20:20 -0800254 } else if (!strcasecmp(name,"recoverysw_ec_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800255 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_EC_BOOT);
Randall Spangler54218662011-02-07 11:20:20 -0800256 } else if (!strcasecmp(name,"wpsw_cur")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800257 value = ReadGpio(GPIO_SIGNAL_TYPE_WP);
258 if (-1 != value && FwidStartsWith("Mario."))
259 value = 1 - value; /* Mario reports this backwards */
Randall Spangler54218662011-02-07 11:20:20 -0800260 } else if (!strcasecmp(name,"wpsw_boot")) {
Randall Spanglerc80fe652011-02-17 11:06:47 -0800261 value = ReadFileBit(ACPI_CHSW_PATH, CHSW_WP_BOOT);
262 if (-1 != value && FwidStartsWith("Mario."))
263 value = 1 - value; /* Mario reports this backwards */
264 }
Randall Spanglere73302c2011-02-18 14:53:01 -0800265 /* NV storage values for older H2C BIOS */
266 else if (!strcasecmp(name,"recovery_request")) {
267 value = VbGetCmosRebootField(CMOSRF_RECOVERY);
268 } else if (!strcasecmp(name,"dbg_reset")) {
269 value = VbGetCmosRebootField(CMOSRF_DEBUG_RESET);
270 } else if (!strcasecmp(name,"fwb_tries")) {
271 value = VbGetCmosRebootField(CMOSRF_TRY_B);
272 }
Randall Spangler54218662011-02-07 11:20:20 -0800273
274 /* TODO: remaining properties from spec */
Randall Spanglerc80fe652011-02-17 11:06:47 -0800275
276 return value;
Randall Spangler54218662011-02-07 11:20:20 -0800277}
278
279
280/* Read a system property string into a destination buffer of the specified
281 * size.
282 *
283 * Returns the passed buffer, or NULL if error. */
284const char* VbGetSystemPropertyString(const char* name, char* dest, int size) {
285
286 if (!strcasecmp(name,"hwid")) {
287 return ReadFileString(dest, size, ACPI_BASE_PATH "/HWID");
288 } else if (!strcasecmp(name,"fwid")) {
289 return ReadFileString(dest, size, ACPI_BASE_PATH "/FWID");
290 } else if (!strcasecmp(name,"ro_fwid")) {
291 return ReadFileString(dest, size, ACPI_BASE_PATH "/FRID");
292 } else
293 return NULL;
294
295 /* TODO: remaining properties from spec */
296}
297
298
299/* Set a system property integer.
300 *
301 * Returns 0 if success, -1 if error. */
302int VbSetSystemPropertyInt(const char* name, int value) {
303
Randall Spanglere73302c2011-02-18 14:53:01 -0800304 /* NV storage values for older H2C BIOS */
305 if (!strcasecmp(name,"recovery_request")) {
306 return VbSetCmosRebootField(CMOSRF_RECOVERY, value);
307 } else if (!strcasecmp(name,"dbg_reset")) {
308 return VbSetCmosRebootField(CMOSRF_DEBUG_RESET, value);
309 } else if (!strcasecmp(name,"fwb_tries")) {
310 return VbSetCmosRebootField(CMOSRF_TRY_B, value);
311 }
312
Randall Spangler54218662011-02-07 11:20:20 -0800313 return -1;
314}
315
316
317/* Set a system property string.
318 *
319 * Returns 0 if success, -1 if error. */
320int VbSetSystemPropertyString(const char* name, const char* value) {
321
322 /* TODO: support setting */
323 return -1;
324}