blob: 995249655982421041907d79dbbb37b6c88fcf33 [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
17use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IVfioHandler::IVfioHandler;
18use android_system_virtualizationservice_internal::binder::ParcelFileDescriptor;
19use binder::{self, ExceptionCode, Interface, Status};
20use lazy_static::lazy_static;
Inseob Kimf36347b2023-08-03 12:52:48 +090021use std::fs::{read_link, write};
22use std::io::Write;
Inseob Kimbdca0472023-07-28 19:20:56 +090023use std::path::Path;
Inseob Kimbdca0472023-07-28 19:20:56 +090024
25#[derive(Debug, Default)]
26pub struct VfioHandler {}
27
28impl VfioHandler {
29 pub fn init() -> VfioHandler {
30 VfioHandler::default()
31 }
32}
33
34impl Interface for VfioHandler {}
35
36impl IVfioHandler for VfioHandler {
Inseob Kimf36347b2023-08-03 12:52:48 +090037 fn bindDevicesToVfioDriver(
38 &self,
39 devices: &[String],
40 dtbo: &ParcelFileDescriptor,
41 ) -> binder::Result<()> {
Inseob Kimbdca0472023-07-28 19:20:56 +090042 // permission check is already done by IVirtualizationServiceInternal.
43 if !*IS_VFIO_SUPPORTED {
44 return Err(Status::new_exception_str(
45 ExceptionCode::UNSUPPORTED_OPERATION,
46 Some("VFIO-platform not supported"),
47 ));
48 }
49
50 devices.iter().try_for_each(|x| bind_device(Path::new(x)))?;
51
Inseob Kimf36347b2023-08-03 12:52:48 +090052 let mut dtbo = dtbo.as_ref().try_clone().map_err(|e| {
Inseob Kimbdca0472023-07-28 19:20:56 +090053 Status::new_exception_str(
Inseob Kimf36347b2023-08-03 12:52:48 +090054 ExceptionCode::BAD_PARCELABLE,
55 Some(format!("Failed to clone File from ParcelFileDescriptor: {e:?}")),
Inseob Kimbdca0472023-07-28 19:20:56 +090056 )
57 })?;
Inseob Kimf36347b2023-08-03 12:52:48 +090058 // TODO(b/291191362): write DTBO for devices to dtbo.
59 dtbo.write(b"\n").map_err(|e| {
60 Status::new_exception_str(
61 ExceptionCode::BAD_PARCELABLE,
62 Some(format!("Can't write to ParcelFileDescriptor: {e:?}")),
63 )
64 })?;
65 Ok(())
Inseob Kimbdca0472023-07-28 19:20:56 +090066 }
67}
68
69const DEV_VFIO_PATH: &str = "/dev/vfio/vfio";
70const SYSFS_PLATFORM_DEVICES_PATH: &str = "/sys/devices/platform/";
71const VFIO_PLATFORM_DRIVER_PATH: &str = "/sys/bus/platform/drivers/vfio-platform";
72const SYSFS_PLATFORM_DRIVERS_PROBE_PATH: &str = "/sys/bus/platform/drivers_probe";
73
74lazy_static! {
75 static ref IS_VFIO_SUPPORTED: bool = is_vfio_supported();
76}
77
78fn is_vfio_supported() -> bool {
79 Path::new(DEV_VFIO_PATH).exists() && Path::new(VFIO_PLATFORM_DRIVER_PATH).exists()
80}
81
82fn check_platform_device(path: &Path) -> binder::Result<()> {
83 if !path.exists() {
84 return Err(Status::new_exception_str(
85 ExceptionCode::ILLEGAL_ARGUMENT,
86 Some(format!("no such device {path:?}")),
87 ));
88 }
89
90 if !path.starts_with(SYSFS_PLATFORM_DEVICES_PATH) {
91 return Err(Status::new_exception_str(
92 ExceptionCode::ILLEGAL_ARGUMENT,
93 Some(format!("{path:?} is not a platform device")),
94 ));
95 }
96
97 Ok(())
98}
99
100fn get_device_iommu_group(path: &Path) -> Option<u64> {
101 let group_path = read_link(path.join("iommu_group")).ok()?;
102 let group = group_path.file_name()?;
103 group.to_str()?.parse().ok()
104}
105
106fn is_bound_to_vfio_driver(path: &Path) -> bool {
107 let Ok(driver_path) = read_link(path.join("driver")) else {
108 return false;
109 };
110 let Some(driver) = driver_path.file_name() else {
111 return false;
112 };
113 driver.to_str().unwrap_or("") == "vfio-platform"
114}
115
116fn bind_vfio_driver(path: &Path) -> binder::Result<()> {
117 if is_bound_to_vfio_driver(path) {
118 // already bound
119 return Ok(());
120 }
121
122 // unbind
123 let Some(device) = path.file_name() else {
124 return Err(Status::new_exception_str(
125 ExceptionCode::ILLEGAL_ARGUMENT,
Inseob Kimf36347b2023-08-03 12:52:48 +0900126 Some(format!("can't get device name from {path:?}")),
Inseob Kimbdca0472023-07-28 19:20:56 +0900127 ));
128 };
129 let Some(device_str) = device.to_str() else {
130 return Err(Status::new_exception_str(
131 ExceptionCode::ILLEGAL_ARGUMENT,
Inseob Kimf36347b2023-08-03 12:52:48 +0900132 Some(format!("invalid filename {device:?}")),
Inseob Kimbdca0472023-07-28 19:20:56 +0900133 ));
134 };
Inseob Kim55438b22023-08-09 20:16:01 +0900135 let unbind_path = path.join("driver/unbind");
136 if unbind_path.exists() {
137 write(&unbind_path, device_str.as_bytes()).map_err(|e| {
138 Status::new_exception_str(
139 ExceptionCode::SERVICE_SPECIFIC,
140 Some(format!("could not unbind {device_str}: {e:?}")),
141 )
142 })?;
143 }
Inseob Kimbdca0472023-07-28 19:20:56 +0900144
145 // bind to VFIO
146 write(path.join("driver_override"), b"vfio-platform").map_err(|e| {
147 Status::new_exception_str(
148 ExceptionCode::SERVICE_SPECIFIC,
149 Some(format!("could not bind {device_str} to vfio-platform: {e:?}")),
150 )
151 })?;
152
153 write(SYSFS_PLATFORM_DRIVERS_PROBE_PATH, device_str.as_bytes()).map_err(|e| {
154 Status::new_exception_str(
155 ExceptionCode::SERVICE_SPECIFIC,
156 Some(format!("could not write {device_str} to drivers-probe: {e:?}")),
157 )
158 })?;
159
160 // final check
161 if !is_bound_to_vfio_driver(path) {
162 return Err(Status::new_exception_str(
163 ExceptionCode::SERVICE_SPECIFIC,
164 Some(format!("{path:?} still not bound to vfio driver")),
165 ));
166 }
167
168 if get_device_iommu_group(path).is_none() {
169 return Err(Status::new_exception_str(
170 ExceptionCode::SERVICE_SPECIFIC,
171 Some(format!("can't get iommu group for {path:?}")),
172 ));
173 }
174
175 Ok(())
176}
177
178fn bind_device(path: &Path) -> binder::Result<()> {
179 let path = path.canonicalize().map_err(|e| {
180 Status::new_exception_str(
181 ExceptionCode::ILLEGAL_ARGUMENT,
182 Some(format!("can't canonicalize {path:?}: {e:?}")),
183 )
184 })?;
185
186 check_platform_device(&path)?;
187 bind_vfio_driver(&path)
188}