Introduce mkrk27boot - tool for patching bootloader images of rk27xx targets.

Change-Id: I37e15111eb9e761b8c6c25f9c1f65a827894a192
diff --git a/rbutil/mkrk27boot/Makefile b/rbutil/mkrk27boot/Makefile
new file mode 100644
index 0000000..cd1b780
--- /dev/null
+++ b/rbutil/mkrk27boot/Makefile
@@ -0,0 +1,77 @@
+#             __________               __   ___.
+#   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+#   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+#   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+#   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+#                     \/            \/     \/    \/            \/
+# $Id$
+#
+
+FIRMWARE = ../../firmware/
+
+DRIVERS = $(FIRMWARE)drivers/
+EXPORT = $(FIRMWARE)export/
+
+BUILDDATE=$(shell date -u +'-DYEAR=%Y -DMONTH=%m -DDAY=%d')
+INCLUDE = -I$(EXPORT) -I$(FIRMWARE)include -I$(FIRMWARE)target/hosted -I$(FIRMWARE)target/hosted/sdl
+DEFINES =  -DTEST_FAT -DDISK_WRITE -DHAVE_FAT16SUPPORT -D__PCTOOL__
+
+SIM_FLAGS = -Wall -g -std=gnu99 -Wno-pointer-sign $(DEFINES) $(BUILDDATE) -I. $(INCLUDE) -I$(FIRMWARE)/libc/include
+FLAGS = -Wall -g -std=gnu99 -Wno-pointer-sign $(DEFINES) -I. $(INCLUDE)
+
+OUTPUT = mkrk27boot
+
+
+# inputs
+LIBSOURCES := $(DRIVERS)fat.c $(FIRMWARE)libc/ctype.c  $(FIRMWARE)libc/strtok.c \
+    $(FIRWARE)libc/errno.c$(FIRMWARE)common/strlcpy.c $(FIRMWARE)common/unicode.c \
+    $(FIRMWARE)common/file.c $(FIRMWARE)common/dir_uncached.c $(FIRMWARE)common/disk.c ata-sim.c mkrk27boot.c
+
+SOURCES := $(LIBSOURCES) main.c
+
+include ../libtools.make
+
+define sim_compile
+	@echo CC $<
+	$(SILENT)mkdir -p $(dir $@)
+	$(SILENT)$(CROSS)$(CC) $(SIM_FLAGS) -c -o $@ $<
+endef
+
+define compile
+	@echo CC $<
+	$(SILENT)mkdir -p $(dir $@)
+	$(SILENT)$(CROSS)$(CC) $(FLAGS) -c -o $@ $<
+endef
+
+$(OBJDIR)fat.o: $(DRIVERS)fat.c $(EXPORT)fat.h $(EXPORT)ata.h autoconf.h
+	$(sim_compile)
+
+$(OBJDIR)ctype.o: $(FIRMWARE)libc/ctype.c autoconf.h
+	$(sim_compile)
+
+$(OBJDIR)strtok.o: $(FIRMWARE)libc/strtok.c $(FIRMWARE)libc/include/string.h autoconf.h
+	$(sim_compile)
+
+$(OBJDIR)errno.o: $(FIRMWARE)libc/errno.c $(FIRMWARE)libc/include/errno.h autoconf.h
+	$(sim_compile)
+
+$(OBJDIR)disk.o: $(FIRMWARE)common/disk.c autoconf.h
+	$(sim_compile)
+
+$(OBJDIR)dir_uncached.o: $(FIRMWARE)common/dir_uncached.c autoconf.h
+	$(sim_compile)
+
+$(OBJDIR)file.o: $(FIRMWARE)common/file.c $(FIRMWARE)/include/file.h autoconf.h
+	$(sim_compile)
+
+$(OBJDIR)unicode.o: $(FIRMWARE)common/unicode.c autoconf.h
+	$(sim_compile)
+
+$(OBJDIR)strlcpy.o: $(FIRMWARE)common/strlcpy.c autoconf.h
+	$(sim_compile)
+
+$(OBJDIR)ata-sim.o: ata-sim.c $(EXPORT)ata.h autoconf.h
+	$(compile)
+
+$(OBJDIR)mkrk27boot.o: mkrk27boot.c mkrk27boot.h autoconf.h
+	$(compile)
diff --git a/rbutil/mkrk27boot/ata-sim.c b/rbutil/mkrk27boot/ata-sim.c
new file mode 100644
index 0000000..129c4b3
--- /dev/null
+++ b/rbutil/mkrk27boot/ata-sim.c
@@ -0,0 +1,122 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:$
+ *
+ * Copyright (C) 2012 by Andrew Ryabinin
+ *
+ * major portion of code is taken from firmware/test/fat/ata-sim.c
+ *
+ * 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 <stdarg.h>
+#include "debug.h"
+#include "dir.h"
+
+#define BLOCK_SIZE 512
+
+static FILE* file;
+
+extern char *img_filename;
+
+void mutex_init(struct mutex* l) {}
+void mutex_lock(struct mutex* l) {}
+void mutex_unlock(struct mutex* l) {}
+
+void panicf( char *fmt, ...) {
+    va_list ap;
+    va_start( ap, fmt );
+    fprintf(stderr,"***PANIC*** ");
+    vfprintf(stderr, fmt, ap );
+    va_end( ap );
+    exit(1);
+}
+
+void debugf(const char *fmt, ...) {
+    va_list ap;
+    va_start( ap, fmt );
+    fprintf(stderr,"DEBUGF: ");
+    vfprintf( stderr, fmt, ap );
+    va_end( ap );
+}
+
+void ldebugf(const char* file, int line, const char *fmt, ...) {
+    va_list ap;
+    va_start( ap, fmt );
+    fprintf( stderr, "%s:%d ", file, line );
+    vfprintf( stderr, fmt, ap );
+    va_end( ap );
+}
+
+int storage_read_sectors(unsigned long start, int count, void* buf)
+{
+    if ( count > 1 )
+        DEBUGF("[Reading %d blocks: 0x%lx to 0x%lx]\n",
+               count, start, start+count-1); 
+    else
+        DEBUGF("[Reading block 0x%lx]\n", start); 
+
+    if(fseek(file,start*BLOCK_SIZE,SEEK_SET)) {
+        perror("fseek");
+        return -1;
+    }
+    if(!fread(buf,BLOCK_SIZE,count,file)) {
+        DEBUGF("ata_write_sectors(0x%lx, 0x%x, %p)\n", start, count, buf );
+        perror("fread");
+        panicf("Disk error\n");
+    }
+    return 0;
+}
+
+int storage_write_sectors(unsigned long start, int count, void* buf)
+{
+    if ( count > 1 )
+        DEBUGF("[Writing %d blocks: 0x%lx to 0x%lx]\n",
+               count, start, start+count-1);
+    else
+        DEBUGF("[Writing block 0x%lx]\n", start);
+
+    if (start == 0)
+        panicf("Writing on sector 0!\n");
+
+    if(fseek(file,start*BLOCK_SIZE,SEEK_SET)) {
+        perror("fseek");
+        return -1;
+    }
+    if(!fwrite(buf,BLOCK_SIZE,count,file)) {
+        DEBUGF("ata_write_sectors(0x%lx, 0x%x, %p)\n", start, count, buf );
+        perror("fwrite");
+        panicf("Disk error\n");
+    }
+    return 0;
+}
+
+int ata_init(void)
+{
+    file=fopen(img_filename,"rb+");
+    if(!file) {
+        fprintf(stderr, "read_disk() - Could not find \"%s\"\n",img_filename);
+        return -1;
+    }
+    return 0;
+}
+
+void ata_exit(void)
+{
+    fclose(file);
+}
diff --git a/rbutil/mkrk27boot/autoconf.h b/rbutil/mkrk27boot/autoconf.h
new file mode 100644
index 0000000..a4907d4
--- /dev/null
+++ b/rbutil/mkrk27boot/autoconf.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:$
+ *
+ * Copyright (C) 2012 by Andrew Ryabinin
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef __BUILD_AUTOCONF_H
+#define __BUILD_AUTOCONF_H
+
+/* assume little endian for now */
+#define ROCKBOX_LITTLE_ENDIAN 1
+
+#define ROCKBOX_DIR "../../"
+
+/* Rename rockbox API functions which may conflict with system's functions.
+   This will allow to link libmkrk27boot with rbutil safely. */
+#define read(x,y,z) mkrk27_read(x,y,z)
+#define write(x,y,z) mkrk27_write(x,y,z)
+
+/* This should resolve confilict with ipodpatcher's filesize function. */
+#define filesize(x) mkrk27_filesize(x)
+
+#endif
diff --git a/rbutil/mkrk27boot/main.c b/rbutil/mkrk27boot/main.c
new file mode 100644
index 0000000..5ba69b5
--- /dev/null
+++ b/rbutil/mkrk27boot/main.c
@@ -0,0 +1,55 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:$
+ *
+ * Copyright (C) 2012 by Andrew Ryabinin
+ *
+ * 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 "mkrk27boot.h"
+
+
+int main(int argc, char **argv)
+{
+    char* img_file = NULL;
+    char* rkw_file = NULL;
+    char* out_img_file = NULL;
+    char* out_rkw_file = NULL;
+
+    if (argc == 4) {
+        img_file = argv[1];
+        rkw_file = argv[2];
+        out_img_file = argv[3];
+        out_rkw_file = "BASE.RKW";
+    } else if (argc == 5) {
+        img_file = argv[1];
+        rkw_file = argv[2];
+        out_img_file = argv[3];
+        out_rkw_file = argv[4];
+    } else {
+        fprintf(stdout, "\tusage: mkrk27boot <img file> <rkw file> <output img file> [<output rkw file>]\n");
+        fprintf(stdout, "\tIf output rkw file is not specified, the default is to put rkw file to BASE.RKW\n");
+        return -1;
+    }
+    if (mkrk27_patch(img_file, rkw_file, out_img_file, out_rkw_file)) {
+        fprintf(stdout, "%s\n", mkrk27_get_error());
+        return -1;
+    }
+    fprintf(stdout, "Success!\n");
+    return 0;
+}
+
diff --git a/rbutil/mkrk27boot/mkrk27boot.c b/rbutil/mkrk27boot/mkrk27boot.c
new file mode 100644
index 0000000..4b51e28
--- /dev/null
+++ b/rbutil/mkrk27boot/mkrk27boot.c
@@ -0,0 +1,281 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:$
+ *
+ * Copyright (C) 2012 by Andrew Ryabinin
+ *
+ * based on firmware/test/fat/main.c
+ *
+ * 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 <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include "disk.h"
+#include "dir.h"
+#include "file.h"
+#include "ata.h"
+#include "storage.h"
+#include "mkrk27boot.h"
+
+
+const char *img_filename;
+static char mkrk27_error_msg[256];
+
+static void mkrk27_set_error(const char *msg, ...) {
+    va_list ap;
+    va_start(ap, msg);
+    snprintf(mkrk27_error_msg, sizeof(mkrk27_error_msg), msg, ap);
+    va_end(ap);
+    return;
+}
+
+
+
+/* Extracts in_file from FAT image to out_file */
+static int extract_file(const char *in_file, const char* out_file) {
+    char buf[81920];
+    int nread;
+
+    int fd = open(in_file, O_RDONLY);
+    if (fd < 0) {
+        mkrk27_set_error("Failed to open file %s. ", in_file);
+        return -1;
+    }
+
+    FILE *file = fopen(out_file, "wb");
+    if (!file) {
+        mkrk27_set_error("Failed to open file %s. ", out_file);
+        close(fd);
+        return -1;
+    }
+
+    while (nread = read(fd, buf, sizeof buf), nread > 0) {
+        char *out_ptr = buf;
+        int nwritten;
+
+        do {
+            nwritten = fwrite(buf, 1, nread, file);
+
+            if (nwritten >= 0) {
+                nread -= nwritten;
+                out_ptr += nwritten;
+            } else if (errno != EINTR) {
+                mkrk27_set_error("Writting to file %s failed.", out_file);
+                goto exit;
+            }
+        } while(nread > 0);
+    }
+
+    if (nread == 0) {
+        fclose(file);
+        close(fd);
+        return 0;
+    } else {
+        mkrk27_set_error("Copy from %s to %s failed.", out_file, in_file);
+    }
+exit:
+    fclose(file);
+    close(fd);
+    return -1;
+}
+
+/* Replace out_file in FAT image with in_file */
+static int replace_file(const char *in_file, const char *out_file) {
+    char buf[81920];
+    int fd;
+    int nread;
+
+    fd = creat(out_file, 0666);
+    if (fd < 0) {
+        mkrk27_set_error("Failed to open file %s. ", out_file);
+        return -1;
+    }
+
+    FILE *file = fopen(in_file, "rb");
+    if (!file) {
+        mkrk27_set_error("Failed to open file %s. ", in_file);
+        close(fd);
+        return -1;
+    }
+
+    while (nread = fread(buf, 1, sizeof buf, file), nread > 0) {
+        char *out_ptr = buf;
+        int nwritten;
+
+        do {
+            nwritten = write(fd, buf, nread);
+            if (nwritten >= 0) {
+                nread -= nwritten;
+                out_ptr += nwritten;
+            } else {
+                mkrk27_set_error("Writting to file %s failed.", out_file);
+                goto exit;
+            }
+        } while(nread > 0);
+    }
+
+    if (nread == 0) {
+        fclose(file);
+        close(fd);
+        return 0;
+    } else {
+        mkrk27_set_error("Replacing %s with %s failed.", out_file, in_file);
+    }
+
+exit:
+    fclose(file);
+    close(fd);
+    return -1;
+}
+
+static int mkrk27_init(const char *filename) {
+    int i;
+    int rc;
+    srand(clock());
+
+    img_filename = filename;
+
+    if(ata_init()) {
+        mkrk27_set_error("Warning! The disk is uninitialized\n");
+        return -1;
+    }
+
+    struct partinfo *pinfo = disk_init();
+
+    if (!pinfo) {
+        mkrk27_set_error("Failed reading partitions\n");
+        return -1;
+    }
+
+    for ( i=0; i<4; i++ ) {
+        if ( pinfo[i].type == PARTITION_TYPE_FAT32
+#ifdef HAVE_FAT16SUPPORT
+             || pinfo[i].type == PARTITION_TYPE_FAT16
+#endif
+            ) {
+            rc = fat_mount(IF_MV2(0,) IF_MD2(0,) pinfo[i].start);
+            if(rc) {
+                mkrk27_set_error("mount: %d",rc);
+                return -1;
+            }
+            break;
+        }
+    }
+    if ( i==4 ) {
+        if(fat_mount(IF_MV2(0,) IF_MD2(0,) 0)) {
+            mkrk27_set_error("FAT32 partition!");
+            return -1;
+        }
+    }
+    return 0;
+}
+
+extern void ata_exit(void);
+
+static void mkrk27_deinit(void) {
+    ata_exit();
+}
+
+/* copy file */
+static int copy(const char *to, const char *from) {
+    FILE* fd_to, *fd_from;
+    char buf[4096];
+    ssize_t nread;
+
+    if (to == from) {
+        return 0;
+    }
+
+    fd_from = fopen(from, "rb");
+    if (!fd_from) {
+        mkrk27_set_error("Failed to open file %s.", from);
+        return -1;
+    }
+
+    fd_to = fopen(to, "wb");
+    if (!fd_to) {
+        mkrk27_set_error("Failed to open file %s.", to);
+        goto out_error;
+    }
+
+    while (nread = fread(buf, 1, sizeof buf, fd_from), nread > 0) {
+        char *out_ptr = buf;
+        ssize_t nwritten;
+
+        do {
+            nwritten = fwrite(out_ptr, 1, nread, fd_to);
+
+            if (nwritten >= 0)
+            {
+                nread -= nwritten;
+                out_ptr += nwritten;
+            }
+            else if (errno != EINTR)
+            {
+                mkrk27_set_error( "Writing to file %s failed.", to);
+                goto out_error;
+            }
+        } while (nread > 0);
+    }
+
+    if (nread == 0) {
+        fclose(fd_to);
+        fclose(fd_from);
+        return 0;
+    } else {
+        mkrk27_set_error("Copy from %s to %s failed.", from, to);
+    }
+
+out_error:
+
+    fclose(fd_from);
+    fclose(fd_to);
+
+    return -1;
+}
+
+char* mkrk27_get_error(void) {
+    return mkrk27_error_msg;
+}
+
+
+/* Patch rk27 firmware.
+    - img_file - original FAT image file, containing OF,
+    - rkw_file - rkw file which will replace BASE.RKW from OF,
+    - out_img_file - patched img file,
+    - out_rkw_file - BASE.RKW extracted from OF.
+*/
+int mkrk27_patch(const char* img_file, const char* rkw_file, const char* out_img_file, const char* out_rkw_file) {
+    if (copy(out_img_file, img_file)) {
+        return -1;
+    }
+    if (mkrk27_init(out_img_file)) {
+        return -1;
+    }
+    if (extract_file("/SYSTEM/BASE.RKW", out_rkw_file)) {
+        return -1;
+    }
+    if (replace_file(rkw_file, "/SYSTEM/BASE.RKW") ||
+        replace_file(rkw_file, "/SYSTEM00/BASE.RKW")) {
+        return -1;
+    }
+    mkrk27_deinit();
+    return 0;
+}
diff --git a/rbutil/mkrk27boot/mkrk27boot.h b/rbutil/mkrk27boot/mkrk27boot.h
new file mode 100644
index 0000000..dc8c2e8
--- /dev/null
+++ b/rbutil/mkrk27boot/mkrk27boot.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:$
+ *
+ * Copyright (C) 2012 by Andrew Ryabinin
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef _MKRK27BOOT_H
+#define _MKRK27BOOT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int mkrk27_patch(const char* img_file, const char* rkw_file, const char* out_img_file, const char* out_rkw_file);
+char *mkrk27_get_error(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif