Skip to main content

chipset_device_worker/
worker.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! A worker for running ChipsetDevice implementations in a separate process.
5//!
6//! This worker provides process isolation for any device implementing the
7//! ChipsetDevice trait. It handles serialization and deserialization of
8//! device operations across process boundaries.
9
10#![forbid(unsafe_code)]
11
12mod configure;
13
14use crate::RemoteDynamicResolvers;
15use crate::guestmem::GuestMemoryRemoteBuilder;
16use crate::protocol::*;
17use anyhow::Context;
18use chipset_device::ChipsetDevice;
19use chipset_device::io::IoResult;
20use chipset_device::io::deferred::DeferredToken;
21use chipset_device::pci::ByteEnabledDwordRead;
22use chipset_device::pci::ByteEnabledDwordWrite;
23use chipset_device_resources::ErasedChipsetDevice;
24use chipset_device_resources::ResolveChipsetDeviceHandleParams;
25use mesh::MeshPayload;
26use mesh::error::RemoteError;
27use mesh_worker::Worker;
28use mesh_worker::WorkerId;
29use mesh_worker::WorkerRpc;
30use pal_async::DefaultPool;
31use std::task::Poll;
32use vm_resource::Resource;
33use vm_resource::ResourceResolver;
34use vm_resource::kind::ChipsetDeviceHandleKind;
35use vmcore::device_state::ChangeDeviceState;
36use vmcore::save_restore::AsyncProtobufSaveRestore;
37
38/// Worker ID for ChipsetDevice workers.
39pub(crate) const fn remote_chipset_device_worker_id<T: RemoteDynamicResolvers>()
40-> WorkerId<RemoteChipsetDeviceWorkerParameters<T>> {
41    WorkerId::new(T::WORKER_ID_STR)
42}
43
44/// Parameters for launching a remote chipset device worker.
45#[derive(MeshPayload)]
46pub struct RemoteChipsetDeviceWorkerParameters<T> {
47    pub(crate) device: Resource<ChipsetDeviceHandleKind>,
48    pub(crate) dyn_resolvers: T,
49    pub(crate) inputs: RemoteChipsetDeviceHandleParams,
50
51    pub(crate) req_recv: mesh::Receiver<DeviceRequest>,
52    pub(crate) resp_send: mesh::Sender<DeviceResponse>,
53    pub(crate) cap_send: mesh::OneshotSender<DeviceInit>,
54}
55
56#[derive(MeshPayload)]
57pub(crate) struct RemoteChipsetDeviceHandleParams {
58    pub device_name: String,
59    pub is_restoring: bool,
60    pub vmtime: vmcore::vmtime::VmTimeSourceBuilder,
61    pub guest_memory: GuestMemoryRemoteBuilder,
62    pub encrypted_guest_memory: GuestMemoryRemoteBuilder,
63}
64
65/// The chipset device worker.
66///
67/// This worker wraps any device implementing ChipsetDevice and handles
68/// device operations sent via mesh channels.
69pub struct RemoteChipsetDeviceWorker<T> {
70    running: bool,
71    device: ErasedChipsetDevice,
72    pool: Option<DefaultPool>,
73    req_recv: mesh::Receiver<DeviceRequest>,
74    resp_send: mesh::Sender<DeviceResponse>,
75    deferred_reads: Vec<DeferredRead>,
76    deferred_writes: Vec<DeferredWrite>,
77
78    _phantom_resolvers: std::marker::PhantomData<T>,
79}
80
81struct DeferredRead {
82    id: usize,
83    token: DeferredToken,
84    size: usize,
85}
86
87struct DeferredWrite {
88    id: usize,
89    token: DeferredToken,
90}
91
92impl<T: RemoteDynamicResolvers> Worker for RemoteChipsetDeviceWorker<T> {
93    type Parameters = RemoteChipsetDeviceWorkerParameters<T>;
94    type State = ();
95    const ID: WorkerId<Self::Parameters> = remote_chipset_device_worker_id();
96
97    fn new(params: Self::Parameters) -> anyhow::Result<Self> {
98        let mut pool = DefaultPool::new();
99
100        let RemoteChipsetDeviceWorkerParameters {
101            device,
102            dyn_resolvers,
103            inputs,
104
105            req_recv,
106            resp_send,
107            cap_send,
108        } = params;
109
110        let mut resolver = ResourceResolver::new();
111
112        let driver = pool.driver();
113        let mut device = pool
114            .run_until(async move {
115                dyn_resolvers
116                    .register_remote_dynamic_resolvers(&mut resolver)
117                    .await?;
118                resolver
119                    .resolve(
120                        device,
121                        ResolveChipsetDeviceHandleParams {
122                            device_name: &inputs.device_name,
123                            guest_memory: &inputs.guest_memory.build("remote_gm"),
124                            encrypted_guest_memory: &inputs
125                                .encrypted_guest_memory
126                                .build("remote_enc_gm"),
127                            vmtime: &inputs
128                                .vmtime
129                                .build(&driver)
130                                .await
131                                .context("failed to build vmtime source")?,
132                            is_restoring: inputs.is_restoring,
133                            task_driver_source: &vmcore::vm_task::VmTaskDriverSource::new(
134                                vmcore::vm_task::thread::ThreadDriverBackend::new(driver),
135                            ),
136                            // TODO: Actually wire these up
137                            configure: &mut configure::RemoteConfigureChipsetDevice {},
138                            register_mmio: &mut configure::RemoteRegisterMmio {},
139                            register_pio: &mut configure::RemoteRegisterPio {},
140                        },
141                    )
142                    .await
143                    .context("failed to resolve device")
144            })?
145            .0;
146
147        if device.supports_acknowledge_pic_interrupt().is_some()
148            || device.supports_handle_eoi().is_some()
149            || device.supports_line_interrupt_target().is_some()
150            || device.supports_tdisp_host().is_some()
151        {
152            anyhow::bail!("remote device requires unimplemented functionality");
153        }
154
155        cap_send.send(DeviceInit {
156            mmio: device.supports_mmio().map(|m| MmioInit {
157                static_regions: m
158                    .get_static_regions()
159                    .iter()
160                    .map(|(name, range)| ((*name).into(), *range.start(), *range.end()))
161                    .collect(),
162            }),
163            pio: device.supports_pio().map(|p| PioInit {
164                static_regions: p
165                    .get_static_regions()
166                    .iter()
167                    .map(|(name, range)| ((*name).into(), *range.start(), *range.end()))
168                    .collect(),
169            }),
170            pci: device.supports_pci().map(|pci| PciInit {
171                suggested_bdf: pci.suggested_bdf(),
172            }),
173        });
174
175        Ok(Self {
176            running: false,
177            device,
178            pool: Some(pool),
179            req_recv,
180            resp_send,
181            deferred_reads: Vec::new(),
182            deferred_writes: Vec::new(),
183            _phantom_resolvers: std::marker::PhantomData,
184        })
185    }
186
187    fn restart(_state: Self::State) -> anyhow::Result<Self> {
188        todo!()
189    }
190
191    fn run(mut self, mut rpc_recv: mesh::Receiver<WorkerRpc<Self::State>>) -> anyhow::Result<()> {
192        self.pool.take().unwrap().run_until(async move {
193            loop {
194                enum WorkerEvent {
195                    Rpc(WorkerRpc<()>),
196                    DeviceRequest(DeviceRequest),
197                }
198
199                let event = std::future::poll_fn(|cx| {
200                    if self.running
201                        && let Some(poll_device) = self.device.supports_poll_device()
202                    {
203                        poll_device.poll_device(cx);
204                    }
205
206                    self.deferred_reads
207                        .extract_if(.., |read| {
208                            let mut data = vec![0; read.size];
209                            match read.token.poll_read(cx, &mut data) {
210                                Poll::Ready(r) => {
211                                    self.resp_send.send(DeviceResponse::Read {
212                                        id: read.id,
213                                        result: r.map(|_| data),
214                                    });
215                                    true
216                                }
217                                Poll::Pending => false,
218                            }
219                        })
220                        .for_each(|_| ());
221
222                    self.deferred_writes
223                        .extract_if(.., |write| match write.token.poll_write(cx) {
224                            Poll::Ready(r) => {
225                                self.resp_send.send(DeviceResponse::Write {
226                                    id: write.id,
227                                    result: r,
228                                });
229                                true
230                            }
231                            Poll::Pending => false,
232                        })
233                        .for_each(|_| ());
234
235                    // If either of these channels fail, we fail the worker too.
236                    if let Poll::Ready(r) = rpc_recv.poll_recv(cx) {
237                        return Poll::Ready(r.map(WorkerEvent::Rpc));
238                    }
239                    if let Poll::Ready(r) = self.req_recv.poll_recv(cx) {
240                        return Poll::Ready(r.map(WorkerEvent::DeviceRequest));
241                    }
242                    Poll::Pending
243                })
244                .await?;
245
246                match event {
247                    WorkerEvent::Rpc(rpc) => match rpc {
248                        WorkerRpc::Inspect(deferred) => {
249                            deferred.inspect(&mut self.device);
250                        }
251                        WorkerRpc::Stop => {
252                            if self.running {
253                                self.device.stop().await;
254                                self.running = false;
255                            }
256                            self.device.teardown().await;
257                            return Ok(());
258                        }
259                        WorkerRpc::Restart(rpc) => {
260                            rpc.complete(Err(RemoteError::new(anyhow::anyhow!("not supported"))));
261                        }
262                    },
263                    WorkerEvent::DeviceRequest(req) => match req {
264                        DeviceRequest::Start => {
265                            self.device.start();
266                            self.running = true;
267                        }
268                        DeviceRequest::Teardown(rpc) => {
269                            assert!(
270                                !self.running,
271                                "remote device must be stopped before teardown"
272                            );
273                            self.device.teardown().await;
274                            drop(self.deferred_reads);
275                            drop(self.deferred_writes);
276                            rpc.complete(());
277                            return Ok(());
278                        }
279                        DeviceRequest::Stop(rpc) => {
280                            self.device.stop().await;
281                            self.running = false;
282                            rpc.complete(());
283                        }
284                        DeviceRequest::Reset(rpc) => {
285                            self.deferred_reads.clear();
286                            self.deferred_writes.clear();
287                            rpc.handle(async |()| self.device.reset().await).await
288                        }
289                        DeviceRequest::MmioRead(ReadRequest { id, address, size }) => {
290                            let mut data = vec![0; size];
291                            let result = self
292                                .device
293                                .supports_mmio()
294                                .unwrap()
295                                .mmio_read(address, &mut data);
296                            self.handle_read_result(id, result, data);
297                        }
298                        DeviceRequest::MmioWrite(WriteRequest { id, address, data }) => {
299                            let result = self
300                                .device
301                                .supports_mmio()
302                                .unwrap()
303                                .mmio_write(address, &data);
304                            self.handle_write_result(id, result);
305                        }
306                        DeviceRequest::PioRead(ReadRequest { id, address, size }) => {
307                            let mut data = vec![0; size];
308                            let result = self
309                                .device
310                                .supports_pio()
311                                .unwrap()
312                                .io_read(address, &mut data);
313                            self.handle_read_result(id, result, data);
314                        }
315                        DeviceRequest::PioWrite(WriteRequest { id, address, data }) => {
316                            let result =
317                                self.device.supports_pio().unwrap().io_write(address, &data);
318                            self.handle_write_result(id, result);
319                        }
320                        DeviceRequest::PciConfigRead(
321                            ReadRequest { id, address, size },
322                            byte_enable,
323                        ) => {
324                            assert_eq!(size, 4);
325                            let mut data_u32 = 0;
326                            let mut value = ByteEnabledDwordRead::new(&mut data_u32, byte_enable);
327                            let result = self
328                                .device
329                                .supports_pci()
330                                .unwrap()
331                                .pci_cfg_read(address, value.reborrow());
332                            self.handle_read_result(id, result, data_u32.to_ne_bytes().to_vec());
333                        }
334                        DeviceRequest::PciConfigWrite(
335                            WriteRequest { id, address, data },
336                            byte_enable,
337                        ) => {
338                            let value = ByteEnabledDwordWrite::new(data, byte_enable);
339                            let result = self
340                                .device
341                                .supports_pci()
342                                .unwrap()
343                                .pci_cfg_write(address, value);
344                            self.handle_write_result(id, result);
345                        }
346                        DeviceRequest::Save(rpc) => {
347                            rpc.handle_failable(async |()| self.device.save().await)
348                                .await
349                        }
350                        DeviceRequest::Restore(rpc) => {
351                            rpc.handle_failable(async |state| self.device.restore(state).await)
352                                .await
353                        }
354                    },
355                }
356            }
357        })
358    }
359}
360
361impl<T> RemoteChipsetDeviceWorker<T> {
362    fn handle_read_result(&mut self, id: usize, result: IoResult, data: Vec<u8>) {
363        match result {
364            IoResult::Ok => self.resp_send.send(DeviceResponse::Read {
365                id,
366                result: Ok(data),
367            }),
368            IoResult::Err(io_error) => self.resp_send.send(DeviceResponse::Read {
369                id,
370                result: Err(io_error),
371            }),
372            IoResult::Defer(token) => self.deferred_reads.push(DeferredRead {
373                id,
374                token,
375                size: data.len(),
376            }),
377        }
378    }
379
380    fn handle_write_result(&mut self, id: usize, result: IoResult) {
381        match result {
382            IoResult::Ok => self
383                .resp_send
384                .send(DeviceResponse::Write { id, result: Ok(()) }),
385            IoResult::Err(io_error) => self.resp_send.send(DeviceResponse::Write {
386                id,
387                result: Err(io_error),
388            }),
389            IoResult::Defer(token) => self.deferred_writes.push(DeferredWrite { id, token }),
390        }
391    }
392}