Merge "Use shared preference to store forwarding ports" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 2112125..8a0711c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -72,6 +72,9 @@
"ferrochrome-postsubmit": [
{
"name": "ferrochrome-tests"
+ },
+ {
+ "name": "TerminalAppTests"
}
],
"postsubmit": [
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index 57b6ff2..c006e7b 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -456,11 +456,16 @@
resizeDiskIfNecessary();
- // TODO: implement intent for setting, close and tap to the notification
- // Currently mock a PendingIntent for notification.
- Intent intent = new Intent();
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ Intent tapIntent = new Intent(this, MainActivity.class);
+ tapIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ PendingIntent tapPendingIntent = PendingIntent.getActivity(this, 0, tapIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ Intent settingsIntent = new Intent(this, SettingsActivity.class);
+ settingsIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ PendingIntent settingsPendingIntent = PendingIntent.getActivity(this, 0, settingsIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
Intent stopIntent = new Intent();
stopIntent.setClass(this, VmLauncherService.class);
stopIntent.setAction(VmLauncherServices.ACTION_STOP_VM_LAUNCHER_SERVICE);
@@ -479,7 +484,7 @@
getResources().getString(R.string.service_notification_title))
.setContentText(
getResources().getString(R.string.service_notification_content))
- .setContentIntent(pendingIntent)
+ .setContentIntent(tapPendingIntent)
.setOngoing(true)
.addAction(
new Notification.Action.Builder(
@@ -488,7 +493,7 @@
.getString(
R.string
.service_notification_settings),
- pendingIntent)
+ settingsPendingIntent)
.build())
.addAction(
new Notification.Action.Builder(
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
index 7256015..95bcbbc 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
@@ -15,18 +15,49 @@
*/
package com.android.virtualization.terminal
+import android.content.Intent
import android.os.Bundle
-import android.widget.Toast
+import android.util.Log
import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.lifecycleScope
+import com.android.virtualization.vmlauncher.InstallUtils
import com.google.android.material.card.MaterialCardView
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import java.io.IOException
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+const val TAG: String = "VmTerminalApp"
class SettingsRecoveryActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_recovery)
val resetCard = findViewById<MaterialCardView>(R.id.settings_recovery_reset_card)
+ val dialog = MaterialAlertDialogBuilder(this)
+ .setTitle(R.string.settings_recovery_reset_dialog_title)
+ .setMessage(R.string.settings_recovery_reset_dialog_message)
+ .setPositiveButton(R.string.settings_recovery_reset_dialog_confirm) { _, _ ->
+ // This coroutine will be killed when the activity is killed. The behavior is both acceptable
+ // either removing is done or not
+ lifecycleScope.launch(Dispatchers.IO) {
+ try {
+ InstallUtils.unInstall(this@SettingsRecoveryActivity)
+ // Restart terminal
+ val intent =
+ baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName)
+ intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ finish()
+ startActivity(intent)
+ } catch (e: IOException) {
+ Log.e(TAG, "VM image reset failed.")
+ }
+ }
+ }
+ .setNegativeButton(R.string.settings_recovery_reset_dialog_cancel) { dialog, _ -> dialog.dismiss() }
+ .create()
resetCard.setOnClickListener {
- Toast.makeText(this@SettingsRecoveryActivity, R.string.settings_recovery_reset_message, Toast.LENGTH_SHORT).show()
+ dialog.show()
}
}
}
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index 070807c..ca803ec 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -87,8 +87,14 @@
<string name="settings_recovery_reset_title">Change to Initial version</string>
<!-- Settings menu subtitle for resetting the virtual machine image [CHAR LIMIT=none] -->
<string name="settings_recovery_reset_sub_title">Remove all</string>
- <!-- Toast message for reset is completed [CHAR LIMIT=none] -->
- <string name="settings_recovery_reset_message">VM reset</string>
+ <!-- Dialog title for restarting the terminal [CHAR LIMIT=none] -->
+ <string name="settings_recovery_reset_dialog_title">Reset the virtual machine</string>
+ <!-- Dialog message for restarting the terminal [CHAR LIMIT=none] -->
+ <string name="settings_recovery_reset_dialog_message">Data will be deleted.</string>
+ <!-- Dialog button confirm for restarting the terminal [CHAR LIMIT=16] -->
+ <string name="settings_recovery_reset_dialog_confirm">Confirm</string>
+ <!-- Dialog button cancel for restarting the terminal [CHAR LIMIT=16] -->
+ <string name="settings_recovery_reset_dialog_cancel">Cancel</string>
<!-- Notification action button for settings [CHAR LIMIT=none] -->
<string name="service_notification_settings">Settings</string>
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 0bff52e..e940e71 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -174,6 +174,12 @@
"true": ["virtualizationservice.xml"],
default: unset,
}),
+ required: select(release_flag("RELEASE_AVF_SUPPORT_CUSTOM_VM_WITH_PARAVIRTUALIZED_DEVICES"), {
+ true: [
+ "default-permissions_com.android.virt.xml",
+ ],
+ default: [],
+ }),
}
apex_defaults {
diff --git a/build/apex/permissions/Android.bp b/build/apex/permissions/Android.bp
index 678a4f2..d773df6 100644
--- a/build/apex/permissions/Android.bp
+++ b/build/apex/permissions/Android.bp
@@ -23,3 +23,9 @@
src: "features_com.android.virt.xml",
soc_specific: true,
}
+
+prebuilt_etc {
+ name: "default-permissions_com.android.virt.xml",
+ sub_dir: "default-permissions",
+ src: "default-permissions_com.android.virt.xml",
+}
diff --git a/build/apex/permissions/default-permissions_com.android.virt.xml b/build/apex/permissions/default-permissions_com.android.virt.xml
new file mode 100644
index 0000000..ac15708
--- /dev/null
+++ b/build/apex/permissions/default-permissions_com.android.virt.xml
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<!--
+ Copyright (C) 2024 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
+-->
+
+<!--
+ This file contains permissions to be granted by default. Default
+ permissions are granted to special platform components and to apps
+ that are approved to get default grants. The special components
+ are apps that are expected tto work out-of-the-box as they provide
+ core use cases such as default dialer, default email, etc. These
+ grants are managed by the platform. The apps that are additionally
+ approved for default grants are ones that provide carrier specific
+ functionality, ones legally required at some location, ones providing
+ alternative disclosure and opt-out UI, ones providing highlight features
+ of a dedicated device, etc. This file contains only the latter exceptions.
+ Fixed permissions cannot be controlled by the user and need a special
+ approval. Typically these are to ensure either legally mandated functions
+ or the app is considered a part of the OS.
+-->
+<exceptions>
+ <!-- This is an example of an exception:
+ <exception
+ package="foo.bar.permission"
+ <permission name="android.permission.READ_CONTACTS" fixed="true"/>
+ <permission name="android.permission.READ_CALENDAR" fixed="false"/>
+ </exception>
+ -->
+ <exception package="com.android.virtualization.terminal">
+ <permission name="android.permission.POST_NOTIFICATIONS" fixed="true"/>
+ </exception>
+</exceptions>
diff --git a/build/debian/build.sh b/build/debian/build.sh
index 5829ecc..b4436c1 100755
--- a/build/debian/build.sh
+++ b/build/debian/build.sh
@@ -104,6 +104,7 @@
source "$HOME"/.cargo/env
rustup target add "${arch}"-unknown-linux-gnu
+ cargo install cargo-license
}
download_debian_cloud_image() {
@@ -124,6 +125,9 @@
mkdir -p "${dst}/files/usr/local/bin/$1"
cp "${workdir}/$1/${arch}-unknown-linux-gnu/debug/$1" "${dst}/files/usr/local/bin/$1/AVF"
chmod 777 "${dst}/files/usr/local/bin/$1/AVF"
+
+ mkdir -p "${dst}/files/usr/share/doc/$1"
+ cargo license > "${dst}/files/usr/share/doc/$1/copyright"
popd > /dev/null
}
@@ -140,6 +144,8 @@
mkdir -p "${dst}/files/usr/local/bin/ttyd"
cp /tmp/stage/${arch}-linux-musl/bin/ttyd "${dst}/files/usr/local/bin/ttyd/AVF"
chmod 777 "${dst}/files/usr/local/bin/ttyd/AVF"
+ mkdir -p "${dst}/files/usr/share/doc/ttyd"
+ cp LICENSE "${dst}/files/usr/share/doc/ttyd/copyright"
popd > /dev/null
popd > /dev/null
}
diff --git a/build/debian/fai_config/files/etc/systemd/system/forwarder_guest_launcher.service/AVF b/build/debian/fai_config/files/etc/systemd/system/forwarder_guest_launcher.service/AVF
index 251f88c..4c1b2f5 100644
--- a/build/debian/fai_config/files/etc/systemd/system/forwarder_guest_launcher.service/AVF
+++ b/build/debian/fai_config/files/etc/systemd/system/forwarder_guest_launcher.service/AVF
@@ -2,8 +2,9 @@
Description=Port forwarding service in guest VM
After=syslog.target
After=network.target
+After=virtiofs_internal.service
[Service]
-ExecStart=/usr/local/bin/forwarder_guest_launcher --host 192.168.0.1
+ExecStart=/usr/local/bin/forwarder_guest_launcher --host 192.168.0.1 --grpc_port $(cat /mnt/internal/debian_service_port)
Type=simple
Restart=on-failure
RestartSec=1
diff --git a/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF b/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF
index 7d163fb..81347a7 100644
--- a/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF
+++ b/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF
@@ -3,8 +3,9 @@
After=syslog.target
After=network.target
Requires=ttyd.service
+After=virtiofs_internal.service
[Service]
-ExecStart=/usr/local/bin/ip_addr_reporter
+ExecStart=/usr/local/bin/ip_addr_reporter --grpc_port $(cat /mnt/internal/debian_service_port)
Type=simple
Restart=on-failure
User=root
diff --git a/build/debian/vm_config.json.aarch64 b/build/debian/vm_config.json.aarch64
index f31c5b7..8e16093 100644
--- a/build/debian/vm_config.json.aarch64
+++ b/build/debian/vm_config.json.aarch64
@@ -12,7 +12,7 @@
"sharedPath": "/storage/emulated"
},
{
- "sharedPath": "/data/data/$PACKAGE_NAME/files"
+ "sharedPath": "/data/user/$USER_ID/$PACKAGE_NAME/files"
}
],
"protected": false,
diff --git a/build/debian/vm_config.json.x86_64 b/build/debian/vm_config.json.x86_64
index 12f99c3..09e04b9 100644
--- a/build/debian/vm_config.json.x86_64
+++ b/build/debian/vm_config.json.x86_64
@@ -12,7 +12,7 @@
"sharedPath": "/storage/emulated"
},
{
- "sharedPath": "/data/data/$PACKAGE_NAME/files"
+ "sharedPath": "/data/user/$USER_ID/$PACKAGE_NAME/files"
}
],
"kernel": "$PAYLOAD_DIR/vmlinuz",
diff --git a/guest/forwarder_guest/Cargo.toml b/guest/forwarder_guest/Cargo.toml
index 65f57cf..ce50e4c 100644
--- a/guest/forwarder_guest/Cargo.toml
+++ b/guest/forwarder_guest/Cargo.toml
@@ -2,6 +2,7 @@
name = "forwarder_guest"
version = "0.1.0"
edition = "2021"
+license = "Apache-2.0"
[dependencies]
clap = { version = "4.5.19", features = ["derive"] }
diff --git a/guest/forwarder_guest_launcher/Cargo.toml b/guest/forwarder_guest_launcher/Cargo.toml
index 03d3f7f..b7f9eaf 100644
--- a/guest/forwarder_guest_launcher/Cargo.toml
+++ b/guest/forwarder_guest_launcher/Cargo.toml
@@ -2,6 +2,7 @@
name = "forwarder_guest_launcher"
version = "0.1.0"
edition = "2021"
+license = "Apache-2.0"
[dependencies]
anyhow = "1.0.91"
diff --git a/guest/forwarder_guest_launcher/src/main.rs b/guest/forwarder_guest_launcher/src/main.rs
index 59ee8c6..d753d19 100644
--- a/guest/forwarder_guest_launcher/src/main.rs
+++ b/guest/forwarder_guest_launcher/src/main.rs
@@ -33,13 +33,17 @@
#[arg(long)]
#[arg(alias = "host")]
host_addr: String,
+ /// grpc port number
+ #[arg(long)]
+ #[arg(alias = "grpc_port")]
+ grpc_port: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Starting forwarder_guest_launcher");
let args = Args::parse();
- let addr = format!("https://{}:12000", args.host_addr);
+ let addr = format!("https://{}:{}", args.host_addr, args.grpc_port);
let channel = Endpoint::from_shared(addr)?.connect().await?;
let mut client = DebianServiceClient::new(channel);
diff --git a/guest/ip_addr_reporter/Cargo.toml b/guest/ip_addr_reporter/Cargo.toml
index e255eaf..7592e3f 100644
--- a/guest/ip_addr_reporter/Cargo.toml
+++ b/guest/ip_addr_reporter/Cargo.toml
@@ -2,8 +2,10 @@
name = "ip_addr_reporter"
version = "0.1.0"
edition = "2021"
+license = "Apache-2.0"
[dependencies]
+clap = { version = "4.5.20", features = ["derive"] }
netdev = "0.31.0"
prost = "0.13.3"
tokio = { version = "1.40.0", features = ["rt-multi-thread"] }
diff --git a/guest/ip_addr_reporter/src/main.rs b/guest/ip_addr_reporter/src/main.rs
index 5784a83..2c782d3 100644
--- a/guest/ip_addr_reporter/src/main.rs
+++ b/guest/ip_addr_reporter/src/main.rs
@@ -1,17 +1,27 @@
use api::debian_service_client::DebianServiceClient;
use api::IpAddr;
+use clap::Parser;
pub mod api {
tonic::include_proto!("com.android.virtualization.vmlauncher.proto");
}
+#[derive(Parser)]
+/// Flags for running command
+pub struct Args {
+ /// grpc port number
+ #[arg(long)]
+ #[arg(alias = "grpc_port")]
+ grpc_port: String,
+}
+
#[tokio::main]
async fn main() -> Result<(), String> {
+ let args = Args::parse();
let gateway_ip_addr = netdev::get_default_gateway()?.ipv4[0];
let ip_addr = netdev::get_default_interface()?.ipv4[0].addr();
- const PORT: i32 = 12000;
- let server_addr = format!("http://{}:{}", gateway_ip_addr.to_string(), PORT);
+ let server_addr = format!("http://{}:{}", gateway_ip_addr.to_string(), args.grpc_port);
println!("local ip addr: {}", ip_addr.to_string());
println!("coonect to grpc server {}", server_addr);
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/InstallUtils.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/InstallUtils.java
index 1febe27..0e46a63 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/InstallUtils.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/InstallUtils.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.os.Environment;
+import android.os.FileUtils;
import android.util.Log;
import org.apache.commons.compress.archivers.ArchiveEntry;
@@ -49,6 +50,10 @@
return Files.exists(getInstallationCompletedPath(context));
}
+ public static void unInstall(Context context) throws IOException {
+ Files.delete(getInstallationCompletedPath(context));
+ }
+
public static boolean createInstalledMarker(Context context) {
try {
File file = new File(getInstallationCompletedPath(context).toString());
@@ -59,6 +64,10 @@
}
}
+ public static void deleteInstallation(Context context) {
+ FileUtils.deleteContentsAndDir(getInternalStorageDir(context));
+ }
+
private static Path getPayloadPath() {
File payloadDir = Environment.getExternalStoragePublicDirectory(PAYLOAD_DIR);
if (payloadDir == null) {
@@ -130,6 +139,7 @@
private static Function<String, String> getReplacer(Context context) {
Map<String, String> rules = new HashMap<>();
rules.put("\\$PAYLOAD_DIR", new File(context.getFilesDir(), PAYLOAD_DIR).toString());
+ rules.put("\\$USER_ID", String.valueOf(context.getUserId()));
rules.put("\\$PACKAGE_NAME", context.getPackageName());
return (s) -> {
for (Map.Entry<String, String> rule : rules.entrySet()) {
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
index 5cd7b92..f672b7b 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
@@ -27,11 +27,20 @@
import android.system.virtualmachine.VirtualMachineException;
import android.util.Log;
+import io.grpc.Grpc;
import io.grpc.InsecureServerCredentials;
+import io.grpc.Metadata;
import io.grpc.Server;
+import io.grpc.ServerCall;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import io.grpc.Status;
import io.grpc.okhttp.OkHttpServerBuilder;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
@@ -137,19 +146,54 @@
}
private void startDebianServer() {
+ ServerInterceptor interceptor =
+ new ServerInterceptor() {
+ @Override
+ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
+ ServerCall<ReqT, RespT> call,
+ Metadata headers,
+ ServerCallHandler<ReqT, RespT> next) {
+ // Refer to VirtualizationSystemService.TetheringService
+ final String VM_STATIC_IP_ADDR = "192.168.0.2";
+ InetSocketAddress remoteAddr =
+ (InetSocketAddress)
+ call.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR);
+
+ if (remoteAddr != null
+ && Objects.equals(
+ remoteAddr.getAddress().getHostAddress(),
+ VM_STATIC_IP_ADDR)) {
+ // Allow the request only if it is from VM
+ return next.startCall(call, headers);
+ }
+ Log.d(TAG, "blocked grpc request from " + remoteAddr);
+ call.close(Status.Code.PERMISSION_DENIED.toStatus(), new Metadata());
+ return new ServerCall.Listener<ReqT>() {};
+ }
+ };
new Thread(
() -> {
- // TODO(b/372666638): gRPC for java doesn't support vsock for now.
- // In addition, let's consider using a dynamic port and SSL(and client
- // certificate)
- int port = 12000;
try {
+ // TODO(b/372666638): gRPC for java doesn't support vsock for now.
+ int port = 0;
mServer =
OkHttpServerBuilder.forPort(
port, InsecureServerCredentials.create())
+ .intercept(interceptor)
.addService(new DebianServiceImpl(this))
.build()
.start();
+
+ // TODO(b/373533555): we can use mDNS for that.
+ String debianServicePortFileName = "debian_service_port";
+ File debianServicePortFile =
+ new File(getFilesDir(), debianServicePortFileName);
+ try (FileOutputStream writer =
+ new FileOutputStream(debianServicePortFile)) {
+ writer.write(String.valueOf(mServer.getPort()).getBytes());
+ } catch (IOException e) {
+ Log.d(TAG, "cannot write grpc port number", e);
+ }
} catch (IOException e) {
Log.d(TAG, "grpc server error", e);
}
diff --git a/tests/Terminal/Android.bp b/tests/Terminal/Android.bp
new file mode 100644
index 0000000..745a47c
--- /dev/null
+++ b/tests/Terminal/Android.bp
@@ -0,0 +1,22 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "TerminalAppTests",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner.stubs.system",
+ "android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "junit",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ instrumentation_for: "VmTerminalApp",
+}
diff --git a/tests/Terminal/AndroidManifest.xml b/tests/Terminal/AndroidManifest.xml
new file mode 100644
index 0000000..0abcb5c
--- /dev/null
+++ b/tests/Terminal/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.virtualization.terminal.test">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.google.android.virtualization.terminal"
+ android:label="Test for Linux Terminal"/>
+</manifest>
diff --git a/tests/Terminal/AndroidTest.xml b/tests/Terminal/AndroidTest.xml
new file mode 100644
index 0000000..e9f549f
--- /dev/null
+++ b/tests/Terminal/AndroidTest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 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.
+-->
+<configuration description="Runs Linux Terminal test">
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="TerminalAppTests.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="true"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="throw-if-cmd-fail" value="true" />
+ <option name="run-command"
+ value="pm enable com.google.android.virtualization.terminal/com.android.virtualization.terminal.MainActivity" />
+ <option name="teardown-command"
+ value="pm disable com.google.android.virtualization.terminal/com.android.virtualization.terminal.MainActivity" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.virtualization.terminal.test"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration>
diff --git a/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java b/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
new file mode 100644
index 0000000..64ef79f
--- /dev/null
+++ b/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+package com.android.virtualization.terminal;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.virtualization.vmlauncher.InstallUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TerminalAppTest {
+ private Instrumentation mInstr;
+ private Context mTargetContext;
+
+ @Before
+ public void setup() {
+ mInstr = InstrumentationRegistry.getInstrumentation();
+ mTargetContext = mInstr.getTargetContext();
+ installVmImage();
+ }
+
+ private void installVmImage() {
+ final long INSTALL_TIMEOUT_MILLIS = 300_000; // 5 min
+
+ Intent intent = new Intent(mTargetContext, InstallerActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(InstallerActivity.EXTRA_AUTO_DOWNLOAD, true);
+
+ if (mInstr.startActivitySync(intent) instanceof InstallerActivity activity) {
+ assertTrue(
+ "Failed to install VM image",
+ activity.waitForInstallCompleted(INSTALL_TIMEOUT_MILLIS));
+ }
+ }
+
+ @Test
+ public void boot() throws Exception {
+ final long BOOT_TIMEOUT_MILLIS = 30_000; // 30 sec
+
+ Intent intent = new Intent(mTargetContext, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ if (mInstr.startActivitySync(intent) instanceof MainActivity activity) {
+ assertTrue("Failed to boot in 30s", activity.waitForBootCompleted(BOOT_TIMEOUT_MILLIS));
+ }
+ }
+
+ @After
+ public void tearDown() {
+ InstallUtils.deleteInstallation(mTargetContext);
+ }
+}
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 0f2fe58..4878006 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -26,6 +26,7 @@
":microdroid_general_sepolicy.conf",
":test.com.android.virt.pem",
":test2.com.android.virt.pem",
+ "java/**/goldens/dt_dump_*",
],
data_native_bins: [
"sepolicy-analyze",
@@ -38,6 +39,7 @@
"lz4",
"sign_virt_apex",
"simg2img",
+ "dtc",
],
// java_test_host doesn't have data_native_libs but jni_libs can be used to put
// native modules under ./lib directory.
@@ -48,6 +50,7 @@
"libcrypto_utils",
"libcrypto",
"libext4_utils",
+ "libfdt",
"liblog",
"liblp",
"libsparse",
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 0f77f7a..20f71e5 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -1344,6 +1344,138 @@
}
}
+ @Test
+ @Parameters(method = "gkiVersions")
+ @TestCaseName("{method}_os_{0}")
+ @Ignore("b/360388014") // TODO(b/360388014): fix & re-enable
+ public void microdroidDeviceTreeCompat(String os) throws Exception {
+ assumeArm64Supported();
+ final String configPath = "assets/vm_config.json";
+ // Preconditions
+ assumeKernelSupported(os);
+ int mem_size = 256;
+ assertTrue("Memory size too small", mem_size >= minMemorySize());
+
+ // Start the VM with the dump DT option.
+ mMicrodroidDevice =
+ MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+ .debugLevel("full")
+ .memoryMib(mem_size)
+ .cpuTopology("one_cpu")
+ .protectedVm(false)
+ .os(SUPPORTED_OSES.get(os))
+ .name("test_device_tree")
+ .dumpDt("/data/local/tmp/dump_dt.dtb")
+ .build(getAndroidDevice());
+ assertThat(mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT)).isTrue();
+
+ File goldenDt = findTestFile("dt_dump_golden.dts");
+ testGoldenDeviceTree(goldenDt.getAbsolutePath());
+ }
+
+ @Test
+ @Parameters(method = "gkiVersions")
+ @TestCaseName("{method}_os_{0}")
+ @Ignore("b/360388014") // TODO(b/360388014): fix & re-enable
+ public void microdroidProtectedDeviceTreeCompat(String os) throws Exception {
+ assumeArm64Supported();
+ final String configPath = "assets/vm_config.json";
+ // Preconditions
+ assumeKernelSupported(os);
+ assumeVmTypeSupported(true);
+ int mem_size = 256;
+ assertTrue("Memory size too small", mem_size >= minMemorySize());
+
+ // Start the VM with the dump DT option.
+ mMicrodroidDevice =
+ MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+ .debugLevel("full")
+ .memoryMib(mem_size)
+ .cpuTopology("one_cpu")
+ .protectedVm(true)
+ .os(SUPPORTED_OSES.get(os))
+ .name("test_device_tree")
+ .dumpDt("/data/local/tmp/dump_dt.dtb")
+ .build(getAndroidDevice());
+ assertThat(mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT)).isTrue();
+
+ File goldenDt = findTestFile("dt_dump_protected_golden.dts");
+ testGoldenDeviceTree(goldenDt.getAbsolutePath());
+ }
+
+ private void testGoldenDeviceTree(String goldenDt) throws Exception {
+ // Pull the device tree to host.
+ TestDevice device = getAndroidDevice();
+ boolean disableRoot = !device.isAdbRoot();
+ device.enableAdbRoot();
+ assumeTrue("adb root is not enabled", device.isAdbRoot());
+
+ // Pull DT from device
+ File dtb_from_device = device.pullFile("/data/local/tmp/dump_dt.dtb");
+ if (disableRoot) {
+ device.disableAdbRoot();
+ }
+
+ File dtc = findTestFile("dtc");
+
+ // Create temp file for Device tree conversion
+ File dt_dump_dts = File.createTempFile("dt_dump", "dts");
+ dt_dump_dts.delete();
+ String dt_dump_dts_path = dt_dump_dts.getAbsolutePath();
+ // Convert DT to text format.
+ CommandResult dtb_to_dts =
+ RunUtil.getDefault()
+ .runTimedCmd(
+ 3000,
+ dtc.getAbsolutePath(),
+ "-I",
+ "dtb",
+ "-O",
+ "dts",
+ "-qqq",
+ "-f",
+ "-s",
+ "-o",
+ dt_dump_dts_path,
+ dtb_from_device.getAbsolutePath());
+ assertTrue(
+ "result convert stderr: " + dtb_to_dts.getStderr(),
+ dtb_to_dts.getStderr().trim().isEmpty());
+ assertTrue(
+ "result convert stdout: " + dtb_to_dts.getStdout(),
+ dtb_to_dts.getStdout().trim().isEmpty());
+
+ // Diff device's DT with the golden DT.
+ CommandResult result_compare =
+ RunUtil.getDefault()
+ .runTimedCmd(
+ 3000,
+ "diff",
+ "-u",
+ "-w",
+ "-I",
+ "kaslr-seed",
+ "-I",
+ "instance-id",
+ "-I",
+ "rng-seed",
+ "-I",
+ "linux,initrd-end",
+ "-I",
+ "secretkeeper_public_key",
+ "-I",
+ "interrupt-map",
+ dt_dump_dts_path,
+ goldenDt);
+
+ assertTrue(
+ "result compare stderr: " + result_compare.getStderr(),
+ result_compare.getStderr().trim().isEmpty());
+ assertTrue(
+ "result compare stdout: " + result_compare.getStdout(),
+ result_compare.getStdout().trim().isEmpty());
+ }
+
@Before
public void setUp() throws Exception {
assumeDeviceIsCapable(getDevice());
@@ -1424,4 +1556,11 @@
"Microdroid is not supported for specific VM protection type",
getAndroidDevice().supportsMicrodroid(protectedVm));
}
+
+ private void assumeArm64Supported() throws Exception {
+ CommandRunner android = new CommandRunner(getDevice());
+ String abi = android.run("getprop", "ro.product.cpu.abi");
+ assertThat(abi).isNotEmpty();
+ assumeTrue("Skipping test as the architecture is not supported", abi.startsWith("arm64"));
+ }
}
diff --git a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts
new file mode 100644
index 0000000..795c50f
--- /dev/null
+++ b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts
@@ -0,0 +1,145 @@
+/dts-v1/;
+
+/ {
+ #address-cells = <0x02>;
+ #size-cells = <0x02>;
+ compatible = "linux,dummy-virt";
+ interrupt-parent = <0x01>;
+ name = "reference";
+
+ U6_16550A@2e8 {
+ clock-frequency = <0x1c2000>;
+ compatible = "ns16550a";
+ interrupts = <0x00 0x02 0x01>;
+ reg = <0x00 0x2e8 0x00 0x08>;
+ };
+
+ U6_16550A@2f8 {
+ clock-frequency = <0x1c2000>;
+ compatible = "ns16550a";
+ interrupts = <0x00 0x02 0x01>;
+ reg = <0x00 0x2f8 0x00 0x08>;
+ };
+
+ U6_16550A@3e8 {
+ clock-frequency = <0x1c2000>;
+ compatible = "ns16550a";
+ interrupts = <0x00 0x00 0x01>;
+ reg = <0x00 0x3e8 0x00 0x08>;
+ };
+
+ U6_16550A@3f8 {
+ clock-frequency = <0x1c2000>;
+ compatible = "ns16550a";
+ interrupts = <0x00 0x00 0x01>;
+ reg = <0x00 0x3f8 0x00 0x08>;
+ };
+
+ __symbols__ {
+ intc = "/intc";
+ };
+
+ avf {
+ secretkeeper_public_key = [];
+
+ untrusted {
+ defer-rollback-protection;
+ instance-id = <0xf145d4f8 0x15f03952 0x5af249aa 0xfead94d8 0xb9f05746 0xd9163f48 0x7251b67b 0xe117409e 0x2b14dfa5 0xcaa8caf7 0x14176d2d 0xf88cc94b 0xeed4a59d 0x9a2d8fe5 0x5ac590f1 0xbb6c96f5>;
+ };
+ };
+
+ chosen {
+ bootargs = "panic=-1 crashkernel=17M";
+ kaslr-seed = <>;
+ linux,initrd-end = <0x81200360>;
+ linux,initrd-start = <0x81000000>;
+ linux,pci-probe-only = <0x01>;
+ rng-seed = <>;
+ stdout-path = "/U6_16550A@3f8";
+ };
+
+ config {
+ kernel-address = <0x80000000>;
+ kernel-size = <0xc91000>;
+ };
+
+ cpufreq {
+ compatible = "virtual,kvm-cpufreq";
+ };
+
+ cpus {
+ #address-cells = <0x01>;
+ #size-cells = <0x00>;
+
+ cpu@0 {
+ compatible = "arm,armv8";
+ device_type = "cpu";
+ phandle = <0x100>;
+ reg = <0x00>;
+ };
+ };
+
+ intc {
+ #address-cells = <0x02>;
+ #interrupt-cells = <0x03>;
+ #size-cells = <0x02>;
+ compatible = "arm,gic-v3";
+ interrupt-controller;
+ phandle = <0x01>;
+ reg = <0x00 0x3fff0000 0x00 0x10000 0x00 0x3ffd0000 0x00 0x20000>;
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00 0x80000000 0x00 0x10000000>;
+ };
+
+ pci {
+ #address-cells = <0x03>;
+ #interrupt-cells = <0x01>;
+ #size-cells = <0x02>;
+ bus-range = <0x00 0x00>;
+ compatible = "pci-host-cam-generic";
+ device_type = "pci";
+ dma-coherent;
+ interrupt-map = <0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04 0x2000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x07 0x04 0x2800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x08 0x04 0x3000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x09 0x04 0x3800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0a 0x04 0x4000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0b 0x04 0x4800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0c 0x04>;
+ interrupt-map-mask = <0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07>;
+ ranges = <0x3000000 0x00 0x2000000 0x00 0x2000000 0x00 0x2000000 0x43000000 0x00 0x90800000 0x00 0x90800000 0xff 0x6f800000>;
+ reg = <0x00 0x10000 0x00 0x1000000>;
+ };
+
+ pclk@3M {
+ #clock-cells = <0x00>;
+ clock-frequency = <0x2fefd8>;
+ compatible = "fixed-clock";
+ phandle = <0x18>;
+ };
+
+ psci {
+ compatible = "arm,psci-1.0\0arm,psci-0.2";
+ method = "hvc";
+ };
+
+ rtc@2000 {
+ arm,primecell-periphid = <0x41030>;
+ clock-names = "apb_pclk";
+ clocks = <0x18>;
+ compatible = "arm,primecell";
+ interrupts = <0x00 0x01 0x04>;
+ reg = <0x00 0x2000 0x00 0x1000>;
+ };
+
+ timer {
+ always-on;
+ compatible = "arm,armv8-timer";
+ interrupts = <0x01 0x0d 0x108 0x01 0x0e 0x108 0x01 0x0b 0x108 0x01 0x0a 0x108>;
+ };
+
+ vmwdt@3000 {
+ clock-frequency = <0x02>;
+ compatible = "qemu,vcpu-stall-detector";
+ interrupts = <0x01 0x0f 0x101>;
+ reg = <0x00 0x3000 0x00 0x1000>;
+ timeout-sec = <0x0a>;
+ };
+};
diff --git a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts
new file mode 100644
index 0000000..5761c15
--- /dev/null
+++ b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts
@@ -0,0 +1,159 @@
+/dts-v1/;
+
+/ {
+ #address-cells = <0x02>;
+ #size-cells = <0x02>;
+ compatible = "linux,dummy-virt";
+ interrupt-parent = <0x01>;
+ name = "reference";
+
+ U6_16550A@2e8 {
+ clock-frequency = <0x1c2000>;
+ compatible = "ns16550a";
+ interrupts = <0x00 0x02 0x01>;
+ reg = <0x00 0x2e8 0x00 0x08>;
+ };
+
+ U6_16550A@2f8 {
+ clock-frequency = <0x1c2000>;
+ compatible = "ns16550a";
+ interrupts = <0x00 0x02 0x01>;
+ reg = <0x00 0x2f8 0x00 0x08>;
+ };
+
+ U6_16550A@3e8 {
+ clock-frequency = <0x1c2000>;
+ compatible = "ns16550a";
+ interrupts = <0x00 0x00 0x01>;
+ reg = <0x00 0x3e8 0x00 0x08>;
+ };
+
+ U6_16550A@3f8 {
+ clock-frequency = <0x1c2000>;
+ compatible = "ns16550a";
+ interrupts = <0x00 0x00 0x01>;
+ reg = <0x00 0x3f8 0x00 0x08>;
+ };
+
+ __symbols__ {
+ intc = "/intc";
+ };
+
+ avf {
+ secretkeeper_public_key = [];
+
+ untrusted {
+ defer-rollback-protection;
+ instance-id = <0x4d482941 0x27228238 0x11d7b28 0xaeed3076 0x88eb3fcb 0x2b9de301 0x57ff8977 0xaf8c24b6 0x55466af4 0x23beed37 0x2f976083 0xe630eb28 0x1edbc491 0xa8300897 0xeb3e9f76 0x21ea9284>;
+ };
+ };
+
+ chosen {
+ bootargs = "panic=-1 crashkernel=31M";
+ kaslr-seed = <>;
+ linux,initrd-end = <0x81202104>;
+ linux,initrd-start = <0x81000000>;
+ linux,pci-probe-only = <0x01>;
+ rng-seed = <>;
+ stdout-path = "/U6_16550A@3f8";
+ };
+
+ config {
+ kernel-address = <0x80000000>;
+ kernel-size = <0xc91000>;
+ };
+
+ cpufreq {
+ compatible = "virtual,kvm-cpufreq";
+ };
+
+ cpus {
+ #address-cells = <0x01>;
+ #size-cells = <0x00>;
+
+ cpu@0 {
+ compatible = "arm,armv8";
+ device_type = "cpu";
+ phandle = <0x100>;
+ reg = <0x00>;
+ };
+ };
+
+ intc {
+ #address-cells = <0x02>;
+ #interrupt-cells = <0x03>;
+ #size-cells = <0x02>;
+ compatible = "arm,gic-v3";
+ interrupt-controller;
+ phandle = <0x01>;
+ reg = <0x00 0x3fff0000 0x00 0x10000 0x00 0x3ffd0000 0x00 0x20000>;
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00 0x80000000 0x00 0x10e00000>;
+ };
+
+ pci {
+ #address-cells = <0x03>;
+ #interrupt-cells = <0x01>;
+ #size-cells = <0x02>;
+ bus-range = <0x00 0x00>;
+ compatible = "pci-host-cam-generic";
+ device_type = "pci";
+ dma-coherent;
+ interrupt-map = <0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04 0x2000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x07 0x04 0x2800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x08 0x04 0x3000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x09 0x04 0x3800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0a 0x04 0x4000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0b 0x04>;
+ interrupt-map-mask = <0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07>;
+ memory-region = <0x02>;
+ ranges = <0x3000000 0x00 0x2000000 0x00 0x2000000 0x00 0x2000000 0x43000000 0x00 0x91600000 0x00 0x91600000 0xff 0x6ea00000>;
+ reg = <0x00 0x10000 0x00 0x1000000>;
+ };
+
+ pclk@3M {
+ #clock-cells = <0x00>;
+ clock-frequency = <0x2fefd8>;
+ compatible = "fixed-clock";
+ phandle = <0x18>;
+ };
+
+ psci {
+ compatible = "arm,psci-1.0\0arm,psci-0.2";
+ method = "hvc";
+ };
+
+ reserved-memory {
+ #address-cells = <0x02>;
+ #size-cells = <0x02>;
+ ranges;
+
+ restricted_dma_reserved {
+ alignment = <0x00 0x1000>;
+ compatible = "restricted-dma-pool";
+ phandle = <0x02>;
+ size = <0x00 0xe00000>;
+ };
+ };
+
+ rtc@2000 {
+ arm,primecell-periphid = <0x41030>;
+ clock-names = "apb_pclk";
+ clocks = <0x18>;
+ compatible = "arm,primecell";
+ interrupts = <0x00 0x01 0x04>;
+ reg = <0x00 0x2000 0x00 0x1000>;
+ };
+
+ timer {
+ always-on;
+ compatible = "arm,armv8-timer";
+ interrupts = <0x01 0x0d 0x108 0x01 0x0e 0x108 0x01 0x0b 0x108 0x01 0x0a 0x108>;
+ };
+
+ vmwdt@3000 {
+ clock-frequency = <0x02>;
+ compatible = "qemu,vcpu-stall-detector";
+ interrupts = <0x01 0x0f 0x101>;
+ reg = <0x00 0x3000 0x00 0x1000>;
+ timeout-sec = <0x0a>;
+ };
+};