storage_isolated_driver_manager/
fvm.rs
1use crate::Guid;
6use anyhow::{Context, Result};
7use device_watcher::recursive_wait_and_open;
8use fidl::endpoints::Proxy as _;
9use fidl_fuchsia_device::ControllerProxy;
10use fidl_fuchsia_hardware_block::{BlockMarker, BlockProxy};
11use fidl_fuchsia_hardware_block_partition::Guid as FidlGuid;
12use fidl_fuchsia_hardware_block_volume::{VolumeManagerMarker, VolumeManagerProxy};
13use fidl_fuchsia_io as fio;
14use fuchsia_component::client::connect_to_named_protocol_at_dir_root;
15use zx::sys::{zx_handle_t, zx_status_t};
16use zx::{self as zx, AsHandleRef};
17
18const FVM_DRIVER_PATH: &str = "fvm.cm";
19
20extern "C" {
21 fn fvm_init(device: zx_handle_t, slice_size: usize) -> zx_status_t;
24}
25
26pub fn format_for_fvm(block_device: &BlockProxy, fvm_slice_size: usize) -> Result<()> {
28 let device_raw = block_device.as_channel().raw_handle();
32 let status = unsafe { fvm_init(device_raw, fvm_slice_size) };
33 zx::ok(status).context("fvm_init failed")
34}
35
36pub async fn bind_fvm_driver(controller: &ControllerProxy) -> Result<()> {
38 controller.bind(FVM_DRIVER_PATH).await.context("fvm driver bind call failed").unwrap().unwrap();
39 Ok(())
40}
41
42pub async fn start_fvm_driver(
45 controller: &ControllerProxy,
46 block_device: &fio::DirectoryProxy,
47) -> Result<VolumeManagerProxy> {
48 bind_fvm_driver(controller).await?;
49 const FVM_DEVICE_NAME: &str = "fvm";
50 recursive_wait_and_open::<VolumeManagerMarker>(block_device, FVM_DEVICE_NAME)
51 .await
52 .context("wait_for_fvm_driver wait failed")
53}
54
55pub async fn set_up_fvm(
59 controller: &ControllerProxy,
60 block_device: &fio::DirectoryProxy,
61 fvm_slice_size: usize,
62) -> Result<VolumeManagerProxy> {
63 let block = connect_to_named_protocol_at_dir_root::<BlockMarker>(block_device, ".")?;
64 format_for_fvm(&block, fvm_slice_size)?;
65 start_fvm_driver(controller, block_device).await
66}
67
68pub async fn create_fvm_volume(
76 volume_manager: &VolumeManagerProxy,
77 name: &str,
78 type_guid: &Guid,
79 instance_guid: &Guid,
80 volume_size: Option<u64>,
81 flags: u32,
82) -> Result<()> {
83 let slice_count = match volume_size {
84 Some(volume_size) => {
85 let (status, info) =
86 volume_manager.get_info().await.context("Failed to get FVM info")?;
87 zx::ok(status).context("Get Info Error")?;
88 let slice_size = info.unwrap().slice_size;
89 assert!(slice_size > 0);
90 (volume_size + slice_size - 1) / slice_size
92 }
93 None => 1,
94 };
95 let type_guid = FidlGuid { value: type_guid.clone() };
96 let instance_guid = FidlGuid { value: instance_guid.clone() };
97
98 let status = volume_manager
99 .allocate_partition(slice_count, &type_guid, &instance_guid, name, flags)
100 .await?;
101 zx::ok(status).context("error allocating partition")
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::{wait_for_block_device_devfs, BlockDeviceMatcher};
108 use fidl_fuchsia_hardware_block_volume::{VolumeMarker, ALLOCATE_PARTITION_FLAG_INACTIVE};
109 use fuchsia_component::client::connect_to_protocol_at_path;
110 use ramdevice_client::RamdiskClient;
111
112 const BLOCK_SIZE: u64 = 512;
113 const BLOCK_COUNT: u64 = 64 * 1024 * 1024 / BLOCK_SIZE;
114 const FVM_SLICE_SIZE: usize = 1024 * 1024;
115 const INSTANCE_GUID: Guid = [
116 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
117 0x0f,
118 ];
119 const TYPE_GUID: Guid = [
120 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0,
121 0xf0,
122 ];
123 const VOLUME_NAME: &str = "volume-name";
124
125 #[fuchsia::test]
126 async fn set_up_fvm_test() {
127 let ramdisk = RamdiskClient::create(BLOCK_SIZE, BLOCK_COUNT).await.unwrap();
128 let fvm = set_up_fvm(
129 ramdisk.as_controller().expect("invalid controller"),
130 ramdisk.as_dir().expect("invalid directory proxy"),
131 FVM_SLICE_SIZE,
132 )
133 .await
134 .expect("Failed to set up FVM");
135
136 let fvm_info = fvm.get_info().await.unwrap();
137 zx::ok(fvm_info.0).unwrap();
138 let fvm_info = fvm_info.1.unwrap();
139 assert_eq!(fvm_info.slice_size, FVM_SLICE_SIZE as u64);
140 assert_eq!(fvm_info.assigned_slice_count, 0);
141 }
142
143 #[fuchsia::test]
144 async fn create_fvm_volume_without_volume_size_has_one_slice() {
145 let ramdisk = RamdiskClient::create(BLOCK_SIZE, BLOCK_COUNT).await.unwrap();
146 let fvm = set_up_fvm(
147 ramdisk.as_controller().expect("invalid controller"),
148 ramdisk.as_dir().expect("invalid directory proxy"),
149 FVM_SLICE_SIZE,
150 )
151 .await
152 .expect("Failed to set up FVM");
153
154 create_fvm_volume(
155 &fvm,
156 VOLUME_NAME,
157 &TYPE_GUID,
158 &INSTANCE_GUID,
159 None,
160 ALLOCATE_PARTITION_FLAG_INACTIVE,
161 )
162 .await
163 .expect("Failed to create fvm volume");
164 let block_device_path = wait_for_block_device_devfs(&[
165 BlockDeviceMatcher::TypeGuid(&TYPE_GUID),
166 BlockDeviceMatcher::InstanceGuid(&INSTANCE_GUID),
167 BlockDeviceMatcher::Name(VOLUME_NAME),
168 ])
169 .await
170 .expect("Failed to find block device");
171
172 let volume =
173 connect_to_protocol_at_path::<VolumeMarker>(block_device_path.to_str().unwrap())
174 .unwrap();
175 let volume_info = volume.get_volume_info().await.unwrap();
176 zx::ok(volume_info.0).unwrap();
177 let volume_info = volume_info.2.unwrap();
178 assert_eq!(volume_info.partition_slice_count, 1);
179 }
180
181 #[fuchsia::test]
182 async fn create_fvm_volume_with_unaligned_volume_size_rounds_up_to_slice_multiple() {
183 let ramdisk = RamdiskClient::create(BLOCK_SIZE, BLOCK_COUNT).await.unwrap();
184 let fvm = set_up_fvm(
185 ramdisk.as_controller().expect("invalid controller"),
186 ramdisk.as_dir().expect("invalid directory proxy"),
187 FVM_SLICE_SIZE,
188 )
189 .await
190 .expect("Failed to set up FVM");
191
192 create_fvm_volume(
193 &fvm,
194 VOLUME_NAME,
195 &TYPE_GUID,
196 &INSTANCE_GUID,
197 Some((FVM_SLICE_SIZE * 5 + 4) as u64),
198 ALLOCATE_PARTITION_FLAG_INACTIVE,
199 )
200 .await
201 .expect("Failed to create fvm volume");
202
203 let block_device_path = wait_for_block_device_devfs(&[
204 BlockDeviceMatcher::TypeGuid(&TYPE_GUID),
205 BlockDeviceMatcher::InstanceGuid(&INSTANCE_GUID),
206 BlockDeviceMatcher::Name(VOLUME_NAME),
207 ])
208 .await
209 .expect("Failed to find block device");
210
211 let volume =
212 connect_to_protocol_at_path::<VolumeMarker>(block_device_path.to_str().unwrap())
213 .unwrap();
214 let volume_info = volume.get_volume_info().await.unwrap();
215 zx::ok(volume_info.0).unwrap();
216 let volume_info = volume_info.2.unwrap();
217 assert_eq!(volume_info.partition_slice_count, 6);
218 }
219}