summaryrefslogtreecommitdiffstats
path: root/utils/mkmpioboot/mkmpioboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mkmpioboot/mkmpioboot.c')
-rw-r--r--utils/mkmpioboot/mkmpioboot.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/utils/mkmpioboot/mkmpioboot.c b/utils/mkmpioboot/mkmpioboot.c
new file mode 100644
index 0000000000..ea619ed2f2
--- /dev/null
+++ b/utils/mkmpioboot/mkmpioboot.c
@@ -0,0 +1,243 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id:$
+ *
+ * Copyright (C) 2010 by Marcin Bukat
+ *
+ * code taken mostly from mkboot.c
+ * Copyright (C) 2005 by Linus Nielsen Feltzing
+ *
+ * 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 "mkmpioboot.h"
+
+#define OF_FIRMWARE_LEN 0x100000 /* size of the OF file */
+#define MPIO_STRING_OFFSET 0xfffe0 /* offset of the version string in OF */
+#define BOOTLOADER_MAX_SIZE 0x1f800 /* free space size */
+
+struct mpio_model {
+ /* Descriptive name of this model */
+ const char* model_name;
+ /* Model name used in the Rockbox header in ".mpio" files - these match the
+ -add parameter to the "scramble" tool */
+ const char* rb_model_name;
+ /* Model number used to initialise the checksum in the Rockbox header in
+ ".mpio" files - these are the same as MODEL_NUMBER in config-target.h */
+ const int rb_model_num;
+ /* Strings which indentifies OF version */
+ const char* of_model_string;
+};
+
+static const struct mpio_model mpio_models[] = {
+ [MODEL_HD200] =
+ { "MPIO HD200", "hd20", 69, "HD200 HDD Audio Ver113005" },
+ [MODEL_HD300] =
+ { "MPIO HD300", "hd30", 70, "HD300 HDD Audio Ver113006" },
+};
+
+
+/* MPIO HD200 and HD300 firmware is plain binary image
+ * 4 bytes of initial SP (loaded on reset)
+ * 4 bytes of initial PC (loaded on reset)
+ * binary image with entry point 0x00000008
+ *
+ * We put our bootloader code at 0x000e0000
+ * and patch reset vector to jump directly
+ * into our code on reset
+ */
+
+static unsigned char image[OF_FIRMWARE_LEN];
+
+static unsigned int get_uint32be(unsigned char* p)
+{
+ return ((p[0] << 24) | (p[1] << 16) | (p[2]<<8) | p[3]);
+}
+
+static long checksum(unsigned char* buf, int model, unsigned long length)
+{
+ unsigned long chksum = model;
+ unsigned long i;
+
+ if(buf == NULL)
+ return -1;
+
+ for (i = 0; i < length; i++)
+ {
+ chksum += *buf++;
+ }
+
+return chksum;
+}
+
+int mkmpioboot(const char* infile, const char* bootfile, const char* outfile, int origin)
+{
+ FILE *f;
+ int i;
+ int len;
+ int model_index;
+ unsigned long file_checksum;
+ unsigned char header[8];
+
+ memset(image, 0xff, sizeof(image));
+
+ /* First, read the mpio original firmware into the image */
+ f = fopen(infile, "rb");
+ if(!f)
+ {
+ fprintf(stderr, "[ERR] Can not open %s file for reading\n", infile);
+ return -1;
+ }
+
+ i = fread(image, 1, OF_FIRMWARE_LEN, f);
+ if(i < OF_FIRMWARE_LEN)
+ {
+ fprintf(stderr, "[ERR] %s file read error\n", infile);
+ fclose(f);
+ return -2;
+ }
+
+ fclose(f);
+
+ /* Now check if we have OF file loaded based on presence
+ * of the version string in firmware
+ */
+
+ for(model_index = 0; model_index < NUM_MODELS; model_index++)
+ if (strcmp(mpio_models[model_index].of_model_string,
+ (char*)(image + MPIO_STRING_OFFSET)) == 0)
+ break;
+
+ if(model_index == NUM_MODELS)
+ {
+ fprintf(stderr, "[ERR] Unknown MPIO original firmware version\n");
+ return -3;
+ }
+
+ fprintf(stderr, "[INFO] Loading original firmware file for %s\n",
+ mpio_models[model_index].model_name);
+
+ /* Now, read the boot loader into the image */
+ f = fopen(bootfile, "rb");
+ if(!f)
+ {
+ fprintf(stderr, "[ERR] Can not open %s file for reading\n", bootfile);
+ return -4;
+ }
+
+ fprintf(stderr, "[INFO] Loading Rockbox bootloader file\n");
+
+ /* get bootloader size
+ * excluding header
+ */
+ fseek(f, 0, SEEK_END);
+ len = ftell(f) - 8;
+
+ if (len > BOOTLOADER_MAX_SIZE)
+ {
+ fprintf(stderr, "[ERR] Bootloader doesn't fit in firmware file.\n");
+ fprintf(stderr, "[ERR] This bootloader is %d bytes long\n", len);
+ fprintf(stderr, "[ERR] and maximum allowed size is %d bytes\n",
+ BOOTLOADER_MAX_SIZE);
+ return -5;
+ }
+
+ /* Now check if the place we want to put
+ * our bootloader is free
+ */
+ for(i=0;i<len;i++)
+ {
+ if (image[origin+i] != 0)
+ {
+ fprintf(stderr, "[ERR] Place for bootloader in OF file not empty\n");
+ return -6;
+ }
+ }
+
+ fseek(f, 0, SEEK_SET);
+
+ /* get bootloader header*/
+ fread(header,1,8,f);
+
+ if ( memcmp(header + 4, mpio_models[model_index].rb_model_name, 4) != 0 )
+ {
+ fprintf(stderr, "[ERR] Original firmware and rockbox bootloader mismatch!\n");
+ fprintf(stderr, "[ERR] Double check that you have bootloader for %s\n",
+ mpio_models[model_index].model_name);
+ return -7;
+ }
+
+ /* omit header */
+ fseek(f, 8, SEEK_SET);
+
+ i = fread(image + origin, 1, len, f);
+ if(i < len)
+ {
+ fprintf(stderr, "[ERR] %s file read error\n", bootfile);
+ fclose(f);
+ return -8;
+ }
+
+ fclose(f);
+
+ /* calculate checksum and compare with data
+ * from header
+ */
+ file_checksum = checksum(image + origin, mpio_models[model_index].rb_model_num, len);
+
+ if ( file_checksum != get_uint32be(header) )
+ {
+ fprintf(stderr,"[ERR] Bootloader checksum error\n");
+ return -9;
+ }
+
+ f = fopen(outfile, "wb");
+ if(!f)
+ {
+ fprintf(stderr, "[ERR] Can not open %s file for writing\n" ,outfile);
+ return -10;
+ }
+
+ fprintf(stderr, "[INFO] Patching reset vector\n");
+
+ /* Patch the stack pointer address */
+ image[0] = image[origin + 0];
+ image[1] = image[origin + 1];
+ image[2] = image[origin + 2];
+ image[3] = image[origin + 3];
+
+ /* Patch the reset vector to start the boot loader */
+ image[4] = image[origin + 4];
+ image[5] = image[origin + 5];
+ image[6] = image[origin + 6];
+ image[7] = image[origin + 7];
+
+ i = fwrite(image, 1, OF_FIRMWARE_LEN, f);
+ if(i < OF_FIRMWARE_LEN)
+ {
+ fprintf(stderr,"[ERR] %s file write error\n", outfile);
+ fclose(f);
+ return -11;
+ }
+
+ fprintf(stderr,"[INFO] Wrote 0x%x bytes in %s\n", OF_FIRMWARE_LEN, outfile);
+ fprintf(stderr,"[INFO] Patching succeeded!\n");
+
+ fclose(f);
+
+ return 0;
+}