mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-13 10:07:38 -04:00
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
This commit is contained in:
parent
af644e02a1
commit
6ffd42548b
11 changed files with 150 additions and 59 deletions
|
@ -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
|
||||
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)
|
||||
else
|
||||
{
|
||||
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("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:");
|
||||
for (size_t i = 0; i < boot_data.length; i += 4)
|
||||
{
|
||||
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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
74
firmware/common/bootdata.c
Normal file
74
firmware/common/bootdata.c
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
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;
|
||||
/* 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);
|
||||
|
||||
/* 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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue