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>
<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> <details>
<summary>How to disable AVB verification</summary> <summary>How to disable AVB verification</summary>
@ -158,6 +176,8 @@ boot\_signer<br/>
https://android.googlesource.com/platform/system/extras<br/> https://android.googlesource.com/platform/system/extras<br/>
mkbootimg<br/> mkbootimg<br/>
https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/<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/> kernel info extractor<br/>
https://android.googlesource.com/platform/build/+/refs/heads/master/tools/extract_kernel.py<br/> https://android.googlesource.com/platform/build/+/refs/heads/master/tools/extract_kernel.py<br/>
mkdtboimg<br/> mkdtboimg<br/>

@ -642,6 +642,15 @@ def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
return True 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): class ImageChunk(object):
"""Data structure used for representing chunks in Android sparse files. """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)] self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
o += salt_len o += salt_len
self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_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: if root_digest_len != 0:
raise LookupError('root_digest_len doesn\'t match hash algorithm') raise LookupError('root_digest_len doesn\'t match hash algorithm')
@ -1426,6 +1436,9 @@ class AvbHashtreeDescriptor(AvbDescriptor):
self.root_digest = b'' self.root_digest = b''
self.flags = 0 self.flags = 0
def _hashtree_digest_size(self):
return len(create_avb_hashtree_hasher(self.hash_algorithm, b'').digest())
def print_desc(self, o): def print_desc(self, o):
"""Print the descriptor. """Print the descriptor.
@ -1496,7 +1509,7 @@ class AvbHashtreeDescriptor(AvbDescriptor):
image_filename = os.path.join(image_dir, self.partition_name + image_ext) image_filename = os.path.join(image_dir, self.partition_name + image_ext)
image = ImageHandler(image_filename, read_only=True) image = ImageHandler(image_filename, read_only=True)
# Generate the hashtree and checks that it matches what's in the file. # 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 digest_padding = round_to_pow2(digest_size) - digest_size
(hash_level_offsets, tree_size) = calc_hash_level_offsets( (hash_level_offsets, tree_size) = calc_hash_level_offsets(
self.image_size, self.data_block_size, digest_size + digest_padding) 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)) print('1.{}'.format(required_libavb_version_minor))
return 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 digest_padding = round_to_pow2(digest_size) - digest_size
# If |partition_size| is given (e.g. not 0), calculate the maximum image # 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 = [] level_output_list = []
remaining = hash_src_size remaining = hash_src_size
while remaining > 0: 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 # Only read from the file for the first level - for subsequent
# levels, access the array we're building. # levels, access the array we're building.
if level_num == 0: 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) hash_src_size = len(level_output)
level_num += 1 level_num += 1
hasher = hashlib.new(hash_alg_name, salt) hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
hasher.update(level_output) hasher.update(level_output)
return hasher.digest(), bytes(hash_ret) return hasher.digest(), bytes(hash_ret)

