Merge "Implement long touch to select text" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java b/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java
index b2a2085..7f14179 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java
@@ -16,8 +16,11 @@
package com.android.virtualization.terminal;
+import static com.android.virtualization.terminal.MainActivity.TAG;
+
import android.os.Build;
import android.os.Environment;
+import android.util.Log;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
@@ -43,7 +46,8 @@
class ImageArchive {
private static final String DIR_IN_SDCARD = "linux";
private static final String ARCHIVE_NAME = "images.tar.gz";
- private static final String HOST_URL = "https://dl.google.com/android/ferrochrome/latest";
+ private static final String BUILD_TAG = "latest"; // TODO: use actual tag name
+ private static final String HOST_URL = "https://dl.google.com/android/ferrochrome/" + BUILD_TAG;
// Only one can be non-null
private final URL mUrl;
@@ -138,6 +142,8 @@
* an additional input stream which will be used during the installation.
*/
public void installTo(Path dir, Function<InputStream, InputStream> filter) throws IOException {
+ String source = mPath != null ? mPath.toString() : mUrl.toString();
+ Log.d(TAG, "Installing. source: " + source + ", destination: " + dir.toString());
try (InputStream stream = getInputStream(filter);
GzipCompressorInputStream gzStream = new GzipCompressorInputStream(stream);
TarArchiveInputStream tarStream = new TarArchiveInputStream(gzStream)) {
@@ -148,9 +154,9 @@
Path to = dir.resolve(entry.getName());
if (Files.isDirectory(to)) {
Files.createDirectories(to);
- } else {
- Files.copy(tarStream, to, StandardCopyOption.REPLACE_EXISTING);
+ continue;
}
+ Files.copy(tarStream, to, StandardCopyOption.REPLACE_EXISTING);
}
}
commitInstallationAt(dir);
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java
index f318358..318f49a 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java
@@ -23,7 +23,9 @@
import android.system.Os;
import android.util.Log;
+import java.io.BufferedReader;
import java.io.FileDescriptor;
+import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
@@ -36,6 +38,7 @@
private static final String ROOTFS_FILENAME = "root_part";
private static final String BACKUP_FILENAME = "root_part_backup";
private static final String CONFIG_FILENAME = "vm_config.json";
+ private static final String BUILD_ID_FILENAME = "build_id";
static final String MARKER_FILENAME = "completed";
public static final long RESIZE_STEP_BYTES = 4 << 20; // 4 MiB
@@ -45,6 +48,7 @@
private final Path mBackup;
private final Path mConfig;
private final Path mMarker;
+ private String mBuildId;
/** Returns InstalledImage for a given app context */
public static InstalledImage getDefault(Context context) {
@@ -79,6 +83,26 @@
return mConfig;
}
+ /** Returns the build ID of the installed image */
+ public String getBuildId() {
+ if (mBuildId == null) {
+ mBuildId = readBuildId();
+ }
+ return mBuildId;
+ }
+
+ private String readBuildId() {
+ Path file = mDir.resolve(BUILD_ID_FILENAME);
+ if (!Files.exists(file)) {
+ return "<no build id>";
+ }
+ try (BufferedReader r = new BufferedReader(new FileReader(file.toFile()))) {
+ return r.readLine();
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to read build ID", e);
+ }
+ }
+
public Path uninstallAndBackup() throws IOException {
Files.delete(mMarker);
Files.move(mRootPartition, mBackup, StandardCopyOption.REPLACE_EXISTING);
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
index c2b3fd4..ac05d78 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
@@ -48,11 +48,6 @@
public class InstallerService extends Service {
private static final int NOTIFICATION_ID = 1313; // any unique number among notifications
- private static final String IMAGE_URL =
- Arrays.asList(Build.SUPPORTED_ABIS).contains("x86_64")
- ? "https://dl.google.com/android/ferrochrome/latest/x86_64/images.tar.gz"
- : "https://dl.google.com/android/ferrochrome/latest/aarch64/images.tar.gz";
-
private final Object mLock = new Object();
private Notification mNotification;
@@ -191,8 +186,6 @@
// TODO(b/374015561): Support pause/resume download
private boolean downloadFromUrl(boolean isWifiOnly) {
- Log.i(TAG, "trying to download from " + IMAGE_URL);
-
if (!checkForWifiOnly(isWifiOnly)) {
Log.e(TAG, "Install isn't started because Wifi isn't available");
notifyError(getString(R.string.installer_error_no_wifi));
diff --git a/android/compos_verify/verify.rs b/android/compos_verify/verify.rs
index a3f18d5..b94ebbc 100644
--- a/android/compos_verify/verify.rs
+++ b/android/compos_verify/verify.rs
@@ -124,6 +124,7 @@
&idsig_manifest_ext_apk,
&VmParameters {
name: String::from("ComposVerify"),
+ os: String::from("microdroid"),
cpu_topology: VmCpuTopology::OneCpu, // This VM runs very little work at boot
debug_mode: args.debug,
..Default::default()
diff --git a/android/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl b/android/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
index dde75e1..3748899 100644
--- a/android/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
+++ b/android/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
@@ -48,5 +48,6 @@
* callback, unless the returned ICompilationTask is cancelled. The caller should maintain
* a reference to the ICompilationTask until compilation completes or is cancelled.
*/
- ICompilationTask startTestCompile(ApexSource apexSource, ICompilationTaskCallback callback);
+ ICompilationTask startTestCompile(
+ ApexSource apexSource, ICompilationTaskCallback callback, String os);
}
diff --git a/android/composd/src/instance_manager.rs b/android/composd/src/instance_manager.rs
index 9e94035..d1b0b99 100644
--- a/android/composd/src/instance_manager.rs
+++ b/android/composd/src/instance_manager.rs
@@ -46,11 +46,12 @@
self.start_instance(CURRENT_INSTANCE_DIR, vm_parameters)
}
- pub fn start_test_instance(&self, prefer_staged: bool) -> Result<CompOsInstance> {
+ pub fn start_test_instance(&self, prefer_staged: bool, os: &str) -> Result<CompOsInstance> {
let mut vm_parameters = new_vm_parameters()?;
vm_parameters.name = String::from("ComposdTest");
vm_parameters.debug_mode = true;
vm_parameters.prefer_staged = prefer_staged;
+ vm_parameters.os = os.to_owned();
self.start_instance(TEST_INSTANCE_DIR, vm_parameters)
}
@@ -83,7 +84,8 @@
// number of dex2oat threads.
let cpu_topology = VmCpuTopology::MatchHost;
let memory_mib = Some(compos_memory_mib()?);
- Ok(VmParameters { cpu_topology, memory_mib, ..Default::default() })
+ let os = "microdroid".to_owned();
+ Ok(VmParameters { cpu_topology, memory_mib, os, ..Default::default() })
}
fn compos_memory_mib() -> Result<i32> {
diff --git a/android/composd/src/service.rs b/android/composd/src/service.rs
index 49cfd3a..3cc40af 100644
--- a/android/composd/src/service.rs
+++ b/android/composd/src/service.rs
@@ -60,6 +60,7 @@
&self,
apex_source: ApexSource,
callback: &Strong<dyn ICompilationTaskCallback>,
+ os: &str,
) -> binder::Result<Strong<dyn ICompilationTask>> {
check_permissions()?;
let prefer_staged = match apex_source {
@@ -67,7 +68,7 @@
ApexSource::PreferStaged => true,
_ => unreachable!("Invalid ApexSource {:?}", apex_source),
};
- to_binder_result(self.do_start_test_compile(prefer_staged, callback))
+ to_binder_result(self.do_start_test_compile(prefer_staged, callback, os))
}
}
@@ -93,9 +94,12 @@
&self,
prefer_staged: bool,
callback: &Strong<dyn ICompilationTaskCallback>,
+ os: &str,
) -> Result<Strong<dyn ICompilationTask>> {
- let comp_os =
- self.instance_manager.start_test_instance(prefer_staged).context("Starting CompOS")?;
+ let comp_os = self
+ .instance_manager
+ .start_test_instance(prefer_staged, os)
+ .context("Starting CompOS")?;
let target_dir_name = TEST_ARTIFACTS_SUBDIR.to_owned();
let task = OdrefreshTask::start(
diff --git a/android/composd_cmd/composd_cmd.rs b/android/composd_cmd/composd_cmd.rs
index 6d096a1..6281bd0 100644
--- a/android/composd_cmd/composd_cmd.rs
+++ b/android/composd_cmd/composd_cmd.rs
@@ -46,6 +46,10 @@
/// If any APEX is staged, prefer the staged version.
#[clap(long)]
prefer_staged: bool,
+
+ /// OS for the VM.
+ #[clap(long, default_value = "microdroid")]
+ os: String,
},
}
@@ -56,7 +60,7 @@
match action {
Actions::StagedApexCompile {} => run_staged_apex_compile()?,
- Actions::TestCompile { prefer_staged } => run_test_compile(prefer_staged)?,
+ Actions::TestCompile { prefer_staged, os } => run_test_compile(prefer_staged, &os)?,
}
println!("All Ok!");
@@ -116,9 +120,9 @@
run_async_compilation(|service, callback| service.startStagedApexCompile(callback))
}
-fn run_test_compile(prefer_staged: bool) -> Result<()> {
+fn run_test_compile(prefer_staged: bool, os: &str) -> Result<()> {
let apex_source = if prefer_staged { ApexSource::PreferStaged } else { ApexSource::NoStaged };
- run_async_compilation(|service, callback| service.startTestCompile(apex_source, callback))
+ run_async_compilation(|service, callback| service.startTestCompile(apex_source, callback, os))
}
fn run_async_compilation<F>(start_compile_fn: F) -> Result<()>
diff --git a/guest/pvmfw/src/entry.rs b/guest/pvmfw/src/entry.rs
index ce911b8..48585f3 100644
--- a/guest/pvmfw/src/entry.rs
+++ b/guest/pvmfw/src/entry.rs
@@ -129,7 +129,7 @@
page_table,
crosvm::MEM_START..layout::MAX_VIRT_ADDR,
crosvm::MMIO_RANGE,
- Some(memory::appended_payload_range()),
+ Some(layout::image_footer_range()),
));
let slices = memory::MemorySlices::new(
@@ -189,7 +189,7 @@
const SCTLR_EL1_VAL: u64 = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
- let scratch = layout::scratch_range();
+ let scratch = layout::data_bss_range();
assert_ne!(scratch.end - scratch.start, 0, "scratch memory is empty.");
assert_eq!(scratch.start.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
@@ -205,6 +205,12 @@
assert_eq!(stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
assert_eq!(stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
+ let eh_stack = layout::eh_stack_range();
+
+ assert_ne!(eh_stack.end - eh_stack.start, 0, "EH stack region is empty.");
+ assert_eq!(eh_stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region.");
+ assert_eq!(eh_stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region.");
+
// Zero all memory that could hold secrets and that can't be safely written to from Rust.
// Disable the exception vector, caches and page table and then jump to the payload at the
// given address, passing it the given FDT pointer.
@@ -250,6 +256,18 @@
"cmp {cache_line}, {stack_end}",
"b.lo 0b",
+ "mov {cache_line}, {eh_stack}",
+ // Zero EH stack region.
+ "0: stp xzr, xzr, [{eh_stack}], 16",
+ "cmp {eh_stack}, {eh_stack_end}",
+ "b.lo 0b",
+
+ // Flush d-cache over EH stack region.
+ "0: dc cvau, {cache_line}",
+ "add {cache_line}, {cache_line}, {dcache_line_size}",
+ "cmp {cache_line}, {eh_stack_end}",
+ "b.lo 0b",
+
"msr sctlr_el1, {sctlr_el1_val}",
"isb",
"mov x1, xzr",
@@ -293,6 +311,8 @@
scratch_end = in(reg) u64::try_from(scratch.end.0).unwrap(),
stack = in(reg) u64::try_from(stack.start.0).unwrap(),
stack_end = in(reg) u64::try_from(stack.end.0).unwrap(),
+ eh_stack = in(reg) u64::try_from(eh_stack.start.0).unwrap(),
+ eh_stack_end = in(reg) u64::try_from(eh_stack.end.0).unwrap(),
dcache_line_size = in(reg) u64::try_from(min_dcache_line_size()).unwrap(),
in("x0") fdt_address,
in("x30") payload_start,
@@ -306,7 +326,7 @@
/// This must only be called once, since we are returning a mutable reference.
/// The appended data region must be mapped.
unsafe fn get_appended_data_slice() -> &'static mut [u8] {
- let range = memory::appended_payload_range();
+ let range = layout::image_footer_range();
// SAFETY: This region is mapped and the linker script prevents it from overlapping with other
// objects.
unsafe { slice::from_raw_parts_mut(range.start.0 as *mut u8, range.end - range.start) }
diff --git a/guest/pvmfw/src/memory.rs b/guest/pvmfw/src/memory.rs
index 8e8b338..7d49bca 100644
--- a/guest/pvmfw/src/memory.rs
+++ b/guest/pvmfw/src/memory.rs
@@ -30,18 +30,9 @@
use log::warn;
use vmbase::{
layout::{self, crosvm},
- memory::{PageTable, MEMORY, SIZE_2MB, SIZE_4KB},
- util::align_up,
+ memory::{PageTable, MEMORY},
};
-/// Returns memory range reserved for the appended payload.
-pub fn appended_payload_range() -> Range<VirtualAddress> {
- let start = align_up(layout::binary_end().0, SIZE_4KB).unwrap();
- // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
- let end = align_up(start, SIZE_2MB).unwrap();
- VirtualAddress(start)..VirtualAddress(end)
-}
-
/// Region allocated for the stack.
pub fn stack_range() -> Range<VirtualAddress> {
const STACK_PAGES: usize = 12;
@@ -54,11 +45,12 @@
// Stack and scratch ranges are explicitly zeroed and flushed before jumping to payload,
// so dirty state management can be omitted.
- page_table.map_data(&layout::scratch_range().into())?;
+ page_table.map_data(&layout::data_bss_range().into())?;
+ page_table.map_data(&layout::eh_stack_range().into())?;
page_table.map_data(&stack_range().into())?;
page_table.map_code(&layout::text_range().into())?;
page_table.map_rodata(&layout::rodata_range().into())?;
- page_table.map_data_dbm(&appended_payload_range().into())?;
+ page_table.map_data_dbm(&layout::image_footer_range().into())?;
if let Err(e) = page_table.map_device(&layout::console_uart_page().into()) {
error!("Failed to remap the UART as a dynamic page table entry: {e}");
return Err(e);
diff --git a/guest/rialto/src/main.rs b/guest/rialto/src/main.rs
index ec26e0f..61e9948 100644
--- a/guest/rialto/src/main.rs
+++ b/guest/rialto/src/main.rs
@@ -73,7 +73,8 @@
fn new_page_table() -> Result<PageTable> {
let mut page_table = PageTable::default();
- page_table.map_data(&layout::scratch_range().into())?;
+ page_table.map_data(&layout::data_bss_range().into())?;
+ page_table.map_data(&layout::eh_stack_range().into())?;
page_table.map_data(&layout::stack_range(40 * PAGE_SIZE).into())?;
page_table.map_code(&layout::text_range().into())?;
page_table.map_rodata(&layout::rodata_range().into())?;
diff --git a/guest/vmbase_example/src/main.rs b/guest/vmbase_example/src/main.rs
index c7ef061..f00effa 100644
--- a/guest/vmbase_example/src/main.rs
+++ b/guest/vmbase_example/src/main.rs
@@ -37,7 +37,10 @@
bionic, configure_heap,
fdt::pci::PciInfo,
generate_image_header,
- layout::{console_uart_page, crosvm::FDT_MAX_SIZE, rodata_range, scratch_range, text_range},
+ layout::{
+ console_uart_page, crosvm::FDT_MAX_SIZE, data_bss_range, eh_stack_range, rodata_range,
+ text_range,
+ },
linker, logger, main,
memory::{PageTable, SIZE_64KB},
};
@@ -54,7 +57,8 @@
page_table.map_device(&console_uart_page().into())?;
page_table.map_code(&text_range().into())?;
page_table.map_rodata(&rodata_range().into())?;
- page_table.map_data(&scratch_range().into())?;
+ page_table.map_data(&data_bss_range().into())?;
+ page_table.map_data(&eh_stack_range().into())?;
page_table.map_data(&boot_stack_range().into())?;
info!("Activating IdMap...");
diff --git a/libs/libcompos_common/compos_client.rs b/libs/libcompos_common/compos_client.rs
index 316eaa9..6872582 100644
--- a/libs/libcompos_common/compos_client.rs
+++ b/libs/libcompos_common/compos_client.rs
@@ -58,6 +58,8 @@
pub struct VmParameters {
/// The name of VM for identifying.
pub name: String,
+ /// The OS of VM.
+ pub os: String,
/// Whether the VM should be debuggable.
pub debug_mode: bool,
/// CPU topology of the VM. Defaults to 1 vCPU.
@@ -129,6 +131,7 @@
let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
name: parameters.name.clone(),
+ osName: parameters.os.clone(),
apk: Some(apk_fd),
idsig: Some(idsig_fd),
instanceId: instance_id,
diff --git a/libs/libvmbase/sections.ld b/libs/libvmbase/sections.ld
index 5ca5ff4..222edae 100644
--- a/libs/libvmbase/sections.ld
+++ b/libs/libvmbase/sections.ld
@@ -56,17 +56,6 @@
} >image
rodata_end = .;
- .eh_stack (NOLOAD) : ALIGN(4096) {
- /*
- * Get stack overflow guard from the previous page being from
- * .rodata and mapped read-only or left unmapped.
- */
- eh_stack_limit = .;
- . += 4096;
- . = ALIGN(4096);
- init_eh_stack_pointer = .;
- } >writable_data
-
/*
* Collect together the read-write data including .bss at the end which
* will be zero'd by the entry code. This is page aligned so it can be
@@ -87,6 +76,13 @@
/* Everything beyond this point will not be included in the binary. */
bin_end = data_lma + SIZEOF(.data);
+ /* Data may be appended at load time to our binary. */
+ .image_footer (NOLOAD) : ALIGN(4096) {
+ image_footer_begin = .;
+ . = ALIGN(LENGTH(image));
+ image_footer_end = .;
+ } >image
+
/* The entry point code assumes that .bss is 16-byte aligned. */
.bss : ALIGN(16) {
bss_begin = .;
@@ -96,6 +92,18 @@
bss_end = .;
} >writable_data
+ /* Left unmapped, to catch overflows of the exception handler stack. */
+ .eh_stack_guard_page (NOLOAD) : ALIGN(4096) {
+ . += 4096;
+ } >writable_data
+
+ /* Exception handler stack, mapped read-write. */
+ .eh_stack (NOLOAD) : ALIGN(4096) {
+ eh_stack_limit = .;
+ . += 4096;
+ init_eh_stack_pointer = .;
+ } >writable_data
+
/* Left unmapped, to catch overflows of the stack. */
.stack_guard_page (NOLOAD) : ALIGN(4096) {
. += 4096;
diff --git a/libs/libvmbase/src/layout.rs b/libs/libvmbase/src/layout.rs
index adcb2fa..a8f7827 100644
--- a/libs/libvmbase/src/layout.rs
+++ b/libs/libvmbase/src/layout.rs
@@ -70,6 +70,11 @@
linker_region!(rodata_begin, rodata_end)
}
+/// Region which may contain a footer appended to the binary at load time.
+pub fn image_footer_range() -> Range<VirtualAddress> {
+ linker_region!(image_footer_begin, image_footer_end)
+}
+
/// Initialised writable data.
pub fn data_range() -> Range<VirtualAddress> {
linker_region!(data_begin, data_end)
@@ -80,6 +85,11 @@
linker_region!(bss_begin, bss_end)
}
+/// Writable data region for .data and .bss.
+pub fn data_bss_range() -> Range<VirtualAddress> {
+ linker_region!(data_begin, bss_end)
+}
+
/// Writable data region for the stack.
pub fn stack_range(stack_size: usize) -> Range<VirtualAddress> {
let end = linker_addr!(init_stack_pointer);
@@ -89,9 +99,9 @@
start..end
}
-/// All writable sections, excluding the stack.
-pub fn scratch_range() -> Range<VirtualAddress> {
- linker_region!(eh_stack_limit, bss_end)
+/// Writable data region for the exception handler stack.
+pub fn eh_stack_range() -> Range<VirtualAddress> {
+ linker_region!(eh_stack_limit, init_eh_stack_pointer)
}
/// Range of the page at UART_PAGE_ADDR of PAGE_SIZE.
diff --git a/libs/libvmbase/src/linker.rs b/libs/libvmbase/src/linker.rs
index 97bef3f..8654cf9 100644
--- a/libs/libvmbase/src/linker.rs
+++ b/libs/libvmbase/src/linker.rs
@@ -35,6 +35,12 @@
pub static dtb_end: u8;
/// First byte of the region available for the exception handler stack.
pub static eh_stack_limit: u8;
+ /// First byte of the `.image_footer` section.
+ pub static image_footer_begin: u8;
+ /// First byte beyond the `.image_footer` section.
+ pub static image_footer_end: u8;
+ /// First byte past the region available for the exception handler stack.
+ pub static init_eh_stack_pointer: u8;
/// First byte past the region available for the stack.
pub static init_stack_pointer: u8;
/// First byte of the `.rodata` section.