[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