/* * \brief Handler for ioctl operations on DRM device * \author Norman Feske * \date 2010-07-13 */ /* * Copyright (C) 2010-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ /* Genode includes */ #include #include #include /* libc includes */ #include /* libc plugin interface */ #include #include /* libdrm includes */ extern "C" { #define virtual _virtual #include #include #undef virtual } /* GPU driver interface */ #include enum { verbose_ioctl = false }; long drm_command(long request) { return (request & 0xff) - DRM_COMMAND_BASE; } /** * Return name of DRM command */ const char *command_name(long request) { if (IOCGROUP(request) != DRM_IOCTL_BASE) return ""; switch (drm_command(request)) { case DRM_I915_INIT: return "DRM_I915_INIT"; case DRM_I915_FLUSH: return "DRM_I915_FLUSH"; case DRM_I915_FLIP: return "DRM_I915_FLIP"; case DRM_I915_BATCHBUFFER: return "DRM_I915_BATCHBUFFER"; case DRM_I915_IRQ_EMIT: return "DRM_I915_IRQ_EMIT"; case DRM_I915_IRQ_WAIT: return "DRM_I915_IRQ_WAIT"; case DRM_I915_GETPARAM: return "DRM_I915_GETPARAM"; case DRM_I915_SETPARAM: return "DRM_I915_SETPARAM"; case DRM_I915_ALLOC: return "DRM_I915_ALLOC"; case DRM_I915_FREE: return "DRM_I915_FREE"; case DRM_I915_INIT_HEAP: return "DRM_I915_INIT_HEAP"; case DRM_I915_CMDBUFFER: return "DRM_I915_CMDBUFFER"; case DRM_I915_DESTROY_HEAP: return "DRM_I915_DESTROY_HEAP"; case DRM_I915_SET_VBLANK_PIPE: return "DRM_I915_SET_VBLANK_PIPE"; case DRM_I915_GET_VBLANK_PIPE: return "DRM_I915_GET_VBLANK_PIPE"; case DRM_I915_VBLANK_SWAP: return "DRM_I915_VBLANK_SWAP"; case DRM_I915_HWS_ADDR: return "DRM_I915_HWS_ADDR"; case DRM_I915_GEM_INIT: return "DRM_I915_GEM_INIT"; case DRM_I915_GEM_EXECBUFFER: return "DRM_I915_GEM_EXECBUFFER"; case DRM_I915_GEM_PIN: return "DRM_I915_GEM_PIN"; case DRM_I915_GEM_UNPIN: return "DRM_I915_GEM_UNPIN"; case DRM_I915_GEM_BUSY: return "DRM_I915_GEM_BUSY"; case DRM_I915_GEM_THROTTLE: return "DRM_I915_GEM_THROTTLE"; case DRM_I915_GEM_ENTERVT: return "DRM_I915_GEM_ENTERVT"; case DRM_I915_GEM_LEAVEVT: return "DRM_I915_GEM_LEAVEVT"; case DRM_I915_GEM_CREATE: return "DRM_I915_GEM_CREATE"; case DRM_I915_GEM_PREAD: return "DRM_I915_GEM_PREAD"; case DRM_I915_GEM_PWRITE: return "DRM_I915_GEM_PWRITE"; case DRM_I915_GEM_MMAP: return "DRM_I915_GEM_MMAP"; case DRM_I915_GEM_SET_DOMAIN: return "DRM_I915_GEM_SET_DOMAIN"; case DRM_I915_GEM_SW_FINISH: return "DRM_I915_GEM_SW_FINISH"; case DRM_I915_GEM_SET_TILING: return "DRM_I915_GEM_SET_TILING"; case DRM_I915_GEM_GET_TILING: return "DRM_I915_GEM_GET_TILING"; case DRM_I915_GEM_GET_APERTURE: return "DRM_I915_GEM_GET_APERTURE"; case DRM_I915_GEM_MMAP_GTT: return "DRM_I915_GEM_MMAP_GTT"; case DRM_I915_GET_PIPE_FROM_CRTC_ID: return "DRM_I915_GET_PIPE_FROM_CRTC_ID"; case DRM_I915_GEM_MADVISE: return "DRM_I915_GEM_MADVISE"; case DRM_I915_OVERLAY_PUT_IMAGE: return "DRM_I915_OVERLAY_PUT_IMAGE"; case DRM_I915_OVERLAY_ATTRS: return "DRM_I915_OVERLAY_ATTRS"; case DRM_I915_GEM_EXECBUFFER2: return "DRM_I915_GEM_EXECBUFFER2"; default: return ""; } } static void dump_ioctl(long request) { PDBG("ioctl(request=%lx, %s, len=%ld, cmd=%s)", request, (request & 0xe0000000) == IOC_OUT ? "out" : (request & 0xe0000000) == IOC_IN ? "in" : (request & 0xe0000000) == IOC_INOUT ? "inout" : "void", IOCPARM_LEN(request), command_name(request)); } namespace { struct Plugin_context : Libc::Plugin_context { }; class Plugin : public Libc::Plugin { private: Gpu_driver *_driver; Gpu_driver::Client *_client; /* * Assign a priority of 1 to override libc_vfs. */ enum { PLUGIN_PRIORITY = 1 }; public: Plugin(Gpu_driver *driver) : Libc::Plugin(PLUGIN_PRIORITY), _driver(driver), _client(0) { if (!_driver) { PERR("could not initialize GPU driver"); return; } _client = _driver->create_client(); } bool supports_open(const char *pathname, int flags) { return !Genode::strcmp(pathname, "/dev/drm"); } Libc::File_descriptor *open(const char *pathname, int flags) { Plugin_context *context = new (Genode::env()->heap()) Plugin_context(); return Libc::file_descriptor_allocator()->alloc(this, context); } bool supports_stat(const char *path) { return (Genode::strcmp(path, "/dev") == 0 || Genode::strcmp(path, "/dev/drm") == 0); } int stat(const char *path, struct stat *buf) { if (buf) buf->st_mode = S_IFDIR; return 0; } int ioctl(Libc::File_descriptor *fd, int request, char *argp) { if (verbose_ioctl) dump_ioctl(request); if (drm_command(request) == DRM_I915_GEM_MMAP_GTT) { drm_i915_gem_mmap_gtt *arg = (drm_i915_gem_mmap_gtt *)(argp); arg->offset = (__u64)_driver->map_buffer_object(_client, arg->handle); return arg->offset ? 0 : -1; } return _driver->ioctl(_client, drm_command(request), argp); } bool supports_mmap() { return true; } /** * Pseudo mmap specific for DRM device * * The original protocol between the Gallium driver and the kernel * DRM driver is based on the interplay of the GEM_MMAP_GTT ioctl and * mmap. First, GEM_MMAP_GTT is called with a buffer object as * argument. The DRM driver returns a global ID called "fake offset" * representing the buffer object. The fake offset actually refers * to a region within the virtual 'dev->mm_private' memory map. * The Gallium driver then passes this fake offset back to the DRM * driver as 'offset' argument to 'mmap'. The DRM driver uses this * argument to look up the GEM offset and set up a vm_area specific * for the buffer object - paged by the DRM driver. * * We avoid this round-trip of a global ID. Instead of calling the * 'GEM_MMAP_GTT' ioctl, we use a dedicated driver function called * 'map_buffer_object', which simply returns the local address of * the already mapped buffer object via the 'offset' return value. * Hence, all 'mmap' has to do is passing back the 'offset' argument * as return value. */ void *mmap(void *addr, ::size_t length, int prot, int flags, Libc::File_descriptor *fd, ::off_t offset) { PDBG("\naddr=%p, length=%zd, prot=%x, flags=%x, offset=0x%lx", addr, length, prot, flags, (long)offset); return (void *)offset; } int close(Libc::File_descriptor *fd) { Genode::destroy(Genode::env()->heap(), static_cast(fd->context)); Libc::file_descriptor_allocator()->free(fd); return 0; } }; } void __attribute__((constructor)) init_drm_device_plugin() { static Plugin plugin(gpu_driver()); }