utilize EventDevices to make virtio-input devices

BUG=chromium:1023975
TEST=./build_test

Change-Id: I10267e535d4d1dae90b2b5f30db046c388791a16
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1930409
Reviewed-by: Zach Reizner <zachr@chromium.org>
Commit-Queue: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Zach Reizner 2019-11-22 17:34:58 -08:00 committed by Commit Bot
parent 2767223fdb
commit 65b98f1ccc
5 changed files with 132 additions and 25 deletions

View file

@ -11,6 +11,8 @@ use std::os::unix::io::AsRawFd;
use std::rc::Rc;
use std::usize;
use libc::EINVAL;
use data_model::*;
use msg_socket::{MsgReceiver, MsgSender};
@ -19,7 +21,8 @@ use sys_util::{error, GuestAddress, GuestMemory};
use gpu_display::*;
use gpu_renderer::{
Box3, Context as RendererContext, Renderer, Resource as GpuRendererResource, ResourceCreateArgs,
Box3, Context as RendererContext, Error as GpuRendererError, Renderer,
Resource as GpuRendererResource, ResourceCreateArgs,
};
use super::protocol::{
@ -73,6 +76,7 @@ impl VirtioGpuResource {
let (query, dmabuf) = match self.gpu_resource.export() {
Ok(export) => (export.0, export.1),
Err(GpuRendererError::Virglrenderer(e)) if e == -EINVAL => return None,
Err(e) => {
error!("failed to query resource: {}", e);
return None;
@ -160,6 +164,8 @@ pub struct Backend {
renderer: Renderer,
resources: Map<u32, VirtioGpuResource>,
contexts: Map<u32, RendererContext>,
// Maps event devices to scanout number.
event_devices: Map<u32, u32>,
gpu_device_socket: VmMemoryControlRequestSocket,
scanout_surface: Option<u32>,
cursor_surface: Option<u32>,
@ -187,9 +193,10 @@ impl Backend {
display_width,
display_height,
renderer,
gpu_device_socket,
resources: Default::default(),
contexts: Default::default(),
event_devices: Default::default(),
gpu_device_socket,
scanout_surface: None,
cursor_surface: None,
scanout_resource: 0,
@ -203,6 +210,25 @@ impl Backend {
&self.display
}
pub fn import_event_device(&mut self, event_device: EventDevice, scanout: u32) {
// TODO(zachr): support more than one scanout.
if scanout != 0 {
return;
}
let mut display = self.display.borrow_mut();
let event_device_id = match display.import_event_device(event_device) {
Ok(id) => id,
Err(e) => {
error!("error importing event device: {}", e);
return;
}
};
self.scanout_surface
.map(|s| display.attach_event_device(s, event_device_id));
self.event_devices.insert(event_device_id, scanout);
}
/// Processes the internal `display` events and returns `true` if the main display was closed.
pub fn process_display(&mut self) -> bool {
let mut display = self.display.borrow_mut();
@ -281,9 +307,9 @@ impl Backend {
}
/// Sets the given resource id as the source of scanout to the display.
pub fn set_scanout(&mut self, id: u32) -> GpuResponse {
pub fn set_scanout(&mut self, _scanout_id: u32, resource_id: u32) -> GpuResponse {
let mut display = self.display.borrow_mut();
if id == 0 {
if resource_id == 0 {
if let Some(surface) = self.scanout_surface.take() {
display.release_surface(surface);
}
@ -293,12 +319,19 @@ impl Backend {
}
self.cursor_resource = 0;
GpuResponse::OkNoData
} else if self.resources.get_mut(&id).is_some() {
self.scanout_resource = id;
} else if self.resources.get_mut(&resource_id).is_some() {
self.scanout_resource = resource_id;
if self.scanout_surface.is_none() {
match display.create_surface(None, self.display_width, self.display_height) {
Ok(surface) => self.scanout_surface = Some(surface),
Ok(surface) => {
// TODO(zachr): do not assume every event device belongs to this scanout_id;
// in other words, support multiple display outputs.
for &event_device in self.event_devices.keys() {
display.attach_event_device(surface, event_device);
}
self.scanout_surface = Some(surface);
}
Err(e) => error!("failed to create display surface: {}", e),
}
}

View file

@ -23,6 +23,7 @@ use sys_util::{
debug, error, warn, Error, EventFd, GuestAddress, GuestMemory, PollContext, PollToken,
};
pub use gpu_display::EventDevice;
use gpu_display::*;
use gpu_renderer::{Renderer, RendererFlags};
@ -119,7 +120,9 @@ impl Frontend {
GpuCommand::ResourceUnref(info) => {
self.backend.unref_resource(info.resource_id.to_native())
}
GpuCommand::SetScanout(info) => self.backend.set_scanout(info.resource_id.to_native()),
GpuCommand::SetScanout(info) => self
.backend
.set_scanout(info.scanout_id.to_native(), info.resource_id.to_native()),
GpuCommand::ResourceFlush(info) => self.backend.flush_resource(
info.resource_id.to_native(),
info.r.x.to_native(),
@ -672,6 +675,7 @@ fn build_backend(
possible_displays: &[DisplayBackend],
display_width: u32,
display_height: u32,
event_devices: Vec<EventDevice>,
gpu_device_socket: VmMemoryControlRequestSocket,
pci_bar: Alloc,
) -> Option<Backend> {
@ -717,22 +721,29 @@ fn build_backend(
}
};
Some(Backend::new(
let mut backend = Backend::new(
display,
display_width,
display_height,
renderer,
gpu_device_socket,
pci_bar,
))
);
for event_device in event_devices {
backend.import_event_device(event_device, 0);
}
Some(backend)
}
pub struct Gpu {
config_event: bool,
exit_evt: EventFd,
gpu_device_socket: Option<VmMemoryControlRequestSocket>,
resource_bridges: Vec<ResourceResponseSocket>,
event_devices: Vec<EventDevice>,
kill_evt: Option<EventFd>,
config_event: bool,
worker_thread: Option<thread::JoinHandle<()>>,
num_scanouts: NonZeroU8,
display_backends: Vec<DisplayBackend>,
@ -749,15 +760,17 @@ impl Gpu {
resource_bridges: Vec<ResourceResponseSocket>,
display_backends: Vec<DisplayBackend>,
gpu_parameters: &GpuParameters,
event_devices: Vec<EventDevice>,
) -> Gpu {
Gpu {
config_event: false,
exit_evt,
gpu_device_socket,
num_scanouts,
resource_bridges,
event_devices,
config_event: false,
kill_evt: None,
worker_thread: None,
num_scanouts,
display_backends,
display_width: gpu_parameters.display_width,
display_height: gpu_parameters.display_height,
@ -881,6 +894,7 @@ impl VirtioDevice for Gpu {
let display_backends = self.display_backends.clone();
let display_width = self.display_width;
let display_height = self.display_height;
let event_devices = self.event_devices.split_off(0);
if let (Some(gpu_device_socket), Some(pci_bar)) =
(self.gpu_device_socket.take(), self.pci_bar.take())
{
@ -892,6 +906,7 @@ impl VirtioDevice for Gpu {
&display_backends,
display_width,
display_height,
event_devices,
gpu_device_socket,
pci_bar,
) {

View file

@ -155,6 +155,8 @@ pub struct Config {
pub cras_audio: bool,
pub cras_capture: bool,
pub null_audio: bool,
pub display_window_keyboard: bool,
pub display_window_mouse: bool,
pub serial_parameters: BTreeMap<u8, SerialParameters>,
pub syslog_tag: Option<String>,
pub virtio_single_touch: Option<TouchDeviceOption>,
@ -194,6 +196,8 @@ impl Default for Config {
wayland_socket_path: None,
wayland_dmabuf: false,
x_display: None,
display_window_keyboard: false,
display_window_mouse: false,
shared_dirs: Vec::new(),
sandbox: !cfg!(feature = "default-no-sandbox"),
seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),

View file

@ -28,6 +28,8 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
use libc::{self, c_int, gid_t, uid_t};
use audio_streams::DummyStreamSource;
#[cfg(feature = "gpu")]
use devices::virtio::EventDevice;
use devices::virtio::{self, VirtioDevice};
use devices::{
self, HostBackendDeviceProvider, PciDevice, VfioDevice, VfioPciDevice, VirtioPciDevice,
@ -456,7 +458,7 @@ fn create_tpm_device(cfg: &Config) -> DeviceResult {
}
fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOption) -> DeviceResult {
let socket = create_input_socket(&single_touch_spec.path).map_err(|e| {
let socket = single_touch_spec.path.into_unix_stream().map_err(|e| {
error!("failed configuring virtio single touch: {:?}", e);
e
})?;
@ -470,7 +472,7 @@ fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOptio
}
fn create_trackpad_device(cfg: &Config, trackpad_spec: &TouchDeviceOption) -> DeviceResult {
let socket = create_input_socket(&trackpad_spec.path).map_err(|e| {
let socket = trackpad_spec.path.into_unix_stream().map_err(|e| {
error!("failed configuring virtio trackpad: {}", e);
e
})?;
@ -484,8 +486,8 @@ fn create_trackpad_device(cfg: &Config, trackpad_spec: &TouchDeviceOption) -> De
})
}
fn create_mouse_device(cfg: &Config, mouse_socket: &Path) -> DeviceResult {
let socket = create_input_socket(&mouse_socket).map_err(|e| {
fn create_mouse_device<T: IntoUnixStream>(cfg: &Config, mouse_socket: T) -> DeviceResult {
let socket = mouse_socket.into_unix_stream().map_err(|e| {
error!("failed configuring virtio mouse: {}", e);
e
})?;
@ -498,8 +500,8 @@ fn create_mouse_device(cfg: &Config, mouse_socket: &Path) -> DeviceResult {
})
}
fn create_keyboard_device(cfg: &Config, keyboard_socket: &Path) -> DeviceResult {
let socket = create_input_socket(&keyboard_socket).map_err(|e| {
fn create_keyboard_device<T: IntoUnixStream>(cfg: &Config, keyboard_socket: T) -> DeviceResult {
let socket = keyboard_socket.into_unix_stream().map_err(|e| {
error!("failed configuring virtio keyboard: {}", e);
e
})?;
@ -589,6 +591,7 @@ fn create_gpu_device(
gpu_sockets: Vec<virtio::resource_bridge::ResourceResponseSocket>,
wayland_socket_path: Option<PathBuf>,
x_display: Option<String>,
event_devices: Vec<EventDevice>,
) -> DeviceResult {
let jailed_wayland_path = Path::new("/wayland-0");
@ -615,6 +618,7 @@ fn create_gpu_device(
gpu_sockets,
display_backends,
cfg.gpu_parameters.as_ref().unwrap(),
event_devices,
);
let jail = match simple_jail(&cfg, "gpu_device.policy")? {
@ -986,6 +990,31 @@ fn create_virtio_devices(
#[cfg(feature = "gpu")]
{
if cfg.gpu_parameters.is_some() {
let mut event_devices = Vec::new();
if cfg.display_window_mouse {
let (event_device_socket, virtio_dev_socket) =
UnixStream::pair().map_err(Error::CreateSocket)?;
// TODO(nkgold): the width/height here should match the display's height/width. When
// those settings are available as CLI options, we should use the CLI options here
// as well.
let dev = virtio::new_single_touch(virtio_dev_socket, 1280, 1024)
.map_err(Error::InputDeviceNew)?;
devs.push(VirtioDeviceStub {
dev: Box::new(dev),
jail: simple_jail(&cfg, "input_device.policy")?,
});
event_devices.push(EventDevice::touchscreen(event_device_socket));
}
if cfg.display_window_keyboard {
let (event_device_socket, virtio_dev_socket) =
UnixStream::pair().map_err(Error::CreateSocket)?;
let dev = virtio::new_keyboard(virtio_dev_socket).map_err(Error::InputDeviceNew)?;
devs.push(VirtioDeviceStub {
dev: Box::new(dev),
jail: simple_jail(&cfg, "input_device.policy")?,
});
event_devices.push(EventDevice::keyboard(event_device_socket));
}
devs.push(create_gpu_device(
cfg,
_exit_evt,
@ -993,6 +1022,7 @@ fn create_virtio_devices(
resource_bridges,
cfg.wayland_socket_path.clone(),
cfg.x_display.clone(),
event_devices,
)?);
}
}
@ -1160,12 +1190,29 @@ fn raw_fd_from_path(path: &Path) -> Result<RawFd> {
validate_raw_fd(raw_fd).map_err(Error::ValidateRawFd)
}
fn create_input_socket(path: &Path) -> Result<UnixStream> {
if path.parent() == Some(Path::new("/proc/self/fd")) {
// Safe because we will validate |raw_fd|.
unsafe { Ok(UnixStream::from_raw_fd(raw_fd_from_path(path)?)) }
} else {
UnixStream::connect(path).map_err(Error::InputEventsOpen)
trait IntoUnixStream {
fn into_unix_stream(self) -> Result<UnixStream>;
}
impl<'a> IntoUnixStream for &'a Path {
fn into_unix_stream(self) -> Result<UnixStream> {
if self.parent() == Some(Path::new("/proc/self/fd")) {
// Safe because we will validate |raw_fd|.
unsafe { Ok(UnixStream::from_raw_fd(raw_fd_from_path(self)?)) }
} else {
UnixStream::connect(self).map_err(Error::InputEventsOpen)
}
}
}
impl<'a> IntoUnixStream for &'a PathBuf {
fn into_unix_stream(self) -> Result<UnixStream> {
self.as_path().into_unix_stream()
}
}
impl IntoUnixStream for UnixStream {
fn into_unix_stream(self) -> Result<UnixStream> {
Ok(self)
}
}

View file

@ -511,6 +511,12 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
}
cfg.x_display = Some(value.unwrap().to_owned());
}
"display-window-keyboard" => {
cfg.display_window_keyboard = true;
}
"display-window-mouse" => {
cfg.display_window_mouse = true;
}
"socket" => {
if cfg.socket_path.is_some() {
return Err(argument::Error::TooManyArguments(
@ -931,6 +937,8 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
"),
Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
Argument::value("x-display", "DISPLAY", "X11 display name to use."),
Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
#[cfg(feature = "wl-dmabuf")]
Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),