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 */