Index: host/lib/crossystem.c |
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c |
index 04dc9756446878651dc0226ffa226e5c716fce38..5c5dec0d9541cb4304f6da6b0f5b123a960d7a7d 100644 |
--- a/host/lib/crossystem.c |
+++ b/host/lib/crossystem.c |
@@ -5,6 +5,10 @@ |
#include <stdio.h> |
#include <string.h> |
+#include <sys/types.h> |
+#include <sys/stat.h> |
+#include <unistd.h> |
+#include <ctype.h> |
#include "host_common.h" |
@@ -12,6 +16,7 @@ |
#include "utility.h" |
#include "vboot_common.h" |
#include "vboot_nvstorage.h" |
+#include "vboot_struct.h" |
/* ACPI constants from Chrome OS Main Processor Firmware Spec */ |
/* GPIO signal types */ |
@@ -68,6 +73,7 @@ |
#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" |
@@ -79,6 +85,31 @@ |
/* Filename for kernel command line */ |
#define KERNEL_CMDLINE_PATH "/proc/cmdline" |
+/* A structure to contain buffer data retrieved from the ACPI. */ |
+typedef struct { |
+ int buffer_size; |
+ uint8_t* buffer; |
+} AcpiBuffer; |
+ |
+ |
+/* Fields that GetVdatString() can get */ |
+typedef enum VdatStringField { |
+ VDAT_STRING_TIMERS = 0, /* Timer values */ |
+ VDAT_STRING_LOAD_FIRMWARE_DEBUG, /* LoadFirmware() debug information */ |
+ VDAT_STRING_LOAD_KERNEL_DEBUG /* LoadKernel() debug information */ |
+} VdatStringField; |
+ |
+ |
+/* Fields that GetVdatInt() can get */ |
+typedef enum VdatIntField { |
+ VDAT_INT_FLAGS = 0, /* Flags */ |
+ VDAT_INT_FW_VERSION_TPM, /* Current firmware version in TPM */ |
+ VDAT_INT_KERNEL_VERSION_TPM, /* Current kernel version in TPM */ |
+ VDAT_INT_TRIED_FIRMWARE_B, /* Tried firmware B due to fwb_tries */ |
+ VDAT_INT_KERNEL_KEY_VERIFIED /* Kernel key verified using |
+ * signature, not just hash */ |
+} VdatIntField; |
+ |
/* Copy up to dest_size-1 characters from src to dest, ensuring null |
termination (which strncpy() doesn't do). Returns the destination |
@@ -284,6 +315,97 @@ int VbSetCmosRebootField(uint8_t mask, int value) { |
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. |
+ * |
+ * 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. |
+ */ |
+ |
+AcpiBuffer* VbGetBuffer(const char* filename) |
+{ |
+ FILE* f = NULL; |
+ char* file_buffer = NULL; |
+ AcpiBuffer* acpi_buffer = NULL; |
+ AcpiBuffer* return_value = NULL; |
+ |
+ do { |
+ struct stat fs; |
+ uint8_t* output_ptr; |
+ int rv, i, real_size; |
+ |
+ 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). */ |
+ acpi_buffer = Malloc(sizeof(AcpiBuffer) + real_size/3); |
+ if (!acpi_buffer) |
+ break; |
+ acpi_buffer->buffer = (uint8_t*)(acpi_buffer + 1); |
+ acpi_buffer->buffer_size = 0; |
+ output_ptr = acpi_buffer->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[acpi_buffer->buffer_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 = acpi_buffer; |
+ acpi_buffer = NULL; /* prevent it from deallocating */ |
+ } |
+ } while(0); |
+ |
+ /* wrap up */ |
+ if (f) |
+ fclose(f); |
+ |
+ if (file_buffer) |
+ Free(file_buffer); |
+ |
+ if (acpi_buffer) |
+ Free(acpi_buffer); |
+ |
+ return return_value; |
+} |
/* Read an integer property from VbNvStorage. |
* |
@@ -497,6 +619,193 @@ int VbGetCrosDebug(void) { |
} |
+char* GetVdatLoadFirmwareDebug(char* dest, int size, |
+ const VbSharedDataHeader* sh) { |
+ snprintf(dest, size, |
+ "Check A result=%d\n" |
+ "Check B result=%d\n" |
+ "Firmware index booted=0x%02x\n" |
+ "TPM combined version at start=0x%08x\n" |
+ "Lowest combined version from firmware=0x%08x\n", |
+ sh->check_fw_a_result, |
+ sh->check_fw_b_result, |
+ sh->firmware_index, |
+ sh->fw_version_tpm_start, |
+ sh->fw_version_lowest); |
+ return dest; |
+} |
+ |
+ |
+#define TRUNCATED "\n(truncated)\n" |
+ |
+char* GetVdatLoadKernelDebug(char* dest, int size, |
+ const VbSharedDataHeader* sh) { |
+ int used = 0; |
+ int first_call_tracked = 0; |
+ int call; |
+ |
+ /* Make sure we have space for truncation warning */ |
+ if (size < strlen(TRUNCATED) + 1) |
+ return NULL; |
+ size -= strlen(TRUNCATED) + 1; |
+ |
+ used += snprintf( |
+ dest + used, size - used, |
+ "Calls to LoadKernel()=%d\n", |
+ sh->lk_call_count); |
+ if (used > size) |
+ goto LoadKernelDebugExit; |
+ |
+ /* Report on the last calls */ |
+ if (sh->lk_call_count > VBSD_MAX_KERNEL_CALLS) |
+ first_call_tracked = sh->lk_call_count - VBSD_MAX_KERNEL_CALLS; |
+ for (call = first_call_tracked; call < sh->lk_call_count; call++) { |
+ const VbSharedDataKernelCall* shc = |
+ sh->lk_calls + (call & (VBSD_MAX_KERNEL_CALLS - 1)); |
+ int first_part_tracked = 0; |
+ int part; |
+ |
+ used += snprintf( |
+ dest + used, size - used, |
+ "Call %d:\n" |
+ " Boot flags=0x%02x\n" |
+ " Boot mode=%d\n" |
+ " Test error=%d\n" |
+ " Return code=%d\n" |
+ " Debug flags=0x%02x\n" |
+ " Drive sectors=%" PRIu64 "\n" |
+ " Sector size=%d\n" |
+ " Check result=%d\n" |
+ " Kernel partitions found=%d\n", |
+ call + 1, |
+ shc->boot_flags, |
+ shc->boot_mode, |
+ shc->test_error_num, |
+ shc->return_code, |
+ shc->flags, |
+ shc->sector_count, |
+ shc->sector_size, |
+ shc->check_result, |
+ shc->kernel_parts_found); |
+ if (used > size) |
+ goto LoadKernelDebugExit; |
+ |
+ /* If we found too many partitions, only prints ones where the |
+ * structure has info. */ |
+ if (shc->kernel_parts_found > VBSD_MAX_KERNEL_PARTS) |
+ first_part_tracked = shc->kernel_parts_found - VBSD_MAX_KERNEL_PARTS; |
+ |
+ /* Report on the partitions checked */ |
+ for (part = first_part_tracked; part < shc->kernel_parts_found; part++) { |
+ const VbSharedDataKernelPart* shp = |
+ shc->parts + (part & (VBSD_MAX_KERNEL_PARTS - 1)); |
+ |
+ used += snprintf( |
+ dest + used, size - used, |
+ " Kernel %d:\n" |
+ " GPT index=%d\n" |
+ " Start sector=%" PRIu64 "\n" |
+ " Sector count=%" PRIu64 "\n" |
+ " Combined version=0x%08x\n" |
+ " Check result=%d\n" |
+ " Debug flags=0x%02x\n", |
+ part + 1, |
+ shp->gpt_index, |
+ shp->sector_start, |
+ shp->sector_count, |
+ shp->combined_version, |
+ shp->check_result, |
+ shp->flags); |
+ if (used > size) |
+ goto LoadKernelDebugExit; |
+ } |
+ } |
+ |
+LoadKernelDebugExit: |
+ |
+ /* Warn if data was truncated; we left space for this above. */ |
+ if (used > size) |
+ strcat(dest, TRUNCATED); |
+ |
+ return dest; |
+} |
+ |
+ |
+char* GetVdatString(char* dest, int size, VdatStringField field) |
+{ |
+ VbSharedDataHeader* sh; |
+ AcpiBuffer* ab = VbGetBuffer(ACPI_VDAT_PATH); |
+ char* value = dest; |
+ if (!ab) |
+ return NULL; |
+ |
+ sh = (VbSharedDataHeader*)ab->buffer; |
+ |
+ switch (field) { |
+ case VDAT_STRING_TIMERS: |
+ snprintf(dest, size, |
+ "LFS=%" PRIu64 ",%" PRIu64 |
+ " LF=%" PRIu64 ",%" PRIu64 |
+ " LK=%" PRIu64 ",%" PRIu64, |
+ sh->timer_load_firmware_start_enter, |
+ sh->timer_load_firmware_start_exit, |
+ sh->timer_load_firmware_enter, |
+ sh->timer_load_firmware_exit, |
+ sh->timer_load_kernel_enter, |
+ sh->timer_load_kernel_exit); |
+ break; |
+ |
+ case VDAT_STRING_LOAD_FIRMWARE_DEBUG: |
+ value = GetVdatLoadFirmwareDebug(dest, size, sh); |
+ break; |
+ |
+ case VDAT_STRING_LOAD_KERNEL_DEBUG: |
+ value = GetVdatLoadKernelDebug(dest, size, sh); |
+ break; |
+ |
+ default: |
+ Free(ab); |
+ return NULL; |
+ } |
+ |
+ Free(ab); |
+ return value; |
+} |
+ |
+ |
+int GetVdatInt(VdatIntField field) { |
+ VbSharedDataHeader* sh; |
+ AcpiBuffer* ab = VbGetBuffer(ACPI_VDAT_PATH); |
+ int value = -1; |
+ |
+ if (!ab) |
+ return -1; |
+ |
+ sh = (VbSharedDataHeader*)ab->buffer; |
+ |
+ switch (field) { |
+ case VDAT_INT_FLAGS: |
+ value = (int)sh->flags; |
+ break; |
+ case VDAT_INT_FW_VERSION_TPM: |
+ value = (int)sh->fw_version_tpm; |
+ break; |
+ case VDAT_INT_KERNEL_VERSION_TPM: |
+ value = (int)sh->kernel_version_tpm; |
+ break; |
+ case VDAT_INT_TRIED_FIRMWARE_B: |
+ value = (sh->flags & VBSD_FWB_TRIED ? 1 : 0); |
+ break; |
+ case VDAT_INT_KERNEL_KEY_VERIFIED: |
+ value = (sh->flags & VBSD_KERNEL_KEY_VERIFIED ? 1 : 0); |
+ break; |
+ } |
+ |
+ Free(ab); |
+ return value; |
+} |
+ |
+ |
/* Read a system property integer. |
* |
* Returns the property value, or -1 if error. */ |
@@ -531,12 +840,14 @@ int VbGetSystemPropertyInt(const char* name) { |
return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00100000); |
} |
/* NV storage values with no defaults for older BIOS. */ |
- else if (!strcasecmp(name,"tried_fwb")) { |
- value = VbGetNvStorage(VBNV_TRIED_FIRMWARE_B); |
- } else if (!strcasecmp(name,"kern_nv")) { |
+ else if (!strcasecmp(name,"kern_nv")) { |
value = VbGetNvStorage(VBNV_KERNEL_FIELD); |
} else if (!strcasecmp(name,"nvram_cleared")) { |
value = VbGetNvStorage(VBNV_KERNEL_SETTINGS_RESET); |
+ } else if (!strcasecmp(name,"vbtest_errfunc")) { |
+ value = VbGetNvStorage(VBNV_TEST_ERROR_FUNC); |
+ } else if (!strcasecmp(name,"vbtest_errno")) { |
+ value = VbGetNvStorage(VBNV_TEST_ERROR_NUM); |
} |
/* NV storage values. If unable to get from NV storage, fall back to the |
* CMOS reboot field used by older BIOS. */ |
@@ -560,12 +871,19 @@ int VbGetSystemPropertyInt(const char* name) { |
value = ReadFileInt(ACPI_FMAP_PATH); |
} else if (!strcasecmp(name,"cros_debug")) { |
value = VbGetCrosDebug(); |
+ } else if (!strcasecmp(name,"vdat_flags")) { |
+ value = GetVdatInt(VDAT_INT_FLAGS); |
+ } else if (!strcasecmp(name,"tpm_fwver")) { |
+ value = GetVdatInt(VDAT_INT_FW_VERSION_TPM); |
+ } else if (!strcasecmp(name,"tpm_kernver")) { |
+ value = GetVdatInt(VDAT_INT_KERNEL_VERSION_TPM); |
+ } else if (!strcasecmp(name,"tried_fwb")) { |
+ value = GetVdatInt(VDAT_INT_TRIED_FIRMWARE_B); |
} |
return value; |
} |
- |
/* Read a system property string into a destination buffer of the specified |
* size. |
* |
@@ -601,7 +919,7 @@ const char* VbGetSystemPropertyString(const char* name, char* dest, int size) { |
return NULL; |
} |
} else if (!strcasecmp(name,"kernkey_vfy")) { |
- switch(VbGetNvStorage(VBNV_FW_VERIFIED_KERNEL_KEY)) { |
+ switch(GetVdatInt(VDAT_INT_KERNEL_KEY_VERIFIED)) { |
case 0: |
return "hash"; |
case 1: |
@@ -609,6 +927,12 @@ const char* VbGetSystemPropertyString(const char* name, char* dest, int size) { |
default: |
return NULL; |
} |
+ } else if (!strcasecmp(name, "vdat_timers")) { |
+ return GetVdatString(dest, size, VDAT_STRING_TIMERS); |
+ } else if (!strcasecmp(name, "vdat_lfdebug")) { |
+ return GetVdatString(dest, size, VDAT_STRING_LOAD_FIRMWARE_DEBUG); |
+ } else if (!strcasecmp(name, "vdat_lkdebug")) { |
+ return GetVdatString(dest, size, VDAT_STRING_LOAD_KERNEL_DEBUG); |
} else |
return NULL; |
} |
@@ -625,6 +949,10 @@ int VbSetSystemPropertyInt(const char* name, int value) { |
return VbSetNvStorage(VBNV_KERNEL_SETTINGS_RESET, 0); |
} else if (!strcasecmp(name,"kern_nv")) { |
return VbSetNvStorage(VBNV_KERNEL_FIELD, value); |
+ } else if (!strcasecmp(name,"vbtest_errfunc")) { |
+ return VbSetNvStorage(VBNV_TEST_ERROR_FUNC, value); |
+ } else if (!strcasecmp(name,"vbtest_errno")) { |
+ return VbSetNvStorage(VBNV_TEST_ERROR_NUM, value); |
} |
/* NV storage values. If unable to get from NV storage, fall back to the |
* CMOS reboot field used by older BIOS. */ |