Add VbSharedData debug output to LoadKernel()

Change-Id: Id2837c83877520067e46e2434460349ef001b7b9

R=dlaurie@chromium.org
BUG=chrome-os-partner:2748
TEST=nmake uefi64; boot in normal and recovery modes

Review URL: http://codereview.chromium.org/6688035
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index 204874f..302a7f6 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -21,9 +21,9 @@
 #define LOWEST_TPM_VERSION 0xffffffff
 
 typedef enum BootMode {
-  kBootNormal,   /* Normal firmware */
-  kBootDev,      /* Dev firmware AND dev switch is on */
-  kBootRecovery  /* Recovery firmware, regardless of dev switch position */
+  kBootRecovery = 0,  /* Recovery firmware, regardless of dev switch position */
+  kBootNormal = 1,    /* Normal firmware */
+  kBootDev = 2        /* Dev firmware AND dev switch is on */
 } BootMode;
 
 
@@ -121,6 +121,7 @@
 
 int LoadKernel(LoadKernelParams* params) {
   VbSharedDataHeader* shared = (VbSharedDataHeader*)params->shared_data_blob;
+  VbSharedDataKernelCall* shcall = NULL;
   VbNvContext* vnc = params->nv_context;
   GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)params->gbb_data;
   VbPublicKey* kernel_subkey;
@@ -161,11 +162,51 @@
   params->bootloader_address = 0;
   params->bootloader_size = 0;
 
+  /* Calculate switch positions and boot mode */
+  rec_switch = (BOOT_FLAG_RECOVERY & params->boot_flags ? 1 : 0);
+  dev_switch = (BOOT_FLAG_DEVELOPER & params->boot_flags ? 1 : 0);
+  if (rec_switch)
+    boot_mode = kBootRecovery;
+  else if (BOOT_FLAG_DEV_FIRMWARE & params->boot_flags)
+    boot_mode = kBootDev;
+  else {
+    /* Normal firmware */
+    boot_mode = kBootNormal;
+    dev_switch = 0;  /* Always do a fully verified boot */
+  }
+
+  if (kBootRecovery == boot_mode) {
+    /* Initialize the shared data structure, since LoadFirmware() didn't do it
+     * for us. */
+    if (0 != VbSharedDataInit(shared, params->shared_data_size)) {
+      /* Error initializing the shared data, but we can keep going.  We just
+       * can't use the shared data. */
+      VBDEBUG(("Shared data init error\n"));
+      params->shared_data_size = 0;
+      shared = NULL;
+    }
+  }
+
+  if (shared) {
+    /* Set up tracking for this call.  This wraps around if called many times,
+     * so we need to initialize the call entry each time. */
+    shcall = shared->lk_calls + (shared->lk_call_count
+                                 & (VBSD_MAX_KERNEL_CALLS - 1));
+    Memset(shcall, 0, sizeof(VbSharedDataKernelCall));
+    shcall->boot_flags = (uint32_t)params->boot_flags;
+    shcall->boot_mode = boot_mode;
+    shcall->sector_size = (uint32_t)params->bytes_per_lba;
+    shcall->sector_count = params->ending_lba + 1;
+    shared->lk_call_count++;
+  }
+
   /* Handle test errors */
   VbNvGet(vnc, VBNV_TEST_ERROR_FUNC, &test_err);
   if (VBNV_TEST_ERROR_LOAD_KERNEL == test_err) {
     /* Get error code */
     VbNvGet(vnc, VBNV_TEST_ERROR_NUM, &test_err);
+    if (shcall)
+      shcall->test_error_num = (uint8_t)test_err;
     /* Clear test params so we don't repeat the error */
     VbNvSet(vnc, VBNV_TEST_ERROR_FUNC, 0);
     VbNvSet(vnc, VBNV_TEST_ERROR_NUM, 0);
@@ -192,43 +233,25 @@
     goto LoadKernelExit;
   }
 
