diff options
Diffstat (limited to 'common/bbu.c')
-rw-r--r-- | common/bbu.c | 246 |
1 files changed, 193 insertions, 53 deletions
diff --git a/common/bbu.c b/common/bbu.c index b976b99d7c..ba2566acdf 100644 --- a/common/bbu.c +++ b/common/bbu.c @@ -1,20 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * bbu.c - barebox update functions * * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * 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. */ + +#define pr_fmt(fmt) "bbu: " fmt + #include <common.h> #include <bbu.h> #include <linux/list.h> @@ -27,22 +19,48 @@ #include <malloc.h> #include <linux/stat.h> #include <image-metadata.h> +#include <environment.h> +#include <file-list.h> static LIST_HEAD(bbu_image_handlers); -int bbu_handlers_iterate(int (*fn)(struct bbu_handler *, void *), void *ctx) +static void append_bbu_entry(const char *_name, + const char *devicefile, + struct file_list *files) +{ + char *name; + + name = basprintf("bbu-%s", _name); + + if (file_list_add_entry(files, name, devicefile, 0)) + pr_warn("duplicate partition name %s\n", name); + + free(name); +} + +void bbu_append_handlers_to_file_list(struct file_list *files) { struct bbu_handler *handler; list_for_each_entry(handler, &bbu_image_handlers, list) { - int ret; + const char *cdevname; + struct stat s; + char *devpath; - ret = fn(handler, ctx); - if (ret) - return ret; - } + cdevname = devpath_to_name(handler->devicefile); + device_detect_by_name(cdevname); - return 0; + devpath = basprintf("/dev/%s", cdevname); + + if (stat(devpath, &s) == 0) { + append_bbu_entry(handler->name, devpath, files); + } else { + pr_info("Skipping unavailable handler bbu-%s\n", + handler->name); + } + + free(devpath); + } } int bbu_force(struct bbu_data *data, const char *fmt, ...) @@ -77,17 +95,23 @@ out: int bbu_confirm(struct bbu_data *data) { int key; + const char *prompt; if (data->flags & BBU_FLAG_YES) - return 0; + prompt = "."; + else + prompt = " (y/n)?"; if (data->imagefile) - printf("update barebox from %s using handler %s to %s (y/n)?\n", - data->imagefile, data->handler_name, - data->devicefile); + printf("update barebox on %s from %s using handler %s%s\n", + data->devicefile, data->imagefile, + data->handler_name, prompt); else - printf("Refresh barebox on %s using handler %s (y/n)?\n", - data->devicefile, data->handler_name); + printf("Refresh barebox on %s using handler %s%s\n", + data->devicefile, data->handler_name, prompt); + + if (data->flags & BBU_FLAG_YES) + return 0; key = read_key(); @@ -136,36 +160,47 @@ struct bbu_handler *bbu_find_handler_by_device(const char *devicepath) return NULL; } -static int bbu_check_of_compat(struct bbu_data *data) +static int bbu_check_of_compat(struct bbu_data *data, unsigned short of_compat_nr) { + const struct imd_header *imd = data->imd_data; + const struct imd_header *of_compat; struct device_node *root_node; const char *machine, *str; int ret; - const struct imd_header *of_compat; if (!IS_ENABLED(CONFIG_OFDEVICE) || !IS_ENABLED(CONFIG_IMD)) return 0; - of_compat = imd_find_type(data->imd_data, IMD_TYPE_OF_COMPATIBLE); - if (!of_compat) - return 0; - root_node = of_get_root_node(); if (!root_node) return 0; - str = imd_string_data(of_compat, 0); - - if (of_machine_is_compatible(str)) { - pr_info("Devicetree compatible \"%s\" matches current machine\n", str); + if (!of_compat_nr) return 0; - } ret = of_property_read_string(root_node, "compatible", &machine); if (ret) return 0; - if (!bbu_force(data, "machine is incompatible with \"%s\", have \"%s\"\n", str, machine)) + for (; of_compat_nr; of_compat_nr--) { + of_compat = imd_find_type(imd, IMD_TYPE_OF_COMPATIBLE); + if (!of_compat) + return 0; + + str = imd_string_data(of_compat, 0); + + if (of_machine_is_compatible(str)) { + pr_info("Devicetree compatible \"%s\" matches current machine\n", str); + return 0; + } + + pr_debug("machine is incompatible with \"%s\", have \"%s\"\n", + str, machine); + + imd = of_compat; + } + + if (!bbu_force(data, "incompatible machine \"%s\"\n", machine)) return -EINVAL; return 0; @@ -173,6 +208,7 @@ static int bbu_check_of_compat(struct bbu_data *data) static int bbu_check_metadata(struct bbu_data *data) { + unsigned short imd_of_compat_nr = 0; const struct imd_header *imd; int ret; char *str; @@ -193,6 +229,9 @@ static int bbu_check_metadata(struct bbu_data *data) imd_for_each(data->imd_data, imd) { uint32_t type = imd_read_type(imd); + if (imd_read_type(imd) == IMD_TYPE_OF_COMPATIBLE) + imd_of_compat_nr++; + if (!imd_is_string(type)) continue; @@ -202,7 +241,7 @@ static int bbu_check_metadata(struct bbu_data *data) free(str); } - ret = bbu_check_of_compat(data); + ret = bbu_check_of_compat(data, imd_of_compat_nr); if (ret) return ret; @@ -286,23 +325,107 @@ struct bbu_std { enum filetype filetype; }; -static int bbu_std_file_handler(struct bbu_handler *handler, - struct bbu_data *data) +int bbu_mmcboot_handler(struct bbu_handler *handler, struct bbu_data *data, + int (*chained_handler)(struct bbu_handler *, struct bbu_data *)) +{ + struct bbu_data _data = *data; + int ret; + char *devicefile = NULL, *bootpartvar = NULL, *bootackvar = NULL; + const char *bootpart; + const char *devname = devpath_to_name(data->devicefile); + + ret = device_detect_by_name(devname); + if (ret) { + pr_err("Couldn't detect device '%s'\n", devname); + return ret; + } + + ret = asprintf(&bootpartvar, "%s.boot", devname); + if (ret < 0) + return ret; + + bootpart = getenv(bootpartvar); + if (!bootpart) { + ret = -ENOENT; + goto out; + } + + if (!strcmp(bootpart, "boot0")) { + bootpart = "boot1"; + } else { + bootpart = "boot0"; + } + + ret = asprintf(&devicefile, "/dev/%s.%s", devname, bootpart); + if (ret < 0) + goto out; + + _data.devicefile = devicefile; + + ret = chained_handler(handler, &_data); + if (ret < 0) + goto out; + + if (handler->flags & BBU_HANDLER_FLAG_MMC_BOOT_ACK) { + ret = asprintf(&bootackvar, "%s.boot_ack", devname); + if (ret < 0) + goto out; + + ret = setenv(bootackvar, "1"); + if (ret) + goto out; + } + + /* on success switch boot source */ + ret = setenv(bootpartvar, bootpart); + +out: + free(bootackvar); + free(devicefile); + free(bootpartvar); + + return ret; +} + +static int bbu_internal_mmcboot_update(struct bbu_handler *handler, + struct bbu_data *data) +{ + int ret; + + ret = bbu_mmcboot_handler(handler, data, bbu_std_file_handler); + if (ret == -ENOENT) + pr_err("Couldn't read the value of .boot parameter\n"); + + return ret; +} + +int bbu_mmcboot_register_handler(const char *name, + const char *devicefile, + unsigned long flags) +{ + struct bbu_handler *handler; + int ret; + + handler = xzalloc(sizeof(*handler)); + handler->devicefile = devicefile; + handler->name = name; + handler->handler = bbu_internal_mmcboot_update; + handler->flags = flags; + + ret = bbu_register_handler(handler); + if (ret) + free(handler); + + return ret; +} + +int bbu_std_file_handler(struct bbu_handler *handler, + struct bbu_data *data) { - struct bbu_std *std = container_of(handler, struct bbu_std, handler); int fd, ret; - enum filetype filetype; struct stat s; unsigned oflags = O_WRONLY; - filetype = file_detect_type(data->image, data->len); - if (filetype != std->filetype) { - if (!bbu_force(data, "incorrect image type. Expected: %s, got %s", - file_type_to_string(std->filetype), - file_type_to_string(filetype))) - return -EINVAL; - } - device_detect_by_name(devpath_to_name(data->devicefile)); ret = stat(data->devicefile, &s); @@ -324,7 +447,7 @@ static int bbu_std_file_handler(struct bbu_handler *handler, return fd; ret = protect(fd, data->len, 0, 0); - if (ret && ret != -ENOSYS) { + if (ret && (ret != -ENOSYS) && (ret != -ENOTSUPP)) { printf("unprotecting %s failed with %s\n", data->devicefile, strerror(-ret)); goto err_close; @@ -351,6 +474,23 @@ err_close: return ret; } +static int bbu_std_file_handler_checked(struct bbu_handler *handler, + struct bbu_data *data) +{ + struct bbu_std *std = container_of(handler, struct bbu_std, handler); + enum filetype filetype; + + filetype = file_detect_type(data->image, data->len); + if (filetype != std->filetype) { + if (!bbu_force(data, "incorrect image type. Expected: %s, got %s", + file_type_to_string(std->filetype), + file_type_to_string(filetype))) + return -EINVAL; + } + + return bbu_std_file_handler(handler, data); +} + /** * bbu_register_std_file_update() - register a barebox update handler for a * standard file-to-device-copy operation @@ -361,7 +501,7 @@ err_close: * * This update handler us suitable for a standard file-to-device copy operation. * The handler checks for a filetype and unprotects/erases the device if - * necessary. If devicefile belongs to a device then the device is checkd for + * necessary. If devicefile belongs to a device then the device is checked for * enough space before touching it. * * Return: 0 if successful, negative error code otherwise @@ -381,7 +521,7 @@ int bbu_register_std_file_update(const char *name, unsigned long flags, handler->flags = flags; handler->devicefile = devicefile; handler->name = name; - handler->handler = bbu_std_file_handler; + handler->handler = bbu_std_file_handler_checked; ret = bbu_register_handler(handler); if (ret) |