diff --git a/firmware/export/rkw-loader.h b/firmware/export/rkw-loader.h index 2101bd03b7..076aca2eba 100644 --- a/firmware/export/rkw-loader.h +++ b/firmware/export/rkw-loader.h @@ -30,7 +30,9 @@ struct rkw_header_t { uint32_t image_base; /* Base address of the firmware image */ uint32_t load_address; /* Load address */ uint32_t load_limit; /* End of the firmware image */ - uint32_t bss_start; /* This is the start of .bss section of the firmware I suppose */ + uint32_t handoff; /* Where the loader leaves its three handoff + * words for the image (see rkw-loader.c) + */ uint32_t reserved0; /* reserved - I've seen only zeros in this field so far */ uint32_t reserved1; /* reserved - I've seen only zeros in this field so far */ uint32_t entry_point; /* Entry point address */ diff --git a/firmware/target/arm/rk27xx/rkw-loader.c b/firmware/target/arm/rk27xx/rkw-loader.c index 9b50e1b187..c7f694e8a0 100644 --- a/firmware/target/arm/rk27xx/rkw-loader.c +++ b/firmware/target/arm/rk27xx/rkw-loader.c @@ -19,6 +19,7 @@ ****************************************************************************/ #include +#include #include "config.h" #include "loader_strerror.h" #include "rkw-loader.h" @@ -26,6 +27,82 @@ #include "file.h" #include "panic.h" +#ifdef BOOTLOADER +/* Before the NAND bootloader jumps to an image it writes three words at + * the address in header field 0x14: a magic, its own version and which + * copy of the image it loaded. The OF never initialises them; it reports + * the version over USB and counts its reboots in the third word. When we + * start the OF ourselves we pass on what the NAND bootloader gave us, so + * the OF sees the same as when it is started directly. + */ +#define RKW_HANDOFF_MAGIC 0x03df479a +/* written over the magic once the words have been read */ +#define RKW_HANDOFF_CONSUMED 0x123456ad + +/* The last 12 bytes of DRAM. tools/rkw.c puts this address in every + * Rockbox RKW, so the NAND bootloader leaves our words here. + */ +#define RKW_HANDOFF_ADDR (0x60000000 + MEMORYSIZE * 0x100000 - 12) +#if MEMORYSIZE != 16 +#error "tools/rkw.c assumes 16 MB of DRAM, update RKW_HANDOFF_ADDR there" +#endif + +struct rkw_handoff_t { + uint32_t magic; + uint32_t version; + uint32_t source; +}; + +/* The NAND bootloader's words, read once: loading an image may overwrite + * them, and a reboot keeps DRAM, so they are marked consumed. + */ +static struct rkw_handoff_t *rkw_own_handoff(void) +{ + static struct rkw_handoff_t own; + static bool read_done = false; + volatile struct rkw_handoff_t *h = + (volatile struct rkw_handoff_t *)RKW_HANDOFF_ADDR; + + if (!read_done) + { + if (h->magic == RKW_HANDOFF_MAGIC) + { + own.version = h->version; + own.source = h->source; + } + else + { + /* not started by the NAND bootloader, e.g. over USB */ + own.version = 0; + own.source = 0; + } + own.magic = RKW_HANDOFF_MAGIC; + h->magic = RKW_HANDOFF_CONSUMED; + read_done = true; + } + + return &own; +} + +/* Hand the words on to an image loaded at its own address, when its + * header points past the image and below us. + */ +static void rkw_pass_handoff(const struct rkw_header_t *hdr, + const unsigned char *buf, int len, + int buffer_size) +{ + uintptr_t addr = hdr->handoff; + uintptr_t lo = (uintptr_t)buf + len; + uintptr_t hi = (uintptr_t)buf + buffer_size; + + if ((uintptr_t)buf != hdr->load_address || (addr & 3) || + addr < lo || addr > hi - sizeof(struct rkw_handoff_t)) + return; + + *(struct rkw_handoff_t *)addr = *rkw_own_handoff(); +} +#endif /* BOOTLOADER */ + /* loosely based on load_firmware() * on success we return size of loaded image * on error we return negative value which can be deciphered by means @@ -116,6 +193,11 @@ int load_rkw(unsigned char* buf, const char* firmware, int buffer_size) goto end; } +#ifdef BOOTLOADER + /* before the image can overwrite them */ + rkw_own_handoff(); +#endif + /* skip header */ lseek(fd, sizeof(rkw_info), SEEK_SET); @@ -141,6 +223,10 @@ int load_rkw(unsigned char* buf, const char* firmware, int buffer_size) } } +#ifdef BOOTLOADER + rkw_pass_handoff(&rkw_info, buf, len, buffer_size); +#endif + ret = len; end: close(fd); diff --git a/tools/rkw.c b/tools/rkw.c index db86b4f748..6d5f4c61d3 100644 --- a/tools/rkw.c +++ b/tools/rkw.c @@ -26,6 +26,14 @@ #define RKLD_MAGIC 0x4c44524b #define RKW_HEADER_SIZE 0x2c +/* The NAND bootloader writes three handoff words at the address in + * header field 0x14 before it jumps to the image. Point it at the last + * 12 bytes of DRAM, which nothing touches before our bootloader reads + * them. Every rk27xx target has 16 MB; keep in step with + * RKW_HANDOFF_ADDR in firmware/target/arm/rk27xx/rkw-loader.c. + */ +#define RKW_HANDOFF_ADDR (0x60000000 + 16 * 0x100000 - 12) + /* slightly modified version from crc32.c in rockbox */ static uint32_t rkw_crc32(const void *src, uint32_t len) { @@ -114,7 +122,7 @@ int rkw_encode(char *iname, char *oname, unsigned long modelnum) int2le(0x60000000, outbuf+0x08); /* base address */ int2le(0x60000000, outbuf+0x0c); /* load address */ int2le(0x60000000+length, outbuf+0x10); /* end address */ - int2le(0x6035a5e4, outbuf+0x14); /* points to some unknown struct */ + int2le(RKW_HANDOFF_ADDR, outbuf+0x14); /* loader handoff words */ int2le(modelnum, outbuf+0x18); /* reserved (we abuse the format * to store modelnum here */