summary refs log blame commit diff
path: root/devices/src/virtio/controller.rs
blob: 22e28506fe535f15309a5abe70fe27e94f00c355 (plain) (tree)
































                                                                                                    
                   

                

                                                                                              
            
                                                                           

                 

                                                             

                                                      
                                 
                                                                                  
 


























                                                                      
                             
                             



                                                          
                                    
      
 

               

             

                     





                                                      


                                             
 

          
         

 

                                        
                                           
                                               

                              
                  















                                                                              
                             
                              
                    
                  
                


         
                                        

                          

                                            
                                            
 

















                                                                                            
 





                                                      
               
                               
                         


                                                         

 

             
                                   











                                                             
                                      
                                         
                                                           


                                     


                                                                 
 
                       
                                               










                                                                                           
                                                   



                    


                                                                                    
                                                 
         

     






                                         
 
                                                                           
                                                  




                                                       
                                                             

             
 


                                                
                           
                                                               







                                                            








                                                  
                        


                 
                  


                                                       
                                    
                       

                                                     


                                                              
                          

            


                                
                                     




















                                                                             



                                                   
                                               




                                  









                                                                            

     

                                           


                               









                                                                        


                                            
                                                                                  
                                                        


         



                                                                                      
                                                       





                                                                              
                                                                             


         








                                                                      







                                                                                       




                                                            




                                                                                                    
                                                                   



                                            
                                                                         
 








                                                                                             
                                                     










                                                                                   
                                                                  


                                                       


             














                                                                     












                                                                                    















                                                                         
 
// 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::{
    pci::{PciAddress, PciBarConfiguration, PciCapability, PciCapabilityID},
    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};

// As far as I can tell, these never change on the other side, so it's
// fine to just copy them over.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RemotePciCapability {
    bytes: Vec<u8>,
    id: PciCapabilityID,
}

impl RemotePciCapability {
    pub fn from(capability: &dyn PciCapability) -> Self {
        Self {
            bytes: capability.bytes().to_vec(),
            id: capability.id(),
        }
    }
}

impl PciCapability for RemotePciCapability {
    fn bytes(&self) -> &[u8] {
        &self.bytes
    }

    fn id(&self) -> PciCapabilityID {
        self.id
    }
}

#[derive(Debug, MsgOnSocket)]
pub enum MsgOnSocketRequest {
    Create {
        // wayland_paths: Map<String, PathBuf>,
        vm_socket: MaybeOwnedFd<UnixSeqpacket>,
        // resource_bridge: Option<ResourceRequestSocket>,
        memory_params: MemoryParams,
    },

    DeviceType,

    Features,

    AckFeatures(u64),

    Activate {
        shm: MaybeOwnedFd<SharedMemory>,
        interrupt: MaybeOwnedFd<UnixSeqpacket>,
        interrupt_resample_evt: MaybeOwnedFd<EventFd>,
        in_queue: Queue,
        out_queue: Queue,
        in_queue_evt: MaybeOwnedFd<EventFd>,
        out_queue_evt: MaybeOwnedFd<EventFd>,
    },

    Reset,

    Kill,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum BincodeRequest {
    ReadConfig { offset: u64, len: usize },
    WriteConfig { offset: u64, data: Vec<u8> },

    GetDeviceBars(PciAddress),
    GetDeviceCaps,
}

pub type Request = poly_msg_socket::Value<MsgOnSocketRequest, BincodeRequest>;

impl From<MsgOnSocketRequest> for Request {
    fn from(request: MsgOnSocketRequest) -> Self {
        Self::MsgOnSocket(request)
    }
}

impl From<BincodeRequest> for Request {
    fn from(request: BincodeRequest) -> Self {
        Self::Bincode(request)
    }
}

#[derive(Debug, MsgOnSocket)]
pub enum MsgOnSocketResponse {
    DeviceType(u32),
    Features(u64),
    Reset(bool),
    Kill,
}

#[derive(Debug, Deserialize, Serialize)]
pub enum BincodeResponse {
    ReadConfig(Vec<u8>),

    GetDeviceBars(Vec<PciBarConfiguration>),
    GetDeviceCaps(Vec<RemotePciCapability>),
}

pub type Response = poly_msg_socket::Value<MsgOnSocketResponse, BincodeResponse>;

impl From<MsgOnSocketResponse> for Response {
    fn from(response: MsgOnSocketResponse) -> Self {
        Self::MsgOnSocket(response)
    }
}

impl From<BincodeResponse> for Response {
    fn from(response: BincodeResponse) -> Self {
        Self::Bincode(response)
    }
}

use poly_msg_socket::PolyMsgSocket;
type Socket =
    PolyMsgSocket<MsgOnSocketRequest, MsgOnSocketResponse, BincodeRequest, BincodeResponse>;

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<Socket>,
    interrupt: Interrupt,
    interrupt_socket: MsgSocket<(), InterruptProxyEvent>,

    shutdown: bool,
}

impl Worker {
    fn new(
        device_socket: Arc<Socket>,
        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(msg) => {
                panic!("unexpected message received: {:?}", msg);
            }

            Err(e) => {
                panic!("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) => panic!("recv failed: {}", e),
        }
    }

    fn kill(&self) {
        if let Err(e) = self.device_socket.send(poly_msg_socket::Value::MsgOnSocket(
            MsgOnSocketRequest::Kill,
        )) {
            error!("failed to send Kill: {}", e);
        }
    }

    fn run(mut self, kill_evt: EventFd) {
        #[derive(Debug, PollToken)]
        enum Token {
            Device,
            Interrupt,
            Kill,
        }

        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
            (&*self.device_socket, Token::Device),
            (&self.interrupt_socket, Token::Interrupt),
            (&kill_evt, Token::Kill),
        ]) {
            Ok(pc) => pc,
            Err(e) => {
                panic!("failed creating PollContext: {}", e);
            }
        };

