@@ -11251,6 +11251,7 @@ L: linux-kernel@vger.kernel.org
S: Maintained
F: include/linux/usermode_driver_mgmt.h
F: kernel/usermode_driver_mgmt.c
+F: tools/testing/selftests/umd_mgmt/*
KERNEL VIRTUAL MACHINE (KVM)
M: Paolo Bonzini <pbonzini@redhat.com>
@@ -84,6 +84,7 @@ TARGETS += timers
endif
TARGETS += tmpfs
TARGETS += tpm2
+TARGETS += umd_mgmt
TARGETS += user
TARGETS += vDSO
TARGETS += mm
new file mode 100644
@@ -0,0 +1 @@
+/sample_umd/sample_umh
new file mode 100644
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+TEST_GEN_PROGS_EXTENDED = sample_umd.ko
+
+$(OUTPUT)/%.ko: $(wildcard sample_umd/Makefile sample_umd/*.[ch])
+ $(call msg,MOD,,$@)
+ $(Q)$(MAKE) -C sample_umd install
+
+OVERRIDE_TARGETS := 1
+override define CLEAN
+ $(call msg,CLEAN)
+ $(Q)$(MAKE) -C sample_umd clean
+endef
+
+include ../lib.mk
new file mode 100644
@@ -0,0 +1 @@
+CONFIG_BPFILTER=y
new file mode 100644
@@ -0,0 +1,22 @@
+KDIR ?= ../../../../..
+
+userprogs := sample_umh
+sample_umh-objs := sample_handler.o
+userccflags += -I $(srctree)
+
+$(obj)/sample_binary_blob.o: $(obj)/sample_umh
+
+MODULES = sample_loader_kmod.ko sample_mgr.ko
+
+obj-m += sample_loader_kmod.o sample_mgr.o
+
+sample_loader_kmod-objs = sample_loader.o sample_binary_blob.o
+
+all:
+ +$(Q)$(MAKE) -C $(KDIR) M=$$PWD modules
+
+clean:
+ +$(Q)$(MAKE) -C $(KDIR) M=$$PWD clean
+
+install: all
+ +$(Q)$(MAKE) -C $(KDIR) M=$$PWD modules_install
new file mode 100644
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _SAMPLE_UMH_MSGFMT_H
+#define _SAMPLE_UMH_MSGFMT_H
+
+struct sample_request {
+ uint32_t offset;
+};
+
+struct sample_reply {
+ uint8_t data[128 * 1024];
+};
+
+#endif
new file mode 100644
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+ .section .init.rodata, "a"
+ .global sample_umh_start
+sample_umh_start:
+ .incbin "tools/testing/selftests/umd_mgmt/sample_umd/sample_umh"
+ .global sample_umh_end
+sample_umh_end:
new file mode 100644
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Implement the UMD Handler.
+ */
+#include <unistd.h>
+#include <malloc.h>
+#include <stdint.h>
+
+#include "msgfmt.h"
+
+FILE *debug_f;
+
+static void loop(void)
+{
+ struct sample_request *req = NULL;
+ struct sample_reply *reply = NULL;
+
+ req = calloc(1, sizeof(*req));
+ if (!req)
+ return;
+
+ reply = calloc(1, sizeof(*reply));
+ if (!reply)
+ goto out;
+
+ while (1) {
+ int n, len, offset;
+
+ offset = 0;
+ len = sizeof(*req);
+
+ while (len) {
+ n = read(0, ((void *)req) + offset, len);
+ if (n <= 0) {
+ fprintf(debug_f, "invalid request %d\n", n);
+ goto out;
+ }
+
+ len -= n;
+ offset += n;
+ }
+
+ if (req->offset < sizeof(reply->data))
+ reply->data[req->offset] = 1;
+
+ offset = 0;
+ len = sizeof(*reply);
+
+ while (len) {
+ n = write(1, ((void *)reply) + offset, len);
+ if (n <= 0) {
+ fprintf(debug_f, "reply failed %d\n", n);
+ goto out;
+ }
+
+ len -= n;
+ offset += n;
+ }
+
+ if (req->offset < sizeof(reply->data))
+ reply->data[req->offset] = 0;
+ }
+out:
+ free(req);
+ free(reply);
+}
+
+int main(void)
+{
+ debug_f = fopen("/dev/kmsg", "w");
+ setvbuf(debug_f, 0, _IOLBF, 0);
+ fprintf(debug_f, "<5>Started sample_umh\n");
+ loop();
+ fclose(debug_f);
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Implement the UMD Loader (credits: bpfilter).
+ */
+#include <linux/module.h>
+#include <linux/usermode_driver_mgmt.h>
+
+extern char sample_umh_start;
+extern char sample_umh_end;
+extern struct umd_mgmt sample_mgmt_ops;
+
+static int __init load_umh(void)
+{
+ return umd_mgmt_load(&sample_mgmt_ops, &sample_umh_start,
+ &sample_umh_end);
+}
+
+static void __exit fini_umh(void)
+{
+ umd_mgmt_unload(&sample_mgmt_ops);
+}
+module_init(load_umh);
+module_exit(fini_umh);
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Implement the UMD Manager.
+ */
+#include <linux/module.h>
+#include <linux/security.h>
+#include <linux/seq_file.h>
+#include <linux/usermode_driver_mgmt.h>
+
+#include "msgfmt.h"
+
+struct umd_mgmt sample_mgmt_ops;
+EXPORT_SYMBOL_GPL(sample_mgmt_ops);
+
+struct dentry *sample_umd_dentry;
+
+static int sample_write_common(u32 offset, bool test)
+{
+ struct sample_request *req;
+ struct sample_reply *reply;
+ int ret;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ reply = kzalloc(sizeof(*reply), GFP_KERNEL);
+ if (!reply) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ req->offset = offset;
+
+ if (test)
+ /* Lock is already taken. */
+ ret = umd_send_recv(&sample_mgmt_ops.info, req, sizeof(*req),
+ reply, sizeof(*reply));
+ else
+ ret = umd_mgmt_send_recv(&sample_mgmt_ops, req, sizeof(*req),
+ reply, sizeof(*reply));
+ if (ret < 0)
+ goto out;
+
+ if (reply->data[req->offset] != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+out:
+ kfree(req);
+ kfree(reply);
+
+ return ret;
+}
+
+static ssize_t sample_umd_write(struct file *file, const char __user *buf,
+ size_t datalen, loff_t *ppos)
+{
+ char offset_str[8];
+ u32 offset;
+ int ret;
+
+ if (datalen >= sizeof(offset_str))
+ return -EINVAL;
+
+ ret = copy_from_user(offset_str, buf, datalen);
+ if (ret < 0)
+ return ret;
+
+ offset_str[datalen] = '\0';
+
+ ret = kstrtou32(offset_str, 10, &offset);
+ if (ret < 0)
+ return ret;
+
+ if (offset >= sizeof(((struct sample_reply *)0)->data))
+ return -EINVAL;
+
+ ret = sample_write_common(offset, false);
+ if (ret < 0)
+ return ret;
+
+ return datalen;
+}
+
+static const struct file_operations sample_umd_file_ops = {
+ .write = sample_umd_write,
+};
+
+static int sample_post_start_umh(struct umd_mgmt *mgmt)
+{
+ return sample_write_common(0, true);
+}
+
+static int __init load_umh(void)
+{
+ mutex_init(&sample_mgmt_ops.lock);
+ sample_mgmt_ops.info.tgid = NULL;
+ sample_mgmt_ops.info.driver_name = "sample_umh";
+ sample_mgmt_ops.post_start = sample_post_start_umh;
+ sample_mgmt_ops.kmod = "sample_loader_kmod";
+ sample_mgmt_ops.kmod_loaded = false;
+
+ sample_umd_dentry = securityfs_create_file("sample_umd", 0200, NULL,
+ NULL, &sample_umd_file_ops);
+ if (IS_ERR(sample_umd_dentry))
+ return PTR_ERR(sample_umd_dentry);
+
+ return 0;
+}
+
+static void __exit fini_umh(void)
+{
+ securityfs_remove(sample_umd_dentry);
+}
+module_init(load_umh);
+module_exit(fini_umh);
+MODULE_LICENSE("GPL");
new file mode 100755
@@ -0,0 +1,40 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+#
+# Author: Roberto Sassu <roberto.sassu@huawei.com>
+#
+# Script to test the UMD management library.
+
+# Kselftest framework defines: ksft_pass=0, ksft_fail=1, ksft_skip=4
+ksft_pass=0
+ksft_fail=1
+ksft_skip=4
+
+if ! /sbin/modprobe -q sample_mgr; then
+ echo "umd_mgmt: module sample_mgr is not found [SKIP]"
+ exit $ksft_skip
+fi
+
+if [ ! -f /sys/kernel/security/sample_umd ]; then
+ echo "umd_mgmt: kernel interface is not found [SKIP]"
+ exit $ksft_skip
+fi
+
+i=0
+
+while [ $i -lt 500 ]; do
+ if ! echo $(( RANDOM % 128 * 1024 )) > /sys/kernel/security/sample_umd; then
+ echo "umd_mgmt: test failed"
+ exit $ksft_fail
+ fi
+
+ if [ $(( i % 50 )) -eq 0 ]; then
+ rmmod sample_loader_kmod
+ fi
+
+ (( i++ ))
+done
+
+exit $ksft_pass