cros_bundle_firmware: Add --set-node-enable option

Add an option to allow device tree nodes to be enabled or disabled.

--set-node-enable <node> <value>

<node> is either a full path to a node, or an alias which itself points
to a node.
<value> is either 0 (to set status = "ok") or 1 (to set status = "disabled").

BUG=chrome-os-partner:7952
TEST=manual
Using the tegra2-seaboard fdt:
$ cros_bundle_firmware --set-node-enable console 0 -O out
$ fdtdump out/updated.dtb
Manually verify that status = "disabled"; appears in node serial@70006300

$ cros_bundle_firmware --set-node-enable console 1 -O out
$ fdtdump out/updated.dtb
Manually verify that status = "ok"; appears in node serial@70006300

Do the same with:
$ cros_bundle_firmware --set-node-enable console 0 -O out

Try:
$ cros_bundle_firmware --set-node-enable fred 0 -O out
See that we get a warning:
Cannot find alias 'fred' - ignoring

Change-Id: I45442b24c9c26955f0c9dcc6467d20ebb0b34930
Reviewed-on: https://gerrit.chromium.org/gerrit/22283
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
Commit-Ready: Simon Glass <sjg@chromium.org>
Tested-by: Simon Glass <sjg@chromium.org>
diff --git a/host/cros_bundle_firmware b/host/cros_bundle_firmware
index 5d9598e..46437ff 100755
--- a/host/cros_bundle_firmware
+++ b/host/cros_bundle_firmware
@@ -88,6 +88,7 @@
     bundle.SetBootcmd(options.bootcmd, options.bootsecure)
     bundle.AddConfigList(options.add_config_str)
     bundle.AddConfigList(options.add_config_int, use_int=True)
+    bundle.AddEnableList(options.add_node_enable)
 
     out_fname = bundle.Start(options.hardware_id, options.output,
         options.show_map)
@@ -137,6 +138,14 @@
       action='store', help='Exynos preboot (BL1) file')
   parser.add_option('--bl2', dest='exynos_bl2', type='string',
       action='store', help='Exynos Secondary Program Loader (SPL / BL2) file')
+  parser.add_option('--add-node-enable', dest='add_node_enable',
+      type='string', nargs=2, action='append',
+      help='''Set a node to status = "ok" / "disabled".
+You can refer to the node with /aliases/... also.
+Examples:
+'--set-node-enable /uart@3f8 1' to enable the uart;
+'--set-node-enable console 1' to enable the node pointed to by
+/aliases/console.''')
   parser.add_option('--hwid', dest='hardware_id', type='string',
       action='store', help='Hardware ID string to use')
   parser.add_option('-B', '--bmpblk', dest='bmpblk', type='string',
diff --git a/host/lib/bundle_firmware.py b/host/lib/bundle_firmware.py
index 2a2f5cd..511b0bc 100644
--- a/host/lib/bundle_firmware.py
+++ b/host/lib/bundle_firmware.py
@@ -65,7 +65,7 @@
     bundle.SetFiles(...)
     bundle.SetOptions(...)
     bundle.SelectFdt(fdt.Fdt('filename.dtb')
-    .. can call bundle.AddConfigList() if required
+    .. can call bundle.AddConfigList(), AddEnableList() if required
     bundle.Start(...)
 
   Public properties:
@@ -285,6 +285,70 @@
       self.fdt.PutInteger('/config', 'bootsecure', int(bootsecure))
       self._out.Info('Boot command: %s' % bootcmd)
 
+  def SetNodeEnabled(self, node_name, enabled):
+    """Set whether an node is enabled or disabled.
+
+    This simply sets the 'status' property of a node to "ok", or "disabled".
+
+    The node should either be a full path to the node (like '/uart@10200000')
+    or an alias property.
+
+    Aliases are supported like this:
+
+        aliases {
+                console = "/uart@10200000";
+        };
+
+    pointing to a node:
+
+        uart@10200000 {
+                status = "ok";
+        };
+
+    In this case, this function takes the name of the alias ('console' in
+    this case) and updates the status of the node that is pointed to, to
+    either ok or disabled. If the alias does not exist, a warning is
+    displayed.
+
+    Args:
+      node_name: Name of node (e.g. '/uart@10200000') or alias alias
+          (e.g. 'console') to adjust
+      enabled: True to enable, False to disable
+    """
+    # Look up the alias if this is an alias reference
+    if not node_name.startswith('/'):
+      lookup = self.fdt.GetString('/aliases', node_name, '')
+      if not lookup:
+        self._out.Warning("Cannot find alias '%s' - ignoring" % node_name)
+        return
+      node_name = lookup
+    if enabled:
+      status = 'ok'
+    else:
+      status = 'disabled'
+    self.fdt.PutString(node_name, 'status', status)
+
+  def AddEnableList(self, enable_list):
+    """Process a list of nodes to enable/disable.
+
+    Args:
+      config_list: List of (node, value) tuples to add to the fdt. For each
+          tuple:
+              node: The fdt node to write to will be <node> or pointed to by
+                  /aliases/<node>. We can tell which
+              value: 0 to disable the node, 1 to enable it
+    """
+    if enable_list:
+      for node_name, enabled in enable_list:
+        try:
+          enabled = int(enabled)
+          if enabled not in (0, 1):
+            raise ValueError
+        except ValueError as str:
+          raise CmdError("Invalid enable option value '%s' "
+              "(should be 0 or 1)" % enabled)
+        self.SetNodeEnabled(node_name, enabled)
+
   def AddConfigList(self, config_list, use_int=False):
     """Add a list of config items to the fdt.