Bug Summary

File:3rdparty/vboot/host/arch/x86/lib/crossystem_arch.c
Warning:line 408, column 8
The right operand of '==' is a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name crossystem_arch.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/home/coreboot/node-root/workspace/coreboot_scanbuild/3rdparty/vboot -resource-dir /opt/xgcc/lib/clang/17 -D CHROMEOS_ENVIRONMENT -D EC_EFS=0 -D EXTERNAL_TPM_CLEAR_REQUEST=0 -D _GNU_SOURCE -D _FILE_OFFSET_BITS=64 -D HAVE_EXECINFO_H -D HAVE_NSS -I /usr/include/nss -I /usr/include/nspr -I firmware/include -I firmware/lib/include -I firmware/lib/cgptlib/include -I firmware/lib/tpm_lite/include -I firmware/2lib/include -I host/include -I host/lib/include -I host/lib21/include -internal-isystem /opt/xgcc/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -source-date-epoch 1714465709 -Os -Wno-deprecated-declarations -Wno-trigraphs -Wwrite-strings -Wno-format-security -Wno-address-of-packed-member -Wno-unknown-warning -Wno-error=deprecated-declarations -std=gnu11 -fconst-strings -fdebug-compilation-dir=/home/coreboot/node-root/workspace/coreboot_scanbuild/3rdparty/vboot -ferror-limit 19 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /cb-build/coreboot_scanbuild.0/sharedutils-scanbuildtmp/2024-05-02-073004-2299942-1 -x c host/arch/x86/lib/crossystem_arch.c
1/* Copyright 2012 The ChromiumOS Authors
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(*__errno_location ()).h>
9#include <fcntl.h>
10#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
11#include <linux1/nvram.h>
12#include <linux1/version.h>
13#endif
14#include <stddef.h>
15#include <stdint.h>
16#include <stdio.h>
17#include <string.h>
18#include <sys/ioctl.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <sys/utsname.h>
22#include <unistd.h>
23
24#include "crossystem_arch.h"
25#include "crossystem.h"
26#include "crossystem_vbnv.h"
27#include "host_common.h"
28#include "vboot_struct.h"
29
30/* ACPI constants from Chrome OS Main Processor Firmware Spec */
31/* Boot reasons from BINF.0, from early H2C firmware */
32/* Unknown */
33#define BINF0_UNKNOWN0 0
34/* Normal boot to Chrome OS */
35#define BINF0_NORMAL1 1
36/* Developer mode boot (developer mode warning displayed) */
37#define BINF0_DEVELOPER2 2
38/* Recovery initiated by user, using recovery button */
39#define BINF0_RECOVERY_BUTTON3 3
40/* Recovery initiated by user pressing a key at developer mode warning
41 * screen */
42#define BINF0_RECOVERY_DEV_SCREEN_KEY4 4
43/* Recovery caused by BIOS failed signature check (neither rewritable
44 * firmware was valid) */
45#define BINF0_RECOVERY_RW_FW_BAD5 5
46/* Recovery caused by no OS kernel detected */
47#define BINF0_RECOVERY_NO_OS6 6
48/* Recovery caused by OS kernel failed signature check */
49#define BINF0_RECOVERY_BAD_OS7 7
50/* Recovery initiated by OS */
51#define BINF0_RECOVERY_OS_INITIATED8 8
52/* OS-initiated S3 diagnostic path (debug mode boot) */
53#define BINF0_S3_DIAGNOSTIC_PATH9 9
54/* S3 resume failed */
55#define BINF0_S3_RESUME_FAILED10 10
56/* Recovery caused by TPM error */
57#define BINF0_RECOVERY_TPM_ERROR11 11
58/* CHSW bitflags */
59#define CHSW_RECOVERY_BOOT0x00000002 0x00000002
60#define CHSW_RECOVERY_EC_BOOT0x00000004 0x00000004
61#define CHSW_DEV_BOOT0x00000020 0x00000020
62/* CMOS reboot field bitflags */
63#define CMOSRF_RECOVERY0x80 0x80
64#define CMOSRF_DEBUG_RESET0x40 0x40
65#define CMOSRF_TRY_B0x20 0x20
66/* GPIO signal types */
67#define GPIO_SIGNAL_TYPE_RECOVERY1 1
68#define GPIO_SIGNAL_TYPE_DEPRECATED_DEV2 2 /* Deprecated; see chromium:942901 */
69#define GPIO_SIGNAL_TYPE_WP3 3
70#define GPIO_SIGNAL_TYPE_PHASE_ENFORCEMENT4 4
71
72/* Base name for GPIO files */
73#define GPIO_BASE_PATH"/sys/class/gpio" "/sys/class/gpio"
74#define GPIO_EXPORT_PATH"/sys/class/gpio" "/export" GPIO_BASE_PATH"/sys/class/gpio" "/export"
75
76/* Base for SMBIOS information files */
77#define SMBIOS_BASE_PATH"/sys/class/dmi/id" "/sys/class/dmi/id"
78#define SMBIOS_PRODUCT_VERSION_PATH"/sys/class/dmi/id" "/product_version" SMBIOS_BASE_PATH"/sys/class/dmi/id" "/product_version"
79
80/* Filename for NVRAM file */
81#define NVRAM_PATH"/dev/nvram" "/dev/nvram"
82
83/* Filename for legacy firmware update tries */
84#define NEED_FWUPDATE_PATH"/mnt/stateful_partition/.need_firmware_update" "/mnt/stateful_partition/.need_firmware_update"
85
86/* Filenames for PCI Vendor and Device IDs */
87#define PCI_VENDOR_ID_PATH"/sys/bus/pci/devices/0000:00:00.0/vendor" "/sys/bus/pci/devices/0000:00:00.0/vendor"
88#define PCI_DEVICE_ID_PATH"/sys/bus/pci/devices/0000:00:00.0/device" "/sys/bus/pci/devices/0000:00:00.0/device"
89
90typedef struct {
91 unsigned int base;
92 unsigned int uid;
93} Basemapping;
94
95static void VbFixCmosChecksum(FILE* file)
96{
97#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
98 int fd = fileno(file);
99 ioctl(fd, NVRAM_SETCKS(((0U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((0x41)) << 0) | ((0) << ((0 +8)+8)))
);
100#endif
101}
102
103
104/*
105 * Get ChromeOS ACPI sysfs path.
106 *
107 * Note: the returned pointer should be passed to free(3) to release
108 * the allocated storage when it is no longer needed.
109 */
110static char* GetAcpiSysfsPath(const char* name)
111{
112 /*
113 * TODO: revert the legacy driver path lookup once all ChromeOS kernels
114 * switch to use CHROMEOS_ACPI.
115 */
116 static const char* legacy_driver_path = "/sys/devices/platform/chromeos_acpi";
117 static const char* legacy_fw_path = "/sys/devices/platform/GGL0001:00";
118 static const char* current_path = "/sys/devices/platform/GOOG0016:00";
119 char* path;
120 struct stat fs;
121 int ret;
122
123 if (stat(legacy_driver_path, &fs) == 0 && S_ISDIR(fs.st_mode)((((fs.st_mode)) & 0170000) == (0040000)))
124 ret = asprintf(&path, "%s/%s", legacy_driver_path, name);
125 else if (stat(legacy_fw_path, &fs) == 0 && S_ISDIR(fs.st_mode)((((fs.st_mode)) & 0170000) == (0040000)))
126 ret = asprintf(&path, "%s/%s", legacy_fw_path, name);
127 else
128 ret = asprintf(&path, "%s/%s", current_path, name);
129
130 return ret == -1 ? NULL((void*)0) : path;
131}
132
133
134static char* ReadAcpiSysfsString(char* dest, int size, const char* name)
135{
136 char* path;
137 char* ret;
138
139 path = GetAcpiSysfsPath(name);
140 if (!path)
141 return NULL((void*)0);
142
143 ret = ReadFileFirstLine(dest, size, path);
144 free(path);
145 return ret;
146}
147
148
149static int ReadAcpiSysfsInt(const char* name, unsigned* value)
150{
151 char* path;
152 int ret;
153
154 path = GetAcpiSysfsPath(name);
155 if (!path)
156 return -1;
157
158 ret = ReadFileInt(path, value);
159 free(path);
160 return ret;
161}
162
163
164static int ReadAcpiSysfsBit(const char* name, int bitmask)
165{
166 char* path;
167 int ret;
168
169 path = GetAcpiSysfsPath(name);
170 if (!path)
171 return -1;
172
173 ret = ReadFileBit(path, bitmask);
174 free(path);
175 return ret;
176}
177
178
179static int VbCmosRead(unsigned offs, size_t size, void *ptr)
180{
181 size_t res;
182 FILE* f;
183
184 f = fopen(NVRAM_PATH"/dev/nvram", "rb");
185 if (!f)
186 return -1;
187
188 if (0 != fseek(f, offs, SEEK_SET0)) {
189 fclose(f);
190 return -1;
191 }
192
193 res = fread(ptr, size, 1, f);
194 if (1 != res && errno(*__errno_location ()) == EIO5 && ferror(f)) {
195 VbFixCmosChecksum(f);
196 res = fread(ptr, size, 1, f);
197 }
198
199 fclose(f);
200 return (1 == res) ? 0 : -1;
201}
202
203
204static int VbCmosWrite(unsigned offs, size_t size, const void *ptr)
205{
206 size_t res;
207 FILE* f;
208
209 f = fopen(NVRAM_PATH"/dev/nvram", "w+b");
210 if (!f)
211 return -1;
212
213 if (0 != fseek(f, offs, SEEK_SET0)) {
214 fclose(f);
215 return -1;
216 }
217
218 res = fwrite(ptr, size, 1, f);
219 if (1 != res && errno(*__errno_location ()) == EIO5 && ferror(f)) {
220 VbFixCmosChecksum(f);
221 res = fwrite(ptr, size, 1, f);
222 }
223
224 fclose(f);
225 return (1 == res) ? 0 : -1;
226}
227
228
229int vb2_read_nv_storage(struct vb2_context *ctx)
230{
231 unsigned offs, blksz;
232 unsigned expectsz = vb2_nv_get_size(ctx);
233
234 /* Get the byte offset from VBNV */
235 if (ReadAcpiSysfsInt("VBNV.0", &offs) < 0)
236 return -1;
237 if (ReadAcpiSysfsInt("VBNV.1", &blksz) < 0)
238 return -1;
239 if (expectsz > blksz)
240 return -1; /* NV storage block is too small */
241
242 if (0 != VbCmosRead(offs, expectsz, ctx->nvdata))
243 return -1;
244
245 return 0;
246}
247
248
249int vb2_write_nv_storage(struct vb2_context *ctx)
250{
251 unsigned offs, blksz;
252 unsigned expectsz = vb2_nv_get_size(ctx);
253
254 if (!(ctx->flags & VB2_CONTEXT_NVDATA_CHANGED))
1
Assuming the condition is false
2
Taking false branch
255 return 0; /* Nothing changed, so no need to write */
256
257 /* Get the byte offset from VBNV */
258 if (ReadAcpiSysfsInt("VBNV.0", &offs) < 0)
3
Assuming the condition is false
4
Taking false branch
259 return -1;
260 if (ReadAcpiSysfsInt("VBNV.1", &blksz) < 0)
5
Assuming the condition is false
6
Taking false branch
261 return -1;
262 if (expectsz > blksz)
7
Assuming 'expectsz' is <= 'blksz'
8
Taking false branch
263 return -1; /* NV storage block is too small */
264
265 if (0 != VbCmosWrite(offs, expectsz, ctx->nvdata))
9
Taking false branch
266 return -1;
267
268 /* Also attempt to write using flashrom if using vboot2 */
269 VbSharedDataHeader *sh = VbSharedDataRead();
10
Calling 'VbSharedDataRead'
270 if (sh) {
271 if (sh->flags & VBSD_BOOT_FIRMWARE_VBOOT20x00008000)
272 vb2_write_nv_storage_flashrom(ctx);
273 free(sh);
274 }
275
276 return 0;
277}
278
279
280/*
281 * Get buffer data from ACPI.
282 *
283 * Buffer data is expected to be represented by a file which is a text dump of
284 * the buffer, representing each byte by two hex numbers, space and newline
285 * separated.
286 *
287 * On success, stores the amount of data read in bytes to *buffer_size; on
288 * erros, sets *buffer_size=0.
289 *
290 * Input - ACPI file name to get data from.
291 *
292 * Output: a pointer to AcpiBuffer structure containing the binary
293 * representation of the data. The caller is responsible for
294 * deallocating the pointer, this will take care of both the structure
295 * and the buffer. Null in case of error.
296 */
297static uint8_t* VbGetBuffer(const char* filename, int* buffer_size)
298{
299 FILE* f = NULL((void*)0);
300 char* file_buffer = NULL((void*)0);
301 uint8_t* output_buffer = NULL((void*)0);
302 uint8_t* return_value = NULL((void*)0);
303
304 /* Assume error until proven otherwise */
305 if (buffer_size
13.1
'buffer_size' is non-null
)
14
Taking true branch
306 *buffer_size = 0;
307
308 do {
40
Loop condition is false. Exiting loop
309 struct stat fs;
310 uint8_t* output_ptr;
311 int rv, i, real_size;
312 int parsed_size = 0;
313
314 int fd = open(filename, O_RDONLY00);
315 if (fd == -1)
15
Assuming the condition is false
16
Taking false branch
316 break;
317
318 rv = fstat(fd, &fs);
319 if (rv || !S_ISREG(fs.st_mode)((((fs.st_mode)) & 0170000) == (0100000))) {
17
Assuming 'rv' is 0
18
Assuming the condition is true
19
Taking false branch
320 close(fd);
321 break;
322 }
323
324 f = fdopen(fd, "r");
325 if (!f) {
20
Assuming 'f' is non-null, which participates in a condition later
21
Taking false branch
326 close(fd);
327 break;
328 }
329
330 file_buffer = malloc(fs.st_size + 1);
331 if (!file_buffer)
22
Assuming 'file_buffer' is non-null, which participates in a condition later
23
Taking false branch
332 break;
333
334 real_size = fread(file_buffer, 1, fs.st_size, f);
335 if (!real_size)
24
Assuming 'real_size' is not equal to 0, which participates in a condition later
25
Taking false branch
336 break;
337 file_buffer[real_size] = '\0';
338
339 /* Each byte in the output will replace two characters and a
340 * space in the input, so the output size does not exceed input
341 * side/3 (a little less if account for newline characters). */
342 output_buffer = malloc(real_size/3);
26
Uninitialized value stored to field 'struct_version'
343 if (!output_buffer)
27
Assuming 'output_buffer' is non-null, which participates in a condition later
28
Taking false branch
344 break;
345 output_ptr = output_buffer;
346
347 /* process the file contents */
348 for (i = 0; i
28.1
'i' is < 'real_size'
< real_size
; i++) {
29
Loop condition is true. Entering loop body
35
Assuming 'i' is >= 'real_size'
36
Loop condition is false. Execution continues on line 367
349 char* base, *end;
350
351 base = file_buffer + i;
352
353 if (!isxdigit(*base)((*__ctype_b_loc ())[(int) ((*base))] & (unsigned short int
) _ISxdigit)
)
30
Assuming the condition is false
31
Taking false branch
354 continue;
355
356 output_ptr[parsed_size++] =
32
Assigning value, which participates in a condition later
357 strtol(base, &end, 16) & 0xff;
358
359 if ((end - base) != 2)
33
Assuming the condition is false
34
Taking false branch
360 /* Input file format error */
361 break;
362
363 /* skip the second character and the following space */
364 i += 2;
365 }
366
367 if (i == real_size) {
37
Assuming 'i' is equal to 'real_size'
38
Taking true branch
368 /* all is well */
369 return_value = output_buffer;
370 output_buffer = NULL((void*)0); /* prevent it from deallocating */
371 if (buffer_size
38.1
'buffer_size' is non-null
)
39
Taking true branch
372 *buffer_size = parsed_size;
373 }
374 } while(0);
375
376 /* wrap up */
377 if (f
40.1
'f' is non-null
)
41
Taking true branch
378 fclose(f);
379
380 if (file_buffer
41.1
'file_buffer' is non-null
)
42
Taking true branch
381 free(file_buffer);
382
383 if (output_buffer
42.1
'output_buffer' is null
)
43
Taking false branch
384 free(output_buffer);
385
386 return return_value;
44
Returning pointer (loaded from 'return_value'), which participates in a condition later
387}
388
389
390VbSharedDataHeader* VbSharedDataRead(void)
391{
392 VbSharedDataHeader* sh;
393 int got_size = 0;
394 int expect_size;
395 char* path;
396
397 path = GetAcpiSysfsPath("VDAT");
398 if (!path)
11
Assuming 'path' is non-null
12
Taking false branch
399 return NULL((void*)0);
400
401 sh = (VbSharedDataHeader*)VbGetBuffer(path, &got_size);
13
Calling 'VbGetBuffer'
45
Returning from 'VbGetBuffer'
402 free(path);
403 if (!sh
45.1
'sh' is non-null
)
46
Taking false branch
404 return NULL((void*)0);
405
406 /* Make sure the size is sufficient for the struct version we got.
407 * Check supported old versions first. */
408 if (1 == sh->struct_version)
47
The right operand of '==' is a garbage value
409 expect_size = VB_SHARED_DATA_HEADER_SIZE_V11072;
410 else {
411 /* There'd better be enough data for the current header size. */
412 expect_size = sizeof(VbSharedDataHeader);
413 }
414
415 if (got_size < expect_size) {
416 free(sh);
417 return NULL((void*)0);
418 }
419 if (sh->data_size > got_size)
420 sh->data_size = got_size; /* Truncated read */
421
422 return sh;
423}
424
425
426/* Read the CMOS reboot field in NVRAM.
427 *
428 * Returns 0 if the mask is clear in the field, 1 if set, or -1 if error. */
429static int VbGetCmosRebootField(uint8_t mask)
430{
431 unsigned chnv;
432 uint8_t nvbyte;
433
434 /* Get the byte offset from CHNV */
435 if (ReadAcpiSysfsInt("CHNV", &chnv) < 0)
436 return -1;
437
438 if (0 != VbCmosRead(chnv, 1, &nvbyte))
439 return -1;
440
441 return (nvbyte & mask ? 1 : 0);
442}
443
444
445/* Write the CMOS reboot field in NVRAM.
446 *
447 * Sets (value=0) or clears (value!=0) the mask in the byte.
448 *
449 * Returns 0 if success, or -1 if error. */
450static int VbSetCmosRebootField(uint8_t mask, int value)
451{
452 unsigned chnv;
453 uint8_t nvbyte;
454
455 /* Get the byte offset from CHNV */
456 if (ReadAcpiSysfsInt("CHNV", &chnv) < 0)
457 return -1;
458
459 if (0 != VbCmosRead(chnv, 1, &nvbyte))
460 return -1;
461
462 /* Set/clear the mask */
463 if (value)
464 nvbyte |= mask;
465 else
466 nvbyte &= ~mask;
467
468 /* Write the byte back */
469 if (0 != VbCmosWrite(chnv, 1, &nvbyte))
470 return -1;
471
472 /* Success */
473 return 0;
474}
475
476
477/* Read the active main firmware type into the destination buffer.
478 * Passed the destination and its size. Returns the destination, or
479 * NULL if error. */
480static const char* VbReadMainFwType(char* dest, int size)
481{
482 unsigned value;
483
484 /* Try reading type from BINF.3 */
485 if (ReadAcpiSysfsInt("BINF.3", &value) == 0) {
486 switch(value) {
487 case BINF3_LEGACY4:
488 return StrCopy(dest, "legacy", size);
489 case BINF3_NETBOOT3:
490 return StrCopy(dest, "netboot", size);
491 case BINF3_RECOVERY0:
492 return StrCopy(dest, "recovery", size);
493 case BINF3_NORMAL1:
494 return StrCopy(dest, "normal", size);
495 case BINF3_DEVELOPER2:
496 return StrCopy(dest, "developer", size);
497 default:
498 break; /* Fall through to legacy handling */
499 }
500 }
501
502 /* Fall back to BINF.0 for legacy systems like Mario. */
503 if (ReadAcpiSysfsInt("BINF.0", &value) < 0)
504 /* Both BINF.0 and BINF.3 are missing, so this isn't Chrome OS
505 * firmware. */
506 return StrCopy(dest, "nonchrome", size);
507
508 switch(value) {
509 case BINF0_NORMAL1:
510 return StrCopy(dest, "normal", size);
511 case BINF0_DEVELOPER2:
512 return StrCopy(dest, "developer", size);
513 case BINF0_RECOVERY_BUTTON3:
514 case BINF0_RECOVERY_DEV_SCREEN_KEY4:
515 case BINF0_RECOVERY_RW_FW_BAD5:
516 case BINF0_RECOVERY_NO_OS6:
517 case BINF0_RECOVERY_BAD_OS7:
518 case BINF0_RECOVERY_OS_INITIATED8:
519 case BINF0_RECOVERY_TPM_ERROR11:
520 /* Assorted flavors of recovery boot reason. */
521 return StrCopy(dest, "recovery", size);
522 default:
523 /* Other values don't map cleanly to firmware type. */
524 return NULL((void*)0);
525 }
526}
527
528
529/* Read the recovery reason. Returns the reason code or -1 if error. */
530static vb2_error_t VbGetRecoveryReason(void)
531{
532 unsigned value;
533
534 /* Try reading type from BINF.4 */
535 if (ReadAcpiSysfsInt("BINF.4", &value) == 0)
536 return value;
537
538 /* Fall back to BINF.0 for legacy systems like Mario. */
539 if (ReadAcpiSysfsInt("BINF.0", &value) < 0)
540 return -1;
541 switch(value) {
542 case BINF0_NORMAL1:
543 case BINF0_DEVELOPER2:
544 return VB2_RECOVERY_NOT_REQUESTED;
545 case BINF0_RECOVERY_BUTTON3:
546 return VB2_RECOVERY_RO_MANUAL;
547 case BINF0_RECOVERY_RW_FW_BAD5:
548 return VB2_RECOVERY_RO_INVALID_RW;
549 case BINF0_RECOVERY_NO_OS6:
550 return VB2_RECOVERY_RW_NO_KERNEL;
551 case BINF0_RECOVERY_BAD_OS7:
552 return VB2_RECOVERY_RW_INVALID_OS;
553 case BINF0_RECOVERY_OS_INITIATED8:
554 return VB2_RECOVERY_LEGACY;
555 default:
556 /* Other values don't map cleanly to firmware type. */
557 return -1;
558 }
559}
560
561/* Physical GPIO number <N> may be accessed through /sys/class/gpio/gpio<M>/,
562 * but <N> and <M> may differ by some offset <O>. To determine that constant,
563 * we look for a directory named /sys/class/gpio/gpiochip<O>/. If there's not
564 * exactly one match for that, we're SOL.
565 */
566static int FindGpioChipOffset(unsigned *gpio_num, unsigned *offset,
567 const char *name)
568{
569 DIR *dir;
570 struct dirent *ent;
571 int match = 0;
572
573 dir = opendir(GPIO_BASE_PATH"/sys/class/gpio");
574 if (!dir) {
575 return 0;
576 }
577
578 while(0 != (ent = readdir(dir))) {
579 if (1 == sscanf(ent->d_name, "gpiochip%u", offset)) {
580 match++;
581 }
582 }
583
584 closedir(dir);
585 return (1 == match);
586}
587
588/* Physical GPIO number <N> may be accessed through /sys/class/gpio/gpio<M>/,
589 * but <N> and <M> may differ by some offset <O>. To determine that constant,
590 * we look for a directory named /sys/class/gpio/gpiochip<O>/ and check for
591 * a 'label' file inside of it to find the expected the controller name.
592 */
593static int FindGpioChipOffsetByLabel(unsigned *gpio_num, unsigned *offset,
594 const char *name)
595{
596 DIR *dir;
597 struct dirent *ent;
598 char filename[128];
599 char chiplabel[128];
600 int match = 0;
601 unsigned controller_offset = 0;
602
603 dir = opendir(GPIO_BASE_PATH"/sys/class/gpio");
604 if (!dir) {
605 return 0;
606 }
607
608 while(0 != (ent = readdir(dir))) {
609 if (1 == sscanf(ent->d_name, "gpiochip%u",
610 &controller_offset)) {
611 /*
612 * Read the file at gpiochip<O>/label to get the
613 * identifier for this bank of GPIOs.
614 */
615 snprintf(filename, sizeof(filename),
616 "%s/gpiochip%u/label",
617 GPIO_BASE_PATH"/sys/class/gpio", controller_offset);
618 if (ReadFileFirstLine(chiplabel, sizeof(chiplabel),
619 filename)) {
620 if (!strncasecmp(chiplabel, name,
621 strlen(name))) {
622 /*
623 * Store offset when chip label is
624 * matched.
625 */
626 *offset = controller_offset;
627 match++;
628 }
629 }
630 }
631 }
632
633 closedir(dir);
634 return (1 == match);
635}
636
637static int FindGpioChipOffsetByNumber(unsigned *gpio_num, unsigned *offset,
638 Basemapping *data)
639{
640 DIR *dir;
641 struct dirent *ent;
642 int match = 0;
643
644 /* Obtain relative GPIO number.
645 * The assumption here is the Basemapping
646 * table is arranged in decreasing order of
647 * base address and ends with 0.
648 * A UID with value 0 indicates an invalid range
649 * and causes an early return to avoid the directory
650 * opening code below.
651 */
652 do {
653 if (*gpio_num >= data->base) {
654 *gpio_num -= data->base;
655 break;
656 }
657 data++;
658 } while(1);
659
660 if (data->uid == 0) {
661 return 0;
662 }
663
664 dir = opendir(GPIO_BASE_PATH"/sys/class/gpio");
665 if (!dir) {
666 return 0;
667 }
668
669 while(0 != (ent = readdir(dir))) {
670 /* For every gpiochip entry determine uid. */
671 if (1 == sscanf(ent->d_name, "gpiochip%u", offset)) {
672 char uid_file[128];
673 unsigned uid_value;
674 snprintf(uid_file, sizeof(uid_file),
675 "%s/gpiochip%u/device/firmware_node/uid",
676 GPIO_BASE_PATH"/sys/class/gpio", *offset);
677 if (ReadFileInt(uid_file, &uid_value) < 0)
678 continue;
679 if (data->uid == uid_value) {
680 match++;
681 break;
682 }
683 }
684 }
685
686 closedir(dir);
687 return (1 == match);
688}
689
690
691/* Braswell has 4 sets of GPIO banks. It is expected the firmware exposes each
692 * bank of gpios using a UID in ACPI. Furthermore the gpio number exposed is
693 * relative to the bank. e.g. gpio MF_ISH_GPIO_4 in the bank specified by UID 3
694 * would be encoded as 0x10016.
695 *
696 * UID | Bank Offset
697 * ----+------------
698 * 1 | 0x0000
699 * 2 | 0x8000
700 * 3 | 0x10000
701 * 4 | 0x18000
702 */
703static int BraswellFindGpioChipOffset(unsigned *gpio_num, unsigned *offset,
704 const char *name)
705{
706 int ret;
707 struct utsname host;
708 unsigned int maj, min;
709 int gpe = 0;
710 static Basemapping data[]={
711 {0x20000, 0},
712 {0x18000, 4},
713 {0x10000, 3},
714 {0x08000, 2},
715 {0x00000, 1}};
716
717 /*
718 * This quirk addresses b:143174998 and is required on kernels >= 4.16
719 * when GPIO numbering has changed with an upstream commit:
720 * 03c4749dd6c7ff948a0ce59a44a1b97c015353c2
721 * "gpio / ACPI: Drop unnecessary ACPI GPIO to Linux GPIO translation".
722 * With that change gpio ACPI/Linux kernel 1:1 mapping was introduced which
723 * made mismatch for gpio number and backward compatibility for user-space.
724 * Details on review commit review
725 * https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2153155
726 */
727
728 /*
729 * Here we are addressing particular wpsw_cur pin which is connected to
730 * East Community GPIO chip (uid == 3, base == 0x10000). In this case there
731 * is only one gap between 11 and 15 (0..11 15..26). For now crosssystem
732 * is not checking pins in other gpio banks, but it is worth to mention that
733 * there are gaps as well.
734 */
735 if (*gpio_num >= 0x10000 && *gpio_num < 0x18000)
736 gpe = 1;
737
738 ret = FindGpioChipOffsetByNumber(gpio_num, offset, data);
739 if (!ret || !gpe)
740 return ret;
741
742 if (uname(&host) == 0) {
743 if (sscanf(host.release, "%u.%u.", &maj, &min) == 2) {
744#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
745 if (KERNEL_VERSION(maj, min, 0)(((maj) << 16) + ((min) << 8) + ((0) > 255 ? 255
: (0)))
>= KERNEL_VERSION(4, 16, 0)(((4) << 16) + ((16) << 8) + ((0) > 255 ? 255 :
(0)))
&&
746 *offset > 11)
747 *offset += 3;
748#endif
749 } else {
750 printf("Couldn't retrieve kernel version!\n");
751 ret = 0;
752 }
753 } else {
754 perror("uname");
755 ret = 0;
756 }
757
758 return ret;
759}
760
761/* BayTrail has 3 sets of GPIO banks. It is expected the firmware exposes
762 * each bank of gpios using a UID in ACPI. Furthermore the gpio number exposed
763 * is relative to the bank. e.g. gpio 6 in the bank specified by UID 3 would
764 * be encoded as 0x2006.
765 * UID | Bank Offset
766 * ----+------------
767 * 1 | 0x0000
768 * 2 | 0x1000
769 * 3 | 0x2000
770 */
771static int BayTrailFindGpioChipOffset(unsigned *gpio_num, unsigned *offset,
772 const char *name)
773{
774 static Basemapping data[]={
775 {0x3000, 0},
776 {0x2000, 3},
777 {0x1000, 2},
778 {0x0000, 1}};
779
780 return FindGpioChipOffsetByNumber(gpio_num, offset, data);
781}
782
783struct GpioChipset {
784 const char *name;
785 int (*ChipOffsetAndGpioNumber)(unsigned *gpio_num,
786 unsigned *chip_offset,
787 const char *name);
788};
789
790static const struct GpioChipset chipsets_supported[] = {
791 { "AMD0030", FindGpioChipOffset },
792 { "NM10", FindGpioChipOffset },
793 { "CougarPoint", FindGpioChipOffset },
794 { "PantherPoint", FindGpioChipOffset },
795 { "LynxPoint", FindGpioChipOffset },
796 { "PCH-LP", FindGpioChipOffset },
797 { "INT3437:00", FindGpioChipOffsetByLabel },
798 { "INT344B:00", FindGpioChipOffsetByLabel },
799 /* INT3452 are for Apollolake */
800 { "INT3452:00", FindGpioChipOffsetByLabel },
801 { "INT3452:01", FindGpioChipOffsetByLabel },
802 { "INT3452:02", FindGpioChipOffsetByLabel },
803 { "INT3452:03", FindGpioChipOffsetByLabel },
804 { "INT3455:00", FindGpioChipOffsetByLabel },
805 { "INT34BB:00", FindGpioChipOffsetByLabel },
806 { "INT34C8:00", FindGpioChipOffsetByLabel },
807 { "INT34C5:00", FindGpioChipOffsetByLabel },
808 /* INTC105x are for Alderlake */
809 { "INTC1055:00", FindGpioChipOffsetByLabel },
810 { "INTC1056:00", FindGpioChipOffsetByLabel },
811 { "INTC1057:00", FindGpioChipOffsetByLabel },
812 /* INTC108x are for Meteor Lake */
813 { "INTC1083:00", FindGpioChipOffsetByLabel },
814 /* INT3453 are for GLK */
815 { "INT3453:00", FindGpioChipOffsetByLabel },
816 { "INT3453:01", FindGpioChipOffsetByLabel },
817 { "INT3453:02", FindGpioChipOffsetByLabel },
818 { "INT3453:03", FindGpioChipOffsetByLabel },
819 { "BayTrail", BayTrailFindGpioChipOffset },
820 { "Braswell", BraswellFindGpioChipOffset },
821 { NULL((void*)0) },
822};
823
824static const struct GpioChipset *FindChipset(const char *name)
825{
826 const struct GpioChipset *chipset = &chipsets_supported[0];
827
828 while (chipset->name != NULL((void*)0)) {
829 if (!strcmp(name, chipset->name))
830 return chipset;
831 chipset++;
832 }
833 return NULL((void*)0);
834}
835
836/* Read a GPIO of the specified signal type (see ACPI GPIO SignalType).
837 *
838 * Returns 1 if the signal is asserted, 0 if not asserted, or -1 if error. */
839static int ReadGpio(unsigned signal_type)
840{
841 char name[256];
842 int index = 0;
843 unsigned gpio_type;
844 unsigned active_high;
845 unsigned controller_num;
846 unsigned controller_offset = 0;
847 char controller_name[128];
848 unsigned value;
849 const struct GpioChipset *chipset;
850 char base_path[128];
851 char* path;
852
853 path = GetAcpiSysfsPath("GPIO");
854 if (!path)
855 return -1;
856 strncpy(base_path, path, sizeof(base_path) - 1);
857 base_path[sizeof(base_path) - 1] = 0;
858 free(path);
859
860 /* Scan GPIO.* to find a matching signal type */
861 for (index = 0; ; index++) {
862 snprintf(name, sizeof(name), "%s.%d/GPIO.0", base_path, index);
863 if (ReadFileInt(name, &gpio_type) < 0)
864 return -1; /* Ran out of GPIOs before finding a match */
865 if (gpio_type == signal_type)
866 break;
867 }
868
869 /* Read attributes and controller info for the GPIO */
870 snprintf(name, sizeof(name), "%s.%d/GPIO.1", base_path, index);
871 if (ReadFileInt(name, &active_high) < 0)
872 return -1;
873 snprintf(name, sizeof(name), "%s.%d/GPIO.2", base_path, index);
874 if (ReadFileInt(name, &controller_num) < 0)
875 return -1;
876 /* Do not attempt to read GPIO that is set to -1 in ACPI */
877 if (controller_num == 0xFFFFFFFF)
878 return -1;
879
880 /* Check for chipsets we recognize. */
881 snprintf(name, sizeof(name), "%s.%d/GPIO.3", base_path, index);
882 if (!ReadFileFirstLine(controller_name, sizeof(controller_name), name))
883 return -1;
884 chipset = FindChipset(controller_name);
885 if (chipset == NULL((void*)0))
886 return -1;
887
888 /* Modify GPIO number by driver's offset */
889 if (!chipset->ChipOffsetAndGpioNumber(&controller_num,
890 &controller_offset,
891 chipset->name))
892 return -1;
893 controller_offset += controller_num;
894
895 /* Try reading the GPIO value */
896 snprintf(name, sizeof(name), "%s/gpio%d/value",
897 GPIO_BASE_PATH"/sys/class/gpio", controller_offset);
898 if (ReadFileInt(name, &value) < 0) {
899 /* Try exporting the GPIO */
900 FILE* f = fopen(GPIO_EXPORT_PATH"/sys/class/gpio" "/export", "wt");
901 if (!f)
902 return -1;
903 fprintf(f, "%u", controller_offset);
904 fclose(f);
905
906 /* Try re-reading the GPIO value */
907 if (ReadFileInt(name, &value) < 0)
908 return -1;
909 }
910
911 /* Normalize the value read from the kernel in case it is not always
912 * 1. */
913 value = value ? 1 : 0;
914
915 /* Compare the GPIO value with the active value and return 1 if
916 * match. */
917 return (value == active_high ? 1 : 0);
918}
919
920static int GetBoardId(void)
921{
922 /*
923 * Can't use vb2_read_file here, as it expects to be able to
924 * seek to the end of the file to tell the size, and the sysfs
925 * SMBIOS implementation will seek to offset 4096.
926 */
927 int board_id = -1;
928 FILE *f = fopen(SMBIOS_PRODUCT_VERSION_PATH"/sys/class/dmi/id" "/product_version", "r");
929
930 if (!f)
931 return -1;
932
933 if (fscanf(f, "rev%d\n", &board_id) != 1)
934 board_id = -1;
935
936 fclose(f);
937 return board_id;
938}
939
940int VbGetArchPropertyInt(const char* name)
941{
942 int value = -1;
943
944 /* Switch positions */
945 if (!strcasecmp(name,"devsw_cur")) {
946 /* Systems with virtual developer switches return at-boot
947 * value */
948 value = VbGetSystemPropertyInt("devsw_boot");
949 } else if (!strcasecmp(name,"recoverysw_cur")) {
950 value = ReadGpio(GPIO_SIGNAL_TYPE_RECOVERY1);
951 } else if (!strcasecmp(name,"wpsw_cur")) {
952 value = ReadGpio(GPIO_SIGNAL_TYPE_WP3);
953 } else if (!strcasecmp(name,"recoverysw_ec_boot")) {
954 value = ReadAcpiSysfsBit("CHSW", CHSW_RECOVERY_EC_BOOT0x00000004);
955 } else if (!strcasecmp(name,"phase_enforcement")) {
956 value = ReadGpio(GPIO_SIGNAL_TYPE_PHASE_ENFORCEMENT4);
957 }
958
959 /* Fields for old systems which don't have VbSharedData */
960 if (VbSharedDataVersion() < 2) {
961 if (!strcasecmp(name,"recovery_reason")) {
962 value = VbGetRecoveryReason();
963 } else if (!strcasecmp(name,"devsw_boot")) {
964 value = ReadAcpiSysfsBit("CHSW", CHSW_DEV_BOOT0x00000020);
965 } else if (!strcasecmp(name,"recoverysw_boot")) {
966 value = ReadAcpiSysfsBit("CHSW", CHSW_RECOVERY_BOOT0x00000002);
967 }
968 }
969
970 /* NV storage values. If unable to get from NV storage, fall back to
971 * the CMOS reboot field used by older BIOS (e.g. Mario). */
972 if (!strcasecmp(name,"recovery_request")) {
973 value = vb2_get_nv_storage(VB2_NV_RECOVERY_REQUEST);
974 if (-1 == value)
975 value = VbGetCmosRebootField(CMOSRF_RECOVERY0x80);
976 } else if (!strcasecmp(name,"dbg_reset")) {
977 value = vb2_get_nv_storage(VB2_NV_DEBUG_RESET_MODE);
978 if (-1 == value)
979 value = VbGetCmosRebootField(CMOSRF_DEBUG_RESET0x40);
980 } else if (!strcasecmp(name,"fwb_tries")) {
981 value = vb2_get_nv_storage(VB2_NV_TRY_COUNT);
982 if (-1 == value)
983 value = VbGetCmosRebootField(CMOSRF_TRY_B0x20);
984 }
985
986 /* Firmware update tries is now stored in the kernel field. On
987 * older systems where it's not, it was stored in a file in the
988 * stateful partition. */
989 if (!strcasecmp(name,"fwupdate_tries")) {
990 unsigned fwupdate_value;
991 if (-1 != vb2_get_nv_storage(VB2_NV_KERNEL_FIELD))
992 return -1; /* NvStorage supported; fail through
993 * arch-specific implementation to normal
994 * implementation. */
995 /* Read value from file; missing file means value=0. */
996 if (ReadFileInt(NEED_FWUPDATE_PATH"/mnt/stateful_partition/.need_firmware_update", &fwupdate_value) < 0)
997 value = 0;
998 else
999 value = (int)fwupdate_value;
1000 }
1001
1002 if (!strcasecmp(name, "board_id"))
1003 return GetBoardId();
1004
1005 return value;
1006}
1007
1008
1009const char* VbGetArchPropertyString(const char* name, char* dest,
1010 size_t size)
1011{
1012 unsigned value;
1013
1014 if (!strcasecmp(name,"arch")) {
1015 return StrCopy(dest, "x86", size);
1016 } else if (!strcasecmp(name,"hwid")) {
1017 return ReadAcpiSysfsString(dest, size, "HWID");
1018 } else if (!strcasecmp(name,"fwid")) {
1019 return ReadAcpiSysfsString(dest, size, "FWID");
1020 } else if (!strcasecmp(name,"ro_fwid")) {
1021 return ReadAcpiSysfsString(dest, size, "FRID");
1022 } else if (!strcasecmp(name,"mainfw_act")) {
1023 if (ReadAcpiSysfsInt("BINF.1", &value) < 0)
1024 return NULL((void*)0);
1025 switch(value) {
1026 case 0:
1027 return StrCopy(dest, "recovery", size);
1028 case 1:
1029 return StrCopy(dest, "A", size);
1030 case 2:
1031 return StrCopy(dest, "B", size);
1032 default:
1033 return NULL((void*)0);
1034 }
1035 } else if (!strcasecmp(name,"mainfw_type")) {
1036 return VbReadMainFwType(dest, size);
1037 } else if (!strcasecmp(name,"ecfw_act")) {
1038 if (ReadAcpiSysfsInt("BINF.2", &value) < 0)
1039 return NULL((void*)0);
1040 switch(value) {
1041 case 0:
1042 return StrCopy(dest, "RO", size);
1043 case 1:
1044 return StrCopy(dest, "RW", size);
1045 default:
1046 return NULL((void*)0);
1047 }
1048 }
1049
1050 return NULL((void*)0);
1051}
1052
1053
1054int VbSetArchPropertyInt(const char* name, int value)
1055{
1056 /* NV storage values. If unable to get from NV storage, fall back to
1057 * the CMOS reboot field used by older BIOS. */
1058 if (!strcasecmp(name,"recovery_request")) {
1059 if (0 == vb2_set_nv_storage(VB2_NV_RECOVERY_REQUEST, value))
1060 return 0;
1061 return VbSetCmosRebootField(CMOSRF_RECOVERY0x80, value);
1062 } else if (!strcasecmp(name,"dbg_reset")) {
1063 if (0 == vb2_set_nv_storage(VB2_NV_DEBUG_RESET_MODE, value))
1064 return 0;
1065 return VbSetCmosRebootField(CMOSRF_DEBUG_RESET0x40, value);
1066 } else if (!strcasecmp(name,"fwb_tries")) {
1067 if (0 == vb2_set_nv_storage(VB2_NV_TRY_COUNT, value))
1068 return 0;
1069 return VbSetCmosRebootField(CMOSRF_TRY_B0x20, value);
1070 }
1071 /* Firmware update tries is now stored in the kernel field. On
1072 * older systems where it's not, it was stored in a file in the
1073 * stateful partition. */
1074 else if (!strcasecmp(name,"fwupdate_tries")) {
1075 if (-1 != vb2_get_nv_storage(VB2_NV_KERNEL_FIELD))
1076 return -1; /* NvStorage supported; fail through
1077 * arch-specific implementation to normal
1078 * implementation */
1079
1080 if (value) {
1081 char buf[32];
1082 snprintf(buf, sizeof(buf), "%d", value);
1083 return WriteFile(NEED_FWUPDATE_PATH"/mnt/stateful_partition/.need_firmware_update", buf, strlen(buf));
1084 } else {
1085 /* No update tries, so remove file if it exists. */
1086 unlink(NEED_FWUPDATE_PATH"/mnt/stateful_partition/.need_firmware_update");
1087 return 0;
1088 }
1089 }
1090
1091 return -1;
1092}
1093
1094int VbSetArchPropertyString(const char* name, const char* value)
1095{
1096 /* If there were settable architecture-dependent string properties,
1097 * they'd be here. */
1098 return -1;
1099}