-  rec_switch = (BOOT_FLAG_RECOVERY & params->boot_flags ? 1 : 0);
-  dev_switch = (BOOT_FLAG_DEVELOPER & params->boot_flags ? 1 : 0);
-
-  if (rec_switch)
-    boot_mode = kBootRecovery;
-  else if (BOOT_FLAG_DEV_FIRMWARE & params->boot_flags) {
-    if (!dev_switch) {
+  if (kBootDev == boot_mode && !dev_switch) {
       /* Dev firmware should be signed such that it never boots with the dev
        * switch is off; so something is terribly wrong. */
       VBDEBUG(("LoadKernel() called with dev firmware but dev switch off\n"));
+    if (shcall)
+      shcall->check_result = VBSD_LKC_CHECK_DEV_SWITCH_MISMATCH;
       recovery = VBNV_RECOVERY_RW_DEV_MISMATCH;
       goto LoadKernelExit;
     }
-    boot_mode = kBootDev;
-  } else {
-    /* Normal firmware */
-    boot_mode = kBootNormal;
-    dev_switch = 0;  /* Always do a fully verified boot */
-  }
 
   if (kBootRecovery == boot_mode) {
-    /* Initialize the shared data structure, since LoadFirmware() didn't do it
-     * for us. */
-    if (0 != VbSharedDataInit(shared, params->shared_data_size)) {
-      /* Error initializing the shared data, but we can keep going.  We just
-       * can't use the shared data. */
-      VBDEBUG(("Shared data init error\n"));
-      params->shared_data_size = 0;
-      shared = NULL;
-    }
-
     /* Use the recovery key to verify the kernel */
     kernel_subkey = (VbPublicKey*)((uint8_t*)gbb + gbb->recovery_key_offset);
 
     /* Let the TPM know if we're in recovery mode */
     if (0 != RollbackKernelRecovery(dev_switch)) {
       VBDEBUG(("Error setting up TPM for recovery kernel\n"));
+      if (shcall)
+        shcall->flags |= VBSD_LK_FLAG_REC_TPM_INIT_ERROR;
       /* Ignore return code, since we need to boot recovery mode to
        * fix the TPM. */
     }
@@ -263,12 +286,16 @@
     gpt.drive_sectors = params->ending_lba + 1;
     if (0 != AllocAndReadGptData(&gpt)) {
       VBDEBUG(("Unable to read GPT data\n"));
+      if (shcall)
+        shcall->check_result = VBSD_LKC_CHECK_GPT_READ_ERROR;
       break;
     }
 
     /* Initialize GPT library */
     if (GPT_SUCCESS != GptInit(&gpt)) {
       VBDEBUG(("Error parsing GPT\n"));
+      if (shcall)
+        shcall->check_result = VBSD_LKC_CHECK_GPT_PARSE_ERROR;
       break;
     }
 
@@ -279,6 +306,7 @@
 
     /* Loop over candidate kernel partitions */
     while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) {
+      VbSharedDataKernelPart* shpart = NULL;
       VbKeyBlockHeader* key_block;
       VbKernelPreambleHeader* preamble;
       RSAPublicKey* data_key = NULL;
@@ -292,17 +320,35 @@
       VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n",
               part_start, part_size));
 
