Introduce ability to change the kernel command line.

After this change vbutil_kernel allows to repack an existing
signed ChromeOS kernel such that the kernel command line is
changed on operator's request.

The new command line parameter is --verbose which causes
--verify to print out current contents of the kernel
command line.

Some refactoring and cleaning were also done:
 - provide a macro to access command line buffer inside
   a kernel blob
 - ReadConfigFile() a new wrapper to preprocess the
   config file.
 - keep the key_block and preamble in the blob when
   unpacking an existing signed kernel for --repack and
   --verify.
 - make --pack expect at least one of the two:
   --config or --keyblock, thus allowing to change the
   command line without replacing anything else in the
   signed kernel image.
 - refactor Verify() to use OldBlob() to preprocess the
   image.

The top level Makefile was changed to allow compiling for debugging.

Build with DEBUG=1 in the make command line to enable gdb debugging and debug printouts. Build with DISABLE_NDEBUG=1 in the make command line to enable cryptolib debug outputs.

BUG=http://code.google.com/p/chromium-os/issues/detail?id=4814

TEST=see below

1. Observe that all unit tests still pass by running

(vboot_reference $) RUNTESTS=1 make

2. On a working DVT system copy the running kernel into a
file using

dd if=/dev/sda2 of=/tmp/dev.kernel

and transfer the file to the host into /tmp/try/dev.kernel

Then create the new config file in /tmp/try/new.conf.txt and run the following commands:
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
(vboot_reference $) ./build/utility/vbutil_kernel  --verify /tmp/try/dev.kernel  --signpubkey tests/devkeys/kernel_subkey.vbpubk --verbose
Key block:
  Size:                0x4b8
  Data key algorithm:  4 RSA2048 SHA256
  Data key version:    1
  Flags:               7
Preamble:
  Size:                0xfb48
  Header version:      2.0
  Kernel version:      1
  Body load address:   0x100000
  Body size:           0x302000
  Bootloader address:  0x3fe000
  Bootloader size:     0x4000
Body verification succeeded.
Config:
earlyprintk=serial,ttyS0,115200 console=ttyS0,115200 init=/sbin/init add_efi_memmap boot=local rootwait ro noresume noswap i915.modeset=1 loglevel=7 cros_secure root=/dev/sd%D%P dm_verity.error_behavior=2 dm_verity.max_bios=1024 dm="0 2097152 verity ROOT_DEV HASH_DEV 2097152 1 sha1 a7fbd641ba25488509987959d5756d802790ef8f" noinitrd

(vboot_reference $)   ./build/utility/vbutil_kernel  --repack /tmp/try/dev.kernel.repacked  --signprivate tests/devkeys/kernel_data_key.vbprivk  --oldblob /tmp/try/dev.kernel --config /tmp/try/new.conf.txt
(vboot_reference $)  ./build/utility/vbutil_kernel  --verify /tmp/try/dev.kernel.repacked  --signpubkey tests/devkeys/kernel_subkey.vbpubk --verbose
Key block:
  Size:                0x4b8
  Data key algorithm:  4 RSA2048 SHA256
  Data key version:    1
  Flags:               7
Preamble:
  Size:                0xfb48
  Header version:      2.0
  Kernel version:      1
  Body load address:   0x100000
  Body size:           0x302000
  Bootloader address:  0x3fe000
  Bootloader size:     0x4000
Body verification succeeded.
Config:
console=tty2 init=/sbin/init add_efi_memmap boot=local rootwait ro noresume noswap i915.modeset=1 loglevel=7 cros_secure root=/dev/sd%D%P dm_verity.error_behavior=2 dm_verity.max_bios=1024 dm="0 2097152 verity ROOT_DEV HASH_DEV 2097152 1 sha1 ff06384015a7726baff719ee68eab312b1d45570" noinitrd
(vboot_reference $)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Observe the chanegd command line printed by --verify --verbose. Then transfer the new kernel image back to the DVT system, dd it into /dev/sda2 and restart the DVT system.