        while !self.shutdown {
            let events = match poll_ctx.wait() {
                Ok(v) => v,
                Err(e) => {
                    panic!("failed polling for events: {}", e);
                }
            };

            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<EventFd>,
    worker_thread: Option<thread::JoinHandle<()>>,
    socket: Arc<Socket>,
}

impl Controller {
    pub fn create(
        wayland_paths: Map<String, PathBuf>,
        vm_socket: VmMemoryControlRequestSocket,
        resource_bridge: Option<ResourceRequestSocket>,
        memory_params: MemoryParams,
        socket: Socket,
    ) -> Result<Controller, poly_msg_socket::Error> {
        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,
            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<RawFd> {
        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 {
        if let Err(e) = self.socket.send(MsgOnSocketRequest::DeviceType) {
            panic!("failed to send DeviceType: {}", e);
        }

        match self.socket.recv_msg_on_socket() {
            Ok(MsgOnSocketResponse::DeviceType(device_type)) => device_type,
            response => {
                panic!("bad response to Reset: {:?}", response);
            }
        }
    }

    fn queue_max_sizes(&self) -> Vec<u16> {
        QUEUE_SIZES.to_vec()
    }

    fn features(&self) -> u64 {
        if let Err(e) = self.socket.send(MsgOnSocketRequest::Features) {
            panic!("failed to send Features: {}", e);
        }

        match self.socket.recv_msg_on_socket() {
            Ok(MsgOnSocketResponse::Features(features)) => features,
            response => {
                panic!("bad response to Reset: {:?}", response);
            }
        }
    }

    fn ack_features(&mut self, value: u64) {
        if let Err(e) = self.socket.send(MsgOnSocketRequest::AckFeatures(value)) {
            panic!("failed to send AckFeatures: {}", e);
        }
    }

    fn read_config(&self, offset: u64, data: &mut [u8]) {
        let len = data.len();

        if let Err(e) = self.socket.send(BincodeRequest::ReadConfig { offset, len }) {
            panic!("failed to send ReadConfig: {}", e);
        }

        match self.socket.recv_bincode() {
            Ok(BincodeResponse::ReadConfig(response)) => {
                data.copy_from_slice(&response[..len]); // TODO: test no panic
            }
            response => panic!("bad response to ReadConfig: {:?}", response),
        }
    }

    fn write_config(&mut self, offset: u64, data: &[u8]) {
        if let Err(e) = self.socket.send(BincodeRequest::WriteConfig {
            offset,
            data: data.to_vec(),
        }) {
            error!("failed to send WriteConfig: {}", e);
        }
    }

    fn activate(
        &mut self,
        mem: GuestMemory,
        interrupt: Interrupt,
        mut queues: Vec<Queue>,
        queue_evts: Vec<EventFd>,
    ) {
        if queues.len() != QUEUE_SIZES.len() || queue_evts.len() != QUEUE_SIZES.len() {
            panic!(
                "queues ({}) or queue_evts ({}) wrong size",
                queues.len(),
                queue_evts.len()
            );
        }

        let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
            Ok(v) => v,
            Err(e) => {
                panic!("failed creating kill EventFd pair: {}", e);
            }
        };
        self.kill_evt = Some(self_kill_evt);

        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]),
        }) {
            panic!("failed to send Activate: {}", e);
        }

        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) => {
                panic!("failed to spawn virtio_wl worker: {}", e);
            }
            Ok(join_handle) => {
                self.worker_thread = Some(join_handle);
            }
        }
    }

    fn reset(&mut self) -> bool {
        if let Err(e) = self.socket.send(MsgOnSocketRequest::Reset) {
            error!("failed to send Reset: {}", e);
            return false;
        }

        match self.socket.recv_msg_on_socket() {
            Ok(MsgOnSocketResponse::Reset(result)) => result,
            response => {
                error!("bad response to Reset: {:?}", response);
                false
            }
        }
    }

    fn get_device_bars(&mut self, address: PciAddress) -> Vec<PciBarConfiguration> {
        if let Err(e) = self.socket.send(BincodeRequest::GetDeviceBars(address)) {
            panic!("failed to send GetDeviceBars: {}", e);
        }

        match self.socket.recv_bincode() {
            Ok(BincodeResponse::GetDeviceBars(bars)) => bars,
            response => {
                panic!("bad response to GetDeviceBars: {:?}", response);
            }
        }
    }

    fn get_device_caps(&self) -> Vec<Box<dyn PciCapability>> {
        if let Err(e) = self.socket.send(BincodeRequest::GetDeviceCaps) {
            panic!("failed to send GetDeviceCaps: {}", e);
        }

        match self.socket.recv_bincode() {
            Ok(BincodeResponse::GetDeviceCaps(caps)) => caps
                .into_iter()
                .map(|cap| Box::new(cap) as Box<dyn PciCapability>)
                .collect(),
            response => {
                panic!("bad response to GetDeviceCaps: {:?}", response);
            }
        }
    }
}