+      if (shcall) {
+        /* Set up tracking for this partition.  This wraps around if called
+         * many times, so initialize the partition entry each time. */
+        shpart = shcall->parts + (shcall->kernel_parts_found
+                                 & (VBSD_MAX_KERNEL_PARTS - 1));
+        Memset(shpart, 0, sizeof(VbSharedDataKernelPart));
+        shpart->sector_start = part_start;
+        shpart->sector_count = part_size;
+        /* TODO: GPT partitions start at 1, but cgptlib starts them at 0.
+         * Adjust here, until cgptlib is fixed. */
+        shpart->gpt_index = (uint8_t)(gpt.current_kernel + 1);
+        shcall->kernel_parts_found++;
+      }
+
       /* Found at least one kernel partition. */
       found_partitions++;
 
       /* Read the first part of the kernel partition. */
       if (part_size < kbuf_sectors) {
         VBDEBUG(("Partition too small to hold kernel.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_TOO_SMALL;
         goto bad_kernel;
       }
 
       if (0 != BootDeviceReadLBA(part_start, kbuf_sectors, kbuf)) {
         VBDEBUG(("Unable to read start of partition.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_READ_START;
         goto bad_kernel;
       }
 
@@ -310,6 +356,9 @@
       key_block = (VbKeyBlockHeader*)kbuf;
       if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 0)) {
         VBDEBUG(("Verifying key block signature failed.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_SIG;
+
         key_block_valid = 0;
 
         /* If we're not in developer mode, this kernel is bad. */
@@ -320,6 +369,8 @@
          * block is valid. */
         if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 1)) {
           VBDEBUG(("Verifying key block hash failed.\n"));
+          if (shpart)
+            shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_HASH;
           goto bad_kernel;
         }
       }
@@ -329,12 +380,16 @@
             (dev_switch ? KEY_BLOCK_FLAG_DEVELOPER_1 :
              KEY_BLOCK_FLAG_DEVELOPER_0))) {
         VBDEBUG(("Key block developer flag mismatch.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_DEV_MISMATCH;
         key_block_valid = 0;
       }
       if (!(key_block->key_block_flags &
             (rec_switch ? KEY_BLOCK_FLAG_RECOVERY_1 :
              KEY_BLOCK_FLAG_RECOVERY_0))) {
         VBDEBUG(("Key block recovery flag mismatch.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_REC_MISMATCH;
         key_block_valid = 0;
       }
 
@@ -343,6 +398,8 @@
       if (kBootRecovery != boot_mode) {
         if (key_version < (tpm_version >> 16)) {
           VBDEBUG(("Key version too old.\n"));
+          if (shpart)
+            shpart->check_result = VBSD_LKP_CHECK_KEY_ROLLBACK;
           key_block_valid = 0;
         }
       }
@@ -357,6 +414,8 @@
       data_key = PublicKeyToRSA(&key_block->data_key);
       if (!data_key) {
         VBDEBUG(("Data key bad.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_DATA_KEY_PARSE;
         goto bad_kernel;
       }
 
@@ -366,6 +425,8 @@
                                      KBUF_SIZE - key_block->key_block_size,
                                      data_key))) {
         VBDEBUG(("Preamble verification failed.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_VERIFY_PREAMBLE;
         goto bad_kernel;
       }
 
@@ -373,9 +434,13 @@
        * rollback of the kernel version. */
       combined_version = ((key_version << 16) |
                           (preamble->kernel_version & 0xFFFF));
+      if (shpart)
+        shpart->combined_version = (uint32_t)combined_version;
       if (key_block_valid && kBootRecovery != boot_mode) {
         if (combined_version < tpm_version) {
           VBDEBUG(("Kernel version too low.\n"));
+          if (shpart)
+            shpart->check_result = VBSD_LKP_CHECK_KERNEL_ROLLBACK;
           /* If we're not in developer mode, kernel version must be valid. */
           if (kBootDev != boot_mode)
             goto bad_kernel;
@@ -383,6 +448,8 @@
       }
 
       VBDEBUG(("Kernel preamble is good.\n"));
+      if (shpart)
+        shpart->check_result = VBSD_LKP_CHECK_PREAMBLE_VALID;
 
       /* Check for lowest version from a valid header. */
       if (key_block_valid && lowest_version > combined_version)
@@ -402,6 +469,8 @@
       if ((preamble->body_load_address != (size_t)params->kernel_buffer) &&
           !(params->boot_flags & BOOT_FLAG_SKIP_ADDR_CHECK)) {
         VBDEBUG(("Wrong body load address.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_BODY_ADDRESS;
         goto bad_kernel;
       }
 
@@ -409,6 +478,8 @@
       body_offset = key_block->key_block_size + preamble->preamble_size;
       if (0 != body_offset % blba) {
         VBDEBUG(("Kernel body not at multiple of sector size.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_BODY_OFFSET;
         goto bad_kernel;
       }
       body_offset_sectors = body_offset / blba;
@@ -417,12 +488,16 @@
       body_sectors = (preamble->body_signature.data_size + blba - 1) / blba;
       if (body_sectors * blba > params->kernel_buffer_size) {
         VBDEBUG(("Kernel body doesn't fit in memory.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_MEM;
         goto bad_kernel;
       }
 
       /* Verify kernel body fits in the partition */
       if (body_offset_sectors + body_sectors > part_size) {
         VBDEBUG(("Kernel body doesn't fit in partition.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_PART;
         goto bad_kernel;
       }
 
@@ -433,6 +508,8 @@
                                  params->kernel_buffer)) {
         VBDEBUG(("Unable to read kernel data.\n"));
         VBPERFEND("VB_RKD");
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_READ_DATA;
         goto bad_kernel;
       }
       VBPERFEND("VB_RKD");
@@ -442,6 +519,8 @@
                           params->kernel_buffer_size,
                           &preamble->body_signature, data_key)) {
         VBDEBUG(("Kernel data verification failed.\n"));
+        if (shpart)
+          shpart->check_result = VBSD_LKP_CHECK_VERIFY_DATA;
         goto bad_kernel;
       }
 
@@ -452,6 +531,12 @@
       /* If we're still here, the kernel is valid. */
       /* Save the first good partition we find; that's the one we'll boot */
       VBDEBUG(("Partition is good.\n"));
+      if (shpart) {
+        shpart->check_result = VBSD_LKP_CHECK_KERNEL_GOOD;
+        if (key_block_valid)
+          shpart->flags |= VBSD_LKP_FLAG_KEY_BLOCK_VALID;
+      }
+
       good_partition_key_block_valid = key_block_valid;
       /* TODO: GPT partitions start at 1, but cgptlib starts them at 0.
        * Adjust here, until cgptlib is fixed. */
@@ -508,6 +593,8 @@
   /* Handle finding a good partition */
   if (good_partition >= 0) {
     VBDEBUG(("Good_partition >= 0\n"));
+    if (shcall)
+      shcall->check_result = VBSD_LKC_CHECK_GOOD_PARTITION;
 
     /* See if we need to update the TPM */
     if (kBootRecovery != boot_mode && good_partition_key_block_valid) {
@@ -551,6 +638,11 @@
     /* Success! */
     retval = LOAD_KERNEL_SUCCESS;
   } else {
+    if (shcall)
+      shcall->check_result = (found_partitions > 0
+                              ? VBSD_LKC_CHECK_INVALID_PARTITIONS
+                              : VBSD_LKC_CHECK_NO_PARTITIONS);
+
     /* TODO: differentiate between finding an invalid kernel
      * (found_partitions>0) and not finding one at all.  Right now we
      * treat them the same, and return LOAD_KERNEL_INVALID for both. */
@@ -568,6 +660,13 @@
   VbNvTeardown(vnc);
 
   if (shared) {
+    if (shcall)
+      shcall->return_code = (uint8_t)retval;
+
+    /* Save whether the good partition's key block was fully verified */
+    if (good_partition_key_block_valid)
+      shared->flags |= VBSD_KERNEL_KEY_VERIFIED;
+
     /* Save timer values */
     shared->timer_load_kernel_enter = timer_enter;
     shared->timer_load_kernel_exit = VbGetTimer();