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