Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id:$ |
| 9 | * |
| 10 | * Copyright (C) 2010 by Marcin Bukat |
| 11 | * |
| 12 | * code taken mostly from mkboot.c |
| 13 | * Copyright (C) 2005 by Linus Nielsen Feltzing |
| 14 | * |
| 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. |
| 19 | * |
| 20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 21 | * KIND, either express or implied. |
| 22 | * |
| 23 | ****************************************************************************/ |
| 24 | #include <stdio.h> |
| 25 | #include <stdlib.h> |
| 26 | #include <string.h> |
| 27 | #include "mkmpioboot.h" |
| 28 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 29 | #define OF_FIRMWARE_LEN 0x100000 /* size of the OF file */ |
| 30 | #define MPIO_STRING_OFFSET 0xfffe0 /* offset of the version string in OF */ |
| 31 | #define BOOTLOADER_MAX_SIZE 0x1f800 /* free space size */ |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 32 | |
Dominik Riebeling | 6f2bba9 | 2011-01-31 19:16:06 +0000 | [diff] [blame] | 33 | struct mpio_model { |
| 34 | /* Descriptive name of this model */ |
| 35 | const char* model_name; |
| 36 | /* Model name used in the Rockbox header in ".mpio" files - these match the |
| 37 | -add parameter to the "scramble" tool */ |
| 38 | const char* rb_model_name; |
| 39 | /* Model number used to initialise the checksum in the Rockbox header in |
| 40 | ".mpio" files - these are the same as MODEL_NUMBER in config-target.h */ |
| 41 | const int rb_model_num; |
| 42 | /* Strings which indentifies OF version */ |
| 43 | const char* of_model_string; |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 44 | }; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 45 | |
Dominik Riebeling | 6f2bba9 | 2011-01-31 19:16:06 +0000 | [diff] [blame] | 46 | static const struct mpio_model mpio_models[] = { |
| 47 | [MODEL_HD200] = |
| 48 | { "MPIO HD200", "hd20", 69, "HD200 HDD Audio Ver113005" }, |
| 49 | [MODEL_HD300] = |
| 50 | { "MPIO HD300", "hd30", 70, "HD300 HDD Audio Ver113006" }, |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 51 | }; |
| 52 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 53 | |
| 54 | /* MPIO HD200 and HD300 firmware is plain binary image |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 55 | * 4 bytes of initial SP (loaded on reset) |
| 56 | * 4 bytes of initial PC (loaded on reset) |
| 57 | * binary image with entry point 0x00000008 |
| 58 | * |
| 59 | * We put our bootloader code at 0x000e0000 |
| 60 | * and patch reset vector to jump directly |
| 61 | * into our code on reset |
| 62 | */ |
| 63 | |
| 64 | static unsigned char image[OF_FIRMWARE_LEN]; |
| 65 | |
| 66 | static unsigned int get_uint32be(unsigned char* p) |
| 67 | { |
| 68 | return ((p[0] << 24) | (p[1] << 16) | (p[2]<<8) | p[3]); |
| 69 | } |
| 70 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 71 | static long checksum(unsigned char* buf, int model, unsigned long length) |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 72 | { |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 73 | unsigned long chksum = model; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 74 | unsigned long i; |
| 75 | |
| 76 | if(buf == NULL) |
| 77 | return -1; |
| 78 | |
| 79 | for (i = 0; i < length; i++) |
| 80 | { |
| 81 | chksum += *buf++; |
| 82 | } |
| 83 | |
| 84 | return chksum; |
| 85 | } |
| 86 | |
| 87 | int mkmpioboot(const char* infile, const char* bootfile, const char* outfile, int origin) |
| 88 | { |
| 89 | FILE *f; |
| 90 | int i; |
| 91 | int len; |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 92 | int model_index; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 93 | unsigned long file_checksum; |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 94 | unsigned char header[8]; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 95 | |
| 96 | memset(image, 0xff, sizeof(image)); |
| 97 | |
| 98 | /* First, read the mpio original firmware into the image */ |
| 99 | f = fopen(infile, "rb"); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 100 | if(!f) |
| 101 | { |
| 102 | fprintf(stderr, "[ERR] Can not open %s file for reading\n", infile); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 103 | return -1; |
| 104 | } |
| 105 | |
| 106 | i = fread(image, 1, OF_FIRMWARE_LEN, f); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 107 | if(i < OF_FIRMWARE_LEN) |
| 108 | { |
| 109 | fprintf(stderr, "[ERR] %s file read error\n", infile); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 110 | fclose(f); |
| 111 | return -2; |
| 112 | } |
| 113 | |
| 114 | fclose(f); |
| 115 | |
| 116 | /* Now check if we have OF file loaded based on presence |
| 117 | * of the version string in firmware |
| 118 | */ |
| 119 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 120 | for(model_index = 0; model_index < NUM_MODELS; model_index++) |
Dominik Riebeling | 6f2bba9 | 2011-01-31 19:16:06 +0000 | [diff] [blame] | 121 | if (strcmp(mpio_models[model_index].of_model_string, |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 122 | (char*)(image + MPIO_STRING_OFFSET)) == 0) |
| 123 | break; |
| 124 | |
| 125 | if(model_index == NUM_MODELS) |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 126 | { |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 127 | fprintf(stderr, "[ERR] Unknown MPIO original firmware version\n"); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 128 | return -3; |
| 129 | } |
| 130 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 131 | fprintf(stderr, "[INFO] Loading original firmware file for %s\n", |
Dominik Riebeling | 6f2bba9 | 2011-01-31 19:16:06 +0000 | [diff] [blame] | 132 | mpio_models[model_index].model_name); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 133 | |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 134 | /* Now, read the boot loader into the image */ |
| 135 | f = fopen(bootfile, "rb"); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 136 | if(!f) |
| 137 | { |
| 138 | fprintf(stderr, "[ERR] Can not open %s file for reading\n", bootfile); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 139 | return -4; |
| 140 | } |
| 141 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 142 | fprintf(stderr, "[INFO] Loading Rockbox bootloader file\n"); |
| 143 | |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 144 | /* get bootloader size |
| 145 | * excluding header |
| 146 | */ |
| 147 | fseek(f, 0, SEEK_END); |
| 148 | len = ftell(f) - 8; |
| 149 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 150 | if (len > BOOTLOADER_MAX_SIZE) |
| 151 | { |
| 152 | fprintf(stderr, "[ERR] Bootloader doesn't fit in firmware file.\n"); |
| 153 | fprintf(stderr, "[ERR] This bootloader is %d bytes long\n", len); |
| 154 | fprintf(stderr, "[ERR] and maximum allowed size is %d bytes\n", |
| 155 | BOOTLOADER_MAX_SIZE); |
| 156 | return -5; |
| 157 | } |
| 158 | |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 159 | /* Now check if the place we want to put |
| 160 | * our bootloader is free |
| 161 | */ |
| 162 | for(i=0;i<len;i++) |
| 163 | { |
| 164 | if (image[origin+i] != 0) |
| 165 | { |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 166 | fprintf(stderr, "[ERR] Place for bootloader in OF file not empty\n"); |
| 167 | return -6; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 168 | } |
| 169 | } |
| 170 | |
| 171 | fseek(f, 0, SEEK_SET); |
| 172 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 173 | /* get bootloader header*/ |
| 174 | fread(header,1,8,f); |
| 175 | |
Dominik Riebeling | 6f2bba9 | 2011-01-31 19:16:06 +0000 | [diff] [blame] | 176 | if ( memcmp(header + 4, mpio_models[model_index].rb_model_name, 4) != 0 ) |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 177 | { |
| 178 | fprintf(stderr, "[ERR] Original firmware and rockbox bootloader mismatch!\n"); |
| 179 | fprintf(stderr, "[ERR] Double check that you have bootloader for %s\n", |
Dominik Riebeling | 6f2bba9 | 2011-01-31 19:16:06 +0000 | [diff] [blame] | 180 | mpio_models[model_index].model_name); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 181 | return -7; |
| 182 | } |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 183 | |
| 184 | /* omit header */ |
| 185 | fseek(f, 8, SEEK_SET); |
| 186 | |
| 187 | i = fread(image + origin, 1, len, f); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 188 | if(i < len) |
| 189 | { |
| 190 | fprintf(stderr, "[ERR] %s file read error\n", bootfile); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 191 | fclose(f); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 192 | return -8; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | fclose(f); |
| 196 | |
| 197 | /* calculate checksum and compare with data |
| 198 | * from header |
| 199 | */ |
Dominik Riebeling | 6f2bba9 | 2011-01-31 19:16:06 +0000 | [diff] [blame] | 200 | file_checksum = checksum(image + origin, mpio_models[model_index].rb_model_num, len); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 201 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 202 | if ( file_checksum != get_uint32be(header) ) |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 203 | { |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 204 | fprintf(stderr,"[ERR] Bootloader checksum error\n"); |
| 205 | return -9; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | f = fopen(outfile, "wb"); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 209 | if(!f) |
| 210 | { |
| 211 | fprintf(stderr, "[ERR] Can not open %s file for writing\n" ,outfile); |
| 212 | return -10; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 213 | } |
| 214 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 215 | fprintf(stderr, "[INFO] Patching reset vector\n"); |
| 216 | |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 217 | /* Patch the stack pointer address */ |
| 218 | image[0] = image[origin + 0]; |
| 219 | image[1] = image[origin + 1]; |
| 220 | image[2] = image[origin + 2]; |
| 221 | image[3] = image[origin + 3]; |
| 222 | |
| 223 | /* Patch the reset vector to start the boot loader */ |
| 224 | image[4] = image[origin + 4]; |
| 225 | image[5] = image[origin + 5]; |
| 226 | image[6] = image[origin + 6]; |
| 227 | image[7] = image[origin + 7]; |
| 228 | |
| 229 | i = fwrite(image, 1, OF_FIRMWARE_LEN, f); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 230 | if(i < OF_FIRMWARE_LEN) |
| 231 | { |
| 232 | fprintf(stderr,"[ERR] %s file write error\n", outfile); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 233 | fclose(f); |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 234 | return -11; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 235 | } |
| 236 | |
Marcin Bukat | d130294 | 2010-11-15 20:42:58 +0000 | [diff] [blame] | 237 | fprintf(stderr,"[INFO] Wrote 0x%x bytes in %s\n", OF_FIRMWARE_LEN, outfile); |
| 238 | fprintf(stderr,"[INFO] Patching succeeded!\n"); |
| 239 | |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 240 | fclose(f); |
| 241 | |
| 242 | return 0; |
| 243 | } |