1 //! Driver for VirtIO socket devices.
2 #![deny(unsafe_op_in_unsafe_fn)]
3 
4 use super::error::SocketError;
5 use super::protocol::{
6     Feature, StreamShutdown, VirtioVsockConfig, VirtioVsockHdr, VirtioVsockOp, VsockAddr,
7 };
8 use super::DEFAULT_RX_BUFFER_SIZE;
9 use crate::hal::Hal;
10 use crate::queue::VirtQueue;
11 use crate::transport::Transport;
12 use crate::volatile::volread;
13 use crate::{Error, Result};
14 use alloc::boxed::Box;
15 use core::mem::size_of;
16 use core::ptr::{null_mut, NonNull};
17 use log::debug;
18 use zerocopy::{AsBytes, FromBytes, FromZeroes};
19 
20 pub(crate) const RX_QUEUE_IDX: u16 = 0;
21 pub(crate) const TX_QUEUE_IDX: u16 = 1;
22 const EVENT_QUEUE_IDX: u16 = 2;
23 
24 pub(crate) const QUEUE_SIZE: usize = 8;
25 const SUPPORTED_FEATURES: Feature = Feature::RING_EVENT_IDX.union(Feature::RING_INDIRECT_DESC);
26 
27 /// Information about a particular vsock connection.
28 #[derive(Clone, Debug, Default, PartialEq, Eq)]
29 pub struct ConnectionInfo {
30     /// The address of the peer.
31     pub dst: VsockAddr,
32     /// The local port number associated with the connection.
33     pub src_port: u32,
34     /// The last `buf_alloc` value the peer sent to us, indicating how much receive buffer space in
35     /// bytes it has allocated for packet bodies.
36     peer_buf_alloc: u32,
37     /// The last `fwd_cnt` value the peer sent to us, indicating how many bytes of packet bodies it
38     /// has finished processing.
39     peer_fwd_cnt: u32,
40     /// The number of bytes of packet bodies which we have sent to the peer.
41     tx_cnt: u32,
42     /// The number of bytes of buffer space we have allocated to receive packet bodies from the
43     /// peer.
44     pub buf_alloc: u32,
45     /// The number of bytes of packet bodies which we have received from the peer and handled.
46     fwd_cnt: u32,
47     /// Whether we have recently requested credit from the peer.
48     ///
49     /// This is set to true when we send a `VIRTIO_VSOCK_OP_CREDIT_REQUEST`, and false when we
50     /// receive a `VIRTIO_VSOCK_OP_CREDIT_UPDATE`.
51     has_pending_credit_request: bool,
52 }
53 
54 impl ConnectionInfo {
55     /// Creates a new `ConnectionInfo` for the given peer address and local port, and default values
56     /// for everything else.
new(destination: VsockAddr, src_port: u32) -> Self57     pub fn new(destination: VsockAddr, src_port: u32) -> Self {
58         Self {
59             dst: destination,
60             src_port,
61             ..Default::default()
62         }
63     }
64 
65     /// Updates this connection info with the peer buffer allocation and forwarded count from the
66     /// given event.
update_for_event(&mut self, event: &VsockEvent)67     pub fn update_for_event(&mut self, event: &VsockEvent) {
68         self.peer_buf_alloc = event.buffer_status.buffer_allocation;
69         self.peer_fwd_cnt = event.buffer_status.forward_count;
70 
71         if let VsockEventType::CreditUpdate = event.event_type {
72             self.has_pending_credit_request = false;
73         }
74     }
75 
76     /// Increases the forwarded count recorded for this connection by the given number of bytes.
77     ///
78     /// This should be called once received data has been passed to the client, so there is buffer
79     /// space available for more.
done_forwarding(&mut self, length: usize)80     pub fn done_forwarding(&mut self, length: usize) {
81         self.fwd_cnt += length as u32;
82     }
83 
84     /// Returns the number of bytes of RX buffer space the peer has available to receive packet body
85     /// data from us.
peer_free(&self) -> u3286     fn peer_free(&self) -> u32 {
87         self.peer_buf_alloc - (self.tx_cnt - self.peer_fwd_cnt)
88     }
89 
new_header(&self, src_cid: u64) -> VirtioVsockHdr90     fn new_header(&self, src_cid: u64) -> VirtioVsockHdr {
91         VirtioVsockHdr {
92             src_cid: src_cid.into(),
93             dst_cid: self.dst.cid.into(),
94             src_port: self.src_port.into(),
95             dst_port: self.dst.port.into(),
96             buf_alloc: self.buf_alloc.into(),
97             fwd_cnt: self.fwd_cnt.into(),
98             ..Default::default()
99         }
100     }
101 }
102 
103 /// An event received from a VirtIO socket device.
104 #[derive(Clone, Debug, Eq, PartialEq)]
105 pub struct VsockEvent {
106     /// The source of the event, i.e. the peer who sent it.
107     pub source: VsockAddr,
108     /// The destination of the event, i.e. the CID and port on our side.
109     pub destination: VsockAddr,
110     /// The peer's buffer status for the connection.
111     pub buffer_status: VsockBufferStatus,
112     /// The type of event.
113     pub event_type: VsockEventType,
114 }
115 
116 impl VsockEvent {
117     /// Returns whether the event matches the given connection.
matches_connection(&self, connection_info: &ConnectionInfo, guest_cid: u64) -> bool118     pub fn matches_connection(&self, connection_info: &ConnectionInfo, guest_cid: u64) -> bool {
119         self.source == connection_info.dst
120             && self.destination.cid == guest_cid
121             && self.destination.port == connection_info.src_port
122     }
123 
from_header(header: &VirtioVsockHdr) -> Result<Self>124     fn from_header(header: &VirtioVsockHdr) -> Result<Self> {
125         let op = header.op()?;
126         let buffer_status = VsockBufferStatus {
127             buffer_allocation: header.buf_alloc.into(),
128             forward_count: header.fwd_cnt.into(),
129         };
130         let source = header.source();
131         let destination = header.destination();
132 
133         let event_type = match op {
134             VirtioVsockOp::Request => {
135                 header.check_data_is_empty()?;
136                 VsockEventType::ConnectionRequest
137             }
138             VirtioVsockOp::Response => {
139                 header.check_data_is_empty()?;
140                 VsockEventType::Connected
141             }
142             VirtioVsockOp::CreditUpdate => {
143                 header.check_data_is_empty()?;
144                 VsockEventType::CreditUpdate
145             }
146             VirtioVsockOp::Rst | VirtioVsockOp::Shutdown => {
147                 header.check_data_is_empty()?;
148                 debug!("Disconnected from the peer");
149                 let reason = if op == VirtioVsockOp::Rst {
150                     DisconnectReason::Reset
151                 } else {
152                     DisconnectReason::Shutdown
153                 };
154                 VsockEventType::Disconnected { reason }
155             }
156             VirtioVsockOp::Rw => VsockEventType::Received {
157                 length: header.len() as usize,
158             },
159             VirtioVsockOp::CreditRequest => {
160                 header.check_data_is_empty()?;
161                 VsockEventType::CreditRequest
162             }
163             VirtioVsockOp::Invalid => return Err(SocketError::InvalidOperation.into()),
164         };
165 
166         Ok(VsockEvent {
167             source,
168             destination,
169             buffer_status,
170             event_type,
171         })
172     }
173 }
174 
175 #[derive(Clone, Debug, Eq, PartialEq)]
176 pub struct VsockBufferStatus {
177     pub buffer_allocation: u32,
178     pub forward_count: u32,
179 }
180 
181 /// The reason why a vsock connection was closed.
182 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
183 pub enum DisconnectReason {
184     /// The peer has either closed the connection in response to our shutdown request, or forcibly
185     /// closed it of its own accord.
186     Reset,
187     /// The peer asked to shut down the connection.
188     Shutdown,
189 }
190 
191 /// Details of the type of an event received from a VirtIO socket.
192 #[derive(Clone, Debug, Eq, PartialEq)]
193 pub enum VsockEventType {
194     /// The peer requests to establish a connection with us.
195     ConnectionRequest,
196     /// The connection was successfully established.
197     Connected,
198     /// The connection was closed.
199     Disconnected {
200         /// The reason for the disconnection.
201         reason: DisconnectReason,
202     },
203     /// Data was received on the connection.
204     Received {
205         /// The length of the data in bytes.
206         length: usize,
207     },
208     /// The peer requests us to send a credit update.
209     CreditRequest,
210     /// The peer just sent us a credit update with nothing else.
211     CreditUpdate,
212 }
213 
214 /// Low-level driver for a VirtIO socket device.
215 ///
216 /// You probably want to use [`VsockConnectionManager`](super::VsockConnectionManager) rather than
217 /// using this directly.
218 ///
219 /// `RX_BUFFER_SIZE` is the size in bytes of each buffer used in the RX virtqueue. This must be
220 /// bigger than `size_of::<VirtioVsockHdr>()`.
221 pub struct VirtIOSocket<H: Hal, T: Transport, const RX_BUFFER_SIZE: usize = DEFAULT_RX_BUFFER_SIZE>
222 {
223     transport: T,
224     /// Virtqueue to receive packets.
225     rx: VirtQueue<H, { QUEUE_SIZE }>,
226     tx: VirtQueue<H, { QUEUE_SIZE }>,
227     /// Virtqueue to receive events from the device.
228     event: VirtQueue<H, { QUEUE_SIZE }>,
229     /// The guest_cid field contains the guest’s context ID, which uniquely identifies
230     /// the device for its lifetime. The upper 32 bits of the CID are reserved and zeroed.
231     guest_cid: u64,
232     rx_queue_buffers: [NonNull<[u8; RX_BUFFER_SIZE]>; QUEUE_SIZE],
233 }
234 
235 // SAFETY: The `rx_queue_buffers` can be accessed from any thread.
236 unsafe impl<H: Hal, T: Transport + Send, const RX_BUFFER_SIZE: usize> Send
237     for VirtIOSocket<H, T, RX_BUFFER_SIZE>
238 where
239     VirtQueue<H, QUEUE_SIZE>: Send,
240 {
241 }
242 
243 // SAFETY: A `&VirtIOSocket` only allows reading the guest CID from a field.
244 unsafe impl<H: Hal, T: Transport + Sync, const RX_BUFFER_SIZE: usize> Sync
245     for VirtIOSocket<H, T, RX_BUFFER_SIZE>
246 where
247     VirtQueue<H, QUEUE_SIZE>: Sync,
248 {
249 }
250 
251 impl<H: Hal, T: Transport, const RX_BUFFER_SIZE: usize> Drop
252     for VirtIOSocket<H, T, RX_BUFFER_SIZE>
253 {
drop(&mut self)254     fn drop(&mut self) {
255         // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
256         // after they have been freed.
257         self.transport.queue_unset(RX_QUEUE_IDX);
258         self.transport.queue_unset(TX_QUEUE_IDX);
259         self.transport.queue_unset(EVENT_QUEUE_IDX);
260 
261         for buffer in self.rx_queue_buffers {
262             // Safe because we obtained the RX buffer pointer from Box::into_raw, and it won't be
263             // used anywhere else after the driver is destroyed.
264             unsafe { drop(Box::from_raw(buffer.as_ptr())) };
265         }
266     }
267 }
268 
269 impl<H: Hal, T: Transport, const RX_BUFFER_SIZE: usize> VirtIOSocket<H, T, RX_BUFFER_SIZE> {
270     /// Create a new VirtIO Vsock driver.
new(mut transport: T) -> Result<Self>271     pub fn new(mut transport: T) -> Result<Self> {
272         assert!(RX_BUFFER_SIZE > size_of::<VirtioVsockHdr>());
273 
274         let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
275 
276         let config = transport.config_space::<VirtioVsockConfig>()?;
277         debug!("config: {:?}", config);
278         // Safe because config is a valid pointer to the device configuration space.
279         let guest_cid = unsafe {
280             volread!(config, guest_cid_low) as u64 | (volread!(config, guest_cid_high) as u64) << 32
281         };
282         debug!("guest cid: {guest_cid:?}");
283 
284         let mut rx = VirtQueue::new(
285             &mut transport,
286             RX_QUEUE_IDX,
287             negotiated_features.contains(Feature::RING_INDIRECT_DESC),
288             negotiated_features.contains(Feature::RING_EVENT_IDX),
289         )?;
290         let tx = VirtQueue::new(
291             &mut transport,
292             TX_QUEUE_IDX,
293             negotiated_features.contains(Feature::RING_INDIRECT_DESC),
294             negotiated_features.contains(Feature::RING_EVENT_IDX),
295         )?;
296         let event = VirtQueue::new(
297             &mut transport,
298             EVENT_QUEUE_IDX,
299             negotiated_features.contains(Feature::RING_INDIRECT_DESC),
300             negotiated_features.contains(Feature::RING_EVENT_IDX),
301         )?;
302 
303         // Allocate and add buffers for the RX queue.
304         let mut rx_queue_buffers = [null_mut(); QUEUE_SIZE];
305         for (i, rx_queue_buffer) in rx_queue_buffers.iter_mut().enumerate() {
306             let mut buffer: Box<[u8; RX_BUFFER_SIZE]> = FromZeroes::new_box_zeroed();
307             // Safe because the buffer lives as long as the queue, as specified in the function
308             // safety requirement, and we don't access it until it is popped.
309             let token = unsafe { rx.add(&[], &mut [buffer.as_mut_slice()]) }?;
310             assert_eq!(i, token.into());
311             *rx_queue_buffer = Box::into_raw(buffer);
312         }
313         let rx_queue_buffers = rx_queue_buffers.map(|ptr| NonNull::new(ptr).unwrap());
314 
315         transport.finish_init();
316         if rx.should_notify() {
317             transport.notify(RX_QUEUE_IDX);
318         }
319 
320         Ok(Self {
321             transport,
322             rx,
323             tx,
324             event,
325             guest_cid,
326             rx_queue_buffers,
327         })
328     }
329 
330     /// Returns the CID which has been assigned to this guest.
guest_cid(&self) -> u64331     pub fn guest_cid(&self) -> u64 {
332         self.guest_cid
333     }
334 
335     /// Sends a request to connect to the given destination.
336     ///
337     /// This returns as soon as the request is sent; you should wait until `poll` returns a
338     /// `VsockEventType::Connected` event indicating that the peer has accepted the connection
339     /// before sending data.
connect(&mut self, connection_info: &ConnectionInfo) -> Result340     pub fn connect(&mut self, connection_info: &ConnectionInfo) -> Result {
341         let header = VirtioVsockHdr {
342             op: VirtioVsockOp::Request.into(),
343             ..connection_info.new_header(self.guest_cid)
344         };
345         // Sends a header only packet to the TX queue to connect the device to the listening socket
346         // at the given destination.
347         self.send_packet_to_tx_queue(&header, &[])
348     }
349 
350     /// Accepts the given connection from a peer.
accept(&mut self, connection_info: &ConnectionInfo) -> Result351     pub fn accept(&mut self, connection_info: &ConnectionInfo) -> Result {
352         let header = VirtioVsockHdr {
353             op: VirtioVsockOp::Response.into(),
354             ..connection_info.new_header(self.guest_cid)
355         };
356         self.send_packet_to_tx_queue(&header, &[])
357     }
358 
359     /// Requests the peer to send us a credit update for the given connection.
request_credit(&mut self, connection_info: &ConnectionInfo) -> Result360     fn request_credit(&mut self, connection_info: &ConnectionInfo) -> Result {
361         let header = VirtioVsockHdr {
362             op: VirtioVsockOp::CreditRequest.into(),
363             ..connection_info.new_header(self.guest_cid)
364         };
365         self.send_packet_to_tx_queue(&header, &[])
366     }
367 
368     /// Sends the buffer to the destination.
send(&mut self, buffer: &[u8], connection_info: &mut ConnectionInfo) -> Result369     pub fn send(&mut self, buffer: &[u8], connection_info: &mut ConnectionInfo) -> Result {
370         self.check_peer_buffer_is_sufficient(connection_info, buffer.len())?;
371 
372         let len = buffer.len() as u32;
373         let header = VirtioVsockHdr {
374             op: VirtioVsockOp::Rw.into(),
375             len: len.into(),
376             ..connection_info.new_header(self.guest_cid)
377         };
378         connection_info.tx_cnt += len;
379         self.send_packet_to_tx_queue(&header, buffer)
380     }
381 
check_peer_buffer_is_sufficient( &mut self, connection_info: &mut ConnectionInfo, buffer_len: usize, ) -> Result382     fn check_peer_buffer_is_sufficient(
383         &mut self,
384         connection_info: &mut ConnectionInfo,
385         buffer_len: usize,
386     ) -> Result {
387         if connection_info.peer_free() as usize >= buffer_len {
388             Ok(())
389         } else {
390             // Request an update of the cached peer credit, if we haven't already done so, and tell
391             // the caller to try again later.
392             if !connection_info.has_pending_credit_request {
393                 self.request_credit(connection_info)?;
394                 connection_info.has_pending_credit_request = true;
395             }
396             Err(SocketError::InsufficientBufferSpaceInPeer.into())
397         }
398     }
399 
400     /// Tells the peer how much buffer space we have to receive data.
credit_update(&mut self, connection_info: &ConnectionInfo) -> Result401     pub fn credit_update(&mut self, connection_info: &ConnectionInfo) -> Result {
402         let header = VirtioVsockHdr {
403             op: VirtioVsockOp::CreditUpdate.into(),
404             ..connection_info.new_header(self.guest_cid)
405         };
406         self.send_packet_to_tx_queue(&header, &[])
407     }
408 
409     /// Polls the RX virtqueue for the next event, and calls the given handler function to handle
410     /// it.
poll( &mut self, handler: impl FnOnce(VsockEvent, &[u8]) -> Result<Option<VsockEvent>>, ) -> Result<Option<VsockEvent>>411     pub fn poll(
412         &mut self,
413         handler: impl FnOnce(VsockEvent, &[u8]) -> Result<Option<VsockEvent>>,
414     ) -> Result<Option<VsockEvent>> {
415         let Some((header, body, token)) = self.pop_packet_from_rx_queue()? else {
416             return Ok(None);
417         };
418 
419         let result = VsockEvent::from_header(&header).and_then(|event| handler(event, body));
420 
421         unsafe {
422             // TODO: What about if both handler and this give errors?
423             self.add_buffer_to_rx_queue(token)?;
424         }
425 
426         result
427     }
428 
429     /// Requests to shut down the connection cleanly, sending hints about whether we will send or
430     /// receive more data.
431     ///
432     /// This returns as soon as the request is sent; you should wait until `poll` returns a
433     /// `VsockEventType::Disconnected` event if you want to know that the peer has acknowledged the
434     /// shutdown.
shutdown_with_hints( &mut self, connection_info: &ConnectionInfo, hints: StreamShutdown, ) -> Result435     pub fn shutdown_with_hints(
436         &mut self,
437         connection_info: &ConnectionInfo,
438         hints: StreamShutdown,
439     ) -> Result {
440         let header = VirtioVsockHdr {
441             op: VirtioVsockOp::Shutdown.into(),
442             flags: hints.into(),
443             ..connection_info.new_header(self.guest_cid)
444         };
445         self.send_packet_to_tx_queue(&header, &[])
446     }
447 
448     /// Requests to shut down the connection cleanly, telling the peer that we won't send or receive
449     /// any more data.
450     ///
451     /// This returns as soon as the request is sent; you should wait until `poll` returns a
452     /// `VsockEventType::Disconnected` event if you want to know that the peer has acknowledged the
453     /// shutdown.
shutdown(&mut self, connection_info: &ConnectionInfo) -> Result454     pub fn shutdown(&mut self, connection_info: &ConnectionInfo) -> Result {
455         self.shutdown_with_hints(
456             connection_info,
457             StreamShutdown::SEND | StreamShutdown::RECEIVE,
458         )
459     }
460 
461     /// Forcibly closes the connection without waiting for the peer.
force_close(&mut self, connection_info: &ConnectionInfo) -> Result462     pub fn force_close(&mut self, connection_info: &ConnectionInfo) -> Result {
463         let header = VirtioVsockHdr {
464             op: VirtioVsockOp::Rst.into(),
465             ..connection_info.new_header(self.guest_cid)
466         };
467         self.send_packet_to_tx_queue(&header, &[])?;
468         Ok(())
469     }
470 
send_packet_to_tx_queue(&mut self, header: &VirtioVsockHdr, buffer: &[u8]) -> Result471     fn send_packet_to_tx_queue(&mut self, header: &VirtioVsockHdr, buffer: &[u8]) -> Result {
472         let _len = if buffer.is_empty() {
473             self.tx
474                 .add_notify_wait_pop(&[header.as_bytes()], &mut [], &mut self.transport)?
475         } else {
476             self.tx.add_notify_wait_pop(
477                 &[header.as_bytes(), buffer],
478                 &mut [],
479                 &mut self.transport,
480             )?
481         };
482         Ok(())
483     }
484 
485     /// Adds the buffer at the given index in `rx_queue_buffers` back to the RX queue.
486     ///
487     /// # Safety
488     ///
489     /// The buffer must not currently be in the RX queue, and no other references to it must exist
490     /// between when this method is called and when it is popped from the queue.
add_buffer_to_rx_queue(&mut self, index: u16) -> Result491     unsafe fn add_buffer_to_rx_queue(&mut self, index: u16) -> Result {
492         // Safe because the buffer lives as long as the queue, and the caller guarantees that it's
493         // not currently in the queue or referred to anywhere else until it is popped.
494         unsafe {
495             let buffer = self
496                 .rx_queue_buffers
497                 .get_mut(usize::from(index))
498                 .ok_or(Error::WrongToken)?
499                 .as_mut();
500             let new_token = self.rx.add(&[], &mut [buffer])?;
501             // If the RX buffer somehow gets assigned a different token, then our safety assumptions
502             // are broken and we can't safely continue to do anything with the device.
503             assert_eq!(new_token, index);
504         }
505 
506         if self.rx.should_notify() {
507             self.transport.notify(RX_QUEUE_IDX);
508         }
509 
510         Ok(())
511     }
512 
513     /// Pops one packet from the RX queue, if there is one pending. Returns the header, and a
514     /// reference to the buffer containing the body.
515     ///
516     /// Returns `None` if there is no pending packet.
pop_packet_from_rx_queue(&mut self) -> Result<Option<(VirtioVsockHdr, &[u8], u16)>>517     fn pop_packet_from_rx_queue(&mut self) -> Result<Option<(VirtioVsockHdr, &[u8], u16)>> {
518         let Some(token) = self.rx.peek_used() else {
519             return Ok(None);
520         };
521 
522         // Safe because we maintain a consistent mapping of tokens to buffers, so we pass the same
523         // buffer to `pop_used` as we previously passed to `add` for the token. Once we add the
524         // buffer back to the RX queue then we don't access it again until next time it is popped.
525         let (header, body) = unsafe {
526             let buffer = self.rx_queue_buffers[usize::from(token)].as_mut();
527             let _len = self.rx.pop_used(token, &[], &mut [buffer])?;
528 
529             // Read the header and body from the buffer. Don't check the result yet, because we need
530             // to add the buffer back to the queue either way.
531             let header_result = read_header_and_body(buffer);
532             if header_result.is_err() {
533                 // If there was an error, add the buffer back immediately. Ignore any errors, as we
534                 // need to return the first error.
535                 let _ = self.add_buffer_to_rx_queue(token);
536             }
537 
538             header_result
539         }?;
540 
541         debug!("Received packet {:?}. Op {:?}", header, header.op());
542         Ok(Some((header, body, token)))
543     }
544 }
545 
read_header_and_body(buffer: &[u8]) -> Result<(VirtioVsockHdr, &[u8])>546 fn read_header_and_body(buffer: &[u8]) -> Result<(VirtioVsockHdr, &[u8])> {
547     // Shouldn't panic, because we know `RX_BUFFER_SIZE > size_of::<VirtioVsockHdr>()`.
548     let header = VirtioVsockHdr::read_from_prefix(buffer).unwrap();
549     let body_length = header.len() as usize;
550 
551     // This could fail if the device returns an unreasonably long body length.
552     let data_end = size_of::<VirtioVsockHdr>()
553         .checked_add(body_length)
554         .ok_or(SocketError::InvalidNumber)?;
555     // This could fail if the device returns a body length longer than the buffer we gave it.
556     let data = buffer
557         .get(size_of::<VirtioVsockHdr>()..data_end)
558         .ok_or(SocketError::BufferTooShort)?;
559     Ok((header, data))
560 }
561 
562 #[cfg(test)]
563 mod tests {
564     use super::*;
565     use crate::{
566         hal::fake::FakeHal,
567         transport::{
568             fake::{FakeTransport, QueueStatus, State},
569             DeviceType,
570         },
571         volatile::ReadOnly,
572     };
573     use alloc::{sync::Arc, vec};
574     use core::ptr::NonNull;
575     use std::sync::Mutex;
576 
577     #[test]
config()578     fn config() {
579         let mut config_space = VirtioVsockConfig {
580             guest_cid_low: ReadOnly::new(66),
581             guest_cid_high: ReadOnly::new(0),
582         };
583         let state = Arc::new(Mutex::new(State {
584             queues: vec![
585                 QueueStatus::default(),
586                 QueueStatus::default(),
587                 QueueStatus::default(),
588             ],
589             ..Default::default()
590         }));
591         let transport = FakeTransport {
592             device_type: DeviceType::Socket,
593             max_queue_size: 32,
594             device_features: 0,
595             config_space: NonNull::from(&mut config_space),
596             state: state.clone(),
597         };
598         let socket =
599             VirtIOSocket::<FakeHal, FakeTransport<VirtioVsockConfig>>::new(transport).unwrap();
600         assert_eq!(socket.guest_cid(), 0x00_0000_0042);
601     }
602 }
603