blob: a826c7512e88d0ee684bccc017626ada50459680 [file] [log] [blame]
Inseob Kimbdca0472023-07-28 19:20:56 +09001// Copyright 2023, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Implementation of the AIDL interface of the VirtualizationService.
16
Jiyong Park2227eaa2023-08-04 11:59:18 +090017use anyhow::{anyhow, Context};
Inseob Kimbdca0472023-07-28 19:20:56 +090018use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IVfioHandler::IVfioHandler;
19use android_system_virtualizationservice_internal::binder::ParcelFileDescriptor;
Jiyong Park2227eaa2023-08-04 11:59:18 +090020use binder::{self, ExceptionCode, Interface, IntoBinderResult};
Inseob Kimbdca0472023-07-28 19:20:56 +090021use lazy_static::lazy_static;
Inseob Kimf36347b2023-08-03 12:52:48 +090022use std::fs::{read_link, write};
23use std::io::Write;
Inseob Kimbdca0472023-07-28 19:20:56 +090024use std::path::Path;
Inseob Kimbdca0472023-07-28 19:20:56 +090025
26#[derive(Debug, Default)]
27pub struct VfioHandler {}
28
29impl VfioHandler {
30 pub fn init() -> VfioHandler {
31 VfioHandler::default()
32 }
33}
34
35impl Interface for VfioHandler {}
36
37impl IVfioHandler for VfioHandler {
Inseob Kimf36347b2023-08-03 12:52:48 +090038 fn bindDevicesToVfioDriver(
39 &self,
40 devices: &[String],
41 dtbo: &ParcelFileDescriptor,
42 ) -> binder::Result<()> {
Inseob Kimbdca0472023-07-28 19:20:56 +090043 // permission check is already done by IVirtualizationServiceInternal.
44 if !*IS_VFIO_SUPPORTED {
Jiyong Park2227eaa2023-08-04 11:59:18 +090045 return Err(anyhow!("VFIO-platform not supported"))
46 .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
Inseob Kimbdca0472023-07-28 19:20:56 +090047 }
48
49 devices.iter().try_for_each(|x| bind_device(Path::new(x)))?;
50
Jiyong Park2227eaa2023-08-04 11:59:18 +090051 let mut dtbo = dtbo
52 .as_ref()
53 .try_clone()
54 .context("Failed to clone File from ParcelFileDescriptor")
55 .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?;
Inseob Kimf36347b2023-08-03 12:52:48 +090056 // TODO(b/291191362): write DTBO for devices to dtbo.
Jiyong Park2227eaa2023-08-04 11:59:18 +090057 dtbo.write(b"\n")
58 .context("Can't write to ParcelFileDescriptor")
59 .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?;
Inseob Kimf36347b2023-08-03 12:52:48 +090060 Ok(())
Inseob Kimbdca0472023-07-28 19:20:56 +090061 }
62}
63
64const DEV_VFIO_PATH: &str = "/dev/vfio/vfio";
65const SYSFS_PLATFORM_DEVICES_PATH: &str = "/sys/devices/platform/";
66const VFIO_PLATFORM_DRIVER_PATH: &str = "/sys/bus/platform/drivers/vfio-platform";
67const SYSFS_PLATFORM_DRIVERS_PROBE_PATH: &str = "/sys/bus/platform/drivers_probe";
68
69lazy_static! {
70 static ref IS_VFIO_SUPPORTED: bool = is_vfio_supported();
71}
72
73fn is_vfio_supported() -> bool {
74 Path::new(DEV_VFIO_PATH).exists() && Path::new(VFIO_PLATFORM_DRIVER_PATH).exists()
75}
76
77fn check_platform_device(path: &Path) -> binder::Result<()> {
78 if !path.exists() {
Jiyong Park2227eaa2023-08-04 11:59:18 +090079 return Err(anyhow!("no such device {path:?}"))
80 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
Inseob Kimbdca0472023-07-28 19:20:56 +090081 }
82
83 if !path.starts_with(SYSFS_PLATFORM_DEVICES_PATH) {
Jiyong Park2227eaa2023-08-04 11:59:18 +090084 return Err(anyhow!("{path:?} is not a platform device"))
85 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
Inseob Kimbdca0472023-07-28 19:20:56 +090086 }
87
88 Ok(())
89}
90
91fn get_device_iommu_group(path: &Path) -> Option<u64> {
92 let group_path = read_link(path.join("iommu_group")).ok()?;
93 let group = group_path.file_name()?;
94 group.to_str()?.parse().ok()
95}
96
97fn is_bound_to_vfio_driver(path: &Path) -> bool {
98 let Ok(driver_path) = read_link(path.join("driver")) else {
99 return false;
100 };
101 let Some(driver) = driver_path.file_name() else {
102 return false;
103 };
104 driver.to_str().unwrap_or("") == "vfio-platform"
105}
106
107fn bind_vfio_driver(path: &Path) -> binder::Result<()> {
108 if is_bound_to_vfio_driver(path) {
109 // already bound
110 return Ok(());
111 }
112
113 // unbind
114 let Some(device) = path.file_name() else {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900115 return Err(anyhow!("can't get device name from {path:?}"))
116 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
Inseob Kimbdca0472023-07-28 19:20:56 +0900117 };
118 let Some(device_str) = device.to_str() else {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900119 return Err(anyhow!("invalid filename {device:?}"))
120 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
Inseob Kimbdca0472023-07-28 19:20:56 +0900121 };
Inseob Kim55438b22023-08-09 20:16:01 +0900122 let unbind_path = path.join("driver/unbind");
123 if unbind_path.exists() {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900124 write(&unbind_path, device_str.as_bytes())
125 .with_context(|| format!("could not unbind {device_str}"))
126 .or_service_specific_exception(-1)?;
Inseob Kim55438b22023-08-09 20:16:01 +0900127 }
Inseob Kimbdca0472023-07-28 19:20:56 +0900128
129 // bind to VFIO
Jiyong Park2227eaa2023-08-04 11:59:18 +0900130 write(path.join("driver_override"), b"vfio-platform")
131 .with_context(|| format!("could not bind {device_str} to vfio-platform"))
132 .or_service_specific_exception(-1)?;
Inseob Kimbdca0472023-07-28 19:20:56 +0900133
Jiyong Park2227eaa2023-08-04 11:59:18 +0900134 write(SYSFS_PLATFORM_DRIVERS_PROBE_PATH, device_str.as_bytes())
135 .with_context(|| format!("could not write {device_str} to drivers-probe"))
136 .or_service_specific_exception(-1)?;
Inseob Kimbdca0472023-07-28 19:20:56 +0900137
138 // final check
139 if !is_bound_to_vfio_driver(path) {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900140 return Err(anyhow!("{path:?} still not bound to vfio driver"))
141 .or_service_specific_exception(-1);
Inseob Kimbdca0472023-07-28 19:20:56 +0900142 }
143
144 if get_device_iommu_group(path).is_none() {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900145 return Err(anyhow!("can't get iommu group for {path:?}"))
146 .or_service_specific_exception(-1);
Inseob Kimbdca0472023-07-28 19:20:56 +0900147 }
148
149 Ok(())
150}
151
152fn bind_device(path: &Path) -> binder::Result<()> {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900153 let path = path
154 .canonicalize()
155 .with_context(|| format!("can't canonicalize {path:?}"))
156 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?;
Inseob Kimbdca0472023-07-28 19:20:56 +0900157
158 check_platform_device(&path)?;
159 bind_vfio_driver(&path)
160}