1#![deny(missing_docs)]
5use fdf_component::DriverError;
8use fidl::{Persistable, Serializable};
9use fidl_next_fuchsia_hardware_platform_device as fpdev;
10use log::{debug, error};
11use mmio::region::MmioRegion;
12use mmio::vmo::{VmoMapping, VmoMemory};
13use std::future::Future;
14use zx_status::Status;
15
16pub trait PlatformDevice {
18 type Mmio;
20
21 fn map_mmio_by_id(&self, id: u32) -> impl Future<Output = Result<Self::Mmio, DriverError>>;
23
24 fn map_mmio_by_name(&self, name: &str)
26 -> impl Future<Output = Result<Self::Mmio, DriverError>>;
27
28 fn get_typed_metadata<T: Persistable + Serializable>(
30 &self,
31 ) -> impl Future<Output = Result<T, DriverError>>;
32
33 fn get_deserialized_metadata<T: serde::de::DeserializeOwned>(
35 &self,
36 ) -> impl Future<Output = Result<T, DriverError>>;
37
38 fn get_persisted_metadata_by_id(
40 &self,
41 metadata_id: &str,
42 ) -> impl Future<Output = Result<Vec<u8>, DriverError>>;
43
44 fn get_dictionary_metadata(
46 &self,
47 metadata_id: &str,
48 ) -> impl Future<Output = Result<fidl_fuchsia_driver_metadata::Dictionary, DriverError>>;
49}
50
51impl PlatformDevice for fidl_next::Client<fpdev::Device> {
52 type Mmio = MmioRegion<VmoMemory>;
53
54 async fn map_mmio_by_id(&self, id: u32) -> Result<Self::Mmio, DriverError> {
55 let mmio = self.get_mmio_by_id(id).await??;
56 Ok(map_mmio(mmio)?)
57 }
58
59 async fn map_mmio_by_name(&self, name: &str) -> Result<Self::Mmio, DriverError> {
60 let mmio = self.get_mmio_by_name(name).await??;
61 Ok(map_mmio(mmio)?)
62 }
63
64 async fn get_typed_metadata<T: Persistable + Serializable>(&self) -> Result<T, DriverError> {
65 let name = T::SERIALIZABLE_NAME;
66 let metadata_res = self.get_metadata(name).await??;
67 fidl::unpersist(&metadata_res.metadata).map_err(|err| {
68 error!("Failed to parse pdev metadata: {err}");
69 DriverError::Status(Status::INVALID_ARGS)
70 })
71 }
72
73 async fn get_deserialized_metadata<T: serde::de::DeserializeOwned>(
74 &self,
75 ) -> Result<T, DriverError> {
76 let name = "fuchsia.driver.metadata.Dictionary";
77 let metadata_res = self.get_metadata(name).await??;
78 let dict: fidl_fuchsia_driver_metadata::Dictionary =
79 fidl::unpersist(&metadata_res.metadata).map_err(|err| {
80 error!("Failed to unpersist dictionary: {err}");
81 DriverError::Status(Status::INVALID_ARGS)
82 })?;
83 fdf_metadata::from_dictionary(dict).map_err(|err| {
84 error!("Failed to deserialize config from dictionary: {err:?}");
85 DriverError::Status(Status::INVALID_ARGS)
86 })
87 }
88
89 async fn get_persisted_metadata_by_id(
90 &self,
91 metadata_id: &str,
92 ) -> Result<Vec<u8>, DriverError> {
93 let metadata_res = self.get_metadata(metadata_id).await??;
94 Ok(metadata_res.metadata)
95 }
96
97 async fn get_dictionary_metadata(
98 &self,
99 metadata_id: &str,
100 ) -> Result<fidl_fuchsia_driver_metadata::Dictionary, DriverError> {
101 let bytes = self.get_persisted_metadata_by_id(metadata_id).await?;
102 fidl::unpersist(&bytes).map_err(|err| {
103 debug!("Failed to unpersist metadata dictionary for {}: {:?}", metadata_id, err);
104 DriverError::Status(Status::INVALID_ARGS)
105 })
106 }
107}
108
109pub trait PdevExt {
112 fn connect_to_pdev(&self) -> Result<fidl_next::Client<fpdev::Device>, DriverError>;
114}
115
116impl PdevExt for fdf_component::DriverContext {
117 fn connect_to_pdev(&self) -> Result<fidl_next::Client<fpdev::Device>, DriverError> {
118 let service = self
119 .incoming
120 .service::<fdf_component::ServiceInstance<fpdev::Service>>()
121 .instance("pdev")
122 .connect_next()?;
123 let (client_end, server_end) = fidl_next::fuchsia::create_channel();
124 service.device(server_end)?;
125 Ok(client_end.spawn())
126 }
127}
128
129fn map_mmio(mmio: fpdev::Mmio) -> Result<MmioRegion<VmoMemory>, Status> {
130 let (Some(vmo), Some(offset), Some(size)) = (mmio.vmo, mmio.offset, mmio.size) else {
131 error!("Mmio device missing vmo, offset or size");
132 return Err(Status::INTERNAL);
133 };
134 let offset = offset as usize;
135 let size = size as usize;
136
137 let mmio = VmoMapping::map(offset, size, vmo).map_err(|err| {
138 error!("Failed to map Mmio memory for vmo: {err}");
139 Status::INTERNAL
140 })?;
141 Ok(mmio)
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use fidl_next::{Request, Responder};
148 use fidl_test_metadata::{IntMetadata, Metadata};
149 use fuchsia_async::Task;
150 use mmio::Mmio;
151 use std::collections::HashMap;
152 use zx::{Vmo, VmoOp};
153
154 struct TestServer {
155 mmios: Vec<(&'static str, Option<fpdev::Mmio>)>,
156 metadata: HashMap<&'static str, Vec<u8>>,
157 }
158
159 impl TestServer {
160 fn new() -> Self {
161 Self { mmios: Vec::new(), metadata: HashMap::new() }
162 }
163
164 fn append_mmio(&mut self, name: &'static str, vmo: Vmo, offset: usize, size: usize) {
165 self.mmios.push((
166 name,
167 Some(fpdev::Mmio {
168 offset: Some(offset as u64),
169 size: Some(size as u64),
170 vmo: Some(vmo),
171 }),
172 ));
173 }
174
175 fn set_typed_metadata<T: Persistable + Serializable>(&mut self, metadata: &T) {
176 let bytes = fidl::persist(metadata).unwrap();
177 self.metadata.insert(T::SERIALIZABLE_NAME, bytes);
178 }
179
180 fn take_mmio_by_id(&mut self, id: u32) -> Result<fpdev::Mmio, Status> {
181 self.mmios
182 .get_mut(id as usize)
183 .ok_or(Status::NOT_FOUND)?
184 .1
185 .take()
186 .ok_or(Status::ALREADY_BOUND)
187 }
188
189 fn take_mmio_by_name(&mut self, name: &str) -> Result<fpdev::Mmio, Status> {
190 self.mmios
191 .iter_mut()
192 .find(|(n, _)| *n == name)
193 .ok_or(Status::NOT_FOUND)?
194 .1
195 .take()
196 .ok_or(Status::ALREADY_BOUND)
197 }
198
199 fn read_metadata(&self, id: &str) -> Result<&[u8], Status> {
200 self.metadata.get(id).map(|v| v.as_slice()).ok_or(Status::NOT_FOUND)
201 }
202
203 fn run(
204 self,
205 ) -> (
206 fidl_next::Client<fpdev::Device>,
207 Task<Result<(), fidl_next::ProtocolError<zx::Status>>>,
208 ) {
209 let (client_end, server_end) = fidl_next::fuchsia::create_channel::<fpdev::Device>();
210 let client = client_end.spawn();
211 let server = Task::local(async move {
212 let dispatcher = fidl_next::ServerDispatcher::new(server_end);
213 dispatcher.run_local(self).await.map(|_| ())
214 });
215 (client, server)
216 }
217 }
218
219 impl fpdev::DeviceLocalServerHandler for TestServer {
220 async fn get_mmio_by_id(
221 &mut self,
222 request: Request<fpdev::device::GetMmioById>,
223 responder: Responder<fpdev::device::GetMmioById>,
224 ) {
225 let index = request.payload().index;
226 match self.take_mmio_by_id(index) {
227 Ok(mmio) => {
228 let _ = responder.respond(mmio).await;
229 }
230 Err(status) => {
231 let _ = responder.respond_err(status).await;
232 }
233 }
234 }
235
236 async fn get_mmio_by_name(
237 &mut self,
238 request: Request<fpdev::device::GetMmioByName>,
239 responder: Responder<fpdev::device::GetMmioByName>,
240 ) {
241 let name = &request.payload().name;
242 match self.take_mmio_by_name(name) {
243 Ok(mmio) => {
244 let _ = responder.respond(mmio).await;
245 }
246 Err(status) => {
247 let _ = responder.respond_err(status).await;
248 }
249 }
250 }
251
252 async fn get_interrupt_by_id(
253 &mut self,
254 _request: Request<fpdev::device::GetInterruptById>,
255 _responder: Responder<fpdev::device::GetInterruptById>,
256 ) {
257 unimplemented!("not used by tests");
258 }
259
260 async fn get_interrupt_by_name(
261 &mut self,
262 _request: Request<fpdev::device::GetInterruptByName>,
263 _responder: Responder<fpdev::device::GetInterruptByName>,
264 ) {
265 unimplemented!("not used by tests");
266 }
267
268 async fn get_bti_by_id(
269 &mut self,
270 _request: Request<fpdev::device::GetBtiById>,
271 _responder: Responder<fpdev::device::GetBtiById>,
272 ) {
273 unimplemented!("not used by tests");
274 }
275
276 async fn get_bti_by_name(
277 &mut self,
278 _request: Request<fpdev::device::GetBtiByName>,
279 _responder: Responder<fpdev::device::GetBtiByName>,
280 ) {
281 unimplemented!("not used by tests");
282 }
283
284 async fn get_smc_by_id(
285 &mut self,
286 _request: Request<fpdev::device::GetSmcById>,
287 _responder: Responder<fpdev::device::GetSmcById>,
288 ) {
289 unimplemented!("not used by tests");
290 }
291
292 async fn get_smc_by_name(
293 &mut self,
294 _request: Request<fpdev::device::GetSmcByName>,
295 _responder: Responder<fpdev::device::GetSmcByName>,
296 ) {
297 unimplemented!("not used by tests");
298 }
299
300 async fn get_power_configuration(
301 &mut self,
302 _responder: Responder<fpdev::device::GetPowerConfiguration>,
303 ) {
304 unimplemented!("not used by tests");
305 }
306
307 async fn get_node_device_info(
308 &mut self,
309 _responder: Responder<fpdev::device::GetNodeDeviceInfo>,
310 ) {
311 unimplemented!("not used by tests");
312 }
313
314 async fn get_board_info(&mut self, _responder: Responder<fpdev::device::GetBoardInfo>) {
315 unimplemented!("not used by tests");
316 }
317
318 async fn get_metadata(
319 &mut self,
320 request: Request<fpdev::device::GetMetadata>,
321 responder: Responder<fpdev::device::GetMetadata>,
322 ) {
323 let id = &request.payload().id;
324 match self.read_metadata(id) {
325 Ok(metadata) => {
326 let _ = responder.respond(metadata).await;
327 }
328 Err(status) => {
329 let _ = responder.respond_err(status).await;
330 }
331 }
332 }
333 }
334
335 #[fuchsia::test]
336 async fn test_pdev() {
337 let mut server = TestServer::new();
338
339 let vmo = Vmo::create(4096).unwrap();
340 vmo.op_range(VmoOp::ZERO, 0, 4096).unwrap();
341 server.append_mmio("zero", vmo, 0, 4096);
342
343 let vmo = Vmo::create(1024).unwrap();
345 for i in 0..256 {
346 vmo.write(&((i as u32).to_le_bytes()), (i * size_of::<u32>()) as u64).unwrap();
347 }
348 server.append_mmio("dev", vmo, 32 * size_of::<u32>(), 16);
349
350 server.set_typed_metadata(&Metadata {
351 test_field: Some("foo".to_string()),
352 ..Default::default()
353 });
354
355 let dict = fidl_fuchsia_driver_metadata::Dictionary {
356 entries: Some(vec![fidl_fuchsia_driver_metadata::DictionaryEntry {
357 key: "test_key".to_string(),
358 value: fidl_fuchsia_driver_metadata::DictionaryValue::Str("test_val".to_string()),
359 }]),
360 ..Default::default()
361 };
362 let dict_bytes = fidl::persist(&dict).unwrap();
363 server.metadata.insert("test_dict_id", dict_bytes.clone());
364
365 let (client, server) = server.run();
366
367 let mmio = client.map_mmio_by_id(1).await.unwrap();
368 assert_eq!(
369 client.map_mmio_by_id(1).await.err().map(|e| e.log_to_status()),
370 Some(Status::ALREADY_BOUND)
371 );
372 assert_eq!(
373 client.map_mmio_by_id(2).await.err().map(|e| e.log_to_status()),
374 Some(Status::NOT_FOUND)
375 );
376 assert_eq!(
377 client.map_mmio_by_name("dev").await.err().map(|e| e.log_to_status()),
378 Some(Status::ALREADY_BOUND)
379 );
380
381 assert_eq!(mmio.load32(0), 32);
382
383 let mmio = client.map_mmio_by_name("zero").await.unwrap();
384 assert_eq!(mmio.len(), 4096);
385 assert_eq!(mmio.load64(128), 0);
386
387 assert_eq!(
388 client.get_typed_metadata::<Metadata>().await.unwrap(),
389 Metadata { test_field: Some("foo".to_string()), ..Default::default() }
390 );
391
392 assert_eq!(
393 client.get_typed_metadata::<IntMetadata>().await.err().map(|e| e.log_to_status()),
394 Some(Status::NOT_FOUND)
395 );
396
397 let raw_bytes = client.get_persisted_metadata_by_id("test_dict_id").await.unwrap();
398 assert_eq!(raw_bytes, dict_bytes);
399
400 let retrieved_dict = client.get_dictionary_metadata("test_dict_id").await.unwrap();
401 assert_eq!(retrieved_dict, dict);
402
403 assert_eq!(
405 client
406 .get_dictionary_metadata(Metadata::SERIALIZABLE_NAME)
407 .await
408 .err()
409 .map(|e| e.log_to_status()),
410 Some(Status::INVALID_ARGS)
411 );
412
413 let _ = server.abort().await;
414 }
415}