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