Print LoadKernel() debug data from VbSharedData

Change-Id: I60cf9c4dd07e83b1ed1a5bac8a3ce8c2a54df45b

R=reinauer@chromium.org
BUG=chrome-os-partner:2748
TEST=manually check output of 'crossystem vdat_lkdebug'

Review URL: http://codereview.chromium.org/6685097
diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h
index f82f7e9..f8fd8c8 100644
--- a/firmware/include/vboot_struct.h
+++ b/firmware/include/vboot_struct.h
@@ -203,6 +203,7 @@
   uint8_t gpt_index;                  /* Index of partition in GPT */
   uint8_t check_result;               /* Check result; see VBSD_LKP_CHECK_* */
   uint8_t flags;                      /* Flags (see VBSD_LKP_FLAG_* */
+  uint8_t reserved0;                  /* Reserved for padding */
 } VbSharedDataKernelPart;
 
 /* Number of kernel partitions to track per call.  Must be power of 2. */
@@ -234,6 +235,7 @@
   uint8_t test_error_num;             /* Test error number, if non-zero */
   uint8_t return_code;                /* Return code from LoadKernel() */
   uint8_t kernel_parts_found;         /* Number of kernel partitions found */
+  uint8_t reserved0[7];               /* Reserved for padding */
   VbSharedDataKernelPart parts[VBSD_MAX_KERNEL_PARTS]; /* Data on kernels */
 } VbSharedDataKernelCall;
 
@@ -261,6 +263,7 @@
   uint64_t data_size;                 /* Size of shared data buffer in bytes */
   uint64_t data_used;                 /* Amount of shared data used so far */
   uint32_t flags;                     /* Flags */
+  uint32_t reserved0;                 /* Reserved for padding */
 
   VbPublicKey kernel_subkey;          /* Kernel subkey, from firmware */
   uint64_t kernel_subkey_data_offset; /* Offset of kernel subkey data from
@@ -286,6 +289,7 @@
   uint8_t check_fw_b_result;          /* Result of checking RW firmware B */
   uint8_t firmware_index;             /* Firmware index returned by
                                        * LoadFirmware() or 0xFF if failure */
+  uint8_t reserved1;                  /* Reserved for padding */
   uint32_t fw_version_tpm_start;      /* Firmware TPM version at start of
                                        * LoadFirmware() */
   uint32_t fw_version_lowest;         /* Firmware lowest version found */
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index e841bad..5c5dec0 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -94,8 +94,9 @@
 
 /* Fields that GetVdatString() can get */
 typedef enum VdatStringField {
-  VDAT_STRING_TIMERS = 0,          /* Timer values */
-  VDAT_STRING_LOAD_FIRMWARE_DEBUG  /* LoadFirmware() debug information */
+  VDAT_STRING_TIMERS = 0,           /* Timer values */
+  VDAT_STRING_LOAD_FIRMWARE_DEBUG,  /* LoadFirmware() debug information */
+  VDAT_STRING_LOAD_KERNEL_DEBUG     /* LoadKernel() debug information */
 } VdatStringField;
 
 
@@ -618,10 +619,123 @@
 }
 
 
+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;
 
@@ -642,13 +756,11 @@
       break;
 
     case VDAT_STRING_LOAD_FIRMWARE_DEBUG:
-      snprintf(dest, size,
-               "check=%d,%d index=0x%02x tpmver=0x%x lowestver=0x%x",
-               sh->check_fw_a_result,
-               sh->check_fw_b_result,
-               sh->firmware_index,
-               sh->fw_version_tpm_start,
-               sh->fw_version_lowest);
+      value = GetVdatLoadFirmwareDebug(dest, size, sh);
+      break;
+
+    case VDAT_STRING_LOAD_KERNEL_DEBUG:
+      value = GetVdatLoadKernelDebug(dest, size, sh);
       break;
 
     default:
@@ -657,7 +769,7 @@
   }
 
   Free(ab);
-  return dest;
+  return value;
 }
 
 
@@ -819,6 +931,8 @@
     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;
 }
diff --git a/utility/crossystem_main.c b/utility/crossystem_main.c
index 6f352ed..53166e4 100644
--- a/utility/crossystem_main.c
+++ b/utility/crossystem_main.c
@@ -11,6 +11,9 @@
 
 #include "crossystem.h"
 
+/* Max length of a string parameter */
+#define MAX_STRING 8192
+
 /* Flags for Param */
 #define IS_STRING      0x01  /* String (not present = integer) */
 #define CAN_WRITE      0x02  /* Writable (not present = read-only */
@@ -52,7 +55,6 @@
   {"ecfw_act", IS_STRING, "Active EC firmware"},
   {"kernkey_vfy", IS_STRING, "Type of verification done on kernel key block"},
   {"vdat_timers", IS_STRING, "Timer values from VbSharedData"},
-  {"vdat_lfdebug", IS_STRING, "LoadFirmware() debug data VbSharedData"},
   /* Writable integers */
   {"nvram_cleared", CAN_WRITE, "Have NV settings been lost?  Write 0 to clear"},
   {"kern_nv", CAN_WRITE, "Non-volatile field for kernel use", "0x%08x"},
@@ -61,7 +63,11 @@
   {"fwb_tries", CAN_WRITE, "Try firmware B count (writable)"},
   {"vbtest_errfunc", CAN_WRITE, "Verified boot test error function (writable)"},
   {"vbtest_errno", CAN_WRITE, "Verified boot test error number (writable)"},
-
+  /* Fields not shown in a print-all list */
+  {"vdat_lfdebug", IS_STRING|NO_PRINT_ALL,
+   "LoadFirmware() debug data (not in print-all)"},
+  {"vdat_lkdebug", IS_STRING|NO_PRINT_ALL,
+   "LoadKernel() debug data (not in print-all)"},
   /* Terminate with null name */
   {NULL, 0, NULL}
 };
@@ -125,7 +131,7 @@
  * Returns 0 if success (match), non-zero if error (mismatch). */
 int CheckParam(const Param* p, char* expect) {
   if (p->flags & IS_STRING) {
-    char buf[256];
+    char buf[MAX_STRING];
     const char* v = VbGetSystemPropertyString(p->name, buf, sizeof(buf));
     if (!v || 0 != strcmp(v, expect))
       return 1;
@@ -147,7 +153,7 @@
  * Returns 0 if success, non-zero if error. */
 int PrintParam(const Param* p) {
   if (p->flags & IS_STRING) {
-    char buf[256];
+    char buf[MAX_STRING];
     const char* v = VbGetSystemPropertyString(p->name, buf, sizeof(buf));
     if (!v)
       return 1;
@@ -168,7 +174,7 @@
 int PrintAllParams(void) {
   const Param* p;
   int retval = 0;
-  char buf[256];
+  char buf[MAX_STRING];
   const char* value;
 
   for (p = sys_param_list; p->name; p++) {