update_kernel: Let users override the rootfs that's updated

In some kernel development workflows the device doesn't boot far enough
with the new test kernel to get a network connection up and running. It
could crash early on in the boot process, requiring the developer to
reboot into a netboot kernel or usb stick rootfs+kernel to dd the "good"
kernel back to the on-board storage. This is time consuming to do, and
requires things like usb stick and network connections.

Let's introduce a new option --rootfs that lets the user specify the
rootfs they want to copy the firmware and kernel modules to. We already
have an option to target a particular kernel partition with --partition,
so this new option allows someone to do something like:

   update_kernel.sh --remote=<IP> --partition=/dev/sda4 --rootfs=/dev/sda5 --bootonce

to try out an experimental kernel on the B copy of the kernel. If the
kernel crashes early in boot then it should reboot and the bootloader
will pick the A copy of the kernel and rootfs.

BUG=None
TEST=run update_kernel with just --remote, run it with example above,
and with the A version too (sda2+sda3)

Change-Id: I73098a110c4d4248976870fa12ced0d1500cfb8b
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1511953
Reviewed-by: Douglas Anderson <dianders@chromium.org>
diff --git a/update_kernel.sh b/update_kernel.sh
index da2f604..fab12f6 100755
--- a/update_kernel.sh
+++ b/update_kernel.sh
@@ -17,6 +17,7 @@
 DEFINE_string device "" "Override boot device reported by target"
 DEFINE_string partition "" "Override kernel partition reported by target"
 DEFINE_string rootoff "" "Override root offset"
+DEFINE_string rootfs "" "Override rootfs partition reported by target"
 DEFINE_string arch "" "Override architecture reported by target"
 DEFINE_boolean ignore_verity $FLAGS_FALSE "Update kernel even if system is using verity"
 DEFINE_boolean reboot $FLAGS_TRUE "Reboot system after update"
@@ -74,6 +75,15 @@
     REMOTE_VERITY=${FLAGS_FALSE}
     info "System is not using verity: updating firmware and modules"
   fi
+  if [[ -z "${FLAGS_rootfs}" ]]; then
+    FLAGS_rootfs="${REMOTE_OUT}"
+  fi
+  # If rootfs is for different partition than we're currently running on
+  # mount it manually to update the right modules, firmware, etc.
+  REMOTE_NEEDS_ROOTFS_MOUNTED=${FLAGS_FALSE}
+  if [[ "${REMOTE_OUT}" != "${FLAGS_rootfs}" ]]; then
+    REMOTE_NEEDS_ROOTFS_MOUNTED=${FLAGS_TRUE}
+  fi
   [ -n "${FLAGS_partition}" ] && return
   if [ "${REMOTE_OUT}" == "${FLAGS_device}${PARTITION_NUM_ROOT_A}" ]; then
     FLAGS_partition="${FLAGS_device}${PARTITION_NUM_KERN_A}"
@@ -150,22 +160,28 @@
 }
 
 copy_kernelmodules() {
+  local basedir="$1" # rootfs directory (could be in /tmp) or empty string
   echo "copying modules"
   local modules_dir=/build/"${FLAGS_board}"/lib/modules/
   if [ ! -d "${modules_dir}" ]; then
     info "No modules.  Skipping."
     return
   fi
-  remote_send_to "${modules_dir}" /lib/modules/
+  remote_send_to "${modules_dir}" "${basedir}"/lib/modules
   local kernel_release
-  remote_sh "cd /lib/modules; echo *"
+  remote_sh "cd ${basedir}/lib/modules; echo *"
   for kernel_release in "${REMOTE_OUT}"; do
     local system_map="${modules_dir}"/"${kernel_release}"/build/System.map
     if [ -r "${system_map}" ]; then
       remote_sh mktemp -d /tmp/update_kernel_system_map_"${kernel_release}".XXXXXX
       local temp_dir="${REMOTE_OUT}"
       remote_cp_to "${system_map}" "${temp_dir}"
-      remote_sh depmod -ae -F "${temp_dir}"/System.map "${kernel_release}"
+      local b_opt
+      if [ -n "${basedir}" ]; then
+        b_opt="-b ${basedir}"
+      fi
+      remote_sh depmod "${b_opt}" -ae \
+                       -F "${temp_dir}"/System.map "${kernel_release}"
       remote_sh rm -rf "${temp_dir}"
     fi
   done
@@ -267,22 +283,34 @@
   fi
 
   if [[ ${REMOTE_VERITY} -eq ${FLAGS_FALSE} ]]; then
-    remote_sh mount -o remount,rw /
+    local remote_basedir
+    if [[ ${REMOTE_NEEDS_ROOTFS_MOUNTED} -eq ${FLAGS_TRUE} ]]; then
+      remote_sh mktemp -d /tmp/"${FLAGS_rootfs#$FLAGS_device}".XXXXXX
+      remote_basedir="${REMOTE_OUT}"
+      remote_sh mount "${FLAGS_rootfs}" "${remote_basedir}"
+    else
+      remote_sh mount -o remount,rw /
+    fi
     echo "copying kernel"
-    remote_send_to /build/"${FLAGS_board}"/boot/ /boot/
+    remote_send_to /build/"${FLAGS_board}"/boot/ "${remote_basedir}"/boot/
 
     if [ ${FLAGS_syslinux} -eq ${FLAGS_TRUE} ]; then
       update_syslinux_kernel
     fi
 
-    copy_kernelmodules
+    copy_kernelmodules "${remote_basedir}"
 
     if [[ ${FLAGS_firmware} -eq ${FLAGS_TRUE} ]]; then
       echo "copying firmware"
-      remote_send_to /build/"${FLAGS_board}"/lib/firmware/ /lib/firmware/
+      remote_send_to /build/"${FLAGS_board}"/lib/firmware/ \
+                     "${remote_basedir}"/lib/firmware/
     else
       info "Skipping update of firmware (per request)."
     fi
+    if [[ ${REMOTE_NEEDS_ROOTFS_MOUNTED} -eq ${FLAGS_TRUE} ]]; then
+      remote_sh umount "${remote_basedir}"
+      remote_sh rmdir "${remote_basedir}"
+    fi
   fi
 
   if [ ${FLAGS_vboot} -eq ${FLAGS_TRUE} ]; then