summaryrefslogtreecommitdiff
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-6ffd42548bf10cda13a01555ff4fa56d4213cdf2.tar.gz
rockbox-6ffd42548bf10cda13a01555ff4fa56d4213cdf2.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)
2530#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) 2530#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
2531static bool dbg_boot_data(void) 2531static bool dbg_boot_data(void)
2532{ 2532{
2533 unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
2534 struct simplelist_info info; 2533 struct simplelist_info info;
2535 info.scroll_all = true; 2534 info.scroll_all = true;
2536 simplelist_info_init(&info, "Boot data", 1, NULL); 2535 simplelist_info_init(&info, "Boot data", 1, NULL);
2537 simplelist_set_line_count(0); 2536 simplelist_set_line_count(0);
2538 2537
2539#if defined(HAVE_MULTIBOOT) 2538 if (!boot_data_valid)
2540 char rootpath[MAX_PATH / 2] = RB_ROOT_CONTENTS_DIR;
2541 int boot_volume = 0;
2542 if(crc == boot_data.crc)
2543 { 2539 {
2544 boot_volume = boot_data.boot_volume; /* boot volume contained in uint8_t payload */ 2540 simplelist_addline("Boot data invalid");
2545 int rtlen = get_redirect_dir(rootpath, sizeof(rootpath), boot_volume, "", ""); 2541 simplelist_addline("Magic[0]: %08lx", boot_data.magic[0]);
2546 while (rtlen > 0 && rootpath[--rtlen] == PATH_SEPCH) /* remove extra separators */ 2542 simplelist_addline("Magic[1]: %08lx", boot_data.magic[1]);
2547 rootpath[rtlen] = '\0'; 2543 simplelist_addline("Length: %lu", boot_data.length);
2548 } 2544 }
2549 simplelist_addline("Boot Volume: <%lu>", boot_volume); 2545 else
2550 simplelist_addline("Root:"); 2546 {
2551 simplelist_addline("%s", rootpath); 2547 simplelist_addline("Boot data valid");
2552 simplelist_addline(""); 2548 simplelist_addline("Version: %d", (int)boot_data.version);
2553#endif 2549 simplelist_addline("Boot volume: %d", (int)boot_data.boot_volume);
2550 }
2551
2554 simplelist_addline("Bootdata RAW:"); 2552 simplelist_addline("Bootdata RAW:");
2555 if (crc != boot_data.crc) 2553 for (size_t i = 0; i < boot_data.length; i += 4)
2556 simplelist_addline("Magic: %.8s", boot_data.magic);
2557 simplelist_addline("Length: %lu", boot_data.length);
2558 simplelist_addline("CRC: %lx", boot_data.crc);
2559 (crc == boot_data.crc) ? simplelist_addline("CRC: OK!") :
2560 simplelist_addline("CRC: BAD");
2561 for (unsigned i = 0; i < boot_data.length; i += 4)
2562 { 2554 {
2563 simplelist_addline("%02x: %02x %02x %02x %02x", i, boot_data.payload[i], 2555 simplelist_addline("%02x: %02x %02x %02x %02x", i,
2564 boot_data.payload[i+1], boot_data.payload[i+2], boot_data.payload[i+3]); 2556 boot_data.payload[i + 0], boot_data.payload[i + 1],
2557 boot_data.payload[i + 2], boot_data.payload[i + 3]);
2565 } 2558 }
2566 2559
2567 return simplelist_show_list(&info); 2560 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 @@
77#include "statusbar-skinned.h" 77#include "statusbar-skinned.h"
78#include "bootchart.h" 78#include "bootchart.h"
79#include "logdiskf.h" 79#include "logdiskf.h"
80#include "bootdata.h"
80#if (CONFIG_PLATFORM & PLATFORM_ANDROID) 81#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
81#include "notification.h" 82#include "notification.h"
82#endif 83#endif
@@ -445,6 +446,10 @@ static void init(void)
445 core_allocator_init(); 446 core_allocator_init();
446 kernel_init(); 447 kernel_init();
447 448
449#if defined(HAVE_BOOTDATA) && !defined(BOOTLOADER)
450 verify_boot_data();
451#endif
452
448 /* early early early! */ 453 /* early early early! */
449 filesystem_init(); 454 filesystem_init();
450 455
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
57 57
58#if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT) 58#if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT)
59common/multiboot.c 59common/multiboot.c
60#ifndef BOOTLOADER
61common/bootdata.c
62#endif
60#endif 63#endif
61 64
62#ifdef HAVE_SDL 65#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2022 by Aidan MacDonald
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#include "bootdata.h"
22#include "crc32.h"
23#include <stddef.h>
24
25#ifdef BOOTLOADER
26# error "not to be included in bootloader builds"
27#endif
28
29bool boot_data_valid;
30
31static bool verify_boot_data_v0(void) INIT_ATTR;
32static bool verify_boot_data_v0(void)
33{
34 /* validate protocol version */
35 if (boot_data.version != 0)
36 return false;
37
38 /* validate length */
39 if (boot_data.length != 4)
40 return false;
41
42 return true;
43}
44
45struct verify_bd_entry
46{
47 int version;
48 bool (*verify) (void);
49};
50
51static const struct verify_bd_entry verify_bd[] INITDATA_ATTR = {
52 { 0, verify_boot_data_v0 },
53};
54
55void verify_boot_data(void)
56{
57 /* verify payload with checksum - all protocol versions */
58 uint32_t crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
59 if (crc != boot_data.crc)
60 return;
61
62 /* apply verification specific to the protocol version */
63 for (size_t i = 0; i < ARRAYLEN(verify_bd); ++i)
64 {
65 const struct verify_bd_entry *e = &verify_bd[i];
66 if (e->version == boot_data.version)
67 {
68 if (e->verify())
69 boot_data_valid = true;
70
71 return;
72 }
73 }
74}
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 @@
26#include <string.h> 26#include <string.h>
27#include <stdio.h> 27#include <stdio.h>
28 28
29static void write_bootdata_v0(struct boot_data_t *data, unsigned int boot_volume)
30{
31 memset(data->payload, data->length, 0);
32
33 data->boot_volume = boot_volume;
34 data->version = 0;
35}
36
29/* Write bootdata into location in FIRMWARE marked by magic header 37/* Write bootdata into location in FIRMWARE marked by magic header
30 * Assumes buffer is already loaded with the firmware image 38 * Assumes buffer is already loaded with the firmware image
31 * We just need to find the location and write data into the 39 * We just need to find the location and write data into the
32 * payload region along with the crc for later verification and use. 40 * payload region along with the crc for later verification and use.
33 * Returns payload len on success, 41 * Returns payload len on success,
34 * On error returns EKEY_NOT_FOUND 42 * On error returns false
35 */ 43 */
36int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume) 44bool write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
37{ 45{
38 struct boot_data_t bl_boot_data;
39 struct boot_data_t *fw_boot_data = NULL;
40 int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t); 46 int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t);
41 int payload_len = EKEY_NOT_FOUND;
42 47
43 /* search for boot data header prior to search_len */ 48 /* search for boot data header prior to search_len */
44 for(int i = 0; i < search_len; i++) 49 for(int i = 0; i < search_len; i++)
45 { 50 {
46 fw_boot_data = (struct boot_data_t*) &buf[i]; 51 struct boot_data_t *data = (struct boot_data_t *)&buf[i];
47 if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 || 52 if (data->magic[0] != BOOT_DATA_MAGIC0 ||
48 fw_boot_data->magic[1] != BOOT_DATA_MAGIC1) 53 data->magic[1] != BOOT_DATA_MAGIC1)
49 continue; 54 continue;
50 55
51 memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE); 56 /* Ignore it if the length extends past the end of the buffer. */
52 bl_boot_data.boot_volume = boot_volume; 57 int data_len = offsetof(struct boot_data_t, payload) + data->length;
58 if (i + data_len > len)
59 continue;
60
61 /* Determine the maximum supported boot protocol version.
62 * Version 0 firmware may use 0 or 0xff, all other versions
63 * declare the highest supported version in the version byte. */
64 int proto_version = 0;
65 if (data->version < 0xff)
66 proto_version = MIN(BOOT_DATA_VERSION, data->version);
53 67
54 memset(fw_boot_data->payload, 0, fw_boot_data->length); 68 /* Write boot data according to the selected protocol */
55 /* determine maximum bytes we can write to firmware 69 if (proto_version == 0)
56 BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */ 70 write_bootdata_v0(data, boot_volume);
57 payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length); 71 else
58 fw_boot_data->length = payload_len; 72 break;
59 /* copy data to FIRMWARE bootdata struct */
60 memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
61 /* crc will be used within the firmware to check validity of bootdata */
62 fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
63 break;
64 73
74 /* Calculate payload CRC, used by all protocol versions. */
75 data->crc = crc_32(data->payload, data->length, 0xffffffff);
76 return true;
65 } 77 }
66 return payload_len; 78
79 return false;
67} 80}
68 81
69#ifdef HAVE_MULTIBOOT 82#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)
112 { 112 {
113 ret = load_firmware_filename(buf, filename, buffer_size); 113 ret = load_firmware_filename(buf, filename, buffer_size);
114 /* if firmware has no boot_data don't load from external drive */ 114 /* if firmware has no boot_data don't load from external drive */
115 if (write_bootdata(buf, ret, i) <= 0) 115 if (!write_bootdata(buf, ret, i))
116 ret = EKEY_NOT_FOUND; 116 ret = EKEY_NOT_FOUND;
117 } 117 }
118 /* if ret is valid breaks from loop to continue loading */ 118 /* 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 @@
22 22
23#ifndef __ASSEMBLER__ 23#ifndef __ASSEMBLER__
24#include <stdint.h> 24#include <stdint.h>
25#include "system.h"
25#endif 26#endif
26 27
27/* /!\ This file can be included in assembly files /!\ */ 28/* /!\ This file can be included in assembly files /!\ */
@@ -36,6 +37,7 @@
36 37
37#define BOOT_DATA_MAGIC0 ('r' | 'b' << 8 | 'm' << 16 | 'a' << 24) 38#define BOOT_DATA_MAGIC0 ('r' | 'b' << 8 | 'm' << 16 | 'a' << 24)
38#define BOOT_DATA_MAGIC1 ('g' | 'i' << 8 | 'c' << 16 | '!' << 24) 39#define BOOT_DATA_MAGIC1 ('g' | 'i' << 8 | 'c' << 16 | '!' << 24)
40#define BOOT_DATA_VERSION 0
39 41
40/* maximum size of payload */ 42/* maximum size of payload */
41#define BOOT_DATA_PAYLOAD_SIZE 4 43#define BOOT_DATA_PAYLOAD_SIZE 4
@@ -58,6 +60,7 @@ struct boot_data_t
58 struct 60 struct
59 { 61 {
60 uint8_t boot_volume; 62 uint8_t boot_volume;
63 uint8_t version;
61 }; 64 };
62 uint8_t payload[BOOT_DATA_PAYLOAD_SIZE]; 65 uint8_t payload[BOOT_DATA_PAYLOAD_SIZE];
63 }; 66 };
@@ -65,6 +68,9 @@ struct boot_data_t
65 68
66#if !defined(BOOTLOADER) 69#if !defined(BOOTLOADER)
67extern struct boot_data_t boot_data; 70extern struct boot_data_t boot_data;
71extern bool boot_data_valid;
72
73void verify_boot_data(void) INIT_ATTR;
68#endif 74#endif
69#else /* __ASSEMBLER__ */ 75#else /* __ASSEMBLER__ */
70 76
@@ -76,7 +82,9 @@ boot_data:
76 .word BOOT_DATA_MAGIC0 82 .word BOOT_DATA_MAGIC0
77 .word BOOT_DATA_MAGIC1 83 .word BOOT_DATA_MAGIC1
78 .word BOOT_DATA_PAYLOAD_SIZE 84 .word BOOT_DATA_PAYLOAD_SIZE
79 .space BOOT_DATA_PAYLOAD_SIZE, 0xff /* payload, initialised with value 0xff */ 85 .byte 0xff /* boot volume */
86 .byte BOOT_DATA_VERSION /* maximum supported boot protocol version */
87 .space (BOOT_DATA_PAYLOAD_SIZE - 2), 0xff /* remainder of payload */
80.endm 88.endm
81 89
82#endif 90#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 @@
21#ifndef __MULTIBOOT_H__ 21#ifndef __MULTIBOOT_H__
22#define __MULTIBOOT_H__ 22#define __MULTIBOOT_H__
23 23
24extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); 24extern bool write_bootdata(unsigned char* buf, int len, unsigned int boot_volume);
25#ifdef HAVE_MULTIBOOT 25#ifdef HAVE_MULTIBOOT
26extern int get_redirect_dir(char* buf, int buffer_size, int volume, 26extern int get_redirect_dir(char* buf, int buffer_size, int volume,
27 const char* rootdir, const char* firmware); 27 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 @@
29#include "rb-loader.h" 29#include "rb-loader.h"
30#include "multiboot.h" 30#include "multiboot.h"
31#include "bootdata.h" 31#include "bootdata.h"
32#include "crc32.h"
33#endif 32#endif
34 33
35#ifndef RB_ROOT_VOL_HIDDEN 34#ifndef RB_ROOT_VOL_HIDDEN
@@ -144,8 +143,7 @@ static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
144 char rtpath[MAX_PATH / 2]; 143 char rtpath[MAX_PATH / 2];
145 make_volume_root(volume, path); 144 make_volume_root(volume, path);
146 145
147 unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff); 146 if (boot_data_valid)
148 if (crc > 0 && crc == boot_data.crc)
149 { 147 {
150 /* we need to mount the drive before we can access it */ 148 /* we need to mount the drive before we can access it */
151 root_mount_path(path, 0); /* root could be different folder don't hide */ 149 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))
174 root_mount_path(rtpath, NSITEM_CONTENTS); 172 root_mount_path(rtpath, NSITEM_CONTENTS);
175 } 173 }
176 174
177 } /*CRC OK*/ 175 }
178 else 176 else
179 { 177 {
180standard_redirect: 178standard_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)
250 250
251 err = LOAD_FIRMWARE(filebuf, filename, filebuf_size); 251 err = LOAD_FIRMWARE(filebuf, filename, filebuf_size);
252#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) 252#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
253 /* write the bootdata as if rolo were the bootloader */ 253 /* write the bootdata as if rolo were the bootloader
254 unsigned int crc = 0; 254 * FIXME: this won't work for root redirect... */
255 if (strcmp(filename, BOOTDIR "/" BOOTFILE) == 0) 255 if (!strcmp(filename, BOOTDIR "/" BOOTFILE) && boot_data_valid)
256 crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
257
258 if(crc > 0 && crc == boot_data.crc)
259 write_bootdata(filebuf, filebuf_size, boot_data.boot_volume); /* rb-loader.c */ 256 write_bootdata(filebuf, filebuf_size, boot_data.boot_volume); /* rb-loader.c */
260#endif 257#endif
261 258
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)
256 { 256 {
257 ret = load_mi4_filename(buf, filename, buffer_size); 257 ret = load_mi4_filename(buf, filename, buffer_size);
258 /* if firmware has no boot_data don't load from external drive */ 258 /* if firmware has no boot_data don't load from external drive */
259 if (write_bootdata(buf, ret, i) <= 0) 259 if (!write_bootdata(buf, ret, i))
260 ret = EKEY_NOT_FOUND; 260 ret = EKEY_NOT_FOUND;
261 } 261 }
262 /* if ret is valid breaks from loop to continue loading */ 262 /* if ret is valid breaks from loop to continue loading */