Merge "Stop configuring the VM with specific cpu set"
diff --git a/apex/Android.bp b/apex/Android.bp
index 83985cc..a9fad55 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -63,6 +63,9 @@
],
prebuilts: [
"com.android.virt.init.rc",
+ "microdroid_initrd_app_debuggable",
+ "microdroid_initrd_full_debuggable",
+ "microdroid_initrd_normal",
"microdroid.json",
"microdroid_bootloader",
"microdroid_bootloader.avbpubkey",
diff --git a/libs/apkverify/Android.bp b/libs/apkverify/Android.bp
index ab9265d..50d7a60 100644
--- a/libs/apkverify/Android.bp
+++ b/libs/apkverify/Android.bp
@@ -30,6 +30,7 @@
name: "libapkverify.test",
defaults: ["libapkverify.defaults"],
test_suites: ["general-tests"],
+ data: ["tests/data/*"],
}
rust_test {
diff --git a/libs/apkverify/src/sigutil.rs b/libs/apkverify/src/sigutil.rs
index 2b2f9da..1d1534c 100644
--- a/libs/apkverify/src/sigutil.rs
+++ b/libs/apkverify/src/sigutil.rs
@@ -49,6 +49,13 @@
const CHUNK_SIZE_BYTES: u64 = 1024 * 1024;
+/// The [APK structure] has four major sections:
+///
+/// | Zip contents | APK Signing Block | Central directory | EOCD(End of Central Directory) |
+///
+/// This structure contains the offset/size information of all the sections except the Zip contents.
+///
+/// [APK structure]: https://source.android.com/docs/security/apksigning/v2#apk-signing-block
pub struct ApkSections<R> {
inner: R,
signing_block_offset: u32,
@@ -180,11 +187,11 @@
// v2/v3 digests are computed after prepending "header" byte and "size" info.
fn digest(&self, data: &[u8], header: &[u8], size: u32) -> Result<DigestBytes> {
- let mut ctx = Hasher::new(self.algorithm)?;
- ctx.update(header)?;
- ctx.update(&size.to_le_bytes())?;
- ctx.update(data)?;
- Ok(ctx.finish()?)
+ let mut hasher = Hasher::new(self.algorithm)?;
+ hasher.update(header)?;
+ hasher.update(&size.to_le_bytes())?;
+ hasher.update(data)?;
+ Ok(hasher.finish()?)
}
}
@@ -282,16 +289,73 @@
}
}
-/// Rank the signature algorithm according to the preferences of the v4 signing scheme.
-pub fn rank_signature_algorithm(algo: u32) -> Result<u32> {
- rank_content_digest_algorithm(to_content_digest_algorithm(algo)?)
-}
-
-fn rank_content_digest_algorithm(id: u32) -> Result<u32> {
- match id {
+/// This method is used to help pick v4 apk digest. According to APK Signature
+/// Scheme v4, apk digest is the first available content digest of the highest
+/// rank (rank N).
+///
+/// This rank was also used for step 3a of the v3 signature verification.
+///
+/// [v3 verification]: https://source.android.com/docs/security/apksigning/v3#v3-verification
+pub fn get_signature_algorithm_rank(algo: u32) -> Result<u32> {
+ let content_digest = to_content_digest_algorithm(algo)?;
+ match content_digest {
CONTENT_DIGEST_CHUNKED_SHA256 => Ok(0),
CONTENT_DIGEST_VERITY_CHUNKED_SHA256 => Ok(1),
CONTENT_DIGEST_CHUNKED_SHA512 => Ok(2),
- _ => bail!("Unknown digest algorithm: {}", id),
+ _ => bail!("Unknown digest algorithm: {}", content_digest),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use byteorder::LittleEndian;
+ use std::fs::File;
+ use std::mem::size_of_val;
+
+ use crate::v3::to_hex_string;
+
+ const CENTRAL_DIRECTORY_HEADER_SIGNATURE: u32 = 0x02014b50;
+
+ #[test]
+ fn test_apk_sections() {
+ let apk_file = File::open("tests/data/v3-only-with-ecdsa-sha512-p521.apk").unwrap();
+ let apk_sections = ApkSections::new(apk_file).unwrap();
+ let mut reader = &apk_sections.inner;
+
+ // Checks APK Signing Block.
+ assert_eq!(
+ apk_sections.signing_block_offset + apk_sections.signing_block_size,
+ apk_sections.central_directory_offset
+ );
+ let apk_signature_offset = SeekFrom::Start(
+ apk_sections.central_directory_offset as u64 - size_of_val(&APK_SIG_BLOCK_MAGIC) as u64,
+ );
+ reader.seek(apk_signature_offset).unwrap();
+ assert_eq!(reader.read_u128::<LittleEndian>().unwrap(), APK_SIG_BLOCK_MAGIC);
+
+ // Checks Central directory.
+ assert_eq!(reader.read_u32::<LittleEndian>().unwrap(), CENTRAL_DIRECTORY_HEADER_SIGNATURE);
+ assert_eq!(
+ apk_sections.central_directory_offset + apk_sections.central_directory_size,
+ apk_sections.eocd_offset
+ );
+
+ // Checks EOCD.
+ assert_eq!(
+ reader.metadata().unwrap().len(),
+ (apk_sections.eocd_offset + apk_sections.eocd_size) as u64
+ );
+ }
+
+ #[test]
+ fn test_apk_digest() {
+ let apk_file = File::open("tests/data/v3-only-with-dsa-sha256-1024.apk").unwrap();
+ let mut apk_sections = ApkSections::new(apk_file).unwrap();
+ let digest = apk_sections.compute_digest(SIGNATURE_DSA_WITH_SHA256).unwrap();
+ assert_eq!(
+ "0DF2426EA33AEDAF495D88E5BE0C6A1663FF0A81C5ED12D5B2929AE4B4300F2F",
+ to_hex_string(&digest[..])
+ );
}
}
diff --git a/libs/apkverify/src/v3.rs b/libs/apkverify/src/v3.rs
index 0c20a2e..876e98b 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -16,9 +16,6 @@
//! Verifies APK Signature Scheme V3
-// TODO(jooyung) remove this
-#![allow(dead_code)]
-
use anyhow::{anyhow, bail, ensure, Context, Result};
use bytes::Bytes;
use openssl::hash::MessageDigest;
@@ -63,6 +60,7 @@
certificates: LengthPrefixed<Vec<LengthPrefixed<X509Certificate>>>,
min_sdk: u32,
max_sdk: u32,
+ #[allow(dead_code)]
additional_attributes: LengthPrefixed<Vec<LengthPrefixed<AdditionalAttributes>>>,
}
@@ -128,14 +126,14 @@
})
}
-/// Gets the APK digest.
+/// Gets the v4 [apk_digest].
+///
+/// [apk_digest]: https://source.android.com/docs/security/apksigning/v4#apk-digest
pub fn pick_v4_apk_digest<R: Read + Seek>(apk: R) -> Result<(u32, Box<[u8]>)> {
let mut sections = ApkSections::new(apk)?;
let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID)?;
let signers = block.read::<Signers>()?;
- if signers.len() != 1 {
- bail!("should only have one signer");
- }
+ ensure!(signers.len() == 1, "should only have one signer");
signers[0].pick_v4_apk_digest()
}
@@ -147,7 +145,7 @@
.signatures
.iter()
.filter(|sig| is_supported_signature_algorithm(sig.signature_algorithm_id))
- .max_by_key(|sig| rank_signature_algorithm(sig.signature_algorithm_id).unwrap())
+ .max_by_key(|sig| get_signature_algorithm_rank(sig.signature_algorithm_id).unwrap())
.ok_or_else(|| anyhow!("No supported signatures found"))?)
}
@@ -301,6 +299,39 @@
}
#[inline]
-fn to_hex_string(buf: &[u8]) -> String {
+pub(crate) fn to_hex_string(buf: &[u8]) -> String {
buf.iter().map(|b| format!("{:02X}", b)).collect()
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::fs::File;
+
+ #[test]
+ fn test_pick_v4_apk_digest_only_with_v3_dsa_sha256() {
+ check_v4_apk_digest(
+ "tests/data/v3-only-with-dsa-sha256-1024.apk",
+ SIGNATURE_DSA_WITH_SHA256,
+ "0DF2426EA33AEDAF495D88E5BE0C6A1663FF0A81C5ED12D5B2929AE4B4300F2F",
+ );
+ }
+
+ #[test]
+ fn test_pick_v4_apk_digest_only_with_v3_pkcs1_sha512() {
+ check_v4_apk_digest(
+ "tests/data/v3-only-with-rsa-pkcs1-sha512-1024.apk",
+ SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512,
+ "9B9AE02DA60B18999BF541790F00D380006FDF0655C3C482AA0BB0AF17CF7A42\
+ ECF56B973518546C9080B2FEF83027E895ED2882BFC88EA19790BBAB29AF53B3",
+ );
+ }
+
+ fn check_v4_apk_digest(apk_filename: &str, expected_algorithm: u32, expected_digest: &str) {
+ let apk_file = File::open(apk_filename).unwrap();
+ let (signature_algorithm_id, apk_digest) = pick_v4_apk_digest(apk_file).unwrap();
+
+ assert_eq!(expected_algorithm, signature_algorithm_id);
+ assert_eq!(expected_digest, to_hex_string(apk_digest.as_ref()));
+ }
+}
diff --git a/libs/apkverify/src/ziputil.rs b/libs/apkverify/src/ziputil.rs
index ebb66e0..8badff2 100644
--- a/libs/apkverify/src/ziputil.rs
+++ b/libs/apkverify/src/ziputil.rs
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-//! Utilities for zip handling
+//! Utilities for zip handling of APK files.
use anyhow::{bail, Result};
use bytes::{Buf, BufMut};
use std::io::{Read, Seek, SeekFrom};
use zip::ZipArchive;
-const EOCD_MIN_SIZE: usize = 22;
+const EOCD_SIZE_WITHOUT_COMMENT: usize = 22;
const EOCD_CENTRAL_DIRECTORY_SIZE_FIELD_OFFSET: usize = 12;
const EOCD_CENTRAL_DIRECTORY_OFFSET_FIELD_OFFSET: usize = 16;
-const EOCD_MAGIC: u32 = 0x06054b50;
+/// End of Central Directory signature
+const EOCD_SIGNATURE: u32 = 0x06054b50;
const ZIP64_MARK: u32 = 0xffffffff;
#[derive(Debug, PartialEq, Eq)]
@@ -39,7 +40,7 @@
pub fn zip_sections<R: Read + Seek>(mut reader: R) -> Result<(R, ZipSections)> {
// open a zip to parse EOCD
let archive = ZipArchive::new(reader)?;
- let eocd_size = archive.comment().len() + EOCD_MIN_SIZE;
+ let eocd_size = archive.comment().len() + EOCD_SIZE_WITHOUT_COMMENT;
if archive.offset() != 0 {
bail!("Invalid ZIP: offset should be 0, but {}.", archive.offset());
}
@@ -49,7 +50,7 @@
let eocd_offset = reader.seek(SeekFrom::Current(0))? as u32;
let mut eocd = vec![0u8; eocd_size as usize];
reader.read_exact(&mut eocd)?;
- if (&eocd[0..]).get_u32_le() != EOCD_MAGIC {
+ if (&eocd[0..]).get_u32_le() != EOCD_SIGNATURE {
bail!("Invalid ZIP: ZipArchive::new() should point EOCD after reading.");
}
let (central_directory_size, central_directory_offset) = get_central_directory(&eocd)?;
@@ -72,7 +73,7 @@
}
fn get_central_directory(buf: &[u8]) -> Result<(u32, u32)> {
- if buf.len() < EOCD_MIN_SIZE {
+ if buf.len() < EOCD_SIZE_WITHOUT_COMMENT {
bail!("Invalid EOCD size: {}", buf.len());
}
let mut buf = &buf[EOCD_CENTRAL_DIRECTORY_SIZE_FIELD_OFFSET..];
@@ -83,7 +84,7 @@
/// Update EOCD's central_directory_offset field.
pub fn set_central_directory_offset(buf: &mut [u8], value: u32) -> Result<()> {
- if buf.len() < EOCD_MIN_SIZE {
+ if buf.len() < EOCD_SIZE_WITHOUT_COMMENT {
bail!("Invalid EOCD size: {}", buf.len());
}
(&mut buf[EOCD_CENTRAL_DIRECTORY_OFFSET_FIELD_OFFSET..]).put_u32_le(value);
@@ -94,6 +95,8 @@
mod tests {
use super::*;
use crate::testing::assert_contains;
+ use byteorder::{LittleEndian, ReadBytesExt};
+ use std::fs::File;
use std::io::{Cursor, Write};
use zip::{write::FileOptions, ZipWriter};
@@ -107,7 +110,10 @@
#[test]
fn test_zip_sections() {
let (cursor, sections) = zip_sections(create_test_zip()).unwrap();
- assert_eq!(sections.eocd_offset, (cursor.get_ref().len() - EOCD_MIN_SIZE) as u32);
+ assert_eq!(
+ sections.eocd_offset,
+ (cursor.get_ref().len() - EOCD_SIZE_WITHOUT_COMMENT) as u32
+ );
}
#[test]
@@ -118,7 +124,7 @@
// insert garbage between CD and EOCD.
// by the way, to mock zip-rs, use CD as garbage. This is implementation detail of zip-rs,
// which reads CD at (eocd_offset - cd_size) instead of at cd_offset from EOCD.
- let (pre_eocd, eocd) = buf.split_at(buf.len() - EOCD_MIN_SIZE);
+ let (pre_eocd, eocd) = buf.split_at(buf.len() - EOCD_SIZE_WITHOUT_COMMENT);
let (_, cd_offset) = get_central_directory(eocd).unwrap();
let cd = &pre_eocd[cd_offset as usize..];
@@ -127,4 +133,24 @@
assert!(res.is_err());
assert_contains(&res.err().unwrap().to_string(), "Invalid ZIP: offset should be 0");
}
+
+ #[test]
+ fn test_zip_sections_with_apk() {
+ let apk = File::open("tests/data/v3-only-with-stamp.apk").unwrap();
+ let (mut reader, sections) = zip_sections(apk).unwrap();
+
+ // Checks Central directory.
+ assert_eq!(
+ sections.central_directory_offset + sections.central_directory_size,
+ sections.eocd_offset
+ );
+
+ // Checks EOCD.
+ reader.seek(SeekFrom::Start(sections.eocd_offset as u64)).unwrap();
+ assert_eq!(reader.read_u32::<LittleEndian>().unwrap(), EOCD_SIGNATURE);
+ assert_eq!(
+ reader.metadata().unwrap().len(),
+ (sections.eocd_offset + sections.eocd_size) as u64
+ );
+ }
}
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 65aeb07..281416b 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -578,3 +578,18 @@
filename: "event-log-tags",
installable: false,
}
+
+filegroup {
+ name: "microdroid_bootconfig_full_debuggable_src",
+ srcs: ["bootconfig.full_debuggable"],
+}
+
+filegroup {
+ name: "microdroid_bootconfig_app_debuggable_src",
+ srcs: ["bootconfig.app_debuggable"],
+}
+
+filegroup {
+ name: "microdroid_bootconfig_normal_src",
+ srcs: ["bootconfig.normal"],
+}
diff --git a/microdroid/initrd/Android.bp b/microdroid/initrd/Android.bp
new file mode 100644
index 0000000..a9d0da3
--- /dev/null
+++ b/microdroid/initrd/Android.bp
@@ -0,0 +1,161 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_binary_host {
+ name: "initrd_bootconfig",
+ srcs: ["src/main.rs"],
+ rustlibs: [
+ "libanyhow",
+ "libstructopt",
+ ],
+ prefer_rlib: true,
+}
+
+python_binary_host {
+ name: "gen_vbmeta_bootconfig",
+ srcs: ["gen_vbmeta_bootconfig.py"],
+}
+
+genrule {
+ name: "microdroid_initrd_gen",
+ srcs: [
+ ":microdroid_ramdisk",
+ ":microdroid_vendor_ramdisk",
+ ],
+ out: ["microdroid_initrd.img"],
+ cmd: "cat $(in) > $(out)",
+}
+
+// This contains vbmeta hashes & related (boot)configs which are passed to kernel/init
+genrule {
+ name: "microdroid_vbmeta_bootconfig_gen",
+ srcs: [":microdroid_vbmeta"],
+ out: ["bootconfig_microdroid_vbmeta"],
+ tools: [
+ "gen_vbmeta_bootconfig",
+ "avbtool",
+ ],
+ cmd: "$(location gen_vbmeta_bootconfig) $(location avbtool) $(in) > $(out)",
+}
+
+bootconfigs_arm64 = [
+ ":microdroid_bootconfig_arm64_gen",
+ ":microdroid_vbmeta_bootconfig_gen",
+]
+
+bootconfigs_x86_64 = [
+ ":microdroid_bootconfig_x86_64_gen",
+ ":microdroid_vbmeta_bootconfig_gen",
+]
+
+genrule {
+ name: "microdroid_initrd_full_debuggable_arm64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_initrd_gen",
+ ":microdroid_bootconfig_full_debuggable_src",
+ ] + bootconfigs_arm64,
+ out: ["microdroid_initrd_full_debuggable_arm64"],
+ cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+}
+
+genrule {
+ name: "microdroid_initrd_full_debuggable_x86_64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_initrd_gen",
+ ":microdroid_bootconfig_full_debuggable_src",
+ ] + bootconfigs_x86_64,
+ out: ["microdroid_initrd_full_debuggable_x86_64"],
+ cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+}
+
+genrule {
+ name: "microdroid_initrd_app_debuggable_arm64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_initrd_gen",
+ ":microdroid_bootconfig_app_debuggable_src",
+ ] + bootconfigs_arm64,
+ out: ["microdroid_initrd_app_debuggable_arm64"],
+ cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+}
+
+genrule {
+ name: "microdroid_initrd_app_debuggable_x86_64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_initrd_gen",
+ ":microdroid_bootconfig_app_debuggable_src",
+ ] + bootconfigs_x86_64,
+ out: ["microdroid_initrd_app_debuggable_x86_64"],
+ cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+}
+
+genrule {
+ name: "microdroid_initrd_normal_arm64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_initrd_gen",
+ ":microdroid_bootconfig_normal_src",
+ ] + bootconfigs_arm64,
+ out: ["microdroid_initrd_normal_arm64"],
+ cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+}
+
+genrule {
+ name: "microdroid_initrd_normal_x86_64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_initrd_gen",
+ ":microdroid_bootconfig_normal_src",
+ ] + bootconfigs_x86_64,
+ out: ["microdroid_initrd_normal_x86_64"],
+ cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+}
+
+prebuilt_etc {
+ name: "microdroid_initrd_full_debuggable",
+ // We don't have ramdisk for architectures other than x86_64 & arm64
+ src: "empty_file",
+ arch: {
+ x86_64: {
+ src: ":microdroid_initrd_full_debuggable_x86_64",
+ },
+ arm64: {
+ src: ":microdroid_initrd_full_debuggable_arm64",
+ },
+ },
+ filename: "microdroid_initrd_full_debuggable.img",
+}
+
+prebuilt_etc {
+ name: "microdroid_initrd_app_debuggable",
+ // We don't have ramdisk for architectures other than x86_64 & arm64
+ src: "empty_file",
+ arch: {
+ x86_64: {
+ src: ":microdroid_initrd_app_debuggable_x86_64",
+ },
+ arm64: {
+ src: ":microdroid_initrd_app_debuggable_arm64",
+ },
+ },
+ filename: "microdroid_initrd_app_debuggable.img",
+}
+
+prebuilt_etc {
+ name: "microdroid_initrd_normal",
+ // We don't have ramdisk for architectures other than x86_64 & arm64
+ src: "empty_file",
+ arch: {
+ x86_64: {
+ src: ":microdroid_initrd_normal_x86_64",
+ },
+ arm64: {
+ src: ":microdroid_initrd_normal_arm64",
+ },
+ },
+ filename: "microdroid_initrd_normal.img",
+}
diff --git a/microdroid/initrd/empty_file b/microdroid/initrd/empty_file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/microdroid/initrd/empty_file
diff --git a/microdroid/initrd/gen_vbmeta_bootconfig.py b/microdroid/initrd/gen_vbmeta_bootconfig.py
new file mode 100755
index 0000000..f0fc2e8
--- /dev/null
+++ b/microdroid/initrd/gen_vbmeta_bootconfig.py
@@ -0,0 +1,54 @@
+"""Extract information about vbmeta (digest/size) required in (androidboot.*)
+bootconfig. It uses avbtool to find some values such as vbmeta size, digest"""
+#!/usr/bin/env python3
+
+import sys
+import subprocess
+
+def main(args):
+ """Runs avbtool to generate vbmeta related bootconfigs"""
+ avbtool = args[0]
+ vbmeta_img = args[1]
+ hash_algorithm = 'sha256'
+ size = 0
+
+ with subprocess.Popen([avbtool, 'version'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT) as proc:
+ stdout, _ = proc.communicate()
+ avb_version = stdout.decode("utf-8").split(" ")[1].strip()
+ avb_version = avb_version[0:avb_version.rfind('.')]
+
+ with subprocess.Popen([avbtool, 'info_image', '--image', vbmeta_img],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT) as proc:
+ stdout, _ = proc.communicate()
+ for line in stdout.decode("utf-8").split("\n"):
+ line = line.split(":")
+ if line[0] in \
+ ['Header Block', 'Authentication Block', 'Auxiliary Block']:
+ size += int(line[1].strip()[0:-6])
+
+ with subprocess.Popen([avbtool, 'calculate_vbmeta_digest',
+ '--image', vbmeta_img,
+ '--hash_algorithm', hash_algorithm],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT) as proc:
+ stdout, _ = proc.communicate()
+ vbmeta_hash = stdout.decode("utf-8").strip()
+
+ print(f'androidboot.vbmeta.size = {size}')
+ print(f'androidboot.vbmeta.digest = \"{vbmeta_hash}\"')
+ print(f'androidboot.vbmeta.hash_alg = \"{hash_algorithm}\"')
+ print(f'androidboot.vbmeta.avb_version = \"{avb_version}\"')
+ print('androidboot.veritymode = "enforcing"')
+ print('androidboot.vbmeta.invalidate_on_error = "yes"')
+ print('androidboot.vbmeta.device_state = "locked"')
+ print('androidboot.vbmeta.device = "/dev/block/by-name/vbmeta_a"')
+ print('androidboot.slot_suffix = "_a"')
+ print('androidboot.force_normal_boot = "1"')
+ print('androidboot.verifiedbootstate = "green"')
+
+## Main body
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/microdroid/initrd/src/main.rs b/microdroid/initrd/src/main.rs
new file mode 100644
index 0000000..1023a40
--- /dev/null
+++ b/microdroid/initrd/src/main.rs
@@ -0,0 +1,77 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Append bootconfig to initrd image
+use anyhow::Result;
+
+use std::fs::File;
+use std::io::{Read, Write};
+use std::path::PathBuf;
+use structopt::StructOpt;
+
+const FOOTER_ALIGNMENT: usize = 4;
+const ZEROS: [u8; 4] = [0u8; 4_usize];
+
+#[derive(StructOpt, Debug)]
+struct Args {
+ /// Output
+ #[structopt(parse(from_os_str), long = "output")]
+ output: PathBuf,
+ /// Initrd (without bootconfig)
+ #[structopt(parse(from_os_str))]
+ initrd: PathBuf,
+ /// Bootconfig
+ #[structopt(parse(from_os_str))]
+ bootconfigs: Vec<PathBuf>,
+}
+
+fn get_checksum(file_path: &PathBuf) -> Result<u32> {
+ File::open(file_path)?.bytes().map(|x| Ok(x? as u32)).sum()
+}
+
+// Bootconfig is attached to the initrd in the following way:
+// [initrd][bootconfig][padding][size(le32)][checksum(le32)][#BOOTCONFIG\n]
+fn attach_bootconfig(initrd: PathBuf, bootconfigs: Vec<PathBuf>, output: PathBuf) -> Result<()> {
+ let mut output_file = File::create(&output)?;
+ let mut initrd_file = File::open(&initrd)?;
+ let initrd_size: usize = initrd_file.metadata()?.len().try_into()?;
+ let mut bootconfig_size: usize = 0;
+ let mut checksum: u32 = 0;
+
+ std::io::copy(&mut initrd_file, &mut output_file)?;
+ for bootconfig in bootconfigs {
+ let mut bootconfig_file = File::open(&bootconfig)?;
+ std::io::copy(&mut bootconfig_file, &mut output_file)?;
+ bootconfig_size += bootconfig_file.metadata()?.len() as usize;
+ checksum += get_checksum(&bootconfig)?;
+ }
+
+ let padding_size: usize = FOOTER_ALIGNMENT - (initrd_size + bootconfig_size) % FOOTER_ALIGNMENT;
+ output_file.write_all(&ZEROS[..padding_size])?;
+ output_file.write_all(&((padding_size + bootconfig_size) as u32).to_le_bytes())?;
+ output_file.write_all(&checksum.to_le_bytes())?;
+ output_file.write_all(b"#BOOTCONFIG\n")?;
+ output_file.flush()?;
+ Ok(())
+}
+
+fn try_main() -> Result<()> {
+ let args = Args::from_args_safe()?;
+ attach_bootconfig(args.initrd, args.bootconfigs, args.output)?;
+ Ok(())
+}
+
+fn main() {
+ try_main().unwrap()
+}
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index eab9474..5bcafd5 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -15,8 +15,7 @@
}
cc_binary {
- name: "pvmfw_elf",
- stem: "pvmfw",
+ name: "pvmfw",
defaults: ["vmbase_elf_defaults"],
srcs: [
"idmap.S",
@@ -33,8 +32,9 @@
}
raw_binary {
- name: "pvmfw",
- src: ":pvmfw_elf",
+ name: "pvmfw_bin",
+ stem: "pvmfw.bin",
+ src: ":pvmfw",
enabled: false,
target: {
android_arm64: {
diff --git a/pvmfw/src/exceptions.rs b/pvmfw/src/exceptions.rs
index 0e637ac..596ecc7 100644
--- a/pvmfw/src/exceptions.rs
+++ b/pvmfw/src/exceptions.rs
@@ -19,8 +19,9 @@
#[no_mangle]
extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
+ let esr = read_esr();
emergency_write_str("sync_exception_current\n");
- print_esr();
+ print_esr(esr);
reboot();
}
@@ -38,15 +39,17 @@
#[no_mangle]
extern "C" fn serr_current(_elr: u64, _spsr: u64) {
+ let esr = read_esr();
emergency_write_str("serr_current\n");
- print_esr();
+ print_esr(esr);
reboot();
}
#[no_mangle]
extern "C" fn sync_lower(_elr: u64, _spsr: u64) {
+ let esr = read_esr();
emergency_write_str("sync_lower\n");
- print_esr();
+ print_esr(esr);
reboot();
}
@@ -64,16 +67,22 @@
#[no_mangle]
extern "C" fn serr_lower(_elr: u64, _spsr: u64) {
+ let esr = read_esr();
emergency_write_str("serr_lower\n");
- print_esr();
+ print_esr(esr);
reboot();
}
#[inline]
-fn print_esr() {
+fn read_esr() -> u64 {
let mut esr: u64;
unsafe {
asm!("mrs {esr}, esr_el1", esr = out(reg) esr);
}
+ esr
+}
+
+#[inline]
+fn print_esr(esr: u64) {
eprintln!("esr={:#08x}", esr);
}
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index c9ceaae..7bf3c4e 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -143,11 +143,7 @@
// To grab boot events from log, set debug mode to FULL
VirtualMachineConfig normalConfig =
- builder.debugLevel(DebugLevel.FULL)
- .memoryMib(256)
- .numCpus(2)
- .cpuAffinity("0=0:1=1")
- .build();
+ builder.debugLevel(DebugLevel.FULL).memoryMib(256).build();
mInner.forceCreateNewVirtualMachine("test_vm_boot_time", normalConfig);
BootResult result = tryBootVm(TAG, "test_vm_boot_time");
@@ -194,8 +190,6 @@
VirtualMachineConfig config =
mInner.newVmConfigBuilder("assets/vm_config_io.json")
.debugLevel(DebugLevel.FULL)
- .numCpus(2)
- .cpuAffinity("0=0:1=1")
.build();
List<Double> transferRates = new ArrayList<>(IO_TEST_TRIAL_COUNT);
@@ -223,8 +217,6 @@
VirtualMachineConfig config =
mInner.newVmConfigBuilder("assets/vm_config_io.json")
.debugLevel(DebugLevel.FULL)
- .numCpus(2)
- .cpuAffinity("0=0:1=1")
.build();
List<Double> readRates = new ArrayList<>(IO_TEST_TRIAL_COUNT);
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index 045be94..c7ee0f2 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -34,6 +34,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,6 +67,7 @@
private static final String METRIC_PREFIX = "avf_perf/hostside/";
private final MetricsProcessor mMetricsProcessor = new MetricsProcessor(METRIC_PREFIX);
+ @Rule public TestMetrics mMetrics = new TestMetrics();
@Before
public void setUp() throws Exception {
@@ -161,14 +163,13 @@
assumeTrue(!result.getStderr().contains("Invalid oem command"));
// Skip the test if running on a build with pkvm_enabler. Disabling pKVM
// for such builds results in a bootloop.
- assumeTrue(result.getStdout().contains("misc=auto"));
+ assumeTrue(result.getStderr().contains("misc=auto"));
}
private void reportMetric(List<Double> data, String name, String unit) {
Map<String, Double> stats = mMetricsProcessor.computeStats(data, name, unit);
- TestMetrics metrics = new TestMetrics();
for (Map.Entry<String, Double> entry : stats.entrySet()) {
- metrics.addTestMetric(entry.getKey(), Double.toString(entry.getValue()));
+ mMetrics.addTestMetric(entry.getKey(), entry.getValue().toString());
}
}
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 0c048b9..b92a526 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -170,19 +170,25 @@
.debugLevel(DebugLevel.NONE)
.build();
VirtualMachine vm = mInner.forceCreateNewVirtualMachine("low_mem", lowMemConfig);
- final CompletableFuture<Integer> exception = new CompletableFuture<>();
+ final CompletableFuture<Boolean> onPayloadReadyExecuted = new CompletableFuture<>();
+ final CompletableFuture<Boolean> onDiedExecuted = new CompletableFuture<>();
VmEventListener listener =
new VmEventListener() {
@Override
+ public void onPayloadReady(VirtualMachine vm) {
+ onPayloadReadyExecuted.complete(true);
+ super.onPayloadReady(vm);
+ }
+ @Override
public void onDied(VirtualMachine vm, int reason) {
- exception.complete(reason);
+ onDiedExecuted.complete(true);
super.onDied(vm, reason);
}
};
listener.runToFinish(TAG, vm);
- assertThat(exception.getNow(0)).isAnyOf(VirtualMachineCallback.DEATH_REASON_REBOOT,
- VirtualMachineCallback.DEATH_REASON_HANGUP,
- VirtualMachineCallback.DEATH_REASON_CRASH);
+ // Assert that onDied() was executed but onPayloadReady() was never run
+ assertThat(onDiedExecuted.getNow(false)).isTrue();
+ assertThat(onPayloadReadyExecuted.getNow(false)).isFalse();
}
}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index d86f2bf..a6b228d 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -68,11 +68,15 @@
* Comma-separated list of CPUs or CPU ranges to run vCPUs on (e.g. 0,1-3,5), or
* colon-separated list of assignments of vCPU to host CPU assignments (e.g. 0=0:1=1:2=2).
* Default is no mask which means a vCPU can run on any host CPU.
+ *
+ * Note: Using a non-null value requires android.permission.USE_CUSTOM_VIRTUAL_MACHINE.
*/
@nullable String cpuAffinity;
/**
* List of task profile names to apply for the VM
+ *
+ * Note: Specifying a value here requires android.permission.USE_CUSTOM_VIRTUAL_MACHINE.
*/
String[] taskProfiles;
}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 352b4f1..dcc2d48 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -596,6 +596,12 @@
config: &VirtualMachineAppConfig,
temporary_directory: &Path,
) -> Result<VirtualMachineRawConfig> {
+ // Controlling CPUs is reserved for platform apps only, even when using
+ // VirtualMachineAppConfig.
+ if config.cpuAffinity.is_some() || !config.taskProfiles.is_empty() {
+ check_use_custom_virtual_machine()?
+ }
+
let apk_file = clone_file(config.apk.as_ref().unwrap())?;
let idsig_file = clone_file(config.idsig.as_ref().unwrap())?;
let instance_file = clone_file(config.instanceImage.as_ref().unwrap())?;
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
index ab87053..fb525b5 100644
--- a/vmbase/Android.bp
+++ b/vmbase/Android.bp
@@ -49,6 +49,13 @@
"libarm-optimized-routines-mem",
"libvmbase_entry",
],
+ installable: false,
+ enabled: false,
+ target: {
+ android_arm64: {
+ enabled: true,
+ },
+ },
}
rust_library_rlib {
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index e11bb2f..4e62090 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -18,8 +18,7 @@
}
cc_binary {
- name: "vmbase_example_elf",
- stem: "vmbase_example",
+ name: "vmbase_example",
defaults: ["vmbase_elf_defaults"],
srcs: [
"idmap.S",
@@ -35,8 +34,9 @@
}
raw_binary {
- name: "vmbase_example",
- src: ":vmbase_example_elf",
+ name: "vmbase_example_bin",
+ stem: "vmbase_example.bin",
+ src: ":vmbase_example",
enabled: false,
target: {
android_arm64: {
@@ -60,7 +60,7 @@
"libvmclient",
],
data: [
- ":vmbase_example",
+ ":vmbase_example_bin",
],
test_suites: ["general-tests"],
enabled: false,