[igt-dev] [PATCH i-g-t 4/6] igt/shell: Primitive drm device/drivers builtin

Chris Wilson chris at chris-wilson.co.uk
Mon Aug 20 13:01:47 UTC 2018


The drm module (builtin) provides access to devices (fd) and their
associated drivers. By itself, it provides the common KMS/GEM
abstractions and ioctls.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 shell/drm/drm-constants.c   | 173 +++++++++++++++
 shell/drm/drm-constants.h   |   6 +
 shell/drm/drm-device.c      | 417 ++++++++++++++++++++++++++++++++++++
 shell/drm/drm-device.h      |   9 +
 shell/drm/drm-gem.c         |  23 ++
 shell/drm/drm-kmod.c        | 125 +++++++++++
 shell/drm/drm-kmod.h        |   9 +
 shell/drm/drm-module.c      | 130 +++++++++++
 shell/examples/drm/auth.js  |  62 ++++++
 shell/include/drm-builtin.h |  14 ++
 shell/include/drm-gem.h     |   8 +
 shell/meson.build           |   6 +
 12 files changed, 982 insertions(+)
 create mode 100644 shell/drm/drm-constants.c
 create mode 100644 shell/drm/drm-constants.h
 create mode 100644 shell/drm/drm-device.c
 create mode 100644 shell/drm/drm-device.h
 create mode 100644 shell/drm/drm-gem.c
 create mode 100644 shell/drm/drm-kmod.c
 create mode 100644 shell/drm/drm-kmod.h
 create mode 100644 shell/drm/drm-module.c
 create mode 100644 shell/examples/drm/auth.js
 create mode 100644 shell/include/drm-builtin.h
 create mode 100644 shell/include/drm-gem.h

