diff --git a/README.md b/README.md
index 93047d7..abae94b 100644
--- a/README.md
+++ b/README.md
@@ -123,6 +123,24 @@ Your boot.img.signed and vbmeta.img.signd will be updated together, then you can
+
+ working with vendor_boot.img + vbmeta.img (Pixel 5 etc.)
+Most devices include hash descriptor of vendor_boot.img in vbmeta.img, so if you need to modify vendor_boot.img, you need to update vbmeta.img together.
+
+```bash
+rm *.img
+cp vendor_boot.img
+cp vbmeta.img
+./gradlew unpack
+./gradlew pack
+./gradlew flash
+```
+
+Please note that to use 'gradle flash', your host machine must be connectted to your DUT with adb, and you already 'adb root'.
+
+
+
+
How to disable AVB verification
@@ -158,6 +176,8 @@ boot\_signer
https://android.googlesource.com/platform/system/extras
mkbootimg
https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/
+boot header definition
+https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h
kernel info extractor
https://android.googlesource.com/platform/build/+/refs/heads/master/tools/extract_kernel.py
mkdtboimg
diff --git a/aosp/avb/avbtool.v1.2.py b/aosp/avb/avbtool.v1.2.py
index 1211df3..8647b29 100755
--- a/aosp/avb/avbtool.v1.2.py
+++ b/aosp/avb/avbtool.v1.2.py
@@ -642,6 +642,15 @@ def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
return True
+def create_avb_hashtree_hasher(algorithm, salt):
+ """Create the hasher for AVB hashtree based on the input algorithm."""
+
+ if algorithm.lower() == 'blake2b-256':
+ return hashlib.new('blake2b', salt, digest_size=32)
+
+ return hashlib.new(algorithm, salt)
+
+
class ImageChunk(object):
"""Data structure used for representing chunks in Android sparse files.
@@ -1406,7 +1415,8 @@ class AvbHashtreeDescriptor(AvbDescriptor):
self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
o += salt_len
self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
- if root_digest_len != len(hashlib.new(self.hash_algorithm).digest()):
+
+ if root_digest_len != self._hashtree_digest_size():
if root_digest_len != 0:
raise LookupError('root_digest_len doesn\'t match hash algorithm')
@@ -1426,6 +1436,9 @@ class AvbHashtreeDescriptor(AvbDescriptor):
self.root_digest = b''
self.flags = 0
+ def _hashtree_digest_size(self):
+ return len(create_avb_hashtree_hasher(self.hash_algorithm, b'').digest())
+
def print_desc(self, o):
"""Print the descriptor.
@@ -1496,7 +1509,7 @@ class AvbHashtreeDescriptor(AvbDescriptor):
image_filename = os.path.join(image_dir, self.partition_name + image_ext)
image = ImageHandler(image_filename, read_only=True)
# Generate the hashtree and checks that it matches what's in the file.
- digest_size = len(hashlib.new(self.hash_algorithm).digest())
+ digest_size = self._hashtree_digest_size()
digest_padding = round_to_pow2(digest_size) - digest_size
(hash_level_offsets, tree_size) = calc_hash_level_offsets(
self.image_size, self.data_block_size, digest_size + digest_padding)
@@ -3579,7 +3592,8 @@ class Avb(object):
print('1.{}'.format(required_libavb_version_minor))
return
- digest_size = len(hashlib.new(hash_algorithm).digest())
+ digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'')
+ .digest())
digest_padding = round_to_pow2(digest_size) - digest_size
# If |partition_size| is given (e.g. not 0), calculate the maximum image
@@ -4064,7 +4078,7 @@ def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
level_output_list = []
remaining = hash_src_size
while remaining > 0:
- hasher = hashlib.new(hash_alg_name, salt)
+ hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
# Only read from the file for the first level - for subsequent
# levels, access the array we're building.
if level_num == 0:
@@ -4096,7 +4110,7 @@ def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
hash_src_size = len(level_output)
level_num += 1
- hasher = hashlib.new(hash_alg_name, salt)
+ hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
hasher.update(level_output)
return hasher.digest(), bytes(hash_ret)
diff --git a/bbootimg/src/main/kotlin/bootimg/Common.kt b/bbootimg/src/main/kotlin/bootimg/Common.kt
index 831725d..8a158aa 100644
--- a/bbootimg/src/main/kotlin/bootimg/Common.kt
+++ b/bbootimg/src/main/kotlin/bootimg/Common.kt
@@ -23,7 +23,6 @@ import java.nio.ByteOrder
import java.security.MessageDigest
import java.util.regex.Pattern
-
@OptIn(ExperimentalUnsignedTypes::class)
class Common {
data class VeritySignature(
@@ -34,13 +33,6 @@ class Common {
var jarPath: String = ""
)
- data class Slice(
- var srcFile: String,
- var offset: Int,
- var length: Int,
- var dumpFile: String
- )
-
companion object {
private val log = LoggerFactory.getLogger(Common::class.java)
private const val MAX_ANDROID_VER = 11
@@ -111,12 +103,12 @@ class Common {
return listOf()
}
- fun dumpKernel(s: Slice) {
+ fun dumpKernel(s: Helper.Slice) {
Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
parseKernelInfo(s.dumpFile)
}
- fun dumpRamdisk(s: Slice, root: String): String {
+ fun dumpRamdisk(s: Helper.Slice, root: String): String {
var ret = "gz"
Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
when {
@@ -162,7 +154,7 @@ class Common {
return ret
}
- fun dumpDtb(s: Slice) {
+ fun dumpDtb(s: Helper.Slice) {
Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
//extract DTB
if (EnvironmentVerifier().hasDtc) {
diff --git a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt
index e253e4a..da089ee 100644
--- a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt
+++ b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt
@@ -4,7 +4,6 @@ import cfig.Avb
import cfig.EnvironmentVerifier
import cfig.bootimg.Common
import cfig.bootimg.Common.Companion.deleleIfExists
-import cfig.bootimg.Common.Slice
import cfig.bootimg.Signer
import cfig.helper.Helper
import cfig.packable.VBMetaParser
@@ -169,12 +168,11 @@ data class BootV2(
//info
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this)
//kernel
- Common.dumpKernel(Slice(info.output, kernel.position.toInt(), kernel.size, kernel.file!!))
+ Common.dumpKernel(Helper.Slice(info.output, kernel.position.toInt(), kernel.size, kernel.file!!))
//ramdisk
if (this.ramdisk.size > 0) {
val fmt = Common.dumpRamdisk(
- Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!),
- "${workDir}root"
+ Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), "${workDir}root"
)
this.ramdisk.file = this.ramdisk.file!! + ".$fmt"
//dump info again
@@ -200,7 +198,7 @@ data class BootV2(
}
//dtb
this.dtb?.let { _ ->
- Common.dumpDtb(Slice(info.output, dtb!!.position.toInt(), dtb!!.size, dtb!!.file!!))
+ Common.dumpDtb(Helper.Slice(info.output, dtb!!.position.toInt(), dtb!!.size, dtb!!.file!!))
}
return this
diff --git a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt
index ffab8f8..b9cfa93 100644
--- a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt
+++ b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt
@@ -20,9 +20,11 @@ import java.nio.ByteOrder
import cfig.bootimg.Common as C
@OptIn(ExperimentalUnsignedTypes::class)
-data class BootV3(var info: MiscInfo = MiscInfo(),
- var kernel: CommArgs = CommArgs(),
- val ramdisk: CommArgs = CommArgs()
+data class BootV3(
+ var info: MiscInfo = MiscInfo(),
+ var kernel: CommArgs = CommArgs(),
+ val ramdisk: CommArgs = CommArgs(),
+ var bootSignature: CommArgs = CommArgs()
) {
companion object {
private val log = LoggerFactory.getLogger(BootV3::class.java)
@@ -51,6 +53,13 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
ret.ramdisk.size = header.ramdiskSize
ret.ramdisk.position = ret.kernel.position + header.kernelSize +
getPaddingSize(header.kernelSize, BootHeaderV3.pageSize)
+ //boot signature
+ if (header.signatureSize > 0) {
+ ret.bootSignature.file = workDir + "bootsig"
+ ret.bootSignature.size = header.signatureSize
+ ret.bootSignature.position = ret.ramdisk.position + ret.ramdisk.size +
+ getPaddingSize(header.ramdiskSize, BootHeaderV3.pageSize)
+ }
}
ret.info.imageSize = File(fileName).length()
return ret
@@ -58,22 +67,23 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
}
data class MiscInfo(
- var output: String = "",
- var json: String = "",
- var headerVersion: Int = 0,
- var headerSize: Int = 0,
- var pageSize: Int = 0,
- var cmdline: String = "",
- var osVersion: String = "",
- var osPatchLevel: String = "",
- var imageSize: Long = 0,
- var signatureSize: Int = 0
+ var output: String = "",
+ var json: String = "",
+ var headerVersion: Int = 0,
+ var headerSize: Int = 0,
+ var pageSize: Int = 0,
+ var cmdline: String = "",
+ var osVersion: String = "",
+ var osPatchLevel: String = "",
+ var imageSize: Long = 0,
+ var signatureSize: Int = 0
)
data class CommArgs(
- var file: String = "",
- var position: Int = 0,
- var size: Int = 0)
+ var file: String = "",
+ var position: Int = 0,
+ var size: Int = 0
+ )
fun pack(): BootV3 {
if (File(this.ramdisk.file).exists() && !File(workDir + "root").exists()) {
@@ -94,9 +104,9 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
FileOutputStream(this.info.output + ".clear", false).use { fos ->
val encodedHeader = this.toHeader().encode()
fos.write(encodedHeader)
- fos.write(ByteArray(
- Helper.round_to_multiple(encodedHeader.size,
- this.info.pageSize) - encodedHeader.size))
+ fos.write(
+ ByteArray(Helper.round_to_multiple(encodedHeader.size, this.info.pageSize) - encodedHeader.size)
+ )
}
//data
@@ -128,13 +138,14 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
private fun toHeader(): BootHeaderV3 {
return BootHeaderV3(
- kernelSize = kernel.size,
- ramdiskSize = ramdisk.size,
- headerVersion = info.headerVersion,
- osVersion = info.osVersion,
- osPatchLevel = info.osPatchLevel,
- headerSize = info.headerSize,
- cmdline = info.cmdline)
+ kernelSize = kernel.size,
+ ramdiskSize = ramdisk.size,
+ headerVersion = info.headerVersion,
+ osVersion = info.osVersion,
+ osPatchLevel = info.osPatchLevel,
+ headerSize = info.headerSize,
+ cmdline = info.cmdline
+ )
}
fun extractImages(): BootV3 {
@@ -142,11 +153,21 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
//info
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
//kernel
- C.dumpKernel(C.Slice(info.output, kernel.position, kernel.size, kernel.file))
+ C.dumpKernel(Helper.Slice(info.output, kernel.position, kernel.size, kernel.file))
//ramdisk
val fmt = C.dumpRamdisk(
- C.Slice(info.output, ramdisk.position, ramdisk.size, ramdisk.file), "${workDir}root")
+ Helper.Slice(info.output, ramdisk.position, ramdisk.size, ramdisk.file), "${workDir}root"
+ )
this.ramdisk.file = this.ramdisk.file + ".$fmt"
+ //bootsig
+ if (info.signatureSize > 0) {
+ Helper.extractFile(
+ info.output, this.bootSignature.file,
+ this.bootSignature.position.toLong(), this.bootSignature.size
+ )
+ Avb().parseVbMeta(this.bootSignature.file)
+ }
+
//dump info again
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
return this
@@ -187,6 +208,13 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
it.addRow("ramdisk", this.ramdisk.file)
it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root")
it.addRule()
+
+ if (this.info.signatureSize > 0) {
+ it.addRow("boot signature", this.bootSignature.file)
+ it.addRow("\\-- decoded boot signature", Avb.getJsonFileName(this.bootSignature.file))
+ it.addRule()
+ }
+
it.addRow("AVB info", Avb.getJsonFileName(info.output))
it.addRule()
it
@@ -201,8 +229,10 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
""
}
}
- log.info("\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}",
- tableHeader.render(), tab.render(), tabVBMeta)
+ log.info(
+ "\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}",
+ tableHeader.render(), tab.render(), tabVBMeta
+ )
return this
}
diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt
index 4ad4b83..b8d9e9c 100644
--- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt
+++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt
@@ -17,30 +17,100 @@ import java.nio.ByteBuffer
import java.nio.ByteOrder
import cfig.bootimg.Common as C
import cfig.EnvironmentVerifier
+import cfig.io.Struct3
+import java.io.InputStream
@OptIn(ExperimentalUnsignedTypes::class)
-data class VendorBoot(var info: MiscInfo = MiscInfo(),
- var ramdisk: CommArgs = CommArgs(),
- var dtb: CommArgs = CommArgs()) {
+data class VendorBoot(
+ var info: MiscInfo = MiscInfo(),
+ var ramdisk: CommArgs = CommArgs(),
+ var dtb: CommArgs = CommArgs(),
+ var ramdisk_table: Vrt = Vrt(),
+ var bootconfig: CommArgs = CommArgs()
+) {
data class CommArgs(
- var file: String = "",
- var position: Long = 0,
- var size: Int = 0,
- var loadAddr: Long = 0)
+ var file: String = "",
+ var position: Long = 0,
+ var size: Int = 0,
+ var loadAddr: Long = 0
+ )
data class MiscInfo(
- var output: String = "",
- var json: String = "",
- var headerVersion: Int = 0,
- var product: String = "",
- var headerSize: Int = 0,
- var pageSize: Int = 0,
- var cmdline: String = "",
- var tagsLoadAddr: Long = 0,
- var kernelLoadAddr: Long = 0,
- var imageSize: Long = 0
+ var output: String = "",
+ var json: String = "",
+ var headerVersion: Int = 0,
+ var product: String = "",
+ var headerSize: Int = 0,
+ var pageSize: Int = 0,
+ var cmdline: String = "",
+ var tagsLoadAddr: Long = 0,
+ var kernelLoadAddr: Long = 0,
+ var imageSize: Long = 0
+ )
+
+ enum class VrtType {
+ NONE,
+ PLATFORM,
+ RECOVERY,
+ DLKM;
+
+ companion object {
+ fun fromInt(value: Int): VrtType {
+ return when (value) {
+ NONE.ordinal -> NONE
+ PLATFORM.ordinal -> PLATFORM
+ RECOVERY.ordinal -> RECOVERY
+ DLKM.ordinal -> DLKM
+ else -> throw IllegalArgumentException()
+ }
+ }
+ }
+ }
+
+ class Vrt(
+ var size: Int = 0,
+ var position: Long = 0,
+ var ramdidks: MutableList = mutableListOf()
)
+ class VrtEntry(
+ var size: Int = 0,
+ var offset: Int = 0,
+ var type: VrtType = VrtType.NONE,
+ var name: String = "", //32s
+ var boardId: Array = arrayOf(), //16I
+ var file: String = ""
+ ) {
+ companion object {
+ private val log = LoggerFactory.getLogger(VrtEntry::class.java)
+ const val VENDOR_RAMDISK_NAME_SIZE = 32
+ const val VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
+ const val FORMAT_STRING = "3I${VENDOR_RAMDISK_NAME_SIZE}s${VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I"
+
+ init {
+ log.info(Struct3(FORMAT_STRING).calcSize().toString())
+ }
+ }
+
+ constructor(iS: InputStream?, dumpFile: String) : this() {
+ if (iS == null) {
+ return
+ }
+ val info = Struct3(FORMAT_STRING).unpack(iS)
+ assert((3 + 1 + VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE) == info.size)
+ this.size = (info[0] as UInt).toInt()
+ this.offset = (info[1] as UInt).toInt()
+ this.type = VrtType.fromInt((info[2] as UInt).toInt())
+ this.name = info[3] as String
+ this.file = dumpFile
+ }
+
+ override fun toString(): String {
+ return "VrtEntry(ramdiskSize=$size, ramdiskOffset=$offset, ramdiskType=$type, ramdiskName='$name', boardId=${boardId.contentToString()})"
+ }
+
+ }
+
companion object {
private val log = LoggerFactory.getLogger(VendorBoot::class.java)
fun parse(fileName: String): VendorBoot {
@@ -59,27 +129,44 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
ret.info.headerVersion = header.headerVersion
//ramdisk
ret.ramdisk.file = workDir + "ramdisk.img"
- ret.ramdisk.size = header.vndRamdiskSize
+ ret.ramdisk.size = header.vndRamdiskTotalSize
ret.ramdisk.loadAddr = header.ramdiskLoadAddr
ret.ramdisk.position = Helper.round_to_multiple(
- VendorBootHeader.VENDOR_BOOT_IMAGE_HEADER_V3_SIZE.toLong(),
- header.pageSize)
+ VendorBootHeader.VENDOR_BOOT_IMAGE_HEADER_V3_SIZE.toLong(), header.pageSize
+ )
//dtb
ret.dtb.file = workDir + "dtb"
ret.dtb.size = header.dtbSize
ret.dtb.loadAddr = header.dtbLoadAddr
- ret.dtb.position = ret.ramdisk.position +
- Helper.round_to_multiple(ret.ramdisk.size, header.pageSize)
+ ret.dtb.position = ret.ramdisk.position + Helper.round_to_multiple(ret.ramdisk.size, header.pageSize)
+ //vrt
+ if (header.vrtSize > 0) {
+ ret.ramdisk_table.size = header.vrtSize
+ ret.ramdisk_table.position =
+ ret.dtb.position + Helper.round_to_multiple(ret.ramdisk_table.size, header.pageSize)
+ FileInputStream(ret.info.output).use {
+ it.skip(ret.ramdisk_table.position)
+ for (item in 0 until header.vrtEntryNum) {
+ ret.ramdisk_table.ramdidks.add(VrtEntry(it, workDir + "ramdisk.${item + 1}"))
+ }
+ }
+ ret.ramdisk_table.ramdidks.forEach {
+ log.warn(it.toString())
+ }
+ }
+ //bootconfig
+ if (header.bootconfigSize > 0) {
+ ret.bootconfig.file = workDir + "bootconfig"
+ ret.bootconfig.size = header.bootconfigSize
+ ret.bootconfig.position =
+ ret.ramdisk_table.position + Helper.round_to_multiple(ret.bootconfig.size, header.pageSize)
+ }
}
ret.info.imageSize = File(fileName).length()
return ret
}
}
- private fun parseOsMajor(): Int {
- return 11
- }
-
fun pack(): VendorBoot {
val workDir = Helper.prop("workDir")
if (File(workDir + this.ramdisk.file).exists() && !File(workDir + "root").exists()) {
@@ -88,7 +175,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
} else {
File(this.ramdisk.file).deleleIfExists()
File(this.ramdisk.file.removeSuffix(".gz")).deleleIfExists()
- //TODO: remove cpio in C/C++
+ //Fixed: remove cpio in C/C++
//C.packRootfs("$workDir/root", this.ramdisk.file, parseOsMajor())
//enable advance JAVA cpio
C.packRootfs("$workDir/root", this.ramdisk.file)
@@ -99,9 +186,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
FileOutputStream(this.info.output + ".clear", false).use { fos ->
val encodedHeader = this.toHeader().encode()
fos.write(encodedHeader)
- fos.write(ByteArray(
- Helper.round_to_multiple(encodedHeader.size,
- this.info.pageSize) - encodedHeader.size))
+ fos.write(ByteArray(Helper.round_to_multiple(encodedHeader.size, this.info.pageSize) - encodedHeader.size))
}
//data
log.info("Writing data ...")
@@ -110,7 +195,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
it.order(ByteOrder.LITTLE_ENDIAN)
C.writePaddedFile(it, this.ramdisk.file, this.info.pageSize)
C.writePaddedFile(it, this.dtb.file, this.info.pageSize)
- it
+ it
}
//write
FileOutputStream("${this.info.output}.clear", true).use { fos ->
@@ -135,17 +220,17 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
private fun toHeader(): VendorBootHeader {
return VendorBootHeader(
- headerVersion = info.headerVersion,
- pageSize = info.pageSize,
- kernelLoadAddr = info.kernelLoadAddr,
- ramdiskLoadAddr = ramdisk.loadAddr,
- vndRamdiskSize = ramdisk.size,
- cmdline = info.cmdline,
- tagsLoadAddr = info.tagsLoadAddr,
- product = info.product,
- headerSize = info.headerSize,
- dtbSize = dtb.size,
- dtbLoadAddr = dtb.loadAddr
+ headerVersion = info.headerVersion,
+ pageSize = info.pageSize,
+ kernelLoadAddr = info.kernelLoadAddr,
+ ramdiskLoadAddr = ramdisk.loadAddr,
+ vndRamdiskTotalSize = ramdisk.size,
+ cmdline = info.cmdline,
+ tagsLoadAddr = info.tagsLoadAddr,
+ product = info.product,
+ headerSize = info.headerSize,
+ dtbSize = dtb.size,
+ dtbLoadAddr = dtb.loadAddr
)
}
@@ -154,13 +239,27 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
//header
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
//ramdisk
- val fmt = C.dumpRamdisk(C.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file),
- "${workDir}root")
+ //@formatter:off
+ val fmt = C.dumpRamdisk(
+ Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file), "${workDir}root")
+ //@formatter:on
this.ramdisk.file = this.ramdisk.file + ".$fmt"
//dump info again
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
//dtb
- C.dumpDtb(C.Slice(info.output, dtb.position.toInt(), dtb.size, dtb.file))
+ C.dumpDtb(Helper.Slice(info.output, dtb.position.toInt(), dtb.size, dtb.file))
+ //vrt
+ this.ramdisk_table.ramdidks.forEachIndexed { index, it ->
+ log.info("dumping vendor ramdisk ${index + 1}/${this.ramdisk_table.ramdidks.size} ...")
+ val s = Helper.Slice(ramdisk.file, it.offset, it.size, it.file)
+ C.dumpRamdisk(s, workDir + "root.${index + 1}")
+ }
+ //bootconfig
+ if (bootconfig.size > 0) {
+ Helper.Slice(info.output, bootconfig.position.toInt(), bootconfig.size, bootconfig.file).let { s ->
+ Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
+ }
+ }
return this
}
@@ -189,12 +288,23 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
it.addRow("image info", workDir + info.output.removeSuffix(".img") + ".json")
it.addRule()
it.addRow("ramdisk", this.ramdisk.file)
- it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root")
+ if (this.ramdisk_table.size > 0) {
+ this.ramdisk_table.ramdidks.forEachIndexed { index, entry ->
+ it.addRow("-- ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file)
+ it.addRow("------- extracted rootfs", "${workDir}root.${index + 1}")
+ }
+ } else {
+ it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root")
+ }
it.addRule()
it.addRow("dtb", this.dtb.file)
if (File(this.dtb.file + ".src").exists()) {
it.addRow("\\-- decompiled dts", dtb.file + ".src")
}
+ if (this.bootconfig.size > 0) {
+ it.addRule()
+ it.addRow("bootconfig", this.bootconfig.file)
+ }
it.addRule()
it.addRow("AVB info", Avb.getJsonFileName(info.output))
it.addRule()
@@ -210,25 +320,24 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
""
}
}
- log.info("\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}",
- tableHeader.render(), tab.render(), tabVBMeta)
+ log.info("\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}", tableHeader.render(), tab.render(), tabVBMeta)
return this
}
private fun toCommandLine(): CommandLine {
val cmdPrefix = if (EnvironmentVerifier().isWindows) "python " else ""
return CommandLine.parse(cmdPrefix + Helper.prop("mkbootimg")).apply {
- addArgument("--vendor_ramdisk").addArgument(ramdisk.file)
- addArgument("--dtb").addArgument(dtb.file)
- addArgument("--vendor_cmdline").addArgument(info.cmdline, false)
- addArgument("--header_version").addArgument(info.headerVersion.toString())
- addArgument("--base").addArgument("0")
- addArgument("--tags_offset").addArgument(info.tagsLoadAddr.toString())
- addArgument("--kernel_offset").addArgument(info.kernelLoadAddr.toString())
- addArgument("--ramdisk_offset").addArgument(ramdisk.loadAddr.toString())
- addArgument("--dtb_offset").addArgument(dtb.loadAddr.toString())
- addArgument("--pagesize").addArgument(info.pageSize.toString())
- addArgument("--vendor_boot")
+ addArgument("--vendor_ramdisk").addArgument(ramdisk.file)
+ addArgument("--dtb").addArgument(dtb.file)
+ addArgument("--vendor_cmdline").addArgument(info.cmdline, false)
+ addArgument("--header_version").addArgument(info.headerVersion.toString())
+ addArgument("--base").addArgument("0")
+ addArgument("--tags_offset").addArgument(info.tagsLoadAddr.toString())
+ addArgument("--kernel_offset").addArgument(info.kernelLoadAddr.toString())
+ addArgument("--ramdisk_offset").addArgument(ramdisk.loadAddr.toString())
+ addArgument("--dtb_offset").addArgument(dtb.loadAddr.toString())
+ addArgument("--pagesize").addArgument(info.pageSize.toString())
+ addArgument("--vendor_boot")
}
}
}
diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBootHeader.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBootHeader.kt
index 0b06fa3..90dae45 100644
--- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBootHeader.kt
+++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBootHeader.kt
@@ -10,7 +10,7 @@ class VendorBootHeader(
var pageSize: Int = 0,
var kernelLoadAddr: Long = 0,
var ramdiskLoadAddr: Long = 0,
- var vndRamdiskSize: Int = 0,
+ var vndRamdiskTotalSize: Int = 0,
var cmdline: String = "",
var tagsLoadAddr: Long = 0,
var product: String = "",
@@ -37,7 +37,7 @@ class VendorBootHeader(
this.pageSize = (info[2] as UInt).toInt()
this.kernelLoadAddr = (info[3] as UInt).toLong()
this.ramdiskLoadAddr = (info[4] as UInt).toLong()
- this.vndRamdiskSize = (info[5] as UInt).toInt()
+ this.vndRamdiskTotalSize = (info[5] as UInt).toInt()
this.cmdline = info[6] as String
this.tagsLoadAddr = (info[7] as UInt).toLong()
this.product = info[8] as String
@@ -64,7 +64,7 @@ class VendorBootHeader(
pageSize,
kernelLoadAddr,
ramdiskLoadAddr,
- vndRamdiskSize,
+ vndRamdiskTotalSize,
cmdline,
tagsLoadAddr,
product,
@@ -106,7 +106,7 @@ class VendorBootHeader(
}
override fun toString(): String {
- return "VendorBootHeader(headerVersion=$headerVersion, pageSize=$pageSize, kernelLoadAddr=$kernelLoadAddr, ramdiskLoadAddr=$ramdiskLoadAddr, vndRamdiskSize=$vndRamdiskSize, cmdline='$cmdline', tagsLoadAddr=$tagsLoadAddr, product='$product', headerSize=$headerSize, dtbSize=$dtbSize, dtbLoadAddr=$dtbLoadAddr, vrtSize=$vrtSize, vrtEntryNum=$vrtEntryNum, vrtEntrySize=$vrtEntrySize, bootconfigSize=$bootconfigSize)"
+ return "VendorBootHeader(headerVersion=$headerVersion, pageSize=$pageSize, kernelLoadAddr=$kernelLoadAddr, ramdiskLoadAddr=$ramdiskLoadAddr, vndRamdiskSize=$vndRamdiskTotalSize, cmdline='$cmdline', tagsLoadAddr=$tagsLoadAddr, product='$product', headerSize=$headerSize, dtbSize=$dtbSize, dtbLoadAddr=$dtbLoadAddr, vrtSize=$vrtSize, vrtEntryNum=$vrtEntryNum, vrtEntrySize=$vrtEntrySize, bootconfigSize=$bootconfigSize)"
}
diff --git a/doc/additional_tricks.md b/doc/additional_tricks.md
index 76d42ed..2fac77a 100644
--- a/doc/additional_tricks.md
+++ b/doc/additional_tricks.md
@@ -77,3 +77,14 @@ Avoid using this feature on Windows, create regular file instead.
* remember to close File streams to avoid any potential problems
+## Boot image signature in BootImage V4
+"boot signature" is designed for GKI, it's to be verified by VTS, not bootloader, so this part can be seen as part of the raw boot.img for bootloader.
+
+Emulate creating GKI image:
+```
+out/host/linux-x86/bin/mkbootimg --kernel out/target/product/vsoc_arm64/kernel --ramdisk out/target/product/vsoc_arm64/ramdisk.img --gki_signing_key external/avb/test/data/testkey_rsa4096.pem --gki_signing_algorithm SHA256_RSA4096 --os_version 11 --os_patch_level 2021-03-05 --header_version 4 --output out/target/product/vsoc_arm64/boot.img
+out/host/linux-x86/bin/avbtool add_hash_footer --image out/target/product/vsoc_arm64/boot.img --partition_size 67108864 --partition_name boot --algorithm SHA256_RSA2048 --key external/avb/test/data/testkey_rsa2048.pem --prop com.android.build.boot.fingerprint:nicefinger --prop com.android.build.boot.os_version:11 --rollback_index 1614902400
+```
+As it's only used for GKI verification, I don't want to spend too much time on any special steps in 'gradle pack' flow, as long as DUT can boot up properly.
+
+
diff --git a/doc/layout.md b/doc/layout.md
index 2423b9a..f870e7f 100644
--- a/doc/layout.md
+++ b/doc/layout.md
@@ -125,7 +125,6 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi
### data
-```
+-----------------------------------------------------------+ --> pagesize
| | kernel length |
+-----------------------------------------------------------+
@@ -136,7 +135,6 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi
padding calculation:
| | min(n * page_size - len) |
-```
@@ -156,7 +154,7 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi
|--------------------------------+--------------------------| --> 20
| | 4 |
|--------------------------------+--------------------------| --> 24
- | | 4 |
+ | | 4 |
|--------------------------------+--------------------------| --> 28
| | 2048 |
|--------------------------------+--------------------------| --> 2076
@@ -184,17 +182,28 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi
### data
-```
- +-----------------------------------------------------------+ --> pagesize
- | | padded len |
- +--------------------------------+--------------------------+
+
+ +------------------+-------------+--------------------------+ --> pagesize
+ | | ramdisk 1 | |
+ | +-------------+ |
+ | | ramdisk 2 | |
+ | +-------------+ padded len |
+ | | ramdisk n | |
+ | +-------------+ | --> pagesize + vendor_ramdisk_total_size
+ | | padding | |
+ +--------------------------------+--------------------------+ --> pagesize + vendor_ramdisk_total_size + padding
| | padded len |
+ +--------------------------------+--------------------------+ --> dtb offset + dtb size + padding
+ | | entry 1 | |
+ | table> +-------------+ |
+ | | entry 2 | padded len |
+ | +-------------+ |
+ | | entry n | |
+ | (v4) +-------------+ |
+ | | padding | |
+ +-----------------------------------------------------------+ --> vrt offset + vrt size + padding
+ | (v4) | padded len |
+--------------------------------+--------------------------+
- | | padded len |
- +-----------------------------------------------------------+
- | | padded len |
- +--------------------------------+--------------------------+
-```
diff --git a/helper/src/main/kotlin/cfig/helper/Helper.kt b/helper/src/main/kotlin/cfig/helper/Helper.kt
index a930bec..1652b75 100644
--- a/helper/src/main/kotlin/cfig/helper/Helper.kt
+++ b/helper/src/main/kotlin/cfig/helper/Helper.kt
@@ -18,6 +18,13 @@ import java.text.CharacterIterator
@OptIn(ExperimentalUnsignedTypes::class)
class Helper {
+ data class Slice(
+ var srcFile: String,
+ var offset: Int,
+ var length: Int,
+ var dumpFile: String
+ )
+
companion object {
private val gcfg: Properties = Properties().apply {
load(Helper::class.java.classLoader.getResourceAsStream("general.cfg"))
@@ -85,6 +92,10 @@ class Helper {
return data
}
+ fun extractFile(s: Slice) {
+ return extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
+ }
+
fun extractFile(fileName: String, outImgName: String, offset: Long, length: Int) {
if (0 == length) {
return