xref: /freebsd/sys/amd64/sgx/sgx.c (revision 0f9e06e18b1aeb7c9e7b650b2ce8815c06882aec)
12164af29SRuslan Bukin /*-
22164af29SRuslan Bukin  * Copyright (c) 2017 Ruslan Bukin <br@bsdpad.com>
32164af29SRuslan Bukin  * All rights reserved.
42164af29SRuslan Bukin  *
52164af29SRuslan Bukin  * This software was developed by BAE Systems, the University of Cambridge
62164af29SRuslan Bukin  * Computer Laboratory, and Memorial University under DARPA/AFRL contract
72164af29SRuslan Bukin  * FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent Computing
82164af29SRuslan Bukin  * (TC) research program.
92164af29SRuslan Bukin  *
102164af29SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
112164af29SRuslan Bukin  * modification, are permitted provided that the following conditions
122164af29SRuslan Bukin  * are met:
132164af29SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
142164af29SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
152164af29SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
162164af29SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
172164af29SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
182164af29SRuslan Bukin  *
192164af29SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
202164af29SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
212164af29SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
222164af29SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
232164af29SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
242164af29SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
252164af29SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
262164af29SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
272164af29SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
282164af29SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
292164af29SRuslan Bukin  * SUCH DAMAGE.
302164af29SRuslan Bukin  */
312164af29SRuslan Bukin 
322164af29SRuslan Bukin /*
332164af29SRuslan Bukin  * Design overview.
342164af29SRuslan Bukin  *
352164af29SRuslan Bukin  * The driver provides character device for mmap(2) and ioctl(2) system calls
362164af29SRuslan Bukin  * allowing user to manage isolated compartments ("enclaves") in user VA space.
372164af29SRuslan Bukin  *
382164af29SRuslan Bukin  * The driver duties is EPC pages management, enclave management, user data
392164af29SRuslan Bukin  * validation.
402164af29SRuslan Bukin  *
412164af29SRuslan Bukin  * This driver requires Intel SGX support from hardware.
422164af29SRuslan Bukin  *
432164af29SRuslan Bukin  * /dev/sgx:
442164af29SRuslan Bukin  *    .mmap:
452164af29SRuslan Bukin  *        sgx_mmap_single() allocates VM object with following pager
462164af29SRuslan Bukin  *        operations:
472164af29SRuslan Bukin  *              a) sgx_pg_ctor():
482164af29SRuslan Bukin  *                  VM object constructor does nothing
492164af29SRuslan Bukin  *              b) sgx_pg_dtor():
502164af29SRuslan Bukin  *                  VM object destructor destroys the SGX enclave associated
512164af29SRuslan Bukin  *                  with the object: it frees all the EPC pages allocated for
522164af29SRuslan Bukin  *                  enclave and removes the enclave.
532164af29SRuslan Bukin  *              c) sgx_pg_fault():
542164af29SRuslan Bukin  *                  VM object fault handler does nothing
552164af29SRuslan Bukin  *
562164af29SRuslan Bukin  *    .ioctl:
572164af29SRuslan Bukin  *        sgx_ioctl():
582164af29SRuslan Bukin  *               a) SGX_IOC_ENCLAVE_CREATE
592164af29SRuslan Bukin  *                   Adds Enclave SECS page: initial step of enclave creation.
602164af29SRuslan Bukin  *               b) SGX_IOC_ENCLAVE_ADD_PAGE
612164af29SRuslan Bukin  *                   Adds TCS, REG pages to the enclave.
622164af29SRuslan Bukin  *               c) SGX_IOC_ENCLAVE_INIT
632164af29SRuslan Bukin  *                   Finalizes enclave creation.
642164af29SRuslan Bukin  *
652164af29SRuslan Bukin  * Enclave lifecycle:
662164af29SRuslan Bukin  *          .-- ECREATE  -- Add SECS page
672164af29SRuslan Bukin  *   Kernel |   EADD     -- Add TCS, REG pages
682164af29SRuslan Bukin  *    space |   EEXTEND  -- Measure the page (take unique hash)
692164af29SRuslan Bukin  *    ENCLS |   EPA      -- Allocate version array page
702164af29SRuslan Bukin  *          '-- EINIT    -- Finalize enclave creation
712164af29SRuslan Bukin  *   User   .-- EENTER   -- Go to entry point of enclave
722164af29SRuslan Bukin  *    space |   EEXIT    -- Exit back to main application
732164af29SRuslan Bukin  *    ENCLU '-- ERESUME  -- Resume enclave execution (e.g. after exception)
742164af29SRuslan Bukin  *
752164af29SRuslan Bukin  * Enclave lifecycle from driver point of view:
762164af29SRuslan Bukin  *  1) User calls mmap() on /dev/sgx: we allocate a VM object
772164af29SRuslan Bukin  *  2) User calls ioctl SGX_IOC_ENCLAVE_CREATE: we look for the VM object
782164af29SRuslan Bukin  *     associated with user process created on step 1, create SECS physical
792164af29SRuslan Bukin  *     page and store it in enclave's VM object queue by special index
802164af29SRuslan Bukin  *     SGX_SECS_VM_OBJECT_INDEX.
812164af29SRuslan Bukin  *  3) User calls ioctl SGX_IOC_ENCLAVE_ADD_PAGE: we look for enclave created
822164af29SRuslan Bukin  *     on step 2, create TCS or REG physical page and map it to specified by
832164af29SRuslan Bukin  *     user address of enclave VM object.
842164af29SRuslan Bukin  *  4) User finalizes enclave creation with ioctl SGX_IOC_ENCLAVE_INIT call.
852164af29SRuslan Bukin  *  5) User can freely enter to and exit from enclave using ENCLU instructions
862164af29SRuslan Bukin  *     from userspace: the driver does nothing here.
872164af29SRuslan Bukin  *  6) User proceed munmap(2) system call (or the process with enclave dies):
882164af29SRuslan Bukin  *     we destroy the enclave associated with the object.
892164af29SRuslan Bukin  *
902164af29SRuslan Bukin  * EPC page types and their indexes in VM object queue:
912164af29SRuslan Bukin  *   - PT_SECS index is special and equals SGX_SECS_VM_OBJECT_INDEX (-1);
922164af29SRuslan Bukin  *   - PT_TCS and PT_REG indexes are specified by user in addr field of ioctl
932164af29SRuslan Bukin  *     request data and determined as follows:
942164af29SRuslan Bukin  *       pidx = OFF_TO_IDX(addp->addr - vmh->base);
952164af29SRuslan Bukin  *   - PT_VA index is special, created for PT_REG, PT_TCS and PT_SECS pages
962164af29SRuslan Bukin  *     and determined by formula:
972164af29SRuslan Bukin  *       va_page_idx = - SGX_VA_PAGES_OFFS - (page_idx / SGX_VA_PAGE_SLOTS);
982164af29SRuslan Bukin  *     PT_VA page can hold versions of up to 512 pages, and slot for each
992164af29SRuslan Bukin  *     page in PT_VA page is determined as follows:
1002164af29SRuslan Bukin  *       va_slot_idx = page_idx % SGX_VA_PAGE_SLOTS;
1012164af29SRuslan Bukin  *   - PT_TRIM is unused.
1022164af29SRuslan Bukin  *
1032164af29SRuslan Bukin  * Locking:
1042164af29SRuslan Bukin  *    SGX ENCLS set of instructions have limitations on concurrency:
1052164af29SRuslan Bukin  *    some instructions can't be executed same time on different CPUs.
1062164af29SRuslan Bukin  *    We use sc->mtx_encls lock around them to prevent concurrent execution.
1072164af29SRuslan Bukin  *    sc->mtx lock is used to manage list of created enclaves and the state of
1082164af29SRuslan Bukin  *    SGX driver.
1092164af29SRuslan Bukin  *
1102164af29SRuslan Bukin  * Eviction of EPC pages:
1112164af29SRuslan Bukin  *    Eviction support is not implemented in this driver, however the driver
1122164af29SRuslan Bukin  *    manages VA (version array) pages: it allocates a VA slot for each EPC
1132164af29SRuslan Bukin  *    page. This will be required for eviction support in future.
1142164af29SRuslan Bukin  *    VA pages and slots are currently unused.
1152164af29SRuslan Bukin  *
1162164af29SRuslan Bukin  * Intel® 64 and IA-32 Architectures Software Developer's Manual
1172164af29SRuslan Bukin  * https://software.intel.com/en-us/articles/intel-sdm
1182164af29SRuslan Bukin  */
1192164af29SRuslan Bukin 
1202164af29SRuslan Bukin #include <sys/cdefs.h>
1212164af29SRuslan Bukin __FBSDID("$FreeBSD$");
1222164af29SRuslan Bukin 
1232164af29SRuslan Bukin #include <sys/param.h>
1242164af29SRuslan Bukin #include <sys/systm.h>
1252164af29SRuslan Bukin #include <sys/ioccom.h>
1262164af29SRuslan Bukin #include <sys/malloc.h>
1272164af29SRuslan Bukin #include <sys/kernel.h>
1282164af29SRuslan Bukin #include <sys/lock.h>
1292164af29SRuslan Bukin #include <sys/mutex.h>
1302164af29SRuslan Bukin #include <sys/rwlock.h>
1312164af29SRuslan Bukin #include <sys/conf.h>
1322164af29SRuslan Bukin #include <sys/module.h>
1332164af29SRuslan Bukin #include <sys/proc.h>
1342164af29SRuslan Bukin #include <sys/vmem.h>
1352164af29SRuslan Bukin #include <sys/vmmeter.h>
1362164af29SRuslan Bukin 
1372164af29SRuslan Bukin #include <vm/vm.h>
1382164af29SRuslan Bukin #include <vm/vm_param.h>
1392164af29SRuslan Bukin #include <vm/vm_extern.h>
1402164af29SRuslan Bukin #include <vm/vm_kern.h>
1412164af29SRuslan Bukin #include <vm/vm_page.h>
1422164af29SRuslan Bukin #include <vm/vm_map.h>
1432164af29SRuslan Bukin #include <vm/vm_object.h>
1442164af29SRuslan Bukin #include <vm/vm_pager.h>
1452164af29SRuslan Bukin #include <vm/vm_phys.h>
1462164af29SRuslan Bukin #include <vm/vm_radix.h>
1472164af29SRuslan Bukin #include <vm/pmap.h>
1482164af29SRuslan Bukin 
1492164af29SRuslan Bukin #include <machine/md_var.h>
1502164af29SRuslan Bukin #include <machine/specialreg.h>
1512164af29SRuslan Bukin #include <machine/cpufunc.h>
1522164af29SRuslan Bukin #include <machine/sgx.h>
1532164af29SRuslan Bukin #include <machine/sgxreg.h>
1542164af29SRuslan Bukin 
1552164af29SRuslan Bukin #include <amd64/sgx/sgxvar.h>
1562164af29SRuslan Bukin 
1577dea7660SRuslan Bukin #define	SGX_DEBUG
1587dea7660SRuslan Bukin #undef	SGX_DEBUG
1592164af29SRuslan Bukin 
1607dea7660SRuslan Bukin #ifdef	SGX_DEBUG
1612164af29SRuslan Bukin #define	dprintf(fmt, ...)	printf(fmt, ##__VA_ARGS__)
1622164af29SRuslan Bukin #else
1632164af29SRuslan Bukin #define	dprintf(fmt, ...)
1642164af29SRuslan Bukin #endif
1652164af29SRuslan Bukin 
1662164af29SRuslan Bukin static struct cdev_pager_ops sgx_pg_ops;
1672164af29SRuslan Bukin struct sgx_softc sgx_sc;
1682164af29SRuslan Bukin 
1692164af29SRuslan Bukin static int
1702164af29SRuslan Bukin sgx_get_epc_page(struct sgx_softc *sc, struct epc_page **epc)
1712164af29SRuslan Bukin {
1722164af29SRuslan Bukin 	vmem_addr_t addr;
1732164af29SRuslan Bukin 	int i;
1742164af29SRuslan Bukin 
1752164af29SRuslan Bukin 	if (vmem_alloc(sc->vmem_epc, PAGE_SIZE, M_FIRSTFIT | M_NOWAIT,
1762164af29SRuslan Bukin 	    &addr) == 0) {
1772164af29SRuslan Bukin 		i = (addr - sc->epc_base) / PAGE_SIZE;
1782164af29SRuslan Bukin 		*epc = &sc->epc_pages[i];
1792164af29SRuslan Bukin 		return (0);
1802164af29SRuslan Bukin 	}
1812164af29SRuslan Bukin 
1822164af29SRuslan Bukin 	return (ENOMEM);
1832164af29SRuslan Bukin }
1842164af29SRuslan Bukin 
1852164af29SRuslan Bukin static void
1862164af29SRuslan Bukin sgx_put_epc_page(struct sgx_softc *sc, struct epc_page *epc)
1872164af29SRuslan Bukin {
1882164af29SRuslan Bukin 	vmem_addr_t addr;
1892164af29SRuslan Bukin 
1902164af29SRuslan Bukin 	if (epc == NULL)
1912164af29SRuslan Bukin 		return;
1922164af29SRuslan Bukin 
1932164af29SRuslan Bukin 	addr = (epc->index * PAGE_SIZE) + sc->epc_base;
1942164af29SRuslan Bukin 	vmem_free(sc->vmem_epc, addr, PAGE_SIZE);
1952164af29SRuslan Bukin }
1962164af29SRuslan Bukin 
1972164af29SRuslan Bukin static int
1982164af29SRuslan Bukin sgx_va_slot_init_by_index(struct sgx_softc *sc, vm_object_t object,
1992164af29SRuslan Bukin     uint64_t idx)
2002164af29SRuslan Bukin {
2012164af29SRuslan Bukin 	struct epc_page *epc;
2022164af29SRuslan Bukin 	vm_page_t page;
2032164af29SRuslan Bukin 	vm_page_t p;
2042164af29SRuslan Bukin 	int ret;
2052164af29SRuslan Bukin 
2062164af29SRuslan Bukin 	VM_OBJECT_ASSERT_WLOCKED(object);
2072164af29SRuslan Bukin 
2082164af29SRuslan Bukin 	p = vm_page_lookup(object, idx);
2092164af29SRuslan Bukin 	if (p == NULL) {
2102164af29SRuslan Bukin 		ret = sgx_get_epc_page(sc, &epc);
2112164af29SRuslan Bukin 		if (ret) {
2122164af29SRuslan Bukin 			dprintf("%s: No free EPC pages available.\n",
2132164af29SRuslan Bukin 			    __func__);
2142164af29SRuslan Bukin 			return (ret);
2152164af29SRuslan Bukin 		}
2162164af29SRuslan Bukin 
2172164af29SRuslan Bukin 		mtx_lock(&sc->mtx_encls);
2182164af29SRuslan Bukin 		sgx_epa((void *)epc->base);
2192164af29SRuslan Bukin 		mtx_unlock(&sc->mtx_encls);
2202164af29SRuslan Bukin 
2212164af29SRuslan Bukin 		page = PHYS_TO_VM_PAGE(epc->phys);
2222164af29SRuslan Bukin 
2232164af29SRuslan Bukin 		page->valid = VM_PAGE_BITS_ALL;
2240012f373SJeff Roberson 		vm_page_insert(page, object, idx);
2252164af29SRuslan Bukin 	}
2262164af29SRuslan Bukin 
2272164af29SRuslan Bukin 	return (0);
2282164af29SRuslan Bukin }
2292164af29SRuslan Bukin 
2302164af29SRuslan Bukin static int
2312164af29SRuslan Bukin sgx_va_slot_init(struct sgx_softc *sc,
2322164af29SRuslan Bukin     struct sgx_enclave *enclave,
2332164af29SRuslan Bukin     uint64_t addr)
2342164af29SRuslan Bukin {
2352164af29SRuslan Bukin 	vm_pindex_t pidx;
2362164af29SRuslan Bukin 	uint64_t va_page_idx;
2372164af29SRuslan Bukin 	uint64_t idx;
2382164af29SRuslan Bukin 	vm_object_t object;
2392164af29SRuslan Bukin 	int va_slot;
2402164af29SRuslan Bukin 	int ret;
2412164af29SRuslan Bukin 
2422164af29SRuslan Bukin 	object = enclave->object;
2432164af29SRuslan Bukin 
2442164af29SRuslan Bukin 	VM_OBJECT_ASSERT_WLOCKED(object);
2452164af29SRuslan Bukin 
2462164af29SRuslan Bukin 	pidx = OFF_TO_IDX(addr);
2472164af29SRuslan Bukin 
2482164af29SRuslan Bukin 	va_slot = pidx % SGX_VA_PAGE_SLOTS;
2492164af29SRuslan Bukin 	va_page_idx = pidx / SGX_VA_PAGE_SLOTS;
2502164af29SRuslan Bukin 	idx = - SGX_VA_PAGES_OFFS - va_page_idx;
2512164af29SRuslan Bukin 
2522164af29SRuslan Bukin 	ret = sgx_va_slot_init_by_index(sc, object, idx);
2532164af29SRuslan Bukin 
2542164af29SRuslan Bukin 	return (ret);
2552164af29SRuslan Bukin }
2562164af29SRuslan Bukin 
2572164af29SRuslan Bukin static int
2582164af29SRuslan Bukin sgx_mem_find(struct sgx_softc *sc, uint64_t addr,
2592164af29SRuslan Bukin     vm_map_entry_t *entry0, vm_object_t *object0)
2602164af29SRuslan Bukin {
2612164af29SRuslan Bukin 	vm_map_t map;
2622164af29SRuslan Bukin 	vm_map_entry_t entry;
2632164af29SRuslan Bukin 	vm_object_t object;
2642164af29SRuslan Bukin 
2652164af29SRuslan Bukin 	map = &curproc->p_vmspace->vm_map;
2662164af29SRuslan Bukin 
2672164af29SRuslan Bukin 	vm_map_lock_read(map);
2682164af29SRuslan Bukin 	if (!vm_map_lookup_entry(map, addr, &entry)) {
2692164af29SRuslan Bukin 		vm_map_unlock_read(map);
2702164af29SRuslan Bukin 		dprintf("%s: Can't find enclave.\n", __func__);
2712164af29SRuslan Bukin 		return (EINVAL);
2722164af29SRuslan Bukin 	}
2732164af29SRuslan Bukin 
2742164af29SRuslan Bukin 	object = entry->object.vm_object;
2752164af29SRuslan Bukin 	if (object == NULL || object->handle == NULL) {
2762164af29SRuslan Bukin 		vm_map_unlock_read(map);
2772164af29SRuslan Bukin 		return (EINVAL);
2782164af29SRuslan Bukin 	}
2792164af29SRuslan Bukin 
2802164af29SRuslan Bukin 	if (object->type != OBJT_MGTDEVICE ||
2812164af29SRuslan Bukin 	    object->un_pager.devp.ops != &sgx_pg_ops) {
2822164af29SRuslan Bukin 		vm_map_unlock_read(map);
2832164af29SRuslan Bukin 		return (EINVAL);
2842164af29SRuslan Bukin 	}
2852164af29SRuslan Bukin 
2862164af29SRuslan Bukin 	vm_object_reference(object);
2872164af29SRuslan Bukin 
2882164af29SRuslan Bukin 	*object0 = object;
2892164af29SRuslan Bukin 	*entry0 = entry;
2902164af29SRuslan Bukin 	vm_map_unlock_read(map);
2912164af29SRuslan Bukin 
2922164af29SRuslan Bukin 	return (0);
2932164af29SRuslan Bukin }
2942164af29SRuslan Bukin 
2952164af29SRuslan Bukin static int
2962164af29SRuslan Bukin sgx_enclave_find(struct sgx_softc *sc, uint64_t addr,
2972164af29SRuslan Bukin     struct sgx_enclave **encl)
2982164af29SRuslan Bukin {
2992164af29SRuslan Bukin 	struct sgx_vm_handle *vmh;
3002164af29SRuslan Bukin 	struct sgx_enclave *enclave;
3012164af29SRuslan Bukin 	vm_map_entry_t entry;
3022164af29SRuslan Bukin 	vm_object_t object;
3032164af29SRuslan Bukin 	int ret;
3042164af29SRuslan Bukin 
3052164af29SRuslan Bukin 	ret = sgx_mem_find(sc, addr, &entry, &object);
3062164af29SRuslan Bukin 	if (ret)
3072164af29SRuslan Bukin 		return (ret);
3082164af29SRuslan Bukin 
3092164af29SRuslan Bukin 	vmh = object->handle;
3102164af29SRuslan Bukin 	if (vmh == NULL) {
3112164af29SRuslan Bukin 		vm_object_deallocate(object);
3122164af29SRuslan Bukin 		return (EINVAL);
3132164af29SRuslan Bukin 	}
3142164af29SRuslan Bukin 
3152164af29SRuslan Bukin 	enclave = vmh->enclave;
3162164af29SRuslan Bukin 	if (enclave == NULL || enclave->object == NULL) {
3172164af29SRuslan Bukin 		vm_object_deallocate(object);
3182164af29SRuslan Bukin 		return (EINVAL);
3192164af29SRuslan Bukin 	}
3202164af29SRuslan Bukin 
3212164af29SRuslan Bukin 	*encl = enclave;
3222164af29SRuslan Bukin 
3232164af29SRuslan Bukin 	return (0);
3242164af29SRuslan Bukin }
3252164af29SRuslan Bukin 
3262164af29SRuslan Bukin static int
3272164af29SRuslan Bukin sgx_enclave_alloc(struct sgx_softc *sc, struct secs *secs,
3282164af29SRuslan Bukin     struct sgx_enclave **enclave0)
3292164af29SRuslan Bukin {
3302164af29SRuslan Bukin 	struct sgx_enclave *enclave;
3312164af29SRuslan Bukin 
3322164af29SRuslan Bukin 	enclave = malloc(sizeof(struct sgx_enclave),
3332164af29SRuslan Bukin 	    M_SGX, M_WAITOK | M_ZERO);
3342164af29SRuslan Bukin 
3352164af29SRuslan Bukin 	enclave->base = secs->base;
3362164af29SRuslan Bukin 	enclave->size = secs->size;
3372164af29SRuslan Bukin 
3382164af29SRuslan Bukin 	*enclave0 = enclave;
3392164af29SRuslan Bukin 
3402164af29SRuslan Bukin 	return (0);
3412164af29SRuslan Bukin }
3422164af29SRuslan Bukin 
3432164af29SRuslan Bukin static void
3442164af29SRuslan Bukin sgx_epc_page_remove(struct sgx_softc *sc,
3452164af29SRuslan Bukin     struct epc_page *epc)
3462164af29SRuslan Bukin {
3472164af29SRuslan Bukin 
3482164af29SRuslan Bukin 	mtx_lock(&sc->mtx_encls);
3492164af29SRuslan Bukin 	sgx_eremove((void *)epc->base);
3502164af29SRuslan Bukin 	mtx_unlock(&sc->mtx_encls);
3512164af29SRuslan Bukin }
3522164af29SRuslan Bukin 
3532164af29SRuslan Bukin static void
3542164af29SRuslan Bukin sgx_page_remove(struct sgx_softc *sc, vm_page_t p)
3552164af29SRuslan Bukin {
3562164af29SRuslan Bukin 	struct epc_page *epc;
3572164af29SRuslan Bukin 	vm_paddr_t pa;
3582164af29SRuslan Bukin 	uint64_t offs;
3592164af29SRuslan Bukin 
3600fd977b3SMark Johnston 	(void)vm_page_remove(p);
3612164af29SRuslan Bukin 
3622164af29SRuslan Bukin 	dprintf("%s: p->pidx %ld\n", __func__, p->pindex);
3632164af29SRuslan Bukin 
3642164af29SRuslan Bukin 	pa = VM_PAGE_TO_PHYS(p);
3652164af29SRuslan Bukin 	epc = &sc->epc_pages[0];
3662164af29SRuslan Bukin 	offs = (pa - epc->phys) / PAGE_SIZE;
3672164af29SRuslan Bukin 	epc = &sc->epc_pages[offs];
3682164af29SRuslan Bukin 
3692164af29SRuslan Bukin 	sgx_epc_page_remove(sc, epc);
3702164af29SRuslan Bukin 	sgx_put_epc_page(sc, epc);
3712164af29SRuslan Bukin }
3722164af29SRuslan Bukin 
3732164af29SRuslan Bukin static void
3742164af29SRuslan Bukin sgx_enclave_remove(struct sgx_softc *sc,
3752164af29SRuslan Bukin     struct sgx_enclave *enclave)
3762164af29SRuslan Bukin {
3772164af29SRuslan Bukin 	vm_object_t object;
3782164af29SRuslan Bukin 	vm_page_t p, p_secs, p_next;
3792164af29SRuslan Bukin 
3802164af29SRuslan Bukin 	mtx_lock(&sc->mtx);
3812164af29SRuslan Bukin 	TAILQ_REMOVE(&sc->enclaves, enclave, next);
3822164af29SRuslan Bukin 	mtx_unlock(&sc->mtx);
3832164af29SRuslan Bukin 
3842164af29SRuslan Bukin 	object = enclave->object;
3852164af29SRuslan Bukin 
3862164af29SRuslan Bukin 	VM_OBJECT_WLOCK(object);
3872164af29SRuslan Bukin 
3882164af29SRuslan Bukin 	/*
3892164af29SRuslan Bukin 	 * First remove all the pages except SECS,
3902164af29SRuslan Bukin 	 * then remove SECS page.
3912164af29SRuslan Bukin 	 */
392*0f9e06e1SJeff Roberson restart:
3932164af29SRuslan Bukin 	TAILQ_FOREACH_SAFE(p, &object->memq, listq, p_next) {
394*0f9e06e1SJeff Roberson 		if (p->pindex == SGX_SECS_VM_OBJECT_INDEX)
3952164af29SRuslan Bukin 			continue;
396*0f9e06e1SJeff Roberson 		if (vm_page_busy_acquire(p, VM_ALLOC_WAITFAIL) == 0)
397*0f9e06e1SJeff Roberson 			goto restart;
3982164af29SRuslan Bukin 		sgx_page_remove(sc, p);
3992164af29SRuslan Bukin 	}
400*0f9e06e1SJeff Roberson 	p_secs = vm_page_grab(object, SGX_SECS_VM_OBJECT_INDEX,
401*0f9e06e1SJeff Roberson 	    VM_ALLOC_NOCREAT);
4022164af29SRuslan Bukin 	/* Now remove SECS page */
4032164af29SRuslan Bukin 	if (p_secs != NULL)
4042164af29SRuslan Bukin 		sgx_page_remove(sc, p_secs);
4052164af29SRuslan Bukin 
4062164af29SRuslan Bukin 	KASSERT(TAILQ_EMPTY(&object->memq) == 1, ("not empty"));
4072164af29SRuslan Bukin 	KASSERT(object->resident_page_count == 0, ("count"));
4082164af29SRuslan Bukin 
4092164af29SRuslan Bukin 	VM_OBJECT_WUNLOCK(object);
4102164af29SRuslan Bukin }
4112164af29SRuslan Bukin 
4122164af29SRuslan Bukin static int
4132164af29SRuslan Bukin sgx_measure_page(struct sgx_softc *sc, struct epc_page *secs,
4142164af29SRuslan Bukin     struct epc_page *epc, uint16_t mrmask)
4152164af29SRuslan Bukin {
4162164af29SRuslan Bukin 	int i, j;
4172164af29SRuslan Bukin 	int ret;
4182164af29SRuslan Bukin 
4192164af29SRuslan Bukin 	mtx_lock(&sc->mtx_encls);
4202164af29SRuslan Bukin 
4212164af29SRuslan Bukin 	for (i = 0, j = 1; i < PAGE_SIZE; i += 0x100, j <<= 1) {
4222164af29SRuslan Bukin 		if (!(j & mrmask))
4232164af29SRuslan Bukin 			continue;
4242164af29SRuslan Bukin 
4252164af29SRuslan Bukin 		ret = sgx_eextend((void *)secs->base,
4262164af29SRuslan Bukin 		    (void *)(epc->base + i));
4272164af29SRuslan Bukin 		if (ret == SGX_EFAULT) {
4282164af29SRuslan Bukin 			mtx_unlock(&sc->mtx_encls);
4292164af29SRuslan Bukin 			return (ret);
4302164af29SRuslan Bukin 		}
4312164af29SRuslan Bukin 	}
4322164af29SRuslan Bukin 
4332164af29SRuslan Bukin 	mtx_unlock(&sc->mtx_encls);
4342164af29SRuslan Bukin 
4352164af29SRuslan Bukin 	return (0);
4362164af29SRuslan Bukin }
4372164af29SRuslan Bukin 
4382164af29SRuslan Bukin static int
4392164af29SRuslan Bukin sgx_secs_validate(struct sgx_softc *sc, struct secs *secs)
4402164af29SRuslan Bukin {
4412164af29SRuslan Bukin 	struct secs_attr *attr;
4422164af29SRuslan Bukin 	int i;
4432164af29SRuslan Bukin 
4442164af29SRuslan Bukin 	if (secs->size == 0)
4452164af29SRuslan Bukin 		return (EINVAL);
4462164af29SRuslan Bukin 
4472164af29SRuslan Bukin 	/* BASEADDR must be naturally aligned on an SECS.SIZE boundary. */
4482164af29SRuslan Bukin 	if (secs->base & (secs->size - 1))
4492164af29SRuslan Bukin 		return (EINVAL);
4502164af29SRuslan Bukin 
4512164af29SRuslan Bukin 	/* SECS.SIZE must be at least 2 pages. */
4522164af29SRuslan Bukin 	if (secs->size < 2 * PAGE_SIZE)
4532164af29SRuslan Bukin 		return (EINVAL);
4542164af29SRuslan Bukin 
4552164af29SRuslan Bukin 	if ((secs->size & (secs->size - 1)) != 0)
4562164af29SRuslan Bukin 		return (EINVAL);
4572164af29SRuslan Bukin 
4582164af29SRuslan Bukin 	attr = &secs->attributes;
4592164af29SRuslan Bukin 
4602164af29SRuslan Bukin 	if (attr->reserved1 != 0 ||
4612164af29SRuslan Bukin 	    attr->reserved2 != 0 ||
4622164af29SRuslan Bukin 	    attr->reserved3 != 0)
4632164af29SRuslan Bukin 		return (EINVAL);
4642164af29SRuslan Bukin 
4652164af29SRuslan Bukin 	for (i = 0; i < SECS_ATTR_RSV4_SIZE; i++)
4662164af29SRuslan Bukin 		if (attr->reserved4[i])
4672164af29SRuslan Bukin 			return (EINVAL);
4682164af29SRuslan Bukin 
4692164af29SRuslan Bukin 	/*
4702164af29SRuslan Bukin 	 * Intel® Software Guard Extensions Programming Reference
4712164af29SRuslan Bukin 	 * 6.7.2 Relevant Fields in Various Data Structures
4722164af29SRuslan Bukin 	 * 6.7.2.1 SECS.ATTRIBUTES.XFRM
4732164af29SRuslan Bukin 	 * XFRM[1:0] must be set to 0x3.
4742164af29SRuslan Bukin 	 */
4752164af29SRuslan Bukin 	if ((attr->xfrm & 0x3) != 0x3)
4762164af29SRuslan Bukin 		return (EINVAL);
4772164af29SRuslan Bukin 
4782164af29SRuslan Bukin 	if (!attr->mode64bit)
4792164af29SRuslan Bukin 		return (EINVAL);
4802164af29SRuslan Bukin 
4812164af29SRuslan Bukin 	if (secs->size > sc->enclave_size_max)
4822164af29SRuslan Bukin 		return (EINVAL);
4832164af29SRuslan Bukin 
4842164af29SRuslan Bukin 	for (i = 0; i < SECS_RSV1_SIZE; i++)
4852164af29SRuslan Bukin 		if (secs->reserved1[i])
4862164af29SRuslan Bukin 			return (EINVAL);
4872164af29SRuslan Bukin 
4882164af29SRuslan Bukin 	for (i = 0; i < SECS_RSV2_SIZE; i++)
4892164af29SRuslan Bukin 		if (secs->reserved2[i])
4902164af29SRuslan Bukin 			return (EINVAL);
4912164af29SRuslan Bukin 
4922164af29SRuslan Bukin 	for (i = 0; i < SECS_RSV3_SIZE; i++)
4932164af29SRuslan Bukin 		if (secs->reserved3[i])
4942164af29SRuslan Bukin 			return (EINVAL);
4952164af29SRuslan Bukin 
4962164af29SRuslan Bukin 	for (i = 0; i < SECS_RSV4_SIZE; i++)
4972164af29SRuslan Bukin 		if (secs->reserved4[i])
4982164af29SRuslan Bukin 			return (EINVAL);
4992164af29SRuslan Bukin 
5002164af29SRuslan Bukin 	return (0);
5012164af29SRuslan Bukin }
5022164af29SRuslan Bukin 
5032164af29SRuslan Bukin static int
5042164af29SRuslan Bukin sgx_tcs_validate(struct tcs *tcs)
5052164af29SRuslan Bukin {
5062164af29SRuslan Bukin 	int i;
5072164af29SRuslan Bukin 
5082164af29SRuslan Bukin 	if ((tcs->flags) ||
5092164af29SRuslan Bukin 	    (tcs->ossa & (PAGE_SIZE - 1)) ||
5102164af29SRuslan Bukin 	    (tcs->ofsbasgx & (PAGE_SIZE - 1)) ||
5112164af29SRuslan Bukin 	    (tcs->ogsbasgx & (PAGE_SIZE - 1)) ||
5122164af29SRuslan Bukin 	    ((tcs->fslimit & 0xfff) != 0xfff) ||
5132164af29SRuslan Bukin 	    ((tcs->gslimit & 0xfff) != 0xfff))
5142164af29SRuslan Bukin 		return (EINVAL);
5152164af29SRuslan Bukin 
5162164af29SRuslan Bukin 	for (i = 0; i < nitems(tcs->reserved3); i++)
5172164af29SRuslan Bukin 		if (tcs->reserved3[i])
5182164af29SRuslan Bukin 			return (EINVAL);
5192164af29SRuslan Bukin 
5202164af29SRuslan Bukin 	return (0);
5212164af29SRuslan Bukin }
5222164af29SRuslan Bukin 
5232164af29SRuslan Bukin static void
5242164af29SRuslan Bukin sgx_tcs_dump(struct sgx_softc *sc, struct tcs *t)
5252164af29SRuslan Bukin {
5262164af29SRuslan Bukin 
5272164af29SRuslan Bukin 	dprintf("t->flags %lx\n", t->flags);
5282164af29SRuslan Bukin 	dprintf("t->ossa %lx\n", t->ossa);
5292164af29SRuslan Bukin 	dprintf("t->cssa %x\n", t->cssa);
5302164af29SRuslan Bukin 	dprintf("t->nssa %x\n", t->nssa);
5312164af29SRuslan Bukin 	dprintf("t->oentry %lx\n", t->oentry);
5322164af29SRuslan Bukin 	dprintf("t->ofsbasgx %lx\n", t->ofsbasgx);
5332164af29SRuslan Bukin 	dprintf("t->ogsbasgx %lx\n", t->ogsbasgx);
5342164af29SRuslan Bukin 	dprintf("t->fslimit %x\n", t->fslimit);
5352164af29SRuslan Bukin 	dprintf("t->gslimit %x\n", t->gslimit);
5362164af29SRuslan Bukin }
5372164af29SRuslan Bukin 
5382164af29SRuslan Bukin static int
5392164af29SRuslan Bukin sgx_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
5402164af29SRuslan Bukin     vm_ooffset_t foff, struct ucred *cred, u_short *color)
5412164af29SRuslan Bukin {
5422164af29SRuslan Bukin 	struct sgx_vm_handle *vmh;
5432164af29SRuslan Bukin 
5442164af29SRuslan Bukin 	vmh = handle;
5452164af29SRuslan Bukin 	if (vmh == NULL) {
5462164af29SRuslan Bukin 		dprintf("%s: vmh not found.\n", __func__);
5472164af29SRuslan Bukin 		return (0);
5482164af29SRuslan Bukin 	}
5492164af29SRuslan Bukin 
5502164af29SRuslan Bukin 	dprintf("%s: vmh->base %lx foff 0x%lx size 0x%lx\n",
5512164af29SRuslan Bukin 	    __func__, vmh->base, foff, size);
5522164af29SRuslan Bukin 
5532164af29SRuslan Bukin 	return (0);
5542164af29SRuslan Bukin }
5552164af29SRuslan Bukin 
5562164af29SRuslan Bukin static void
5572164af29SRuslan Bukin sgx_pg_dtor(void *handle)
5582164af29SRuslan Bukin {
5592164af29SRuslan Bukin 	struct sgx_vm_handle *vmh;
5602164af29SRuslan Bukin 	struct sgx_softc *sc;
5612164af29SRuslan Bukin 
5622164af29SRuslan Bukin 	vmh = handle;
5632164af29SRuslan Bukin 	if (vmh == NULL) {
5642164af29SRuslan Bukin 		dprintf("%s: vmh not found.\n", __func__);
5652164af29SRuslan Bukin 		return;
5662164af29SRuslan Bukin 	}
5672164af29SRuslan Bukin 
5682164af29SRuslan Bukin 	sc = vmh->sc;
5692164af29SRuslan Bukin 	if (sc == NULL) {
5702164af29SRuslan Bukin 		dprintf("%s: sc is NULL\n", __func__);
5712164af29SRuslan Bukin 		return;
5722164af29SRuslan Bukin 	}
5732164af29SRuslan Bukin 
5742164af29SRuslan Bukin 	if (vmh->enclave == NULL) {
5752164af29SRuslan Bukin 		dprintf("%s: Enclave not found.\n", __func__);
5762164af29SRuslan Bukin 		return;
5772164af29SRuslan Bukin 	}
5782164af29SRuslan Bukin 
5792164af29SRuslan Bukin 	sgx_enclave_remove(sc, vmh->enclave);
5802164af29SRuslan Bukin 
5812164af29SRuslan Bukin 	free(vmh->enclave, M_SGX);
5822164af29SRuslan Bukin 	free(vmh, M_SGX);
5832164af29SRuslan Bukin }
5842164af29SRuslan Bukin 
5852164af29SRuslan Bukin static int
5862164af29SRuslan Bukin sgx_pg_fault(vm_object_t object, vm_ooffset_t offset,
5872164af29SRuslan Bukin     int prot, vm_page_t *mres)
5882164af29SRuslan Bukin {
5892164af29SRuslan Bukin 
5902164af29SRuslan Bukin 	/*
5912164af29SRuslan Bukin 	 * The purpose of this trivial handler is to handle the race
5922164af29SRuslan Bukin 	 * when user tries to access mmaped region before or during
5932164af29SRuslan Bukin 	 * enclave creation ioctl calls.
5942164af29SRuslan Bukin 	 */
5952164af29SRuslan Bukin 
5962164af29SRuslan Bukin 	dprintf("%s: offset 0x%lx\n", __func__, offset);
5972164af29SRuslan Bukin 
5982164af29SRuslan Bukin 	return (VM_PAGER_FAIL);
5992164af29SRuslan Bukin }
6002164af29SRuslan Bukin 
6012164af29SRuslan Bukin static struct cdev_pager_ops sgx_pg_ops = {
6022164af29SRuslan Bukin 	.cdev_pg_ctor = sgx_pg_ctor,
6032164af29SRuslan Bukin 	.cdev_pg_dtor = sgx_pg_dtor,
6042164af29SRuslan Bukin 	.cdev_pg_fault = sgx_pg_fault,
6052164af29SRuslan Bukin };
6062164af29SRuslan Bukin 
6072164af29SRuslan Bukin 
6082164af29SRuslan Bukin static void
6092164af29SRuslan Bukin sgx_insert_epc_page_by_index(vm_page_t page, vm_object_t object,
6102164af29SRuslan Bukin     vm_pindex_t pidx)
6112164af29SRuslan Bukin {
6122164af29SRuslan Bukin 
6132164af29SRuslan Bukin 	VM_OBJECT_ASSERT_WLOCKED(object);
6142164af29SRuslan Bukin 
6152164af29SRuslan Bukin 	page->valid = VM_PAGE_BITS_ALL;
6160012f373SJeff Roberson 	vm_page_insert(page, object, pidx);
6172164af29SRuslan Bukin }
6182164af29SRuslan Bukin 
6192164af29SRuslan Bukin static void
6202164af29SRuslan Bukin sgx_insert_epc_page(struct sgx_enclave *enclave,
6212164af29SRuslan Bukin     struct epc_page *epc, uint64_t addr)
6222164af29SRuslan Bukin {
6232164af29SRuslan Bukin 	vm_pindex_t pidx;
6242164af29SRuslan Bukin 	vm_page_t page;
6252164af29SRuslan Bukin 
6262164af29SRuslan Bukin 	VM_OBJECT_ASSERT_WLOCKED(enclave->object);
6272164af29SRuslan Bukin 
6282164af29SRuslan Bukin 	pidx = OFF_TO_IDX(addr);
6292164af29SRuslan Bukin 	page = PHYS_TO_VM_PAGE(epc->phys);
6302164af29SRuslan Bukin 
6312164af29SRuslan Bukin 	sgx_insert_epc_page_by_index(page, enclave->object, pidx);
6322164af29SRuslan Bukin }
6332164af29SRuslan Bukin 
6342164af29SRuslan Bukin static int
6352164af29SRuslan Bukin sgx_ioctl_create(struct sgx_softc *sc, struct sgx_enclave_create *param)
6362164af29SRuslan Bukin {
6372164af29SRuslan Bukin 	struct sgx_vm_handle *vmh;
6382164af29SRuslan Bukin 	vm_map_entry_t entry;
6392164af29SRuslan Bukin 	vm_page_t p;
6402164af29SRuslan Bukin 	struct page_info pginfo;
6412164af29SRuslan Bukin 	struct secinfo secinfo;
6422164af29SRuslan Bukin 	struct sgx_enclave *enclave;
6432164af29SRuslan Bukin 	struct epc_page *epc;
6442164af29SRuslan Bukin 	struct secs *secs;
6452164af29SRuslan Bukin 	vm_object_t object;
6462164af29SRuslan Bukin 	vm_page_t page;
6472164af29SRuslan Bukin 	int ret;
6482164af29SRuslan Bukin 
6492164af29SRuslan Bukin 	epc = NULL;
6502164af29SRuslan Bukin 	secs = NULL;
6512164af29SRuslan Bukin 	enclave = NULL;
6522164af29SRuslan Bukin 	object = NULL;
6532164af29SRuslan Bukin 
6542164af29SRuslan Bukin 	/* SGX Enclave Control Structure (SECS) */
6552164af29SRuslan Bukin 	secs = malloc(PAGE_SIZE, M_SGX, M_WAITOK | M_ZERO);
6562164af29SRuslan Bukin 	ret = copyin((void *)param->src, secs, sizeof(struct secs));
6572164af29SRuslan Bukin 	if (ret) {
6582164af29SRuslan Bukin 		dprintf("%s: Can't copy SECS.\n", __func__);
6592164af29SRuslan Bukin 		goto error;
6602164af29SRuslan Bukin 	}
6612164af29SRuslan Bukin 
6622164af29SRuslan Bukin 	ret = sgx_secs_validate(sc, secs);
6632164af29SRuslan Bukin 	if (ret) {
6642164af29SRuslan Bukin 		dprintf("%s: SECS validation failed.\n", __func__);
6652164af29SRuslan Bukin 		goto error;
6662164af29SRuslan Bukin 	}
6672164af29SRuslan Bukin 
6682164af29SRuslan Bukin 	ret = sgx_mem_find(sc, secs->base, &entry, &object);
6692164af29SRuslan Bukin 	if (ret) {
6702164af29SRuslan Bukin 		dprintf("%s: Can't find vm_map.\n", __func__);
6712164af29SRuslan Bukin 		goto error;
6722164af29SRuslan Bukin 	}
6732164af29SRuslan Bukin 
6742164af29SRuslan Bukin 	vmh = object->handle;
6752164af29SRuslan Bukin 	if (!vmh) {
6762164af29SRuslan Bukin 		dprintf("%s: Can't find vmh.\n", __func__);
6772164af29SRuslan Bukin 		ret = ENXIO;
6782164af29SRuslan Bukin 		goto error;
6792164af29SRuslan Bukin 	}
6802164af29SRuslan Bukin 
6812164af29SRuslan Bukin 	dprintf("%s: entry start %lx offset %lx\n",
6822164af29SRuslan Bukin 	    __func__, entry->start, entry->offset);
6832164af29SRuslan Bukin 	vmh->base = (entry->start - entry->offset);
6842164af29SRuslan Bukin 
6852164af29SRuslan Bukin 	ret = sgx_enclave_alloc(sc, secs, &enclave);
6862164af29SRuslan Bukin 	if (ret) {
6872164af29SRuslan Bukin 		dprintf("%s: Can't alloc enclave.\n", __func__);
6882164af29SRuslan Bukin 		goto error;
6892164af29SRuslan Bukin 	}
6902164af29SRuslan Bukin 	enclave->object = object;
6912164af29SRuslan Bukin 	enclave->vmh = vmh;
6922164af29SRuslan Bukin 
6932164af29SRuslan Bukin 	memset(&secinfo, 0, sizeof(struct secinfo));
6942164af29SRuslan Bukin 	memset(&pginfo, 0, sizeof(struct page_info));
6952164af29SRuslan Bukin 	pginfo.linaddr = 0;
6962164af29SRuslan Bukin 	pginfo.srcpge = (uint64_t)secs;
6972164af29SRuslan Bukin 	pginfo.secinfo = &secinfo;
6982164af29SRuslan Bukin 	pginfo.secs = 0;
6992164af29SRuslan Bukin 
7002164af29SRuslan Bukin 	ret = sgx_get_epc_page(sc, &epc);
7012164af29SRuslan Bukin 	if (ret) {
7022164af29SRuslan Bukin 		dprintf("%s: Failed to get free epc page.\n", __func__);
7032164af29SRuslan Bukin 		goto error;
7042164af29SRuslan Bukin 	}
7052164af29SRuslan Bukin 	enclave->secs_epc_page = epc;
7062164af29SRuslan Bukin 
7072164af29SRuslan Bukin 	VM_OBJECT_WLOCK(object);
7082164af29SRuslan Bukin 	p = vm_page_lookup(object, SGX_SECS_VM_OBJECT_INDEX);
7092164af29SRuslan Bukin 	if (p) {
7102164af29SRuslan Bukin 		VM_OBJECT_WUNLOCK(object);
7112164af29SRuslan Bukin 		/* SECS page already added. */
7122164af29SRuslan Bukin 		ret = ENXIO;
7132164af29SRuslan Bukin 		goto error;
7142164af29SRuslan Bukin 	}
7152164af29SRuslan Bukin 
7162164af29SRuslan Bukin 	ret = sgx_va_slot_init_by_index(sc, object,
7172164af29SRuslan Bukin 	    - SGX_VA_PAGES_OFFS - SGX_SECS_VM_OBJECT_INDEX);
7182164af29SRuslan Bukin 	if (ret) {
7192164af29SRuslan Bukin 		VM_OBJECT_WUNLOCK(object);
7202164af29SRuslan Bukin 		dprintf("%s: Can't init va slot.\n", __func__);
7212164af29SRuslan Bukin 		goto error;
7222164af29SRuslan Bukin 	}
7232164af29SRuslan Bukin 
7242164af29SRuslan Bukin 	mtx_lock(&sc->mtx);
7252164af29SRuslan Bukin 	if ((sc->state & SGX_STATE_RUNNING) == 0) {
7262164af29SRuslan Bukin 		mtx_unlock(&sc->mtx);
7272164af29SRuslan Bukin 		/* Remove VA page that was just created for SECS page. */
728*0f9e06e1SJeff Roberson 		p = vm_page_grab(enclave->object,
729*0f9e06e1SJeff Roberson 		    - SGX_VA_PAGES_OFFS - SGX_SECS_VM_OBJECT_INDEX,
730*0f9e06e1SJeff Roberson 		    VM_ALLOC_NOCREAT);
7312164af29SRuslan Bukin 		sgx_page_remove(sc, p);
7322164af29SRuslan Bukin 		VM_OBJECT_WUNLOCK(object);
7332164af29SRuslan Bukin 		goto error;
7342164af29SRuslan Bukin 	}
7352164af29SRuslan Bukin 	mtx_lock(&sc->mtx_encls);
7362164af29SRuslan Bukin 	ret = sgx_ecreate(&pginfo, (void *)epc->base);
7372164af29SRuslan Bukin 	mtx_unlock(&sc->mtx_encls);
7382164af29SRuslan Bukin 	if (ret == SGX_EFAULT) {
7392164af29SRuslan Bukin 		dprintf("%s: gp fault\n", __func__);
7402164af29SRuslan Bukin 		mtx_unlock(&sc->mtx);
7412164af29SRuslan Bukin 		/* Remove VA page that was just created for SECS page. */
742*0f9e06e1SJeff Roberson 		p = vm_page_grab(enclave->object,
743*0f9e06e1SJeff Roberson 		    - SGX_VA_PAGES_OFFS - SGX_SECS_VM_OBJECT_INDEX,
744*0f9e06e1SJeff Roberson 		    VM_ALLOC_NOCREAT);
7452164af29SRuslan Bukin 		sgx_page_remove(sc, p);
7462164af29SRuslan Bukin 		VM_OBJECT_WUNLOCK(object);
7472164af29SRuslan Bukin 		goto error;
7482164af29SRuslan Bukin 	}
7492164af29SRuslan Bukin 
7502164af29SRuslan Bukin 	TAILQ_INSERT_TAIL(&sc->enclaves, enclave, next);
7512164af29SRuslan Bukin 	mtx_unlock(&sc->mtx);
7522164af29SRuslan Bukin 
7532164af29SRuslan Bukin 	vmh->enclave = enclave;
7542164af29SRuslan Bukin 
7552164af29SRuslan Bukin 	page = PHYS_TO_VM_PAGE(epc->phys);
7562164af29SRuslan Bukin 	sgx_insert_epc_page_by_index(page, enclave->object,
7572164af29SRuslan Bukin 	    SGX_SECS_VM_OBJECT_INDEX);
7582164af29SRuslan Bukin 
7592164af29SRuslan Bukin 	VM_OBJECT_WUNLOCK(object);
7602164af29SRuslan Bukin 
7612164af29SRuslan Bukin 	/* Release the reference. */
7622164af29SRuslan Bukin 	vm_object_deallocate(object);
7632164af29SRuslan Bukin 
7642164af29SRuslan Bukin 	free(secs, M_SGX);
7652164af29SRuslan Bukin 
7662164af29SRuslan Bukin 	return (0);
7672164af29SRuslan Bukin 
7682164af29SRuslan Bukin error:
7692164af29SRuslan Bukin 	free(secs, M_SGX);
7702164af29SRuslan Bukin 	sgx_put_epc_page(sc, epc);
7712164af29SRuslan Bukin 	free(enclave, M_SGX);
7722164af29SRuslan Bukin 	vm_object_deallocate(object);
7732164af29SRuslan Bukin 
7742164af29SRuslan Bukin 	return (ret);
7752164af29SRuslan Bukin }
7762164af29SRuslan Bukin 
7772164af29SRuslan Bukin static int
7782164af29SRuslan Bukin sgx_ioctl_add_page(struct sgx_softc *sc,
7792164af29SRuslan Bukin     struct sgx_enclave_add_page *addp)
7802164af29SRuslan Bukin {
7812164af29SRuslan Bukin 	struct epc_page *secs_epc_page;
7822164af29SRuslan Bukin 	struct sgx_enclave *enclave;
7832164af29SRuslan Bukin 	struct sgx_vm_handle *vmh;
7842164af29SRuslan Bukin 	struct epc_page *epc;
7852164af29SRuslan Bukin 	struct page_info pginfo;
7862164af29SRuslan Bukin 	struct secinfo secinfo;
7872164af29SRuslan Bukin 	vm_object_t object;
7882164af29SRuslan Bukin 	void *tmp_vaddr;
7892164af29SRuslan Bukin 	uint64_t page_type;
7902164af29SRuslan Bukin 	struct tcs *t;
7912164af29SRuslan Bukin 	uint64_t addr;
7922164af29SRuslan Bukin 	uint64_t pidx;
7932164af29SRuslan Bukin 	vm_page_t p;
7942164af29SRuslan Bukin 	int ret;
7952164af29SRuslan Bukin 
7962164af29SRuslan Bukin 	tmp_vaddr = NULL;
7972164af29SRuslan Bukin 	epc = NULL;
7982164af29SRuslan Bukin 	object = NULL;
7992164af29SRuslan Bukin 
8002164af29SRuslan Bukin 	/* Find and get reference to VM object. */
8012164af29SRuslan Bukin 	ret = sgx_enclave_find(sc, addp->addr, &enclave);
8022164af29SRuslan Bukin 	if (ret) {
8032164af29SRuslan Bukin 		dprintf("%s: Failed to find enclave.\n", __func__);
8042164af29SRuslan Bukin 		goto error;
8052164af29SRuslan Bukin 	}
8062164af29SRuslan Bukin 
8072164af29SRuslan Bukin 	object = enclave->object;
8082164af29SRuslan Bukin 	KASSERT(object != NULL, ("vm object is NULL\n"));
8092164af29SRuslan Bukin 	vmh = object->handle;
8102164af29SRuslan Bukin 
8112164af29SRuslan Bukin 	ret = sgx_get_epc_page(sc, &epc);
8122164af29SRuslan Bukin 	if (ret) {
8132164af29SRuslan Bukin 		dprintf("%s: Failed to get free epc page.\n", __func__);
8142164af29SRuslan Bukin 		goto error;
8152164af29SRuslan Bukin 	}
8162164af29SRuslan Bukin 
8172164af29SRuslan Bukin 	memset(&secinfo, 0, sizeof(struct secinfo));
8182164af29SRuslan Bukin 	ret = copyin((void *)addp->secinfo, &secinfo,
8192164af29SRuslan Bukin 	    sizeof(struct secinfo));
8202164af29SRuslan Bukin 	if (ret) {
8212164af29SRuslan Bukin 		dprintf("%s: Failed to copy secinfo.\n", __func__);
8222164af29SRuslan Bukin 		goto error;
8232164af29SRuslan Bukin 	}
8242164af29SRuslan Bukin 
8252164af29SRuslan Bukin 	tmp_vaddr = malloc(PAGE_SIZE, M_SGX, M_WAITOK | M_ZERO);
8262164af29SRuslan Bukin 	ret = copyin((void *)addp->src, tmp_vaddr, PAGE_SIZE);
8272164af29SRuslan Bukin 	if (ret) {
8282164af29SRuslan Bukin 		dprintf("%s: Failed to copy page.\n", __func__);
8292164af29SRuslan Bukin 		goto error;
8302164af29SRuslan Bukin 	}
8312164af29SRuslan Bukin 
8322164af29SRuslan Bukin 	page_type = (secinfo.flags & SECINFO_FLAGS_PT_M) >>
8332164af29SRuslan Bukin 	    SECINFO_FLAGS_PT_S;
8342164af29SRuslan Bukin 	if (page_type != SGX_PT_TCS && page_type != SGX_PT_REG) {
8352164af29SRuslan Bukin 		dprintf("%s: page can't be added.\n", __func__);
8362164af29SRuslan Bukin 		goto error;
8372164af29SRuslan Bukin 	}
8382164af29SRuslan Bukin 	if (page_type == SGX_PT_TCS) {
8392164af29SRuslan Bukin 		t = (struct tcs *)tmp_vaddr;
8402164af29SRuslan Bukin 		ret = sgx_tcs_validate(t);
8412164af29SRuslan Bukin 		if (ret) {
8422164af29SRuslan Bukin 			dprintf("%s: TCS page validation failed.\n",
8432164af29SRuslan Bukin 			    __func__);
8442164af29SRuslan Bukin 			goto error;
8452164af29SRuslan Bukin 		}
8462164af29SRuslan Bukin 		sgx_tcs_dump(sc, t);
8472164af29SRuslan Bukin 	}
8482164af29SRuslan Bukin 
8492164af29SRuslan Bukin 	addr = (addp->addr - vmh->base);
8502164af29SRuslan Bukin 	pidx = OFF_TO_IDX(addr);
8512164af29SRuslan Bukin 
8522164af29SRuslan Bukin 	VM_OBJECT_WLOCK(object);
8532164af29SRuslan Bukin 	p = vm_page_lookup(object, pidx);
8542164af29SRuslan Bukin 	if (p) {
8552164af29SRuslan Bukin 		VM_OBJECT_WUNLOCK(object);
8562164af29SRuslan Bukin 		/* Page already added. */
8572164af29SRuslan Bukin 		ret = ENXIO;
8582164af29SRuslan Bukin 		goto error;
8592164af29SRuslan Bukin 	}
8602164af29SRuslan Bukin 
8612164af29SRuslan Bukin 	ret = sgx_va_slot_init(sc, enclave, addr);
8622164af29SRuslan Bukin 	if (ret) {
8632164af29SRuslan Bukin 		VM_OBJECT_WUNLOCK(object);
8642164af29SRuslan Bukin 		dprintf("%s: Can't init va slot.\n", __func__);
8652164af29SRuslan Bukin 		goto error;
8662164af29SRuslan Bukin 	}
8672164af29SRuslan Bukin 
8682164af29SRuslan Bukin 	secs_epc_page = enclave->secs_epc_page;
8692164af29SRuslan Bukin 	memset(&pginfo, 0, sizeof(struct page_info));
8702164af29SRuslan Bukin 	pginfo.linaddr = (uint64_t)addp->addr;
8712164af29SRuslan Bukin 	pginfo.srcpge = (uint64_t)tmp_vaddr;
8722164af29SRuslan Bukin 	pginfo.secinfo = &secinfo;
8732164af29SRuslan Bukin 	pginfo.secs = (uint64_t)secs_epc_page->base;
8742164af29SRuslan Bukin 
8752164af29SRuslan Bukin 	mtx_lock(&sc->mtx_encls);
8762164af29SRuslan Bukin 	ret = sgx_eadd(&pginfo, (void *)epc->base);
8772164af29SRuslan Bukin 	if (ret == SGX_EFAULT) {
8782164af29SRuslan Bukin 		dprintf("%s: gp fault on eadd\n", __func__);
8792164af29SRuslan Bukin 		mtx_unlock(&sc->mtx_encls);
8802164af29SRuslan Bukin 		VM_OBJECT_WUNLOCK(object);
8812164af29SRuslan Bukin 		goto error;
8822164af29SRuslan Bukin 	}
8832164af29SRuslan Bukin 	mtx_unlock(&sc->mtx_encls);
8842164af29SRuslan Bukin 
8852164af29SRuslan Bukin 	ret = sgx_measure_page(sc, enclave->secs_epc_page, epc, addp->mrmask);
8862164af29SRuslan Bukin 	if (ret == SGX_EFAULT) {
8872164af29SRuslan Bukin 		dprintf("%s: gp fault on eextend\n", __func__);
8882164af29SRuslan Bukin 		sgx_epc_page_remove(sc, epc);
8892164af29SRuslan Bukin 		VM_OBJECT_WUNLOCK(object);
8902164af29SRuslan Bukin 		goto error;
8912164af29SRuslan Bukin 	}
8922164af29SRuslan Bukin 
8932164af29SRuslan Bukin 	sgx_insert_epc_page(enclave, epc, addr);
8942164af29SRuslan Bukin 
8952164af29SRuslan Bukin 	VM_OBJECT_WUNLOCK(object);
8962164af29SRuslan Bukin 
8972164af29SRuslan Bukin 	/* Release the reference. */
8982164af29SRuslan Bukin 	vm_object_deallocate(object);
8992164af29SRuslan Bukin 
9002164af29SRuslan Bukin 	free(tmp_vaddr, M_SGX);
9012164af29SRuslan Bukin 
9022164af29SRuslan Bukin 	return (0);
9032164af29SRuslan Bukin 
9042164af29SRuslan Bukin error:
9052164af29SRuslan Bukin 	free(tmp_vaddr, M_SGX);
9062164af29SRuslan Bukin 	sgx_put_epc_page(sc, epc);
9072164af29SRuslan Bukin 	vm_object_deallocate(object);
9082164af29SRuslan Bukin 
9092164af29SRuslan Bukin 	return (ret);
9102164af29SRuslan Bukin }
9112164af29SRuslan Bukin 
9122164af29SRuslan Bukin static int
9132164af29SRuslan Bukin sgx_ioctl_init(struct sgx_softc *sc, struct sgx_enclave_init *initp)
9142164af29SRuslan Bukin {
9152164af29SRuslan Bukin 	struct epc_page *secs_epc_page;
9162164af29SRuslan Bukin 	struct sgx_enclave *enclave;
9172164af29SRuslan Bukin 	struct thread *td;
9182164af29SRuslan Bukin 	void *tmp_vaddr;
9192164af29SRuslan Bukin 	void *einittoken;
9202164af29SRuslan Bukin 	void *sigstruct;
9212164af29SRuslan Bukin 	vm_object_t object;
9222164af29SRuslan Bukin 	int retry;
9232164af29SRuslan Bukin 	int ret;
9242164af29SRuslan Bukin 
9252164af29SRuslan Bukin 	td = curthread;
9262164af29SRuslan Bukin 	tmp_vaddr = NULL;
9272164af29SRuslan Bukin 	object = NULL;
9282164af29SRuslan Bukin 
9292164af29SRuslan Bukin 	dprintf("%s: addr %lx, sigstruct %lx, einittoken %lx\n",
9302164af29SRuslan Bukin 	    __func__, initp->addr, initp->sigstruct, initp->einittoken);
9312164af29SRuslan Bukin 
9322164af29SRuslan Bukin 	/* Find and get reference to VM object. */
9332164af29SRuslan Bukin 	ret = sgx_enclave_find(sc, initp->addr, &enclave);
9342164af29SRuslan Bukin 	if (ret) {
9352164af29SRuslan Bukin 		dprintf("%s: Failed to find enclave.\n", __func__);
9362164af29SRuslan Bukin 		goto error;
9372164af29SRuslan Bukin 	}
9382164af29SRuslan Bukin 
9392164af29SRuslan Bukin 	object = enclave->object;
9402164af29SRuslan Bukin 
9412164af29SRuslan Bukin 	tmp_vaddr = malloc(PAGE_SIZE, M_SGX, M_WAITOK | M_ZERO);
9422164af29SRuslan Bukin 	sigstruct = tmp_vaddr;
9432164af29SRuslan Bukin 	einittoken = (void *)((uint64_t)sigstruct + PAGE_SIZE / 2);
9442164af29SRuslan Bukin 
9452164af29SRuslan Bukin 	ret = copyin((void *)initp->sigstruct, sigstruct,
9462164af29SRuslan Bukin 	    SGX_SIGSTRUCT_SIZE);
9472164af29SRuslan Bukin 	if (ret) {
9482164af29SRuslan Bukin 		dprintf("%s: Failed to copy SIGSTRUCT page.\n", __func__);
9492164af29SRuslan Bukin 		goto error;
9502164af29SRuslan Bukin 	}
9512164af29SRuslan Bukin 
9522164af29SRuslan Bukin 	ret = copyin((void *)initp->einittoken, einittoken,
9532164af29SRuslan Bukin 	    SGX_EINITTOKEN_SIZE);
9542164af29SRuslan Bukin 	if (ret) {
9552164af29SRuslan Bukin 		dprintf("%s: Failed to copy EINITTOKEN page.\n", __func__);
9562164af29SRuslan Bukin 		goto error;
9572164af29SRuslan Bukin 	}
9582164af29SRuslan Bukin 
9592164af29SRuslan Bukin 	secs_epc_page = enclave->secs_epc_page;
9602164af29SRuslan Bukin 	retry = 16;
9612164af29SRuslan Bukin 	do {
9622164af29SRuslan Bukin 		mtx_lock(&sc->mtx_encls);
9632164af29SRuslan Bukin 		ret = sgx_einit(sigstruct, (void *)secs_epc_page->base,
9642164af29SRuslan Bukin 		    einittoken);
9652164af29SRuslan Bukin 		mtx_unlock(&sc->mtx_encls);
9662164af29SRuslan Bukin 		dprintf("%s: sgx_einit returned %d\n", __func__, ret);
9672164af29SRuslan Bukin 	} while (ret == SGX_UNMASKED_EVENT && retry--);
9682164af29SRuslan Bukin 
9692164af29SRuslan Bukin 	if (ret) {
9702164af29SRuslan Bukin 		dprintf("%s: Failed init enclave: %d\n", __func__, ret);
9712164af29SRuslan Bukin 		td->td_retval[0] = ret;
9722164af29SRuslan Bukin 		ret = 0;
9732164af29SRuslan Bukin 	}
9742164af29SRuslan Bukin 
9752164af29SRuslan Bukin error:
9762164af29SRuslan Bukin 	free(tmp_vaddr, M_SGX);
9772164af29SRuslan Bukin 
9782164af29SRuslan Bukin 	/* Release the reference. */
9792164af29SRuslan Bukin 	vm_object_deallocate(object);
9802164af29SRuslan Bukin 
9812164af29SRuslan Bukin 	return (ret);
9822164af29SRuslan Bukin }
9832164af29SRuslan Bukin 
9842164af29SRuslan Bukin static int
9852164af29SRuslan Bukin sgx_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
9862164af29SRuslan Bukin     struct thread *td)
9872164af29SRuslan Bukin {
9882164af29SRuslan Bukin 	struct sgx_enclave_add_page *addp;
9892164af29SRuslan Bukin 	struct sgx_enclave_create *param;
9902164af29SRuslan Bukin 	struct sgx_enclave_init *initp;
9912164af29SRuslan Bukin 	struct sgx_softc *sc;
9922164af29SRuslan Bukin 	int ret;
9932164af29SRuslan Bukin 	int len;
9942164af29SRuslan Bukin 
9952164af29SRuslan Bukin 	sc = &sgx_sc;
9962164af29SRuslan Bukin 
9972164af29SRuslan Bukin 	len = IOCPARM_LEN(cmd);
9982164af29SRuslan Bukin 
9992164af29SRuslan Bukin 	dprintf("%s: cmd %lx, addr %lx, len %d\n",
10002164af29SRuslan Bukin 	    __func__, cmd, (uint64_t)addr, len);
10012164af29SRuslan Bukin 
10022164af29SRuslan Bukin 	if (len > SGX_IOCTL_MAX_DATA_LEN)
10032164af29SRuslan Bukin 		return (EINVAL);
10042164af29SRuslan Bukin 
10052164af29SRuslan Bukin 	switch (cmd) {
10062164af29SRuslan Bukin 	case SGX_IOC_ENCLAVE_CREATE:
10072164af29SRuslan Bukin 		param = (struct sgx_enclave_create *)addr;
10082164af29SRuslan Bukin 		ret = sgx_ioctl_create(sc, param);
10092164af29SRuslan Bukin 		break;
10102164af29SRuslan Bukin 	case SGX_IOC_ENCLAVE_ADD_PAGE:
10112164af29SRuslan Bukin 		addp = (struct sgx_enclave_add_page *)addr;
10122164af29SRuslan Bukin 		ret = sgx_ioctl_add_page(sc, addp);
10132164af29SRuslan Bukin 		break;
10142164af29SRuslan Bukin 	case SGX_IOC_ENCLAVE_INIT:
10152164af29SRuslan Bukin 		initp = (struct sgx_enclave_init *)addr;
10162164af29SRuslan Bukin 		ret = sgx_ioctl_init(sc, initp);
10172164af29SRuslan Bukin 		break;
10182164af29SRuslan Bukin 	default:
10192164af29SRuslan Bukin 		return (EINVAL);
10202164af29SRuslan Bukin 	}
10212164af29SRuslan Bukin 
10222164af29SRuslan Bukin 	return (ret);
10232164af29SRuslan Bukin }
10242164af29SRuslan Bukin 
10252164af29SRuslan Bukin static int
10262164af29SRuslan Bukin sgx_mmap_single(struct cdev *cdev, vm_ooffset_t *offset,
10272164af29SRuslan Bukin     vm_size_t mapsize, struct vm_object **objp, int nprot)
10282164af29SRuslan Bukin {
10292164af29SRuslan Bukin 	struct sgx_vm_handle *vmh;
10302164af29SRuslan Bukin 	struct sgx_softc *sc;
10312164af29SRuslan Bukin 
10322164af29SRuslan Bukin 	sc = &sgx_sc;
10332164af29SRuslan Bukin 
10342164af29SRuslan Bukin 	dprintf("%s: mapsize 0x%lx, offset %lx\n",
10352164af29SRuslan Bukin 	    __func__, mapsize, *offset);
10362164af29SRuslan Bukin 
10372164af29SRuslan Bukin 	vmh = malloc(sizeof(struct sgx_vm_handle),
10382164af29SRuslan Bukin 	    M_SGX, M_WAITOK | M_ZERO);
10392164af29SRuslan Bukin 	vmh->sc = sc;
10402164af29SRuslan Bukin 	vmh->size = mapsize;
10412164af29SRuslan Bukin 	vmh->mem = cdev_pager_allocate(vmh, OBJT_MGTDEVICE, &sgx_pg_ops,
10422164af29SRuslan Bukin 	    mapsize, nprot, *offset, NULL);
10432164af29SRuslan Bukin 	if (vmh->mem == NULL) {
10442164af29SRuslan Bukin 		free(vmh, M_SGX);
10452164af29SRuslan Bukin 		return (ENOMEM);
10462164af29SRuslan Bukin 	}
10472164af29SRuslan Bukin 
10482164af29SRuslan Bukin 	VM_OBJECT_WLOCK(vmh->mem);
10492164af29SRuslan Bukin 	vm_object_set_flag(vmh->mem, OBJ_PG_DTOR);
10502164af29SRuslan Bukin 	VM_OBJECT_WUNLOCK(vmh->mem);
10512164af29SRuslan Bukin 
10522164af29SRuslan Bukin 	*objp = vmh->mem;
10532164af29SRuslan Bukin 
10542164af29SRuslan Bukin 	return (0);
10552164af29SRuslan Bukin }
10562164af29SRuslan Bukin 
10572164af29SRuslan Bukin static struct cdevsw sgx_cdevsw = {
10582164af29SRuslan Bukin 	.d_version =		D_VERSION,
10592164af29SRuslan Bukin 	.d_ioctl =		sgx_ioctl,
10602164af29SRuslan Bukin 	.d_mmap_single =	sgx_mmap_single,
10612164af29SRuslan Bukin 	.d_name =		"Intel SGX",
10622164af29SRuslan Bukin };
10632164af29SRuslan Bukin 
10642164af29SRuslan Bukin static int
10652164af29SRuslan Bukin sgx_get_epc_area(struct sgx_softc *sc)
10662164af29SRuslan Bukin {
10672164af29SRuslan Bukin 	vm_offset_t epc_base_vaddr;
10682164af29SRuslan Bukin 	u_int cp[4];
10692164af29SRuslan Bukin 	int error;
10702164af29SRuslan Bukin 	int i;
10712164af29SRuslan Bukin 
10722164af29SRuslan Bukin 	cpuid_count(SGX_CPUID, 0x2, cp);
10732164af29SRuslan Bukin 
10742164af29SRuslan Bukin 	sc->epc_base = ((uint64_t)(cp[1] & 0xfffff) << 32) +
10752164af29SRuslan Bukin 	    (cp[0] & 0xfffff000);
10762164af29SRuslan Bukin 	sc->epc_size = ((uint64_t)(cp[3] & 0xfffff) << 32) +
10772164af29SRuslan Bukin 	    (cp[2] & 0xfffff000);
10782164af29SRuslan Bukin 	sc->npages = sc->epc_size / SGX_PAGE_SIZE;
10792164af29SRuslan Bukin 
10803caad0b8SMarcin Wojtas 	if (sc->epc_size == 0 || sc->epc_base == 0) {
10813caad0b8SMarcin Wojtas 		printf("%s: Incorrect EPC data: EPC base %lx, size %lu\n",
10823caad0b8SMarcin Wojtas 		    __func__, sc->epc_base, sc->epc_size);
10833caad0b8SMarcin Wojtas 		return (EINVAL);
10843caad0b8SMarcin Wojtas 	}
10853caad0b8SMarcin Wojtas 
10862164af29SRuslan Bukin 	if (cp[3] & 0xffff)
10872164af29SRuslan Bukin 		sc->enclave_size_max = (1 << ((cp[3] >> 8) & 0xff));
10882164af29SRuslan Bukin 	else
10892164af29SRuslan Bukin 		sc->enclave_size_max = SGX_ENCL_SIZE_MAX_DEF;
10902164af29SRuslan Bukin 
10912164af29SRuslan Bukin 	epc_base_vaddr = (vm_offset_t)pmap_mapdev_attr(sc->epc_base,
10922164af29SRuslan Bukin 	    sc->epc_size, VM_MEMATTR_DEFAULT);
10932164af29SRuslan Bukin 
10942164af29SRuslan Bukin 	sc->epc_pages = malloc(sizeof(struct epc_page) * sc->npages,
10952164af29SRuslan Bukin 	    M_DEVBUF, M_WAITOK | M_ZERO);
10962164af29SRuslan Bukin 
10972164af29SRuslan Bukin 	for (i = 0; i < sc->npages; i++) {
10982164af29SRuslan Bukin 		sc->epc_pages[i].base = epc_base_vaddr + SGX_PAGE_SIZE * i;
10992164af29SRuslan Bukin 		sc->epc_pages[i].phys = sc->epc_base + SGX_PAGE_SIZE * i;
11002164af29SRuslan Bukin 		sc->epc_pages[i].index = i;
11012164af29SRuslan Bukin 	}
11022164af29SRuslan Bukin 
11032164af29SRuslan Bukin 	sc->vmem_epc = vmem_create("SGX EPC", sc->epc_base, sc->epc_size,
11042164af29SRuslan Bukin 	    PAGE_SIZE, PAGE_SIZE, M_FIRSTFIT | M_WAITOK);
11052164af29SRuslan Bukin 	if (sc->vmem_epc == NULL) {
11062164af29SRuslan Bukin 		printf("%s: Can't create vmem arena.\n", __func__);
11072164af29SRuslan Bukin 		free(sc->epc_pages, M_SGX);
11082164af29SRuslan Bukin 		return (EINVAL);
11092164af29SRuslan Bukin 	}
11102164af29SRuslan Bukin 
11112164af29SRuslan Bukin 	error = vm_phys_fictitious_reg_range(sc->epc_base,
11122164af29SRuslan Bukin 	    sc->epc_base + sc->epc_size, VM_MEMATTR_DEFAULT);
11132164af29SRuslan Bukin 	if (error) {
11142164af29SRuslan Bukin 		printf("%s: Can't register fictitious space.\n", __func__);
11152164af29SRuslan Bukin 		free(sc->epc_pages, M_SGX);
11162164af29SRuslan Bukin 		return (EINVAL);
11172164af29SRuslan Bukin 	}
11182164af29SRuslan Bukin 
11192164af29SRuslan Bukin 	return (0);
11202164af29SRuslan Bukin }
11212164af29SRuslan Bukin 
11222164af29SRuslan Bukin static void
11232164af29SRuslan Bukin sgx_put_epc_area(struct sgx_softc *sc)
11242164af29SRuslan Bukin {
11252164af29SRuslan Bukin 
11262164af29SRuslan Bukin 	vm_phys_fictitious_unreg_range(sc->epc_base,
11272164af29SRuslan Bukin 	    sc->epc_base + sc->epc_size);
11282164af29SRuslan Bukin 
11292164af29SRuslan Bukin 	free(sc->epc_pages, M_SGX);
11302164af29SRuslan Bukin }
11312164af29SRuslan Bukin 
11322164af29SRuslan Bukin static int
11332164af29SRuslan Bukin sgx_load(void)
11342164af29SRuslan Bukin {
11352164af29SRuslan Bukin 	struct sgx_softc *sc;
11362164af29SRuslan Bukin 	int error;
11372164af29SRuslan Bukin 
11382164af29SRuslan Bukin 	sc = &sgx_sc;
11392164af29SRuslan Bukin 
11402164af29SRuslan Bukin 	if ((cpu_stdext_feature & CPUID_STDEXT_SGX) == 0)
11412164af29SRuslan Bukin 		return (ENXIO);
11422164af29SRuslan Bukin 
11432164af29SRuslan Bukin 	error = sgx_get_epc_area(sc);
11442164af29SRuslan Bukin 	if (error) {
11452164af29SRuslan Bukin 		printf("%s: Failed to get Processor Reserved Memory area.\n",
11462164af29SRuslan Bukin 		    __func__);
11472164af29SRuslan Bukin 		return (ENXIO);
11482164af29SRuslan Bukin 	}
11492164af29SRuslan Bukin 
115056512942SRuslan Bukin 	mtx_init(&sc->mtx_encls, "SGX ENCLS", NULL, MTX_DEF);
115156512942SRuslan Bukin 	mtx_init(&sc->mtx, "SGX driver", NULL, MTX_DEF);
115256512942SRuslan Bukin 
11532164af29SRuslan Bukin 	TAILQ_INIT(&sc->enclaves);
11542164af29SRuslan Bukin 
11552164af29SRuslan Bukin 	sc->sgx_cdev = make_dev(&sgx_cdevsw, 0, UID_ROOT, GID_WHEEL,
11562164af29SRuslan Bukin 	    0600, "isgx");
11572164af29SRuslan Bukin 
11582164af29SRuslan Bukin 	sc->state |= SGX_STATE_RUNNING;
11592164af29SRuslan Bukin 
11602164af29SRuslan Bukin 	printf("SGX initialized: EPC base 0x%lx size %ld (%d pages)\n",
11612164af29SRuslan Bukin 	    sc->epc_base, sc->epc_size, sc->npages);
11622164af29SRuslan Bukin 
11632164af29SRuslan Bukin 	return (0);
11642164af29SRuslan Bukin }
11652164af29SRuslan Bukin 
11662164af29SRuslan Bukin static int
11672164af29SRuslan Bukin sgx_unload(void)
11682164af29SRuslan Bukin {
11692164af29SRuslan Bukin 	struct sgx_softc *sc;
11702164af29SRuslan Bukin 
11712164af29SRuslan Bukin 	sc = &sgx_sc;
11722164af29SRuslan Bukin 
117356512942SRuslan Bukin 	if ((sc->state & SGX_STATE_RUNNING) == 0)
117456512942SRuslan Bukin 		return (0);
117556512942SRuslan Bukin 
11762164af29SRuslan Bukin 	mtx_lock(&sc->mtx);
11772164af29SRuslan Bukin 	if (!TAILQ_EMPTY(&sc->enclaves)) {
11782164af29SRuslan Bukin 		mtx_unlock(&sc->mtx);
11792164af29SRuslan Bukin 		return (EBUSY);
11802164af29SRuslan Bukin 	}
11812164af29SRuslan Bukin 	sc->state &= ~SGX_STATE_RUNNING;
11822164af29SRuslan Bukin 	mtx_unlock(&sc->mtx);
11832164af29SRuslan Bukin 
11842164af29SRuslan Bukin 	destroy_dev(sc->sgx_cdev);
11852164af29SRuslan Bukin 
11862164af29SRuslan Bukin 	vmem_destroy(sc->vmem_epc);
11872164af29SRuslan Bukin 	sgx_put_epc_area(sc);
11882164af29SRuslan Bukin 
11892164af29SRuslan Bukin 	mtx_destroy(&sc->mtx_encls);
11902164af29SRuslan Bukin 	mtx_destroy(&sc->mtx);
11912164af29SRuslan Bukin 
11922164af29SRuslan Bukin 	return (0);
11932164af29SRuslan Bukin }
11942164af29SRuslan Bukin 
11952164af29SRuslan Bukin static int
11962164af29SRuslan Bukin sgx_handler(module_t mod, int what, void *arg)
11972164af29SRuslan Bukin {
11982164af29SRuslan Bukin 	int error;
11992164af29SRuslan Bukin 
12002164af29SRuslan Bukin 	switch (what) {
12012164af29SRuslan Bukin 	case MOD_LOAD:
12022164af29SRuslan Bukin 		error = sgx_load();
12032164af29SRuslan Bukin 		break;
12042164af29SRuslan Bukin 	case MOD_UNLOAD:
12052164af29SRuslan Bukin 		error = sgx_unload();
12062164af29SRuslan Bukin 		break;
12072164af29SRuslan Bukin 	default:
12082164af29SRuslan Bukin 		error = 0;
12092164af29SRuslan Bukin 		break;
12102164af29SRuslan Bukin 	}
12112164af29SRuslan Bukin 
12122164af29SRuslan Bukin 	return (error);
12132164af29SRuslan Bukin }
12142164af29SRuslan Bukin 
12152164af29SRuslan Bukin static moduledata_t sgx_kmod = {
12162164af29SRuslan Bukin 	"sgx",
12172164af29SRuslan Bukin 	sgx_handler,
12182164af29SRuslan Bukin 	NULL
12192164af29SRuslan Bukin };
12202164af29SRuslan Bukin 
12212164af29SRuslan Bukin DECLARE_MODULE(sgx, sgx_kmod, SI_SUB_LAST, SI_ORDER_ANY);
12222164af29SRuslan Bukin MODULE_VERSION(sgx, 1);
1223