// Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! This module implements the virtio wayland used by the guest to access the host's wayland server. //! //! The virtio wayland protocol is done over two queues: `in` and `out`. The `in` queue is used for //! sending commands to the guest that are generated by the host, usually messages from the wayland //! server. The `out` queue is for commands from the guest, usually requests to allocate shared //! memory, open a wayland server connection, or send data over an existing connection. //! //! Each `WlVfd` represents one virtual file descriptor created by either the guest or the host. //! Virtual file descriptors contain actual file descriptors, either a shared memory file descriptor //! or a unix domain socket to the wayland server. In the shared memory case, there is also an //! associated slot that indicates which KVM memory slot the memory is installed into, as well as a //! page frame number that the guest can access the memory from. //! //! The types starting with `Ctrl` are structures representing the virtio wayland protocol "on the //! wire." They are decoded and executed in the `execute` function and encoded as some variant of //! `WlResp` for responses. //! //! There is one `WlState` instance that contains every known vfd and the current state of `in` //! queue. The `in` queue requires extra state to buffer messages to the guest in case the `in` //! queue is already full. The `WlState` also has a control socket necessary to fulfill certain //! requests, such as those registering guest memory. //! //! The `Worker` is responsible for the poll loop over all possible events, encoding/decoding from //! the virtio queue, and routing messages in and out of `WlState`. Possible events include the kill //! event, available descriptors on the `in` or `out` queue, and incoming data on any vfd's socket. use std::collections::BTreeMap as Map; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::PathBuf; use std::sync::Arc; use std::thread; use super::resource_bridge::*; use super::{Interrupt, InterruptProxyEvent, Queue, VirtioDevice, TYPE_WL, VIRTIO_F_VERSION_1}; use crate::MemoryParams; use vm_control::{MaybeOwnedFd, VmMemoryControlRequestSocket}; use msg_socket::{MsgOnSocket, MsgReceiver, MsgSocket}; use serde::{Deserialize, Serialize}; use sys_util::net::UnixSeqpacket; use sys_util::{error, EventFd, GuestMemory, PollContext, PollToken, SharedMemory}; #[derive(Debug, MsgOnSocket)] pub enum MsgOnSocketRequest { Create { // wayland_paths: Map, vm_socket: MaybeOwnedFd, // resource_bridge: Option, memory_params: MemoryParams, }, Activate { shm: MaybeOwnedFd, interrupt: MaybeOwnedFd, interrupt_resample_evt: MaybeOwnedFd, in_queue: Queue, out_queue: Queue, in_queue_evt: MaybeOwnedFd, out_queue_evt: MaybeOwnedFd, }, Kill, } #[derive(Debug, Serialize, Deserialize)] pub enum BincodeRequest { WriteConfig { offset: u64, data: Vec }, } pub type Request = poly_msg_socket::Value; impl From for Request { fn from(request: MsgOnSocketRequest) -> Self { Self::MsgOnSocket(request) } } impl From for Request { fn from(request: BincodeRequest) -> Self { Self::Bincode(request) } } #[derive(Debug, MsgOnSocket)] pub enum MsgOnSocketResponse { Kill, } #[derive(Debug, Deserialize, Serialize)] pub struct BincodeResponse; pub type Response = poly_msg_socket::Value; impl From for Response { fn from(response: MsgOnSocketResponse) -> Self { Self::MsgOnSocket(response) } } impl From for Response { fn from(response: BincodeResponse) -> Self { Self::Bincode(response) } } use poly_msg_socket::PolyMsgSocket; type Socket = PolyMsgSocket; const VIRTIO_WL_F_TRANS_FLAGS: u32 = 0x01; // TODO: support arbitrary number of queues const QUEUE_SIZE: u16 = 16; const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE, QUEUE_SIZE]; struct Worker { device_socket: Arc, interrupt: Interrupt, interrupt_socket: MsgSocket<(), InterruptProxyEvent>, shutdown: bool, } impl Worker { fn new( device_socket: Arc, interrupt: Interrupt, interrupt_socket: MsgSocket<(), InterruptProxyEvent>, ) -> Self { Self { device_socket, interrupt, interrupt_socket, shutdown: false, } } fn handle_response(&mut self) { use poly_msg_socket::Value::*; match self.device_socket.recv() { Ok(MsgOnSocket(MsgOnSocketResponse::Kill)) => { self.shutdown = true; } Ok(Bincode(BincodeResponse)) => unreachable!(), Err(e) => { error!("recv failed: {:?}", e); } } } fn interrupt(&self) { use InterruptProxyEvent::*; match self.interrupt_socket.recv() { Ok(SignalUsedQueue(value)) => self.interrupt.signal_used_queue(value).unwrap(), Ok(SignalConfigChanged) => self.interrupt.signal_config_changed().unwrap(), Ok(InterruptResample) => self.interrupt.interrupt_resample().unwrap(), Err(e) => { eprintln!("recv error: {}", e); panic!("recv error: {}", e) } } } fn kill(&self) { if let Err(e) = self.device_socket.send(poly_msg_socket::Value::MsgOnSocket( MsgOnSocketRequest::Kill, )) { error!("failed to send Kill message: {}", e); } } fn run(mut self, kill_evt: EventFd) { #[derive(Debug, PollToken)] enum Token { Device, Interrupt, Kill, } let poll_ctx: PollContext = match PollContext::build_with(&[ (&*self.device_socket, Token::Device), (&self.interrupt_socket, Token::Interrupt), (&kill_evt, Token::Kill), ]) { Ok(pc) => pc, Err(e) => { error!("failed creating PollContext: {}", e); return; } }; while !self.shutdown { let events = match poll_ctx.wait() { Ok(v) => v, Err(e) => { error!("failed polling for events: {}", e); break; } }; for event in &events { match event.token() { Token::Device => self.handle_response(), Token::Interrupt => self.interrupt(), Token::Kill => self.kill(), } } } } } pub struct Controller { kill_evt: Option, worker_thread: Option>, use_transition_flags: bool, socket: Arc, } impl Controller { pub fn create( wayland_paths: Map, vm_socket: VmMemoryControlRequestSocket, resource_bridge: Option, memory_params: MemoryParams, socket: Socket, ) -> Result { socket.send(MsgOnSocketRequest::Create { // wayland_paths, vm_socket: MaybeOwnedFd::new_borrowed(&vm_socket), // resource_bridge, memory_params, })?; Ok(Controller { kill_evt: None, worker_thread: None, use_transition_flags: false, socket: Arc::new(socket), }) } } impl Drop for Controller { fn drop(&mut self) { if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); } if let Some(worker_thread) = self.worker_thread.take() { let _ = worker_thread.join(); } } } impl VirtioDevice for Controller { fn keep_fds(&self) -> Vec { let mut keep_fds = Vec::new(); if let Some(ref kill_evt) = self.kill_evt { keep_fds.push(kill_evt.as_raw_fd()); } keep_fds.push(self.socket.as_raw_fd()); keep_fds } fn device_type(&self) -> u32 { TYPE_WL } fn queue_max_sizes(&self) -> &[u16] { QUEUE_SIZES } fn features(&self) -> u64 { 1 << VIRTIO_WL_F_TRANS_FLAGS | 1 << VIRTIO_F_VERSION_1 } fn ack_features(&mut self, value: u64) { if value & (1 << VIRTIO_WL_F_TRANS_FLAGS) != 0 { self.use_transition_flags = true; } } fn activate( &mut self, mem: GuestMemory, interrupt: Interrupt, mut queues: Vec, queue_evts: Vec, ) { if queues.len() != QUEUE_SIZES.len() || queue_evts.len() != QUEUE_SIZES.len() { return; } let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) { Ok(v) => v, Err(e) => { error!("failed creating kill EventFd pair: {}", e); return; } }; self.kill_evt = Some(self_kill_evt); let use_transition_flags = self.use_transition_flags; let (ours, theirs) = UnixSeqpacket::pair().expect("pair failed"); if let Err(e) = self.socket.send(MsgOnSocketRequest::Activate { shm: MaybeOwnedFd::new_borrowed(&mem), interrupt: MaybeOwnedFd::new_borrowed(&theirs), interrupt_resample_evt: MaybeOwnedFd::new_borrowed(interrupt.get_resample_evt()), in_queue: queues.remove(0), out_queue: queues.remove(0), in_queue_evt: MaybeOwnedFd::new_borrowed(&queue_evts[0]), out_queue_evt: MaybeOwnedFd::new_borrowed(&queue_evts[1]), }) { error!("failed to send Activate: {}", e); return; } let socket = Arc::clone(&self.socket); let worker_result = thread::Builder::new() .name("virtio_wl".to_string()) .spawn(move || { Worker::new(socket, interrupt, MsgSocket::new(ours)).run(kill_evt); }); match worker_result { Err(e) => { error!("failed to spawn virtio_wl worker: {}", e); return; } Ok(join_handle) => { self.worker_thread = Some(join_handle); } } } }