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