cros_bundle_firmware: Allow sending a kernel over USB

When all you want to do is test a new kernel, it is convenient to be
able to send it over USB. This avoids the other slower options, like

1)
./build_packages --board daisy
./build_image --board daisy test
./image_to_usb ...
(put SD card in machine and boot)

2)
cros_workon_make --board daisy chromeos-kernel
./build_image --board daisy test
./image_to_usb ...
(put SD card in machine and boot)

3)
FEATURES=noclean cros_workon_make chromeos-kernel --install
(ssh to copy kernel to machine over network, or perhaps better
update_kernel.sh)

It is most similar to:

4)
cros_workon_make --board daisy chromeos-kernel
(reset board with network adaptor in USB, and have it pick up the
kernel using tftp or nfs)

but of course you can't easily fiddle with firmware with that path.

To use it on daisy, try:

cros_bundle_firmware -b daisy ---kernel /build/daisy/boot/vmlinux.uimg

or similar. It will reset the board, download U-Boot and the kernel,
then start U-Boot, setting the kernaddr environment variable so that the
scripts will substitute the downloaded kernel for the one that is loaded
through normal means.

BUG=chromium-os:32386
TEST=manual:
cros_bundle_firmware -b daisy ---kernel /build/daisy/boot/vmlinux.uimg
-d exynos5250-snow
on a snow and see that it boots U-Boot and then picks up the latest
kernel, ignoring the one on the machine.

Change-Id: I36f229aa1d55c8c6859fc7f62b4a9443e7d6c924
Reviewed-on: https://gerrit.chromium.org/gerrit/26792
Reviewed-by: Simon Glass <sjg@chromium.org>
Tested-by: Simon Glass <sjg@chromium.org>
Commit-Ready: Che-Liang Chiou <clchiou@chromium.org>
diff --git a/host/cros_bundle_firmware b/host/cros_bundle_firmware
index 832339a..6f7142d 100755
--- a/host/cros_bundle_firmware
+++ b/host/cros_bundle_firmware
@@ -80,7 +80,8 @@
                   bmpblk=options.bmpblk, coreboot=options.coreboot,
                   postload=options.postload, seabios=options.seabios,
                   exynos_bl1=options.exynos_bl1, exynos_bl2=options.exynos_bl2,
-                  skeleton=options.skeleton, ecbin=options.ecbin)
+                  skeleton=options.skeleton, ecbin=options.ecbin,
+                  kernel=options.kernel)
   bundle.SetOptions(small=options.small)
 
   try:
@@ -103,7 +104,7 @@
       file_list = bundle.GetFiles()
       write_firmware.DoWriteFirmware(output, tools, fdt, flasher,
           file_list, out_fname, bundle, dest=options.write,
-          flash_dest=options.flash_dest, props=props)
+          flash_dest=options.flash_dest, kernel=options.kernel, props=props)
 
   except (CmdError, ValueError) as err:
     # For verbosity 4 we want to display all possible information
@@ -163,6 +164,8 @@
       default='##/usr/share/vboot/devkeys')
   parser.add_option('-K', '--skeleton', dest='skeleton', type='string',
         action='store', help='Coreboot skeleton file')
+  parser.add_option('--kernel', dest='kernel', type='string',
+        action='store', help='Kernel file to ask U-Boot to boot')
   parser.add_option('-I', '--includedir', dest='includedirs', type='string',
       action='append', help='Include directory to search for files')
   parser.add_option('-m', '--map', dest='show_map', action='store_true',\
diff --git a/host/lib/bundle_firmware.py b/host/lib/bundle_firmware.py
index 4aa4123..ad8d803 100644
--- a/host/lib/bundle_firmware.py
+++ b/host/lib/bundle_firmware.py
@@ -116,7 +116,7 @@
 
   def SetFiles(self, board, bct, uboot=None, bmpblk=None, coreboot=None,
                postload=None, seabios=None, exynos_bl1=None, exynos_bl2=None,
-               skeleton=None, ecbin=None):
+               skeleton=None, ecbin=None, kernel=None):
     """Set up files required for Bundle.
 
     Args:
@@ -131,6 +131,7 @@
       exynos_bl2: The filename of the exynos BL2 file (U-Boot spl)
       skeleton: The filename of the coreboot skeleton file.
       ecbin: The filename of the EC (Embedded Controller) file.
+      kernel: The filename of the kernel file if any.
     """
     self._board = board
     self.uboot_fname = uboot
