mass update for next generation boot.img
remove original abootimg by Gilles Grandou <gilles@grandou.net> create groovy abootimg and mkbootimg previous bootimg.cfg moved to bootimg.json update doc: README.expert.mdpull/6/head
parent
56dacf31bd
commit
460c5b05eb
@ -0,0 +1,58 @@
|
||||
# layout of boot.img
|
||||
|
||||
### 1. header part
|
||||
|
||||
item size in bytes
|
||||
+----------------------------------------------------------+
|
||||
|<MAGIC HEADER> | 8 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<kernel length> | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<kernel offset> | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<ramdisk length> | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<ramdisk offset> | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<second bootloader length> | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<second bootloader offset> | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<tags offset> | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<page size> | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
| RESERVED | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<os version& os patch level> | 4 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<board name> | 16 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<cmdline part 1> | 512 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<hash digest> | 32 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<cmdline part 2> | 1024 |
|
||||
|--------------------------------+-------------------------|
|
||||
|<padding> | min(n * page_zie - 1632)|
|
||||
+----------------------------------------------------------+
|
||||
|
||||
### 2. data part
|
||||
|
||||
+----------------------------------------------------------+
|
||||
|<kernel> | kernel length |
|
||||
|--------------------------------+-------------------------|
|
||||
|<padding> | |
|
||||
+----------------------------------------------------------+
|
||||
|
||||
+--------------------------------+-------------------------+
|
||||
|<ramdisk> | ramdisk length |
|
||||
|--------------------------------+-------------------------|
|
||||
|<padding> | |
|
||||
+----------------------------------------------------------+
|
||||
|
||||
+--------------------------------+-------------------------+
|
||||
|<second bootloader> | second bootloader length|
|
||||
|--------------------------------+-------------------------|
|
||||
|<padding> | |
|
||||
+----------------------------------------------------------+
|
@ -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'
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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 <path_to_boot_image> [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);
|
@ -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 <option>\n".getBytes());
|
||||
p.printHelpOn(out);
|
||||
System.out.println(out.toString());
|
||||
out.close();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
void write_padded_file(ByteBuffer inBF, String srcFile, int padding) {
|
||||
if (null == srcFile) {
|
||||
return;
|
||||
}
|
||||
InputStream is = new FileInputStream(new File(srcFile))
|
||||
int byteRead;
|
||||
byte[] dataRead = new byte[128]
|
||||
while (true) {
|
||||
byteRead = is.read(dataRead)
|
||||
if (-1 == byteRead) {
|
||||
break;
|
||||
}
|
||||
inBF.put(dataRead, 0, byteRead);
|
||||
}
|
||||
is.close();
|
||||
pad_file(inBF, padding)
|
||||
}
|
||||
|
||||
void pad_file(ByteBuffer inBF, int padding) {
|
||||
int pad = (padding - (inBF.position() & (padding - 1))) & (padding - 1);
|
||||
inBF.put(new byte[pad]);
|
||||
}
|
||||
|
||||
void write_data(CArgs inArgs) {
|
||||
ByteBuffer bf = ByteBuffer.allocate(1024 * 1024 * 64);
|
||||
bf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
write_padded_file(bf, inArgs.kernel, inArgs.pagesize)
|
||||
write_padded_file(bf, inArgs.ramdisk, inArgs.pagesize)
|
||||
write_padded_file(bf, inArgs.second, inArgs.pagesize)
|
||||
|
||||
//write
|
||||
FileOutputStream fos = new FileOutputStream(inArgs.output, true);
|
||||
fos.write(bf.array(), 0, bf.position())
|
||||
fos.close();
|
||||
}
|
||||
|
||||
int parse_os_patch_level(x) {
|
||||
if (null == x) {
|
||||
return 0;
|
||||
}
|
||||
int ret = 0
|
||||
Pattern pattern = Pattern.compile("^(\\d{4})-(\\d{2})-(\\d{2})")
|
||||
Matcher matcher = pattern.matcher(x)
|
||||
if (matcher.find()) {
|
||||
int y = Integer.decode(matcher.group(1)) - 2000
|
||||
int m = Integer.decode(matcher.group(2))
|
||||
// 7 bits allocated for the year, 4 bits for the month
|
||||
assert y >= 0 && y < 128
|
||||
assert m > 0 && m <= 12
|
||||
ret = (y << 4) | m
|
||||
} else {
|
||||
throw new IllegalArgumentException("invalid os_patch_level")
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int parse_os_version(x) {
|
||||
int ret = 0;
|
||||
if (null != x) {
|
||||
Pattern pattern = Pattern.compile("^(\\d{1,3})(?:\\.(\\d{1,3})(?:\\.(\\d{1,3}))?)?");
|
||||
Matcher m = pattern.matcher(x)
|
||||
if (m.find()) {
|
||||
int a = Integer.decode(m.group(1))
|
||||
int b = 0;
|
||||
int c = 0;
|
||||
if (m.groupCount() >= 2) {
|
||||
b = Integer.decode(m.group(2))
|
||||
}
|
||||
if (m.groupCount() == 3) {
|
||||
c = Integer.decode(m.group(3))
|
||||
}
|
||||
assert a < 128
|
||||
assert b < 128
|
||||
assert c < 128
|
||||
ret = ((a << 14) | (b << 7) | c)
|
||||
} else {
|
||||
throw new IllegalArgumentException("invalid os_version")
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void test() {
|
||||
ByteBuffer b2 = ByteBuffer.allocate(1024);
|
||||
b2.order(ByteOrder.LITTLE_ENDIAN);
|
||||
b2.putInt(Integer.MAX_VALUE); //4 bytes
|
||||
println("max: " + Integer.MAX_VALUE)
|
||||
println("min: " + Integer.MIN_VALUE)
|
||||
b2.putInt(0x11111111)
|
||||
b2.putInt(Integer.MIN_VALUE);
|
||||
b2.putInt(0x11111111)
|
||||
b2.put("welcome".getBytes())
|
||||
b2.put(new byte[5]);
|
||||
b2.putInt(0x11111111)
|
||||
b2.putInt(0);
|
||||
b2.putInt(0x11111111)
|
||||
//b2.put((byte)0);
|
||||
b2.flip();
|
||||
FileChannel fc2 = new FileOutputStream(new File("ftest"), false).getChannel();
|
||||
fc2.write(b2);
|
||||
fc2.close();
|
||||
|
||||
//ByteBuffer bf = ByteBuffer.allocate(1024 * 1024 * 50);
|
||||
//bf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
//bf.flip()
|
||||
//boolean append = false;
|
||||
//FileChannel fc = new FileOutputStream(new File("f1"), append).getChannel();
|
||||
//fc.write(bf);
|
||||
//fc.close();
|
||||
//
|
||||
//FileOutputStream stream = new FileOutputStream("f2");
|
||||
//stream.write(bf.array(), 0, bf.position())
|
||||
//stream.close();
|
||||
}
|
||||
|
||||
byte[] hashFile(String... inFiles) {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1")
|
||||
|
||||
for (String item : inFiles) {
|
||||
ByteBuffer itemBF = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
|
||||
if (null == item) {
|
||||
md.update(itemBF.putInt(0).array())
|
||||
} else {
|
||||
InputStream is = new FileInputStream(new File(item))
|
||||
int byteRead;
|
||||
byte[] dataRead = new byte[128]
|
||||
while (true) {
|
||||
byteRead = is.read(dataRead)
|
||||
if (-1 == byteRead) {
|
||||
break;
|
||||
}
|
||||
md.update(dataRead, 0, byteRead)
|
||||
}
|
||||
is.close();
|
||||
md.update(itemBF.putInt((int) new File(item).length()).array())
|
||||
}
|
||||
}
|
||||
|
||||
return md.digest();
|
||||
}
|
||||
|
||||
void dumpBytes(byte[] inData) {
|
||||
StringBuffer sb = new StringBuffer("");
|
||||
for (int i = 0; i < inData.length; i++) {
|
||||
sb.append(Integer.toString((inData[i] & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
println("0x" + sb.toString());
|
||||
}
|
||||
|
||||
CArgs theArgs = parse_cmdline(args)
|
||||
byte[] img_id = write_header(theArgs)
|
||||
write_data(theArgs)
|
||||
if (theArgs.id) {
|
||||
ByteBuffer bf = ByteBuffer.allocate(32);
|
||||
bf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
bf.put(img_id);
|
||||
bf.put(new byte[32 - img_id.length])
|
||||
dumpBytes(bf.array());
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
include 'bouncycastle:bcpkix'
|
||||
include 'bouncycastle:bcprov'
|
||||
include 'boot_signer'
|
||||
include 'abootimg'
|
||||
|
@ -1,952 +0,0 @@
|
||||
/* abootimg - Manipulate (read, modify, create) Android Boot Images
|
||||
* Copyright (c) 2010-2011 Gilles Grandou <gilles@grandou.net>
|
||||
*
|
||||
* 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fs.h> /* BLKGETSIZE64 */
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include <sys/ioctl.h>
|
||||
#include <cygwin/fs.h> /* BLKGETSIZE64 */
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
#include <sys/disk.h> /* DIOCGMEDIASIZE */
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <sys/disk.h> /* DKIOCGETBLOCKCOUNT */
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAS_BLKID
|
||||
#include <blkid/blkid.h>
|
||||
#endif
|
||||
|
||||
#include "version.h"
|
||||
#include "bootimg.h"
|
||||
|
||||
|
||||
enum command {
|
||||
none,
|
||||
help,
|
||||
info,
|
||||
extract,
|
||||
update,
|
||||
create
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned size;
|
||||
int is_blkdev;
|
||||
|
||||
char* fname;
|
||||
char* config_fname;
|
||||
char* kernel_fname;
|
||||
char* ramdisk_fname;
|
||||
char* second_fname;
|
||||
|
||||
FILE* stream;
|
||||
|
||||
boot_img_hdr header;
|
||||
|
||||
char* kernel;
|
||||
char* ramdisk;
|
||||
char* second;
|
||||
} t_abootimg;
|
||||
|
||||
|
||||
#define MAX_CONF_LEN 4096
|
||||
char config_args[MAX_CONF_LEN] = "";
|
||||
|
||||
|
||||
|
||||
void abort_perror(char* str)
|
||||
{
|
||||
perror(str);
|
||||
exit(errno);
|
||||
}
|
||||
|
||||
void abort_printf(char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
int blkgetsize(int fd, unsigned long long *pbsize)
|
||||
{
|
||||
# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
return ioctl(fd, DIOCGMEDIASIZE, pbsize);
|
||||
# elif defined(__APPLE__)
|
||||
return ioctl(fd, DKIOCGETBLOCKCOUNT, pbsize);
|
||||
# elif defined(__NetBSD__)
|
||||
// does a suitable ioctl exist?
|
||||
// return (ioctl(fd, DIOCGDINFO, &label) == -1);
|
||||
return 1;
|
||||
# elif defined(__linux__) || defined(__CYGWIN__)
|
||||
return ioctl(fd, BLKGETSIZE64, pbsize);
|
||||
# elif defined(__GNU__)
|
||||
// does a suitable ioctl for HURD exist?
|
||||
return 1;
|
||||
# else
|
||||
return 1;
|
||||
# endif
|
||||
|
||||
}
|
||||
|
||||
void print_usage(void)
|
||||
{
|
||||
printf (
|
||||
" abootimg - manipulate Android Boot Images.\n"
|
||||
" (c) 2010-2011 Gilles Grandou <gilles@grandou.net>\n"
|
||||
" " VERSION_STR "\n"
|
||||
"\n"
|
||||
" abootimg [-h]\n"
|
||||
"\n"
|
||||
" print usage\n"
|
||||
"\n"
|
||||
" abootimg -i <bootimg>\n"
|
||||
"\n"
|
||||
" print boot image information\n"
|
||||
"\n"
|
||||
" abootimg -x <bootimg> [<bootimg.cfg> [<kernel> [<ramdisk> [<secondstage>]]]]\n"
|
||||
"\n"
|
||||
" extract objects from boot image:\n"
|
||||
" - config file (default name bootimg.cfg)\n"
|
||||
" - kernel image (default name zImage)\n"
|
||||
" - ramdisk image (default name initrd.img)\n"
|
||||
" - second stage image (default name stage2.img)\n"
|
||||
"\n"
|
||||
" abootimg -u <bootimg> [-c \"param=value\"] [-f <bootimg.cfg>] [-k <kernel>] [-r <ramdisk>] [-s <secondstage>]\n"
|
||||
"\n"
|
||||
" update a current boot image with objects given in command line\n"
|
||||
" - header informations given in arguments (several can be provided)\n"
|
||||
" - header informations given in config file\n"
|
||||
" - kernel image\n"
|
||||
" - ramdisk image\n"
|
||||
" - second stage image\n"
|
||||
"\n"
|
||||
" bootimg has to be valid Android Boot Image, or the update will abort.\n"
|
||||
"\n"
|
||||
" abootimg --create <bootimg> [-c \"param=value\"] [-f <bootimg.cfg>] -k <kernel> -r <ramdisk> [-s <secondstage>]\n"
|
||||
"\n"
|
||||
" create a new image from scratch.\n"
|
||||
" if the boot image file is a block device, sanity check will be performed to avoid overwriting a existing\n"
|
||||
" filesystem.\n"
|
||||
"\n"
|
||||
" argurments are the same than for -u.\n"
|
||||
" kernel and ramdisk are mandatory.\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
enum command parse_args(int argc, char** argv, t_abootimg* img)
|
||||
{
|
||||
enum command cmd = none;
|
||||
int i;
|
||||
|
||||
if (argc<2)
|
||||
return none;
|
||||
|
||||
if (!strcmp(argv[1], "-h")) {
|
||||
return help;
|
||||
}
|
||||
else if (!strcmp(argv[1], "-i")) {
|
||||
cmd=info;
|
||||
}
|
||||
else if (!strcmp(argv[1], "-x")) {
|
||||
cmd=extract;
|
||||
}
|
||||
else if (!strcmp(argv[1], "-u")) {
|
||||
cmd=update;
|
||||
}
|
||||
else if (!strcmp(argv[1], "--create")) {
|
||||
cmd=create;
|
||||
}
|
||||
else
|
||||
return none;
|
||||
|
||||
switch(cmd) {
|
||||
case none:
|
||||
case help:
|
||||
break;
|
||||
|
||||
case info:
|
||||
if (argc != 3)
|
||||
return none;
|
||||
img->fname = argv[2];
|
||||
break;
|
||||
|
||||
case extract:
|
||||
if ((argc < 3) || (argc > 7))
|
||||
return none;
|
||||
img->fname = argv[2];
|
||||
if (argc >= 4)
|
||||
img->config_fname = argv[3];
|
||||
if (argc >= 5)
|
||||
img->kernel_fname = argv[4];
|
||||
if (argc >= 6)
|
||||
img->ramdisk_fname = argv[5];
|
||||
if (argc >= 7)
|
||||
img->second_fname = argv[6];
|
||||
break;
|
||||
|
||||
case update:
|
||||
case create:
|
||||
if (argc < 3)
|
||||
return none;
|
||||
img->fname = argv[2];
|
||||
img->config_fname = NULL;
|
||||
img->kernel_fname = NULL;
|
||||
img->ramdisk_fname = NULL;
|
||||
img->second_fname = NULL;
|
||||
for(i=3; i<argc; i++) {
|
||||
if (!strcmp(argv[i], "-c")) {
|
||||
if (++i >= argc)
|
||||
return none;
|
||||
unsigned len = strlen(argv[i]);
|
||||
if (strlen(config_args)+len+1 >= MAX_CONF_LEN)
|
||||
abort_printf("too many config parameters.\n");
|
||||
strcat(config_args, argv[i]);
|
||||
strcat(config_args, "\n");
|
||||
}
|
||||
else if (!strcmp(argv[i], "-f")) {
|
||||
if (++i >= argc)
|
||||
return none;
|
||||
img->config_fname = argv[i];
|
||||
}
|
||||
else if (!strcmp(argv[i], "-k")) {
|
||||
if (++i >= argc)
|
||||
return none;
|
||||
img->kernel_fname = argv[i];
|
||||
}
|
||||
else if (!strcmp(argv[i], "-r")) {
|
||||
if (++i >= argc)
|
||||
return none;
|
||||
img->ramdisk_fname = argv[i];
|
||||
}
|
||||
else if (!strcmp(argv[i], "-s")) {
|
||||
if (++i >= argc)
|
||||
return none;
|
||||
img->second_fname = argv[i];
|
||||
}
|
||||
else
|
||||
return none;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int check_boot_img_header(t_abootimg* img)
|
||||
{
|
||||
if (strncmp((char*)(img->header.magic), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
|
||||
fprintf(stderr, "%s: no Android Magic Value\n", img->fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(img->header.kernel_size)) {
|
||||
fprintf(stderr, "%s: kernel size is null\n", img->fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(img->header.ramdisk_size)) {
|
||||
fprintf(stderr, "%s: ramdisk size is null\n", img->fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned page_size = img->header.page_size;
|
||||
if (!page_size) {
|
||||
fprintf(stderr, "%s: Image page size is null\n", img->fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned n = (img->header.kernel_size + page_size - 1) / page_size;
|
||||
unsigned m = (img->header.ramdisk_size + page_size - 1) / page_size;
|
||||
unsigned o = (img->header.second_size + page_size - 1) / page_size;
|
||||
|
||||
unsigned total_size = (1+n+m+o)*page_size;
|
||||
|
||||
if (total_size > img->size) {
|
||||
fprintf(stderr, "%s: sizes mismatches in boot image\n", img->fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check_if_block_device(t_abootimg* img)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(img->fname, &st))
|
||||
if (errno != ENOENT) {
|
||||
printf("errno=%d\n", errno);
|
||||
abort_perror(img->fname);
|
||||
}
|
||||
|
||||
#ifdef HAS_BLKID
|
||||
if (S_ISBLK(st.st_mode)) {
|
||||
img->is_blkdev = 1;
|
||||
|
||||
char* type = blkid_get_tag_value(NULL, "TYPE", img->fname);
|
||||
if (type)
|
||||
abort_printf("%s: refuse to write on a valid partition type (%s)\n", img->fname, type);
|
||||
|
||||
int fd = open(img->fname, O_RDONLY);
|
||||
if (fd == -1)
|
||||
abort_perror(img->fname);
|
||||
|
||||
unsigned long long bsize = 0;
|
||||
if (blkgetsize(fd, &bsize))
|
||||
abort_perror(img->fname);
|
||||
img->size = bsize;
|
||||
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void open_bootimg(t_abootimg* img, char* mode)
|
||||
{
|
||||
img->stream = fopen(img->fname, mode);
|
||||
if (!img->stream)
|
||||
abort_perror(img->fname);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void read_header(t_abootimg* img)
|
||||
{
|
||||
size_t rb = fread(&img->header, sizeof(boot_img_hdr), 1, img->stream);
|
||||
if ((rb!=1) || ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
else if (feof(img->stream))
|
||||
abort_printf("%s: cannot read image header\n", img->fname);
|
||||
|
||||
struct stat s;
|
||||
int fd = fileno(img->stream);
|
||||
if (fstat(fd, &s))
|
||||
abort_perror(img->fname);
|
||||
|
||||
if (S_ISBLK(s.st_mode)) {
|
||||
unsigned long long bsize = 0;
|
||||
|
||||
if (blkgetsize(fd, &bsize))
|
||||
abort_perror(img->fname);
|
||||
img->size = bsize;
|
||||
img->is_blkdev = 1;
|
||||
}
|
||||
else {
|
||||
img->size = s.st_size;
|
||||
img->is_blkdev = 0;
|
||||
}
|
||||
|
||||
if (check_boot_img_header(img))
|
||||
abort_printf("%s: not a valid Android Boot Image.\n", img->fname);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void update_header_entry(t_abootimg* img, char* cmd)
|
||||
{
|
||||
char *p;
|
||||
char *token;
|
||||
char *endtoken;
|
||||
char *value;
|
||||
|
||||
p = strchr(cmd, '\n');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
|
||||
p = cmd;
|
||||
p += strspn(p, " \t");
|
||||
token = p;
|
||||
|
||||
p += strcspn(p, " =\t");
|
||||
endtoken = p;
|
||||
p += strspn(p, " \t");
|
||||
|
||||
if (*p++ != '=')
|
||||
goto err;
|
||||
|
||||
p += strspn(p, " \t");
|
||||
value = p;
|
||||
|
||||
*endtoken = '\0';
|
||||
|
||||
unsigned valuenum = strtoul(value, NULL, 0);
|
||||
|
||||
if (!strcmp(token, "cmdline")) {
|
||||
unsigned len = strlen(value);
|
||||
if (len >= BOOT_ARGS_SIZE)
|
||||
abort_printf("cmdline length (%d) is too long (max %d)", len, BOOT_ARGS_SIZE-1);
|
||||
memset(img->header.cmdline, 0, BOOT_ARGS_SIZE);
|
||||
strcpy((char*)(img->header.cmdline), value);
|
||||
}
|
||||
else if (!strncmp(token, "name", 4)) {
|
||||
strncpy((char*)(img->header.name), value, BOOT_NAME_SIZE);
|
||||
img->header.name[BOOT_NAME_SIZE-1] = '\0';
|
||||
}
|
||||
else if (!strncmp(token, "bootsize", 8)) {
|
||||
if (img->is_blkdev && (img->size != valuenum))
|
||||
abort_printf("%s: cannot change Boot Image size for a block device\n", img->fname);
|
||||
img->size = valuenum;
|
||||
}
|
||||
else if (!strncmp(token, "pagesize", 8)) {
|
||||
img->header.page_size = valuenum;
|
||||
}
|
||||
else if (!strncmp(token, "kerneladdr", 10)) {
|
||||
img->header.kernel_addr = valuenum;
|
||||
}
|
||||
else if (!strncmp(token, "ramdiskaddr", 11)) {
|
||||
img->header.ramdisk_addr = valuenum;
|
||||
}
|
||||
else if (!strncmp(token, "secondaddr", 10)) {
|
||||
img->header.second_addr = valuenum;
|
||||
}
|
||||
else if (!strncmp(token, "tagsaddr", 8)) {
|
||||
img->header.tags_addr = valuenum;
|
||||
}
|
||||
else
|
||||
goto err;
|
||||
return;
|
||||
|
||||
err:
|
||||
abort_printf("%s: bad config entry\n", token);
|
||||
}
|
||||
|
||||
|
||||
void update_header(t_abootimg* img)
|
||||
{
|
||||
if (img->config_fname) {
|
||||
FILE* config_file = fopen(img->config_fname, "r");
|
||||
if (!config_file)
|
||||
abort_perror(img->config_fname);
|
||||
|
||||
printf("reading config file %s\n", img->config_fname);
|
||||
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
int read;
|
||||
|
||||
while ((read = getline(&line, &len, config_file)) != -1) {
|
||||
update_header_entry(img, line);
|
||||
free(line);
|
||||
line = NULL;
|
||||
}
|
||||
if (ferror(config_file))
|
||||
abort_perror(img->config_fname);
|
||||
}
|
||||
|
||||
unsigned len = strlen(config_args);
|
||||
if (len) {
|
||||
FILE* config_file = fmemopen(config_args, len, "r");
|
||||
if (!config_file)
|
||||
abort_perror("-c args");
|
||||
|
||||
printf("reading config args\n");
|
||||
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
int read;
|
||||
|
||||
while ((read = getline(&line, &len, config_file)) != -1) {
|
||||
update_header_entry(img, line);
|
||||
free(line);
|
||||
line = NULL;
|
||||
}
|
||||
if (ferror(config_file))
|
||||
abort_perror("-c args");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void update_images(t_abootimg *img)
|
||||
{
|
||||
unsigned page_size = img->header.page_size;
|
||||
unsigned ksize = img->header.kernel_size;
|
||||
unsigned rsize = img->header.ramdisk_size;
|
||||
unsigned ssize = img->header.second_size;
|
||||
|
||||
if (!page_size)
|
||||
abort_printf("%s: Image page size is null\n", img->fname);
|
||||
|
||||
unsigned n = (ksize + page_size - 1) / page_size;
|
||||
unsigned m = (rsize + page_size - 1) / page_size;
|
||||
unsigned o = (ssize + page_size - 1) / page_size;
|
||||
|
||||
unsigned roffset = (1+n)*page_size;
|
||||
unsigned soffset = (1+n+m)*page_size;
|
||||
|
||||
if (img->kernel_fname) {
|
||||
printf("reading kernel from %s\n", img->kernel_fname);
|
||||
FILE* stream = fopen(img->kernel_fname, "r");
|
||||
if (!stream)
|
||||
abort_perror(img->kernel_fname);
|
||||
struct stat st;
|
||||
if (fstat(fileno(stream), &st))
|
||||
abort_perror(img->kernel_fname);
|
||||
ksize = st.st_size;
|
||||
char* k = malloc(ksize);
|
||||
if (!k)
|
||||
abort_perror("");
|
||||
size_t rb = fread(k, ksize, 1, stream);
|
||||
if ((rb!=1) || ferror(stream))
|
||||
abort_perror(img->kernel_fname);
|
||||
else if (feof(stream))
|
||||
abort_printf("%s: cannot read kernel\n", img->kernel_fname);
|
||||
fclose(stream);
|
||||
img->header.kernel_size = ksize;
|
||||
img->kernel = k;
|
||||
}
|
||||
|
||||
if (img->ramdisk_fname) {
|
||||
printf("reading ramdisk from %s\n", img->ramdisk_fname);
|
||||
FILE* stream = fopen(img->ramdisk_fname, "r");
|
||||
if (!stream)
|
||||
abort_perror(img->ramdisk_fname);
|
||||
struct stat st;
|
||||
if (fstat(fileno(stream), &st))
|
||||
abort_perror(img->ramdisk_fname);
|
||||
rsize = st.st_size;
|
||||
char* r = malloc(rsize);
|
||||
if (!r)
|
||||
abort_perror("");
|
||||
size_t rb = fread(r, rsize, 1, stream);
|
||||
if ((rb!=1) || ferror(stream))
|
||||
abort_perror(img->ramdisk_fname);
|
||||
else if (feof(stream))
|
||||
abort_printf("%s: cannot read ramdisk\n", img->ramdisk_fname);
|
||||
fclose(stream);
|
||||
img->header.ramdisk_size = rsize;
|
||||
img->ramdisk = r;
|
||||
}
|
||||
else if (img->kernel) {
|
||||
// if kernel is updated, copy the ramdisk from original image
|
||||
char* r = malloc(rsize);
|
||||
if (!r)
|
||||
abort_perror("");
|
||||
if (fseek(img->stream, roffset, SEEK_SET))
|
||||
abort_perror(img->fname);
|
||||
size_t rb = fread(r, rsize, 1, img->stream);
|
||||
if ((rb!=1) || ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
else if (feof(img->stream))
|
||||
abort_printf("%s: cannot read ramdisk\n", img->fname);
|
||||
img->ramdisk = r;
|
||||
}
|
||||
|
||||
if (img->second_fname) {
|
||||
printf("reading second stage from %s\n", img->second_fname);
|
||||
FILE* stream = fopen(img->second_fname, "r");
|
||||
if (!stream)
|
||||
abort_perror(img->second_fname);
|
||||
struct stat st;
|
||||
if (fstat(fileno(stream), &st))
|
||||
abort_perror(img->second_fname);
|
||||
ssize = st.st_size;
|
||||
char* s = malloc(ssize);
|
||||
if (!s)
|
||||
abort_perror("");
|
||||
size_t rb = fread(s, ssize, 1, stream);
|
||||
if ((rb!=1) || ferror(stream))
|
||||
abort_perror(img->second_fname);
|
||||
else if (feof(stream))
|
||||
abort_printf("%s: cannot read second stage\n", img->second_fname);
|
||||
fclose(stream);
|
||||
img->header.second_size = ssize;
|
||||
img->second = s;
|
||||
}
|
||||
else if (img->ramdisk && img->header.second_size) {
|
||||
// if ramdisk is updated, copy the second stage from original image
|
||||
char* s = malloc(ssize);
|
||||
if (!s)
|
||||
abort_perror("");
|
||||
if (fseek(img->stream, soffset, SEEK_SET))
|
||||
abort_perror(img->fname);
|
||||
size_t rb = fread(s, ssize, 1, img->stream);
|
||||
if ((rb!=1) || ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
else if (feof(img->stream))
|
||||
abort_printf("%s: cannot read second stage\n", img->fname);
|
||||
img->second = s;
|
||||
}
|
||||
|
||||
n = (img->header.kernel_size + page_size - 1) / page_size;
|
||||
m = (img->header.ramdisk_size + page_size - 1) / page_size;
|
||||
o = (img->header.second_size + page_size - 1) / page_size;
|
||||
unsigned total_size = (1+n+m+o)*page_size;
|
||||
|
||||
if (!img->size)
|
||||
img->size = total_size;
|
||||
else if (total_size > img->size)
|
||||
abort_printf("%s: updated is too big for the Boot Image (%u vs %u bytes)\n", img->fname, total_size, img->size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void write_bootimg(t_abootimg* img)
|
||||
{
|
||||
unsigned psize;
|
||||
char* padding;
|
||||
|
||||
printf ("Writing Boot Image %s\n", img->fname);
|
||||
|
||||
psize = img->header.page_size;
|
||||
padding = calloc(psize, 1);
|
||||
if (!padding)
|
||||
abort_perror("");
|
||||
|
||||
unsigned n = (img->header.kernel_size + psize - 1) / psize;
|
||||
unsigned m = (img->header.ramdisk_size + psize - 1) / psize;
|
||||
//unsigned o = (img->header.second_size + psize - 1) / psize;
|
||||
|
||||
if (fseek(img->stream, 0, SEEK_SET))
|
||||
abort_perror(img->fname);
|
||||
|
||||
fwrite(&img->header, sizeof(img->header), 1, img->stream);
|
||||
if (ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
|
||||
fwrite(padding, psize - sizeof(img->header), 1, img->stream);
|
||||
if (ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
|
||||
if (img->kernel) {
|
||||
fwrite(img->kernel, img->header.kernel_size, 1, img->stream);
|
||||
if (ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
|
||||
fwrite(padding, psize - (img->header.kernel_size % psize), 1, img->stream);
|
||||
if (ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
}
|
||||
|
||||
if (img->ramdisk) {
|
||||
if (fseek(img->stream, (1+n)*psize, SEEK_SET))
|
||||
abort_perror(img->fname);
|
||||
|
||||
fwrite(img->ramdisk, img->header.ramdisk_size, 1, img->stream);
|
||||
if (ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
|
||||
fwrite(padding, psize - (img->header.ramdisk_size % psize), 1, img->stream);
|
||||
if (ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
}
|
||||
|
||||
if (img->header.second_size) {
|
||||
if (fseek(img->stream, (1+n+m)*psize, SEEK_SET))
|
||||
abort_perror(img->fname);
|
||||
|
||||
fwrite(img->second, img->header.second_size, 1, img->stream);
|
||||
if (ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
|
||||
fwrite(padding, psize - (img->header.second_size % psize), 1, img->stream);
|
||||
if (ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
}
|
||||
|
||||
ftruncate (fileno(img->stream), img->size);
|
||||
|
||||
free(padding);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void print_bootimg_info(t_abootimg* img)
|
||||
{
|
||||
printf ("\nAndroid Boot Image Info:\n\n");
|
||||
|
||||
printf ("* file name = %s %s\n\n", img->fname, img->is_blkdev ? "[block device]":"");
|
||||
|
||||
printf ("* image size = %u bytes (%.2f MB)\n", img->size, (double)img->size/0x100000);
|
||||
printf (" page size = %u bytes\n\n", img->header.page_size);
|
||||
|
||||
printf ("* Boot Name = \"%s\"\n\n", img->header.name);
|
||||
|
||||
unsigned kernel_size = img->header.kernel_size;
|
||||
unsigned ramdisk_size = img->header.ramdisk_size;
|
||||
unsigned second_size = img->header.second_size;
|
||||
|
||||
printf ("* kernel size = %u bytes (%.2f MB)\n", kernel_size, (double)kernel_size/0x100000);
|
||||
printf (" ramdisk size = %u bytes (%.2f MB)\n", ramdisk_size, (double)ramdisk_size/0x100000);
|
||||
if (second_size)
|
||||
printf (" second stage size = %u bytes (%.2f MB)\n", ramdisk_size, (double)ramdisk_size/0x100000);
|
||||
|
||||
printf ("\n* load addresses:\n");
|
||||
printf (" kernel: 0x%08x\n", img->header.kernel_addr);
|
||||
printf (" ramdisk: 0x%08x\n", img->header.ramdisk_addr);
|
||||
if (second_size)
|
||||
printf (" second stage: 0x%08x\n", img->header.second_addr);
|
||||
printf (" tags: 0x%08x\n\n", img->header.tags_addr);
|
||||
|
||||
if (img->header.cmdline[0])
|
||||
printf ("* cmdline = %s\n\n", img->header.cmdline);
|
||||
else
|
||||
printf ("* empty cmdline\n");
|
||||
|
||||
printf ("* id = ");
|
||||
int i;
|
||||
for (i=0; i<8; i++)
|
||||
printf ("0x%08x ", img->header.id[i]);
|
||||
printf ("\n\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void write_bootimg_config(t_abootimg* img)
|
||||
{
|
||||
printf ("writing boot image config in %s\n", img->config_fname);
|
||||
|
||||
FILE* config_file = fopen(img->config_fname, "w");
|
||||
if (!config_file)
|
||||
abort_perror(img->config_fname);
|
||||
|
||||
fprintf(config_file, "bootsize = 0x%x\n", img->size);
|
||||
fprintf(config_file, "pagesize = 0x%x\n", img->header.page_size);
|
||||
|
||||
fprintf(config_file, "kerneladdr = 0x%x\n", img->header.kernel_addr);
|
||||
fprintf(config_file, "ramdiskaddr = 0x%x\n", img->header.ramdisk_addr);
|
||||
fprintf(config_file, "secondaddr = 0x%x\n", img->header.second_addr);
|
||||
fprintf(config_file, "tagsaddr = 0x%x\n", img->header.tags_addr);
|
||||
|
||||
fprintf(config_file, "name = %s\n", img->header.name);
|
||||
fprintf(config_file, "cmdline = %s\n", img->header.cmdline);
|
||||
|
||||
fclose(config_file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void extract_kernel(t_abootimg* img)
|
||||
{
|
||||
unsigned psize = img->header.page_size;
|
||||
unsigned ksize = img->header.kernel_size;
|
||||
|
||||
printf ("extracting kernel in %s\n", img->kernel_fname);
|
||||
|
||||
void* k = malloc(ksize);
|
||||
if (!k)
|
||||
abort_perror(NULL);
|
||||
|
||||
if (fseek(img->stream, psize, SEEK_SET))
|
||||
abort_perror(img->fname);
|
||||
|
||||
size_t rb = fread(k, ksize, 1, img->stream);
|
||||
if ((rb!=1) || ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
|
||||
FILE* kernel_file = fopen(img->kernel_fname, "w");
|
||||
if (!kernel_file)
|
||||
abort_perror(img->kernel_fname);
|
||||
|
||||
fwrite(k, ksize, 1, kernel_file);
|
||||
if (ferror(kernel_file))
|
||||
abort_perror(img->kernel_fname);
|
||||
|
||||
fclose(kernel_file);
|
||||
free(k);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void extract_ramdisk(t_abootimg* img)
|
||||
{
|
||||
unsigned psize = img->header.page_size;
|
||||
unsigned ksize = img->header.kernel_size;
|
||||
unsigned rsize = img->header.ramdisk_size;
|
||||
|
||||
unsigned n = (ksize + psize - 1) / psize;
|
||||
unsigned roffset = (1+n)*psize;
|
||||
|
||||
printf ("extracting ramdisk in %s\n", img->ramdisk_fname);
|
||||
|
||||
void* r = malloc(rsize);
|
||||
if (!r)
|
||||
abort_perror(NULL);
|
||||
|
||||
if (fseek(img->stream, roffset, SEEK_SET))
|
||||
abort_perror(img->fname);
|
||||
|
||||
size_t rb = fread(r, rsize, 1, img->stream);
|
||||
if ((rb!=1) || ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
|
||||
FILE* ramdisk_file = fopen(img->ramdisk_fname, "w");
|
||||
if (!ramdisk_file)
|
||||
abort_perror(img->ramdisk_fname);
|
||||
|
||||
fwrite(r, rsize, 1, ramdisk_file);
|
||||
if (ferror(ramdisk_file))
|
||||
abort_perror(img->ramdisk_fname);
|
||||
|
||||
fclose(ramdisk_file);
|
||||
free(r);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void extract_second(t_abootimg* img)
|
||||
{
|
||||
unsigned psize = img->header.page_size;
|
||||
unsigned ksize = img->header.kernel_size;
|
||||
unsigned rsize = img->header.ramdisk_size;
|
||||
unsigned ssize = img->header.second_size;
|
||||
|
||||
if (!ssize) // Second Stage not present
|
||||
return;
|
||||
|
||||
unsigned n = (rsize + ksize + psize - 1) / psize;
|
||||
unsigned soffset = (1+n)*psize;
|
||||
|
||||
printf ("extracting second stage image in %s\n", img->second_fname);
|
||||
|
||||
void* s = malloc(ksize);
|
||||
if (!s)
|
||||
abort_perror(NULL);
|
||||
|
||||
if (fseek(img->stream, soffset, SEEK_SET))
|
||||
abort_perror(img->fname);
|
||||
|
||||
size_t rb = fread(s, ssize, 1, img->stream);
|
||||
if ((rb!=1) || ferror(img->stream))
|
||||
abort_perror(img->fname);
|
||||
|
||||
FILE* second_file = fopen(img->second_fname, "w");
|
||||
if (!second_file)
|
||||
abort_perror(img->second_fname);
|
||||
|
||||
fwrite(s, ssize, 1, second_file);
|
||||
if (ferror(second_file))
|
||||
abort_perror(img->second_fname);
|
||||
|
||||
fclose(second_file);
|
||||
free(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
t_abootimg* new_bootimg()
|
||||
{
|
||||
t_abootimg* img;
|
||||
|
||||
img = calloc(sizeof(t_abootimg), 1);
|
||||
if (!img)
|
||||
abort_perror(NULL);
|
||||
|
||||
img->config_fname = "bootimg.cfg";
|
||||
img->kernel_fname = "zImage";
|
||||
img->ramdisk_fname = "initrd.img";
|
||||
img->second_fname = "stage2.img";
|
||||
|
||||
memcpy(img->header.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
|
||||
img->header.page_size = 2048; // a sensible default page size
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
t_abootimg* bootimg = new_bootimg();
|
||||
|
||||
switch(parse_args(argc, argv, bootimg))
|
||||
{
|
||||
case none:
|
||||
printf("error - bad arguments\n\n");
|
||||
print_usage();
|
||||
break;
|
||||
|
||||
case help:
|
||||
print_usage();
|
||||
break;
|
||||
|
||||
case info:
|
||||
open_bootimg(bootimg, "r");
|
||||
read_header(bootimg);
|
||||
print_bootimg_info(bootimg);
|
||||
break;
|
||||
|
||||
case extract:
|
||||
open_bootimg(bootimg, "r");
|
||||
read_header(bootimg);
|
||||
write_bootimg_config(bootimg);
|
||||
extract_kernel(bootimg);
|
||||
extract_ramdisk(bootimg);
|
||||
extract_second(bootimg);
|
||||
break;
|
||||
|
||||
case update:
|
||||
open_bootimg(bootimg, "r+");
|
||||
read_header(bootimg);
|
||||
update_header(bootimg);
|
||||
update_images(bootimg);
|
||||
write_bootimg(bootimg);
|
||||
break;
|
||||
|
||||
case create:
|
||||
if (!bootimg->kernel_fname || !bootimg->ramdisk_fname) {
|
||||
print_usage();
|
||||
break;
|
||||
}
|
||||
check_if_block_device(bootimg);
|
||||
open_bootimg(bootimg, "w");
|
||||
update_header(bootimg);
|
||||
update_images(bootimg);
|
||||
if (check_boot_img_header(bootimg))
|
||||
abort_printf("%s: Sanity cheks failed", bootimg->fname);
|
||||
write_bootimg(bootimg);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,97 +0,0 @@
|
||||
/* tools/mkbootimg/bootimg.h
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _BOOT_IMAGE_H_
|
||||
#define _BOOT_IMAGE_H_
|
||||
|
||||
typedef struct boot_img_hdr boot_img_hdr;
|
||||
|
||||
#define BOOT_MAGIC "ANDROID!"
|
||||
#define BOOT_MAGIC_SIZE 8
|
||||
#define BOOT_NAME_SIZE 16
|
||||
#define BOOT_ARGS_SIZE 512
|
||||
|
||||
struct boot_img_hdr
|
||||
{
|
||||
unsigned char magic[BOOT_MAGIC_SIZE];
|
||||
|
||||
unsigned kernel_size; /* size in bytes */
|
||||
unsigned kernel_addr; /* physical load addr */
|
||||
|
||||
unsigned ramdisk_size; /* size in bytes */
|
||||
unsigned ramdisk_addr; /* physical load addr */
|
||||
|
||||
unsigned second_size; /* size in bytes */
|
||||
unsigned second_addr; /* physical load addr */
|
||||
|
||||
unsigned tags_addr; /* physical addr for kernel tags */
|
||||
unsigned page_size; /* flash page size we assume */
|
||||
unsigned unused[2]; /* future expansion: should be 0 */
|
||||
|
||||
unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
|
||||
|
||||
unsigned char cmdline[BOOT_ARGS_SIZE];
|
||||
|
||||
unsigned id[8]; /* timestamp / checksum / sha1 / etc */
|
||||
};
|
||||
|
||||
/*
|
||||
** +-----------------+
|
||||
** | boot header | 1 page
|
||||
** +-----------------+
|
||||
** | kernel | n pages
|
||||
** +-----------------+
|
||||
** | ramdisk | m pages
|
||||
** +-----------------+
|
||||
** | second stage | o pages
|
||||
** +-----------------+
|
||||
**
|
||||
** n = (kernel_size + page_size - 1) / page_size
|
||||
** m = (ramdisk_size + page_size - 1) / page_size
|
||||
** o = (second_size + page_size - 1) / page_size
|
||||
**
|
||||
** 0. all entities are page_size aligned in flash
|
||||
** 1. kernel and ramdisk are required (size != 0)
|
||||
** 2. second is optional (second_size == 0 -> no second)
|
||||
** 3. load each element (kernel, ramdisk, second) at
|
||||
** the specified physical address (kernel_addr, etc)
|
||||
** 4. prepare tags at tag_addr. kernel_args[] is
|
||||
** appended to the kernel commandline in the tags.
|
||||
** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
|
||||
** 6. if second_size != 0: jump to second_addr
|
||||
** else: jump to kernel_addr
|
||||
*/
|
||||
|
||||
#if 0
|
||||
typedef struct ptentry ptentry;
|
||||
|
||||
struct ptentry {
|
||||
char name[16]; /* asciiz partition name */
|
||||
unsigned start; /* starting block number */
|
||||
unsigned length; /* length in blocks */
|
||||
unsigned flags; /* set to zero */
|
||||
};
|
||||
|
||||
/* MSM Partition Table ATAG
|
||||
**
|
||||
** length: 2 + 7 * n
|
||||
** atag: 0x4d534d70
|
||||
** <ptentry> x n
|
||||
*/
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,2 +0,0 @@
|
||||
//https://gitorious.org/ac100/abootimg
|
||||
#define VERSION_STR "gitorious-7e127fee6a3981f6b0a50ce9910267cd501e09d4"
|
Loading…
Reference in New Issue