1#![deny(missing_docs)]
5use fidl::{Persistable, Serializable};
8use fidl_fuchsia_hardware_platform_device as fpdev;
9use log::error;
10use mmio::region::MmioRegion;
11use mmio::vmo::{VmoMapping, VmoMemory};
12use std::future::Future;
13use zx_status::Status;
14
15pub trait PlatformDevice {
17 type Mmio;
19
20 fn map_mmio_by_id(&self, id: u32) -> impl Future<Output = Result<Self::Mmio, Status>>;
22
23 fn map_mmio_by_name(&self, name: &str) -> impl Future<Output = Result<Self::Mmio, Status>>;
25
26 fn get_typed_metadata<T: Persistable + Serializable>(
28 &self,
29 ) -> impl Future<Output = Result<T, Status>>;
30
31 fn get_deserialized_metadata<T: serde::de::DeserializeOwned>(
33 &self,
34 ) -> impl Future<Output = Result<T, Status>>;
35}
36
37impl PlatformDevice for fpdev::DeviceProxy {
38 type Mmio = MmioRegion<VmoMemory>;
39
40 async fn map_mmio_by_id(&self, id: u32) -> Result<Self::Mmio, Status> {
41 let mmio = self
42 .get_mmio_by_id(id)
43 .await
44 .map_err(|err| {
45 error!("Could not get mmio for id {id}: {err}");
46 Status::INTERNAL
47 })?
48 .map_err(Status::from_raw)?;
49 map_mmio(mmio)
50 }
51
52 async fn map_mmio_by_name(&self, name: &str) -> Result<Self::Mmio, Status> {
53 let mmio = self
54 .get_mmio_by_name(name)
55 .await
56 .map_err(|err| {
57 error!("Could not get mmio for name {name}: {err}");
58 Status::INTERNAL
59 })?
60 .map_err(Status::from_raw)?;
61 map_mmio(mmio)
62 }
63
64 async fn get_typed_metadata<T: Persistable + Serializable>(&self) -> Result<T, Status> {
65 let name = T::SERIALIZABLE_NAME;
66 let metadata = self
67 .get_metadata(name)
68 .await
69 .map_err(|err| {
70 error!("Failed to get metadata from pdev: {name} {err}");
71 Status::INTERNAL
72 })?
73 .map_err(Status::from_raw)?;
74 fidl::unpersist(&metadata).map_err(|err| {
75 error!("Failed to parse pdev metadata: {err}");
76 Status::INVALID_ARGS
77 })
78 }
79
80 async fn get_deserialized_metadata<T: serde::de::DeserializeOwned>(&self) -> Result<T, Status> {
81 let name = "fuchsia.driver.metadata.Dictionary";
82 let metadata = self
83 .get_metadata(name)
84 .await
85 .map_err(|err| {
86 error!("Failed to get metadata from pdev: {name} {err}");
87 Status::INTERNAL
88 })?
89 .map_err(Status::from_raw)?;
90 let dict: fidl_fuchsia_driver_metadata::Dictionary =
91 fidl::unpersist(&metadata).map_err(|err| {
92 error!("Failed to unpersist dictionary: {err}");
93 Status::INVALID_ARGS
94 })?;
95 fdf_metadata::from_dictionary(dict).map_err(|err| {
96 error!("Failed to deserialize config from dictionary: {err:?}");
97 Status::INVALID_ARGS
98 })
99 }
100}
101
102pub trait PdevExt {
105 fn connect_to_pdev(&self) -> Result<fpdev::DeviceProxy, fdf_component::DriverError>;
107}
108
109impl PdevExt for fdf_component::DriverContext {
110 fn connect_to_pdev(&self) -> Result<fpdev::DeviceProxy, fdf_component::DriverError> {
111 Ok(self
112 .incoming
113 .service_marker(fpdev::ServiceMarker)
114 .instance("pdev")
115 .connect()?
116 .connect_to_device()?)
117 }
118}
119
120fn map_mmio(mmio: fpdev::Mmio) -> Result<MmioRegion<VmoMemory>, Status> {
121 let (Some(vmo), Some(offset), Some(size)) = (mmio.vmo, mmio.offset, mmio.size) else {
122 error!("Mmio device missing vmo, offset or size");
123 return Err(Status::INTERNAL);
124 };
125 let offset = offset as usize;
126 let size = size as usize;
127
128 let mmio = VmoMapping::map(offset, size, vmo).map_err(|err| {
129 error!("Failed to map Mmio memory for vmo: {err}");
130 Status::INTERNAL
131 })?;
132 Ok(mmio)
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use fidl_test_metadata::{IntMetadata, Metadata};
139 use fuchsia_async::Task;
140 use futures_util::TryStreamExt;
141 use mmio::Mmio;
142 use std::collections::HashMap;
143 use zx::{Vmo, VmoOp};
144
145 struct TestServer {
146 mmios: Vec<(&'static str, Option<fpdev::Mmio>)>,
147 metadata: HashMap<&'static str, Vec<u8>>,
148 }
149
150 impl TestServer {
151 fn new() -> Self {
152 Self { mmios: Vec::new(), metadata: HashMap::new() }
153 }
154
155 fn append_mmio(&mut self, name: &'static str, vmo: Vmo, offset: usize, size: usize) {
156 self.mmios.push((
157 name,
158 Some(fpdev::Mmio {
159 offset: Some(offset as u64),
160 size: Some(size as u64),
161 vmo: Some(vmo),
162 ..Default::default()
163 }),
164 ));
165 }
166
167 fn set_typed_metadata<T: Persistable + Serializable>(&mut self, metadata: &T) {
168 let bytes = fidl::persist(metadata).unwrap();
169 self.metadata.insert(T::SERIALIZABLE_NAME, bytes);
170 }
171
172 async fn handle_requests(
173 &mut self,
174 mut requests: fpdev::DeviceRequestStream,
175 ) -> Result<(), fidl::Error> {
176 while let Some(req) = requests.try_next().await? {
177 match req {
178 fpdev::DeviceRequest::GetMmioById { index, responder } => {
179 responder.send(self.take_mmio_by_id(index).map_err(Status::into_raw))?;
180 }
181 fpdev::DeviceRequest::GetMmioByName { name, responder } => {
182 responder.send(self.take_mmio_by_name(&name).map_err(Status::into_raw))?;
183 }
184 fpdev::DeviceRequest::GetMetadata { id, responder } => {
185 responder.send(self.get_metadata(&id).map_err(Status::into_raw))?;
186 }
187 _ => {
188 unreachable!("not used by tests")
189 }
190 }
191 }
192 Ok(())
193 }
194
195 fn take_mmio_by_id(&mut self, id: u32) -> Result<fpdev::Mmio, Status> {
196 self.mmios
197 .get_mut(id as usize)
198 .ok_or(Status::NOT_FOUND)?
199 .1
200 .take()
201 .ok_or(Status::ALREADY_BOUND)
202 }
203
204 fn take_mmio_by_name(&mut self, name: &str) -> Result<fpdev::Mmio, Status> {
205 self.mmios
206 .iter_mut()
207 .find(|(n, _)| *n == name)
208 .ok_or(Status::NOT_FOUND)?
209 .1
210 .take()
211 .ok_or(Status::ALREADY_BOUND)
212 }
213
214 fn get_metadata(&self, id: &str) -> Result<&[u8], Status> {
215 self.metadata.get(id).map(|v| v.as_slice()).ok_or(Status::NOT_FOUND)
216 }
217
218 fn run(mut self) -> (fpdev::DeviceProxy, Task<Result<(), fidl::Error>>) {
219 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fpdev::DeviceMarker>();
220 let server = Task::local(async move { self.handle_requests(stream).await });
221 (proxy, server)
222 }
223 }
224
225 #[fuchsia::test]
226 async fn test_pdev() {
227 let mut server = TestServer::new();
228
229 let vmo = Vmo::create(4096).unwrap();
230 vmo.op_range(VmoOp::ZERO, 0, 4096).unwrap();
231 server.append_mmio("zero", vmo, 0, 4096);
232
233 let vmo = Vmo::create(1024).unwrap();
235 for i in 0..256 {
236 vmo.write(&((i as u32).to_le_bytes()), (i * size_of::<u32>()) as u64).unwrap();
237 }
238 server.append_mmio("dev", vmo, 32 * size_of::<u32>(), 16);
239
240 server.set_typed_metadata(&Metadata {
241 test_field: Some("foo".to_string()),
242 ..Default::default()
243 });
244
245 let (client, server) = server.run();
246
247 let mmio = client.map_mmio_by_id(1).await.unwrap();
248 assert_eq!(client.map_mmio_by_id(1).await.err(), Some(Status::ALREADY_BOUND));
249 assert_eq!(client.map_mmio_by_id(2).await.err(), Some(Status::NOT_FOUND));
250 assert_eq!(client.map_mmio_by_name("dev").await.err(), Some(Status::ALREADY_BOUND));
251
252 assert_eq!(mmio.load32(0), 32);
253
254 let mmio = client.map_mmio_by_name("zero").await.unwrap();
255 assert_eq!(mmio.len(), 4096);
256 assert_eq!(mmio.load64(128), 0);
257
258 assert_eq!(
259 client.get_typed_metadata::<Metadata>().await.unwrap(),
260 Metadata { test_field: Some("foo".to_string()), ..Default::default() }
261 );
262
263 assert_eq!(client.get_typed_metadata::<IntMetadata>().await.err(), Some(Status::NOT_FOUND));
264
265 let _ = server.abort().await;
266 }
267}