Add %tr to the manual.
[kugel-rb.git] / flash / make_firmware / make_firmware.c
bloba79f2b970d30a24b24cf9b4e2a5694c982088674
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2003 by Jörg Hohensohn
12 * Autoring tool for the firmware image to be programmed into Flash ROM
13 * It composes the flash content with header, bootloader and image(s)
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <inttypes.h>
29 #include <string.h>
31 #define UINT8 unsigned char
32 #define UINT16 unsigned short
33 #define UINT32 unsigned long
34 #define BOOL int
35 #define TRUE 1
36 #define FALSE 0
38 // size of one flash sector, the granularity with which it can be erased
39 #define SECTORSIZE 4096
41 #define BOOTLOAD_DEST 0x0FFFF500 // for the "normal" one
42 #define FLASH_START 0x02000000
43 #define BOOTLOAD_SCR 0x02000100
44 #define ROCKBOX_DEST 0x09000000
45 #define ROCKBOX_EXEC 0x09000200
48 // place a 32 bit value into memory, big endian
49 void Write32(UINT8* pByte, UINT32 value)
51 pByte[0] = (UINT8)(value >> 24);
52 pByte[1] = (UINT8)(value >> 16);
53 pByte[2] = (UINT8)(value >> 8);
54 pByte[3] = (UINT8)(value);
58 // read a 32 bit value from memory, big endian
59 UINT32 Read32(UINT8* pByte)
61 UINT32 value = 0;
63 value |= (UINT32)pByte[0] << 24;
64 value |= (UINT32)pByte[1] << 16;
65 value |= (UINT32)pByte[2] << 8;
66 value |= (UINT32)pByte[3];
68 return value;
72 UINT32 CalcCRC32 (const UINT8* buf, UINT32 len)
74 static const UINT32 crc_table[256] =
75 { // CRC32 lookup table for polynomial 0x04C11DB7
76 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B,
77 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
78 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, 0x48D0C6C7,
79 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75,
80 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3,
81 0x709F7B7A, 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,
82 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, 0xBAEA46EF,
83 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D,
84 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB,
85 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1,
86 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0,
87 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072,
88 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, 0x054BF6A4,
89 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE,
90 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08,
91 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
92 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC,
93 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6,
94 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, 0xE0B41DE7, 0xE4750050,
95 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2,
96 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34,
97 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637,
98 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, 0x4BC510E1,
99 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53,
100 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5,
101 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,
102 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, 0xF5EE4BB9,
103 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B,
104 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD,
105 0xCDA1F604, 0xC960EBB3, 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7,
106 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71,
107 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3,
108 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2,
109 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8,
110 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E,
111 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
112 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A,
113 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0,
114 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, 0xE760D676,
115 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4,
116 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662,
117 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668,
118 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
120 UINT32 i;
121 UINT32 crc = 0xffffffff;
123 for (i = 0; i < len; i++)
124 crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *buf++) & 0xFF];
126 return crc;
130 UINT32 PlaceImage(char* filename, UINT32 pos, UINT8* pFirmware, UINT32 limit)
132 UINT32 size, read;
133 FILE* pFile;
134 UINT32 align;
135 UINT32 flags;
136 UINT32 load_addr = ROCKBOX_DEST, exec_addr = ROCKBOX_EXEC; // defaults
138 // magic file header for compressed files
139 static const UINT8 magic[8] = { 0x00,0xe9,0x55,0x43,0x4c,0xff,0x01,0x1a };
140 UINT8 ucl_header[26];
142 pFile = fopen(filename, "rb"); // open the current image
143 if (pFile == NULL)
145 printf("Image file %s not found!\n", filename);
146 exit(5);
149 fseek(pFile, 0, SEEK_END);
150 size = ftell(pFile);
151 fseek(pFile, 0, SEEK_SET);
153 // determine if compressed
154 flags = 0x00000000; // default: flags for uncompressed
155 fread(ucl_header, 1, sizeof(ucl_header), pFile);
156 if (memcmp(magic, ucl_header, sizeof(magic)) == 0)
158 if (ucl_header[12] != 0x2E // check algorithm
159 && ucl_header[12] != 0x2B) // or uncompressed
161 printf("UCL compressed files must use algorithm 2e, not %d\n", ucl_header[12]);
162 printf("Generate with: uclpack --best --2e rockbox.bin %s\n", filename);
163 exit(6);
166 size = Read32(ucl_header + 22); // compressed size
167 if (Read32(ucl_header + 18) > size) // compare with uncompressed size
168 { // normal case
169 flags = 0x00000001; // flags for UCL compressed
172 if (ucl_header[12] == 0x2B) // uncompressed means "ROMbox", for direct flash execution
174 UINT8 start_addr[4];
175 UINT8 reset_vec[4];
176 fread(start_addr, 1, sizeof(start_addr), pFile); // read the link address from image
177 fread(reset_vec, 1, sizeof(reset_vec), pFile); // read the reset vector from image
178 fseek(pFile, 0-sizeof(start_addr)-sizeof(reset_vec), SEEK_CUR); // wind back
179 load_addr = Read32(start_addr);
180 if (load_addr != FLASH_START + pos + 16) // behind 16 byte header
182 printf("Error: Rombox .ucl file is linked to 0x%08X instead of 0x%08X\n", load_addr, FLASH_START + pos + 16);
183 exit(11);
185 exec_addr = Read32(reset_vec);
188 else
190 fseek(pFile, 0, SEEK_SET); // go back
193 if (pos + 16 + size > limit) // enough space for all that?
195 printf("Exceeding maximum image size %d\n", limit);
196 exit(7);
199 // write header
200 align = (pos + 16 + size + SECTORSIZE-1) & ~(SECTORSIZE-1); // round up to next flash sector
201 Write32(pFirmware + pos, load_addr); // load address
202 Write32(pFirmware + pos + 4, align - (pos + 16)); // image size
203 Write32(pFirmware + pos + 8, exec_addr); // execution address
204 Write32(pFirmware + pos + 12, flags); // compressed or not
205 pos += 16;
207 // load image
208 read = fread(pFirmware + pos, 1, size, pFile);
209 if (read != size)
211 printf("Read error, expecting %d bytes, got only %d\n", size, read);
212 exit(8);
214 fclose (pFile);
216 pos += size;
218 return pos;
222 int main(int argc, char* argv[])
224 static UINT8 aFirmware[512*1024]; // maximum with exchanged chip
225 FILE* pFile;
226 UINT32 size; // size of loaded item
227 UINT32 pos; // current position in firmware
228 UINT32 crc32; // checksum of "payload"
229 BOOL hasBootRom; // flag if regular boot ROM or directly starts from flash
230 UINT32 template_F8, template_FC; // my platform ID, mask and version
232 int i;
234 if (argc <= 4)
236 printf("Usage:\n");
237 printf("make_firmware <output> <template.bin> <bootloader.ajz> <image1.ucl> {image2.ucl}\n");
238 printf("<template.bin> is the original firmware from your box\n");
239 printf("<bootloader.ajz> is the scrambled bootloader\n");
240 printf("<image1.ucl> is the first image, compressed (recommended) or uncompressed\n");
241 printf("<image1.ucl> is the second image, compressed (recommended) or uncompressed\n");
242 printf("More images may follow, but keep the flash size in mind!\n");
243 printf("Compression must be UCL, algorithm 2e.\n");
244 printf("Generated with: uclpack --best --2e rockbox.bin imageN.ucl\n");
245 exit(0);
248 memset(aFirmware, 0xFF, sizeof(aFirmware));
250 /******* process template *******/
252 pFile = fopen(argv[2], "rb"); // open the template
253 if (pFile == NULL)
255 printf("Template file %s not found!\n", argv[2]);
256 exit(1);
258 size = fread(aFirmware, 1, 256, pFile); // need only the header
259 fclose(pFile);
260 if (size < 256) // need at least the firmware header
262 printf("Template file %s too small, need at least the header!\n", argv[2]);
263 exit(2);
266 if (strncmp(aFirmware, "ARCH", 4) == 0)
268 hasBootRom = TRUE;
269 pos = 256; // place bootloader after this "boot block"
271 else if (Read32(aFirmware) == 0x0200)
273 hasBootRom = FALSE;
274 pos = 0; // directly start with the bootloader
275 template_F8 = Read32(aFirmware + 0xF8); // my platform ID and future info
276 template_FC = Read32(aFirmware + 0xFC); // use mask+version from template
278 else
280 printf("Template file %s invalid!\n", argv[2]);
281 exit(3);
284 /******* process bootloader *******/
286 pFile = fopen(argv[3], "rb"); // open the bootloader
287 if (pFile == NULL)
289 printf("Bootloader file %s not found!\n", argv[3]);
290 exit(4);
292 if (hasBootRom && fseek(pFile, 6, SEEK_SET)) // skip the ajz header
294 printf("Bootloader file %s too short!\n", argv[3]);
295 exit(5);
298 // place bootloader after header
299 size = fread(aFirmware + pos, 1, sizeof(aFirmware) - pos, pFile);
300 fclose(pFile);
302 if (hasBootRom)
304 Write32(aFirmware + 4, BOOTLOAD_DEST); // boot code destination address
306 for (i=0x08; i<=0x28; i+=8)
308 Write32(aFirmware + i, BOOTLOAD_SCR); // boot code source address
309 Write32(aFirmware + i + 4, size); // boot code size
312 else
314 Write32(aFirmware + 0xF8, template_F8); // values from template
315 Write32(aFirmware + 0xFC, template_FC); // mask and version
318 size = (size + 3) & ~3; // make shure it's 32 bit aligned
319 pos += size; // prepare position for first image
321 /******* process images *******/
322 for (i = 4; i < argc; i++)
324 pos = PlaceImage(argv[i], pos, aFirmware, sizeof(aFirmware));
326 if (i < argc-1)
327 { // not the last: round up to next flash sector
328 pos = (pos + SECTORSIZE-1) & ~(SECTORSIZE-1);
333 /******* append CRC32 checksum *******/
334 crc32 = CalcCRC32(aFirmware, pos);
335 Write32(aFirmware + pos, crc32);
336 pos += sizeof(crc32); // 4 bytes
339 /******* save result to output file *******/
341 pFile = fopen(argv[1], "wb"); // open the output file
342 if (pFile == NULL)
344 printf("Output file %s cannot be created!\n", argv[1]);
345 exit(9);
347 size = fwrite(aFirmware, 1, pos, pFile);
348 fclose(pFile);
350 if (size != pos)
352 printf("Error writing %d bytes to output file %s!\n", pos, argv[1]);
353 exit(10);
356 printf("Firmware file generated with %d bytes.\n", pos);
358 return 0;