summaryrefslogtreecommitdiff
path: root/host/arch/x86/lib/crossystem_arch.c
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2011-04-07 10:02:00 -0700
committerRandall Spangler <rspangler@chromium.org>2011-04-07 10:02:00 -0700
commiteb59195473246f8ff3076950f3c3b8d3564d433d (patch)
treebdb23c2b5d57060d5517c041df1373a360818980 /host/arch/x86/lib/crossystem_arch.c
parent9522b84029e48579303e9dbc287266eae93e0af9 (diff)
downloadvboot-eb59195473246f8ff3076950f3c3b8d3564d433d.tar.gz
Refactor crossystem to move x86-specific implementation to its own file.
This should be ready for the ARM team to pick up and work on. I added a placeholder ARM implementation file, though it's not hooked up in the Makefile yet. As soon as you implement the VbNvStorage APIs, all the related crossystem commands will start working. Ditto for VbSharedData. The params which x86 gets from ACPI you'll need to get from u-boot somehow, probably via your own kernel driver. R=robotboy@chromium.org BUG=chromium-os:12522 TEST=emerge-x86-alex vboot_reference, make sure it still works on x86 Review URL: http://codereview.chromium.org/6780008 Change-Id: I628ee56508421b937ed50db7cb9b8385408d2f5e
Diffstat (limited to 'host/arch/x86/lib/crossystem_arch.c')
-rw-r--r--host/arch/x86/lib/crossystem_arch.c591
1 files changed, 591 insertions, 0 deletions
diff --git a/host/arch/x86/lib/crossystem_arch.c b/host/arch/x86/lib/crossystem_arch.c
new file mode 100644
index 00000000..1e1f75b8
--- /dev/null
+++ b/host/arch/x86/lib/crossystem_arch.c
@@ -0,0 +1,591 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "host_common.h"
+
+#include "crossystem.h"
+#include "crossystem_arch.h"
+#include "utility.h"
+#include "vboot_common.h"
+#include "vboot_nvstorage.h"
+#include "vboot_struct.h"
+
+
+/* ACPI constants from Chrome OS Main Processor Firmware Spec */
+/* Boot reasons from BINF.0, from early H2C firmware */
+/* Unknown */
+#define BINF0_UNKNOWN 0
+/* Normal boot to Chrome OS */
+#define BINF0_NORMAL 1
+/* Developer mode boot (developer mode warning displayed) */
+#define BINF0_DEVELOPER 2
+/* Recovery initiated by user, using recovery button */
+#define BINF0_RECOVERY_BUTTON 3
+/* Recovery initiated by user pressing a key at developer mode warning
+ * screen */
+#define BINF0_RECOVERY_DEV_SCREEN_KEY 4
+/* Recovery caused by BIOS failed signature check (neither rewritable
+ * firmware was valid) */
+#define BINF0_RECOVERY_RW_FW_BAD 5
+/* Recovery caused by no OS kernel detected */
+#define BINF0_RECOVERY_NO_OS 6
+/* Recovery caused by OS kernel failed signature check */
+#define BINF0_RECOVERY_BAD_OS 7
+/* Recovery initiated by OS */
+#define BINF0_RECOVERY_OS_INITIATED 8
+/* OS-initiated S3 diagnostic path (debug mode boot) */
+#define BINF0_S3_DIAGNOSTIC_PATH 9
+/* S3 resume failed */
+#define BINF0_S3_RESUME_FAILED 10
+/* Recovery caused by TPM error */
+#define BINF0_RECOVERY_TPM_ERROR 11
+/* Firmware types from BINF.3 */
+#define BINF3_RECOVERY 0
+#define BINF3_NORMAL 1
+#define BINF3_DEVELOPER 2
+/* CHSW bitflags */
+#define CHSW_RECOVERY_BOOT 0x00000002
+#define CHSW_RECOVERY_EC_BOOT 0x00000004
+#define CHSW_DEV_BOOT 0x00000020
+#define CHSW_WP_BOOT 0x00000200
+/* CMOS reboot field bitflags */
+#define CMOSRF_RECOVERY 0x80
+#define CMOSRF_DEBUG_RESET 0x40
+#define CMOSRF_TRY_B 0x20
+/* GPIO signal types */
+#define GPIO_SIGNAL_TYPE_RECOVERY 1
+#define GPIO_SIGNAL_TYPE_DEV 2
+#define GPIO_SIGNAL_TYPE_WP 3
+
+/* Base name for ACPI files */
+#define ACPI_BASE_PATH "/sys/devices/platform/chromeos_acpi"
+/* Paths for frequently used ACPI files */
+#define ACPI_BINF_PATH ACPI_BASE_PATH "/BINF"
+#define ACPI_CHNV_PATH ACPI_BASE_PATH "/CHNV"
+#define ACPI_CHSW_PATH ACPI_BASE_PATH "/CHSW"
+#define ACPI_FMAP_PATH ACPI_BASE_PATH "/FMAP"
+#define ACPI_GPIO_PATH ACPI_BASE_PATH "/GPIO"
+#define ACPI_VBNV_PATH ACPI_BASE_PATH "/VBNV"
+#define ACPI_VDAT_PATH ACPI_BASE_PATH "/VDAT"
+
+/* Base name for GPIO files */
+#define GPIO_BASE_PATH "/sys/class/gpio"
+#define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
+
+/* Filename for NVRAM file */
+#define NVRAM_PATH "/dev/nvram"
+
+
+int VbReadNvStorage(VbNvContext* vnc) {
+ FILE* f;
+ int offs;
+
+ /* Get the byte offset from VBNV */
+ offs = ReadFileInt(ACPI_VBNV_PATH ".0");
+ if (offs == -1)
+ return -1;
+ if (VBNV_BLOCK_SIZE > ReadFileInt(ACPI_VBNV_PATH ".1"))
+ return -1; /* NV storage block is too small */
+
+ f = fopen(NVRAM_PATH, "rb");
+ if (!f)
+ return -1;
+
+ if (0 != fseek(f, offs, SEEK_SET) ||
+ 1 != fread(vnc->raw, VBNV_BLOCK_SIZE, 1, f)) {
+ fclose(f);
+ return -1;
+ }
+
+ fclose(f);
+ return 0;
+}
+
+
+int VbWriteNvStorage(VbNvContext* vnc) {
+ FILE* f;
+ int offs;
+
+ if (!vnc->raw_changed)
+ return 0; /* Nothing changed, so no need to write */
+
+ /* Get the byte offset from VBNV */
+ offs = ReadFileInt(ACPI_VBNV_PATH ".0");
+ if (offs == -1)
+ return -1;
+ if (VBNV_BLOCK_SIZE > ReadFileInt(ACPI_VBNV_PATH ".1"))
+ return -1; /* NV storage block is too small */
+
+ f = fopen(NVRAM_PATH, "w+b");
+ if (!f)
+ return -1;
+
+ if (0 != fseek(f, offs, SEEK_SET) ||
+ 1 != fwrite(vnc->raw, VBNV_BLOCK_SIZE, 1, f)) {
+ fclose(f);
+ return -1;
+ }
+
+ fclose(f);
+ return 0;
+}
+
+
+/*
+ * Get buffer data from ACPI.
+ *
+ * Buffer data is expected to be represented by a file which is a text dump of
+ * the buffer, representing each byte by two hex numbers, space and newline
+ * separated.
+ *
+ * On success, stores the amount of data read in bytes to *buffer_size; on
+ * erros, sets *buffer_size=0.
+ *
+ * Input - ACPI file name to get data from.
+ *
+ * Output: a pointer to AcpiBuffer structure containing the binary
+ * representation of the data. The caller is responsible for
+ * deallocating the pointer, this will take care of both the structure
+ * and the buffer. Null in case of error.
+ */
+static uint8_t* VbGetBuffer(const char* filename, int* buffer_size)
+{
+ FILE* f = NULL;
+ char* file_buffer = NULL;
+ uint8_t* output_buffer = NULL;
+ uint8_t* return_value = NULL;
+
+ /* Assume error until proven otherwise */
+ if (buffer_size)
+ *buffer_size = 0;
+
+ do {
+ struct stat fs;
+ uint8_t* output_ptr;
+ int rv, i, real_size;
+ int parsed_size = 0;
+
+ rv = stat(filename, &fs);
+ if (rv || !S_ISREG(fs.st_mode))
+ break;
+
+ f = fopen(filename, "r");
+ if (!f)
+ break;
+
+ file_buffer = Malloc(fs.st_size + 1);
+ if (!file_buffer)
+ break;
+
+ real_size = fread(file_buffer, 1, fs.st_size, f);
+ if (!real_size)
+ break;
+ file_buffer[real_size] = '\0';
+
+ /* Each byte in the output will replace two characters and a space
+ * in the input, so the output size does not exceed input side/3
+ * (a little less if account for newline characters). */
+ output_buffer = Malloc(real_size/3);
+ if (!output_buffer)
+ break;
+ output_ptr = output_buffer;
+
+ /* process the file contents */
+ for (i = 0; i < real_size; i++) {
+ char* base, *end;
+
+ base = file_buffer + i;
+
+ if (!isxdigit(*base))
+ continue;
+
+ output_ptr[parsed_size++] = strtol(base, &end, 16) & 0xff;
+
+ if ((end - base) != 2)
+ /* Input file format error */
+ break;
+
+ i += 2; /* skip the second character and the following space */
+ }
+
+ if (i == real_size) {
+ /* all is well */
+ return_value = output_buffer;
+ output_buffer = NULL; /* prevent it from deallocating */
+ if (buffer_size)
+ *buffer_size = parsed_size;
+ }
+ } while(0);
+
+ /* wrap up */
+ if (f)
+ fclose(f);
+
+ if (file_buffer)
+ Free(file_buffer);
+
+ if (output_buffer)
+ Free(output_buffer);
+
+ return return_value;
+}
+
+
+VbSharedDataHeader* VbSharedDataRead(void) {
+
+ VbSharedDataHeader* sh;
+ int got_size = 0;
+
+ sh = (VbSharedDataHeader*)VbGetBuffer(ACPI_VDAT_PATH, &got_size);
+ if (!sh)
+ return NULL;
+ if (got_size < sizeof(VbSharedDataHeader)) {
+ Free(sh);
+ return NULL;
+ }
+ if (sh->data_size > got_size)
+ sh->data_size = got_size; /* Truncated read */
+
+ return sh;
+}
+
+
+/* Read the CMOS reboot field in NVRAM.
+ *
+ * Returns 0 if the mask is clear in the field, 1 if set, or -1 if error. */
+static int VbGetCmosRebootField(uint8_t mask) {
+ FILE* f;
+ int chnv, nvbyte;
+
+ /* Get the byte offset from CHNV */
+ chnv = ReadFileInt(ACPI_CHNV_PATH);
+ if (chnv == -1)
+ return -1;
+
+ f = fopen(NVRAM_PATH, "rb");
+ if (!f)
+ return -1;
+
+ if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
+ fclose(f);
+ return -1;
+ }
+
+ fclose(f);
+ return (nvbyte & mask ? 1 : 0);
+}
+
+
+/* Write the CMOS reboot field in NVRAM.
+ *
+ * Sets (value=0) or clears (value!=0) the mask in the byte.
+ *
+ * Returns 0 if success, or -1 if error. */
+static int VbSetCmosRebootField(uint8_t mask, int value) {
+ FILE* f;
+ int chnv, nvbyte;
+
+ /* Get the byte offset from CHNV */
+ chnv = ReadFileInt(ACPI_CHNV_PATH);
+ if (chnv == -1)
+ return -1;
+
+ f = fopen(NVRAM_PATH, "w+b");
+ if (!f)
+ return -1;
+
+ /* Read the current value */
+ if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
+ fclose(f);
+ return -1;
+ }
+
+ /* Set/clear the mask */
+ if (value)
+ nvbyte |= mask;
+ else
+ nvbyte &= ~mask;
+
+ /* Write the byte back */
+ if (0 != fseek(f, chnv, SEEK_SET) || EOF == (fputc(nvbyte, f))) {
+ fclose(f);
+ return -1;
+ }
+
+ /* Success */
+ fclose(f);
+ return 0;
+}
+
+
+/* Read the active main firmware type into the destination buffer.
+ * Passed the destination and its size. Returns the destination, or
+ * NULL if error. */
+static const char* VbReadMainFwType(char* dest, int size) {
+
+ /* Try reading type from BINF.3 */
+ switch(ReadFileInt(ACPI_BINF_PATH ".3")) {
+ case BINF3_RECOVERY:
+ return StrCopy(dest, "recovery", size);
+ case BINF3_NORMAL:
+ return StrCopy(dest, "normal", size);
+ case BINF3_DEVELOPER:
+ return StrCopy(dest, "developer", size);
+ default:
+ break; /* Fall through to legacy handling */
+ }
+
+ /* Fall back to BINF.0 for legacy systems like Mario. */
+ switch(ReadFileInt(ACPI_BINF_PATH ".0")) {
+ case -1:
+ /* Both BINF.0 and BINF.3 are missing, so this isn't Chrome OS
+ * firmware. */
+ return StrCopy(dest, "nonchrome", size);
+ case BINF0_NORMAL:
+ return StrCopy(dest, "normal", size);
+ case BINF0_DEVELOPER:
+ return StrCopy(dest, "developer", size);
+ case BINF0_RECOVERY_BUTTON:
+ case BINF0_RECOVERY_DEV_SCREEN_KEY:
+ case BINF0_RECOVERY_RW_FW_BAD:
+ case BINF0_RECOVERY_NO_OS:
+ case BINF0_RECOVERY_BAD_OS:
+ case BINF0_RECOVERY_OS_INITIATED:
+ case BINF0_RECOVERY_TPM_ERROR:
+ /* Assorted flavors of recovery boot reason. */
+ return StrCopy(dest, "recovery", size);
+ default:
+ /* Other values don't map cleanly to firmware type. */
+ return NULL;
+ }
+}
+
+
+/* Read the recovery reason. Returns the reason code or -1 if error. */
+static int VbGetRecoveryReason(void) {
+ int value;
+
+ /* Try reading type from BINF.4 */
+ value = ReadFileInt(ACPI_BINF_PATH ".4");
+ if (-1 != value)
+ return value;
+
+ /* Fall back to BINF.0 for legacy systems like Mario. */
+ switch(ReadFileInt(ACPI_BINF_PATH ".0")) {
+ case BINF0_NORMAL:
+ case BINF0_DEVELOPER:
+ return VBNV_RECOVERY_NOT_REQUESTED;
+ case BINF0_RECOVERY_BUTTON:
+ return VBNV_RECOVERY_RO_MANUAL;
+ case BINF0_RECOVERY_DEV_SCREEN_KEY:
+ return VBNV_RECOVERY_RW_DEV_SCREEN;
+ case BINF0_RECOVERY_RW_FW_BAD:
+ case BINF0_RECOVERY_NO_OS:
+ return VBNV_RECOVERY_RW_NO_OS;
+ case BINF0_RECOVERY_BAD_OS:
+ return VBNV_RECOVERY_RW_INVALID_OS;
+ case BINF0_RECOVERY_OS_INITIATED:
+ return VBNV_RECOVERY_LEGACY;
+ default:
+ /* Other values don't map cleanly to firmware type. */
+ return -1;
+ }
+}
+
+
+/* Read a GPIO of the specified signal type (see ACPI GPIO SignalType).
+ *
+ * Returns 1 if the signal is asserted, 0 if not asserted, or -1 if error. */
+static int ReadGpio(int signal_type) {
+ char name[128];
+ int index = 0;
+ int gpio_type;
+ int active_high;
+ int controller_offset;
+ char controller_name[128];
+ int value;
+
+ /* Scan GPIO.* to find a matching signal type */
+ for (index = 0; ; index++) {
+ snprintf(name, sizeof(name), "%s.%d/GPIO.0", ACPI_GPIO_PATH, index);
+ gpio_type = ReadFileInt(name);
+ if (gpio_type == signal_type)
+ break;
+ else if (gpio_type == -1)
+ return -1; /* Ran out of GPIOs before finding a match */
+ }
+
+ /* Read attributes and controller info for the GPIO */
+ snprintf(name, sizeof(name), "%s.%d/GPIO.1", ACPI_GPIO_PATH, index);
+ active_high = ReadFileBit(name, 0x00000001);
+ snprintf(name, sizeof(name), "%s.%d/GPIO.2", ACPI_GPIO_PATH, index);
+ controller_offset = ReadFileInt(name);
+ if (active_high == -1 || controller_offset == -1)
+ return -1; /* Missing needed info */
+
+ /* We only support the NM10 for now */
+ snprintf(name, sizeof(name), "%s.%d/GPIO.3", ACPI_GPIO_PATH, index);
+ if (!ReadFileString(controller_name, sizeof(controller_name), name))
+ return -1;
+ if (0 != strcmp(controller_name, "NM10"))
+ return -1;
+
+ /* Assume the NM10 has offset 192 */
+ /* TODO: should really check gpiochipNNN/label to see if it's the
+ * address we expect for the NM10, and then read the offset from
+ * gpiochipNNN/base. */
+ controller_offset += 192;
+
+ /* Try reading the GPIO value */
+ snprintf(name, sizeof(name), "%s/gpio%d/value",
+ GPIO_BASE_PATH, controller_offset);
+ value = ReadFileInt(name);
+
+ if (value == -1) {
+ /* Try exporting the GPIO */
+ FILE* f = fopen(GPIO_EXPORT_PATH, "wt");
+ if (!f)
+ return -1;
+ fprintf(f, "%d", controller_offset);
+ fclose(f);
+
+ /* Try re-reading the GPIO value */
+ value = ReadFileInt(name);
+ }
+
+ if (value == -1)
+ return -1;
+
+ /* Compare the GPIO value with the active value and return 1 if match. */
+ return (value == active_high ? 1 : 0);
+}
+
+
+int VbGetArchPropertyInt(const char* name) {
+ int value = -1;
+
+ /* Values from ACPI */
+ if (!strcasecmp(name,"recovery_reason")) {
+ value = VbGetRecoveryReason();
+ } else if (!strcasecmp(name,"fmap_base")) {
+ value = ReadFileInt(ACPI_FMAP_PATH);
+ }
+ /* Switch positions */
+ else if (!strcasecmp(name,"devsw_cur")) {
+ value = ReadGpio(GPIO_SIGNAL_TYPE_DEV);
+ } else if (!strcasecmp(name,"recoverysw_cur")) {
+ value = ReadGpio(GPIO_SIGNAL_TYPE_RECOVERY);
+ } else if (!strcasecmp(name,"wpsw_cur")) {
+ value = ReadGpio(GPIO_SIGNAL_TYPE_WP);
+ if (-1 != value && FwidStartsWith("Mario."))
+ value = 1 - value; /* Mario reports this backwards */
+ } else if (!strcasecmp(name,"devsw_boot")) {
+ value = ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT);
+ } else if (!strcasecmp(name,"recoverysw_boot")) {
+ value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_BOOT);
+ } else if (!strcasecmp(name,"recoverysw_ec_boot")) {
+ value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_EC_BOOT);
+ } else if (!strcasecmp(name,"wpsw_boot")) {
+ value = ReadFileBit(ACPI_CHSW_PATH, CHSW_WP_BOOT);
+ if (-1 != value && FwidStartsWith("Mario."))
+ value = 1 - value; /* Mario reports this backwards */
+ }
+
+ /* Saved memory is at a fixed location for all H2C BIOS. If the CHSW
+ * path exists in sysfs, it's a H2C BIOS. */
+ else if (!strcasecmp(name,"savedmem_base")) {
+ return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00F00000);
+ } else if (!strcasecmp(name,"savedmem_size")) {
+ return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00100000);
+ }
+ /* NV storage values. If unable to get from NV storage, fall back to the
+ * CMOS reboot field used by older BIOS. */
+ else if (!strcasecmp(name,"recovery_request")) {
+ value = VbGetNvStorage(VBNV_RECOVERY_REQUEST);
+ if (-1 == value)
+ value = VbGetCmosRebootField(CMOSRF_RECOVERY);
+ } else if (!strcasecmp(name,"dbg_reset")) {
+ value = VbGetNvStorage(VBNV_DEBUG_RESET_MODE);
+ if (-1 == value)
+ value = VbGetCmosRebootField(CMOSRF_DEBUG_RESET);
+ } else if (!strcasecmp(name,"fwb_tries")) {
+ value = VbGetNvStorage(VBNV_TRY_B_COUNT);
+ if (-1 == value)
+ value = VbGetCmosRebootField(CMOSRF_TRY_B);
+ }
+
+ return value;
+}
+
+
+const char* VbGetArchPropertyString(const char* name, char* dest, int size) {
+
+ if (!strcasecmp(name,"hwid")) {
+ return ReadFileString(dest, size, ACPI_BASE_PATH "/HWID");
+ } else if (!strcasecmp(name,"fwid")) {
+ return ReadFileString(dest, size, ACPI_BASE_PATH "/FWID");
+ } else if (!strcasecmp(name,"ro_fwid")) {
+ return ReadFileString(dest, size, ACPI_BASE_PATH "/FRID");
+ } else if (!strcasecmp(name,"mainfw_act")) {
+ switch(ReadFileInt(ACPI_BINF_PATH ".1")) {
+ case 0:
+ return StrCopy(dest, "recovery", size);
+ case 1:
+ return StrCopy(dest, "A", size);
+ case 2:
+ return StrCopy(dest, "B", size);
+ default:
+ return NULL;
+ }
+ } else if (!strcasecmp(name,"mainfw_type")) {
+ return VbReadMainFwType(dest, size);
+ } else if (!strcasecmp(name,"ecfw_act")) {
+ switch(ReadFileInt(ACPI_BINF_PATH ".2")) {
+ case 0:
+ return StrCopy(dest, "RO", size);
+ case 1:
+ return StrCopy(dest, "RW", size);
+ default:
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+
+int VbSetArchPropertyInt(const char* name, int value) {
+ /* NV storage values. If unable to get from NV storage, fall back to the
+ * CMOS reboot field used by older BIOS. */
+ if (!strcasecmp(name,"recovery_request")) {
+ if (0 == VbSetNvStorage(VBNV_RECOVERY_REQUEST, value))
+ return 0;
+ return VbSetCmosRebootField(CMOSRF_RECOVERY, value);
+ } else if (!strcasecmp(name,"dbg_reset")) {
+ if (0 == VbSetNvStorage(VBNV_DEBUG_RESET_MODE, value))
+ return 0;
+ return VbSetCmosRebootField(CMOSRF_DEBUG_RESET, value);
+ } else if (!strcasecmp(name,"fwb_tries")) {
+ if (0 == VbSetNvStorage(VBNV_TRY_B_COUNT, value))
+ return 0;
+ return VbSetCmosRebootField(CMOSRF_TRY_B, value);
+ }
+
+ return -1;
+}
+
+
+int VbSetArchPropertyString(const char* name, const char* value) {
+ /* If there were settable architecture-dependent string properties,
+ * they'd be here. */
+ return -1;
+}