Observe kernel startup messages dumped on the screen (due to the changed kernel command line).

Then examine /proc/cmdline to verify that the command line indeed matches the contents of /tmp/try/new.conf.txt on the host.

3. Build the code with

(vboot_reference$) DEBUG=1 make

 observe that debug information is visible by gdb.

  Build the code with

(vboot_reference$) DISABLE_DEBUG=1 make

and observe that  -DNDEBUG is dropped from the compiler invocation line.

Review URL: http://codereview.chromium.org/3004001
diff --git a/utility/vbutil_kernel.c b/utility/vbutil_kernel.c
index 349cc8e..d688e61 100644
--- a/utility/vbutil_kernel.c
+++ b/utility/vbutil_kernel.c
@@ -43,6 +43,7 @@
   OPT_CONFIG,
   OPT_VBLOCKONLY,
   OPT_PAD,
+  OPT_VERBOSE,
 };
 
 static struct option long_opts[] = {
@@ -59,6 +60,7 @@
   {"config", 1, 0,                    OPT_CONFIG                  },
   {"vblockonly", 0, 0,                OPT_VBLOCKONLY              },
   {"pad", 1, 0,                       OPT_PAD                     },
+  {"verbose", 0, 0,                   OPT_VERBOSE                 },
   {"debug", 0, &opt_debug, 1                                      },
   {NULL, 0, 0, 0}
 };
@@ -78,7 +80,7 @@
           "    --version <number>        Kernel version\n"
           "    --vmlinuz <file>          Linux kernel bzImage file\n"
           "    --bootloader <file>       Bootloader stub\n"
-          "    --config <file>           Config file\n"
+          "    --config <file>           Command line file\n"
           "\n"
           "  Optional:\n"
           "    --pad <number>            Verification padding size in bytes\n"
@@ -88,10 +90,12 @@
           "\nOR\n\n"
           "Usage:  %s --repack <file> [PARAMETERS]\n"
           "\n"
-          "  Required parameters:\n"
+          "  Required parameters (of --keyblock and --config at least "
+          "one is required):\n"
           "    --keyblock <file>         Key block in .keyblock format\n"
           "    --signprivate <file>      Signing private key in .pem format\n"
           "    --oldblob <file>          Previously packed kernel blob\n"
+          "    --config <file>           New command line file\n"
           "\n"
           "  Optional:\n"
           "    --pad <number>            Verification padding size in bytes\n"
@@ -103,6 +107,9 @@
           "\n"
           "  Required parameters:\n"
           "    --signpubkey <file>       Signing public key in .vbpubk format\n"
+          "\n"
+          "  Optional:\n"
+          "    --verbose                 Print a more detailed report\n"
           "\n",
           progname);
   return 1;
@@ -161,17 +168,60 @@
   /* Raw kernel blob data */
   uint64_t blob_size;
   uint8_t *blob;
+
+  /* these fields are not always initialized */
+  VbKernelPreambleHeader* preamble;
+  VbKeyBlockHeader* key_block;
+  uint8_t *buf;
+
 } blob_t;
 
+/* Given a blob return the location of the kernel command line buffer. */
+static char* BpCmdLineLocation(blob_t *bp)
+{
+  return (char*)(bp->blob + bp->bootloader_address - CROS_32BIT_ENTRY_ADDR -
+                 CROS_CONFIG_SIZE - CROS_PARAMS_SIZE);
+}
 
 static void FreeBlob(blob_t *bp) {
   if (bp) {
     if (bp->blob)
       Free(bp->blob);
+    if (bp->buf)
+      Free(bp->buf);
     Free(bp);
   }
 }
 
