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