• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Handles IPC for controlling the main VM process.
6 //!
7 //! The VM Control IPC protocol is synchronous, meaning that each `VmRequest` sent over a connection
8 //! will receive a `VmResponse` for that request next time data is received over that connection.
9 //!
10 //! The wire message format is a little-endian C-struct of fixed size, along with a file descriptor
11 //! if the request type expects one.
12 
13 use std::fmt::{self, Display};
14 use std::fs::File;
15 use std::io::{Seek, SeekFrom};
16 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
17 
18 use libc::{EINVAL, EIO, ENODEV};
19 
20 use kvm::Vm;
21 use msg_socket::{MsgOnSocket, MsgReceiver, MsgResult, MsgSender, MsgSocket};
22 use resources::{GpuMemoryDesc, SystemAllocator};
23 use sys_util::{error, Error as SysError, GuestAddress, MemoryMapping, MmapError, Result};
24 
25 /// A file descriptor either borrowed or owned by this.
26 #[derive(Debug)]
27 pub enum MaybeOwnedFd {
28     /// Owned by this enum variant, and will be destructed automatically if not moved out.
29     Owned(File),
30     /// A file descriptor borrwed by this enum.
31     Borrowed(RawFd),
32 }
33 
34 impl AsRawFd for MaybeOwnedFd {
as_raw_fd(&self) -> RawFd35     fn as_raw_fd(&self) -> RawFd {
36         match self {
37             MaybeOwnedFd::Owned(f) => f.as_raw_fd(),
38             MaybeOwnedFd::Borrowed(fd) => *fd,
39         }
40     }
41 }
42 
43 // When sent, it could be owned or borrowed. On the receiver end, it always owned.
44 impl MsgOnSocket for MaybeOwnedFd {
msg_size() -> usize45     fn msg_size() -> usize {
46         0usize
47     }
max_fd_count() -> usize48     fn max_fd_count() -> usize {
49         1usize
50     }
read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)>51     unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
52         let (fd, size) = RawFd::read_from_buffer(buffer, fds)?;
53         let file = File::from_raw_fd(fd);
54         Ok((MaybeOwnedFd::Owned(file), size))
55     }
write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize>56     fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
57         let fd = self.as_raw_fd();
58         fd.write_to_buffer(buffer, fds)
59     }
60 }
61 
62 /// Mode of execution for the VM.
63 #[derive(Debug)]
64 pub enum VmRunMode {
65     /// The default run mode indicating the VCPUs are running.
66     Running,
67     /// Indicates that the VCPUs are suspending execution until the `Running` mode is set.
68     Suspending,
69     /// Indicates that the VM is exiting all processes.
70     Exiting,
71 }
72 
73 impl Display for VmRunMode {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result74     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75         use self::VmRunMode::*;
76 
77         match self {
78             Running => write!(f, "running"),
79             Suspending => write!(f, "suspending"),
80             Exiting => write!(f, "exiting"),
81         }
82     }
83 }
84 
85 impl Default for VmRunMode {
default() -> Self86     fn default() -> Self {
87         VmRunMode::Running
88     }
89 }
90 
91 /// The maximum number of devices that can be listed in one `UsbControlCommand`.
92 ///
93 /// This value was set to be equal to `xhci_regs::MAX_PORTS` for convenience, but it is not
94 /// necessary for correctness. Importing that value directly would be overkill because it would
95 /// require adding a big dependency for a single const.
96 pub const USB_CONTROL_MAX_PORTS: usize = 16;
97 
98 #[derive(MsgOnSocket, Debug)]
99 pub enum BalloonControlCommand {
100     /// Set the size of the VM's balloon.
101     Adjust { num_bytes: u64 },
102 }
103 
104 #[derive(MsgOnSocket, Debug)]
105 pub enum DiskControlCommand {
106     /// Resize a disk to `new_size` in bytes.
107     Resize { new_size: u64 },
108 }
109 
110 impl Display for DiskControlCommand {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result111     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112         use self::DiskControlCommand::*;
113 
114         match self {
115             Resize { new_size } => write!(f, "disk_resize {}", new_size),
116         }
117     }
118 }
119 
120 #[derive(MsgOnSocket, Debug)]
121 pub enum DiskControlResult {
122     Ok,
123     Err(SysError),
124 }
125 
126 #[derive(MsgOnSocket, Debug)]
127 pub enum UsbControlCommand {
128     AttachDevice {
129         bus: u8,
130         addr: u8,
131         vid: u16,
132         pid: u16,
133         fd: Option<MaybeOwnedFd>,
134     },
135     DetachDevice {
136         port: u8,
137     },
138     ListDevice {
139         ports: [u8; USB_CONTROL_MAX_PORTS],
140     },
141 }
142 
143 #[derive(MsgOnSocket, Copy, Clone, Debug, Default)]
144 pub struct UsbControlAttachedDevice {
145     pub port: u8,
146     pub vendor_id: u16,
147     pub product_id: u16,
148 }
149 
150 impl UsbControlAttachedDevice {
valid(self) -> bool151     fn valid(self) -> bool {
152         self.port != 0
153     }
154 }
155 
156 #[derive(MsgOnSocket, Debug)]
157 pub enum UsbControlResult {
158     Ok { port: u8 },
159     NoAvailablePort,
160     NoSuchDevice,
161     NoSuchPort,
162     FailedToOpenDevice,
163     Devices([UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS]),
164 }
165 
166 impl Display for UsbControlResult {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result167     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168         use self::UsbControlResult::*;
169 
170         match self {
171             Ok { port } => write!(f, "ok {}", port),
172             NoAvailablePort => write!(f, "no_available_port"),
173             NoSuchDevice => write!(f, "no_such_device"),
174             NoSuchPort => write!(f, "no_such_port"),
175             FailedToOpenDevice => write!(f, "failed_to_open_device"),
176             Devices(devices) => {
177                 write!(f, "devices")?;
178                 for d in devices.iter().filter(|d| d.valid()) {
179                     write!(f, " {} {:04x} {:04x}", d.port, d.vendor_id, d.product_id)?;
180                 }
181                 std::result::Result::Ok(())
182             }
183         }
184     }
185 }
186 
187 #[derive(MsgOnSocket, Debug)]
188 pub enum VmMemoryRequest {
189     /// Register shared memory represented by the given fd into guest address space. The response
190     /// variant is `VmResponse::RegisterMemory`.
191     RegisterMemory(MaybeOwnedFd, usize),
192     /// Unregister the given memory slot that was previously registereed with `RegisterMemory`.
193     UnregisterMemory(u32),
194     /// Allocate GPU buffer of a given size/format and register the memory into guest address space.
195     /// The response variant is `VmResponse::AllocateAndRegisterGpuMemory`
196     AllocateAndRegisterGpuMemory {
197         width: u32,
198         height: u32,
199         format: u32,
200     },
201 }
202 
203 impl VmMemoryRequest {
204     /// Executes this request on the given Vm.
205     ///
206     /// # Arguments
207     /// * `vm` - The `Vm` to perform the request on.
208     /// * `allocator` - Used to allocate addresses.
209     ///
210     /// This does not return a result, instead encapsulating the success or failure in a
211     /// `VmMemoryResponse` with the intended purpose of sending the response back over the socket
212     /// that received this `VmMemoryResponse`.
execute(&self, vm: &mut Vm, sys_allocator: &mut SystemAllocator) -> VmMemoryResponse213     pub fn execute(&self, vm: &mut Vm, sys_allocator: &mut SystemAllocator) -> VmMemoryResponse {
214         use self::VmMemoryRequest::*;
215         match *self {
216             RegisterMemory(ref fd, size) => match register_memory(vm, sys_allocator, fd, size) {
217                 Ok((pfn, slot)) => VmMemoryResponse::RegisterMemory { pfn, slot },
218                 Err(e) => VmMemoryResponse::Err(e),
219             },
220             UnregisterMemory(slot) => match vm.remove_device_memory(slot) {
221                 Ok(_) => VmMemoryResponse::Ok,
222                 Err(e) => VmMemoryResponse::Err(e),
223             },
224             AllocateAndRegisterGpuMemory {
225                 width,
226                 height,
227                 format,
228             } => {
229                 let (mut fd, desc) = match sys_allocator.gpu_memory_allocator() {
230                     Some(gpu_allocator) => match gpu_allocator.allocate(width, height, format) {
231                         Ok(v) => v,
232                         Err(e) => return VmMemoryResponse::Err(e),
233                     },
234                     None => return VmMemoryResponse::Err(SysError::new(ENODEV)),
235                 };
236                 // Determine size of buffer using 0 byte seek from end. This is preferred over
237                 // `stride * height` as it's not limited to packed pixel formats.
238                 let size = match fd.seek(SeekFrom::End(0)) {
239                     Ok(v) => v,
240                     Err(e) => return VmMemoryResponse::Err(SysError::from(e)),
241                 };
242                 match register_memory(vm, sys_allocator, &fd, size as usize) {
243                     Ok((pfn, slot)) => VmMemoryResponse::AllocateAndRegisterGpuMemory {
244                         fd: MaybeOwnedFd::Owned(fd),
245                         pfn,
246                         slot,
247                         desc,
248                     },
249                     Err(e) => VmMemoryResponse::Err(e),
250                 }
251             }
252         }
253     }
254 }
255 
256 #[derive(MsgOnSocket, Debug)]
257 pub enum VmMemoryResponse {
258     /// The request to register memory into guest address space was successfully done at page frame
259     /// number `pfn` and memory slot number `slot`.
260     RegisterMemory {
261         pfn: u64,
262         slot: u32,
263     },
264     /// The request to allocate and register GPU memory into guest address space was successfully
265     /// done at page frame number `pfn` and memory slot number `slot` for buffer with `desc`.
266     AllocateAndRegisterGpuMemory {
267         fd: MaybeOwnedFd,
268         pfn: u64,
269         slot: u32,
270         desc: GpuMemoryDesc,
271     },
272     Ok,
273     Err(SysError),
274 }
275 
276 pub type BalloonControlRequestSocket = MsgSocket<BalloonControlCommand, ()>;
277 pub type BalloonControlResponseSocket = MsgSocket<(), BalloonControlCommand>;
278 
279 pub type DiskControlRequestSocket = MsgSocket<DiskControlCommand, DiskControlResult>;
280 pub type DiskControlResponseSocket = MsgSocket<DiskControlResult, DiskControlCommand>;
281 
282 pub type UsbControlSocket = MsgSocket<UsbControlCommand, UsbControlResult>;
283 
284 pub type VmMemoryControlRequestSocket = MsgSocket<VmMemoryRequest, VmMemoryResponse>;
285 pub type VmMemoryControlResponseSocket = MsgSocket<VmMemoryResponse, VmMemoryRequest>;
286 
287 pub type VmControlRequestSocket = MsgSocket<VmRequest, VmResponse>;
288 pub type VmControlResponseSocket = MsgSocket<VmResponse, VmRequest>;
289 
290 /// A request to the main process to perform some operation on the VM.
291 ///
292 /// Unless otherwise noted, each request should expect a `VmResponse::Ok` to be received on success.
293 #[derive(MsgOnSocket, Debug)]
294 pub enum VmRequest {
295     /// Break the VM's run loop and exit.
296     Exit,
297     /// Suspend the VM's VCPUs until resume.
298     Suspend,
299     /// Resume the VM's VCPUs that were previously suspended.
300     Resume,
301     /// Command for balloon driver.
302     BalloonCommand(BalloonControlCommand),
303     /// Send a command to a disk chosen by `disk_index`.
304     /// `disk_index` is a 0-based count of `--disk`, `--rwdisk`, and `-r` command-line options.
305     DiskCommand {
306         disk_index: usize,
307         command: DiskControlCommand,
308     },
309     /// Command to use controller.
310     UsbCommand(UsbControlCommand),
311 }
312 
register_memory( vm: &mut Vm, allocator: &mut SystemAllocator, fd: &dyn AsRawFd, size: usize, ) -> Result<(u64, u32)>313 fn register_memory(
314     vm: &mut Vm,
315     allocator: &mut SystemAllocator,
316     fd: &dyn AsRawFd,
317     size: usize,
318 ) -> Result<(u64, u32)> {
319     let mmap = match MemoryMapping::from_fd(fd, size) {
320         Ok(v) => v,
321         Err(MmapError::SystemCallFailed(e)) => return Err(e),
322         _ => return Err(SysError::new(EINVAL)),
323     };
324     let alloc = allocator.get_anon_alloc();
325     let addr = match allocator.device_allocator().allocate(
326         size as u64,
327         alloc,
328         "vmcontrol_register_memory".to_string(),
329     ) {
330         Ok(a) => a,
331         Err(_) => return Err(SysError::new(EINVAL)),
332     };
333     let slot = match vm.add_device_memory(GuestAddress(addr), mmap, false, false) {
334         Ok(v) => v,
335         Err(e) => return Err(e),
336     };
337 
338     Ok((addr >> 12, slot))
339 }
340 
341 impl VmRequest {
342     /// Executes this request on the given Vm and other mutable state.
343     ///
344     /// This does not return a result, instead encapsulating the success or failure in a
345     /// `VmResponse` with the intended purpose of sending the response back over the  socket that
346     /// received this `VmRequest`.
execute( &self, run_mode: &mut Option<VmRunMode>, balloon_host_socket: &BalloonControlRequestSocket, disk_host_sockets: &[DiskControlRequestSocket], usb_control_socket: &UsbControlSocket, ) -> VmResponse347     pub fn execute(
348         &self,
349         run_mode: &mut Option<VmRunMode>,
350         balloon_host_socket: &BalloonControlRequestSocket,
351         disk_host_sockets: &[DiskControlRequestSocket],
352         usb_control_socket: &UsbControlSocket,
353     ) -> VmResponse {
354         match *self {
355             VmRequest::Exit => {
356                 *run_mode = Some(VmRunMode::Exiting);
357                 VmResponse::Ok
358             }
359             VmRequest::Suspend => {
360                 *run_mode = Some(VmRunMode::Suspending);
361                 VmResponse::Ok
362             }
363             VmRequest::Resume => {
364                 *run_mode = Some(VmRunMode::Running);
365                 VmResponse::Ok
366             }
367             VmRequest::BalloonCommand(ref command) => match balloon_host_socket.send(command) {
368                 Ok(_) => VmResponse::Ok,
369                 Err(_) => VmResponse::Err(SysError::last()),
370             },
371             VmRequest::DiskCommand {
372                 disk_index,
373                 ref command,
374             } => {
375                 // Forward the request to the block device process via its control socket.
376                 if let Some(sock) = disk_host_sockets.get(disk_index) {
377                     if let Err(e) = sock.send(command) {
378                         error!("disk socket send failed: {}", e);
379                         VmResponse::Err(SysError::new(EINVAL))
380                     } else {
381                         match sock.recv() {
382                             Ok(DiskControlResult::Ok) => VmResponse::Ok,
383                             Ok(DiskControlResult::Err(e)) => VmResponse::Err(e),
384                             Err(e) => {
385                                 error!("disk socket recv failed: {}", e);
386                                 VmResponse::Err(SysError::new(EINVAL))
387                             }
388                         }
389                     }
390                 } else {
391                     VmResponse::Err(SysError::new(ENODEV))
392                 }
393             }
394             VmRequest::UsbCommand(ref cmd) => {
395                 let res = usb_control_socket.send(cmd);
396                 if let Err(e) = res {
397                     error!("fail to send command to usb control socket: {}", e);
398                     return VmResponse::Err(SysError::new(EIO));
399                 }
400                 match usb_control_socket.recv() {
401                     Ok(response) => VmResponse::UsbResponse(response),
402                     Err(e) => {
403                         error!("fail to recv command from usb control socket: {}", e);
404                         VmResponse::Err(SysError::new(EIO))
405                     }
406                 }
407             }
408         }
409     }
410 }
411 
412 /// Indication of success or failure of a `VmRequest`.
413 ///
414 /// Success is usually indicated `VmResponse::Ok` unless there is data associated with the response.
415 #[derive(MsgOnSocket, Debug)]
416 pub enum VmResponse {
417     /// Indicates the request was executed successfully.
418     Ok,
419     /// Indicates the request encountered some error during execution.
420     Err(SysError),
421     /// The request to register memory into guest address space was successfully done at page frame
422     /// number `pfn` and memory slot number `slot`.
423     RegisterMemory { pfn: u64, slot: u32 },
424     /// The request to allocate and register GPU memory into guest address space was successfully
425     /// done at page frame number `pfn` and memory slot number `slot` for buffer with `desc`.
426     AllocateAndRegisterGpuMemory {
427         fd: MaybeOwnedFd,
428         pfn: u64,
429         slot: u32,
430         desc: GpuMemoryDesc,
431     },
432     /// Results of usb control commands.
433     UsbResponse(UsbControlResult),
434 }
435 
436 impl Display for VmResponse {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result437     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
438         use self::VmResponse::*;
439 
440         match self {
441             Ok => write!(f, "ok"),
442             Err(e) => write!(f, "error: {}", e),
443             RegisterMemory { pfn, slot } => write!(
444                 f,
445                 "memory registered to page frame number {:#x} and memory slot {}",
446                 pfn, slot
447             ),
448             AllocateAndRegisterGpuMemory { pfn, slot, .. } => write!(
449                 f,
450                 "gpu memory allocated and registered to page frame number {:#x} and memory slot {}",
451                 pfn, slot
452             ),
453             UsbResponse(result) => write!(f, "usb control request get result {:?}", result),
454         }
455     }
456 }
457