arch/riscv: Keystone

Driver for the Keystone trusted execution environment.

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@tuni.fi>
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index d607ab0..ae5d0a8 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -353,6 +353,17 @@
 	bool
 	select HAVE_MOD_ARCH_SPECIFIC
 
+config RISCV_KEYSTONE
+	bool "Keystone"
+	default n
+	depends on CMA
+	help
+	  Keystone is a trusted execution environment hosted by Keystone security
+	  monitor, which runs inside SBI. Keystone driver provides ioctl interface
+	  for creating and running Keystone enclaves.
+
+	  If you don't know what to do here, say N.
+
 config SMP
 	bool "Symmetric Multi-Processing"
 	help
diff --git a/arch/riscv/include/uapi/asm/keystone.h b/arch/riscv/include/uapi/asm/keystone.h
new file mode 100644
index 0000000..f0f42f1
--- /dev/null
+++ b/arch/riscv/include/uapi/asm/keystone.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2018-2022 The Regents of the University of California (Regents).
+ * Copyright (c) 2023 Univrsity of Tampere.
+ *
+ * Authors:
+ * Dayeol Lee <dayeol@berkeley.edu>
+ * Evgeny Pobachienko <evgenyp@berkeley.edu>
+ * Jarkko Sakkinen <jarkko.sakkinen@tuni.fi>
+ */
+
+#ifndef UAPI_ASM_RISCV_KEYSTONE_H
+#define UAPI_ASM_RISCV_KEYSTONE_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* Same as TEE_IOC_MAGIC */
+#define KEYSTONE_IOC 0xA4
+
+#define KEYSTONE_IOC_CREATE_ENCLAVE _IOR(KEYSTONE_IOC, 0x00, struct keystone_enclave)
+#define KEYSTONE_IOC_DESTROY_ENCLAVE _IOW(KEYSTONE_IOC, 0x01, struct keystone_enclave)
+#define KEYSTONE_IOC_RUN_ENCLAVE _IOR(KEYSTONE_IOC, 0x04, struct keystone_run)
+#define KEYSTONE_IOC_RESUME_ENCLAVE _IOR(KEYSTONE_IOC, 0x05, struct keystone_run)
+#define KEYSTONE_IOC_FINALIZE_ENCLAVE _IOR(KEYSTONE_IOC, 0x06, struct keystone_enclave)
+#define KEYSTONE_IOC_UTM_INIT _IOR(KEYSTONE_IOC, 0x07, struct keystone_enclave)
+#define KEYSTONE_IOC_FINALIZE_LIBRARY_ENCLAVE _IOR(KEYSTONE_IOC, 0x09, struct keystone_enclave)
+#define KEYSTONE_IOC_DESTROY_LIBRARY_ENCLAVE _IOW(KEYSTONE_IOC, 0x0a, struct keystone_enclave)
+
+#define RT_NOEXEC 0
+#define USER_NOEXEC 1
+#define RT_FULL 2
+#define USER_FULL 3
+#define UTM_FULL 4
+
+struct runtime_params_t {
+	__u64 runtime_entry;
+	__u64 user_entry;
+	__u64 untrusted_ptr;
+	__u64 untrusted_size;
+};
+
+struct keystone_enclave {
+	__u64 eid;
+	__u64 min_pages;
+	__u64 runtime_vaddr;
+	__u64 user_vaddr;
+	__u64 pt_ptr;
+	__u64 utm_free_ptr;
+	__u64 epm_paddr;
+	__u64 utm_paddr;
+	__u64 runtime_paddr;
+	__u64 user_paddr;
+	__u64 free_paddr;
+	__u64 epm_size;
+	__u64 utm_size;
+	struct runtime_params_t params;
+	__u64 library_name;
+};
+
+struct keystone_run {
+	__u64 eid;
+	__u64 error;
+	__u64 value;
+};
+
+#endif /* UAPI_ASM_RISCV_KEYSTONE_H */
diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
index 95cf25d..f0d1df9 100644
--- a/arch/riscv/kernel/Makefile
+++ b/arch/riscv/kernel/Makefile
@@ -101,3 +101,5 @@
 
 obj-$(CONFIG_64BIT)		+= pi/
 obj-$(CONFIG_ACPI)		+= acpi.o
+
+obj-$(CONFIG_RISCV_KEYSTONE)	+= keystone/
diff --git a/arch/riscv/kernel/keystone/Makefile b/arch/riscv/kernel/keystone/Makefile
new file mode 100644
index 0000000..dc31f78
--- /dev/null
+++ b/arch/riscv/kernel/keystone/Makefile
@@ -0,0 +1 @@
+obj-y := keystone.o page.o ioctl.o enclave.o
diff --git a/arch/riscv/kernel/keystone/enclave.c b/arch/riscv/kernel/keystone/enclave.c
new file mode 100644
index 0000000..9b28e01
--- /dev/null
+++ b/arch/riscv/kernel/keystone/enclave.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018-2022 The Regents of the University of California (Regents).
+ * Copyright (c) 2023 Univrsity of Tampere.
+ *
+ * Authors:
+ * Dayeol Lee <dayeol@berkeley.edu>
+ * Evgeny Pobachienko <evgenyp@berkeley.edu>
+ * Jarkko Sakkinen <jarkko.sakkinen@tuni.fi>
+ */
+
+#include <linux/dma-mapping.h>
+#include "keystone.h"
+
+/* database for enclave ID's (EID's) of active enclaves: */
+DEFINE_IDR(idr_enclave);
+/* lock for idr_enclave: */
+DEFINE_MUTEX(idr_enclave_lock);
+
+#define ENCLAVE_IDR_MIN 0x1000
+#define ENCLAVE_IDR_MAX 0xffff
+
+void keystone_enclave_destroy(struct enclave *enclave)
+{
+	struct epm *epm;
+	struct utm *utm;
+
+	WARN_ON_ONCE(!enclave);
+
+	if (!enclave)
+		return;
+
+	epm = enclave->epm;
+	utm = enclave->utm;
+
+	if (epm) {
+#ifdef CONFIG_CMA
+		dma_free_coherent(keystone_dev.this_device, epm->size, epm->ptr, epm->pa);
+#else
+		free_pages(epm->ptr, epm->order);
+#endif
+		kfree(epm);
+	}
+
+	if (utm) {
+		if (utm->ptr)
+			free_pages((vaddr_t)utm->ptr, utm->order);
+
+		kfree(utm);
+	}
+
+	kfree(enclave);
+}
+
+struct enclave *keystone_enclave_create(unsigned long min_pages)
+{
+	struct enclave *enclave;
+
+	enclave = kzalloc(sizeof(*enclave), GFP_KERNEL);
+	if (!enclave)
+		return NULL;
+
+	enclave->eid = -1; /* invalid */
+	enclave->utm = NULL;
+	enclave->close_on_pexit = 1;
+	enclave->is_init = true;
+
+	enclave->epm = kzalloc(sizeof(*enclave->epm), GFP_KERNEL);
+	if (!enclave->epm)
+		goto err;
+
+	if (epm_init(enclave->epm, min_pages))
+		goto err;
+
+	return enclave;
+
+err:
+	keystone_enclave_destroy(enclave);
+	return NULL;
+}
+
+int keystone_alloc_eid(struct enclave *enclave)
+{
+	int ret;
+
+	mutex_lock(&idr_enclave_lock);
+	ret = idr_alloc(&idr_enclave, enclave, ENCLAVE_IDR_MIN, ENCLAVE_IDR_MAX, GFP_KERNEL);
+	mutex_unlock(&idr_enclave_lock);
+
+	return ret;
+}
+
+struct enclave *keystone_free_eid(unsigned int ueid)
+{
+	struct enclave *enclave;
+
+	mutex_lock(&idr_enclave_lock);
+	enclave = idr_remove(&idr_enclave, ueid);
+	mutex_unlock(&idr_enclave_lock);
+
+	return enclave;
+}
diff --git a/arch/riscv/kernel/keystone/ioctl.c b/arch/riscv/kernel/keystone/ioctl.c
new file mode 100644
index 0000000..9e5a727
--- /dev/null
+++ b/arch/riscv/kernel/keystone/ioctl.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018-2022 The Regents of the University of California (Regents).
+ * Copyright (c) 2023 Univrsity of Tampere.
+ *
+ * Authors:
+ * Dayeol Lee <dayeol@berkeley.edu>
+ * Evgeny Pobachienko <evgenyp@berkeley.edu>
+ * Jarkko Sakkinen <jarkko.sakkinen@tuni.fi>
+ */
+
+#include <linux/uaccess.h>
+#include <asm/sbi.h>
+#include <uapi/asm/keystone.h>
+#include "keystone.h"
+#include "sbi.h"
+
+static int keystone_ioc_create_enclave(struct file *filep, unsigned long arg)
+{
+	struct keystone_enclave *enclp = (struct keystone_enclave *)arg;
+	struct enclave *enclave;
+	struct epm *epm;
+	ssize_t ret;
+
+	enclave = keystone_enclave_create(enclp->min_pages);
+	if (!enclave)
+		return -ENOMEM;
+
+	ret = keystone_alloc_eid(enclave);
+	if (ret < 0) {
+		keystone_enclave_destroy(enclave);
+		return ret;
+	}
+
+	filep->private_data = enclave;
+
+	epm = enclave->epm;
+
+	enclp->eid = ret;
+	enclp->pt_ptr = __pa(epm->ptr);
+	enclp->epm_size = epm->size;
+
+	return 0;
+}
+
+static int keystone_ioc_finalize_enclave(struct file *filep, unsigned long arg)
+{
+	struct keystone_enclave *enclp = (struct keystone_enclave *)arg;
+	struct enclave *enclave = filep->private_data;
+	struct keystone_sbi_create args;
+	struct sbiret ret;
+	struct utm *utm;
+
+	if (!enclave)
+		return -EIO;
+
+	enclave->is_init = false;
+
+	/* PMP: */
+	args.epm_region.paddr = enclave->epm->pa;
+	args.epm_region.size = enclave->epm->size;
+
+	pr_debug("Enclave Private Memory (EPM): 0x%016llx - 0x%016llx\n",
+		 args.epm_region.paddr,
+		 args.epm_region.paddr + args.epm_region.size);
+
+	/* Untrusted memory: */
+	utm = enclave->utm;
+	if (utm) {
+		args.utm_region.paddr = __pa(utm->ptr);
+		args.utm_region.size = utm->size;
+
+	} else {
+		args.utm_region.paddr = 0;
+		args.utm_region.size = 0;
+	}
+
+	pr_debug("Untrusted memory (UTM): 0x%016llx - 0x%016llx\n",
+		 args.utm_region.paddr,
+		 args.utm_region.paddr + args.utm_region.size);
+
+	/* Physical addresses: */
+	args.runtime_paddr = enclp->runtime_paddr;
+	args.user_paddr = enclp->user_paddr;
+	args.free_paddr = enclp->free_paddr;
+
+	args.params = enclp->params;
+
+	ret = sbi_sm_call(SBI_SM_CREATE_ENCLAVE, (unsigned long)&args);
+	if (ret.error) {
+		pr_err("SBI_SM_CREATE_ENCLAVE error %ld\n", ret.error);
+		goto err;
+	}
+
+	enclave->eid = ret.value;
+	return 0;
+
+err:
+	keystone_enclave_destroy(enclave);
+	return -EIO;
+}
+
+static int keystone_ioc_run_enclave(struct file *filep, unsigned long data)
+{
+	struct keystone_run *arg = (struct keystone_run *)data;
+	struct enclave *enclave = filep->private_data;
+	struct sbiret ret;
+
+	if (!enclave)
+		return -EIO;
+
+	ret = sbi_sm_call(SBI_SM_RUN_ENCLAVE, enclave->eid);
+	arg->error = ret.error;
+	arg->value = ret.value;
+
+	return 0;
+}
+
+static int __keystone_ioc_destroy_enclave(struct enclave *enclave)
+{
+	struct sbiret ret;
+
+	ret = sbi_sm_call(SBI_SM_DESTROY_ENCLAVE, enclave->eid);
+	if (ret.error) {
+		pr_err("SBI_SM_DESTROY_ENCLAVE error %ld\n", ret.error);
+		return -EIO;
+	}
+
+	keystone_enclave_destroy(enclave);
+	keystone_free_eid(enclave->eid);
+
+	return 0;
+}
+
+/*
+ * Do not free up the software resources (physical memory regions and idr to be
+ * more specific) in the case of SBI failure because otherwise there would be a
+ * mismatch between the software and hardware states.
+ */
+static int keystone_ioc_destroy_enclave(struct file *filep, unsigned long arg)
+{
+	struct enclave *enclave = filep->private_data;
+	int ret;
+
+	if (!enclave) {
+		pr_debug("%s: enclave already destroyed\n", __func__);
+		return -EIO;
+	}
+
+	ret = __keystone_ioc_destroy_enclave(enclave);
+	if (ret)
+		return ret;
+
+	filep->private_data = NULL;
+	return 0;
+}
+
+static int keystone_ioc_resume_enclave(struct file *filep, unsigned long data)
+{
+	struct keystone_run *arg = (struct keystone_run *)data;
+	struct enclave *enclave = filep->private_data;
+	struct sbiret ret;
+
+	if (!enclave)
+		return -EIO;
+
+	ret = sbi_sm_call(SBI_SM_RESUME_ENCLAVE, enclave->eid);
+	arg->error = ret.error;
+	arg->value = ret.value;
+
+	return 0;
+}
+
+static int keystone_ioc_utm_init(struct file *filep, unsigned long arg)
+{
+	struct keystone_enclave *enclp = (struct keystone_enclave *)arg;
+	u64 untrusted_size = enclp->params.untrusted_size;
+	struct enclave *enclave = filep->private_data;
+	struct utm *utm;
+	int ret = 0;
+
+	if (!enclave)
+		return -EIO;
+
+	utm = kzalloc(sizeof(*utm), GFP_KERNEL);
+	if (!utm) {
+		ret = -ENOMEM;
+		return ret;
+	}
+
+	ret = utm_init(utm, PFN_UP(untrusted_size));
+	if (ret) {
+		pr_warn("utm_init() returned %d\n", ret);
+		kfree(utm);
+		return ret;
+	}
+
+	/* Prepare for mmap: */
+	enclave->utm = utm;
+	enclp->utm_free_ptr = __pa(utm->ptr);
+
+	return ret;
+}
+
+static int keystone_ioc_finalize_library_enclave(struct file *filep, unsigned long arg)
+{
+	struct keystone_enclave *enclp = (struct keystone_enclave *)arg;
+	struct enclave *enclave = filep->private_data;
+	struct keystone_sbi_create create_args;
+	struct sbiret ret;
+
+	if (!enclave)
+		return -EIO;
+
+	enclave->is_init = false;
+
+	create_args.epm_region.paddr = enclave->epm->pa;
+	create_args.epm_region.size = enclave->epm->size;
+	// Don't need untrusted memory for library enclaves:
+	create_args.utm_region.paddr = 0;
+	create_args.utm_region.size = 0;
+
+	if (strncpy_from_user(create_args.library_name,
+			      (const char __user *)enclp->library_name,
+			      NAME_MAX) < 0)
+		return -EFAULT;
+
+	ret = sbi_sm_call(SBI_SM_CREATE_LIBRARY_ENCLAVE, (unsigned long)&create_args);
+	if (ret.error) {
+		pr_err("SBI_SM_CREATE_LIBRARY_ENCLAVE error %ld\n", ret.error);
+		goto err;
+	}
+
+	enclave->eid = ret.value;
+	return 0;
+
+err:
+	keystone_enclave_destroy(enclave);
+	return -EIO;
+}
+
+/*
+ * Do not free up the software resources (physical memory regions and idr to be
+ * more specific) in the case of SBI failure because otherwise there would be a
+ * mismatch between the software and hardware states.
+ */
+static int keystone_ioc_destroy_library_enclave(struct file *filep, unsigned long arg)
+{
+	struct enclave *enclave = filep->private_data;
+	struct sbiret ret;
+
+	if (!enclave)
+		return -EIO;
+
+	ret = sbi_sm_call(SBI_SM_DESTROY_LIBRARY_ENCLAVE, enclave->eid);
+	if (ret.error) {
+		pr_err("SBI_SM_DESTROY_LIBRARY_ENCLAVE error %ld\n", ret.error);
+		return -EIO;
+	}
+
+	keystone_enclave_destroy(enclave);
+	filep->private_data = NULL;
+	return 0;
+}
+
+long keystone_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+	struct enclave *enclave = filep->private_data;
+	size_t ioc_size;
+	char data[512];
+	long ret;
+
+	if (!enclave)
+		return -EIO;
+
+	/* Mutually exclude concurrent threads: */
+	if (test_and_set_bit(true, &enclave->busy)) {
+		pr_info("%d busy\n", enclave->eid);
+		return -EBUSY;
+	}
+
+	ioc_size = _IOC_SIZE(cmd);
+	ioc_size = ioc_size > sizeof(data) ? sizeof(data) : ioc_size;
+
+	if (copy_from_user(data, (void __user *)arg, ioc_size)) {
+		ret = -EFAULT;
+		goto err;
+	}
+
+	switch (cmd) {
+	case KEYSTONE_IOC_CREATE_ENCLAVE:
+		ret = keystone_ioc_create_enclave(filep, (unsigned long)data);
+		break;
+	case KEYSTONE_IOC_FINALIZE_ENCLAVE:
+		ret = keystone_ioc_finalize_enclave(filep, (unsigned long)data);
+		break;
+	case KEYSTONE_IOC_DESTROY_ENCLAVE:
+		ret = keystone_ioc_destroy_enclave(filep, (unsigned long)data);
+		break;
+	case KEYSTONE_IOC_RUN_ENCLAVE:
+		ret = keystone_ioc_run_enclave(filep, (unsigned long)data);
+		break;
+	case KEYSTONE_IOC_RESUME_ENCLAVE:
+		ret = keystone_ioc_resume_enclave(filep, (unsigned long)data);
+		break;
+	case KEYSTONE_IOC_UTM_INIT:
+		ret = keystone_ioc_utm_init(filep, (unsigned long)data);
+		break;
+	case KEYSTONE_IOC_FINALIZE_LIBRARY_ENCLAVE:
+		ret = keystone_ioc_finalize_library_enclave(filep, (unsigned long)data);
+		break;
+	case KEYSTONE_IOC_DESTROY_LIBRARY_ENCLAVE:
+		ret = keystone_ioc_destroy_library_enclave(filep, (unsigned long)data);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	/* Copy back only on success: */
+	if (!ret && copy_to_user((void __user *)arg, data, ioc_size))
+		ret = -EFAULT;
+
+err:
+	clear_bit(true, &enclave->busy);
+	return ret;
+}
+
+int keystone_release(struct inode *inode, struct file *file)
+{
+	struct enclave *enclave = file->private_data;
+
+	if (!enclave) {
+		pr_warn("%s: enclave was already destroyed\n", __func__);
+		return 0;
+	}
+
+	if (enclave->close_on_pexit)
+		return __keystone_ioc_destroy_enclave(enclave);
+
+	return 0;
+}
diff --git a/arch/riscv/kernel/keystone/keystone.c b/arch/riscv/kernel/keystone/keystone.c
new file mode 100644
index 0000000..11e407a0
--- /dev/null
+++ b/arch/riscv/kernel/keystone/keystone.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018-2022 The Regents of the University of California (Regents).
+ * Copyright (c) 2023 Univrsity of Tampere.
+ *
+ * Authors:
+ * Dayeol Lee <dayeol@berkeley.edu>
+ * Evgeny Pobachienko <evgenyp@berkeley.edu>
+ * Jarkko Sakkinen <jarkko.sakkinen@tuni.fi>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/anon_inodes.h>
+#include <uapi/asm/keystone.h>
+#include "keystone.h"
+#include "sbi.h"
+
+static const struct file_operations keystone_fops = {
+	.owner = THIS_MODULE,
+	.mmap = keystone_mmap,
+	.unlocked_ioctl = keystone_ioctl,
+	.release = keystone_release
+};
+
+struct miscdevice keystone_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "keystone_enclave",
+	.fops = &keystone_fops,
+	.mode = 0666,
+};
+
+struct file *keystone_open(void)
+{
+	return anon_inode_getfile("[keystone]", &keystone_fops, NULL, O_RDWR);
+}
+EXPORT_SYMBOL_GPL(keystone_open);
+
+int keystone_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+	struct enclave *enclave = filep->private_data;
+	unsigned long vsize, psize;
+	struct utm *utm;
+	struct epm *epm;
+	vaddr_t paddr;
+
+	if (!enclave)
+		return -EIO;
+
+	utm = enclave->utm;
+	epm = enclave->epm;
+	vsize = vma->vm_end - vma->vm_start;
+
+	if (enclave->is_init) {
+		if (vsize > PAGE_SIZE)
+			return -EINVAL;
+
+		paddr = __pa(epm->ptr) + (vma->vm_pgoff << PAGE_SHIFT);
+		remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT, vsize, vma->vm_page_prot);
+	} else {
+		psize = utm->size;
+		if (vsize > psize)
+			return -EINVAL;
+
+		remap_pfn_range(vma, vma->vm_start, __pa(utm->ptr) >> PAGE_SHIFT, vsize,
+				vma->vm_page_prot);
+	}
+
+	return 0;
+}
+
+static int __init keystone_init(void)
+{
+	int ret;
+
+	ret = misc_register(&keystone_dev);
+	if (ret < 0)
+		return ret;
+
+	keystone_dev.this_device->coherent_dma_mask = DMA_BIT_MASK(32);
+	return 0;
+}
+
+device_initcall(keystone_init);
diff --git a/arch/riscv/kernel/keystone/keystone.h b/arch/riscv/kernel/keystone/keystone.h
new file mode 100644
index 0000000..300b394
--- /dev/null
+++ b/arch/riscv/kernel/keystone/keystone.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2022 The Regents of the University of California (Regents).
+ * Copyright (c) 2023 Univrsity of Tampere.
+ *
+ * Authors:
+ * Dayeol Lee <dayeol@berkeley.edu>
+ * Evgeny Pobachienko <evgenyp@berkeley.edu>
+ * Jarkko Sakkinen <jarkko.sakkinen@tuni.fi>
+ */
+
+#ifndef RISCV_KEYSTONE_H
+#define RISCV_KEYSTONE_H
+
+#include <asm/sbi.h>
+#include <asm/csr.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/idr.h>
+#include <linux/file.h>
+#include "riscv64.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "keystone: " fmt
+
+typedef u64 vaddr_t;
+typedef u64 paddr_t;
+
+extern struct miscdevice keystone_dev;
+
+long keystone_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+int keystone_release(struct inode *inode, struct file *file);
+int keystone_mmap(struct file *filp, struct vm_area_struct *vma);
+
+struct epm {
+	void *ptr;
+	size_t size;
+	unsigned long order;
+	phys_addr_t pa;
+};
+
+struct utm {
+	void *ptr;
+	size_t size;
+	unsigned long order;
+};
+
+struct enclave {
+	int eid;
+	int close_on_pexit;
+	struct utm *utm;
+	struct epm *epm;
+	bool is_init;
+	/* marks when ioctl is active with another thread: */
+	unsigned long busy;
+};
+
+struct enclave *keystone_enclave_create(unsigned long min_pages);
+void keystone_enclave_destroy(struct enclave *enclave);
+
+int keystone_alloc_eid(struct enclave *enclave);
+struct enclave *keystone_free_eid(unsigned int ueid);
+
+int epm_init(struct epm *epm, unsigned long requested_pages);
+int utm_init(struct utm *utm, unsigned long requested_pages);
+
+#endif /* RISCV_KEYSTONE_H */
diff --git a/arch/riscv/kernel/keystone/page.c b/arch/riscv/kernel/keystone/page.c
new file mode 100644
index 0000000..4c1fcf6
--- /dev/null
+++ b/arch/riscv/kernel/keystone/page.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018-2022 The Regents of the University of California (Regents).
+ * Copyright (c) 2023 Univrsity of Tampere.
+ *
+ * Authors:
+ * Dayeol Lee <dayeol@berkeley.edu>
+ * Evgeny Pobachienko <evgenyp@berkeley.edu>
+ * Jarkko Sakkinen <jarkko.sakkinen@tuni.fi>
+ */
+
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include "riscv64.h"
+#include "keystone.h"
+
+/*
+ * Initialize Enclave Private Memory (EPM).
+ */
+int epm_init(struct epm *epm, unsigned long requested_pages)
+{
+	unsigned long aligned_pages = __roundup_pow_of_two(requested_pages);
+	phys_addr_t __maybe_unused device_phys_addr = 0;
+	unsigned long order = ilog2(aligned_pages);
+	void *ptr = NULL;
+
+#ifdef CONFIG_CMA
+	ptr = dma_alloc_coherent(keystone_dev.this_device,
+				 aligned_pages << PAGE_SHIFT,
+				 &device_phys_addr,
+				 GFP_KERNEL);
+#else
+	if (order < MAX_ORDER)
+		ptr = __get_free_pages(GFP_HIGHUSER, order);
+	else
+		return -EINVAL;
+#endif
+
+	if (!ptr)
+		return -ENOMEM;
+
+	/* Zero the payload: */
+	memset(ptr, 0, aligned_pages);
+
+	epm->pa = __pa(ptr);
+	epm->order = order;
+	epm->size = aligned_pages << PAGE_SHIFT;
+	epm->ptr = ptr;
+	return 0;
+}
+
+/*
+ * Initialize UnTrusted Memory (UTM).
+ */
+int utm_init(struct utm *utm, unsigned long requested_pages)
+{
+
+	unsigned long aligned_pages = __roundup_pow_of_two(requested_pages);
+	phys_addr_t __maybe_unused device_phys_addr = 0;
+	unsigned long order = ilog2(aligned_pages);
+
+	utm->ptr = (void *)__get_free_pages(GFP_HIGHUSER, order);
+	if (!utm->ptr)
+		return -ENOMEM;
+
+	utm->order = order;
+	utm->size = aligned_pages << PAGE_SHIFT;
+	return 0;
+}
diff --git a/arch/riscv/kernel/keystone/riscv64.h b/arch/riscv/kernel/keystone/riscv64.h
new file mode 100644
index 0000000..9bc59f4
--- /dev/null
+++ b/arch/riscv/kernel/keystone/riscv64.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2022 The Regents of the University of California (Regents).
+ * Copyright (c) 2023 Univrsity of Tampere.
+ *
+ * Authors:
+ * Dayeol Lee <dayeol@berkeley.edu>
+ * Evgeny Pobachienko <evgenyp@berkeley.edu>
+ * Jarkko Sakkinen <jarkko.sakkinen@tuni.fi>
+ */
+
+#ifndef RISCV64_H
+#define RISCV64_H
+
+#include <asm/page.h>
+#include <linux/types.h>
+
+#define PRV_U 0
+#define PRV_S 1
+#define PRV_H 2
+#define PRV_M 3
+
+#define SATP32_MODE 0x80000000
+#define SATP32_ASID 0x7FC00000
+#define SATP32_PPN 0x003FFFFF
+#define SATP64_MODE 0xF000000000000000
+#define SATP64_ASID 0x0FFFF00000000000
+#define SATP64_PPN 0x00000FFFFFFFFFFF
+
+#define SATP_MODE_OFF 0
+#define SATP_MODE_SV32 1
+#define SATP_MODE_SV39 8
+#define SATP_MODE_SV48 9
+#define SATP_MODE_SV57 10
+#define SATP_MODE_SV64 11
+
+/* page table entry (PTE) fields */
+#define PTE_V 0x001 /* Valid */
+#define PTE_R 0x002 /* Read */
+#define PTE_W 0x004 /* Write */
+#define PTE_X 0x008 /* Execute */
+#define PTE_U 0x010 /* User */
+#define PTE_G 0x020 /* Global */
+#define PTE_A 0x040 /* Accessed */
+#define PTE_D 0x080 /* Dirty */
+#define PTE_SOFT 0x300 /* Reserved for Software */
+
+#define PTE_PPN_SHIFT 10
+
+#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
+
+#define MSTATUS_SD MSTATUS64_SD
+#define SSTATUS_SD SSTATUS64_SD
+#define RISCV_PGLEVEL_BITS 9
+
+#define RISCV_PGSHIFT 12
+#define RISCV_PGSIZE BIT(RISCV_PGSHIFT)
+
+#define MEGAPAGE_SIZE ((u64)(RISCV_PGSIZE << RISCV_PGLEVEL_BITS))
+#define SATP_MODE_CHOICE \
+	((0 & (~SATP64_MODE)) | (SATP_MODE_SV39 * (SATP64_MODE & (~(SATP64_MODE - 1)))))
+
+#define GIGAPAGE_SIZE (MEGAPAGE_SIZE << RISCV_PGLEVEL_BITS)
+
+static inline void flush_tlb(void)
+{
+	asm volatile("sfence.vma");
+}
+
+static inline pte_t pte_create(unsigned long ppn, int type)
+{
+	return __pte((ppn << PTE_PPN_SHIFT) | PTE_V | type);
+}
+
+static inline pte_t ptd_create(unsigned long ppn)
+{
+	return pte_create(ppn, PTE_V);
+}
+
+#endif /* RISCV64_H */
diff --git a/arch/riscv/kernel/keystone/sbi.h b/arch/riscv/kernel/keystone/sbi.h
new file mode 100644
index 0000000..a6f2b2d
--- /dev/null
+++ b/arch/riscv/kernel/keystone/sbi.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2022 The Regents of the University of California (Regents).
+ * Copyright (c) 2023 Univrsity of Tampere.
+ *
+ * Dayeol Lee <dayeol@berkeley.edu>
+ * Evgeny Pobachienko <evgenyp@berkeley.edu>
+ * Jarkko Sakkinen <jarkko.sakkinen@tuni.fi>
+ */
+
+#ifndef RISCV_KEYSTONE_SBI_H
+#define RISCV_KEYSTONE_SBI_H
+
+#include <uapi/asm/keystone.h>
+#include <asm/sbi.h>
+
+#define KEYSTONE_SBI_EXT_ID 0x08424b45
+#define SBI_SM_CREATE_ENCLAVE 2001
+#define SBI_SM_DESTROY_ENCLAVE 2002
+#define SBI_SM_RUN_ENCLAVE 2003
+#define SBI_SM_RESUME_ENCLAVE 2005
+#define SBI_SM_CREATE_LIBRARY_ENCLAVE 2006
+#define SBI_SM_DESTROY_LIBRARY_ENCLAVE 2007
+
+struct keystone_sbi_pregion {
+	u64 paddr;
+	size_t size;
+};
+
+struct keystone_sbi_create {
+	/* memory regions */
+	struct keystone_sbi_pregion epm_region;
+	struct keystone_sbi_pregion utm_region;
+
+	/* physical addresses */
+	u64 runtime_paddr;
+	u64 user_paddr;
+	u64 free_paddr;
+
+	/* parameters */
+	struct runtime_params_t params;
+	char library_name[NAME_MAX];
+};
+
+#define sbi_sm_call(id, value) sbi_ecall(KEYSTONE_SBI_EXT_ID, (id), (value), 0, 0, 0, 0, 0)
+
+#endif /* RISCV_KEYSTONE_SBI_H */