summaryrefslogtreecommitdiffstats
path: root/common/image-fit.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/image-fit.c')
-rw-r--r--common/image-fit.c326
1 files changed, 231 insertions, 95 deletions
diff --git a/common/image-fit.c b/common/image-fit.c
index 2681d62a9a..251fda97b3 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) Jan Lübbe, 2014
*
* This code is inspired by the U-Boot FIT image code.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "FIT: " fmt
@@ -33,6 +21,7 @@
#include <linux/err.h>
#include <stringlist.h>
#include <rsa.h>
+#include <uncompress.h>
#include <image-fit.h>
#define FDT_MAX_DEPTH 32
@@ -242,11 +231,9 @@ static struct digest *fit_alloc_digest(struct device_node *sig_node,
return ERR_PTR(-EINVAL);
}
- if (strcmp(algo_name, "sha1,rsa2048") == 0) {
+ if (strncmp(algo_name, "sha1,", 5) == 0) {
algo = HASH_ALGO_SHA1;
- } else if (strcmp(algo_name, "sha256,rsa2048") == 0) {
- algo = HASH_ALGO_SHA256;
- } else if (strcmp(algo_name, "sha256,rsa4096") == 0) {
+ } else if (strncmp(algo_name, "sha256,", 7) == 0) {
algo = HASH_ALGO_SHA256;
} else {
pr_err("unknown algo %s\n", algo_name);
@@ -269,53 +256,44 @@ static struct digest *fit_alloc_digest(struct device_node *sig_node,
static int fit_check_rsa_signature(struct device_node *sig_node,
enum hash_algo algo, void *hash)
{
- struct rsa_public_key *key;
- const char *key_name;
- char *key_path;
- struct device_node *key_node;
+ const struct rsa_public_key *key;
+ const char *key_name = NULL;
int sig_len;
const char *sig_value;
int ret;
sig_value = of_get_property(sig_node, "value", &sig_len);
if (!sig_value) {
- pr_err("signature value not found in %s\n", sig_node->full_name);
- return -EINVAL;
- }
-
- if (of_property_read_string(sig_node, "key-name-hint", &key_name)) {
- pr_err("key name not found in %s\n", sig_node->full_name);
+ pr_err("signature value not found in %pOF\n", sig_node);
return -EINVAL;
}
- key = rsa_get_key(key_name);
- if (IS_ERR(key)) {
- key_path = xasprintf("/signature/key-%s", key_name);
- key_node = of_find_node_by_path(key_path);
- if (!key_node) {
- pr_info("failed to find key node %s\n", key_path);
- free(key_path);
- return -ENOENT;
+ of_property_read_string(sig_node, "key-name-hint", &key_name);
+ if (key_name) {
+ key = rsa_get_key(key_name);
+ if (key) {
+ ret = rsa_verify(key, sig_value, sig_len, hash, algo);
+ if (!ret)
+ goto ok;
}
- free(key_path);
+ }
- key = rsa_of_read_key(key_node);
+ for_each_rsa_key(key) {
+ if (key_name && !strcmp(key->key_name_hint, key_name))
+ continue;
- if (IS_ERR(key)) {
- pr_info("failed to read key in %s\n", key_node->full_name);
- return -ENOENT;
- }
+ ret = rsa_verify(key, sig_value, sig_len, hash, algo);
+ if (!ret)
+ goto ok;
}
- ret = rsa_verify(key, sig_value, sig_len, hash, algo);
- if (ret)
- pr_err("image signature BAD\n");
- else
- pr_info("image signature OK\n");
+ pr_err("image signature BAD\n");
- rsa_key_free(key);
+ return -EBADMSG;
+ok:
+ pr_info("image signature OK\n");
- return ret;
+ return 0;
}
/*
@@ -332,12 +310,12 @@ static int fit_verify_signature(struct device_node *sig_node, const void *fit)
if (of_property_read_u32_index(sig_node, "hashed-strings", 0,
&hashed_strings_start)) {
- pr_err("hashed-strings start not found in %s\n", sig_node->full_name);
+ pr_err("hashed-strings start not found in %pOF\n", sig_node);
return -EINVAL;
}
if (of_property_read_u32_index(sig_node, "hashed-strings", 1,
&hashed_strings_size)) {
- pr_err("hashed-strings size not found in %s\n", sig_node->full_name);
+ pr_err("hashed-strings size not found in %pOF\n", sig_node);
return -EINVAL;
}
@@ -345,7 +323,7 @@ static int fit_verify_signature(struct device_node *sig_node, const void *fit)
string_list_init(&exc_props);
if (of_read_string_list(sig_node, "hashed-nodes", &inc_nodes)) {
- pr_err("hashed-nodes property not found in %s\n", sig_node->full_name);
+ pr_err("hashed-nodes property not found in %pOF\n", sig_node);
ret = -EINVAL;
goto out_sl;
}
@@ -385,7 +363,6 @@ static int fit_verify_hash(struct fit_handle *handle, struct device_node *image,
struct digest *d;
const char *algo;
const char *value_read;
- char *value_calc;
int hash_len, ret;
struct device_node *hash;
@@ -404,50 +381,44 @@ static int fit_verify_hash(struct fit_handle *handle, struct device_node *image,
hash = of_get_child_by_name(image, "hash@1");
if (!hash) {
if (ret)
- pr_err("image %s does not have hashes\n",
- image->full_name);
+ pr_err("image %pOF does not have hashes\n", image);
return ret;
}
value_read = of_get_property(hash, "value", &hash_len);
if (!value_read) {
- pr_err("%s: \"value\" property not found\n", hash->full_name);
+ pr_err("%pOF: \"value\" property not found\n", hash);
return -EINVAL;
}
if (of_property_read_string(hash, "algo", &algo)) {
- pr_err("%s: \"algo\" property not found\n", hash->full_name);
+ pr_err("%pOF: \"algo\" property not found\n", hash);
return -EINVAL;
}
d = digest_alloc(algo);
if (!d) {
- pr_err("%s: unsupported algo %s\n", hash->full_name, algo);
+ pr_err("%pOF: unsupported algo %s\n", hash, algo);
return -EINVAL;
}
if (hash_len != digest_length(d)) {
- pr_err("%s: invalid hash length %d\n", hash->full_name, hash_len);
+ pr_err("%pOF: invalid hash length %d\n", hash, hash_len);
ret = -EINVAL;
goto err_digest_free;
}
- value_calc = xmalloc(hash_len);
-
digest_init(d);
digest_update(d, data, data_len);
- digest_final(d, value_calc);
- if (memcmp(value_read, value_calc, hash_len)) {
- pr_info("%s: hash BAD\n", hash->full_name);
+ if (digest_verify(d, value_read)) {
+ pr_info("%pOF: hash BAD\n", hash);
ret = -EBADMSG;
} else {
- pr_info("%s: hash OK\n", hash->full_name);
+ pr_info("%pOF: hash OK\n", hash);
ret = 0;
}
- free(value_calc);
-
err_digest_free:
digest_free(d);
@@ -481,7 +452,7 @@ static int fit_image_verify_signature(struct fit_handle *handle,
if (!sig_node)
sig_node = of_get_child_by_name(image, "signature@1");
if (!sig_node) {
- pr_err("Image %s has no signature\n", image->full_name);
+ pr_err("Image %pOF has no signature\n", image);
return ret;
}
@@ -517,6 +488,129 @@ int fit_has_image(struct fit_handle *handle, void *configuration,
return 1;
}
+static int fit_get_address(struct device_node *image, const char *property,
+ unsigned long *addr)
+{
+ const __be32 *cell;
+ int len = 0;
+
+ cell = of_get_property(image, property, &len);
+ if (!cell)
+ return -EINVAL;
+ if (len > sizeof(*addr))
+ return -ENOTSUPP;
+
+ *addr = (unsigned long)of_read_number(cell, len / sizeof(*cell));
+ return 0;
+}
+
+static int
+fit_get_image(struct fit_handle *handle, void *configuration,
+ const char **unit, struct device_node **image)
+{
+ struct device_node *conf_node = configuration;
+
+ if (conf_node) {
+ if (of_property_read_string(conf_node, *unit, unit)) {
+ pr_err("No image named '%s'\n", *unit);
+ return -ENOENT;
+ }
+ }
+
+ *image = of_get_child_by_name(handle->images, *unit);
+ if (!*image)
+ return -ENOENT;
+
+ return 0;
+}
+
+/**
+ * fit_get_image_address - Get an address from an image in a FIT image
+ * @handle: The FIT image handle
+ * @name: The name of the image to open
+ * @property: The name of the address to get (for example "load" or "entry")
+ * @address: The address given by the image
+ *
+ * Try to parse the @property in the image @name as an address. @configuration
+ * holds the cookie returned from fit_open_configuration() if the image is
+ * opened as part of a configuration, or NULL if the image is opened without a
+ * configuration. If it exists the value will be returned in @address. Otherwise
+ * @address won't be changed.
+ *
+ * Return: 0 for success, negative error code otherwise
+ */
+int fit_get_image_address(struct fit_handle *handle, void *configuration,
+ const char *name, const char *property,
+ unsigned long *address)
+{
+ struct device_node *image;
+ const char *unit = name;
+ const char *type;
+ int ret;
+
+ if (!address || !property || !name)
+ return -EINVAL;
+
+ ret = fit_get_image(handle, configuration, &unit, &image);
+ if (ret)
+ return ret;
+
+ /* Treat type = "kernel_noload" as if entry/load address is missing */
+ ret = of_property_read_string(image, "type", &type);
+ if (!ret && !strcmp(type, "kernel_noload"))
+ return -ENOENT;
+
+ ret = fit_get_address(image, property, address);
+
+ return ret;
+}
+
+static void fit_uncompress_error_fn(char *x)
+{
+ pr_err("%s\n", x);
+}
+
+static int fit_handle_decompression(struct device_node *image,
+ const char *type,
+ const void **data,
+ int *data_len)
+{
+ const char *compression = NULL;
+ void *uc_data;
+ int ret;
+
+ of_property_read_string(image, "compression", &compression);
+ if (!compression || !strcmp(compression, "none"))
+ return 0;
+
+ if (!strcmp(type, "ramdisk")) {
+ pr_warn("compression != \"none\" for ramdisks is deprecated,"
+ " please fix your .its file!\n");
+ return 0;
+ }
+
+ if (!IS_ENABLED(CONFIG_UNCOMPRESS)) {
+ pr_err("image has compression = \"%s\", but support not compiled in\n",
+ compression);
+ return -ENOSYS;
+ }
+
+ ret = uncompress_buf_to_buf(*data, *data_len, &uc_data,
+ fit_uncompress_error_fn);
+ if (ret < 0) {
+ pr_err("data couldn't be decompressed\n");
+ return ret;
+ }
+
+ *data = uc_data;
+ *data_len = ret;
+
+ /* associate buffer with FIT, so it's not leaked */
+ __of_new_property(image, "uncompressed-data", uc_data, *data_len);
+
+ return 0;
+}
+
/**
* fit_open_image - Open an image in a FIT image
* @handle: The FIT image handle
@@ -539,31 +633,21 @@ int fit_open_image(struct fit_handle *handle, void *configuration,
unsigned long *outsize)
{
struct device_node *image;
- const char *unit, *type = NULL, *desc= "(no description)";
+ const char *unit = name, *type = NULL, *desc= "(no description)";
const void *data;
int data_len;
int ret = 0;
- struct device_node *conf_node = configuration;
- if (conf_node) {
- if (of_property_read_string(conf_node, name, &unit)) {
- pr_err("No image named '%s'\n", name);
- return -ENOENT;
- }
- } else {
- unit = name;
- }
-
- image = of_get_child_by_name(handle->images, unit);
- if (!image)
- return -ENOENT;
+ ret = fit_get_image(handle, configuration, &unit, &image);
+ if (ret)
+ return ret;
of_property_read_string(image, "description", &desc);
pr_info("image '%s': '%s'\n", unit, desc);
of_property_read_string(image, "type", &type);
if (!type) {
- pr_err("No \"type\" property found in %s\n", image->full_name);
+ pr_err("No \"type\" property found in %pOF\n", image);
return -EINVAL;
}
@@ -573,7 +657,7 @@ int fit_open_image(struct fit_handle *handle, void *configuration,
return -EINVAL;
}
- if (conf_node)
+ if (configuration)
ret = fit_verify_hash(handle, image, data, data_len);
else
ret = fit_image_verify_signature(handle, image, data, data_len);
@@ -581,6 +665,10 @@ int fit_open_image(struct fit_handle *handle, void *configuration,
if (ret < 0)
return ret;
+ ret = fit_handle_decompression(image, type, &data, &data_len);
+ if (ret)
+ return ret;
+
*outdata = data;
*outsize = data_len;
@@ -608,27 +696,32 @@ static int fit_config_verify_signature(struct fit_handle *handle, struct device_
}
for_each_child_of_node(conf_node, sig_node) {
+ if (!of_node_has_prefix(sig_node, "signature"))
+ continue;
+
if (handle->verbose)
- of_print_nodes(sig_node, 0);
+ of_print_nodes(sig_node, 0, ~0);
+
ret = fit_verify_signature(sig_node, handle->fit);
if (ret < 0)
return ret;
}
if (ret < 0) {
- pr_err("configuration '%s' does not have a signature\n",
- conf_node->full_name);
+ pr_err("configuration '%pOF' does not have a signature\n", conf_node);
return ret;
}
return ret;
}
-static int fit_find_compatible_unit(struct device_node *conf_node,
+static int fit_find_compatible_unit(struct fit_handle *handle,
+ struct device_node *conf_node,
const char **unit)
{
struct device_node *child = NULL;
struct device_node *barebox_root;
+ int best_score = 0;
const char *machine;
int ret;
@@ -641,13 +734,55 @@ static int fit_find_compatible_unit(struct device_node *conf_node,
return -ENOENT;
for_each_child_of_node(conf_node, child) {
- if (of_device_is_compatible(child, machine)) {
+ int score = of_device_is_compatible(child, machine);
+
+ if (!score && !of_property_present(child, "compatible") &&
+ of_property_present(child, "fdt")) {
+ struct device_node *image;
+ const char *unit = "fdt";
+ int data_len;
+ const void *data;
+ int ret;
+
+ ret = fit_get_image(handle, child, &unit, &image);
+ if (ret)
+ goto next;
+
+ data = of_get_property(image, "data", &data_len);
+ if (!data) {
+ ret = -EINVAL;
+ goto next;
+ }
+
+ ret = fit_handle_decompression(image, "fdt", &data, &data_len);
+ if (ret) {
+ ret = -EILSEQ;
+ goto next;
+ }
+
+ score = fdt_machine_is_compatible(data, data_len, machine);
+
+ of_delete_property_by_name(image, "uncompressed-data");
+next:
+ if (ret)
+ pr_warn("skipping malformed configuration: %pOF (%pe)\n",
+ child, ERR_PTR(ret));
+ }
+
+ if (score > best_score) {
+ best_score = score;
*unit = child->name;
- pr_info("matching unit '%s' found\n", *unit);
- return 0;
+
+ if (score == OF_DEVICE_COMPATIBLE_MAX_SCORE)
+ break;
}
}
+ if (best_score) {
+ pr_info("matching unit '%s' found\n", *unit);
+ return 0;
+ }
+
default_unit:
pr_info("No match found. Trying default.\n");
if (of_property_read_string(conf_node, "default", unit) == 0)
@@ -679,7 +814,7 @@ void *fit_open_configuration(struct fit_handle *handle, const char *name)
if (name) {
unit = name;
} else {
- ret = fit_find_compatible_unit(conf_node, &unit);
+ ret = fit_find_compatible_unit(handle, conf_node, &unit);
if (ret) {
pr_info("Couldn't get a valid configuration. Aborting.\n");
return ERR_PTR(ret);
@@ -707,7 +842,7 @@ static int fit_do_open(struct fit_handle *handle)
const char *desc = "(no description)";
struct device_node *root;
- root = of_unflatten_dtb_const(handle->fit);
+ root = of_unflatten_dtb_const(handle->fit, handle->size);
if (IS_ERR(root))
return PTR_ERR(root);
@@ -765,6 +900,7 @@ struct fit_handle *fit_open_buf(const void *buf, size_t size, bool verbose,
* @filename: The filename of the FIT image
* @verbose: If true, be more verbose
* @verify: The verify mode
+ * @max_size: maximum length to read from file
*
* This opens a FIT image found in @filename. The returned handle is used as
* context for the other FIT functions.
@@ -772,7 +908,7 @@ struct fit_handle *fit_open_buf(const void *buf, size_t size, bool verbose,
* Return: A handle to a FIT image or a ERR_PTR
*/
struct fit_handle *fit_open(const char *filename, bool verbose,
- enum bootm_verify verify)
+ enum bootm_verify verify, loff_t max_size)
{
struct fit_handle *handle;
int ret;
@@ -783,8 +919,8 @@ struct fit_handle *fit_open(const char *filename, bool verbose,
handle->verify = verify;
ret = read_file_2(filename, &handle->size, &handle->fit_alloc,
- FILESIZE_MAX);
- if (ret) {
+ max_size);
+ if (ret && ret != -EFBIG) {
pr_err("unable to read %s: %s\n", filename, strerror(-ret));
return ERR_PTR(ret);
}