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