@ -23,7 +23,6 @@ import java.nio.ByteOrder
import java.security.MessageDigest import java.security.MessageDigest
import java.util.regex.Pattern import java.util.regex.Pattern
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
class Common { class Common {
data class VeritySignature( data class VeritySignature(
@ -34,13 +33,6 @@ class Common {
var jarPath: String = "" var jarPath: String = ""
) )
data class Slice(
var srcFile: String,
var offset: Int,
var length: Int,
var dumpFile: String
)
companion object { companion object {
private val log = LoggerFactory.getLogger(Common::class.java) private val log = LoggerFactory.getLogger(Common::class.java)
private const val MAX_ANDROID_VER = 11 private const val MAX_ANDROID_VER = 11
@ -111,12 +103,12 @@ class Common {
return listOf() return listOf()
} }
fun dumpKernel(s: Slice) { fun dumpKernel(s: Helper.Slice) {
Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
parseKernelInfo(s.dumpFile) parseKernelInfo(s.dumpFile)
} }
fun dumpRamdisk(s: Slice, root: String): String { fun dumpRamdisk(s: Helper.Slice, root: String): String {
var ret = "gz" var ret = "gz"
Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
when { when {
@ -162,7 +154,7 @@ class Common {
return ret return ret
} }
fun dumpDtb(s: Slice) { fun dumpDtb(s: Helper.Slice) {
Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
//extract DTB //extract DTB
if (EnvironmentVerifier().hasDtc) { if (EnvironmentVerifier().hasDtc) {

@ -4,7 +4,6 @@ import cfig.Avb
import cfig.EnvironmentVerifier import cfig.EnvironmentVerifier
import cfig.bootimg.Common import cfig.bootimg.Common
import cfig.bootimg.Common.Companion.deleleIfExists import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Common.Slice
import cfig.bootimg.Signer import cfig.bootimg.Signer
import cfig.helper.Helper import cfig.helper.Helper
import cfig.packable.VBMetaParser import cfig.packable.VBMetaParser
@ -169,12 +168,11 @@ data class BootV2(
//info //info
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this) ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this)
//kernel //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 //ramdisk
if (this.ramdisk.size > 0) { if (this.ramdisk.size > 0) {
val fmt = Common.dumpRamdisk( val fmt = Common.dumpRamdisk(
Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), "${workDir}root"
"${workDir}root"
) )
this.ramdisk.file = this.ramdisk.file!! + ".$fmt" this.ramdisk.file = this.ramdisk.file!! + ".$fmt"
//dump info again //dump info again
@ -200,7 +198,7 @@ data class BootV2(
} }
//dtb //dtb
this.dtb?.let { _ -> 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 return this

@ -20,9 +20,11 @@ import java.nio.ByteOrder
import cfig.bootimg.Common as C import cfig.bootimg.Common as C
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
data class BootV3(var info: MiscInfo = MiscInfo(), data class BootV3(
var kernel: CommArgs = CommArgs(), var info: MiscInfo = MiscInfo(),
val ramdisk: CommArgs = CommArgs() var kernel: CommArgs = CommArgs(),
val ramdisk: CommArgs = CommArgs(),
var bootSignature: CommArgs = CommArgs()
) { ) {
companion object { companion object {
private val log = LoggerFactory.getLogger(BootV3::class.java) 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.size = header.ramdiskSize
ret.ramdisk.position = ret.kernel.position + header.kernelSize + ret.ramdisk.position = ret.kernel.position + header.kernelSize +
getPaddingSize(header.kernelSize, BootHeaderV3.pageSize) 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() ret.info.imageSize = File(fileName).length()
return ret return ret
@ -58,22 +67,23 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
} }
data class MiscInfo( data class MiscInfo(
var output: String = "", var output: String = "",
var json: String = "", var json: String = "",
var headerVersion: Int = 0, var headerVersion: Int = 0,
var headerSize: Int = 0, var headerSize: Int = 0,
var pageSize: Int = 0, var pageSize: Int = 0,
var cmdline: String = "", var cmdline: String = "",
var osVersion: String = "", var osVersion: String = "",
var osPatchLevel: String = "", var osPatchLevel: String = "",
var imageSize: Long = 0, var imageSize: Long = 0,
var signatureSize: Int = 0 var signatureSize: Int = 0
) )
data class CommArgs( data class CommArgs(
var file: String = "", var file: String = "",
var position: Int = 0, var position: Int = 0,
var size: Int = 0) var size: Int = 0
)
fun pack(): BootV3 { fun pack(): BootV3 {
if (File(this.ramdisk.file).exists() && !File(workDir + "root").exists()) { 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 -> FileOutputStream(this.info.output + ".clear", false).use { fos ->
val encodedHeader = this.toHeader().encode() val encodedHeader = this.toHeader().encode()
fos.write(encodedHeader) fos.write(encodedHeader)
fos.write(ByteArray( fos.write(
Helper.round_to_multiple(encodedHeader.size, ByteArray(Helper.round_to_multiple(encodedHeader.size, this.info.pageSize) - encodedHeader.size)
this.info.pageSize) - encodedHeader.size)) )
} }
//data //data
@ -128,13 +138,14 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
private fun toHeader(): BootHeaderV3 { private fun toHeader(): BootHeaderV3 {
return BootHeaderV3( return BootHeaderV3(
kernelSize = kernel.size, kernelSize = kernel.size,
ramdiskSize = ramdisk.size, ramdiskSize = ramdisk.size,
headerVersion = info.headerVersion, headerVersion = info.headerVersion,
osVersion = info.osVersion, osVersion = info.osVersion,
osPatchLevel = info.osPatchLevel, osPatchLevel = info.osPatchLevel,
headerSize = info.headerSize, headerSize = info.headerSize,
cmdline = info.cmdline) cmdline = info.cmdline
)
} }
fun extractImages(): BootV3 { fun extractImages(): BootV3 {
@ -142,11 +153,21 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
//info //info
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
//kernel //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 //ramdisk
val fmt = C.dumpRamdisk( 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" 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 //dump info again
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
return this return this
@ -187,6 +208,13 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
it.addRow("ramdisk", this.ramdisk.file) it.addRow("ramdisk", this.ramdisk.file)
it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root") it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root")
it.addRule() 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.addRow("AVB info", Avb.getJsonFileName(info.output))
it.addRule() it.addRule()
it it
@ -201,8 +229,10 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
"" ""
} }
} }
log.info("\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}", log.info(
tableHeader.render(), tab.render(), tabVBMeta) "\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}",
tableHeader.render(), tab.render(), tabVBMeta
)
return this return this
} }

@ -17,30 +17,100 @@ import java.nio.ByteBuffer
import java.nio.ByteOrder import java.nio.ByteOrder
import cfig.bootimg.Common as C import cfig.bootimg.Common as C
import cfig.EnvironmentVerifier import cfig.EnvironmentVerifier
import cfig.io.Struct3
import java.io.InputStream
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
data class VendorBoot(var info: MiscInfo = MiscInfo(), data class VendorBoot(
var ramdisk: CommArgs = CommArgs(), var info: MiscInfo = MiscInfo(),
var dtb: CommArgs = CommArgs()) { var ramdisk: CommArgs = CommArgs(),
var dtb: CommArgs = CommArgs(),
var ramdisk_table: Vrt = Vrt(),
var bootconfig: CommArgs = CommArgs()
) {
data class CommArgs( data class CommArgs(
var file: String = "", var file: String = "",
var position: Long = 0, var position: Long = 0,
var size: Int = 0, var size: Int = 0,
var loadAddr: Long = 0) var loadAddr: Long = 0
)
data class MiscInfo( data class MiscInfo(
var output: String = "", var output: String = "",
var json: String = "", var json: String = "",
var headerVersion: Int = 0, var headerVersion: Int = 0,
var product: String = "", var product: String = "",
var headerSize: Int = 0, var headerSize: Int = 0,
var pageSize: Int = 0, var pageSize: Int = 0,
var cmdline: String = "", var cmdline: String = "",
var tagsLoadAddr: Long = 0, var tagsLoadAddr: Long = 0,
var kernelLoadAddr: Long = 0, var kernelLoadAddr: Long = 0,
var imageSize: 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 { companion object {
private val log = LoggerFactory.getLogger(VendorBoot::class.java) private val log = LoggerFactory.getLogger(VendorBoot::class.java)
fun parse(fileName: String): VendorBoot { fun parse(fileName: String): VendorBoot {
@ -59,27 +129,44 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
ret.info.headerVersion = header.headerVersion ret.info.headerVersion = header.headerVersion
//ramdisk //ramdisk
ret.ramdisk.file = workDir + "ramdisk.img" ret.ramdisk.file = workDir + "ramdisk.img"
ret.ramdisk.size = header.vndRamdiskSize ret.ramdisk.size = header.vndRamdiskTotalSize
ret.ramdisk.loadAddr = header.ramdiskLoadAddr ret.ramdisk.loadAddr = header.ramdiskLoadAddr
ret.ramdisk.position = Helper.round_to_multiple( ret.ramdisk.position = Helper.round_to_multiple(
VendorBootHeader.VENDOR_BOOT_IMAGE_HEADER_V3_SIZE.toLong(), VendorBootHeader.VENDOR_BOOT_IMAGE_HEADER_V3_SIZE.toLong(), header.pageSize
header.pageSize) )
//dtb //dtb
ret.dtb.file = workDir + "dtb" ret.dtb.file = workDir + "dtb"
ret.dtb.size = header.dtbSize ret.dtb.size = header.dtbSize
ret.dtb.loadAddr = header.dtbLoadAddr ret.dtb.loadAddr = header.dtbLoadAddr
ret.dtb.position = ret.ramdisk.position + ret.dtb.position = ret.ramdisk.position + Helper.round_to_multiple(ret.ramdisk.size, header.pageSize)
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() ret.info.imageSize = File(fileName).length()
return ret return ret
} }
} }
private fun parseOsMajor(): Int {
return 11
}
fun pack(): VendorBoot { fun pack(): VendorBoot {
val workDir = Helper.prop("workDir") val workDir = Helper.prop("workDir")
if (File(workDir + this.ramdisk.file).exists() && !File(workDir + "root").exists()) { if (File(workDir + this.ramdisk.file).exists() && !File(workDir + "root").exists()) {
@ -88,7 +175,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
} else { } else {
File(this.ramdisk.file).deleleIfExists() File(this.ramdisk.file).deleleIfExists()
File(this.ramdisk.file.removeSuffix(".gz")).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()) //C.packRootfs("$workDir/root", this.ramdisk.file, parseOsMajor())
//enable advance JAVA cpio //enable advance JAVA cpio
C.packRootfs("$workDir/root", this.ramdisk.file) 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 -> FileOutputStream(this.info.output + ".clear", false).use { fos ->
val encodedHeader = this.toHeader().encode() val encodedHeader = this.toHeader().encode()
fos.write(encodedHeader) fos.write(encodedHeader)
fos.write(ByteArray( fos.write(ByteArray(Helper.round_to_multiple(encodedHeader.size, this.info.pageSize) - encodedHeader.size))
Helper.round_to_multiple(encodedHeader.size,
this.info.pageSize) - encodedHeader.size))
} }
//data //data
log.info("Writing data ...") log.info("Writing data ...")
@ -110,7 +195,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
it.order(ByteOrder.LITTLE_ENDIAN) it.order(ByteOrder.LITTLE_ENDIAN)
C.writePaddedFile(it, this.ramdisk.file, this.info.pageSize) C.writePaddedFile(it, this.ramdisk.file, this.info.pageSize)
C.writePaddedFile(it, this.dtb.file, this.info.pageSize) C.writePaddedFile(it, this.dtb.file, this.info.pageSize)
it it
} }
//write //write
FileOutputStream("${this.info.output}.clear", true).use { fos -> FileOutputStream("${this.info.output}.clear", true).use { fos ->
@ -135,17 +220,17 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
private fun toHeader(): VendorBootHeader { private fun toHeader(): VendorBootHeader {
return VendorBootHeader( return VendorBootHeader(
headerVersion = info.headerVersion, headerVersion = info.headerVersion,
pageSize = info.pageSize, pageSize = info.pageSize,
kernelLoadAddr = info.kernelLoadAddr, kernelLoadAddr = info.kernelLoadAddr,
ramdiskLoadAddr = ramdisk.loadAddr, ramdiskLoadAddr = ramdisk.loadAddr,
vndRamdiskSize = ramdisk.size, vndRamdiskTotalSize = ramdisk.size,
cmdline = info.cmdline, cmdline = info.cmdline,
tagsLoadAddr = info.tagsLoadAddr, tagsLoadAddr = info.tagsLoadAddr,
product = info.product, product = info.product,
headerSize = info.headerSize, headerSize = info.headerSize,
dtbSize = dtb.size, dtbSize = dtb.size,
dtbLoadAddr = dtb.loadAddr dtbLoadAddr = dtb.loadAddr
) )
} }
@ -154,13 +239,27 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
//header //header
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
//ramdisk //ramdisk
val fmt = C.dumpRamdisk(C.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file), //@formatter:off
"${workDir}root") 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" this.ramdisk.file = this.ramdisk.file + ".$fmt"
//dump info again //dump info again
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
//dtb //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 return this
} }
@ -189,12 +288,23 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
it.addRow("image info", workDir + info.output.removeSuffix(".img") + ".json") it.addRow("image info", workDir + info.output.removeSuffix(".img") + ".json")
it.addRule() it.addRule()
it.addRow("ramdisk", this.ramdisk.file) 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.addRule()
it.addRow("dtb", this.dtb.file) it.addRow("dtb", this.dtb.file)
if (File(this.dtb.file + ".src").exists()) { if (File(this.dtb.file + ".src").exists()) {
it.addRow("\\-- decompiled dts", dtb.file + ".src") it.addRow("\\-- decompiled dts", dtb.file + ".src")
} }
if (this.bootconfig.size > 0) {
it.addRule()
it.addRow("bootconfig", this.bootconfig.file)
}
it.addRule() it.addRule()
it.addRow("AVB info", Avb.getJsonFileName(info.output)) it.addRow("AVB info", Avb.getJsonFileName(info.output))
it.addRule() 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{}{}", log.info("\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}", tableHeader.render(), tab.render(), tabVBMeta)
tableHeader.render(), tab.render(), tabVBMeta)
return this return this
} }
private fun toCommandLine(): CommandLine { private fun toCommandLine(): CommandLine {
val cmdPrefix = if (EnvironmentVerifier().isWindows) "python " else "" val cmdPrefix = if (EnvironmentVerifier().isWindows) "python " else ""
return CommandLine.parse(cmdPrefix + Helper.prop("mkbootimg")).apply { return CommandLine.parse(cmdPrefix + Helper.prop("mkbootimg")).apply {
addArgument("--vendor_ramdisk").addArgument(ramdisk.file) addArgument("--vendor_ramdisk").addArgument(ramdisk.file)
addArgument("--dtb").addArgument(dtb.file) addArgument("--dtb").addArgument(dtb.file)
addArgument("--vendor_cmdline").addArgument(info.cmdline, false) addArgument("--vendor_cmdline").addArgument(info.cmdline, false)
addArgument("--header_version").addArgument(info.headerVersion.toString()) addArgument("--header_version").addArgument(info.headerVersion.toString())
addArgument("--base").addArgument("0") addArgument("--base").addArgument("0")
addArgument("--tags_offset").addArgument(info.tagsLoadAddr.toString()) addArgument("--tags_offset").addArgument(info.tagsLoadAddr.toString())
addArgument("--kernel_offset").addArgument(info.kernelLoadAddr.toString()) addArgument("--kernel_offset").addArgument(info.kernelLoadAddr.toString())
addArgument("--ramdisk_offset").addArgument(ramdisk.loadAddr.toString()) addArgument("--ramdisk_offset").addArgument(ramdisk.loadAddr.toString())
addArgument("--dtb_offset").addArgument(dtb.loadAddr.toString()) addArgument("--dtb_offset").addArgument(dtb.loadAddr.toString())
addArgument("--pagesize").addArgument(info.pageSize.toString()) addArgument("--pagesize").addArgument(info.pageSize.toString())
addArgument("--vendor_boot") addArgument("--vendor_boot")
} }
} }
} }

@ -10,7 +10,7 @@ class VendorBootHeader(
var pageSize: Int = 0, var pageSize: Int = 0,
var kernelLoadAddr: Long = 0, var kernelLoadAddr: Long = 0,
var ramdiskLoadAddr: Long = 0, var ramdiskLoadAddr: Long = 0,
var vndRamdiskSize: Int = 0, var vndRamdiskTotalSize: Int = 0,
var cmdline: String = "", var cmdline: String = "",
var tagsLoadAddr: Long = 0, var tagsLoadAddr: Long = 0,
var product: String = "", var product: String = "",
@ -37,7 +37,7 @@ class VendorBootHeader(
this.pageSize = (info[2] as UInt).toInt() this.pageSize = (info[2] as UInt).toInt()
this.kernelLoadAddr = (info[3] as UInt).toLong() this.kernelLoadAddr = (info[3] as UInt).toLong()
this.ramdiskLoadAddr = (info[4] 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.cmdline = info[6] as String
this.tagsLoadAddr = (info[7] as UInt).toLong() this.tagsLoadAddr = (info[7] as UInt).toLong()
this.product = info[8] as String this.product = info[8] as String
@ -64,7 +64,7 @@ class VendorBootHeader(
pageSize, pageSize,
kernelLoadAddr, kernelLoadAddr,
ramdiskLoadAddr, ramdiskLoadAddr,
vndRamdiskSize, vndRamdiskTotalSize,
cmdline, cmdline,
tagsLoadAddr, tagsLoadAddr,
product, product,
@ -106,7 +106,7 @@ class VendorBootHeader(
} }
override fun toString(): String { 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 * 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 ### data
```
+-----------------------------------------------------------+ --> pagesize +-----------------------------------------------------------+ --> pagesize
|<kernel> | kernel length | |<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 calculation:
|<padding> | min(n * page_size - len) | |<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 |--------------------------------+--------------------------| --> 20
|<ramdisk load addr> | 4 | |<ramdisk load addr> | 4 |
|--------------------------------+--------------------------| --> 24 |--------------------------------+--------------------------| --> 24
|<vendor ramdisk size> | 4 | |<vendor ramdisk total size> | 4 |
|--------------------------------+--------------------------| --> 28 |--------------------------------+--------------------------| --> 28
|<vendor cmdline> | 2048 | |<vendor cmdline> | 2048 |
|--------------------------------+--------------------------| --> 2076 |--------------------------------+--------------------------| --> 2076
@ -184,17 +182,28 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi
### data ### data
```
+-----------------------------------------------------------+ --> pagesize +------------------+-------------+--------------------------+ --> pagesize
|<vendor ramdisk section> | padded len | | | 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> | 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) @OptIn(ExperimentalUnsignedTypes::class)
class Helper { class Helper {
data class Slice(
var srcFile: String,
var offset: Int,
var length: Int,
var dumpFile: String
)
companion object { companion object {
private val gcfg: Properties = Properties().apply { private val gcfg: Properties = Properties().apply {
load(Helper::class.java.classLoader.getResourceAsStream("general.cfg")) load(Helper::class.java.classLoader.getResourceAsStream("general.cfg"))
@ -85,6 +92,10 @@ class Helper {
return data 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) { fun extractFile(fileName: String, outImgName: String, offset: Long, length: Int) {
if (0 == length) { if (0 == length) {
return return

Loading…
Cancel
Save