diff --git a/shell/drm/drm-constants.c b/shell/drm/drm-constants.c
new file mode 100644
index 000000000..1590ef876
--- /dev/null
+++ b/shell/drm/drm-constants.c
@@ -0,0 +1,173 @@
+#include "drm-uapi/drm.h"
+#include "duktape.h"
+
+#include "drm-constants.h"
+
+#define C(x) { #x, x }
+
+static const duk_number_list_entry drm_ioctls[] = {
+	C(DRM_IOCTL_VERSION),
+	C(DRM_IOCTL_GET_UNIQUE),
+	C(DRM_IOCTL_GET_MAGIC),
+	C(DRM_IOCTL_IRQ_BUSID),
+	C(DRM_IOCTL_GET_MAP),
+	C(DRM_IOCTL_GET_CLIENT),
+	C(DRM_IOCTL_GET_STATS),
+	C(DRM_IOCTL_SET_VERSION),
+	C(DRM_IOCTL_MODESET_CTL),
+	C(DRM_IOCTL_GEM_CLOSE),
+	C(DRM_IOCTL_GEM_FLINK),
+	C(DRM_IOCTL_GEM_OPEN),
+	C(DRM_IOCTL_GET_CAP),
+	C(DRM_IOCTL_SET_CLIENT_CAP),
+
+	C(DRM_IOCTL_SET_UNIQUE),
+	C(DRM_IOCTL_AUTH_MAGIC),
+	C(DRM_IOCTL_BLOCK),
+	C(DRM_IOCTL_UNBLOCK),
+	C(DRM_IOCTL_CONTROL),
+	C(DRM_IOCTL_ADD_MAP),
+	C(DRM_IOCTL_ADD_BUFS),
+	C(DRM_IOCTL_MARK_BUFS),
+	C(DRM_IOCTL_INFO_BUFS),
+	C(DRM_IOCTL_MAP_BUFS),
+	C(DRM_IOCTL_FREE_BUFS),
+
+	C(DRM_IOCTL_RM_MAP),
+
+	C(DRM_IOCTL_SET_SAREA_CTX),
+	C(DRM_IOCTL_GET_SAREA_CTX),
+
+	C(DRM_IOCTL_SET_MASTER),
+	C(DRM_IOCTL_DROP_MASTER),
+
+	C(DRM_IOCTL_ADD_CTX),
+	C(DRM_IOCTL_RM_CTX),
+	C(DRM_IOCTL_MOD_CTX),
+	C(DRM_IOCTL_GET_CTX),
+	C(DRM_IOCTL_SWITCH_CTX),
+	C(DRM_IOCTL_NEW_CTX),
+	C(DRM_IOCTL_RES_CTX),
+	C(DRM_IOCTL_ADD_DRAW),
+	C(DRM_IOCTL_RM_DRAW),
+	C(DRM_IOCTL_DMA),
+	C(DRM_IOCTL_LOCK),
+	C(DRM_IOCTL_UNLOCK),
+	C(DRM_IOCTL_FINISH),
+
+	C(DRM_IOCTL_PRIME_HANDLE_TO_FD),
+	C(DRM_IOCTL_PRIME_FD_TO_HANDLE),
+
+	C(DRM_IOCTL_AGP_ACQUIRE),
+	C(DRM_IOCTL_AGP_RELEASE),
+	C(DRM_IOCTL_AGP_ENABLE),
+	C(DRM_IOCTL_AGP_INFO),
+	C(DRM_IOCTL_AGP_ALLOC),
+	C(DRM_IOCTL_AGP_FREE),
+	C(DRM_IOCTL_AGP_BIND),
+	C(DRM_IOCTL_AGP_UNBIND),
+
+	C(DRM_IOCTL_SG_ALLOC),
+	C(DRM_IOCTL_SG_FREE),
+
+	C(DRM_IOCTL_WAIT_VBLANK),
+
+	C(DRM_IOCTL_CRTC_GET_SEQUENCE),
+	C(DRM_IOCTL_CRTC_QUEUE_SEQUENCE),
+
+	C(DRM_IOCTL_UPDATE_DRAW),
+
+	C(DRM_IOCTL_MODE_GETRESOURCES),
+	C(DRM_IOCTL_MODE_GETCRTC),
+	C(DRM_IOCTL_MODE_SETCRTC),
+	C(DRM_IOCTL_MODE_CURSOR),
+	C(DRM_IOCTL_MODE_GETGAMMA),
+	C(DRM_IOCTL_MODE_SETGAMMA),
+	C(DRM_IOCTL_MODE_GETENCODER),
+	C(DRM_IOCTL_MODE_GETCONNECTOR),
+	C(DRM_IOCTL_MODE_ATTACHMODE),
+	C(DRM_IOCTL_MODE_DETACHMODE),
+
+	C(DRM_IOCTL_MODE_GETPROPERTY),
+	C(DRM_IOCTL_MODE_SETPROPERTY),
+	C(DRM_IOCTL_MODE_GETPROPBLOB),
+	C(DRM_IOCTL_MODE_GETFB),
+	C(DRM_IOCTL_MODE_ADDFB),
+	C(DRM_IOCTL_MODE_RMFB),
+	C(DRM_IOCTL_MODE_PAGE_FLIP),
+	C(DRM_IOCTL_MODE_DIRTYFB),
+
+	C(DRM_IOCTL_MODE_CREATE_DUMB),
+	C(DRM_IOCTL_MODE_MAP_DUMB),
+	C(DRM_IOCTL_MODE_DESTROY_DUMB),
+	C(DRM_IOCTL_MODE_GETPLANERESOURCES),
+	C(DRM_IOCTL_MODE_GETPLANE),
+	C(DRM_IOCTL_MODE_SETPLANE),
+	C(DRM_IOCTL_MODE_ADDFB2),
+	C(DRM_IOCTL_MODE_OBJ_GETPROPERTIES),
+	C(DRM_IOCTL_MODE_OBJ_SETPROPERTY),
+	C(DRM_IOCTL_MODE_CURSOR2),
+	C(DRM_IOCTL_MODE_ATOMIC),
+	C(DRM_IOCTL_MODE_CREATEPROPBLOB),
+	C(DRM_IOCTL_MODE_DESTROYPROPBLOB),
+
+	C(DRM_IOCTL_SYNCOBJ_CREATE),
+	C(DRM_IOCTL_SYNCOBJ_DESTROY),
+	C(DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD),
+	C(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE),
+	C(DRM_IOCTL_SYNCOBJ_WAIT),
+	C(DRM_IOCTL_SYNCOBJ_RESET),
+	C(DRM_IOCTL_SYNCOBJ_SIGNAL),
+
+	C(DRM_IOCTL_MODE_CREATE_LEASE),
+	C(DRM_IOCTL_MODE_LIST_LESSEES),
+	C(DRM_IOCTL_MODE_GET_LEASE),
+	C(DRM_IOCTL_MODE_REVOKE_LEASE),
+
+	{ },
+};
+
+static const duk_number_list_entry drm_caps[] = {
+	C(DRM_CAP_DUMB_BUFFER),
+	C(DRM_CAP_VBLANK_HIGH_CRTC),
+	C(DRM_CAP_DUMB_PREFERRED_DEPTH),
+	C(DRM_CAP_DUMB_PREFER_SHADOW),
+	C(DRM_CAP_PRIME),
+	C(DRM_CAP_TIMESTAMP_MONOTONIC),
+	C(DRM_CAP_ASYNC_PAGE_FLIP),
+	C(DRM_CAP_CURSOR_WIDTH),
+	C(DRM_CAP_CURSOR_HEIGHT),
+	C(DRM_CAP_ADDFB2_MODIFIERS),
+	C(DRM_CAP_PAGE_FLIP_TARGET),
+	C(DRM_CAP_CRTC_IN_VBLANK_EVENT),
+	C(DRM_CAP_SYNCOBJ),
+
+	{ },
+};
+
+static const duk_number_list_entry drm_clientcaps[] = {
+	C(DRM_CLIENT_CAP_STEREO_3D),
+	C(DRM_CLIENT_CAP_UNIVERSAL_PLANES),
+	C(DRM_CLIENT_CAP_ATOMIC),
+	//C(DRM_CLIENT_CAP_ASPECT_RATIO),
+	//C(DRM_CLIENT_CAP_WRITEBACK_CONNECTORS),
+
+	{ },
+};
+
+void drm_constants(duk_context *ctx)
+{
+	duk_push_object(ctx);
+	duk_put_number_list(ctx, -1, drm_ioctls);
+	duk_put_prop_string(ctx, -2, "ioctl");
+
+	duk_push_object(ctx);
+	duk_put_number_list(ctx, -1, drm_caps);
+	duk_put_prop_string(ctx, -2, "cap");
+
+	duk_push_object(ctx);
+	duk_put_number_list(ctx, -1, drm_clientcaps);
+	duk_put_prop_string(ctx, -2, "clientcap");
+}
+
+#undef C
diff --git a/shell/drm/drm-constants.h b/shell/drm/drm-constants.h
new file mode 100644
index 000000000..0b046244a
--- /dev/null
+++ b/shell/drm/drm-constants.h
@@ -0,0 +1,6 @@
+#ifndef DRM_CONSTANTS_H
+#define DRM_CONSTANTS_H
+
+void drm_constants(duk_context *ctx);
+
+#endif /* DRM_CONSTANTS_H */
diff --git a/shell/drm/drm-device.c b/shell/drm/drm-device.c
new file mode 100644
index 000000000..e511526e3
--- /dev/null
+++ b/shell/drm/drm-device.c
@@ -0,0 +1,417 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include "drm-uapi/drm.h"
+#include "duktape.h"
+#include "igt-builtins.h"
+#include "os-builtin.h"
+
+#include "drm-device.h"
+#include "drm-kmod.h"
+
+static duk_ret_t drm_device(duk_context *ctx, int fd);
+
+static duk_ret_t drm_ioctl(duk_context *ctx)
+{
+	unsigned long cmd = duk_require_uint(ctx, -2);
+	duk_size_t sz;
+	int err, fd;
+	void *ptr;
+
+	ptr = duk_get_buffer_data(ctx, -1, &sz);
+	if (sz > _IOC_SIZEMASK)
+		return -1;
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, "fd");
+	fd = duk_get_int(ctx, -1);
+
+	cmd &= ~(_IOC_SIZEMASK << _IOC_SIZESHIFT);
+	cmd |= sz << _IOC_SIZESHIFT;
+
+	do {
+		err = ioctl(fd, cmd, ptr);
+	} while (err == -1 && (errno == EAGAIN || errno == EINTR));
+
+	if (err == -1)
+		os_throw_errno(ctx, -errno);
+
+	return 0;
+}
+
+static duk_ret_t drm_expect_ioctl(duk_context *ctx)
+{
+	unsigned long cmd = duk_require_uint(ctx, -3);
+	int expect = duk_require_int(ctx, -1);
+	duk_size_t sz;
+	int err, fd;
+	void *ptr;
+
+	ptr = duk_get_buffer_data(ctx, -2, &sz);
+	if (sz > _IOC_SIZEMASK)
+		return -1;
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, "fd");
+	fd = duk_get_int(ctx, -1);
+
+	cmd &= ~(_IOC_SIZEMASK << _IOC_SIZESHIFT);
+	cmd |= sz << _IOC_SIZESHIFT;
+
+	err = 0;
+	if (ioctl(fd, cmd, ptr))
+		err = -errno;
+	if (err != expect)
+		os_throw_errno(ctx, err);
+
+	return 0;
+}
+
+static int find_device(duk_context *ctx, int fd)
+{
+	struct stat tmpl, st;
+	char path[64];
+	int idx;
+
+	if (fstat(fd, &tmpl) < 0)
+		os_throw_errno(ctx, -errno);
+
+	idx = tmpl.st_rdev & 0xff;
+	if (idx & 0x80)
+		snprintf(path, sizeof(path), "/dev/dri/renderD%d", idx);
+	else if (idx & 0x40)
+		snprintf(path, sizeof(path), "/dev/dri/controlD%d", idx);
+	else
+		snprintf(path, sizeof(path), "/dev/dri/card%d", idx);
+
+	fd = open(path, O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+		os_throw_errno(ctx, -errno);
+
+	if (fstat(fd, &st) < 0) {
+		int err = -errno;
+
+		close(fd);
+		os_throw_errno(ctx, err);
+	}
+
+	if (st.st_rdev != tmpl.st_rdev) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static duk_ret_t drm_reopen(duk_context *ctx)
+{
+	char path[256];
+	int fd;
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, "fd");
+	fd = duk_get_int(ctx, -1);
+
+        snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
+        fd = open(path, O_RDWR);
+	if (fd < 0)
+		fd = find_device(ctx, duk_get_int(ctx, -1));
+
+	return drm_device(ctx, fd);
+}
+
+static duk_ret_t drm_SetMaster(duk_context *ctx)
+{
+	int fd;
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, "fd");
+	fd = duk_get_int(ctx, -1);
+
+	if (ioctl(fd, DRM_IOCTL_SET_MASTER) < 0)
+		os_throw_errno(ctx, -errno);
+
+	return 0;
+}
+
+static duk_ret_t drm_DropMaster(duk_context *ctx)
+{
+	int fd;
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, "fd");
+	fd = duk_get_int(ctx, -1);
+
+	if (ioctl(fd, DRM_IOCTL_SET_MASTER) < 0)
+		os_throw_errno(ctx, -errno);
+
+	return 0;
+}
+
+static duk_ret_t drm_GetMagic(duk_context *ctx)
+{
+	drm_magic_t magic;
+	int fd;
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, "fd");
+	fd = duk_get_int(ctx, -1);
+
+	if (ioctl(fd, DRM_IOCTL_GET_MAGIC, &magic) < 0)
+		os_throw_errno(ctx, -errno);
+
+	duk_push_uint(ctx, magic);
+	return 1;
+}
+
+static duk_ret_t drm_AuthMagic(duk_context *ctx)
+{
+	drm_magic_t magic = duk_get_uint(ctx, -1);
+	int fd;
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, "fd");
+	fd = duk_get_int(ctx, -1);
+
+	if (ioctl(fd, DRM_IOCTL_AUTH_MAGIC, &magic) < 0)
+		os_throw_errno(ctx, -errno);
+
+	return 0;
+}
+
+static const duk_function_list_entry funcs[] = {
+	{ "reopen", drm_reopen, 0 },
+
+	{ "ioctl", drm_ioctl, 2 },
+	{ "expect_ioctl", drm_expect_ioctl, 3 },
+
+	{ "SetMaster", drm_SetMaster, 0 },
+	{ "DropMaster", drm_DropMaster, 0 },
+
+	{ "GetMagic", drm_GetMagic, 0 },
+	{ "AuthMagic", drm_AuthMagic, 1 },
+
+	{}
+};
+
+static duk_ret_t drm_device_close(duk_context *ctx)
+{
+	int fd;
+
+	duk_get_prop_string(ctx, 0, "fd");
+	fd = duk_get_int(ctx, -1);
+	close(fd);
+
+	return 0;
+}
+
+static duk_ret_t drm_device(duk_context *ctx, int fd)
+{
+	char name[16] = {};
+	drm_version_t version = {
+		.name_len = sizeof(name),
+		.name = name,
+	};
+
+	if (ioctl(fd, DRM_IOCTL_VERSION, &version) < 0)
+		return -1;
+
+	duk_push_object(ctx);
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, "drivers");
+	if (duk_get_prop_string(ctx, -1, name)) {
+		duk_dup_top(ctx);
+		duk_set_prototype(ctx, -5);
+		duk_put_prop_string(ctx, -4, "driver");
+		duk_pop(ctx);
+	} else {
+		duk_pop(ctx);
+		//duk_dup_top(ctx);
+		//duk_set_prototype(ctx, -2);
+		duk_put_prop_string(ctx, -1, "driver");
+	}
+	duk_pop(ctx);
+
+	duk_push_int(ctx, fd);
+	duk_put_prop_string(ctx, -2, "fd");
+
+	duk_put_function_list(ctx, -1, funcs);
+
+	duk_push_c_function(ctx, drm_device_close, 1);
+	duk_set_finalizer(ctx, -2);
+
+	return 1;
+}
+
+duk_ret_t drm_device_cards(duk_context *ctx)
+{
+	duk_uarridx_t idx;
+	const char *filter = NULL;
+	int base;
+
+	duk_push_this(ctx);
+	if (duk_get_prop_string(ctx, -1, "name"))
+		filter = duk_get_string(ctx, -1);
+
+	if (filter)
+		drm_kmod_load(filter, NULL);
+
+	duk_push_array(ctx);
+
+	base = open("/dev/dri/", O_RDONLY);
+	if (base < 0)
+		return -1;
+
+	idx = 0;
+	for (int i = 0; i < 16; i++) {
+		char buf[32];
+		int fd;
+
+		snprintf(buf, sizeof(buf), "card%d", i);
+		fd = openat(base, buf, O_RDWR | O_CLOEXEC);
+		if (fd < 0)
+			break;
+
+		if (filter) {
+			char name[16] = {};
+			drm_version_t version = {
+				.name_len = sizeof(name),
+				.name = name,
+			};
+
+			if (ioctl(fd, DRM_IOCTL_VERSION, &version) < 0) {
+				close(fd);
+				return -1;
+			}
+
+			if (strcmp(filter, version.name)) {
+				close(fd);
+				continue;
+			}
+		}
+
+		if (drm_device(ctx, fd) < 0) {
+			close(fd);
+			close(base);
+			return -1;
+		}
+
+		duk_put_prop_index(ctx, -2, idx++);
+	}
+
+	close(base);
+	return 1;
+}
+
+duk_ret_t drm_device_card(duk_context *ctx)
+{
+	duk_int_t idx = duk_opt_int(ctx, -1, -1);
+	const char *filter = NULL;
+	char buf[32];
+	int fd, x;
+
+	duk_push_this(ctx);
+	if (duk_get_prop_string(ctx, -1, "name"))
+		filter = duk_get_string(ctx, -1);
+
+	if (filter)
+		drm_kmod_load(filter, NULL);
+
+	for (x = 0; x < 16; x++) {
+		snprintf(buf, sizeof(buf), "/dev/dri/card%d", x);
+		fd = open(buf, O_RDWR | O_CLOEXEC);
+		if (fd < 0)
+			return -1;
+
+		if (filter) {
+			char name[16] = {};
+			drm_version_t version = {
+				.name_len = sizeof(name),
+				.name = name,
+			};
+
+			if (ioctl(fd, DRM_IOCTL_VERSION, &version) < 0) {
+				close(fd);
+				return -1;
+			}
+
+			if (strcmp(filter, version.name)) {
+				close(fd);
+				continue;
+			}
+		}
+
+		if (idx != -1 && idx--) {
+			close(fd);
+			continue;
+		}
+
+		if (drm_device(ctx, fd) < 0) {
+			close(fd);
+			return -1;
+		}
+
+		break;
+	}
+
+	return 1;
+}
+
+duk_ret_t drm_device_render(duk_context *ctx)
+{
+	duk_uint_t idx = duk_opt_int(ctx, -1, -1);
+	const char *filter = NULL;
+	char buf[32];
+	int fd, x;
+
+	duk_push_this(ctx);
+	if (duk_get_prop_string(ctx, -1, "name"))
+		filter = duk_get_string(ctx, -1);
+
+	if (filter)
+		drm_kmod_load(filter, NULL);
+
+	for (x = 0; x < 16; x++) {
+		snprintf(buf, sizeof(buf), "/dev/dri/renderD%d", x + 128);
+		fd = open(buf, O_RDWR | O_CLOEXEC);
+		if (fd < 0)
+			return -1;
+
+		if (filter) {
+			char name[16] = {};
+			drm_version_t version = {
+				.name_len = sizeof(name),
+				.name = name,
+			};
+
+			if (ioctl(fd, DRM_IOCTL_VERSION, &version) < 0) {
+				close(fd);
+				return -1;
+			}
+
+			if (strcmp(filter, version.name)) {
+				close(fd);
+				continue;
+			}
+		}
+
+		if (idx != -1 && idx--) {
+			close(fd);
+			continue;
+		}
+
+		if (drm_device(ctx, fd) < 0) {
+			close(fd);
+			return -1;
+		}
+
+		break;
+	}
+
+	return 1;
+}
diff --git a/shell/drm/drm-device.h b/shell/drm/drm-device.h
new file mode 100644
index 000000000..08062ad35
--- /dev/null
+++ b/shell/drm/drm-device.h
@@ -0,0 +1,9 @@
+#ifndef DRM_DEVICE_H
+#define DRM_DEVICE_H
+
+int drm_device_cards(duk_context *ctx);
+
+int drm_device_card(duk_context *ctx);
+int drm_device_render(duk_context *ctx);
+
+#endif /* DRM_DEVICE_H */
diff --git a/shell/drm/drm-gem.c b/shell/drm/drm-gem.c
new file mode 100644
index 000000000..7b4116f42
--- /dev/null
+++ b/shell/drm/drm-gem.c
@@ -0,0 +1,23 @@
+#include <sys/ioctl.h>
+
+#include "duktape.h"
+#include "drm-gem.h"
+
+#include "drm-uapi/drm.h"
+
+duk_ret_t drm_gem_object_dtor(duk_context *ctx)
+{
+	uint32_t handle;
+	int fd;
+
+	duk_get_prop_string(ctx, 0, "handle");
+	handle = duk_get_uint(ctx, -1);
+
+	duk_get_prop_string(ctx, 0, "driver");
+	duk_get_prop_string(ctx, -1, "fd");
+	fd = duk_get_int(ctx, -1);
+
+	ioctl(fd, DRM_IOCTL_GEM_CLOSE, &handle);
+
+	return 0;
+}
diff --git a/shell/drm/drm-kmod.c b/shell/drm/drm-kmod.c
new file mode 100644
index 000000000..19edfd421
--- /dev/null
+++ b/shell/drm/drm-kmod.c
@@ -0,0 +1,125 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libkmod.h>
+
+#include "drm-kmod.h"
+
+static void squelch(void *data, int priority,
+		    const char *file, int line, const char *fn,
+		    const char *format, va_list args)
+{
+}
+
+static struct kmod_ctx *kmod_ctx(void)
+{
+	static struct kmod_ctx *ctx;
+	const char **config_paths = NULL;
+	char *config_paths_str;
+	char *dirname;
+
+	if (ctx)
+		goto out;
+
+	dirname = getenv("IGT_KMOD_DIRNAME");
+	config_paths_str = getenv("IGT_KMOD_CONFIG_PATHS");
+
+	if (config_paths_str) {
+		unsigned count = !!strlen(config_paths_str);
+		unsigned i;
+		char* p;
+
+		p = config_paths_str;
+		while ((p = strchr(p, ':')))
+			p++, count++;
+
+		config_paths = malloc(sizeof(*config_paths) * (count + 1));
+
+		p = config_paths_str;
+		for (i = 0; i < count; ++i) {
+			config_paths[i] = p;
+
+			if ((p = strchr(p, ':')))
+				*p++ = '\0';
+		}
+		config_paths[i] = NULL;
+	}
+
+	ctx = kmod_new(dirname, config_paths);
+	free(config_paths);
+
+	kmod_set_log_fn(ctx, squelch, NULL);
+out:
+	return ctx;
+}
+
+int drm_kmod_is_loaded(const char *mod_name)
+{
+	struct kmod_ctx *ctx = kmod_ctx();
+	struct kmod_list *mod, *list;
+	int ret = 0;
+
+	if (kmod_module_new_from_loaded(ctx, &list) < 0)
+		goto out;
+
+	kmod_list_foreach(mod, list) {
+		struct kmod_module *kmod = kmod_module_get_module(mod);
+		const char *kmod_name = kmod_module_get_name(kmod);
+
+		if (!strcmp(kmod_name, mod_name)) {
+			kmod_module_unref(kmod);
+			ret = 1;
+			break;
+		}
+		kmod_module_unref(kmod);
+	}
+	kmod_module_unref_list(list);
+out:
+	return ret;
+}
+
+static int modprobe(struct kmod_module *kmod, const char *options)
+{
+	unsigned int flags;
+
+	flags = 0;
+	if (options) /* force a fresh load to set the new options */
+		flags |= KMOD_PROBE_FAIL_ON_LOADED;
+
+	return kmod_module_probe_insert_module(kmod, flags, options,
+					       NULL, NULL, NULL);
+}
+
+int drm_kmod_load(const char *mod_name, const char *opts)
+{
+	struct kmod_ctx *ctx = kmod_ctx();
+	struct kmod_module *kmod;
+	int err;
+
+	err = kmod_module_new_from_name(ctx, mod_name, &kmod);
+	if (err < 0)
+		goto out;
+
+	err = modprobe(kmod, opts);
+out:
+	kmod_module_unref(kmod);
+	return err < 0 ? err : 0;
+}
+
+int drm_kmod_unload(const char *mod_name, unsigned int flags)
+{
+	struct kmod_ctx *ctx = kmod_ctx();
+	struct kmod_module *kmod;
+	int err;
+
+	err = kmod_module_new_from_name(ctx, mod_name, &kmod);
+	if (err < 0)
+		goto out;
+
+	err = kmod_module_remove_module(kmod, flags);
+out:
+	kmod_module_unref(kmod);
+	return err < 0 ? err : 0;
+}
diff --git a/shell/drm/drm-kmod.h b/shell/drm/drm-kmod.h
new file mode 100644
index 000000000..8bfaa7fd8
--- /dev/null
+++ b/shell/drm/drm-kmod.h
@@ -0,0 +1,9 @@
+#ifndef DRM_KMOD_H
+#define DRM_KMOD_H
+
+int drm_kmod_load(const char *mod_name, const char *opts);
+int drm_kmod_unload(const char *mod_name, unsigned int flags);
+
+int drm_kmod_is_loaded(const char *mod_name);
+
+#endif /* DRM_KMOD_H */
diff --git a/shell/drm/drm-module.c b/shell/drm/drm-module.c
new file mode 100644
index 000000000..05e0789f8
--- /dev/null
+++ b/shell/drm/drm-module.c
@@ -0,0 +1,130 @@
+#include "drm-builtin.h"
+#include "duktape.h"
+#include "igt-builtins.h"
+#include "os-builtin.h"
+
+#include "drm-constants.h"
+#include "drm-device.h"
+#include "drm-kmod.h"
+
+static struct drm_driver *drivers;
+
+static duk_ret_t drm_module_load(duk_context *ctx)
+{
+	const char *options = duk_get_string(ctx, -1);
+	const char *name = NULL;
+	int err;
+
+	duk_push_this(ctx);
+	if (duk_get_prop_string(ctx, -1, "name")) {
+		name = duk_get_string(ctx, -1);
+		duk_pop(ctx);
+	}
+	duk_pop(ctx);
+
+	err = 0;
+	if (name)
+		err = drm_kmod_load(name, options);
+	if (err)
+		os_throw_errno(ctx, err);
+
+	return 0;
+
+}
+
+static duk_ret_t drm_module_loaded(duk_context *ctx)
+{
+	const char *name = NULL;
+
+	duk_push_this(ctx);
+	if (duk_get_prop_string(ctx, -1, "name")) {
+		name = duk_get_string(ctx, -1);
+	}
+	if (!name)
+		return -1;
+
+	duk_push_boolean(ctx, drm_kmod_is_loaded(name));
+	return 1;
+}
+
+static duk_ret_t drm_module_unload(duk_context *ctx)
+{
+	unsigned int flags = duk_get_int(ctx, -1);
+	const char *name = NULL;
+	int err;
+
+	duk_push_this(ctx);
+	if (duk_get_prop_string(ctx, -1, "name")) {
+		name = duk_get_string(ctx, -1);
+		duk_pop(ctx);
+	}
+	duk_pop(ctx);
+
+	err = 0;
+	if (name)
+		err = drm_kmod_unload(name, flags);
+	if (err)
+		os_throw_errno(ctx, err);
+
+	return 0;
+}
+
+static duk_ret_t drm_module_setup_drivers(duk_context *ctx)
+{
+	duk_push_object(ctx);
+
+	for (struct drm_driver *d = drivers; d; d = d->next) {
+		if (drm_kmod_load(d->name, NULL) < 0)
+			continue;
+
+		if (d->ctor(ctx) > 0)
+			duk_put_prop_string(ctx, -2, d->name);
+	}
+
+	duk_put_prop_string(ctx, -2, "drivers");
+	return 0;
+}
+
+void drm_module_register_driver(struct drm_driver *d)
+{
+	d->next = drivers;
+	drivers = d;
+}
+
+static const duk_function_list_entry funcs[] = {
+	{ "cards", drm_device_cards, 0 },
+
+	{ "card", drm_device_card, 1 },
+	{ "render", drm_device_render, 1 },
+
+	{ "load", drm_module_load, 1 },
+	{ "loaded", drm_module_loaded, 0 },
+	{ "unload", drm_module_unload, 1 },
+
+	{}
+};
+
+static duk_ret_t drm_builtin_ctor(duk_context *ctx)
+{
+	duk_push_object(ctx);
+	duk_dup_top(ctx);
+	duk_put_global_string(ctx, "drm");
+
+	drm_module_setup_drivers(ctx);
+
+	drm_constants(ctx);
+	duk_put_function_list(ctx, -1, funcs);
+	duk_pop(ctx);
+
+	return 0;
+}
+
+__attribute__((constructor))
+static void __drm_register_builtin__(void)
+{
+	static struct igt_builtin builtin = {
+		.name = "drm",
+		.ctor = drm_builtin_ctor
+	};
+	igt_register_global(&builtin);
+}
diff --git a/shell/examples/drm/auth.js b/shell/examples/drm/auth.js
new file mode 100644
index 000000000..8af770df0
--- /dev/null
+++ b/shell/examples/drm/auth.js
@@ -0,0 +1,62 @@
+const nop_ioctl = function() { this.ioctl(drm.ioctl.DRM_IOCTL_FINISH) }
+
+igt.fork(drm.cards(), function(master) {
+	var slave, magic, root, authorized;
+
+	// Confirm we are a master and open a second fd to the same device
+	root = true;
+	try {
+		master.SetMaster();
+	} catch(e) {
+		// Alas not starting as root, we'll just have to hope.
+		igt.assert(os.uid());
+		igt.assert(e.errno === -os.EACCES);
+		root = false;
+	}
+	slave = master.reopen();
+	authorized = root;
+
+	for (var depth = 0; depth < 3; depth++) {
+		if (!authorized) {
+			// If !authorized (and !root) auth-only ioctls will fail
+			igt.expect(function(){ nop_ioctl.call(slave) },
+				   -os.EACCES);
+		}
+
+		// Grab the magic, and check it is constant
+		magic = slave.GetMagic();
+		igt.assert(slave.GetMagic() === magic);
+
+		// A slave cannot simply authorize their own magic
+		igt.expect(function(){ slave.AuthMagic(magic) }, -os.EACCES);
+
+		// Magic works first time
+		master.AuthMagic(magic);
+
+		// But it can only be authorized once
+		igt.expect(function(){ master.AuthMagic(magic) }, -os.EINVAL);
+
+		// Verify the magic did not change
+		igt.assert(slave.GetMagic() === magic);
+
+		// And the consumed magic still fails if used again by the slave
+		igt.expect(function(){ slave.AuthMagic(magic) }, -os.EACCES);
+
+		// And confirm the slave is now authorized
+		nop_ioctl.call(slave);
+
+		// But not a master
+		if (root)
+			igt.expect(function(){ slave.SetMaster() }, -os.EINVAL);
+
+		// A second slave is not authorized using the old magic
+		slave = slave.reopen();
+		igt.expect(function(){ slave.AuthMagic(magic) }, -os.EACCES);
+		authorized = root;
+
+		if (root) {
+			os.drop_root();
+			root = false;
+		}
+	}
+})
diff --git a/shell/include/drm-builtin.h b/shell/include/drm-builtin.h
new file mode 100644
index 000000000..a4ba4eeb6
--- /dev/null
+++ b/shell/include/drm-builtin.h
@@ -0,0 +1,14 @@
+#ifndef DRM_MODULE_H
+#define DRM_MODULE_H
+
+#include "duktape.h"
+
+struct drm_driver {
+	struct drm_driver *next;
+	const char *name;
+	duk_ret_t (*ctor)(duk_context *ctx);
+};
+
+void drm_module_register_driver(struct drm_driver *b);
+
+#endif
diff --git a/shell/include/drm-gem.h b/shell/include/drm-gem.h
new file mode 100644
index 000000000..ab299642d
--- /dev/null
+++ b/shell/include/drm-gem.h
@@ -0,0 +1,8 @@
+#ifndef DRM_GEM_H
+#define DRM_GEM_H
+
+#include "duktape.h"
+
+duk_ret_t drm_gem_object_dtor(duk_context *ctx);
+
+#endif /* DRM_GEM_H */
diff --git a/shell/meson.build b/shell/meson.build
index b2450b162..ea871925e 100644
--- a/shell/meson.build
+++ b/shell/meson.build
@@ -1,5 +1,11 @@
 executable('igt', [
 	     'duktape/duktape.c',
+	     'drm/drm-constants.c',
+	     'drm/drm-device.c',
+	     'drm/drm-gem.c',
+	     'drm/drm-kmod.c',
+	     'drm/drm-module.c',
+	     'igt-builtins.c',
 	     'igt-builtins.c',
 	     'igt-math.c',
 	     'igt-shell.c',
-- 
2.18.0



More information about the igt-dev mailing list