A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

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

+149 -58
+16 -23
apps/debug_menu.c
··· 2530 2530 #if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) 2531 2531 static bool dbg_boot_data(void) 2532 2532 { 2533 - unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff); 2534 2533 struct simplelist_info info; 2535 2534 info.scroll_all = true; 2536 2535 simplelist_info_init(&info, "Boot data", 1, NULL); 2537 2536 simplelist_set_line_count(0); 2538 2537 2539 - #if defined(HAVE_MULTIBOOT) 2540 - char rootpath[MAX_PATH / 2] = RB_ROOT_CONTENTS_DIR; 2541 - int boot_volume = 0; 2542 - if(crc == boot_data.crc) 2538 + if (!boot_data_valid) 2543 2539 { 2544 - boot_volume = boot_data.boot_volume; /* boot volume contained in uint8_t payload */ 2545 - int rtlen = get_redirect_dir(rootpath, sizeof(rootpath), boot_volume, "", ""); 2546 - while (rtlen > 0 && rootpath[--rtlen] == PATH_SEPCH) /* remove extra separators */ 2547 - rootpath[rtlen] = '\0'; 2540 + simplelist_addline("Boot data invalid"); 2541 + simplelist_addline("Magic[0]: %08lx", boot_data.magic[0]); 2542 + simplelist_addline("Magic[1]: %08lx", boot_data.magic[1]); 2543 + simplelist_addline("Length: %lu", boot_data.length); 2544 + } 2545 + else 2546 + { 2547 + simplelist_addline("Boot data valid"); 2548 + simplelist_addline("Version: %d", (int)boot_data.version); 2549 + simplelist_addline("Boot volume: %d", (int)boot_data.boot_volume); 2548 2550 } 2549 - simplelist_addline("Boot Volume: <%lu>", boot_volume); 2550 - simplelist_addline("Root:"); 2551 - simplelist_addline("%s", rootpath); 2552 - simplelist_addline(""); 2553 - #endif 2551 + 2554 2552 simplelist_addline("Bootdata RAW:"); 2555 - if (crc != boot_data.crc) 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) 2553 + for (size_t i = 0; i < boot_data.length; i += 4) 2562 2554 { 2563 - simplelist_addline("%02x: %02x %02x %02x %02x", i, boot_data.payload[i], 2564 - boot_data.payload[i+1], boot_data.payload[i+2], boot_data.payload[i+3]); 2555 + simplelist_addline("%02x: %02x %02x %02x %02x", i, 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 2560 return simplelist_show_list(&info);
+5
apps/main.c
··· 77 77 #include "statusbar-skinned.h" 78 78 #include "bootchart.h" 79 79 #include "logdiskf.h" 80 + #include "bootdata.h" 80 81 #if (CONFIG_PLATFORM & PLATFORM_ANDROID) 81 82 #include "notification.h" 82 83 #endif ··· 444 445 system_init(); 445 446 core_allocator_init(); 446 447 kernel_init(); 448 + 449 + #if defined(HAVE_BOOTDATA) && !defined(BOOTLOADER) 450 + verify_boot_data(); 451 + #endif 447 452 448 453 /* early early early! */ 449 454 filesystem_init();
+3
firmware/SOURCES
··· 57 57 58 58 #if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT) 59 59 common/multiboot.c 60 + #ifndef BOOTLOADER 61 + common/bootdata.c 62 + #endif 60 63 #endif 61 64 62 65 #ifdef HAVE_SDL
+74
firmware/common/bootdata.c
··· 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 + 29 + bool boot_data_valid; 30 + 31 + static bool verify_boot_data_v0(void) INIT_ATTR; 32 + static 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 + 45 + struct verify_bd_entry 46 + { 47 + int version; 48 + bool (*verify) (void); 49 + }; 50 + 51 + static const struct verify_bd_entry verify_bd[] INITDATA_ATTR = { 52 + { 0, verify_boot_data_v0 }, 53 + }; 54 + 55 + void 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 + }
+34 -21
firmware/common/multiboot.c
··· 26 26 #include <string.h> 27 27 #include <stdio.h> 28 28 29 + static 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 37 /* Write bootdata into location in FIRMWARE marked by magic header 30 38 * Assumes buffer is already loaded with the firmware image 31 39 * We just need to find the location and write data into the 32 40 * payload region along with the crc for later verification and use. 33 41 * Returns payload len on success, 34 - * On error returns EKEY_NOT_FOUND 42 + * On error returns false 35 43 */ 36 - int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume) 44 + bool 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 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 48 /* search for boot data header prior to search_len */ 44 49 for(int i = 0; i < search_len; i++) 45 50 { 46 - fw_boot_data = (struct boot_data_t*) &buf[i]; 47 - if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 || 48 - fw_boot_data->magic[1] != BOOT_DATA_MAGIC1) 51 + struct boot_data_t *data = (struct boot_data_t *)&buf[i]; 52 + if (data->magic[0] != BOOT_DATA_MAGIC0 || 53 + data->magic[1] != BOOT_DATA_MAGIC1) 49 54 continue; 50 55 51 - memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE); 52 - bl_boot_data.boot_volume = boot_volume; 56 + /* Ignore it if the length extends past the end of the buffer. */ 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); 55 - /* determine maximum bytes we can write to firmware 56 - BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */ 57 - payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length); 58 - fw_boot_data->length = payload_len; 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; 68 + /* Write boot data according to the selected protocol */ 69 + if (proto_version == 0) 70 + write_bootdata_v0(data, boot_volume); 71 + else 72 + 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 82 #ifdef HAVE_MULTIBOOT
+1 -1
firmware/common/rb-loader.c
··· 112 112 { 113 113 ret = load_firmware_filename(buf, filename, buffer_size); 114 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 116 ret = EKEY_NOT_FOUND; 117 117 } 118 118 /* if ret is valid breaks from loop to continue loading */
+9 -1
firmware/export/bootdata.h
··· 22 22 23 23 #ifndef __ASSEMBLER__ 24 24 #include <stdint.h> 25 + #include "system.h" 25 26 #endif 26 27 27 28 /* /!\ This file can be included in assembly files /!\ */ ··· 36 37 37 38 #define BOOT_DATA_MAGIC0 ('r' | 'b' << 8 | 'm' << 16 | 'a' << 24) 38 39 #define BOOT_DATA_MAGIC1 ('g' | 'i' << 8 | 'c' << 16 | '!' << 24) 40 + #define BOOT_DATA_VERSION 0 39 41 40 42 /* maximum size of payload */ 41 43 #define BOOT_DATA_PAYLOAD_SIZE 4 ··· 58 60 struct 59 61 { 60 62 uint8_t boot_volume; 63 + uint8_t version; 61 64 }; 62 65 uint8_t payload[BOOT_DATA_PAYLOAD_SIZE]; 63 66 }; ··· 65 68 66 69 #if !defined(BOOTLOADER) 67 70 extern struct boot_data_t boot_data; 71 + extern bool boot_data_valid; 72 + 73 + void verify_boot_data(void) INIT_ATTR; 68 74 #endif 69 75 #else /* __ASSEMBLER__ */ 70 76 ··· 76 82 .word BOOT_DATA_MAGIC0 77 83 .word BOOT_DATA_MAGIC1 78 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 88 .endm 81 89 82 90 #endif
+1 -1
firmware/export/multiboot.h
··· 21 21 #ifndef __MULTIBOOT_H__ 22 22 #define __MULTIBOOT_H__ 23 23 24 - extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); 24 + extern bool write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); 25 25 #ifdef HAVE_MULTIBOOT 26 26 extern int get_redirect_dir(char* buf, int buffer_size, int volume, 27 27 const char* rootdir, const char* firmware);
+2 -4
firmware/include/dircache_redirect.h
··· 29 29 #include "rb-loader.h" 30 30 #include "multiboot.h" 31 31 #include "bootdata.h" 32 - #include "crc32.h" 33 32 #endif 34 33 35 34 #ifndef RB_ROOT_VOL_HIDDEN ··· 144 143 char rtpath[MAX_PATH / 2]; 145 144 make_volume_root(volume, path); 146 145 147 - unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff); 148 - if (crc > 0 && crc == boot_data.crc) 146 + if (boot_data_valid) 149 147 { 150 148 /* we need to mount the drive before we can access it */ 151 149 root_mount_path(path, 0); /* root could be different folder don't hide */ ··· 174 172 root_mount_path(rtpath, NSITEM_CONTENTS); 175 173 } 176 174 177 - } /*CRC OK*/ 175 + } 178 176 else 179 177 { 180 178 standard_redirect:
+3 -6
firmware/rolo.c
··· 250 250 251 251 err = LOAD_FIRMWARE(filebuf, filename, filebuf_size); 252 252 #if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) 253 - /* write the bootdata as if rolo were the bootloader */ 254 - unsigned int crc = 0; 255 - if (strcmp(filename, BOOTDIR "/" BOOTFILE) == 0) 256 - crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff); 257 - 258 - if(crc > 0 && crc == boot_data.crc) 253 + /* write the bootdata as if rolo were the bootloader 254 + * FIXME: this won't work for root redirect... */ 255 + if (!strcmp(filename, BOOTDIR "/" BOOTFILE) && boot_data_valid) 259 256 write_bootdata(filebuf, filebuf_size, boot_data.boot_volume); /* rb-loader.c */ 260 257 #endif 261 258
+1 -1
firmware/target/arm/pp/mi4-loader.c
··· 256 256 { 257 257 ret = load_mi4_filename(buf, filename, buffer_size); 258 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 260 ret = EKEY_NOT_FOUND; 261 261 } 262 262 /* if ret is valid breaks from loop to continue loading */