From 460c5b05ebdb84519b878ee2c044728b9a44b38a Mon Sep 17 00:00:00 2001 From: cfig Date: Sat, 18 Jun 2016 00:00:59 +0800 Subject: [PATCH] mass update for next generation boot.img remove original abootimg by Gilles Grandou create groovy abootimg and mkbootimg previous bootimg.cfg moved to bootimg.json update doc: README.expert.md --- README.expert.md | 58 ++ README.md | 2 +- abootimg/build.gradle | 34 + .../src/main/groovy/cfig/bootimg/CArgs.groovy | 34 + .../main/groovy/cfig/bootimg/CImgInfo.groovy | 20 + .../main/groovy/cfig/bootimg/abootimg.groovy | 182 ++++ .../main/groovy/cfig/bootimg/mkbootimg.groovy | 344 +++++++ build.gradle | 19 +- settings.gradle | 1 + src/abootimg/c/abootimg.c | 952 ------------------ src/abootimg/headers/bootimg.h | 97 -- src/abootimg/headers/version.h | 2 - 12 files changed, 680 insertions(+), 1065 deletions(-) create mode 100644 README.expert.md create mode 100644 abootimg/build.gradle create mode 100644 abootimg/src/main/groovy/cfig/bootimg/CArgs.groovy create mode 100644 abootimg/src/main/groovy/cfig/bootimg/CImgInfo.groovy create mode 100644 abootimg/src/main/groovy/cfig/bootimg/abootimg.groovy create mode 100644 abootimg/src/main/groovy/cfig/bootimg/mkbootimg.groovy delete mode 100644 src/abootimg/c/abootimg.c delete mode 100644 src/abootimg/headers/bootimg.h delete mode 100644 src/abootimg/headers/version.h diff --git a/README.expert.md b/README.expert.md new file mode 100644 index 0000000..91d98ae --- /dev/null +++ b/README.expert.md @@ -0,0 +1,58 @@ +# layout of boot.img + +### 1. header part + + item size in bytes + +----------------------------------------------------------+ + | | 8 | + |--------------------------------+-------------------------| + | | 4 | + |--------------------------------+-------------------------| + | | 4 | + |--------------------------------+-------------------------| + | | 4 | + |--------------------------------+-------------------------| + | | 4 | + |--------------------------------+-------------------------| + | | 4 | + |--------------------------------+-------------------------| + | | 4 | + |--------------------------------+-------------------------| + | | 4 | + |--------------------------------+-------------------------| + | | 4 | + |--------------------------------+-------------------------| + | RESERVED | 4 | + |--------------------------------+-------------------------| + | | 4 | + |--------------------------------+-------------------------| + | | 16 | + |--------------------------------+-------------------------| + | | 512 | + |--------------------------------+-------------------------| + | | 32 | + |--------------------------------+-------------------------| + | | 1024 | + |--------------------------------+-------------------------| + | | min(n * page_zie - 1632)| + +----------------------------------------------------------+ + +### 2. data part + + +----------------------------------------------------------+ + | | kernel length | + |--------------------------------+-------------------------| + | | | + +----------------------------------------------------------+ + + +--------------------------------+-------------------------+ + | | ramdisk length | + |--------------------------------+-------------------------| + | | | + +----------------------------------------------------------+ + + +--------------------------------+-------------------------+ + | | second bootloader length| + |--------------------------------+-------------------------| + | | | + +----------------------------------------------------------+ diff --git a/README.md b/README.md index 0e0595e..74e9318 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Then put your boot.img at **$(CURDIR)/boot.img**, then start gradle 'unpack' tas Your get the flattened kernel and /root filesystem under **$(CURDIR)/build/unzip\_boot**: build/unzip_boot/ - ├── bootimg.cfg + ├── bootimg.json ├── kernel └── root diff --git a/abootimg/build.gradle b/abootimg/build.gradle new file mode 100644 index 0000000..8f85c60 --- /dev/null +++ b/abootimg/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'groovy' +apply plugin: 'java' + +sourceCompatibility = 1.7 + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.codehaus.groovy:groovy-all:2.3.11' + compile group: 'net.sf.jopt-simple', name: 'jopt-simple', version: '5.0.2' + testCompile group: 'junit', name: 'junit', version: '4.11' +} + +task abootimg(type: Jar, dependsOn:['build']) { + from files(sourceSets.main.output.classesDir) + from configurations.runtime.asFileTree.files.collect { zipTree(it) } + + baseName = 'abootimg' + manifest { + attributes 'Main-Class': 'cfig.bootimg.abootimg' + } +} + +task mkbootimg(type: Jar, dependsOn:['build']) { + from files(sourceSets.main.output.classesDir) + from configurations.runtime.asFileTree.files.collect { zipTree(it) } + + baseName = 'mkbootimg' + manifest { + attributes 'Main-Class': 'cfig.bootimg.mkbootimg' + } +} diff --git a/abootimg/src/main/groovy/cfig/bootimg/CArgs.groovy b/abootimg/src/main/groovy/cfig/bootimg/CArgs.groovy new file mode 100644 index 0000000..1e8b4f7 --- /dev/null +++ b/abootimg/src/main/groovy/cfig/bootimg/CArgs.groovy @@ -0,0 +1,34 @@ +package cfig.bootimg + +import groovy.transform.ToString + +/** + * Created by yu at 09:57 on 2016-06-17 + */ +@ToString(includeNames = true, includeFields = true) +class CArgs { + public String kernel; + public String ramdisk; + public String output; + public String cfg; + public String board; + public String second; + public String cmdline; + public String os_version; + public String os_patch_level; + public int base; + public int kernel_offset; + public int ramdisk_offset; + public int second_offset; + public int pagesize; + public int tags_offset; + public boolean id; + + CArgs() { + kernel = "kernel"; + ramdisk = "ramdisk.img.gz"; + second = "second"; + output = "boot.img"; + cfg = "bootimg.json"; + } +} diff --git a/abootimg/src/main/groovy/cfig/bootimg/CImgInfo.groovy b/abootimg/src/main/groovy/cfig/bootimg/CImgInfo.groovy new file mode 100644 index 0000000..7fddf30 --- /dev/null +++ b/abootimg/src/main/groovy/cfig/bootimg/CImgInfo.groovy @@ -0,0 +1,20 @@ +package cfig.bootimg + +import groovy.transform.ToString + +/** + * Created by yu at 09:58 on 2016-06-17 + */ +@ToString(includeNames=true, includeFields=true) +class CImgInfo extends CArgs { + public int kernel_len; + public int ramdisk_len; + public int second_len; + public int kernel_pos; + public int ramdisk_pos; + public int second_pos; + public byte[] hash; + public String dump() { + return super.toString() + " ; " + toString(); + } +} diff --git a/abootimg/src/main/groovy/cfig/bootimg/abootimg.groovy b/abootimg/src/main/groovy/cfig/bootimg/abootimg.groovy new file mode 100644 index 0000000..8ba6349 --- /dev/null +++ b/abootimg/src/main/groovy/cfig/bootimg/abootimg.groovy @@ -0,0 +1,182 @@ +package cfig.bootimg + +import groovy.json.JsonBuilder +import java.nio.ByteBuffer + +// http://mvnrepository.com/artifact/net.sf.jopt-simple/jopt-simple +//@Grapes( +// @Grab(group='net.sf.jopt-simple', module='jopt-simple', version='5.0.1') +//) +import java.nio.ByteOrder + +int readInt(InputStream is) { + ByteBuffer bf = ByteBuffer.allocate(128); + bf.order(ByteOrder.LITTLE_ENDIAN); + byte[] data4 = new byte[4]; + assert 4 == is.read(data4) + bf.clear(); + bf.put(data4); + bf.flip(); + return bf.getInt(); +} + +byte[] readBytes(InputStream is, int len) { + byte[] data4 = new byte[len]; + assert len == is.read(data4) + return data4; +} + +String unparse_os_version(int x) { + int a = x >> 14; + int b = (x - (a << 14)) >> 7; + int c = x & 0x7f; + + return String.format("%d.%d.%d", a, b, c); +} + +String unparse_os_patch_level(int x) { + int y = x >> 4; + int m = x & 0xf; + y += 2000; + + return String.format("%d-%d-%d", y, m, 0); +} + +int get_header_len(int pagesize) { + int pad = (pagesize - (1632 & (pagesize - 1))) & (pagesize - 1); + return pad + 1632; +} + +int get_pad_len(int position, int pagesize) { + return (pagesize - (position & (pagesize - 1))) & (pagesize - 1); +} + +String bytes2String(byte[] inData) { + StringBuilder sb = new StringBuilder(""); + for (int i = 0; i < inData.length; i++) { + sb.append(Integer.toString((inData[i] & 0xff) + 0x100, 16).substring(1)); + } + return sb.toString(); +} + +void parse_header(String fileName, CImgInfo inImgInfo) { + InputStream is = new FileInputStream(new File(fileName)) + assert Arrays.equals(readBytes(is, 8), "ANDROID!".getBytes()) + inImgInfo.kernel_len = readInt(is); + inImgInfo.kernel_offset = readInt(is); + inImgInfo.ramdisk_len = readInt(is); + inImgInfo.ramdisk_offset = readInt(is); + inImgInfo.second_len = readInt(is); + inImgInfo.second_offset = readInt(is); + inImgInfo.tags_offset = readInt(is); + inImgInfo.pagesize = readInt(is); + assert 0 == readInt(is) //reserved + int os_and_patch = readInt(is) + if (0 != os_and_patch) { //treated as 'reserved' in this boot image + inImgInfo.os_version = unparse_os_version(os_and_patch >> 11) + inImgInfo.os_patch_level = unparse_os_patch_level(os_and_patch & 0x7ff) + } + inImgInfo.board = new String(readBytes(is, 16), "UTF-8").trim(); + inImgInfo.cmdline = new String(readBytes(is, 512), "UTF-8") + inImgInfo.hash = readBytes(is, 32); //hash + inImgInfo.cmdline += new String(readBytes(is, 1024), "UTF-8") + inImgInfo.cmdline = inImgInfo.cmdline.trim(); + is.close(); + + //calc subimg positions + inImgInfo.kernel_pos = get_header_len(inImgInfo.pagesize) + inImgInfo.ramdisk_pos = inImgInfo.kernel_pos + inImgInfo.kernel_len + get_pad_len(inImgInfo.kernel_len, inImgInfo.pagesize) + inImgInfo.second_pos = inImgInfo.ramdisk_pos + inImgInfo.ramdisk_len + get_pad_len(inImgInfo.ramdisk_len, inImgInfo.pagesize) + + //adjust args + if (inImgInfo.kernel_offset > 0x10000000) { + inImgInfo.base = 0x10000000; + inImgInfo.kernel_offset -= inImgInfo.base; + inImgInfo.ramdisk_offset -= inImgInfo.base; + inImgInfo.second_offset -= inImgInfo.base; + inImgInfo.tags_offset -= inImgInfo.base; + } +} + +void extract_img_header(CImgInfo inImgInfo) { + JsonBuilder jb = new JsonBuilder(); + hashString = bytes2String(inImgInfo.hash); + jb.bootimg { + args { +// kernel inImgInfo.kernel; +// ramdisk inImgInfo.ramdisk; +// second inImgInfo.second; +// output inImgInfo.output; + base "0x" + Integer.toHexString(inImgInfo.base); + kernel_offset "0x" + Integer.toHexString(inImgInfo.kernel_offset); + ramdisk_offset "0x" + Integer.toHexString(inImgInfo.ramdisk_offset); + second_offset "0x" + Integer.toHexString(inImgInfo.second_offset); + tags_offset "0x" + Integer.toHexString(inImgInfo.tags_offset); + pagesize inImgInfo.pagesize; + board inImgInfo.board; + cmdline inImgInfo.cmdline; + os_version inImgInfo.os_version; + os_patch_level inImgInfo.os_patch_level; + id inImgInfo.id; + } + img { + kernel_pos inImgInfo.kernel_pos; + kernel_len inImgInfo.kernel_len; + ramdisk_pos inImgInfo.ramdisk_pos; + ramdisk_len inImgInfo.ramdisk_len; + second_pos inImgInfo.second_pos; + second_len inImgInfo.second_len; + hash hashString; + } + } + FileWriter fw = new FileWriter(inImgInfo.cfg); + fw.write(jb.toPrettyString()); + fw.flush(); + fw.close(); +} + +void extract_img_data(String inBootImg, String outImgName, int offset, int length) { + if (0 == length) { + return; + } + RandomAccessFile inRaf = new RandomAccessFile(inBootImg, "r"); + RandomAccessFile outRaf = new RandomAccessFile(outImgName, "rw"); + inRaf.seek(offset); + byte[] data = new byte[length]; + assert length == inRaf.read(data) + outRaf.write(data); + outRaf.close(); + inRaf.close(); +} + +void printUsage() { + println("Usage: abootimg [work_dir]"); + System.exit(1); +} + +void abootimg(String[] arg) { + CImgInfo imgInfo = new CImgInfo(); + String fileName; + String workDir = "unzip_boot"; + if (1 == arg.length) { + fileName = arg[0]; + } else if (2 == arg.length) { + fileName = arg[0]; + workDir = arg[1]; + } else { + printUsage(); + } + imgInfo.kernel = workDir + File.separator + imgInfo.kernel; + imgInfo.ramdisk = workDir + File.separator + imgInfo.ramdisk; + imgInfo.second = workDir + File.separator + imgInfo.second; + imgInfo.cfg = workDir + File.separator + imgInfo.cfg; + + parse_header(fileName, imgInfo); + new File(workDir).mkdir(); + extract_img_data(fileName, imgInfo.kernel, imgInfo.kernel_pos, imgInfo.kernel_len) + extract_img_data(fileName, imgInfo.ramdisk, imgInfo.ramdisk_pos, imgInfo.ramdisk_len) + extract_img_data(fileName, imgInfo.second, imgInfo.second_pos, imgInfo.second_len) + extract_img_header(imgInfo); +} + +abootimg(args); diff --git a/abootimg/src/main/groovy/cfig/bootimg/mkbootimg.groovy b/abootimg/src/main/groovy/cfig/bootimg/mkbootimg.groovy new file mode 100644 index 0000000..e3d847e --- /dev/null +++ b/abootimg/src/main/groovy/cfig/bootimg/mkbootimg.groovy @@ -0,0 +1,344 @@ +package cfig.bootimg + +// http://mvnrepository.com/artifact/net.sf.jopt-simple/jopt-simple +//@Grapes( +// @Grab(group='net.sf.jopt-simple', module='jopt-simple', version='5.0.1') +//) +import java.nio.ByteBuffer; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import java.nio.channels.FileChannel; +import java.nio.ByteOrder; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.security.MessageDigest; + +CArgs parse_cmdline(String[] inArgs) { + OptionParser parser = new OptionParser(); + parser.accepts("kernel", "path to the kernel").withRequiredArg(); + parser.accepts("ramdisk", "path to the ramdisk").withRequiredArg(); + parser.accepts("second", "path to the 2nd bootloader").withRequiredArg(); + parser.accepts("cmdline", "extra arguments to be passed on the kernel command line").withRequiredArg(); + parser.accepts("base", "base address").withRequiredArg(); + parser.accepts("kernel_offset", "kernel offset").withRequiredArg(); + parser.accepts("ramdisk_offset", "ramdisk offset").withRequiredArg(); + parser.accepts("second_offset", "2nd bootloader offset").withRequiredArg(); + parser.accepts("os_version", "operating system version").withRequiredArg(); + parser.accepts("os_patch_level", "operating system patch level").withRequiredArg(); + parser.accepts("tags_offset", "tags offset").withRequiredArg(); + parser.accepts("board", "board name").withRequiredArg(); + parser.accepts("pagesize", "page size").withRequiredArg(); + parser.accepts("id", "print the image ID on standard output"); + parser.accepts("output", "output file name").withRequiredArg(); + + OptionSet options = parser.parse(inArgs) + CArgs ret = new CArgs(); + + ret.kernel = options.valueOf("kernel") + + ret.output = options.valueOf("output") + + ret.ramdisk = options.valueOf("ramdisk") + + ret.second = options.valueOf("second") + + if (options.has("board")) { + ret.board = options.valueOf("board") + } else { + ret.board = "" + } + + ret.id = options.has("id") + + if (options.has("base")) { + ret.base = Integer.decode(options.valueOf("base")) + } else { + ret.base = 0x10000000; + } + + if (options.has("kernel_offset")) { + ret.kernel_offset = Integer.decode(options.valueOf("kernel_offset")) + } else { + ret.kernel_offset = 0x00008000; + } + + if (options.has("ramdisk_offset")) { + ret.ramdisk_offset = Integer.decode(options.valueOf("ramdisk_offset")) + } else { + ret.ramdisk_offset = 0x01000000 + } + + ret.os_version = options.valueOf("os_version") + + ret.os_patch_level = options.valueOf("os_patch_level") + + if (options.has("second_offset")) { + ret.second_offset = Integer.decode(options.valueOf("second_offset")) + } else { + ret.second_offset = 0x00f00000 + } + + if (options.has("tags_offset")) { + ret.tags_offset = Integer.decode(options.valueOf("tags_offset")) + } else { + ret.tags_offset = 0x00000100 + } + + if (options.has("pagesize")) { + ret.pagesize = Integer.decode(options.valueOf("pagesize")) + } else { + ret.pagesize = 2048 + } + + if (options.has("cmdline")) { + ret.cmdline = options.valueOf("cmdline") + } else { + ret.cmdline = "" + } + + if (ret.cmdline.length() > 1536) { + println("cmdline length must <= 1536, current is " + ret.cmdline.length()); + printUsage(parser); + } + if (null == ret.kernel) { + println("kernel must not be empty"); + printUsage(parser); + } + if (null == ret.output) { + println("output file must not be empty"); + printUsage(parser); + } + if (ret.board.length() > 16) { + println("board name length must <= 16") + printUsage(parser); + } + + return ret; +} + +byte[] write_header(CArgs inArgs) { + ByteBuffer bf = ByteBuffer.allocate(1024 * 32); + bf.order(ByteOrder.LITTLE_ENDIAN); + + //header start + bf.put("ANDROID!".getBytes()) + bf.putInt((int) new File(inArgs.kernel).length()); + bf.putInt(inArgs.base + inArgs.kernel_offset) + + if (null == inArgs.ramdisk) { + bf.putInt(0) + } else { + bf.putInt((int) new File(inArgs.ramdisk).length()); + } + + bf.putInt(inArgs.base + inArgs.ramdisk_offset) + + if (null == inArgs.second) { + bf.putInt(0) + } else { + bf.putInt((int) new File(inArgs.second).length()); + } + + bf.putInt(inArgs.base + inArgs.second_offset) + bf.putInt(inArgs.base + inArgs.tags_offset) + bf.putInt(inArgs.pagesize) + bf.putInt(0); + bf.putInt((parse_os_version(inArgs.os_version) << 11) | parse_os_patch_level(inArgs.os_patch_level)) + + bf.put(inArgs.board.getBytes()) + bf.put(new byte[16 - inArgs.board.length()]) + bf.put(inArgs.cmdline.substring(0, Math.min(512, inArgs.cmdline.length())).getBytes()) + bf.put(new byte[512 - Math.min(512, inArgs.cmdline.length())]) + byte[] img_id = hashFile(inArgs.kernel, inArgs.ramdisk, inArgs.second) + bf.put(img_id) + bf.put(new byte[32 - img_id.length]) + + if (inArgs.cmdline.length() > 512) { + bf.put(inArgs.cmdline.substring(512).getBytes()) + bf.put(new byte[1024 + 512 - inArgs.cmdline.length()]) + } else { + bf.put(new byte[1024]) + } + + //padding + pad_file(bf, inArgs.pagesize) + + //write + FileOutputStream fos = new FileOutputStream(inArgs.output, false); + fos.write(bf.array(), 0, bf.position()) + fos.close(); + + return img_id; +} + +void printUsage(OptionParser p) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write("Usage: mkbootimg