new file mode 100644
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors:
+ * Jérôme Glisse <jglisse@redhat.com>
+ */
+/* Heterogeneous memory system (HMS) see Documentation/vm/hms.rst */
+#ifndef LINUX_UAPI_HBIND
+#define LINUX_UAPI_HBIND
+
+
+/* For now just freak out if it is bigger than a page. */
+#define HBIND_MAX_TARGETS (4096 / 4)
+#define HBIND_MAX_ATOMS (4096 / 4)
+
+
+struct hbind_params {
+ uint64_t start;
+ uint64_t end;
+ uint32_t ntargets;
+ uint32_t natoms;
+ uint64_t targets;
+ uint64_t atoms;
+};
+
+
+#define HBIND_ATOM_GET_DWORDS(v) (((v) >> 20) & 0xfff)
+#define HBIND_ATOM_SET_DWORDS(v) (((v) & 0xfff) << 20)
+#define HBIND_ATOM_GET_CMD(v) ((v) & 0xfffff)
+#define HBIND_ATOM_SET_CMD(v) ((v) & 0xfffff)
+
+
+#define HBIND_IOCTL _IOWR('H', 0x00, struct hbind_params)
+
+
+#endif /* LINUX_UAPI_HBIND */
@@ -99,3 +99,4 @@ obj-$(CONFIG_HARDENED_USERCOPY) += usercopy.o
obj-$(CONFIG_PERCPU_STATS) += percpu-stats.o
obj-$(CONFIG_HMM) += hmm.o
obj-$(CONFIG_MEMFD_CREATE) += memfd.o
+obj-$(CONFIG_HMS) += hms.o
new file mode 100644
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors:
+ * Jérôme Glisse <jglisse@redhat.com>
+ */
+/* Heterogeneous memory system (HMS) see Documentation/vm/hms.rst */
+#define pr_fmt(fmt) "hms: " fmt
+
+#include <linux/miscdevice.h>
+#include <linux/sched/mm.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/hms.h>
+#include <linux/fs.h>
+
+#include <uapi/linux/hbind.h>
+
+
+#define HBIND_FIX_ARRAY 64
+
+
+static ssize_t hbind_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static ssize_t hbind_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static long hbind_ioctl(struct file *file, unsigned cmd, unsigned long arg)
+{
+ uint32_t *targets, *_dtargets = NULL, _ftargets[HBIND_FIX_ARRAY];
+ uint32_t *atoms, *_datoms = NULL, _fatoms[HBIND_FIX_ARRAY];
+ void __user *uarg = (void __user *)arg;
+ struct hbind_params params;
+ uint32_t i, ndwords;
+ int ret;
+
+ switch(cmd) {
+ case HBIND_IOCTL:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = copy_from_user(¶ms, uarg, sizeof(params));
+ if (ret)
+ return ret;
+
+ /* Some sanity checks */
+ params.start &= PAGE_MASK;
+ params.end = PAGE_ALIGN(params.end);
+ if (params.end <= params.start)
+ return -EINVAL;
+
+ /* More sanity checks */
+ if (params.ntargets > HBIND_MAX_TARGETS)
+ return -EINVAL;
+
+ /* We need at least one atoms. */
+ if (!params.natoms || params.natoms > HBIND_MAX_ATOMS)
+ return -EINVAL;
+
+ /* Let's allocate memory for parameters. */
+ if (params.ntargets > HBIND_FIX_ARRAY) {
+ _dtargets = kzalloc(4 * params.ntargets, GFP_KERNEL);
+ if (_dtargets == NULL)
+ return -ENOMEM;
+ targets = _dtargets;
+ } else {
+ targets = _ftargets;
+ }
+ if (params.natoms > HBIND_FIX_ARRAY) {
+ _datoms = kzalloc(4 * params.natoms, GFP_KERNEL);
+ if (_datoms == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ atoms = _datoms;
+ } else {
+ atoms = _fatoms;
+ }
+
+ /* Let's fetch hbind() parameters. */
+ ret = copy_from_user(atoms, (void __user *)params.atoms,
+ 4 * params.natoms);
+ if (ret)
+ goto out;
+ ret = copy_from_user(targets, (void __user *)params.targets,
+ 4 * params.ntargets);
+ if (ret)
+ goto out;
+
+ mmget(current->mm);
+
+ /* Sanity checks atoms and execute them. */
+ for (i = 0, ndwords = 1; i < params.natoms; i += ndwords) {
+ ndwords = 1 + HBIND_ATOM_GET_DWORDS(atoms[i]);
+ switch (HBIND_ATOM_GET_CMD(atoms[i])) {
+ default:
+ ret = -EINVAL;
+ goto out_mm;
+ }
+ }
+
+out_mm:
+ copy_to_user((void __user *)params.atoms, atoms, 4 * params.natoms);
+ mmput(current->mm);
+out:
+ kfree(_dtargets);
+ kfree(_datoms);
+ return ret;
+}
+
+const struct file_operations hbind_fops = {
+ .llseek = no_llseek,
+ .read = hbind_read,
+ .write = hbind_write,
+ .unlocked_ioctl = hbind_ioctl,
+ .owner = THIS_MODULE,
+};
+
+static struct miscdevice hbind_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &hbind_fops,
+ .name = "hbind",
+};
+
+int __init hbind_init(void)
+{
+ pr_info("Heterogeneous memory system (HMS) hbind() driver\n");
+ return misc_register(&hbind_device);
+}
+
+void __exit hbind_fini(void)
+{
+ misc_deregister(&hbind_device);
+}
+
+module_init(hbind_init);
+module_exit(hbind_fini);