summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2021-10-12 09:33:52 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2021-10-12 10:28:32 +0200
commit6c8c249deaa01f8c1c2b3381242f5e52273d25c6 (patch)
tree9b928ad7e2d59ef9f3c7478db36c9e3a7a2b8d2f /arch
parent839d934c79aee95a7d67d2f8dceacbfd0e5ea848 (diff)
downloadbarebox-6c8c249deaa01f8c1c2b3381242f5e52273d25c6.tar.gz
barebox-6c8c249deaa01f8c1c2b3381242f5e52273d25c6.tar.xz
ARM: Rockchip: RK3568: implement failsafe barebox update
The RK3568 ROM searches for valid boot images at different positions on SD/eMMC cards. This can be used to implement a failsafe barebox update which is immune against power failures. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Link: https://lore.barebox.org/20211012073352.4071559-9-s.hauer@pengutronix.de Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/boards/rockchip-rk3568-evb/board.c4
-rw-r--r--arch/arm/dts/rk3568-evb1-v10.dts10
-rw-r--r--arch/arm/mach-rockchip/Makefile1
-rw-r--r--arch/arm/mach-rockchip/bbu.c141
-rw-r--r--arch/arm/mach-rockchip/include/mach/bbu.h9
5 files changed, 150 insertions, 15 deletions
diff --git a/arch/arm/boards/rockchip-rk3568-evb/board.c b/arch/arm/boards/rockchip-rk3568-evb/board.c
index 2d472d1331..09385bea29 100644
--- a/arch/arm/boards/rockchip-rk3568-evb/board.c
+++ b/arch/arm/boards/rockchip-rk3568-evb/board.c
@@ -28,8 +28,8 @@ static int rk3568_evb_probe(struct device_d *dev)
else
of_device_enable_path("/chosen/environment-emmc");
- rk3568_bbu_mmc_register("emmc", BBU_HANDLER_FLAG_DEFAULT, "/dev/emmc.barebox");
- rk3568_bbu_mmc_register("sd", 0, "/dev/sd.barebox");
+ rk3568_bbu_mmc_register("emmc", BBU_HANDLER_FLAG_DEFAULT, "/dev/emmc");
+ rk3568_bbu_mmc_register("sd", 0, "/dev/sd");
return 0;
}
diff --git a/arch/arm/dts/rk3568-evb1-v10.dts b/arch/arm/dts/rk3568-evb1-v10.dts
index ebfd45ada8..6f1eebc619 100644
--- a/arch/arm/dts/rk3568-evb1-v10.dts
+++ b/arch/arm/dts/rk3568-evb1-v10.dts
@@ -475,11 +475,6 @@
#address-cells = <2>;
#size-cells = <2>;
- partition@8000 {
- label = "barebox";
- reg = <0x0 0x8000 0x0 0x400000>;
- };
-
environment_emmc: partition@408000 {
label = "barebox-environment";
reg = <0x0 0x408000 0x0 0x8000>;
@@ -506,11 +501,6 @@
#address-cells = <2>;
#size-cells = <2>;
- partition@8000 {
- label = "barebox";
- reg = <0x0 0x8000 0x0 0x400000>;
- };
-
environment_sd: partition@408000 {
label = "barebox-environment";
reg = <0x0 0x408000 0x0 0x8000>;
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index ebaa3a5450..66bcdba2eb 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_ARCH_RK3188) += rk3188.o
obj-$(CONFIG_ARCH_RK3288) += rk3288.o
obj-pbl-$(CONFIG_ARCH_RK3568) += rk3568.o
obj-$(CONFIG_ARCH_RK3568) += bootm.o
+obj-$(CONFIG_BAREBOX_UPDATE) += bbu.o
diff --git a/arch/arm/mach-rockchip/bbu.c b/arch/arm/mach-rockchip/bbu.c
new file mode 100644
index 0000000000..71bbac27e8
--- /dev/null
+++ b/arch/arm/mach-rockchip/bbu.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <common.h>
+#include <malloc.h>
+#include <bbu.h>
+#include <filetype.h>
+#include <errno.h>
+#include <fs.h>
+#include <fcntl.h>
+#include <linux/sizes.h>
+#include <linux/stat.h>
+#include <ioctl.h>
+#include <environment.h>
+#include <mach/bbu.h>
+#include <libfile.h>
+#include <linux/bitfield.h>
+#include <mach/rk3568-regs.h>
+
+/* The MaskROM looks for images on these locations: */
+#define IMG_OFFSET_0 (0 * SZ_1K + SZ_32K)
+#define IMG_OFFSET_1 (512 * SZ_1K + SZ_32K)
+#define IMG_OFFSET_2 (1024 * SZ_1K + SZ_32K)
+#define IMG_OFFSET_3 (1536 * SZ_1K + SZ_32K)
+#define IMG_OFFSET_4 (2048 * SZ_1K + SZ_32K)
+
+#define RK3568_IRAM_ACTIVE_BOOT_SLOT GENMASK(12, 10)
+
+static int rk3568_get_active_slot(void)
+{
+ return FIELD_GET(RK3568_IRAM_ACTIVE_BOOT_SLOT,
+ readl(RK3568_IRAM_BASE + 0x14));
+}
+
+/*
+ * The strategy here is:
+ * The MaskROM iterates over the above five locations until it finds a valid
+ * boot image. The images are protected with sha sums, so any change to an
+ * image on disk is invalidating it. We first check if we have enough space to
+ * write two copies of barebox. To make it simple we only use IMG_OFFSET_0 and
+ * IMG_OFFSET_4 which leaves the maximum size for a single image. When there's
+ * not enough free space on the beginning of the disk we only write a single
+ * image. When we have enough space for two images we first write the inactive one
+ * (leaving the active one intact). Afterwards we write the active one which
+ * leaves the previously written inactive image as a fallback in case writing the
+ * first one gets interrupted.
+ */
+static int rk3568_bbu_mmc_handler(struct bbu_handler *handler,
+ struct bbu_data *data)
+{
+ enum filetype filetype;
+ int ret, fd, wr0, wr1;
+ loff_t space;
+ const char *cdevname;
+
+ filetype = file_detect_type(data->image, data->len);
+ if (filetype != filetype_rockchip_rkns_image) {
+ if (!bbu_force(data, "incorrect image type. Expected: %s, got %s",
+ file_type_to_string(filetype_rockchip_rkns_image),
+ file_type_to_string(filetype)))
+ return -EINVAL;
+ }
+
+ cdevname = devpath_to_name(data->devicefile);
+
+ device_detect_by_name(cdevname);
+
+ ret = bbu_confirm(data);
+ if (ret)
+ return ret;
+
+ space = cdev_unallocated_space(cdev_by_name(cdevname));
+
+ if (space < IMG_OFFSET_0 + data->len) {
+ pr_err("Unallocated space on %s is too small for one image\n",
+ data->devicefile);
+ return -ENOSPC;
+ }
+
+ fd = open(data->devicefile, O_WRONLY);
+ if (fd < 0)
+ return fd;
+
+ if (space >= IMG_OFFSET_4 + data->len) {
+ int slot = rk3568_get_active_slot();
+
+ pr_info("Unallocated space is enough for two copies, doing failsafe update\n");
+
+ if (slot == 0) {
+ wr0 = IMG_OFFSET_4;
+ wr1 = IMG_OFFSET_0;
+ } else {
+ wr0 = IMG_OFFSET_0;
+ wr1 = IMG_OFFSET_4;
+ }
+ } else {
+ wr0 = IMG_OFFSET_0;
+ wr1 = 0;
+ }
+
+ ret = pwrite_full(fd, data->image, data->len, wr0);
+ if (ret < 0) {
+ pr_err("writing to %s failed with %s\n", data->devicefile,
+ strerror(-ret));
+ goto err_close;
+ }
+
+ if (wr1) {
+ ret = pwrite_full(fd, data->image, data->len, wr1);
+ if (ret < 0) {
+ pr_err("writing to %s failed with %s\n", data->devicefile,
+ strerror(-ret));
+ goto err_close;
+ }
+ }
+
+ ret = 0;
+
+err_close:
+ close(fd);
+
+ return ret;
+}
+
+int rk3568_bbu_mmc_register(const char *name, unsigned long flags,
+ const char *devicefile)
+{
+ struct bbu_handler *handler;
+ int ret;
+
+ handler = xzalloc(sizeof(*handler));
+
+ handler->flags = flags;
+ handler->devicefile = devicefile;
+ handler->name = name;
+ handler->handler = rk3568_bbu_mmc_handler;
+
+ ret = bbu_register_handler(handler);
+ if (ret)
+ free(handler);
+
+ return ret;
+}
diff --git a/arch/arm/mach-rockchip/include/mach/bbu.h b/arch/arm/mach-rockchip/include/mach/bbu.h
index e61e0615e2..7fb08a0a9e 100644
--- a/arch/arm/mach-rockchip/include/mach/bbu.h
+++ b/arch/arm/mach-rockchip/include/mach/bbu.h
@@ -3,12 +3,15 @@
#include <bbu.h>
+#ifdef CONFIG_BAREBOX_UPDATE
+int rk3568_bbu_mmc_register(const char *name, unsigned long flags,
+ const char *devicefile);
+#else
static inline int rk3568_bbu_mmc_register(const char *name, unsigned long flags,
const char *devicefile)
{
- return bbu_register_std_file_update(name, flags,
- devicefile, filetype_rockchip_rkns_image);
-
+ return -ENOSYS;
}
+#endif
# endif /* __MACH_ROCKCHIP_BBU_H */