@@ -143,6 +144,7 @@
     self.exynos_bl2 = exynos_bl2
     self.skeleton_fname = skeleton
     self.ecbin_fname = ecbin
+    self.kernel_fname = kernel
 
   def SetOptions(self, small):
     """Set up options supported by Bundle.
@@ -779,6 +781,10 @@
     pack.AddProperty('skeleton', self.skeleton_fname)
     pack.AddProperty('dtb', fdt.fname)
 
+    # If we are writing a kernel, add its offset from TEXT_BASE to the fdt.
+    if self.kernel_fname:
+      fdt.PutInteger('/config', 'kernel-offset', pack.image_size)
+
     # Make a copy of the fdt for the bootstub
     fdt_data = self._tools.ReadFile(fdt.fname)
     uboot_data = self._tools.ReadFile(self.uboot_fname)
diff --git a/host/lib/write_firmware.py b/host/lib/write_firmware.py
index 3b73bdf..12b82d3 100644
--- a/host/lib/write_firmware.py
+++ b/host/lib/write_firmware.py
@@ -352,7 +352,8 @@
     self._tools.WriteFile(image, data[0x6000:])
     return bl1, bl2, image
 
-  def _ExynosFlashImage(self, flash_dest, flash_uboot, bl1, bl2, payload):
+  def _ExynosFlashImage(self, flash_dest, flash_uboot, bl1, bl2, payload,
+                        kernel):
     """Flash the image to SPI flash.
 
     This creates a special Flasher binary, with the image to be flashed as
@@ -365,6 +366,7 @@
       bl1: Full path to file containing BL1 (pre-boot).
       bl2: Full path to file containing BL2 (SPL).
       payload: Full path to payload.
+      kernel: Kernel to send after the payload, or None.
 
     Returns:
       True if ok, False if failed.
@@ -387,13 +389,25 @@
     args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off'] + args
     self._tools.Run('dut-control', args)
 
+    # If we have a kernel to write, create a new image with that added.
+    if kernel:
+      dl_image = os.path.join(self._tools.outdir, 'image-plus-kernel.bin')
+      data = self._tools.ReadFile(image)
+
+      # Pad the original payload out to the original length
+      data += '\0' * (os.stat(payload).st_size - len(data))
+      data += self._tools.ReadFile(kernel)
+      self._tools.WriteFile(dl_image, data)
+    else:
+      dl_image = image
+
     self._out.Progress('Uploading image')
     download_list = [
         # The numbers are the download addresses (in SRAM) for each piece
         # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
         ['bl1', 0x02021400, bl1],
         ['bl2', 0x02023400, bl2],
-        ['u-boot', 0x43e00000, image]
+        ['u-boot', 0x43e00000, dl_image]
         ]
     try:
       for upto in range(len(download_list)):
@@ -600,7 +614,7 @@
 
 def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname,
                     bundle, update=True, verify=False, dest=None,
-                    flash_dest=None, props=None):
+                    flash_dest=None, kernel=None, props=None):
   """A simple function to write firmware to a device.
 
   This creates a WriteFirmware object and uses it to write the firmware image
@@ -618,6 +632,7 @@
     verify: Verify the write by doing a readback and CRC.
     dest: Destination device to write firmware to (usb, sd).
     flash_dest: Destination device for flasher to program payload into.
+    kernel: Kernel file to write after U-Boot
     props: A dictionary containing properties from the PackFirmware object
   """
   write = WriteFirmware(tools, fdt, output, bundle)
@@ -638,7 +653,8 @@
       tools.CheckTool('lsusb', 'usbutils')
       tools.CheckTool('smdk-usbdl', 'smdk-dltool')
       ok = write._ExynosFlashImage(flash_dest, flasher,
-          file_list['exynos-bl1'], file_list['exynos-bl2'], image_fname)
+          file_list['exynos-bl1'], file_list['exynos-bl2'], image_fname,
+          kernel)
     else:
       raise CmdError("Unknown flash method '%s'" % method)
     if ok: