Forráskód Böngészése

Merge branch 'master' of git://git.denx.de/u-boot-fdt

Tom Rini 12 éve
szülő
commit
d6bc7dcc03
7 módosított fájl, 461 hozzáadás és 28 törlés
  1. 11 0
      README
  2. 11 0
      common/cmd_bootm.c
  3. 127 0
      common/image.c
  4. 101 1
      common/main.c
  5. 112 0
      include/fdtdec.h
  6. 1 0
      include/image.h
  7. 98 27
      lib/fdtdec.c

+ 11 - 0
README

@@ -2602,6 +2602,17 @@ FIT uImage format:
  -150	common/cmd_nand.c	Incorrect FIT image format
   151	common/cmd_nand.c	FIT image format OK
 
+- FIT image support:
+		CONFIG_FIT
+		Enable support for the FIT uImage format.
+
+		CONFIG_FIT_BEST_MATCH
+		When no configuration is explicitly selected, default to the
+		one whose fdt's compatibility field best matches that of
+		U-Boot itself. A match is considered "best" if it matches the
+		most specific compatibility entry of U-Boot's fdt's root node.
+		The order of entries in the configuration's fdt is ignored.
+
 - Standalone program support:
 		CONFIG_STANDALONE_LOAD_ADDR
 

+ 11 - 0
common/cmd_bootm.c

@@ -949,8 +949,19 @@ static void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc,
 			 * node
 			 */
 			bootstage_mark(BOOTSTAGE_ID_FIT_NO_UNIT_NAME);
+#ifdef CONFIG_FIT_BEST_MATCH
+			if (fit_uname_config)
+				cfg_noffset =
+					fit_conf_get_node(fit_hdr,
+							  fit_uname_config);
+			else
+				cfg_noffset =
+					fit_conf_find_compat(fit_hdr,
+							     gd->fdt_blob);
+#else
 			cfg_noffset = fit_conf_get_node(fit_hdr,
 							fit_uname_config);
+#endif
 			if (cfg_noffset < 0) {
 				bootstage_error(BOOTSTAGE_ID_FIT_NO_UNIT_NAME);
 				return NULL;

+ 127 - 0
common/image.c

@@ -3049,6 +3049,133 @@ int fit_check_format(const void *fit)
 	return 1;
 }
 
+
+/**
+ * fit_conf_find_compat
+ * @fit: pointer to the FIT format image header
+ * @fdt: pointer to the device tree to compare against
+ *
+ * fit_conf_find_compat() attempts to find the configuration whose fdt is the
+ * most compatible with the passed in device tree.
+ *
+ * Example:
+ *
+ * / o image-tree
+ *   |-o images
+ *   | |-o fdt@1
+ *   | |-o fdt@2
+ *   |
+ *   |-o configurations
+ *     |-o config@1
+ *     | |-fdt = fdt@1
+ *     |
+ *     |-o config@2
+ *       |-fdt = fdt@2
+ *
+ * / o U-Boot fdt
+ *   |-compatible = "foo,bar", "bim,bam"
+ *
+ * / o kernel fdt1
+ *   |-compatible = "foo,bar",
+ *
+ * / o kernel fdt2
+ *   |-compatible = "bim,bam", "baz,biz"
+ *
+ * Configuration 1 would be picked because the first string in U-Boot's
+ * compatible list, "foo,bar", matches a compatible string in the root of fdt1.
+ * "bim,bam" in fdt2 matches the second string which isn't as good as fdt1.
+ *
+ * returns:
+ *     offset to the configuration to use if one was found
+ *     -1 otherwise
+ */
+int fit_conf_find_compat(const void *fit, const void *fdt)
+{
+	int ndepth = 0;
+	int noffset, confs_noffset, images_noffset;
+	const void *fdt_compat;
+	int fdt_compat_len;
+	int best_match_offset = 0;
+	int best_match_pos = 0;
+
+	confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH);
+	images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
+	if (confs_noffset < 0 || images_noffset < 0) {
+		debug("Can't find configurations or images nodes.\n");
+		return -1;
+	}
+
+	fdt_compat = fdt_getprop(fdt, 0, "compatible", &fdt_compat_len);
+	if (!fdt_compat) {
+		debug("Fdt for comparison has no \"compatible\" property.\n");
+		return -1;
+	}
+
+	/*
+	 * Loop over the configurations in the FIT image.
+	 */
+	for (noffset = fdt_next_node(fit, confs_noffset, &ndepth);
+			(noffset >= 0) && (ndepth > 0);
+			noffset = fdt_next_node(fit, noffset, &ndepth)) {
+		const void *kfdt;
+		const char *kfdt_name;
+		int kfdt_noffset;
+		const char *cur_fdt_compat;
+		int len;
+		size_t size;
+		int i;
+
+		if (ndepth > 1)
+			continue;
+
+		kfdt_name = fdt_getprop(fit, noffset, "fdt", &len);
+		if (!kfdt_name) {
+			debug("No fdt property found.\n");
+			continue;
+		}
+		kfdt_noffset = fdt_subnode_offset(fit, images_noffset,
+						  kfdt_name);
+		if (kfdt_noffset < 0) {
+			debug("No image node named \"%s\" found.\n",
+			      kfdt_name);
+			continue;
+		}
+		/*
+		 * Get a pointer to this configuration's fdt.
+		 */
+		if (fit_image_get_data(fit, kfdt_noffset, &kfdt, &size)) {
+			debug("Failed to get fdt \"%s\".\n", kfdt_name);
+			continue;
+		}
+
+		len = fdt_compat_len;
+		cur_fdt_compat = fdt_compat;
+		/*
+		 * Look for a match for each U-Boot compatibility string in
+		 * turn in this configuration's fdt.
+		 */
+		for (i = 0; len > 0 &&
+		     (!best_match_offset || best_match_pos > i); i++) {
+			int cur_len = strlen(cur_fdt_compat) + 1;
+
+			if (!fdt_node_check_compatible(kfdt, 0,
+						       cur_fdt_compat)) {
+				best_match_offset = noffset;
+				best_match_pos = i;
+				break;
+			}
+			len -= cur_len;
+			cur_fdt_compat += cur_len;
+		}
+	}
+	if (!best_match_offset) {
+		debug("No match found.\n");
+		return -1;
+	}
+
+	return best_match_offset;
+}
+
 /**
  * fit_conf_get_node - get node offset for configuration of a given unit name
  * @fit: pointer to the FIT format image header

+ 101 - 1
common/main.c

@@ -30,6 +30,7 @@
 #include <common.h>
 #include <watchdog.h>
 #include <command.h>
+#include <fdtdec.h>
 #include <malloc.h>
 #include <version.h>
 #ifdef CONFIG_MODEM_SUPPORT
@@ -40,6 +41,14 @@
 #include <hush.h>
 #endif
 
+#ifdef CONFIG_OF_CONTROL
+#include <fdtdec.h>
+#endif
+
+#ifdef CONFIG_OF_LIBFDT
+#include <fdt_support.h>
+#endif /* CONFIG_OF_LIBFDT */
+
 #include <post.h>
 #include <linux/ctype.h>
 #include <menu.h>
@@ -274,6 +283,73 @@ int abortboot(int bootdelay)
 # endif	/* CONFIG_AUTOBOOT_KEYED */
 #endif	/* CONFIG_BOOTDELAY >= 0  */
 
+/*
+ * Runs the given boot command securely.  Specifically:
+ * - Doesn't run the command with the shell (run_command or parse_string_outer),
+ *   since that's a lot of code surface that an attacker might exploit.
+ *   Because of this, we don't do any argument parsing--the secure boot command
+ *   has to be a full-fledged u-boot command.
+ * - Doesn't check for keypresses before booting, since that could be a
+ *   security hole; also disables Ctrl-C.
+ * - Doesn't allow the command to return.
+ *
+ * Upon any failures, this function will drop into an infinite loop after
+ * printing the error message to console.
+ */
+
+#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) && \
+	defined(CONFIG_OF_CONTROL)
+static void secure_boot_cmd(char *cmd)
+{
+	cmd_tbl_t *cmdtp;
+	int rc;
+
+	if (!cmd) {
+		printf("## Error: Secure boot command not specified\n");
+		goto err;
+	}
+
+	/* Disable Ctrl-C just in case some command is used that checks it. */
+	disable_ctrlc(1);
+
+	/* Find the command directly. */
+	cmdtp = find_cmd(cmd);
+	if (!cmdtp) {
+		printf("## Error: \"%s\" not defined\n", cmd);
+		goto err;
+	}
+
+	/* Run the command, forcing no flags and faking argc and argv. */
+	rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd);
+
+	/* Shouldn't ever return from boot command. */
+	printf("## Error: \"%s\" returned (code %d)\n", cmd, rc);
+
+err:
+	/*
+	 * Not a whole lot to do here.  Rebooting won't help much, since we'll
+	 * just end up right back here.  Just loop.
+	 */
+	hang();
+}
+
+static void process_fdt_options(const void *blob)
+{
+	ulong addr;
+
+	/* Add an env variable to point to a kernel payload, if available */
+	addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0);
+	if (addr)
+		setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
+
+	/* Add an env variable to point to a root disk, if available */
+	addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0);
+	if (addr)
+		setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
+}
+#endif /* CONFIG_OF_CONTROL */
+
+
 /****************************************************************************/
 
 void main_loop (void)
@@ -284,7 +360,10 @@ void main_loop (void)
 	int rc = 1;
 	int flag;
 #endif
-
+#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) && \
+		defined(CONFIG_OF_CONTROL)
+	char *env;
+#endif
 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
 	char *s;
 	int bootdelay;
@@ -380,6 +459,23 @@ void main_loop (void)
 	else
 #endif /* CONFIG_BOOTCOUNT_LIMIT */
 		s = getenv ("bootcmd");
+#ifdef CONFIG_OF_CONTROL
+	/* Allow the fdt to override the boot command */
+	env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd");
+	if (env)
+		s = env;
+
+	process_fdt_options(gd->fdt_blob);
+
+	/*
+	 * If the bootsecure option was chosen, use secure_boot_cmd().
+	 * Always use 'env' in this case, since bootsecure requres that the
+	 * bootcmd was specified in the FDT too.
+	 */
+	if (fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0))
+		secure_boot_cmd(env);
+
+#endif /* CONFIG_OF_CONTROL */
 
 	debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
 
@@ -404,6 +500,10 @@ void main_loop (void)
 #endif /* CONFIG_MENUKEY */
 #endif /* CONFIG_BOOTDELAY */
 
+#if defined CONFIG_OF_CONTROL
+	set_working_fdt_addr((void *)gd->fdt_blob);
+#endif /* CONFIG_OF_CONTROL */
+
 	/*
 	 * Main Loop for Monitor Command Processing
 	 */

+ 112 - 0
include/fdtdec.h

@@ -40,10 +40,12 @@
 typedef u64 fdt_addr_t;
 #define FDT_ADDR_T_NONE (-1ULL)
 #define fdt_addr_to_cpu(reg) be64_to_cpu(reg)
+#define fdt_size_to_cpu(reg) be64_to_cpu(reg)
 #else
 typedef u32 fdt_addr_t;
 #define FDT_ADDR_T_NONE (-1U)
 #define fdt_addr_to_cpu(reg) be32_to_cpu(reg)
+#define fdt_size_to_cpu(reg) be32_to_cpu(reg)
 #endif
 
 /* Information obtained about memory from the FDT */
@@ -87,6 +89,22 @@ struct fdt_gpio_state {
 /* This tells us whether a fdt_gpio_state record is valid or not */
 #define fdt_gpio_isvalid(x) ((x)->gpio != FDT_GPIO_NONE)
 
+/**
+ * Read the GPIO taking into account the polarity of the pin.
+ *
+ * @param gpio		pointer to the decoded gpio
+ * @return value of the gpio if successful, < 0 if unsuccessful
+ */
+int fdtdec_get_gpio(struct fdt_gpio_state *gpio);
+
+/**
+ * Write the GPIO taking into account the polarity of the pin.
+ *
+ * @param gpio		pointer to the decoded gpio
+ * @return 0 if successful
+ */
+int fdtdec_set_gpio(struct fdt_gpio_state *gpio, int val);
+
 /**
  * Find the next numbered alias for a peripheral. This is used to enumerate
  * all the peripherals of a certain type.
@@ -107,6 +125,19 @@ struct fdt_gpio_state {
 int fdtdec_next_alias(const void *blob, const char *name,
 		enum fdt_compat_id id, int *upto);
 
+/**
+ * Find the compatible ID for a given node.
+ *
+ * Generally each node has at least one compatible string attached to it.
+ * This function looks through our list of known compatible strings and
+ * returns the corresponding ID which matches the compatible string.
+ *
+ * @param blob		FDT blob to use
+ * @param node		Node containing compatible string to find
+ * @return compatible ID, or COMPAT_UNKNOWN if we cannot find a match
+ */
+enum fdt_compat_id fdtdec_lookup(const void *blob, int node);
+
 /**
  * Find the next compatible node for a peripheral.
  *
@@ -166,6 +197,21 @@ fdt_addr_t fdtdec_get_addr(const void *blob, int node,
 s32 fdtdec_get_int(const void *blob, int node, const char *prop_name,
 		s32 default_val);
 
+/**
+ * Look up a 64-bit integer property in a node and return it. The property
+ * must have at least 8 bytes of data (2 cells). The first two cells are
+ * concatenated to form a 8 bytes value, where the first cell is top half and
+ * the second cell is bottom half.
+ *
+ * @param blob	FDT blob
+ * @param node	node to examine
+ * @param prop_name	name of property to find
+ * @param default_val	default value to return if the property is not found
+ * @return integer value, if found, or default_val if not
+ */
+uint64_t fdtdec_get_uint64(const void *blob, int node, const char *prop_name,
+		uint64_t default_val);
+
 /**
  * Checks whether a node is enabled.
  * This looks for a 'status' property. If this exists, then returns 1 if
@@ -342,6 +388,22 @@ int fdtdec_get_bool(const void *blob, int node, const char *prop_name);
 int fdtdec_decode_gpio(const void *blob, int node, const char *prop_name,
 		struct fdt_gpio_state *gpio);
 
+/**
+ * Decode a list of GPIOs from an FDT. This creates a list of GPIOs with no
+ * terminating item.
+ *
+ * @param blob         FDT blob to use
+ * @param node         Node to look at
+ * @param prop_name    Node property name
+ * @param gpio         Array of gpio elements to fill from FDT. This will be
+ *                     untouched if either 0 or an error is returned
+ * @param max_count    Maximum number of elements allowed
+ * @return number of GPIOs read if ok, -FDT_ERR_BADLAYOUT if max_count would
+ * be exceeded, or -FDT_ERR_NOTFOUND if the property is missing.
+ */
+int fdtdec_decode_gpios(const void *blob, int node, const char *prop_name,
+		struct fdt_gpio_state *gpio, int max_count);
+
 /**
  * Set up a GPIO pin according to the provided gpio information. At present this
  * just requests the GPIO.
@@ -354,6 +416,39 @@ int fdtdec_decode_gpio(const void *blob, int node, const char *prop_name,
  */
 int fdtdec_setup_gpio(struct fdt_gpio_state *gpio);
 
+/**
+ * Look in the FDT for a config item with the given name and return its value
+ * as a 32-bit integer. The property must have at least 4 bytes of data. The
+ * value of the first cell is returned.
+ *
+ * @param blob		FDT blob to use
+ * @param prop_name	Node property name
+ * @param default_val	default value to return if the property is not found
+ * @return integer value, if found, or default_val if not
+ */
+int fdtdec_get_config_int(const void *blob, const char *prop_name,
+		int default_val);
+
+/**
+ * Look in the FDT for a config item with the given name
+ * and return whether it exists.
+ *
+ * @param blob		FDT blob
+ * @param prop_name	property name to look up
+ * @return 1, if it exists, or 0 if not
+ */
+int fdtdec_get_config_bool(const void *blob, const char *prop_name);
+
+/**
+ * Look in the FDT for a config item with the given name and return its value
+ * as a string.
+ *
+ * @param blob          FDT blob
+ * @param prop_name     property name to look up
+ * @returns property string, NULL on error.
+ */
+char *fdtdec_get_config_string(const void *blob, const char *prop_name);
+
 /*
  * Look up a property in a node and return its contents in a byte
  * array of given length. The property must have at least enough data for
@@ -385,4 +480,21 @@ int fdtdec_get_byte_array(const void *blob, int node, const char *prop_name,
  */
 const u8 *fdtdec_locate_byte_array(const void *blob, int node,
 			     const char *prop_name, int count);
+
+/**
+ * Look up a property in a node which contains a memory region address and
+ * size. Then return a pointer to this address.
+ *
+ * The property must hold one address with a length. This is only tested on
+ * 32-bit machines.
+ *
+ * @param blob		FDT blob
+ * @param node		node to examine
+ * @param prop_name	name of property to find
+ * @param ptrp		returns pointer to region, or NULL if no address
+ * @param size		returns size of region
+ * @return 0 if ok, -1 on error (propery not found)
+ */
+int fdtdec_decode_region(const void *blob, int node,
+		const char *prop_name, void **ptrp, size_t *size);
 #endif

+ 1 - 0
include/image.h

@@ -615,6 +615,7 @@ int fit_image_check_type(const void *fit, int noffset, uint8_t type);
 int fit_image_check_comp(const void *fit, int noffset, uint8_t comp);
 int fit_check_format(const void *fit);
 
+int fit_conf_find_compat(const void *fit, const void *fdt);
 int fit_conf_get_node(const void *fit, const char *conf_uname);
 int fit_conf_get_kernel_node(const void *fit, int noffset);
 int fit_conf_get_ramdisk_node(const void *fit, int noffset);

+ 98 - 27
lib/fdtdec.c

@@ -52,28 +52,6 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id)
 	return compat_names[id];
 }
 
