boot v4: unpack

boot:
  boot signature
vendor boot:
  vendor ramdisk table
  boot config

TODO:
  pack (later when AOSP gets stable)
pull/66/head
cfig 4 years ago
parent e2583777a0
commit 71fcc9b26e
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -123,6 +123,24 @@ Your boot.img.signed and vbmeta.img.signd will be updated together, then you can
</details>
<details>
<summary>working with vendor_boot.img + vbmeta.img (Pixel 5 etc.)</summary>
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 <your_vendor_boot_image> vendor_boot.img
cp <your_vbmeta_image> 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'.
</details>
<details>
<summary>How to disable AVB verification</summary>
@ -158,6 +176,8 @@ boot\_signer<br/>
https://android.googlesource.com/platform/system/extras<br/>
mkbootimg<br/>
https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/<br/>
boot header definition<br/>
https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h<br/>
kernel info extractor<br/>
https://android.googlesource.com/platform/build/+/refs/heads/master/tools/extract_kernel.py<br/>
mkdtboimg<br/>

@ -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)

@ -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) {

@ -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

@ -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
}

@ -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<VrtEntry> = mutableListOf()
)
class VrtEntry(
var size: Int = 0,
var offset: Int = 0,
var type: VrtType = VrtType.NONE,
var name: String = "", //32s
var boardId: Array<Int> = 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")
}
}
}

@ -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)"
}

@ -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.

@ -125,7 +125,6 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi
### data
```
+-----------------------------------------------------------+ --> pagesize
|<kernel> | 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:
|<padding> | 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
|<ramdisk load addr> | 4 |
|--------------------------------+--------------------------| --> 24
|<vendor ramdisk size> | 4 |
|<vendor ramdisk total size> | 4 |
|--------------------------------+--------------------------| --> 28
|<vendor cmdline> | 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
|<vendor ramdisk section> | padded len |
+--------------------------------+--------------------------+
+------------------+-------------+--------------------------+ --> pagesize
| | ramdisk 1 | |
| +-------------+ |
| | ramdisk 2 | |
|<vendor ramdisks> +-------------+ padded len |
| | ramdisk n | |
| +-------------+ | --> pagesize + vendor_ramdisk_total_size
| | padding | |
+--------------------------------+--------------------------+ --> pagesize + vendor_ramdisk_total_size + padding
|<dtb> | padded len |
+--------------------------------+--------------------------+ --> dtb offset + dtb size + padding
|<vendor ramdisk > | entry 1 | |
| table> +-------------+ |
| | entry 2 | padded len |
| +-------------+ |
| | entry n | |
| (v4) +-------------+ |
| | padding | |
+-----------------------------------------------------------+ --> vrt offset + vrt size + padding
|<bootconfig> (v4) | padded len |
+--------------------------------+--------------------------+
|<vendor ramdisk table> | padded len |
+-----------------------------------------------------------+
|<bootconfig> | padded len |
+--------------------------------+--------------------------+
```

@ -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

Loading…
Cancel
Save