+/*
+ * Read the kernel command line from a file. Get rid of \n characters along
+ * the way and verify that the line fits into a 4K buffer.
+ *
+ * Return the buffer contaning the line on success (and set the line length
+ * using the passed in parameter), or NULL in case something goes wrong.
+ */
+static uint8_t* ReadConfigFile(const char* config_file, uint64_t* config_size)
+{
+  uint8_t* config_buf;
+  int ii;
+
+  config_buf = ReadFile(config_file, config_size);
+  Debug(" config file size=0x%" PRIx64 "\n", *config_size);
+  if (CROS_CONFIG_SIZE <= *config_size) {  /* need room for trailing '\0' */
+    error("Config file %s is too large (>= %d bytes)\n",
+          config_file, CROS_CONFIG_SIZE);
+    return NULL;
+  }
+
+  /* Replace newlines with spaces */
+  for (ii = 0; ii < *config_size; ii++) {
+    if ('\n' == config_buf[ii]) {
+      config_buf[ii] = ' ';
+    }
+  }
+  return config_buf;
+}
+
 /* Create a blob from its components */
 static blob_t *NewBlob(uint64_t version,
                        const char* vmlinuz,
@@ -191,7 +241,6 @@
   uint32_t cmdline_addr;
   uint8_t* blob = NULL;
   uint64_t now = 0;
-  uint64_t i;
 
   if (!vmlinuz || !bootloader_file || !config_file) {
     error("Must specify all input files\n");
@@ -203,23 +252,15 @@
     error("Couldn't allocate bytes for blob_t.\n");
     return 0;
   }
+
+  Memset(bp, 0, sizeof(*bp));
   bp->kernel_version = version;
 
   /* Read the config file */
   Debug("Reading %s\n", config_file);
-  config_buf = ReadFile(config_file, &config_size);
+  config_buf = ReadConfigFile(config_file, &config_size);
   if (!config_buf)
     return 0;
-  Debug(" config file size=0x%" PRIx64 "\n", config_size);
-  if (CROS_CONFIG_SIZE <= config_size) {  /* need room for trailing '\0' */
-    error("Config file %s is too large (>= %d bytes)\n",
-          config_file, CROS_CONFIG_SIZE);
-    return 0;
-  }
-  /* Replace newlines with spaces */
-  for (i = 0; i < config_size; i++)
-    if ('\n' == config_buf[i])
-      config_buf[i] = ' ';
 
   /* Read the bootloader */
   Debug("Reading %s\n", bootloader_file);
@@ -319,13 +360,14 @@
 
 /* Pull the blob_t stuff out of a prepacked kernel blob file */
 static blob_t *OldBlob(const char* filename) {
-  FILE* fp;
-  blob_t *bp;
+  FILE* fp = NULL;
+  blob_t *bp = NULL;
   struct stat statbuf;
   VbKeyBlockHeader* key_block;
   VbKernelPreambleHeader* preamble;
   uint64_t now = 0;
-  uint8_t buf[DEFAULT_PADDING];
+  uint8_t* buf = NULL;
+  int ret_error = 1;
 
   if (!filename) {
     error("Must specify prepacked blob to read\n");
@@ -350,10 +392,15 @@
     return 0;
   }
 
-  if (1 != fread(buf, sizeof(buf), 1, fp)) {
+  buf = Malloc(DEFAULT_PADDING);
+  if (!buf) {
+    error("Unable to allocate padding\n");
+    goto unwind_oldblob;
+  }
+
+  if (1 != fread(buf, DEFAULT_PADDING, 1, fp)) {
     error("Unable to read header from %s: %s\n", filename, strerror(errno));
-    fclose(fp);
-    return 0;
+    goto unwind_oldblob;
   }
 
   /* Skip the key block */
@@ -362,7 +409,7 @@
   now += key_block->key_block_size;
   if (now > statbuf.st_size) {
     error("key_block_size advances past the end of the blob\n");
-    return 0;
+    goto unwind_oldblob;
   }
 
   /* Skip the preamble */
@@ -371,7 +418,7 @@
   now += preamble->preamble_size;
   if (now > statbuf.st_size) {
     error("preamble_size advances past the end of the blob\n");
-    return 0;
+    goto unwind_oldblob;
   }
 
   /* Go find the kernel blob */
@@ -379,18 +426,20 @@
   if (0 != fseek(fp, now, SEEK_SET)) {
     error("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename,
           strerror(errno));
-    fclose(fp);
-    return 0;
+    goto unwind_oldblob;
   }
 
   /* Remember what we've got */
   bp = (blob_t *)Malloc(sizeof(blob_t));
   if (!bp) {
     error("Couldn't allocate bytes for blob_t.\n");
-    fclose(fp);
-    return 0;
+    goto unwind_oldblob;
   }
 
+  bp->buf = buf;
+  bp->key_block = key_block;
+  bp->preamble = preamble;
+
   bp->kernel_version = preamble->kernel_version;
   bp->bootloader_address = preamble->bootloader_address;
   bp->bootloader_size = preamble->bootloader_size;
@@ -404,22 +453,28 @@
   bp->blob = (uint8_t *)Malloc(bp->blob_size);
   if (!bp->blob) {
     error("Couldn't allocate 0x%" PRIx64 " bytes for blob_t.\n", bp->blob_size);
-    fclose(fp);
-    Free(bp);
-    return 0;
+    goto unwind_oldblob;
   }
 
   /* read it in */
   if (1 != fread(bp->blob, bp->blob_size, 1, fp)) {
     error("Unable to read kernel blob from %s: %s\n", filename, strerror(errno));
-    fclose(fp);
-    Free(bp);
-    return 0;
+    goto unwind_oldblob;
   }
 
-  /* done */
-  fclose(fp);
+  ret_error = 0;
 
+  /* done */
+unwind_oldblob:
+  fclose(fp);
+  if (ret_error) {
+    if (bp) {
+      FreeBlob(bp);
+      bp = NULL;
+    } else if (buf) {
+      Free(buf);
+    }
+  }
   return bp;
 }
 
@@ -440,21 +495,27 @@
     error("Must specify output filename\n");
     return 1;
   }
-  if (!keyblock_file || !signprivate) {
+  if ((!keyblock_file && !bp->key_block) || !signprivate) {
     error("Must specify all keys\n");
     return 1;
   }
   if (!bp) {
     error("Refusing to pack invalid kernel blob\n");
     return 1;
-  }    
-
-  /* Read the key block and private key */
-  key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
-  if (!key_block) {
-    error("Error reading key block.\n");
-    return 1;
   }
+
+  /* Get the key block and read the private key. */
+  if (keyblock_file) {
+    key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
+    if (!key_block) {
+      error("Error reading key block.\n");
+      return 1;
+    }
+  } else {
+    key_block = bp->key_block;
+    key_block_size = key_block->key_block_size;
+  }
+
   if (pad < key_block->key_block_size) {
     error("Pad too small\n");
     return 1;
@@ -521,17 +582,40 @@
   return 0;
 }
 
+/*
+ * Replace kernel command line in a blob representing a kernel.
+ */
+static int ReplaceConfig(blob_t* bp, const char* config_file)
+{
+  uint8_t* new_conf;
+  uint64_t config_size;
 
-static int Verify(const char* infile, const char* signpubkey) {
+  if (!config_file) {
+    return 0;
+  }
+
+  new_conf = ReadConfigFile(config_file, &config_size);
+  if (!new_conf) {
+    return 1;
+  }
+
+  /* fill the config buffer with zeros */
+  Memset(BpCmdLineLocation(bp), 0, CROS_CONFIG_SIZE);
+  Memcpy(BpCmdLineLocation(bp), new_conf, config_size);
+  Free(new_conf);
+  return 0;
+}
+
+static int Verify(const char* infile, const char* signpubkey, int verbose) {
 
   VbKeyBlockHeader* key_block;
   VbKernelPreambleHeader* preamble;
   VbPublicKey* data_key;
   VbPublicKey* sign_key;
   RSAPublicKey* rsa;
-  uint8_t* blob;
-  uint64_t blob_size;
-  uint64_t now = 0;
+  blob_t* bp;
+  uint64_t now;
+  int rv = 1;
 
   if (!infile || !signpubkey) {
     error("Must specify filename and signpubkey\n");
@@ -546,20 +630,19 @@
   }
 
   /* Read blob */
-  blob = ReadFile(infile, &blob_size);
-  if (!blob) {
+  bp = OldBlob(infile);
+  if (!bp) {
     error("Error reading input file\n");
     return 1;
   }
 
   /* Verify key block */
-  key_block = (VbKeyBlockHeader*)blob;
-  if (0 != KeyBlockVerify(key_block, blob_size, sign_key)) {
+  key_block = bp->key_block;
+  if (0 != KeyBlockVerify(key_block, bp->blob_size, sign_key)) {
     error("Error verifying key block.\n");
-    return 1;
+    goto verify_exit;
   }
-  Free(sign_key);
-  now += key_block->key_block_size;
+  now = key_block->key_block_size;
 
   printf("Key block:\n");
   data_key = &key_block->data_key;
@@ -573,14 +656,15 @@
   rsa = PublicKeyToRSA(&key_block->data_key);
   if (!rsa) {
     error("Error parsing data key.\n");
-    return 1;
+    goto verify_exit;
   }
 
   /* Verify preamble */
-  preamble = (VbKernelPreambleHeader*)(blob + now);
-  if (0 != VerifyKernelPreamble2(preamble, blob_size - now, rsa)) {
+  preamble = bp->preamble;
+  if (0 != VerifyKernelPreamble2(
+          preamble, bp->blob_size - key_block->key_block_size, rsa)) {
     error("Error verifying preamble.\n");
-    return 1;
+    goto verify_exit;
   }
   now += preamble->preamble_size;
 
@@ -596,12 +680,23 @@
   printf("  Bootloader size:     0x%" PRIx64 "\n", preamble->bootloader_size);
 
   /* Verify body */
-  if (0 != VerifyData(blob + now, &preamble->body_signature, rsa)) {
+  if (0 != VerifyData(bp->blob, &preamble->body_signature, rsa)) {
     error("Error verifying kernel body.\n");
-    return 1;
+    goto verify_exit;
   }
   printf("Body verification succeeded.\n");
-  return 0;
+
+  rv = 0;
+
+  if (!verbose) {
+    goto verify_exit;
+  }
+
+  printf("Config:\n%s\n", BpCmdLineLocation(bp));
+
+verify_exit:
+  FreeBlob(bp);
+  return rv;
 }
 
 
@@ -616,6 +711,7 @@
   char* bootloader = NULL;
   char* config_file = NULL;
   int vblockonly = 0;
+  int verbose = 0;
   uint64_t pad = DEFAULT_PADDING;
   int mode = 0;
   int parse_error = 0;
@@ -630,8 +726,10 @@
   else
     progname = argv[0];
 
-  while ((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) {
+  while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
+         !parse_error) {
     switch (i) {
+      default:
       case '?':
         /* Unhandled option */
         parse_error = 1;
@@ -640,6 +738,11 @@
       case OPT_MODE_PACK:
       case OPT_MODE_REPACK:
       case OPT_MODE_VERIFY:
+        if (mode && (mode != i)) {
+          fprintf(stderr, "Only single mode can be specified\n");
+          parse_error = 1;
+          break;
+        }
         mode = i;
         filename = optarg;
         break;
@@ -691,6 +794,10 @@
           parse_error = 1;
         }
         break;
+
+      case OPT_VERBOSE:
+        verbose = 1;
+        break;
     }
   }
 
@@ -707,15 +814,24 @@
       return r;
 
     case OPT_MODE_REPACK:
+      if (!config_file && !key_block_file) {
+        fprintf(stderr,
+                "You must supply at least one of --config and --keyblock\n");
+        return 1;
+      }
+
       bp = OldBlob(oldfile);
       if (!bp)
         return 1;
-      r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
+      r = ReplaceConfig(bp, config_file);
+      if (!r) {
+        r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
+      }
       FreeBlob(bp);
       return r;
 
     case OPT_MODE_VERIFY:
-      return Verify(filename, signpubkey);
+      return Verify(filename, signpubkey, verbose);
 
     default:
       fprintf(stderr,