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