rockbox/tools/rkw.c
Marcin Bukat c3a62cee96 rk27xx: pass the NAND bootloader's handoff words to the OF
Before it jumps to an image, the rk27xx NAND bootloader writes three
words at the address in RKW header field 0x14: a magic, its version
and which copy of the image it loaded. The original firmware never
initialises them: it reports the version over USB and counts its own
reboots in the third word, and once the count reaches 5 it reboots
into a mode the NAND bootloader will not boot. When our bootloader
started the OF, nothing wrote them, so the OF ran with whatever was
left in DRAM.

Field 0x14 of our own images held a constant taken from some other
image. Point it at the last 12 bytes of DRAM, which nothing uses
before our bootloader runs. load_rkw() in the bootloader now reads
the words there before loading an image, and writes them at the
address in the loaded image's header when that lies between the
image and the bootloader. Started over USB, it passes version 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ife1d63e361a780c28a9f0626d110552b98bb6b7f
2026-10-07 18:09:15 +02:00

174 lines
5.4 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 Marcin Bukat
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#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)
{
const unsigned char *buf = (const unsigned char *)src;
/* polynomial 0x04c10db7 */
static const uint32_t crc32_lookup[16] =
{ /* lookup table for 4 bits at a time is affordable */
0x00000000, 0x04C10DB7, 0x09821B6E, 0x0D4316D9,
0x130436DC, 0x17C53B6B, 0x1A862DB2, 0x1E472005,
0x26086DB8, 0x22C9600F, 0x2F8A76D6, 0x2B4B7B61,
0x350C5B64, 0x31CD56D3, 0x3C8E400A, 0x384F4DBD
};
uint32_t crc32 = 0;
unsigned char byte;
uint32_t t;
while (len--)
{
byte = *buf++; /* get one byte of data */
/* upper nibble of our data */
t = crc32 >> 28; /* extract the 4 most significant bits */
t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */
crc32 <<= 4; /* shift the CRC register left 4 bits */
crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
/* lower nibble of our data */
t = crc32 >> 28; /* extract the 4 most significant bits */
t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */
crc32 <<= 4; /* shift the CRC register left 4 bits */
crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
}
return crc32;
}
static void int2le(unsigned int val, unsigned char* addr)
{
addr[0] = val & 0xFF;
addr[1] = (val >> 8) & 0xff;
addr[2] = (val >> 16) & 0xff;
addr[3] = (val >> 24) & 0xff;
}
int rkw_encode(char *iname, char *oname, unsigned long modelnum)
{
size_t len;
int length;
int rkwlength;
FILE *file;
uint32_t binary_crc, header_crc;
unsigned char *outbuf;
file = fopen(iname, "rb");
if (!file)
{
perror(iname);
return -1;
}
fseek(file,0,SEEK_END);
length = ftell(file);
fseek(file,0,SEEK_SET);
/* length of the RKW header + binary length + 4 bytes of CRC */
rkwlength = (length + RKW_HEADER_SIZE + 4);
outbuf = malloc(rkwlength);
if (!outbuf)
{
printf("out of memory!\n");
fclose(file);
return -1;
}
/* Clear the buffer to zero */
memset(outbuf, 0, rkwlength);
/* Build the RKW header */
int2le(RKLD_MAGIC, outbuf); /* magic */
int2le(RKW_HEADER_SIZE, outbuf+0x04); /* header size */
int2le(0x60000000, outbuf+0x08); /* base address */
int2le(0x60000000, outbuf+0x0c); /* load address */
int2le(0x60000000+length, outbuf+0x10); /* end address */
int2le(RKW_HANDOFF_ADDR, outbuf+0x14); /* loader handoff words */
int2le(modelnum, outbuf+0x18); /* reserved (we abuse the format
* to store modelnum here
*/
int2le(0, outbuf+0x1c); /* reserved */
int2le(0x60000000, outbuf+0x20); /* entry point */
int2le(0xe0000000, outbuf+0x24); /* flags */
header_crc = rkw_crc32(outbuf, RKW_HEADER_SIZE - 4);
int2le(header_crc, outbuf+0x28); /* header CRC */
/* Copy the binary */
len = fread(outbuf + RKW_HEADER_SIZE, 1, length, file);
if(len < (size_t)length)
{
perror(iname);
free(outbuf);
fclose(file);
return -2;
}
fclose(file);
/* calc binary CRC and put at the end */
binary_crc = rkw_crc32 (outbuf + RKW_HEADER_SIZE, length);
int2le(binary_crc, outbuf + rkwlength - 4);
file = fopen(oname, "wb");
if (!file)
{
perror(oname);
free(outbuf);
return -3;
}
len = fwrite(outbuf, 1, rkwlength, file);
if(len < (size_t)length)
{
perror(oname);
fclose(file);
free(outbuf);
return -4;
}
fclose(file);
free(outbuf);
fprintf(stderr, "File encoded successfully\n" );
return 0;
}