Merge "Native API fixes"
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index d14d83c..592a751 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -88,9 +88,6 @@
}
public class VirtualMachineException extends java.lang.Exception {
- ctor public VirtualMachineException(@Nullable String);
- ctor public VirtualMachineException(@Nullable String, @Nullable Throwable);
- ctor public VirtualMachineException(@Nullable Throwable);
}
public class VirtualMachineManager {
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineException.java b/javalib/src/android/system/virtualmachine/VirtualMachineException.java
index 985eb70..9948fda 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineException.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineException.java
@@ -26,15 +26,15 @@
*/
@SystemApi
public class VirtualMachineException extends Exception {
- public VirtualMachineException(@Nullable String message) {
+ VirtualMachineException(@Nullable String message) {
super(message);
}
- public VirtualMachineException(@Nullable String message, @Nullable Throwable cause) {
+ VirtualMachineException(@Nullable String message, @Nullable Throwable cause) {
super(message, cause);
}
- public VirtualMachineException(@Nullable Throwable cause) {
+ VirtualMachineException(@Nullable Throwable cause) {
super(cause);
}
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
index ea0a305..5b30617 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
@@ -150,6 +150,11 @@
* Returns an existing {@link VirtualMachine} with the given name. Returns null if there is no
* such virtual machine.
*
+ * <p>There is at most one {@code VirtualMachine} object corresponding to a given virtual
+ * machine instance. Multiple calls to get() passing the same name will get the same object
+ * returned, until the virtual machine is deleted (via {@link #delete}) and then recreated.
+ *
+ * @see #getOrCreate
* @throws VirtualMachineException if the virtual machine exists but could not be successfully
* retrieved.
* @hide
diff --git a/pvmfw/src/hvc.rs b/pvmfw/src/hvc.rs
new file mode 100644
index 0000000..66f7977
--- /dev/null
+++ b/pvmfw/src/hvc.rs
@@ -0,0 +1,66 @@
+// 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.
+
+//! Wrappers around calls to the hypervisor.
+
+use crate::smccc::{self, checked_hvc64, checked_hvc64_expect_zero};
+use log::info;
+
+const VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID: u32 = 0xc6000005;
+const VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID: u32 = 0xc6000006;
+const VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID: u32 = 0xc6000007;
+const VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID: u32 = 0xc6000008;
+
+pub fn mmio_guard_info() -> smccc::Result<u64> {
+ let args = [0u64; 17];
+
+ checked_hvc64(VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, args)
+}
+
+pub fn mmio_guard_enroll() -> smccc::Result<()> {
+ let args = [0u64; 17];
+
+ checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, args)
+}
+
+pub fn mmio_guard_map(ipa: u64) -> smccc::Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = ipa;
+
+ // TODO(b/253586500): pKVM currently returns a i32 instead of a i64.
+ let is_i32_error_code = |n| u32::try_from(n).ok().filter(|v| (*v as i32) < 0).is_some();
+ match checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args) {
+ Err(smccc::Error::Unexpected(e)) if is_i32_error_code(e) => {
+ info!("Handled a pKVM bug by interpreting the MMIO_GUARD_MAP return value as i32");
+ match e as u32 as i32 {
+ -1 => Err(smccc::Error::NotSupported),
+ -2 => Err(smccc::Error::NotRequired),
+ -3 => Err(smccc::Error::InvalidParameter),
+ ret => Err(smccc::Error::Unknown(ret as i64)),
+ }
+ }
+ res => res,
+ }
+}
+
+pub fn mmio_guard_unmap(ipa: u64) -> smccc::Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = ipa;
+
+ // TODO(b/251426790): pKVM currently returns NOT_SUPPORTED for SUCCESS.
+ match checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args) {
+ Err(smccc::Error::NotSupported) | Ok(_) => Ok(()),
+ x => x,
+ }
+}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 79b6f57..2f1b42a 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -26,6 +26,7 @@
mod fdt;
mod heap;
mod helpers;
+mod hvc;
mod memory;
mod mmio_guard;
mod mmu;
diff --git a/pvmfw/src/mmio_guard.rs b/pvmfw/src/mmio_guard.rs
index 28f928f..e5f376e 100644
--- a/pvmfw/src/mmio_guard.rs
+++ b/pvmfw/src/mmio_guard.rs
@@ -15,9 +15,9 @@
//! Safe MMIO_GUARD support.
use crate::helpers;
+use crate::hvc::{mmio_guard_enroll, mmio_guard_info, mmio_guard_map, mmio_guard_unmap};
use crate::smccc;
use core::{fmt, result};
-use log::info;
#[derive(Debug, Clone)]
pub enum Error {
@@ -63,50 +63,3 @@
pub fn unmap(addr: usize) -> Result<()> {
mmio_guard_unmap(helpers::page_4kb_of(addr) as u64).map_err(Error::UnmapFailed)
}
-
-fn mmio_guard_info() -> smccc::Result<u64> {
- const VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID: u32 = 0xc6000005;
- let args = [0u64; 17];
-
- smccc::checked_hvc64(VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, args)
-}
-
-fn mmio_guard_enroll() -> smccc::Result<()> {
- const VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID: u32 = 0xc6000006;
- let args = [0u64; 17];
-
- smccc::checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, args)
-}
-
-fn mmio_guard_map(ipa: u64) -> smccc::Result<()> {
- const VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID: u32 = 0xc6000007;
- let mut args = [0u64; 17];
- args[0] = ipa;
-
- // TODO(b/253586500): pKVM currently returns a i32 instead of a i64.
- let is_i32_error_code = |n| u32::try_from(n).ok().filter(|v| (*v as i32) < 0).is_some();
- match smccc::checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args) {
- Err(smccc::Error::Unexpected(e)) if is_i32_error_code(e) => {
- info!("Handled a pKVM bug by interpreting the MMIO_GUARD_MAP return value as i32");
- match e as u32 as i32 {
- -1 => Err(smccc::Error::NotSupported),
- -2 => Err(smccc::Error::NotRequired),
- -3 => Err(smccc::Error::InvalidParameter),
- ret => Err(smccc::Error::Unknown(ret as i64)),
- }
- }
- res => res,
- }
-}
-
-fn mmio_guard_unmap(ipa: u64) -> smccc::Result<()> {
- const VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID: u32 = 0xc6000008;
- let mut args = [0u64; 17];
- args[0] = ipa;
-
- // TODO(b/251426790): pKVM currently returns NOT_SUPPORTED for SUCCESS.
- match smccc::checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args) {
- Err(smccc::Error::NotSupported) | Ok(_) => Ok(()),
- x => x,
- }
-}
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 5f24c4b..eb756d8 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -258,14 +258,7 @@
assertThrowsVmExceptionContaining(() -> vm.toDescriptor(), "deleted");
// This is indistinguishable from the VM having never existed, so the message
// is non-specific.
- assertThrows(
- VirtualMachineException.class, () -> getVirtualMachineManager().delete("test_vm"));
- }
-
- private void assertThrowsVmExceptionContaining(
- ThrowingRunnable runnable, String expectedContents) {
- Exception e = assertThrows(VirtualMachineException.class, runnable);
- assertThat(e).hasMessageThat().contains(expectedContents);
+ assertThrowsVmException(() -> getVirtualMachineManager().delete("test_vm"));
}
@Test
@@ -423,6 +416,48 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
+ public void vmmGetAndCreate() throws Exception {
+ assumeSupportedKernel();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilder()
+ .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
+ .setMemoryMib(minMemoryRequired())
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
+
+ VirtualMachineManager vmm = getVirtualMachineManager();
+ String vmName = "vmName";
+
+ // VM does not yet exist
+ assertThat(vmm.get(vmName)).isNull();
+
+ VirtualMachine vm1 = vmm.create(vmName, config);
+
+ // Now it does, and we should get the same instance back
+ assertThat(vmm.get(vmName)).isSameInstanceAs(vm1);
+ assertThat(vmm.getOrCreate(vmName, config)).isSameInstanceAs(vm1);
+
+ // Can't recreate it though
+ assertThrowsVmException(() -> vmm.create(vmName, config));
+
+ vmm.delete(vmName);
+ assertThat(vmm.get(vmName)).isNull();
+
+ // Now that we deleted the old one, this should create rather than get, and it should be a
+ // new instance.
+ VirtualMachine vm2 = vmm.getOrCreate(vmName, config);
+ assertThat(vm2).isNotSameInstanceAs(vm1);
+
+ // Subsequent gets should return this new one.
+ assertThat(vmm.get(vmName)).isSameInstanceAs(vm2);
+ assertThat(vmm.getOrCreate(vmName, config)).isSameInstanceAs(vm2);
+
+ vmm.delete(vmName);
+ }
+
+ @Test
+ @CddTest(requirements = {"9.17/C-1-1"})
public void vmFilesStoredInDeDirWhenCreatedFromDEContext() throws Exception {
final Context ctx = getContext().createDeviceProtectedStorageContext();
final int userId = ctx.getUserId();
@@ -511,10 +546,10 @@
assertThat(vmm.get("test_vm_delete")).isNull();
// Can't start the VM even with an existing reference
- assertThrows(VirtualMachineException.class, vm::run);
+ assertThrowsVmException(vm::run);
// Can't delete the VM since it no longer exists
- assertThrows(VirtualMachineException.class, () -> vmm.delete("test_vm_delete"));
+ assertThrowsVmException(() -> vmm.delete("test_vm_delete"));
}
@Test
@@ -1098,6 +1133,16 @@
return filePath.toFile();
}
+ private void assertThrowsVmException(ThrowingRunnable runnable) {
+ assertThrows(VirtualMachineException.class, runnable);
+ }
+
+ private void assertThrowsVmExceptionContaining(
+ ThrowingRunnable runnable, String expectedContents) {
+ Exception e = assertThrows(VirtualMachineException.class, runnable);
+ assertThat(e).hasMessageThat().contains(expectedContents);
+ }
+
private int minMemoryRequired() {
if (Build.SUPPORTED_ABIS.length > 0) {
String primaryAbi = Build.SUPPORTED_ABIS[0];