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