-/**
- * Look in the FDT for an alias with the given name and return its node.
- *
- * @param blob	FDT blob
- * @param name	alias name to look up
- * @return node offset if found, or an error code < 0 otherwise
- */
-static int find_alias_node(const void *blob, const char *name)
-{
-	const char *path;
-	int alias_node;
-
-	debug("find_alias_node: %s\n", name);
-	alias_node = fdt_path_offset(blob, "/aliases");
-	if (alias_node < 0)
-		return alias_node;
-	path = fdt_getprop(blob, alias_node, name, NULL);
-	if (!path)
-		return -FDT_ERR_NOTFOUND;
-	return fdt_path_offset(blob, path);
-}
-
 fdt_addr_t fdtdec_get_addr(const void *blob, int node,
 		const char *prop_name)
 {
@@ -111,6 +89,19 @@ s32 fdtdec_get_int(const void *blob, int node, const char *prop_name,
 	return default_val;
 }
 
+uint64_t fdtdec_get_uint64(const void *blob, int node, const char *prop_name,
+		uint64_t default_val)
+{
+	const uint64_t *cell64;
+	int length;
+
+	cell64 = fdt_getprop(blob, node, prop_name, &length);
+	if (!cell64 || length < sizeof(*cell64))
+		return default_val;
+
+	return fdt64_to_cpu(*cell64);
+}
+
 int fdtdec_get_is_enabled(const void *blob, int node)
 {
 	const char *cell;
@@ -128,7 +119,7 @@ int fdtdec_get_is_enabled(const void *blob, int node)
 	return 1;
 }
 
-enum fdt_compat_id fd_dec_lookup(const void *blob, int node)
+enum fdt_compat_id fdtdec_lookup(const void *blob, int node)
 {
 	enum fdt_compat_id id;
 
@@ -171,7 +162,7 @@ int fdtdec_next_alias(const void *blob, const char *name,
 	/* snprintf() is not available */
 	assert(strlen(name) < MAX_STR_LEN);
 	sprintf(str, "%.*s%d", MAX_STR_LEN, name, *upto);
-	node = find_alias_node(blob, str);
+	node = fdt_path_offset(blob, str);
 	if (node < 0)
 		return node;
 	err = fdt_node_check_compatible(blob, node, compat_names[id]);
@@ -426,9 +417,8 @@ int fdtdec_get_bool(const void *blob, int node, const char *prop_name)
  * @return number of GPIOs read if ok, -FDT_ERR_BADLAYOUT if max_count would
  * be exceeded, or -FDT_ERR_NOTFOUND if the property is missing.
  */
-static int fdtdec_decode_gpios(const void *blob, int node,
-		const char *prop_name, struct fdt_gpio_state *gpio,
-		int max_count)
+int fdtdec_decode_gpios(const void *blob, int node, const char *prop_name,
+		struct fdt_gpio_state *gpio, int max_count)
 {
 	const struct fdt_property *prop;
 	const u32 *cell;
@@ -475,6 +465,26 @@ int fdtdec_decode_gpio(const void *blob, int node, const char *prop_name,
 	return err == 1 ? 0 : err;
 }
 
+int fdtdec_get_gpio(struct fdt_gpio_state *gpio)
+{
+	int val;
+
+	if (!fdt_gpio_isvalid(gpio))
+		return -1;
+
+	val = gpio_get_value(gpio->gpio);
+	return gpio->flags & FDT_GPIO_ACTIVE_LOW ? val ^ 1 : val;
+}
+
+int fdtdec_set_gpio(struct fdt_gpio_state *gpio, int val)
+{
+	if (!fdt_gpio_isvalid(gpio))
+		return -1;
+
+	val = gpio->flags & FDT_GPIO_ACTIVE_LOW ? val ^ 1 : val;
+	return gpio_set_value(gpio->gpio, val);
+}
+
 int fdtdec_setup_gpio(struct fdt_gpio_state *gpio)
 {
 	/*
@@ -512,3 +522,64 @@ const u8 *fdtdec_locate_byte_array(const void *blob, int node,
 		return NULL;
 	return cell;
 }
+
+int fdtdec_get_config_int(const void *blob, const char *prop_name,
+		int default_val)
+{
+	int config_node;
+
+	debug("%s: %s\n", __func__, prop_name);
+	config_node = fdt_path_offset(blob, "/config");
+	if (config_node < 0)
+		return default_val;
+	return fdtdec_get_int(blob, config_node, prop_name, default_val);
+}
+
+int fdtdec_get_config_bool(const void *blob, const char *prop_name)
+{
+	int config_node;
+	const void *prop;
+
+	debug("%s: %s\n", __func__, prop_name);
+	config_node = fdt_path_offset(blob, "/config");
+	if (config_node < 0)
+		return 0;
+	prop = fdt_get_property(blob, config_node, prop_name, NULL);
+
+	return prop != NULL;
+}
+
+char *fdtdec_get_config_string(const void *blob, const char *prop_name)
+{
+	const char *nodep;
+	int nodeoffset;
+	int len;
+
+	debug("%s: %s\n", __func__, prop_name);
+	nodeoffset = fdt_path_offset(blob, "/config");
+	if (nodeoffset < 0)
+		return NULL;
+
+	nodep = fdt_getprop(blob, nodeoffset, prop_name, &len);
+	if (!nodep)
+		return NULL;
+
+	return (char *)nodep;
+}
+
+int fdtdec_decode_region(const void *blob, int node,
+		const char *prop_name, void **ptrp, size_t *size)
+{
+	const fdt_addr_t *cell;
+	int len;
+
+	debug("%s: %s\n", __func__, prop_name);
+	cell = fdt_getprop(blob, node, prop_name, &len);
+	if (!cell || (len != sizeof(fdt_addr_t) * 2))
+		return -1;
+
+	*ptrp = (void *)fdt_addr_to_cpu(*cell);
+	*size = fdt_size_to_cpu(cell[1]);
+	debug("%s: size=%zx\n", __func__, *size);
+	return 0;
+}