1 // Copyright 2018 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::collections::hash_map::Entry;
6 use std::collections::hash_map::HashMap;
7 use std::collections::hash_map::VacantEntry;
8 use std::env::set_var;
9 use std::fs::File;
10 use std::io::Write;
11 use std::mem::transmute;
12 use std::os::unix::net::UnixDatagram;
13 use std::path::Path;
14 use std::process::Command;
15 use std::result;
16 use std::sync::Arc;
17 use std::sync::RwLock;
18 use std::thread::JoinHandle;
19
20 use base::error;
21 use base::linux::SharedMemoryLinux;
22 use base::AsRawDescriptor;
23 use base::Error as SysError;
24 use base::Event;
25 use base::IntoRawDescriptor;
26 use base::Killable;
27 use base::MemoryMappingBuilder;
28 use base::Result as SysResult;
29 use base::ScmSocket;
30 use base::SharedMemory;
31 use base::SIGRTMIN;
32 use kvm::dirty_log_bitmap_size;
33 use kvm::Datamatch;
34 use kvm::IoeventAddress;
35 use kvm::IrqRoute;
36 use kvm::IrqSource;
37 use kvm::PicId;
38 use kvm::Vm;
39 use kvm_sys::kvm_clock_data;
40 use kvm_sys::kvm_ioapic_state;
41 use kvm_sys::kvm_pic_state;
42 use kvm_sys::kvm_pit_state2;
43 use libc::pid_t;
44 use libc::waitpid;
45 use libc::EINVAL;
46 use libc::ENODATA;
47 use libc::ENOTTY;
48 use libc::STDERR_FILENO;
49 use libc::WEXITSTATUS;
50 use libc::WIFEXITED;
51 use libc::WNOHANG;
52 use libc::WTERMSIG;
53 use minijail::Minijail;
54 use net_util::Error as NetError;
55 use net_util::TapTCommon;
56 use protobuf::EnumOrUnknown;
57 use protobuf::Message;
58 use protos::plugin::*;
59 use sync::Mutex;
60 use vm_memory::GuestAddress;
61 use zerocopy::AsBytes;
62 use zerocopy::FromBytes;
63
64 use super::*;
65
66 const CROSVM_SOCKET_ENV: &str = "CROSVM_SOCKET";
67
get_vm_state(vm: &Vm, state_set: EnumOrUnknown<main_request::StateSet>) -> SysResult<Vec<u8>>68 fn get_vm_state(vm: &Vm, state_set: EnumOrUnknown<main_request::StateSet>) -> SysResult<Vec<u8>> {
69 Ok(
70 match state_set.enum_value().map_err(|_| SysError::new(EINVAL))? {
71 main_request::StateSet::PIC0 => vm.get_pic_state(PicId::Primary)?.as_bytes().to_vec(),
72 main_request::StateSet::PIC1 => vm.get_pic_state(PicId::Secondary)?.as_bytes().to_vec(),
73 main_request::StateSet::IOAPIC => vm.get_ioapic_state()?.as_bytes().to_vec(),
74 main_request::StateSet::PIT => vm.get_pit_state()?.as_bytes().to_vec(),
75 main_request::StateSet::CLOCK => vm.get_clock()?.as_bytes().to_vec(),
76 },
77 )
78 }
79
set_vm_state( vm: &Vm, state_set: EnumOrUnknown<main_request::StateSet>, state: &[u8], ) -> SysResult<()>80 fn set_vm_state(
81 vm: &Vm,
82 state_set: EnumOrUnknown<main_request::StateSet>,
83 state: &[u8],
84 ) -> SysResult<()> {
85 match state_set.enum_value().map_err(|_| SysError::new(EINVAL))? {
86 main_request::StateSet::PIC0 => {
87 let pic_state = kvm_pic_state::read_from(state).ok_or(SysError::new(EINVAL))?;
88 vm.set_pic_state(PicId::Primary, &pic_state)
89 }
90 main_request::StateSet::PIC1 => {
91 let pic_state = kvm_pic_state::read_from(state).ok_or(SysError::new(EINVAL))?;
92 vm.set_pic_state(PicId::Secondary, &pic_state)
93 }
94 main_request::StateSet::IOAPIC => {
95 let ioapic_state = kvm_ioapic_state::read_from(state).ok_or(SysError::new(EINVAL))?;
96 vm.set_ioapic_state(&ioapic_state)
97 }
98 main_request::StateSet::PIT => {
99 let pit_state = kvm_pit_state2::read_from(state).ok_or(SysError::new(EINVAL))?;
100 vm.set_pit_state(&pit_state)
101 }
102 main_request::StateSet::CLOCK => {
103 let clock_data = kvm_clock_data::read_from(state).ok_or(SysError::new(EINVAL))?;
104 vm.set_clock(&clock_data)
105 }
106 }
107 }
108
109 /// The status of a process, either that it is running, or that it exited under some condition.
110 pub enum ProcessStatus {
111 /// The process is running and therefore has no information about its result.
112 Running,
113 /// The process has exited with a successful code.
114 Success,
115 /// The process failed with the given exit code.
116 Fail(i32),
117 /// The process was terminated with the given signal code.
118 Signal(i32),
119 }
120
121 /// Creates, owns, and handles messages from a plugin process.
122 ///
123 /// A plugin process has control over a single VM and a fixed number of VCPUs via a set of pipes &
124 /// unix domain socket connections and a protocol defined in `protos::plugin`. The plugin process is
125 /// run in an unprivileged manner as a child process spawned via a path to a arbitrary executable.
126 pub struct Process {
127 started: bool,
128 plugin_pid: pid_t,
129 request_sockets: Vec<ScmSocket<UnixDatagram>>,
130 objects: HashMap<u32, PluginObject>,
131 shared_vcpu_state: Arc<RwLock<SharedVcpuState>>,
132 per_vcpu_states: Vec<Arc<Mutex<PerVcpuState>>>,
133
134 // Resource to sent to plugin
135 kill_evt: Event,
136 vcpu_pipes: Vec<VcpuPipe>,
137
138 // Socket Transmission
139 request_buffer: Vec<u8>,
140 response_buffer: Vec<u8>,
141 }
142
143 impl Process {
144 /// Creates a new plugin process for the given number of vcpus and VM.
145 ///
146 /// This will immediately spawn the plugin process and wait for the child to signal that it is
147 /// ready to start. This call may block indefinitely.
148 ///
149 /// Set the `jail` argument to spawn the plugin process within the preconfigured jail.
150 /// Due to an API limitation in libminijail necessitating that this function set an environment
151 /// variable, this function is not thread-safe.
152 ///
153 /// Arguments:
154 ///
155 /// * `cpu_count`: number of vcpus
156 /// * `cmd`: path to plugin executable
157 /// * `args`: arguments to plugin executable
158 /// * `jail`: jail to launch plugin in. If None plugin will just be spawned as a child
159 /// * `stderr`: File to redirect stderr of plugin process to
new( cpu_count: u32, cmd: &Path, args: &[&str], jail: Option<Minijail>, stderr: File, ) -> Result<Process>160 pub fn new(
161 cpu_count: u32,
162 cmd: &Path,
163 args: &[&str],
164 jail: Option<Minijail>,
165 stderr: File,
166 ) -> Result<Process> {
167 let (request_socket, child_socket) =
168 new_seqpacket_pair().context("error creating main request socket")?;
169
170 let mut vcpu_pipes: Vec<VcpuPipe> = Vec::with_capacity(cpu_count as usize);
171 for _ in 0..cpu_count {
172 vcpu_pipes.push(new_pipe_pair().context("error creating vcpu request socket")?);
173 }
174
175 let mut per_vcpu_states: Vec<Arc<Mutex<PerVcpuState>>> = Vec::new();
176 per_vcpu_states.resize_with(cpu_count as usize, Default::default);
177
178 let plugin_pid = match jail {
179 Some(jail) => {
180 set_var(
181 CROSVM_SOCKET_ENV,
182 child_socket.as_raw_descriptor().to_string(),
183 );
184 jail.run_remap(
185 cmd,
186 &[
187 (stderr.as_raw_descriptor(), STDERR_FILENO),
188 (
189 child_socket.as_raw_descriptor(),
190 child_socket.as_raw_descriptor(),
191 ),
192 ],
193 args,
194 )
195 .context("failed to run plugin jail")?
196 }
197 None => Command::new(cmd)
198 .args(args)
199 .env(
200 "CROSVM_SOCKET",
201 child_socket.as_raw_descriptor().to_string(),
202 )
203 .stderr(stderr)
204 .spawn()
205 .context("failed to spawn plugin")?
206 .id() as pid_t,
207 };
208
209 Ok(Process {
210 started: false,
211 plugin_pid,
212 request_sockets: vec![request_socket.try_into()?],
213 objects: Default::default(),
214 shared_vcpu_state: Default::default(),
215 per_vcpu_states,
216 kill_evt: Event::new().context("failed to create plugin kill event")?,
217 vcpu_pipes,
218 request_buffer: vec![0; MAX_DATAGRAM_SIZE],
219 response_buffer: Vec::new(),
220 })
221 }
222
223 /// Creates a VCPU plugin connection object, used by a VCPU run loop to communicate with the
224 /// plugin process.
225 ///
226 /// While each invocation of `create_vcpu` with the given `cpu_id` will return a unique
227 /// `PluginVcpu` object, the underlying resources are shared by each `PluginVcpu` resulting from
228 /// the same `cpu_id`.
create_vcpu(&self, cpu_id: u32) -> Result<PluginVcpu>229 pub fn create_vcpu(&self, cpu_id: u32) -> Result<PluginVcpu> {
230 let vcpu_pipe_read = self.vcpu_pipes[cpu_id as usize]
231 .crosvm_read
232 .try_clone()
233 .context("failed to clone vcpu read pipe")?;
234 let vcpu_pipe_write = self.vcpu_pipes[cpu_id as usize]
235 .crosvm_write
236 .try_clone()
237 .context("failed to clone vcpu write pipe")?;
238 Ok(PluginVcpu::new(
239 self.shared_vcpu_state.clone(),
240 self.per_vcpu_states[cpu_id as usize].clone(),
241 vcpu_pipe_read,
242 vcpu_pipe_write,
243 ))
244 }
245
246 /// Returns if the plugin process indicated the VM was ready to start.
is_started(&self) -> bool247 pub fn is_started(&self) -> bool {
248 self.started
249 }
250
251 /// Returns the process ID of the plugin process.
pid(&self) -> pid_t252 pub fn pid(&self) -> pid_t {
253 self.plugin_pid
254 }
255
256 /// Returns a slice of each socket that should be polled.
257 ///
258 /// If any socket in this slice becomes readable, `handle_socket` should be called with the
259 /// index of that socket. If any socket becomes closed, its index should be passed to
260 /// `drop_sockets`.
sockets(&self) -> &[ScmSocket<UnixDatagram>]261 pub fn sockets(&self) -> &[ScmSocket<UnixDatagram>] {
262 &self.request_sockets
263 }
264
265 /// Drops the each socket identified by its index in the slice returned by `sockets`.
266 ///
267 /// The given `socket_idxs` slice will be modified in an arbitrary way for efficient removal of
268 /// the sockets from internal data structures.
drop_sockets(&mut self, socket_idxs: &mut [usize])269 pub fn drop_sockets(&mut self, socket_idxs: &mut [usize]) {
270 // Takes a mutable slice so that the indices can be sorted for efficient removal in
271 // request_sockets..
272 socket_idxs.sort_unstable_by(|a, b| b.cmp(a));
273 let old_len = self.request_sockets.len();
274 for &socket_index in socket_idxs.iter() {
275 // swap_remove changes the index of the last element, but we already know that one
276 // doesn't need to be removed because we are removing sockets in descending order thanks
277 // to the above sort.
278 self.request_sockets.swap_remove(socket_index);
279 }
280 assert_eq!(old_len - socket_idxs.len(), self.request_sockets.len());
281 }
282
283 /// Gently requests that the plugin process exit cleanly, and ends handling of all VCPU
284 /// connections.
285 ///
286 /// The plugin process can ignore the given signal, and so some timeout should be used before
287 /// forcefully terminating the process.
288 ///
289 /// Any blocked VCPU connections will get interrupted so that the VCPU threads can exit cleanly.
290 /// Any subsequent attempt to use the VCPU connections will fail.
signal_kill(&mut self) -> SysResult<()>291 pub fn signal_kill(&mut self) -> SysResult<()> {
292 self.kill_evt.signal()?;
293 // Normally we'd get any blocked recv() calls in the VCPU threads
294 // to unblock by calling shutdown(). However, we're using pipes
295 // (for improved performance), and pipes don't have shutdown so
296 // instead we'll write a shutdown message to ourselves using the
297 // the writable side of the pipe (normally used by the plugin).
298 for pipe in self.vcpu_pipes.iter_mut() {
299 let mut shutdown_request = VcpuRequest::new();
300 shutdown_request.set_shutdown(vcpu_request::Shutdown::new());
301 let mut buffer = Vec::new();
302 shutdown_request
303 .write_to_vec(&mut buffer)
304 .map_err(proto_to_sys_err)?;
305 pipe.plugin_write
306 .write(&buffer[..])
307 .map_err(io_to_sys_err)?;
308 }
309 Ok(())
310 }
311
312 /// Waits without blocking for the plugin process to exit and returns the status.
try_wait(&mut self) -> SysResult<ProcessStatus>313 pub fn try_wait(&mut self) -> SysResult<ProcessStatus> {
314 let mut status = 0;
315 // SAFETY:
316 // Safe because waitpid is given a valid pointer of correct size and mutability, and the
317 // return value is checked.
318 let ret = unsafe { waitpid(self.plugin_pid, &mut status, WNOHANG) };
319 match ret {
320 -1 => Err(SysError::last()),
321 0 => Ok(ProcessStatus::Running),
322 _ => {
323 if WIFEXITED(status) {
324 match WEXITSTATUS(status) {
325 0 => Ok(ProcessStatus::Success),
326 code => Ok(ProcessStatus::Fail(code)),
327 }
328 } else {
329 // Plugin terminated but has no exit status, so it must have been signaled.
330 Ok(ProcessStatus::Signal(WTERMSIG(status)))
331 }
332 }
333 }
334 }
335
handle_io_event( entry: VacantEntry<u32, PluginObject>, vm: &mut Vm, io_event: &main_request::create::IoEvent, ) -> SysResult<RawDescriptor>336 fn handle_io_event(
337 entry: VacantEntry<u32, PluginObject>,
338 vm: &mut Vm,
339 io_event: &main_request::create::IoEvent,
340 ) -> SysResult<RawDescriptor> {
341 let evt = Event::new()?;
342 let addr = match io_event
343 .space
344 .enum_value()
345 .map_err(|_| SysError::new(EINVAL))?
346 {
347 AddressSpace::IOPORT => IoeventAddress::Pio(io_event.address),
348 AddressSpace::MMIO => IoeventAddress::Mmio(io_event.address),
349 };
350 match io_event.length {
351 0 => vm.register_ioevent(&evt, addr, Datamatch::AnyLength)?,
352 1 => vm.register_ioevent(&evt, addr, Datamatch::U8(Some(io_event.datamatch as u8)))?,
353 2 => {
354 vm.register_ioevent(&evt, addr, Datamatch::U16(Some(io_event.datamatch as u16)))?
355 }
356 4 => {
357 vm.register_ioevent(&evt, addr, Datamatch::U32(Some(io_event.datamatch as u32)))?
358 }
359 8 => vm.register_ioevent(&evt, addr, Datamatch::U64(Some(io_event.datamatch)))?,
360 _ => return Err(SysError::new(EINVAL)),
361 };
362
363 let fd = evt.as_raw_descriptor();
364 entry.insert(PluginObject::IoEvent {
365 evt,
366 addr,
367 length: io_event.length,
368 datamatch: io_event.datamatch,
369 });
370 Ok(fd)
371 }
372
handle_memory( entry: VacantEntry<u32, PluginObject>, vm: &mut Vm, memfd: File, offset: u64, start: u64, length: u64, read_only: bool, dirty_log: bool, ) -> SysResult<()>373 fn handle_memory(
374 entry: VacantEntry<u32, PluginObject>,
375 vm: &mut Vm,
376 memfd: File,
377 offset: u64,
378 start: u64,
379 length: u64,
380 read_only: bool,
381 dirty_log: bool,
382 ) -> SysResult<()> {
383 let shm = SharedMemory::from_file(memfd)?;
384 // Checking the seals ensures the plugin process won't shrink the mmapped file, causing us
385 // to SIGBUS in the future.
386 let seals = shm.get_seals()?;
387 if !seals.shrink_seal() {
388 return Err(SysError::new(EPERM));
389 }
390 // Check to make sure we don't mmap areas beyond the end of the memfd.
391 match length.checked_add(offset) {
392 Some(end) if end > shm.size() => return Err(SysError::new(EINVAL)),
393 None => return Err(SysError::new(EOVERFLOW)),
394 _ => {}
395 }
396 let mem = MemoryMappingBuilder::new(length as usize)
397 .from_shared_memory(&shm)
398 .offset(offset)
399 .build()
400 .map_err(mmap_to_sys_err)?;
401 let slot =
402 vm.add_memory_region(GuestAddress(start), Box::new(mem), read_only, dirty_log)?;
403 entry.insert(PluginObject::Memory {
404 slot,
405 length: length as usize,
406 });
407 Ok(())
408 }
409
handle_reserve_range( &mut self, reserve_range: &main_request::ReserveRange, ) -> SysResult<()>410 fn handle_reserve_range(
411 &mut self,
412 reserve_range: &main_request::ReserveRange,
413 ) -> SysResult<()> {
414 match self.shared_vcpu_state.write() {
415 Ok(mut lock) => {
416 let space = match reserve_range
417 .space
418 .enum_value()
419 .map_err(|_| SysError::new(EINVAL))?
420 {
421 AddressSpace::IOPORT => IoSpace::Ioport,
422 AddressSpace::MMIO => IoSpace::Mmio,
423 };
424 match reserve_range.length {
425 0 => lock.unreserve_range(space, reserve_range.start),
426 _ => lock.reserve_range(
427 space,
428 reserve_range.start,
429 reserve_range.length,
430 reserve_range.async_write,
431 ),
432 }
433 }
434 Err(_) => Err(SysError::new(EDEADLK)),
435 }
436 }
437
handle_set_irq_routing( vm: &mut Vm, irq_routing: &main_request::SetIrqRouting, ) -> SysResult<()>438 fn handle_set_irq_routing(
439 vm: &mut Vm,
440 irq_routing: &main_request::SetIrqRouting,
441 ) -> SysResult<()> {
442 let mut routes = Vec::with_capacity(irq_routing.routes.len());
443 for route in &irq_routing.routes {
444 routes.push(IrqRoute {
445 gsi: route.irq_id,
446 source: if route.has_irqchip() {
447 let irqchip = route.irqchip();
448 IrqSource::Irqchip {
449 chip: irqchip.irqchip,
450 pin: irqchip.pin,
451 }
452 } else if route.has_msi() {
453 let msi = route.msi();
454 IrqSource::Msi {
455 address: msi.address,
456 data: msi.data,
457 }
458 } else {
459 // Because route is a oneof field in the proto definition, this should
460 // only happen if a new variant gets added without updating this chained
461 // if block.
462 return Err(SysError::new(EINVAL));
463 },
464 });
465 }
466 vm.set_gsi_routing(&routes[..])
467 }
468
handle_set_call_hint(&mut self, hints: &main_request::SetCallHint) -> SysResult<()>469 fn handle_set_call_hint(&mut self, hints: &main_request::SetCallHint) -> SysResult<()> {
470 let mut regs: Vec<CallHintDetails> = vec![];
471 for hint in &hints.hints {
472 regs.push(CallHintDetails {
473 match_rax: hint.match_rax,
474 match_rbx: hint.match_rbx,
475 match_rcx: hint.match_rcx,
476 match_rdx: hint.match_rdx,
477 rax: hint.rax,
478 rbx: hint.rbx,
479 rcx: hint.rcx,
480 rdx: hint.rdx,
481 send_sregs: hint.send_sregs,
482 send_debugregs: hint.send_debugregs,
483 });
484 }
485 match self.shared_vcpu_state.write() {
486 Ok(mut lock) => {
487 let space = match hints
488 .space
489 .enum_value()
490 .map_err(|_| SysError::new(EINVAL))?
491 {
492 AddressSpace::IOPORT => IoSpace::Ioport,
493 AddressSpace::MMIO => IoSpace::Mmio,
494 };
495 lock.set_hint(space, hints.address, hints.on_write, regs);
496 Ok(())
497 }
498 Err(_) => Err(SysError::new(EDEADLK)),
499 }
500 }
501
handle_pause_vcpus(&self, vcpu_handles: &[JoinHandle<()>], cpu_mask: u64, user_data: u64)502 fn handle_pause_vcpus(&self, vcpu_handles: &[JoinHandle<()>], cpu_mask: u64, user_data: u64) {
503 for (cpu_id, (handle, per_cpu_state)) in
504 vcpu_handles.iter().zip(&self.per_vcpu_states).enumerate()
505 {
506 if cpu_mask & (1 << cpu_id) != 0 {
507 per_cpu_state.lock().request_pause(user_data);
508 if let Err(e) = handle.kill(SIGRTMIN() + 0) {
509 error!("failed to interrupt vcpu {}: {}", cpu_id, e);
510 }
511 }
512 }
513 }
514
handle_get_net_config( tap: &net_util::sys::linux::Tap, config: &mut main_response::GetNetConfig, ) -> SysResult<()>515 fn handle_get_net_config(
516 tap: &net_util::sys::linux::Tap,
517 config: &mut main_response::GetNetConfig,
518 ) -> SysResult<()> {
519 // Log any NetError so that the cause can be found later, but extract and return the
520 // underlying errno for the client as well.
521 fn map_net_error(s: &str, e: NetError) -> SysError {
522 error!("failed to get {}: {}", s, e);
523 e.sys_error()
524 }
525
526 let ip_addr = tap.ip_addr().map_err(|e| map_net_error("IP address", e))?;
527 config.host_ipv4_address = u32::from(ip_addr);
528
529 let netmask = tap.netmask().map_err(|e| map_net_error("netmask", e))?;
530 config.netmask = u32::from(netmask);
531
532 let result_mac_addr = &mut config.host_mac_address;
533 let mac_addr_octets = tap
534 .mac_address()
535 .map_err(|e| map_net_error("mac address", e))?
536 .octets();
537 result_mac_addr.resize(mac_addr_octets.len(), 0);
538 result_mac_addr.clone_from_slice(&mac_addr_octets);
539
540 Ok(())
541 }
542
543 /// Handles a request on a readable socket identified by its index in the slice returned by
544 /// `sockets`.
545 ///
546 /// The `vm` is used to service request that affect the VM. The `vcpu_handles` slice is used to
547 /// interrupt a VCPU thread currently running in the VM if the socket request it.
handle_socket( &mut self, index: usize, kvm: &Kvm, vm: &mut Vm, vcpu_handles: &[JoinHandle<()>], taps: &[Tap], ) -> result::Result<(), CommError>548 pub fn handle_socket(
549 &mut self,
550 index: usize,
551 kvm: &Kvm,
552 vm: &mut Vm,
553 vcpu_handles: &[JoinHandle<()>],
554 taps: &[Tap],
555 ) -> result::Result<(), CommError> {
556 let (msg_size, request_file) = self.request_sockets[index]
557 .recv_with_file(&mut self.request_buffer)
558 .map_err(io_to_sys_err)
559 .map_err(CommError::PluginSocketRecv)?;
560
561 if msg_size == 0 {
562 return Err(CommError::PluginSocketHup);
563 }
564
565 let request: MainRequest = Message::parse_from_bytes(&self.request_buffer[..msg_size])
566 .map_err(CommError::DecodeRequest)?;
567
568 /// Use this to make it easier to stuff various kinds of File-like objects into the
569 /// `boxed_fds` list.
570 fn box_owned_fd<F: IntoRawDescriptor + 'static>(f: F) -> Box<dyn IntoRawDescriptor> {
571 Box::new(f)
572 }
573
574 // This vec is used to extend ownership of certain FDs until the end of this function.
575 let mut boxed_fds = Vec::new();
576 let mut response_fds = Vec::new();
577 let mut response = MainResponse::new();
578 let res = if request.has_create() {
579 response.mut_create();
580 let create = request.create();
581 match self.objects.entry(create.id) {
582 Entry::Vacant(entry) => {
583 if create.has_io_event() {
584 match Self::handle_io_event(entry, vm, create.io_event()) {
585 Ok(fd) => {
586 response_fds.push(fd);
587 Ok(())
588 }
589 Err(e) => Err(e),
590 }
591 } else if create.has_memory() {
592 let memory = create.memory();
593 match request_file {
594 Some(memfd) => Self::handle_memory(
595 entry,
596 vm,
597 memfd,
598 memory.offset,
599 memory.start,
600 memory.length,
601 memory.read_only,
602 memory.dirty_log,
603 ),
604 None => Err(SysError::new(EBADF)),
605 }
606 } else if create.has_irq_event() {
607 let irq_event = create.irq_event();
608 match (Event::new(), Event::new()) {
609 (Ok(evt), Ok(resample_evt)) => match vm.register_irqfd_resample(
610 &evt,
611 &resample_evt,
612 irq_event.irq_id,
613 ) {
614 Ok(()) => {
615 response_fds.push(evt.as_raw_descriptor());
616 response_fds.push(resample_evt.as_raw_descriptor());
617 boxed_fds.push(box_owned_fd(resample_evt));
618 entry.insert(PluginObject::IrqEvent {
619 irq_id: irq_event.irq_id,
620 evt,
621 });
622 Ok(())
623 }
624 Err(e) => Err(e),
625 },
626 (Err(e), _) | (_, Err(e)) => Err(e),
627 }
628 } else {
629 Err(SysError::new(ENOTTY))
630 }
631 }
632 Entry::Occupied(_) => Err(SysError::new(EEXIST)),
633 }
634 } else if request.has_destroy() {
635 response.mut_destroy();
636 match self.objects.entry(request.destroy().id) {
637 Entry::Occupied(entry) => entry.remove().destroy(vm),
638 Entry::Vacant(_) => Err(SysError::new(ENOENT)),
639 }
640 } else if request.has_new_connection() {
641 response.mut_new_connection();
642 match new_seqpacket_pair() {
643 Ok((request_socket, child_socket)) => {
644 self.request_sockets.push(
645 request_socket
646 .try_into()
647 .map_err(|_| CommError::PluginSocketHup)?,
648 );
649 response_fds.push(child_socket.as_raw_descriptor());
650 boxed_fds.push(box_owned_fd(child_socket));
651 Ok(())
652 }
653 Err(e) => Err(e),
654 }
655 } else if request.has_get_shutdown_eventfd() {
656 response.mut_get_shutdown_eventfd();
657 response_fds.push(self.kill_evt.as_raw_descriptor());
658 Ok(())
659 } else if request.has_check_extension() {
660 // SAFETY:
661 // Safe because the Cap enum is not read by the check_extension method. In that method,
662 // cap is cast back to an integer and fed to an ioctl. If the extension name is actually
663 // invalid, the kernel will safely reject the extension under the assumption that the
664 // capability is legitimately unsupported.
665 let cap = unsafe { transmute::<u32, kvm::Cap>(request.check_extension().extension) };
666 response.mut_check_extension().has_extension = vm.check_extension(cap);
667 Ok(())
668 } else if request.has_reserve_range() {
669 response.mut_reserve_range();
670 self.handle_reserve_range(request.reserve_range())
671 } else if request.has_set_irq() {
672 response.mut_set_irq();
673 let irq = request.set_irq();
674 vm.set_irq_line(irq.irq_id, irq.active)
675 } else if request.has_set_irq_routing() {
676 response.mut_set_irq_routing();
677 Self::handle_set_irq_routing(vm, request.set_irq_routing())
678 } else if request.has_get_state() {
679 let response_state = response.mut_get_state();
680 match get_vm_state(vm, request.get_state().set) {
681 Ok(state) => {
682 response_state.state = state;
683 Ok(())
684 }
685 Err(e) => Err(e),
686 }
687 } else if request.has_set_state() {
688 response.mut_set_state();
689 let set_state = request.set_state();
690 set_vm_state(vm, set_state.set, &set_state.state)
691 } else if request.has_set_identity_map_addr() {
692 response.mut_set_identity_map_addr();
693 let addr = request.set_identity_map_addr().address;
694 vm.set_identity_map_addr(GuestAddress(addr as u64))
695 } else if request.has_pause_vcpus() {
696 response.mut_pause_vcpus();
697 let pause_vcpus = request.pause_vcpus();
698 self.handle_pause_vcpus(vcpu_handles, pause_vcpus.cpu_mask, pause_vcpus.user);
699 Ok(())
700 } else if request.has_get_vcpus() {
701 response.mut_get_vcpus();
702 for pipe in self.vcpu_pipes.iter() {
703 response_fds.push(pipe.plugin_write.as_raw_descriptor());
704 response_fds.push(pipe.plugin_read.as_raw_descriptor());
705 }
706 Ok(())
707 } else if request.has_start() {
708 response.mut_start();
709 if self.started {
710 Err(SysError::new(EINVAL))
711 } else {
712 self.started = true;
713 Ok(())
714 }
715 } else if request.has_get_net_config() {
716 match taps.first() {
717 Some(tap) => {
718 match Self::handle_get_net_config(tap, response.mut_get_net_config()) {
719 Ok(_) => {
720 response_fds.push(tap.as_raw_descriptor());
721 Ok(())
722 }
723 Err(e) => Err(e),
724 }
725 }
726 None => Err(SysError::new(ENODATA)),
727 }
728 } else if request.has_set_call_hint() {
729 response.mut_set_call_hint();
730 self.handle_set_call_hint(request.set_call_hint())
731 } else if request.has_dirty_log() {
732 let dirty_log_response = response.mut_dirty_log();
733 match self.objects.get(&request.dirty_log().id) {
734 Some(&PluginObject::Memory { slot, length }) => {
735 dirty_log_response
736 .bitmap
737 .resize(dirty_log_bitmap_size(length), 0);
738 vm.get_dirty_log(slot, &mut dirty_log_response.bitmap)
739 }
740 _ => Err(SysError::new(ENOENT)),
741 }
742 } else if request.has_get_supported_cpuid() {
743 let cpuid_response = &mut response.mut_get_supported_cpuid().entries;
744 match kvm.get_supported_cpuid() {
745 Ok(mut cpuid) => {
746 for entry in cpuid.mut_entries_slice() {
747 cpuid_response.push(cpuid_kvm_to_proto(entry));
748 }
749 Ok(())
750 }
751 Err(e) => Err(e),
752 }
753 } else if request.has_get_emulated_cpuid() {
754 let cpuid_response = &mut response.mut_get_emulated_cpuid().entries;
755 match kvm.get_emulated_cpuid() {
756 Ok(mut cpuid) => {
757 for entry in cpuid.mut_entries_slice() {
758 cpuid_response.push(cpuid_kvm_to_proto(entry));
759 }
760 Ok(())
761 }
762 Err(e) => Err(e),
763 }
764 } else if request.has_get_msr_index_list() {
765 let msr_list_response = &mut response.mut_get_msr_index_list().indices;
766 match kvm.get_msr_index_list() {
767 Ok(indices) => {
768 for entry in indices {
769 msr_list_response.push(entry);
770 }
771 Ok(())
772 }
773 Err(e) => Err(e),
774 }
775 } else {
776 Err(SysError::new(ENOTTY))
777 };
778
779 if let Err(e) = res {
780 response.errno = e.errno();
781 }
782
783 self.response_buffer.clear();
784 response
785 .write_to_vec(&mut self.response_buffer)
786 .map_err(CommError::EncodeResponse)?;
787 assert_ne!(self.response_buffer.len(), 0);
788 self.request_sockets[index]
789 .send_with_fds(&self.response_buffer, &response_fds)
790 .map_err(io_to_sys_err)
791 .map_err(CommError::PluginSocketSend)?;
792
793 Ok(())
794 }
795 }
796
797 impl Drop for Process {
drop(&mut self)798 fn drop(&mut self) {
799 // Ignore the result because there is nothing we can do about it.
800 if let Err(e) = self.signal_kill() {
801 error!("failed to signal kill event for plugin: {}", e);
802 }
803 }
804 }
805