vmm_vhost/
backend_client.rs

1// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::fs::File;
5use std::mem;
6
7use base::AsRawDescriptor;
8#[cfg(windows)]
9use base::CloseNotifier;
10use base::Event;
11use base::RawDescriptor;
12use base::ReadNotifier;
13use zerocopy::FromBytes;
14use zerocopy::Immutable;
15use zerocopy::IntoBytes;
16
17use crate::backend::VhostUserMemoryRegionInfo;
18use crate::backend::VringConfigData;
19use crate::into_single_file;
20use crate::message::*;
21use crate::Connection;
22use crate::Error as VhostUserError;
23use crate::FrontendReq;
24use crate::Result as VhostUserResult;
25use crate::Result;
26use crate::SharedMemoryRegion;
27
28/// Client for a vhost-user device. The API is a thin abstraction over the vhost-user protocol.
29pub struct BackendClient {
30    connection: Connection,
31    set_need_reply: bool,
32}
33
34impl BackendClient {
35    /// Create a new instance.
36    pub fn new(connection: Connection) -> Self {
37        BackendClient {
38            connection,
39            set_need_reply: false,
40        }
41    }
42
43    /// Whether to set the "need_reply" flag in the message header for every request message.
44    ///
45    /// Requires the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature to have been negotiated.
46    pub fn set_need_reply(&mut self, enable: bool) {
47        self.set_need_reply = enable;
48    }
49
50    /// Get a bitmask of supported virtio/vhost features.
51    pub fn get_features(&mut self) -> Result<u64> {
52        let hdr = self.send_request_header(FrontendReq::GET_FEATURES, None)?;
53        let val = self.recv_reply::<VhostUserU64>(&hdr)?;
54        Ok(val.value)
55    }
56
57    /// Inform the vhost subsystem which features to enable.
58    /// This should be a subset of supported features from get_features().
59    pub fn set_features(&mut self, features: u64) -> Result<()> {
60        let val = VhostUserU64::new(features);
61        let hdr = self.send_request_with_body(FrontendReq::SET_FEATURES, &val, None)?;
62        self.wait_for_ack(&hdr)
63    }
64
65    /// Set the current process as the owner of the vhost backend.
66    /// This must be run before any other vhost commands.
67    pub fn set_owner(&self) -> Result<()> {
68        let hdr = self.send_request_header(FrontendReq::SET_OWNER, None)?;
69        self.wait_for_ack(&hdr)
70    }
71
72    /// Used to be sent to request disabling all rings
73    /// This is no longer used.
74    pub fn reset_owner(&self) -> Result<()> {
75        let hdr = self.send_request_header(FrontendReq::RESET_OWNER, None)?;
76        self.wait_for_ack(&hdr)
77    }
78
79    /// Set the memory map regions on the backend so it can translate the vring
80    /// addresses. In the ancillary data there is an array of file descriptors
81    pub fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
82        let mut ctx = VhostUserMemoryContext::new();
83        for region in regions.iter() {
84            let reg = VhostUserMemoryRegion {
85                guest_phys_addr: region.guest_phys_addr,
86                memory_size: region.memory_size,
87                user_addr: region.userspace_addr,
88                mmap_offset: region.mmap_offset,
89            };
90            ctx.append(&reg, region.mmap_handle);
91        }
92
93        let body = VhostUserMemory::new(ctx.regions.len() as u32);
94        let hdr = self.send_request_with_payload(
95            FrontendReq::SET_MEM_TABLE,
96            &body,
97            ctx.regions.as_bytes(),
98            Some(ctx.fds.as_slice()),
99        )?;
100        self.wait_for_ack(&hdr)
101    }
102
103    /// Set base address for page modification logging.
104    pub fn set_log_base(&self, base: u64, fd: Option<RawDescriptor>) -> Result<()> {
105        let val = VhostUserU64::new(base);
106        let hdr = self.send_request_with_body(
107            FrontendReq::SET_LOG_BASE,
108            &val,
109            fd.as_ref().map(std::slice::from_ref),
110        )?;
111        self.wait_for_ack(&hdr)
112    }
113
114    /// Specify an event file descriptor to signal on log write.
115    pub fn set_log_fd(&self, fd: RawDescriptor) -> Result<()> {
116        let fds = [fd];
117        let hdr = self.send_request_header(FrontendReq::SET_LOG_FD, Some(&fds))?;
118        self.wait_for_ack(&hdr)
119    }
120
121    /// Set the number of descriptors in the vring.
122    pub fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> {
123        let val = VhostUserVringState::new(queue_index as u32, num.into());
124        let hdr = self.send_request_with_body(FrontendReq::SET_VRING_NUM, &val, None)?;
125        self.wait_for_ack(&hdr)
126    }
127
128    /// Set the addresses for a given vring.
129    pub fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> {
130        let val = VhostUserVringAddr::from_config_data(queue_index as u32, config_data);
131        let hdr = self.send_request_with_body(FrontendReq::SET_VRING_ADDR, &val, None)?;
132        self.wait_for_ack(&hdr)
133    }
134
135    /// Set the first index to look for available descriptors.
136    // TODO: b/331466964 - Arguments and message format are wrong for packed queues.
137    pub fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()> {
138        let val = VhostUserVringState::new(queue_index as u32, base.into());
139        let hdr = self.send_request_with_body(FrontendReq::SET_VRING_BASE, &val, None)?;
140        self.wait_for_ack(&hdr)
141    }
142
143    /// Get the available vring base offset.
144    // TODO: b/331466964 - Return type is wrong for packed queues.
145    pub fn get_vring_base(&self, queue_index: usize) -> Result<u32> {
146        let req = VhostUserVringState::new(queue_index as u32, 0);
147        let hdr = self.send_request_with_body(FrontendReq::GET_VRING_BASE, &req, None)?;
148        let reply = self.recv_reply::<VhostUserVringState>(&hdr)?;
149        Ok(reply.num)
150    }
151
152    /// Set the event to trigger when buffers have been used by the host.
153    ///
154    /// Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag
155    /// is set when there is no file descriptor in the ancillary data. This signals that polling
156    /// will be used instead of waiting for the call.
157    pub fn set_vring_call(&self, queue_index: usize, event: &Event) -> Result<()> {
158        let hdr = self.send_fd_for_vring(
159            FrontendReq::SET_VRING_CALL,
160            queue_index,
161            event.as_raw_descriptor(),
162        )?;
163        self.wait_for_ack(&hdr)
164    }
165
166    /// Set the event that will be signaled by the guest when buffers are available for the host to
167    /// process.
168    ///
169    /// Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag
170    /// is set when there is no file descriptor in the ancillary data. This signals that polling
171    /// should be used instead of waiting for a kick.
172    pub fn set_vring_kick(&self, queue_index: usize, event: &Event) -> Result<()> {
173        let hdr = self.send_fd_for_vring(
174            FrontendReq::SET_VRING_KICK,
175            queue_index,
176            event.as_raw_descriptor(),
177        )?;
178        self.wait_for_ack(&hdr)
179    }
180
181    /// Set the event that will be signaled by the guest when error happens.
182    ///
183    /// Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag
184    /// is set when there is no file descriptor in the ancillary data.
185    pub fn set_vring_err(&self, queue_index: usize, event: &Event) -> Result<()> {
186        let hdr = self.send_fd_for_vring(
187            FrontendReq::SET_VRING_ERR,
188            queue_index,
189            event.as_raw_descriptor(),
190        )?;
191        self.wait_for_ack(&hdr)
192    }
193
194    /// Front-end and back-end negotiate a channel over which to transfer the back-end’s internal
195    /// state during migration.
196    ///
197    /// Requires VHOST_USER_PROTOCOL_F_DEVICE_STATE to be negotiated.
198    pub fn set_device_state_fd(
199        &self,
200        transfer_direction: VhostUserTransferDirection,
201        migration_phase: VhostUserMigrationPhase,
202        fd: &impl AsRawDescriptor,
203    ) -> Result<Option<File>> {
204        // Send request.
205        let req = DeviceStateTransferParameters {
206            transfer_direction: match transfer_direction {
207                VhostUserTransferDirection::Save => 0,
208                VhostUserTransferDirection::Load => 1,
209            },
210            migration_phase: match migration_phase {
211                VhostUserMigrationPhase::Stopped => 0,
212            },
213        };
214        let hdr = self.send_request_with_body(
215            FrontendReq::SET_DEVICE_STATE_FD,
216            &req,
217            Some(&[fd.as_raw_descriptor()]),
218        )?;
219        // Receive reply.
220        let (reply, files) = self.recv_reply_with_files::<VhostUserU64>(&hdr)?;
221        let has_err = reply.value & 0xff != 0;
222        let invalid_fd = reply.value & 0x100 != 0;
223        if has_err {
224            return Err(VhostUserError::BackendInternalError);
225        }
226        match (invalid_fd, files.len()) {
227            (true, 0) => Ok(None),
228            (false, 1) => Ok(files.into_iter().next()),
229            _ => Err(VhostUserError::IncorrectFds),
230        }
231    }
232
233    /// After transferring the back-end’s internal state during migration, check whether the
234    /// back-end was able to successfully fully process the state.
235    pub fn check_device_state(&self) -> Result<()> {
236        let hdr = self.send_request_header(FrontendReq::CHECK_DEVICE_STATE, None)?;
237        let reply = self.recv_reply::<VhostUserU64>(&hdr)?;
238        if reply.value != 0 {
239            return Err(VhostUserError::BackendInternalError);
240        }
241        Ok(())
242    }
243
244    /// Get the protocol feature bitmask from the underlying vhost implementation.
245    pub fn get_protocol_features(&self) -> Result<VhostUserProtocolFeatures> {
246        let hdr = self.send_request_header(FrontendReq::GET_PROTOCOL_FEATURES, None)?;
247        let val = self.recv_reply::<VhostUserU64>(&hdr)?;
248        Ok(VhostUserProtocolFeatures::from_bits_truncate(val.value))
249    }
250
251    /// Enable protocol features in the underlying vhost implementation.
252    pub fn set_protocol_features(&mut self, features: VhostUserProtocolFeatures) -> Result<()> {
253        let val = VhostUserU64::new(features.bits());
254        let hdr = self.send_request_with_body(FrontendReq::SET_PROTOCOL_FEATURES, &val, None)?;
255        self.wait_for_ack(&hdr)
256    }
257
258    /// Query how many queues the backend supports.
259    pub fn get_queue_num(&self) -> Result<u64> {
260        let hdr = self.send_request_header(FrontendReq::GET_QUEUE_NUM, None)?;
261        let val = self.recv_reply::<VhostUserU64>(&hdr)?;
262        Ok(val.value)
263    }
264
265    /// Signal backend to enable or disable corresponding vring.
266    ///
267    /// Backend must not pass data to/from the ring until ring is enabled by
268    /// VHOST_USER_SET_VRING_ENABLE with parameter 1, or after it has been
269    /// disabled by VHOST_USER_SET_VRING_ENABLE with parameter 0.
270    pub fn set_vring_enable(&self, queue_index: usize, enable: bool) -> Result<()> {
271        let val = VhostUserVringState::new(queue_index as u32, enable.into());
272        let hdr = self.send_request_with_body(FrontendReq::SET_VRING_ENABLE, &val, None)?;
273        self.wait_for_ack(&hdr)
274    }
275
276    /// Fetch the contents of the virtio device configuration space.
277    pub fn get_config(
278        &self,
279        offset: u32,
280        size: u32,
281        flags: VhostUserConfigFlags,
282        buf: &[u8],
283    ) -> Result<(VhostUserConfig, VhostUserConfigPayload)> {
284        let body = VhostUserConfig::new(offset, size, flags);
285
286        // vhost-user spec states that:
287        // "Request payload: virtio device config space"
288        // "Reply payload: virtio device config space"
289        let hdr = self.send_request_with_payload(FrontendReq::GET_CONFIG, &body, buf, None)?;
290        let (body_reply, buf_reply, rfds) =
291            self.recv_reply_with_payload::<VhostUserConfig>(&hdr)?;
292        if !rfds.is_empty() {
293            return Err(VhostUserError::InvalidMessage);
294        } else if body_reply.size == 0 {
295            return Err(VhostUserError::BackendInternalError);
296        } else if body_reply.size != body.size
297            || body_reply.size as usize != buf.len()
298            || body_reply.offset != body.offset
299        {
300            return Err(VhostUserError::InvalidMessage);
301        }
302
303        Ok((body_reply, buf_reply))
304    }
305
306    /// Change the virtio device configuration space. It also can be used for live migration on the
307    /// destination host to set readonly configuration space fields.
308    pub fn set_config(&self, offset: u32, flags: VhostUserConfigFlags, buf: &[u8]) -> Result<()> {
309        let body = VhostUserConfig::new(
310            offset,
311            buf.len()
312                .try_into()
313                .map_err(VhostUserError::InvalidCastToInt)?,
314            flags,
315        );
316
317        let hdr = self.send_request_with_payload(FrontendReq::SET_CONFIG, &body, buf, None)?;
318        self.wait_for_ack(&hdr)
319    }
320
321    /// Setup backend communication channel.
322    pub fn set_backend_req_fd(&self, fd: &dyn AsRawDescriptor) -> Result<()> {
323        let fds = [fd.as_raw_descriptor()];
324        let hdr = self.send_request_header(FrontendReq::SET_BACKEND_REQ_FD, Some(&fds))?;
325        self.wait_for_ack(&hdr)
326    }
327
328    /// Retrieve shared buffer for inflight I/O tracking.
329    pub fn get_inflight_fd(
330        &self,
331        inflight: &VhostUserInflight,
332    ) -> Result<(VhostUserInflight, File)> {
333        let hdr = self.send_request_with_body(FrontendReq::GET_INFLIGHT_FD, inflight, None)?;
334        let (inflight, files) = self.recv_reply_with_files::<VhostUserInflight>(&hdr)?;
335
336        match into_single_file(files) {
337            Some(file) => Ok((inflight, file)),
338            None => Err(VhostUserError::IncorrectFds),
339        }
340    }
341
342    /// Set shared buffer for inflight I/O tracking.
343    pub fn set_inflight_fd(&self, inflight: &VhostUserInflight, fd: RawDescriptor) -> Result<()> {
344        let hdr =
345            self.send_request_with_body(FrontendReq::SET_INFLIGHT_FD, inflight, Some(&[fd]))?;
346        self.wait_for_ack(&hdr)
347    }
348
349    /// Query the maximum amount of memory slots supported by the backend.
350    pub fn get_max_mem_slots(&self) -> Result<u64> {
351        let hdr = self.send_request_header(FrontendReq::GET_MAX_MEM_SLOTS, None)?;
352        let val = self.recv_reply::<VhostUserU64>(&hdr)?;
353
354        Ok(val.value)
355    }
356
357    /// Add a new guest memory mapping for vhost to use.
358    pub fn add_mem_region(&self, region: &VhostUserMemoryRegionInfo) -> Result<()> {
359        let body = VhostUserSingleMemoryRegion::new(
360            region.guest_phys_addr,
361            region.memory_size,
362            region.userspace_addr,
363            region.mmap_offset,
364        );
365        let fds = [region.mmap_handle];
366        let hdr = self.send_request_with_body(FrontendReq::ADD_MEM_REG, &body, Some(&fds))?;
367        self.wait_for_ack(&hdr)
368    }
369
370    /// Remove a guest memory mapping from vhost.
371    pub fn remove_mem_region(&self, region: &VhostUserMemoryRegionInfo) -> Result<()> {
372        let body = VhostUserSingleMemoryRegion::new(
373            region.guest_phys_addr,
374            region.memory_size,
375            region.userspace_addr,
376            region.mmap_offset,
377        );
378        let hdr = self.send_request_with_body(FrontendReq::REM_MEM_REG, &body, None)?;
379        self.wait_for_ack(&hdr)
380    }
381
382    /// Get the shared memory configuration.
383    pub fn get_shmem_config(&self) -> Result<Vec<SharedMemoryRegion>> {
384        let hdr = self.send_request_header(FrontendReq::GET_SHMEM_CONFIG, None)?;
385        let reply: VhostUserShMemConfig = self.recv_reply(&hdr)?;
386
387        let shared_memory_regions = reply
388            .sizes
389            .into_iter()
390            .enumerate()
391            .filter(|&(_, n)| n != 0)
392            .take(reply.nregions.try_into().unwrap())
393            .map(|(id, length)| SharedMemoryRegion {
394                id: id as u8,
395                length,
396            })
397            .collect();
398
399        Ok(shared_memory_regions)
400    }
401
402    fn send_request_header(
403        &self,
404        code: FrontendReq,
405        fds: Option<&[RawDescriptor]>,
406    ) -> VhostUserResult<VhostUserMsgHeader> {
407        let hdr = self.new_request_header(code, 0);
408        self.connection.send_header_only_message(&hdr, fds)?;
409        Ok(hdr)
410    }
411
412    fn send_request_with_body<T: IntoBytes + Immutable>(
413        &self,
414        code: FrontendReq,
415        msg: &T,
416        fds: Option<&[RawDescriptor]>,
417    ) -> VhostUserResult<VhostUserMsgHeader> {
418        let hdr = self.new_request_header(code, mem::size_of::<T>() as u32);
419        self.connection.send_message(&hdr, msg, fds)?;
420        Ok(hdr)
421    }
422
423    fn send_request_with_payload<T: IntoBytes + Immutable>(
424        &self,
425        code: FrontendReq,
426        msg: &T,
427        payload: &[u8],
428        fds: Option<&[RawDescriptor]>,
429    ) -> VhostUserResult<VhostUserMsgHeader> {
430        let len = mem::size_of::<T>()
431            .checked_add(payload.len())
432            .ok_or(VhostUserError::OversizedMsg)?;
433        let hdr = self.new_request_header(
434            code,
435            len.try_into().map_err(VhostUserError::InvalidCastToInt)?,
436        );
437        self.connection
438            .send_message_with_payload(&hdr, msg, payload, fds)?;
439        Ok(hdr)
440    }
441
442    fn send_fd_for_vring(
443        &self,
444        code: FrontendReq,
445        queue_index: usize,
446        fd: RawDescriptor,
447    ) -> VhostUserResult<VhostUserMsgHeader> {
448        // Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag.
449        // This flag is set when there is no file descriptor in the ancillary data. This signals
450        // that polling will be used instead of waiting for the call.
451        let msg = VhostUserU64::new(queue_index as u64);
452        let hdr = self.new_request_header(code, mem::size_of::<VhostUserU64>() as u32);
453        self.connection.send_message(&hdr, &msg, Some(&[fd]))?;
454        Ok(hdr)
455    }
456
457    fn recv_reply<T: Sized + FromBytes + IntoBytes + Default + VhostUserMsgValidator>(
458        &self,
459        hdr: &VhostUserMsgHeader,
460    ) -> VhostUserResult<T> {
461        if hdr.is_reply() {
462            return Err(VhostUserError::InvalidParam(
463                "recv_reply: header is not a reply",
464            ));
465        }
466        let (reply, body, rfds) = self.connection.recv_message::<T>()?;
467        if !reply.is_valid() || !reply.is_reply_for(hdr) || !rfds.is_empty() || !body.is_valid() {
468            return Err(VhostUserError::InvalidMessage);
469        }
470        Ok(body)
471    }
472
473    fn recv_reply_with_files<T: Sized + IntoBytes + FromBytes + Default + VhostUserMsgValidator>(
474        &self,
475        hdr: &VhostUserMsgHeader,
476    ) -> VhostUserResult<(T, Vec<File>)> {
477        if hdr.is_reply() {
478            return Err(VhostUserError::InvalidParam(
479                "with_files: expected a reply, but the header is not marked as a reply",
480            ));
481        }
482
483        let (reply, body, files) = self.connection.recv_message::<T>()?;
484        if !reply.is_valid() || !reply.is_reply_for(hdr) || !body.is_valid() {
485            return Err(VhostUserError::InvalidMessage);
486        }
487        Ok((body, files))
488    }
489
490    fn recv_reply_with_payload<
491        T: Sized + IntoBytes + FromBytes + Default + VhostUserMsgValidator,
492    >(
493        &self,
494        hdr: &VhostUserMsgHeader,
495    ) -> VhostUserResult<(T, Vec<u8>, Vec<File>)> {
496        if hdr.is_reply() {
497            return Err(VhostUserError::InvalidParam(
498                "with_payload: expected a reply, but the header is not marked as a reply",
499            ));
500        }
501
502        let (reply, body, buf, files, more_files) =
503            self.connection.recv_message_with_payload::<T>()?;
504        if !reply.is_valid()
505            || !reply.is_reply_for(hdr)
506            || !files.is_empty()
507            || !more_files.is_empty()
508            || !body.is_valid()
509        {
510            return Err(VhostUserError::InvalidMessage);
511        }
512
513        Ok((body, buf, files))
514    }
515
516    fn wait_for_ack(&self, hdr: &VhostUserMsgHeader) -> VhostUserResult<()> {
517        if !hdr.is_need_reply() {
518            return Ok(());
519        }
520
521        let (reply, body, rfds) = self.connection.recv_message::<VhostUserU64>()?;
522        if !reply.is_valid() || !reply.is_reply_for(hdr) || !rfds.is_empty() || !body.is_valid() {
523            return Err(VhostUserError::InvalidMessage);
524        }
525        if body.value != 0 {
526            return Err(VhostUserError::BackendInternalError);
527        }
528        Ok(())
529    }
530
531    #[inline]
532    fn new_request_header(&self, request: FrontendReq, size: u32) -> VhostUserMsgHeader {
533        VhostUserMsgHeader::new_request_header(request, size, self.set_need_reply)
534    }
535}
536
537#[cfg(windows)]
538impl CloseNotifier for BackendClient {
539    fn get_close_notifier(&self) -> &dyn AsRawDescriptor {
540        self.connection.0.get_close_notifier()
541    }
542}
543
544impl ReadNotifier for BackendClient {
545    fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
546        self.connection.0.get_read_notifier()
547    }
548}
549
550// TODO(b/221882601): likely need pairs of RDs and/or SharedMemory to represent mmaps on Windows.
551/// Context object to pass guest memory configuration to BackendClient::set_mem_table().
552struct VhostUserMemoryContext {
553    regions: VhostUserMemoryPayload,
554    fds: Vec<RawDescriptor>,
555}
556
557impl VhostUserMemoryContext {
558    /// Create a context object.
559    pub fn new() -> Self {
560        VhostUserMemoryContext {
561            regions: VhostUserMemoryPayload::new(),
562            fds: Vec::new(),
563        }
564    }
565
566    /// Append a user memory region and corresponding RawDescriptor into the context object.
567    pub fn append(&mut self, region: &VhostUserMemoryRegion, fd: RawDescriptor) {
568        self.regions.push(*region);
569        self.fds.push(fd);
570    }
571}
572
573#[cfg(test)]
574mod tests {
575    use base::INVALID_DESCRIPTOR;
576
577    use super::*;
578
579    const BUFFER_SIZE: usize = 0x1001;
580    const INVALID_PROTOCOL_FEATURE: u64 = 1 << 63;
581
582    fn create_pair() -> (BackendClient, Connection) {
583        let (client_connection, server_connection) = Connection::pair().unwrap();
584        let backend_client = BackendClient::new(client_connection);
585        (backend_client, server_connection)
586    }
587
588    #[test]
589    fn create_backend_client() {
590        let (backend_client, peer) = create_pair();
591
592        assert!(backend_client.connection.as_raw_descriptor() != INVALID_DESCRIPTOR);
593        // Send two messages continuously
594        backend_client.set_owner().unwrap();
595        backend_client.reset_owner().unwrap();
596
597        let (hdr, rfds) = peer.recv_header().unwrap();
598        assert_eq!(hdr.get_code(), Ok(FrontendReq::SET_OWNER));
599        assert_eq!(hdr.get_size(), 0);
600        assert_eq!(hdr.get_version(), 0x1);
601        assert!(rfds.is_empty());
602
603        let (hdr, rfds) = peer.recv_header().unwrap();
604        assert_eq!(hdr.get_code(), Ok(FrontendReq::RESET_OWNER));
605        assert_eq!(hdr.get_size(), 0);
606        assert_eq!(hdr.get_version(), 0x1);
607        assert!(rfds.is_empty());
608    }
609
610    #[test]
611    fn test_features() {
612        let (mut backend_client, peer) = create_pair();
613
614        backend_client.set_owner().unwrap();
615        let (hdr, rfds) = peer.recv_header().unwrap();
616        assert_eq!(hdr.get_code(), Ok(FrontendReq::SET_OWNER));
617        assert_eq!(hdr.get_size(), 0);
618        assert_eq!(hdr.get_version(), 0x1);
619        assert!(rfds.is_empty());
620
621        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_FEATURES, 8);
622        let msg = VhostUserU64::new(0x15);
623        peer.send_message(&hdr, &msg, None).unwrap();
624        let features = backend_client.get_features().unwrap();
625        assert_eq!(features, 0x15u64);
626        let (_hdr, rfds) = peer.recv_header().unwrap();
627        assert!(rfds.is_empty());
628
629        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::SET_FEATURES, 8);
630        let msg = VhostUserU64::new(0x15);
631        peer.send_message(&hdr, &msg, None).unwrap();
632        backend_client.set_features(0x15).unwrap();
633        let (_hdr, msg, rfds) = peer.recv_message::<VhostUserU64>().unwrap();
634        assert!(rfds.is_empty());
635        let val = msg.value;
636        assert_eq!(val, 0x15);
637
638        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_FEATURES, 8);
639        let msg = 0x15u32;
640        peer.send_message(&hdr, &msg, None).unwrap();
641        assert!(backend_client.get_features().is_err());
642    }
643
644    #[test]
645    fn test_protocol_features() {
646        let (mut backend_client, peer) = create_pair();
647
648        backend_client.set_owner().unwrap();
649        let (hdr, rfds) = peer.recv_header().unwrap();
650        assert_eq!(hdr.get_code(), Ok(FrontendReq::SET_OWNER));
651        assert!(rfds.is_empty());
652
653        let pfeatures = VhostUserProtocolFeatures::all();
654        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_PROTOCOL_FEATURES, 8);
655        // Unknown feature bits should be ignored.
656        let msg = VhostUserU64::new(pfeatures.bits() | INVALID_PROTOCOL_FEATURE);
657        peer.send_message(&hdr, &msg, None).unwrap();
658        let features = backend_client.get_protocol_features().unwrap();
659        assert_eq!(features, pfeatures);
660        let (_hdr, rfds) = peer.recv_header().unwrap();
661        assert!(rfds.is_empty());
662
663        backend_client
664            .set_protocol_features(VhostUserProtocolFeatures::all())
665            .unwrap();
666        let (_hdr, msg, rfds) = peer.recv_message::<VhostUserU64>().unwrap();
667        assert!(rfds.is_empty());
668        let val = msg.value;
669        assert_eq!(val, pfeatures.bits());
670
671        let vfeatures = 0x15 | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
672        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_FEATURES, 8);
673        let msg = VhostUserU64::new(vfeatures);
674        peer.send_message(&hdr, &msg, None).unwrap();
675        let features = backend_client.get_features().unwrap();
676        assert_eq!(features, vfeatures);
677        let (_hdr, rfds) = peer.recv_header().unwrap();
678        assert!(rfds.is_empty());
679
680        backend_client.set_features(vfeatures).unwrap();
681        let (_hdr, msg, rfds) = peer.recv_message::<VhostUserU64>().unwrap();
682        assert!(rfds.is_empty());
683        let val = msg.value;
684        assert_eq!(val, vfeatures);
685
686        let pfeatures = VhostUserProtocolFeatures::all();
687        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_PROTOCOL_FEATURES, 8);
688        // Unknown feature bits should be ignored.
689        let msg = VhostUserU64::new(pfeatures.bits() | INVALID_PROTOCOL_FEATURE);
690        peer.send_message(&hdr, &msg, None).unwrap();
691        let features = backend_client.get_protocol_features().unwrap();
692        assert_eq!(features, pfeatures);
693        let (_hdr, rfds) = peer.recv_header().unwrap();
694        assert!(rfds.is_empty());
695
696        backend_client.set_protocol_features(pfeatures).unwrap();
697        let (_hdr, msg, rfds) = peer.recv_message::<VhostUserU64>().unwrap();
698        assert!(rfds.is_empty());
699        let val = msg.value;
700        assert_eq!(val, pfeatures.bits());
701
702        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::SET_PROTOCOL_FEATURES, 8);
703        let msg = VhostUserU64::new(pfeatures.bits());
704        peer.send_message(&hdr, &msg, None).unwrap();
705        assert!(backend_client.get_protocol_features().is_err());
706    }
707
708    #[test]
709    fn test_backend_client_get_config_negative0() {
710        let (backend_client, peer) = create_pair();
711        let buf = vec![0x0; BUFFER_SIZE];
712
713        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_CONFIG, 16);
714        let msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
715        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
716            .unwrap();
717        assert!(backend_client
718            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
719            .is_ok());
720
721        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_FEATURES, 16);
722        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
723            .unwrap();
724        assert!(backend_client
725            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
726            .is_err());
727    }
728
729    #[test]
730    fn test_backend_client_get_config_negative1() {
731        let (backend_client, peer) = create_pair();
732        let buf = vec![0x0; BUFFER_SIZE];
733
734        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_CONFIG, 16);
735        let msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
736        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
737            .unwrap();
738        assert!(backend_client
739            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
740            .is_ok());
741
742        let hdr = VhostUserMsgHeader::new_request_header(FrontendReq::GET_CONFIG, 16, false);
743        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
744            .unwrap();
745        assert!(backend_client
746            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
747            .is_err());
748    }
749
750    #[test]
751    fn test_backend_client_get_config_negative2() {
752        let (backend_client, peer) = create_pair();
753        let buf = vec![0x0; BUFFER_SIZE];
754
755        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_CONFIG, 16);
756        let msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
757        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
758            .unwrap();
759        assert!(backend_client
760            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
761            .is_ok());
762    }
763
764    #[test]
765    fn test_backend_client_get_config_negative3() {
766        let (backend_client, peer) = create_pair();
767        let buf = vec![0x0; BUFFER_SIZE];
768
769        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_CONFIG, 16);
770        let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
771        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
772            .unwrap();
773        assert!(backend_client
774            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
775            .is_ok());
776
777        msg.offset = 0;
778        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
779            .unwrap();
780        assert!(backend_client
781            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
782            .is_err());
783    }
784
785    #[test]
786    fn test_backend_client_get_config_negative4() {
787        let (backend_client, peer) = create_pair();
788        let buf = vec![0x0; BUFFER_SIZE];
789
790        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_CONFIG, 16);
791        let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
792        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
793            .unwrap();
794        assert!(backend_client
795            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
796            .is_ok());
797
798        msg.offset = 0x101;
799        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
800            .unwrap();
801        assert!(backend_client
802            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
803            .is_err());
804    }
805
806    #[test]
807    fn test_backend_client_get_config_negative5() {
808        let (backend_client, peer) = create_pair();
809        let buf = vec![0x0; BUFFER_SIZE];
810
811        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_CONFIG, 16);
812        let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
813        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
814            .unwrap();
815        assert!(backend_client
816            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
817            .is_ok());
818
819        msg.offset = (BUFFER_SIZE) as u32;
820        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
821            .unwrap();
822        assert!(backend_client
823            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
824            .is_err());
825    }
826
827    #[test]
828    fn test_backend_client_get_config_negative6() {
829        let (backend_client, peer) = create_pair();
830        let buf = vec![0x0; BUFFER_SIZE];
831
832        let hdr = VhostUserMsgHeader::new_reply_header(FrontendReq::GET_CONFIG, 16);
833        let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
834        peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
835            .unwrap();
836        assert!(backend_client
837            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
838            .is_ok());
839
840        msg.size = 6;
841        peer.send_message_with_payload(&hdr, &msg, &buf[0..6], None)
842            .unwrap();
843        assert!(backend_client
844            .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
845            .is_err());
846    }
847}