summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-12-22 19:23:29 +0000
committerAidan MacDonald <amachronic@protonmail.com>2024-03-31 16:57:19 +0100
commit6ffd42548bf10cda13a01555ff4fa56d4213cdf2 (patch)
tree975d6a4f88a0a3469c1450476e841ef0bf8fbb85
parentaf644e02a151bb6d4c229cc1d4846c7ffe952135 (diff)
downloadrockbox-6ffd42548b.tar.gz
rockbox-6ffd42548b.zip
multiboot: Refactor boot data validation, add version numbers
Instead of verifying the CRC before every access of the boot data, verify the CRC once at startup and set a flag to indicate the boot data is valid. Also add a framework to support multiple boot protocol versions. Firmware declares the maximum supported protocol version using a version byte in the boot data header. The bootloader chooses the highest version supported by it and the firmware when deciding what boot protocol to use. Change-Id: I810194625dc0833f026d2a23b8d64ed467fa6aca
-rw-r--r--apps/debug_menu.c39
-rw-r--r--apps/main.c5
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/common/bootdata.c74
-rw-r--r--firmware/common/multiboot.c55
-rw-r--r--firmware/common/rb-loader.c2
-rw-r--r--firmware/export/bootdata.h10
-rw-r--r--firmware/export/multiboot.h2
-rw-r--r--firmware/include/dircache_redirect.h6
-rw-r--r--firmware/rolo.c9
-rw-r--r--firmware/target/arm/pp/mi4-loader.c2
11 files changed, 149 insertions, 58 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 5b73f8badd..5e2451e41b 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -2530,38 +2530,31 @@ static bool dbg_pic(void)
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
static bool dbg_boot_data(void)
{
- unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
struct simplelist_info info;
info.scroll_all = true;
simplelist_info_init(&info, "Boot data", 1, NULL);
simplelist_set_line_count(0);
-#if defined(HAVE_MULTIBOOT)
- char rootpath[MAX_PATH / 2] = RB_ROOT_CONTENTS_DIR;
- int boot_volume = 0;
- if(crc == boot_data.crc)
+ if (!boot_data_valid)
{
- boot_volume = boot_data.boot_volume; /* boot volume contained in uint8_t payload */
- int rtlen = get_redirect_dir(rootpath, sizeof(rootpath), boot_volume, "", "");
- while (rtlen > 0 && rootpath[--rtlen] == PATH_SEPCH) /* remove extra separators */
- rootpath[rtlen] = '\0';
+ simplelist_addline("Boot data invalid");
+ simplelist_addline("Magic[0]: %08lx", boot_data.magic[0]);
+ simplelist_addline("Magic[1]: %08lx", boot_data.magic[1]);
+ simplelist_addline("Length: %lu", boot_data.length);
}
- simplelist_addline("Boot Volume: <%lu>", boot_volume);
- simplelist_addline("Root:");
- simplelist_addline("%s", rootpath);
- simplelist_addline("");
-#endif
+ else
+ {
+ simplelist_addline("Boot data valid");
+ simplelist_addline("Version: %d", (int)boot_data.version);
+ simplelist_addline("Boot volume: %d", (int)boot_data.boot_volume);
+ }
+
simplelist_addline("Bootdata RAW:");
- if (crc != boot_data.crc)
- simplelist_addline("Magic: %.8s", boot_data.magic);
- simplelist_addline("Length: %lu", boot_data.length);
- simplelist_addline("CRC: %lx", boot_data.crc);
- (crc == boot_data.crc) ? simplelist_addline("CRC: OK!") :
- simplelist_addline("CRC: BAD");
- for (unsigned i = 0; i < boot_data.length; i += 4)
+ for (size_t i = 0; i < boot_data.length; i += 4)
{
- simplelist_addline("%02x: %02x %02x %02x %02x", i, boot_data.payload[i],
- boot_data.payload[i+1], boot_data.payload[i+2], boot_data.payload[i+3]);
+ simplelist_addline("%02x: %02x %02x %02x %02x", i,
+ boot_data.payload[i + 0], boot_data.payload[i + 1],
+ boot_data.payload[i + 2], boot_data.payload[i + 3]);
}
return simplelist_show_list(&info);
diff --git a/apps/main.c b/apps/main.c
index 1e012efb3c..cf7d302fc9 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -77,6 +77,7 @@
#include "statusbar-skinned.h"
#include "bootchart.h"
#include "logdiskf.h"
+#include "bootdata.h"
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
#include "notification.h"
#endif
@@ -445,6 +446,10 @@ static void init(void)
core_allocator_init();
kernel_init();
+#if defined(HAVE_BOOTDATA) && !defined(BOOTLOADER)
+ verify_boot_data();
+#endif
+
/* early early early! */
filesystem_init();
diff --git a/firmware/SOURCES b/firmware/SOURCES
index f6d35fb5ea..87cadfd55f 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -57,6 +57,9 @@ target/hosted/rolo.c
#if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT)
common/multiboot.c
+#ifndef BOOTLOADER
+common/bootdata.c
+#endif
#endif
#ifdef HAVE_SDL
diff --git a/firmware/common/bootdata.c b/firmware/common/bootdata.c
new file mode 100644
index 0000000000..fa74c5fe91
--- /dev/null
+++ b/firmware/common/bootdata.c
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2022 by Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "bootdata.h"
+#include "crc32.h"
+#include <stddef.h>
+
+#ifdef BOOTLOADER
+# error "not to be included in bootloader builds"
+#endif
+
+bool boot_data_valid;
+
+static bool verify_boot_data_v0(void) INIT_ATTR;
+static bool verify_boot_data_v0(void)
+{
+ /* validate protocol version */
+ if (boot_data.version != 0)
+ return false;
+
+ /* validate length */
+ if (boot_data.length != 4)
+ return false;
+
+ return true;
+}
+
+struct verify_bd_entry
+{
+ int version;
+ bool (*verify) (void);
+};
+
+static const struct verify_bd_entry verify_bd[] INITDATA_ATTR = {
+ { 0, verify_boot_data_v0 },
+};
+
+void verify_boot_data(void)
+{
+ /* verify payload with checksum - all protocol versions */
+ uint32_t crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
+ if (crc != boot_data.crc)
+ return;
+
+ /* apply verification specific to the protocol version */
+ for (size_t i = 0; i < ARRAYLEN(verify_bd); ++i)
+ {
+ const struct verify_bd_entry *e = &verify_bd[i];
+ if (e->version == boot_data.version)
+ {
+ if (e->verify())
+ boot_data_valid = true;
+
+ return;
+ }
+ }
+}
diff --git a/firmware/common/multiboot.c b/firmware/common/multiboot.c
index c2cedc102d..c292aa1c30 100644
--- a/firmware/common/multiboot.c
+++ b/firmware/common/multiboot.c
@@ -26,44 +26,57 @@
#include <string.h>
#include <stdio.h>
+static void write_bootdata_v0(struct boot_data_t *data, unsigned int boot_volume)
+{
+ memset(data->payload, data->length, 0);
+
+ data->boot_volume = boot_volume;
+ data->version = 0;
+}
+
/* Write bootdata into location in FIRMWARE marked by magic header
* Assumes buffer is already loaded with the firmware image
* We just need to find the location and write data into the
* payload region along with the crc for later verification and use.
* Returns payload len on success,
- * On error returns EKEY_NOT_FOUND
+ * On error returns false
*/
-int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
+bool write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
{
- struct boot_data_t bl_boot_data;
- struct boot_data_t *fw_boot_data = NULL;
int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t);
- int payload_len = EKEY_NOT_FOUND;
/* search for boot data header prior to search_len */
for(int i = 0; i < search_len; i++)
{
- fw_boot_data = (struct boot_data_t*) &buf[i];
- if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 ||
- fw_boot_data->magic[1] != BOOT_DATA_MAGIC1)
+ struct boot_data_t *data = (struct boot_data_t *)&buf[i];
+ if (data->magic[0] != BOOT_DATA_MAGIC0 ||
+ data->magic[1] != BOOT_DATA_MAGIC1)
continue;
- memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE);
- bl_boot_data.boot_volume = boot_volume;
+ /* Ignore it if the length extends past the end of the buffer. */
+ int data_len = offsetof(struct boot_data_t, payload) + data->length;
+ if (i + data_len > len)
+ continue;
+
+ /* Determine the maximum supported boot protocol version.
+ * Version 0 firmware may use 0 or 0xff, all other versions
+ * declare the highest supported version in the version byte. */
+ int proto_version = 0;
+ if (data->version < 0xff)
+ proto_version = MIN(BOOT_DATA_VERSION, data->version);
- memset(fw_boot_data->payload, 0, fw_boot_data->length);
- /* determine maximum bytes we can write to firmware
- BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */
- payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length);
- fw_boot_data->length = payload_len;
- /* copy data to FIRMWARE bootdata struct */
- memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
- /* crc will be used within the firmware to check validity of bootdata */
- fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
- break;
+ /* Write boot data according to the selected protocol */
+ if (proto_version == 0)
+ write_bootdata_v0(data, boot_volume);
+ else
+ break;
+ /* Calculate payload CRC, used by all protocol versions. */
+ data->crc = crc_32(data->payload, data->length, 0xffffffff);
+ return true;
}
- return payload_len;
+
+ return false;
}
#ifdef HAVE_MULTIBOOT
diff --git a/firmware/common/rb-loader.c b/firmware/common/rb-loader.c
index 0256f21884..61d8b1ddd2 100644
--- a/firmware/common/rb-loader.c
+++ b/firmware/common/rb-loader.c
@@ -112,7 +112,7 @@ int load_firmware(unsigned char* buf, const char* firmware, int buffer_size)
{
ret = load_firmware_filename(buf, filename, buffer_size);
/* if firmware has no boot_data don't load from external drive */
- if (write_bootdata(buf, ret, i) <= 0)
+ if (!write_bootdata(buf, ret, i))
ret = EKEY_NOT_FOUND;
}
/* if ret is valid breaks from loop to continue loading */
diff --git a/firmware/export/bootdata.h b/firmware/export/bootdata.h
index 322d50c20d..319f442511 100644
--- a/firmware/export/bootdata.h
+++ b/firmware/export/bootdata.h
@@ -22,6 +22,7 @@
#ifndef __ASSEMBLER__
#include <stdint.h>
+#include "system.h"
#endif
/* /!\ This file can be included in assembly files /!\ */
@@ -36,6 +37,7 @@
#define BOOT_DATA_MAGIC0 ('r' | 'b' << 8 | 'm' << 16 | 'a' << 24)
#define BOOT_DATA_MAGIC1 ('g' | 'i' << 8 | 'c' << 16 | '!' << 24)
+#define BOOT_DATA_VERSION 0
/* maximum size of payload */
#define BOOT_DATA_PAYLOAD_SIZE 4
@@ -58,6 +60,7 @@ struct boot_data_t
struct
{
uint8_t boot_volume;
+ uint8_t version;
};
uint8_t payload[BOOT_DATA_PAYLOAD_SIZE];
};
@@ -65,6 +68,9 @@ struct boot_data_t
#if !defined(BOOTLOADER)
extern struct boot_data_t boot_data;
+extern bool boot_data_valid;
+
+void verify_boot_data(void) INIT_ATTR;
#endif
#else /* __ASSEMBLER__ */
@@ -76,7 +82,9 @@ boot_data:
.word BOOT_DATA_MAGIC0
.word BOOT_DATA_MAGIC1
.word BOOT_DATA_PAYLOAD_SIZE
- .space BOOT_DATA_PAYLOAD_SIZE, 0xff /* payload, initialised with value 0xff */
+ .byte 0xff /* boot volume */
+ .byte BOOT_DATA_VERSION /* maximum supported boot protocol version */
+ .space (BOOT_DATA_PAYLOAD_SIZE - 2), 0xff /* remainder of payload */
.endm
#endif
diff --git a/firmware/export/multiboot.h b/firmware/export/multiboot.h
index 0132b8531f..4174e71d61 100644
--- a/firmware/export/multiboot.h
+++ b/firmware/export/multiboot.h
@@ -21,7 +21,7 @@
#ifndef __MULTIBOOT_H__
#define __MULTIBOOT_H__
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume);
+extern bool write_bootdata(unsigned char* buf, int len, unsigned int boot_volume);
#ifdef HAVE_MULTIBOOT
extern int get_redirect_dir(char* buf, int buffer_size, int volume,
const char* rootdir, const char* firmware);
diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h
index f51ce70690..d4d72978c0 100644
--- a/firmware/include/dircache_redirect.h
+++ b/firmware/include/dircache_redirect.h
@@ -29,7 +29,6 @@
#include "rb-loader.h"
#include "multiboot.h"
#include "bootdata.h"
-#include "crc32.h"
#endif
#ifndef RB_ROOT_VOL_HIDDEN
@@ -144,8 +143,7 @@ static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
char rtpath[MAX_PATH / 2];
make_volume_root(volume, path);
- unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
- if (crc > 0 && crc == boot_data.crc)
+ if (boot_data_valid)
{
/* we need to mount the drive before we can access it */
root_mount_path(path, 0); /* root could be different folder don't hide */
@@ -174,7 +172,7 @@ static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
root_mount_path(rtpath, NSITEM_CONTENTS);
}
- } /*CRC OK*/
+ }
else
{
standard_redirect:
diff --git a/firmware/rolo.c b/firmware/rolo.c
index a3e6d5c2b9..1b37b6f771 100644
--- a/firmware/rolo.c
+++ b/firmware/rolo.c
@@ -250,12 +250,9 @@ int rolo_load(const char* filename)
err = LOAD_FIRMWARE(filebuf, filename, filebuf_size);
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
- /* write the bootdata as if rolo were the bootloader */
- unsigned int crc = 0;
- if (strcmp(filename, BOOTDIR "/" BOOTFILE) == 0)
- crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
-
- if(crc > 0 && crc == boot_data.crc)
+ /* write the bootdata as if rolo were the bootloader
+ * FIXME: this won't work for root redirect... */
+ if (!strcmp(filename, BOOTDIR "/" BOOTFILE) && boot_data_valid)
write_bootdata(filebuf, filebuf_size, boot_data.boot_volume); /* rb-loader.c */
#endif
diff --git a/firmware/target/arm/pp/mi4-loader.c b/firmware/target/arm/pp/mi4-loader.c
index f609e3ff7a..14bb5e6f47 100644
--- a/firmware/target/arm/pp/mi4-loader.c
+++ b/firmware/target/arm/pp/mi4-loader.c
@@ -256,7 +256,7 @@ int load_mi4(unsigned char* buf, const char* firmware, unsigned int buffer_size)
{
ret = load_mi4_filename(buf, filename, buffer_size);
/* if firmware has no boot_data don't load from external drive */
- if (write_bootdata(buf, ret, i) <= 0)
+ if (!write_bootdata(buf, ret, i))
ret = EKEY_NOT_FOUND;
}
/* if ret is valid breaks from loop to continue loading */