Compress arm64 GKI with lz4
Now that crosvm can use lz4-compressed kernel, this change compresses
microdroid kernels with lz4 to reduce bloating of virt apex.
* virt apex size becomes from 101MB to 84MB.
* MicrodroidBenchmarks shows that there is no performance regression.
Bug: 315141974
Test: atest MicrodroidTests MicrodroidHostTests
Test: run MicrodroidBenchmarks and compare metrics
Change-Id: I59e40340260ef89d9e116f7e050fa4f30efb0d3e
diff --git a/apex/Android.bp b/apex/Android.bp
index f2badfa..4586f51 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -255,6 +255,7 @@
"initrd_bootconfig",
"lpmake",
"lpunpack",
+ "lz4",
"simg2img",
],
}
@@ -275,6 +276,7 @@
"initrd_bootconfig",
"lpmake",
"lpunpack",
+ "lz4",
"sign_virt_apex",
"simg2img",
],
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index 0b6137b..fbbd152 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -153,12 +153,18 @@
'--key', key, '--output', output])
+def is_lz4(args, path):
+ # error 44: Unrecognized header
+ result = RunCommand(args, ['lz4', '-t', path], expected_return_values={0, 44})
+ return result[1] == 0
+
+
def AvbInfo(args, image_path):
"""Parses avbtool --info image output
Args:
args: program arguments.
- image_path: The path to the image.
+ image_path: The path to the image, either raw or lz4 compressed
descriptor_name: Descriptor name of interest.
Returns:
@@ -169,6 +175,11 @@
if not os.path.exists(image_path):
raise ValueError(f'Failed to find image: {image_path}')
+ if is_lz4(args, image_path):
+ with tempfile.NamedTemporaryFile() as decompressed_image:
+ RunCommand(args, ['lz4', '-d', '-f', image_path, decompressed_image.name])
+ return AvbInfo(args, decompressed_image.name)
+
output, ret_code = RunCommand(
args, ['avbtool', 'info_image', '--image', image_path], expected_return_values={0, 1})
if ret_code == 1:
@@ -560,11 +571,7 @@
wait=[vbmeta_f])
# Re-sign kernel. Note kernel's vbmeta contain addition descriptor from ramdisk(s)
- def resign_kernel(kernel, initrd_normal, initrd_debug):
- kernel_file = files[kernel]
- initrd_normal_file = files[initrd_normal]
- initrd_debug_file = files[initrd_debug]
-
+ def resign_decompressed_kernel(kernel_file, initrd_normal_file, initrd_debug_file):
_, kernel_image_descriptors = AvbInfo(args, kernel_file)
salts = extract_hash_descriptors(
kernel_image_descriptors, lambda descriptor: descriptor['Salt'])
@@ -580,6 +587,27 @@
additional_images=[initrd_normal_hashdesc, initrd_debug_hashdesc],
wait=[initrd_n_f, initrd_d_f])
+ def resign_compressed_kernel(kernel_file, initrd_normal_file, initrd_debug_file):
+ # decompress, re-sign, compress again
+ with tempfile.TemporaryDirectory() as work_dir:
+ decompressed_kernel_file = os.path.join(work_dir, os.path.basename(kernel_file))
+ RunCommand(args, ['lz4', '-d', kernel_file, decompressed_kernel_file])
+ resign_decompressed_kernel(decompressed_kernel_file, initrd_normal_file,
+ initrd_debug_file).result()
+ RunCommand(args, ['lz4', '-9', '-f', decompressed_kernel_file, kernel_file])
+
+ def resign_kernel(kernel, initrd_normal, initrd_debug):
+ kernel_file = files[kernel]
+ initrd_normal_file = files[initrd_normal]
+ initrd_debug_file = files[initrd_debug]
+
+ # kernel may be compressed with lz4.
+ if is_lz4(args, kernel_file):
+ return Async(resign_compressed_kernel, kernel_file, initrd_normal_file,
+ initrd_debug_file)
+ else:
+ return resign_decompressed_kernel(kernel_file, initrd_normal_file, initrd_debug_file)
+
_, original_kernel_descriptors = AvbInfo(args, files['kernel'])
resign_kernel_task = resign_kernel('kernel', 'initrd_normal.img', 'initrd_debuggable.img')
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 36688fc..999dc52 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -559,7 +559,7 @@
avb_add_hash_footer {
name: "microdroid_gki-android14-6.1_kernel_signed",
defaults: ["microdroid_kernel_signed_defaults"],
- filename: "microdroid_gki-android14-6.1_kernel",
+ filename: "microdroid_gki-android14-6.1_kernel_signed",
arch: {
arm64: {
src: ":microdroid_gki_kernel_prebuilts-6.1-arm64",
@@ -574,13 +574,29 @@
],
}
+// HACK: use cc_genrule for arch-specific properties
+cc_genrule {
+ name: "microdroid_gki-android14-6.1_kernel_signed-lz4",
+ out: ["microdroid_gki-android14-6.1_kernel_signed-lz4"],
+ srcs: [":empty_file"],
+ arch: {
+ arm64: {
+ srcs: [":microdroid_gki-android14-6.1_kernel_signed"],
+ exclude_srcs: [":empty_file"],
+ },
+ },
+ tools: ["lz4"],
+ cmd: "$(location lz4) -9 $(in) $(out)",
+}
+
prebuilt_etc {
name: "microdroid_gki-android14-6.1_kernel",
+ filename: "microdroid_gki-android14-6.1_kernel",
src: ":empty_file",
relative_install_path: "fs",
arch: {
arm64: {
- src: ":microdroid_gki-android14-6.1_kernel_signed",
+ src: ":microdroid_gki-android14-6.1_kernel_signed-lz4",
},
x86_64: {
src: ":microdroid_gki-android14-6.1_kernel_signed",
@@ -605,21 +621,25 @@
srcs: ["extract_microdroid_kernel_hashes.py"],
}
-genrule {
+// HACK: use cc_genrule for arch-specific properties
+cc_genrule {
name: "microdroid_kernel_hashes_rs",
- srcs: [
- ":microdroid_kernel",
- ":microdroid_gki-android14-6.1_kernel",
- ],
+ srcs: [":microdroid_kernel"],
+ arch: {
+ arm64: {
+ srcs: [":microdroid_gki-android14-6.1_kernel_signed"],
+ },
+ x86_64: {
+ srcs: [":microdroid_gki-android14-6.1_kernel_signed"],
+ },
+ },
out: ["lib.rs"],
tools: [
"extract_microdroid_kernel_hashes",
"avbtool",
],
cmd: "$(location extract_microdroid_kernel_hashes) --avbtool $(location avbtool) " +
- "--kernel $(location :microdroid_kernel) " +
- "$(location :microdroid_gki-android14-6.1_kernel) " +
- "> $(out)",
+ "--kernel $(in) > $(out)",
}
rust_library_rlib {
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 13a9925..41d244d 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -35,6 +35,7 @@
"initrd_bootconfig",
"lpmake",
"lpunpack",
+ "lz4",
"sign_virt_apex",
"simg2img",
"dtdiff",
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 0901fd4..253cf6f 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -944,7 +944,43 @@
assertThat(hasDebugPolicy).isFalse();
}
+ private boolean isLz4(String path) throws Exception {
+ File lz4tool = findTestFile("lz4");
+ CommandResult result =
+ new RunUtil().runTimedCmd(5000, lz4tool.getAbsolutePath(), "-t", path);
+ return result.getStatus() == CommandStatus.SUCCESS;
+ }
+
+ private void decompressLz4(String inputPath, String outputPath) throws Exception {
+ File lz4tool = findTestFile("lz4");
+ CommandResult result =
+ new RunUtil()
+ .runTimedCmd(
+ 5000, lz4tool.getAbsolutePath(), "-d", "-f", inputPath, outputPath);
+ String out = result.getStdout();
+ String err = result.getStderr();
+ assertWithMessage(
+ "lz4 image "
+ + inputPath
+ + " decompression failed."
+ + "\n\tout: "
+ + out
+ + "\n\terr: "
+ + err
+ + "\n")
+ .about(command_results())
+ .that(result)
+ .isSuccess();
+ }
+
private String avbInfo(String image_path) throws Exception {
+ if (isLz4(image_path)) {
+ File decompressedImage = FileUtil.createTempFile("decompressed", ".img");
+ decompressedImage.deleteOnExit();
+ decompressLz4(image_path, decompressedImage.getAbsolutePath());
+ image_path = decompressedImage.getAbsolutePath();
+ }
+
File avbtool = findTestFile("avbtool");
List<String> command =
Arrays.asList(avbtool.getAbsolutePath(), "info_image", "--image", image_path);