summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--devices/src/virtio/gpu/mod.rs16
-rw-r--r--gpu_display/src/display_wl.c4
-rw-r--r--gpu_display/src/dwl.rs66
-rw-r--r--gpu_display/src/lib.rs28
-rw-r--r--seccomp/x86_64/gpu_device.policy57
-rw-r--r--src/linux.rs90
6 files changed, 215 insertions, 46 deletions
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index 65c01c5..38efa41 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -13,7 +13,8 @@ use std::cell::RefCell;
 use std::collections::VecDeque;
 use std::i64;
 use std::mem::size_of;
-use std::os::unix::io::RawFd;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::path::{Path, PathBuf};
 use std::rc::Rc;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicUsize, Ordering};
@@ -587,14 +588,16 @@ pub struct Gpu {
     config_event: bool,
     exit_evt: EventFd,
     kill_evt: Option<EventFd>,
+    wayland_socket_path: PathBuf,
 }
 
 impl Gpu {
-    pub fn new(exit_evt: EventFd) -> Gpu {
+    pub fn new<P: AsRef<Path>>(exit_evt: EventFd, wayland_socket_path: P) -> Gpu {
         Gpu {
             config_event: false,
             exit_evt,
             kill_evt: None,
+            wayland_socket_path: wayland_socket_path.as_ref().to_path_buf(),
         }
     }
 
@@ -623,7 +626,9 @@ impl Drop for Gpu {
 
 impl VirtioDevice for Gpu {
     fn keep_fds(&self) -> Vec<RawFd> {
-        Vec::new()
+        let mut keep_fds = Vec::new();
+        keep_fds.push(self.exit_evt.as_raw_fd());
+        keep_fds
     }
 
     fn device_type(&self) -> u32 {
@@ -706,6 +711,7 @@ impl VirtioDevice for Gpu {
         let ctrl_evt = queue_evts.remove(0);
         let cursor_queue = queues.remove(0);
         let cursor_evt = queue_evts.remove(0);
+        let socket_path = self.wayland_socket_path.clone();
         spawn(move || {
             const UNDESIRED_CARDS: &[&str] = &["vgem", "pvr"];
             let drm_card = match gpu_buffer::rendernode::open_device(UNDESIRED_CARDS) {
@@ -724,10 +730,10 @@ impl VirtioDevice for Gpu {
                 }
             };
 
-            let display = match GpuDisplay::new() {
+            let display = match GpuDisplay::new(socket_path) {
                 Ok(c) => c,
                 Err(e) => {
-                    error!("{:?}", e);
+                    error!("failed to open display: {:?}", e);
                     return;
                 }
             };
diff --git a/gpu_display/src/display_wl.c b/gpu_display/src/display_wl.c
index e0c4e24..e308506 100644
--- a/gpu_display/src/display_wl.c
+++ b/gpu_display/src/display_wl.c
@@ -374,9 +374,9 @@ void dwl_context_destroy(struct dwl_context **self)
 	*self = NULL;
 }
 
-bool dwl_context_setup(struct dwl_context *self)
+bool dwl_context_setup(struct dwl_context *self, const char *socket_path)
 {
-	struct wl_display *display = wl_display_connect(NULL);
+	struct wl_display *display = wl_display_connect(socket_path);
 	if (!display) {
 		printf("failed to connect to display\n");
 		return false;
diff --git a/gpu_display/src/dwl.rs b/gpu_display/src/dwl.rs
index 9101c82..cbe337d 100644
--- a/gpu_display/src/dwl.rs
+++ b/gpu_display/src/dwl.rs
@@ -1,19 +1,58 @@
 /* automatically generated by rust-bindgen */
 
-# [ repr ( C ) ]
-# [ derive ( Debug , Copy , Clone ) ]
-pub struct dwl_context {
+
+/// @page page_xdg_shell_unstable_v6 The xdg_shell_unstable_v6 protocol
+/// @section page_ifaces_xdg_shell_unstable_v6 Interfaces
+/// - @subpage page_iface_zxdg_shell_v6 - create desktop-style surfaces
+/// - @subpage page_iface_zxdg_positioner_v6 - child surface positioner
+/// - @subpage page_iface_zxdg_surface_v6 - desktop user interface surface base interface
+/// - @subpage page_iface_zxdg_toplevel_v6 - toplevel surface
+/// - @subpage page_iface_zxdg_popup_v6 - short-lived, popup surfaces for menus
+/// @section page_copyright_xdg_shell_unstable_v6 Copyright
+/// <pre>
+///
+/// Copyright © 2008-2013 Kristian Høgsberg
+/// Copyright © 2013      Rafael Antognolli
+/// Copyright © 2013      Jasper St. Pierre
+/// Copyright © 2010-2013 Intel Corporation
+///
+/// Permission is hereby granted, free of charge, to any person obtaining a
+/// copy of this software and associated documentation files (the "Software"),
+/// to deal in the Software without restriction, including without limitation
+/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+/// and/or sell copies of the Software, and to permit persons to whom the
+/// Software is furnished to do so, subject to the following conditions:
+///
+/// The above copyright notice and this permission notice (including the next
+/// paragraph) shall be included in all copies or substantial portions of the
+/// Software.
+///
+/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+/// DEALINGS IN THE SOFTWARE.
+/// </pre>
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct wl_output {
     _unused: [u8; 0],
 }
-# [ repr ( C ) ]
-# [ derive ( Debug , Copy , Clone ) ]
+#[repr(C)]
+pub struct dwl_context {
+    pub _bindgen_opaque_blob: [u64; 52usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
 pub struct dwl_dmabuf {
-    _unused: [u8; 0],
+    pub _bindgen_opaque_blob: [u64; 3usize],
 }
-# [ repr ( C ) ]
-# [ derive ( Debug , Copy , Clone ) ]
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
 pub struct dwl_surface {
-    _unused: [u8; 0],
+    pub _bindgen_opaque_blob: [u64; 12usize],
 }
 extern "C" {
     pub fn dwl_context_new() -> *mut dwl_context;
@@ -22,7 +61,9 @@ extern "C" {
     pub fn dwl_context_destroy(self_: *mut *mut dwl_context);
 }
 extern "C" {
-    pub fn dwl_context_setup(self_: *mut dwl_context) -> bool;
+    pub fn dwl_context_setup(self_: *mut dwl_context,
+                             socket_path: *const ::std::os::raw::c_char)
+                             -> bool;
 }
 extern "C" {
     pub fn dwl_context_fd(self_: *mut dwl_context) -> ::std::os::raw::c_int;
@@ -42,9 +83,6 @@ extern "C" {
                                   -> *mut dwl_dmabuf;
 }
 extern "C" {
-    pub fn dwl_dmabuf_in_use(self_: *mut dwl_dmabuf) -> bool;
-}
-extern "C" {
     pub fn dwl_dmabuf_destroy(self_: *mut *mut dwl_dmabuf);
 }
 extern "C" {
@@ -78,4 +116,4 @@ extern "C" {
 }
 extern "C" {
     pub fn dwl_surface_set_position(self_: *mut dwl_surface, x: u32, y: u32);
-}
\ No newline at end of file
+}
diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs
index 7f2b775..7ff116a 100644
--- a/gpu_display/src/lib.rs
+++ b/gpu_display/src/lib.rs
@@ -11,8 +11,9 @@ mod dwl;
 
 use std::cell::Cell;
 use std::collections::HashMap;
-use std::ffi::CStr;
+use std::ffi::{CStr, CString};
 use std::os::unix::io::{AsRawFd, RawFd};
+use std::path::Path;
 use std::ptr::null_mut;
 
 use data_model::{VolatileSlice, VolatileMemory};
@@ -40,6 +41,8 @@ pub enum GpuDisplayError {
     FailedImport,
     /// The surface ID is invalid.
     InvalidSurfaceId,
+    /// The path is invalid.
+    InvalidPath,
 }
 
 struct DwlContext(*mut dwl_context);
@@ -108,15 +111,23 @@ pub struct GpuDisplay {
 
 impl GpuDisplay {
     /// Opens a fresh connection to the compositor.
-    pub fn new() -> Result<GpuDisplay, GpuDisplayError> {
+    pub fn new<P: AsRef<Path>>(wayland_path: P) -> Result<GpuDisplay, GpuDisplayError> {
         // The dwl_context_new call should always be safe to call, and we check its result.
         let ctx = DwlContext(unsafe { dwl_context_new() });
         if ctx.0.is_null() {
             return Err(GpuDisplayError::Allocate);
         }
+
         // The dwl_context_setup call is always safe to call given that the supplied context is
         // valid. and we check its result.
-        let setup_success = unsafe { dwl_context_setup(ctx.0) };
+        let cstr_path = match wayland_path.as_ref().as_os_str().to_str() {
+            Some(str) => match CString::new(str) {
+                Ok(cstr) => cstr,
+                Err(_) => return Err(GpuDisplayError::InvalidPath),
+            },
+            None => return Err(GpuDisplayError::InvalidPath),
+        };
+        let setup_success = unsafe { dwl_context_setup(ctx.0, cstr_path.as_ptr() ) };
         if !setup_success {
             return Err(GpuDisplayError::Connect);
         }
@@ -171,17 +182,6 @@ impl GpuDisplay {
         Ok(next_id)
     }
 
-    pub fn import_in_use(&mut self, import_id: u32) -> bool {
-        match self.dmabufs.get(&import_id) {
-            // Safe because only a valid dmabuf is used.
-            Some(dmabuf) => unsafe { dwl_dmabuf_in_use(dmabuf.0) },
-            None => {
-                debug_assert!(false, "invalid import_id {}", import_id);
-                false
-            }
-        }
-    }
-
     /// Releases a previously imported dmabuf identified by the given handle.
     pub fn release_import(&mut self, import_id: u32) {
         self.dmabufs.remove(&import_id);
diff --git a/seccomp/x86_64/gpu_device.policy b/seccomp/x86_64/gpu_device.policy
new file mode 100644
index 0000000..de16d39
--- /dev/null
+++ b/seccomp/x86_64/gpu_device.policy
@@ -0,0 +1,57 @@
+# Copyright 2018 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.
+
+open: 1
+close: 1
+sigaltstack: 1
+munmap: 1
+write: 1
+# Allow mmap to allow loading of GL shared libraries.
+mmap: arg2 == PROT_READ|PROT_WRITE || arg2 == PROT_NONE || arg2 == PROT_READ|PROT_EXEC
+restart_syscall: 1
+exit_group: 1
+rt_sigreturn: 1
+# Allow MADV_DONTDUMP only.
+madvise: arg2 == MADV_DONTDUMP || arg2 == MADV_DONTNEED
+# Used to determine shm size after recvmsg with fd.
+lseek: 1
+mprotect: arg2 == PROT_READ|PROT_WRITE || arg2 == PROT_NONE || arg2 == PROT_READ
+sched_getaffinity: 1
+set_robust_list: 1
+exit: 1
+getpid: 1
+recvfrom: 1
+dup: 1
+eventfd2: 1
+futex: 1
+# Disallow clone's other than new threads.
+# arg0 is flags. Because kernel.
+clone: arg0 & 0x00010000
+# arg1 == FIONBIO || arg1 == FIOCLEX || arg1 == DMA_BUF_IOCTL_SYNC ||
+#   arg1 & DRM_IOCTL
+ioctl: arg1 == FIONBIO || arg1 == FIOCLEX || arg1 == 0x40086200 || arg1 & 0x6400
+fstat: 1
+# Used to communicate with wayland.
+recvmsg: 1
+sendmsg: 1
+poll: 1
+getrandom: 1
+read: 1
+geteuid: 1
+getuid: 1
+readlink: 1
+getdents: 1
+stat: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_wait: 1
+# Used to connect to wayland.
+# arg0 == AF_UNIX && arg1 == SOCK_STREAM|SOCK_CLOEXEC
+socket: arg0 == 1 && arg1 == 0x80001 && arg2 == 0
+connect: 1
+# Used for sharing memory with wayland. arg1 == MFD_CLOEXEC|MFD_ALLOW_SEALING
+memfd_create: arg1 == 3
+# Used to set of size new memfd.
+ftruncate: 1
+fcntl: arg1 == F_DUPFD_CLOEXEC
diff --git a/src/linux.rs b/src/linux.rs
index a858129..a142d1d 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -441,17 +441,85 @@ fn create_virtio_devs(cfg: VirtIoDeviceInfo,
     #[cfg(feature = "gpu")]
     {
         if cfg.gpu {
-            let gpu_box =
-                Box::new(devices::virtio::Gpu::new(_exit_evt
-                                                       .try_clone()
-                                                       .map_err(Error::CloneEventFd)?));
-            let jail = if cfg.multiprocess {
-                error!("jail for virtio-gpu is unimplemented");
-                unimplemented!();
-            } else {
-                None
-            };
-            devs.push(VirtioDeviceStub {dev: gpu_box, jail});
+            if let Some(wayland_socket_path) = cfg.wayland_socket_path.as_ref() {
+                let jailed_wayland_path = Path::new("/wayland-0");
+
+                let gpu_box =
+                    Box::new(devices::virtio::Gpu::new(_exit_evt
+                                                        .try_clone()
+                                                        .map_err(Error::CloneEventFd)?,
+                                                    if cfg.multiprocess {
+                                                        &jailed_wayland_path
+                                                    } else {
+                                                        wayland_socket_path.as_path()
+                                                    }));
+
+                let jail = if cfg.multiprocess {
+                    let policy_path: PathBuf = cfg.seccomp_policy_dir.join("gpu_device.policy");
+                    let mut jail = create_base_minijail(empty_root_path, &policy_path)?;
+
+                    // Create a tmpfs in the device's root directory so that we can bind mount the
+                    // dri directory into it.  The size=67108864 is size=64*1024*1024 or size=64MB.
+                    jail.mount_with_data(Path::new("none"), Path::new("/"), "tmpfs",
+                                         (libc::MS_NOSUID | libc::MS_NODEV |
+                                          libc::MS_NOEXEC) as usize,
+                                         "size=67108864")
+                        .unwrap();
+
+                    // Device nodes required for DRM.
+                    let sys_dev_char_path = Path::new("/sys/dev/char");
+                    jail.mount_bind(sys_dev_char_path, sys_dev_char_path, false)
+                        .unwrap();
+                    let sys_devices_path = Path::new("/sys/devices");
+                    jail.mount_bind(sys_devices_path, sys_devices_path, false)
+                        .unwrap();
+                    let drm_dri_path = Path::new("/dev/dri");
+                    jail.mount_bind(drm_dri_path, drm_dri_path, false)
+                        .unwrap();
+
+                    // Libraries that are required when mesa drivers are dynamically loaded.
+                    let lib_path = Path::new("/lib64");
+                    jail.mount_bind(lib_path, lib_path, false)
+                        .unwrap();
+                    let usr_lib_path = Path::new("/usr/lib64");
+                    jail.mount_bind(usr_lib_path, usr_lib_path, false)
+                        .unwrap();
+
+                    // Bind mount the wayland socket into jail's root. This is necessary since each
+                    // new wayland context must open() the socket.
+                    jail.mount_bind(wayland_socket_path.as_path(), jailed_wayland_path, true)
+                        .unwrap();
+
+                    // Set the uid/gid for the jailed process, and give a basic id map. This
+                    // is required for the above bind mount to work.
+                    let crosvm_user_group = CStr::from_bytes_with_nul(b"crosvm\0").unwrap();
+                    let crosvm_uid = match get_user_id(&crosvm_user_group) {
+                        Ok(u) => u,
+                        Err(e) => {
+                            warn!("falling back to current user id for gpu: {:?}", e);
+                            geteuid()
+                        }
+                    };
+                    let crosvm_gid = match get_group_id(&crosvm_user_group) {
+                        Ok(u) => u,
+                        Err(e) => {
+                            warn!("falling back to current group id for gpu: {:?}", e);
+                            getegid()
+                        }
+                    };
+                    jail.change_uid(crosvm_uid);
+                    jail.change_gid(crosvm_gid);
+                    jail.uidmap(&format!("{0} {0} 1", crosvm_uid))
+                        .map_err(Error::SettingUidMap)?;
+                    jail.gidmap(&format!("{0} {0} 1", crosvm_gid))
+                        .map_err(Error::SettingGidMap)?;
+
+                    Some(jail)
+                } else {
+                    None
+                };
+                devs.push(VirtioDeviceStub {dev: gpu_box, jail});
+            }
         }
     }