vmm/devices/virtio/vsock/
persist.rs

1// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Defines state and support structures for persisting Vsock devices and backends.
5
6use std::fmt::Debug;
7use std::sync::Arc;
8
9use serde::{Deserialize, Serialize};
10
11use super::*;
12use crate::devices::virtio::device::{ActiveState, DeviceState};
13use crate::devices::virtio::generated::virtio_ids::{self, VIRTIO_ID_VSOCK};
14use crate::devices::virtio::persist::VirtioDeviceState;
15use crate::devices::virtio::queue::FIRECRACKER_MAX_QUEUE_SIZE;
16use crate::devices::virtio::transport::VirtioInterrupt;
17use crate::snapshot::Persist;
18use crate::vstate::memory::GuestMemoryMmap;
19
20/// The Vsock serializable state.
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct VsockState {
23    /// The vsock backend state.
24    pub backend: VsockBackendState,
25    /// The vsock frontend state.
26    pub frontend: VsockFrontendState,
27}
28
29/// The Vsock frontend serializable state.
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct VsockFrontendState {
32    /// Context Identifier.
33    pub cid: u64,
34    pub virtio_state: VirtioDeviceState,
35}
36
37/// An enum for the serializable backend state types.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub enum VsockBackendState {
40    /// UDS backend state.
41    Uds(VsockUdsState),
42}
43
44/// The Vsock Unix Backend serializable state.
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct VsockUdsState {
47    /// The path for the UDS socket.
48    pub(crate) path: String,
49}
50
51/// A helper structure that holds the constructor arguments for VsockUnixBackend
52#[derive(Debug)]
53pub struct VsockConstructorArgs<B> {
54    /// Pointer to guest memory.
55    pub mem: GuestMemoryMmap,
56    /// The vsock Unix Backend.
57    pub backend: B,
58}
59
60/// A helper structure that holds the constructor arguments for VsockUnixBackend
61#[derive(Debug)]
62pub struct VsockUdsConstructorArgs {
63    /// cid available in VsockFrontendState.
64    pub cid: u64,
65}
66
67impl Persist<'_> for VsockUnixBackend {
68    type State = VsockBackendState;
69    type ConstructorArgs = VsockUdsConstructorArgs;
70    type Error = VsockUnixBackendError;
71
72    fn save(&self) -> Self::State {
73        VsockBackendState::Uds(VsockUdsState {
74            path: self.host_sock_path.clone(),
75        })
76    }
77
78    fn restore(
79        constructor_args: Self::ConstructorArgs,
80        state: &Self::State,
81    ) -> Result<Self, Self::Error> {
82        match state {
83            VsockBackendState::Uds(uds_state) => Ok(VsockUnixBackend::new(
84                constructor_args.cid,
85                uds_state.path.clone(),
86            )?),
87        }
88    }
89}
90
91impl<B> Persist<'_> for Vsock<B>
92where
93    B: VsockBackend + 'static + Debug,
94{
95    type State = VsockFrontendState;
96    type ConstructorArgs = VsockConstructorArgs<B>;
97    type Error = VsockError;
98
99    fn save(&self) -> Self::State {
100        VsockFrontendState {
101            cid: self.cid(),
102            virtio_state: VirtioDeviceState::from_device(self),
103        }
104    }
105
106    fn restore(
107        constructor_args: Self::ConstructorArgs,
108        state: &Self::State,
109    ) -> Result<Self, Self::Error> {
110        // Restore queues.
111        let queues = state
112            .virtio_state
113            .build_queues_checked(
114                &constructor_args.mem,
115                VIRTIO_ID_VSOCK,
116                defs::VSOCK_NUM_QUEUES,
117                FIRECRACKER_MAX_QUEUE_SIZE,
118            )
119            .map_err(VsockError::VirtioState)?;
120        let mut vsock = Self::with_queues(state.cid, constructor_args.backend, queues)?;
121
122        vsock.acked_features = state.virtio_state.acked_features;
123        vsock.avail_features = state.virtio_state.avail_features;
124        vsock.device_state = DeviceState::Inactive;
125        Ok(vsock)
126    }
127}
128
129#[cfg(test)]
130pub(crate) mod tests {
131    use super::device::AVAIL_FEATURES;
132    use super::*;
133    use crate::devices::virtio::device::VirtioDevice;
134    use crate::devices::virtio::test_utils::default_interrupt;
135    use crate::devices::virtio::vsock::defs::uapi;
136    use crate::devices::virtio::vsock::test_utils::{TestBackend, TestContext};
137    use crate::snapshot::Snapshot;
138    use crate::utils::byte_order;
139
140    impl Persist<'_> for TestBackend {
141        type State = VsockBackendState;
142        type ConstructorArgs = VsockUdsConstructorArgs;
143        type Error = VsockUnixBackendError;
144
145        fn save(&self) -> Self::State {
146            VsockBackendState::Uds(VsockUdsState {
147                path: "test".to_owned(),
148            })
149        }
150
151        fn restore(_: Self::ConstructorArgs, state: &Self::State) -> Result<Self, Self::Error> {
152            match state {
153                VsockBackendState::Uds(_) => Ok(TestBackend::new()),
154            }
155        }
156    }
157
158    #[test]
159    fn test_persist_uds_backend() {
160        let ctx = TestContext::new();
161        let device_features = AVAIL_FEATURES;
162        let driver_features: u64 = AVAIL_FEATURES | 1 | (1 << 32);
163        let device_pages = [
164            (device_features & 0xffff_ffff) as u32,
165            (device_features >> 32) as u32,
166        ];
167        let driver_pages = [
168            (driver_features & 0xffff_ffff) as u32,
169            (driver_features >> 32) as u32,
170        ];
171
172        // Test serialization
173        let mut mem = vec![0; 4096];
174
175        // Save backend and device state separately.
176        let state = VsockState {
177            backend: ctx.device.backend().save(),
178            frontend: ctx.device.save(),
179        };
180
181        Snapshot::new(&state).save(&mut mem.as_mut_slice()).unwrap();
182
183        let restored_state: VsockState = Snapshot::load_without_crc_check(mem.as_slice())
184            .unwrap()
185            .data;
186        let mut restored_device = Vsock::restore(
187            VsockConstructorArgs {
188                mem: ctx.mem.clone(),
189                backend: match restored_state.backend {
190                    VsockBackendState::Uds(uds_state) => {
191                        assert_eq!(uds_state.path, "test".to_owned());
192                        TestBackend::new()
193                    }
194                },
195            },
196            &restored_state.frontend,
197        )
198        .unwrap();
199
200        assert_eq!(restored_device.device_type(), VIRTIO_ID_VSOCK);
201        assert_eq!(restored_device.avail_features_by_page(0), device_pages[0]);
202        assert_eq!(restored_device.avail_features_by_page(1), device_pages[1]);
203        assert_eq!(restored_device.avail_features_by_page(2), 0);
204
205        restored_device.ack_features_by_page(0, driver_pages[0]);
206        restored_device.ack_features_by_page(1, driver_pages[1]);
207        restored_device.ack_features_by_page(2, 0);
208        restored_device.ack_features_by_page(0, !driver_pages[0]);
209        assert_eq!(
210            restored_device.acked_features(),
211            device_features & driver_features
212        );
213
214        // Test reading 32-bit chunks.
215        let mut data = [0u8; 8];
216        restored_device.read_config(0, &mut data[..4]);
217        assert_eq!(
218            u64::from(byte_order::read_le_u32(&data[..])),
219            ctx.cid & 0xffff_ffff
220        );
221        restored_device.read_config(4, &mut data[4..]);
222        assert_eq!(
223            u64::from(byte_order::read_le_u32(&data[4..])),
224            (ctx.cid >> 32) & 0xffff_ffff
225        );
226
227        // Test reading 64-bit.
228        let mut data = [0u8; 8];
229        restored_device.read_config(0, &mut data);
230        assert_eq!(byte_order::read_le_u64(&data), ctx.cid);
231
232        // Check that out-of-bounds reading doesn't mutate the destination buffer.
233        let mut data = [0u8, 1, 2, 3, 4, 5, 6, 7];
234        restored_device.read_config(2, &mut data);
235        assert_eq!(data, [0u8, 1, 2, 3, 4, 5, 6, 7]);
236    }
237}