|
@@ -0,0 +1,362 @@
|
|
|
+/*
|
|
|
+* Copyright 2011-2017 Intel Corporation
|
|
|
+*
|
|
|
+* Redistribution and use in source and binary forms, with or without
|
|
|
+* modification, are permitted provided that the following conditions
|
|
|
+* are met:
|
|
|
+*
|
|
|
+* 1. Redistributions of source code must retain the above copyright notice,
|
|
|
+* this list of conditions and the following disclaimer.
|
|
|
+*
|
|
|
+* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
+* notice, this list of conditions and the following disclaimer in the
|
|
|
+* documentation and/or other materials provided with the distribution.
|
|
|
+*
|
|
|
+* 3. Neither the name of the copyright holder nor the names of its
|
|
|
+* contributors may be used to endorse or promote products derived from
|
|
|
+* this software without specific prior written permission.
|
|
|
+*
|
|
|
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
+* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
|
+* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
+* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
+*/
|
|
|
+
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <sys/types.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <sgx_uae_service.h>
|
|
|
+#include <sgx_capable.h>
|
|
|
+
|
|
|
+/* __cpuid(unsinged int info[4], unsigned int leaf, unsigned int subleaf); */
|
|
|
+/* Because gcc's __get_cpuid() intrinsic is difficult to work with */
|
|
|
+#define __cpuid(x,y,z) asm volatile("cpuid":"=a"(x[0]),"=b"(x[1]),"=c"(x[2]),"=d"(x[3]):"a"(y),"c"(z))
|
|
|
+
|
|
|
+#define EFIFS_PATH "/sys/firmware/efi/"
|
|
|
+#define EFIVARS_PATH EFIFS_PATH"efivars/"
|
|
|
+#define EFIVAR_EPCBIOS EFIVARS_PATH"EPCBIOS-c60aa7f6-e8d6-4956-8ba1-fe26298f5e87"
|
|
|
+#define EFIVAR_EPCSW EFIVARS_PATH"EPCSW-d69a279b-58eb-45d1-a148-771bb9eb5251"
|
|
|
+
|
|
|
+static int _is_sgx_available();
|
|
|
+static int _is_cpu_supported();
|
|
|
+
|
|
|
+sgx_status_t sgx_is_capable (int *sgx_capable)
|
|
|
+{
|
|
|
+ struct stat sb;
|
|
|
+
|
|
|
+ if ( sgx_capable == NULL ) return SGX_ERROR_INVALID_PARAMETER;
|
|
|
+
|
|
|
+ if ( ! _is_cpu_supported() ) {
|
|
|
+ *sgx_capable= 0;
|
|
|
+ return SGX_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( _is_sgx_available() ) {
|
|
|
+ *sgx_capable= 1;
|
|
|
+ return SGX_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check to see if the Software Control Interface is available */
|
|
|
+
|
|
|
+ if ( stat(EFIVAR_EPCBIOS, &sb) == -1 ) {
|
|
|
+ if ( errno == EACCES ) return SGX_ERROR_NO_PRIVILEGE;
|
|
|
+ *sgx_capable = 0;
|
|
|
+ return SGX_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ *sgx_capable= 1;
|
|
|
+ return SGX_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+sgx_status_t sgx_cap_get_status (sgx_device_status_t *sgx_device_status)
|
|
|
+{
|
|
|
+ struct stat sb;
|
|
|
+ int has_efifs= 0;
|
|
|
+
|
|
|
+ if ( sgx_device_status == NULL ) return SGX_ERROR_INVALID_PARAMETER;
|
|
|
+
|
|
|
+ if ( ! _is_cpu_supported() ) {
|
|
|
+ *sgx_device_status= SGX_DISABLED_UNSUPPORTED_CPU;
|
|
|
+ return SGX_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( _is_sgx_available() ) {
|
|
|
+ *sgx_device_status= SGX_ENABLED;
|
|
|
+ return SGX_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Intel SGX is supported, but not enabled. Figure out what
|
|
|
+ * it will take to enable it.
|
|
|
+ */
|
|
|
+
|
|
|
+ /* Were we booted in UEFI mode? */
|
|
|
+
|
|
|
+ if ( stat(EFIFS_PATH, &sb) == 0 ) {
|
|
|
+ has_efifs= 1;
|
|
|
+
|
|
|
+ if ( stat(EFIVARS_PATH, &sb) == -1 )
|
|
|
+ {
|
|
|
+ /* We have /sys/firmware/efi but not efivars */
|
|
|
+
|
|
|
+ switch (errno) {
|
|
|
+ case EACCES:
|
|
|
+ return SGX_ERROR_NO_PRIVILEGE;
|
|
|
+ case ENOENT:
|
|
|
+ case ENOTDIR:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return SGX_ERROR_UNEXPECTED;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ switch (errno) {
|
|
|
+ case EACCES:
|
|
|
+ return SGX_ERROR_NO_PRIVILEGE;
|
|
|
+ case ENOENT:
|
|
|
+ case ENOTDIR:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return SGX_ERROR_UNEXPECTED;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( ! has_efifs ) {
|
|
|
+ /*
|
|
|
+ * We don't have /sys/firmware/efi mounted. It could have been
|
|
|
+ * unmounted by the user, or we might not have UEFI support in
|
|
|
+ * the OS. If /boot/efi exists, then we are probably capable of
|
|
|
+ * UEFI and should report SGX_DISABLED. Otherwise report
|
|
|
+ * SGX_DISABLED_LEGACY_OS.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ( stat("/boot/efi", &sb) == 0 ) *sgx_device_status= SGX_DISABLED;
|
|
|
+ else {
|
|
|
+ switch(errno) {
|
|
|
+ case ENOENT:
|
|
|
+ case ENOTDIR:
|
|
|
+ *sgx_device_status= SGX_DISABLED_LEGACY_OS;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /*
|
|
|
+ * We don't have enough information to figure this out
|
|
|
+ * so report SGX_DISABLED.
|
|
|
+ */
|
|
|
+ *sgx_device_status= SGX_DISABLED;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return SGX_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We have access to efivars. Now examine the EFI variable for the
|
|
|
+ * Software Control Interface.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ( stat(EFIVAR_EPCBIOS, &sb) == -1 ) {
|
|
|
+ if ( errno == EACCES ) return SGX_ERROR_NO_PRIVILEGE;
|
|
|
+
|
|
|
+ /* No SCI is present so we can't do a s/w enabled */
|
|
|
+
|
|
|
+ *sgx_device_status= SGX_DISABLED_MANUAL_ENABLE;
|
|
|
+ return SGX_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check to see if the software enable has already been
|
|
|
+ * performed. If so, then we will be enabled on the next
|
|
|
+ * reboot.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ( stat(EFIVAR_EPCSW, &sb) == -1 ) {
|
|
|
+ if ( errno == EACCES ) return SGX_ERROR_NO_PRIVILEGE;
|
|
|
+
|
|
|
+ /* The software enable hasn't been attempted yet. */
|
|
|
+
|
|
|
+ *sgx_device_status= SGX_DISABLED_SCI_AVAILABLE;
|
|
|
+ return SGX_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Software enable has occurred. Need a reboot. */
|
|
|
+
|
|
|
+ *sgx_device_status= SGX_DISABLED_REBOOT_REQUIRED;
|
|
|
+
|
|
|
+ return SGX_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/* Determine if the CPU supports Intel SGX */
|
|
|
+
|
|
|
+static int _is_cpu_supported()
|
|
|
+{
|
|
|
+ unsigned int info[4];
|
|
|
+ unsigned int *ebx, *ecx, *edx;
|
|
|
+
|
|
|
+ ebx= &info[1];
|
|
|
+ ecx= &info[2];
|
|
|
+ edx= &info[3];
|
|
|
+
|
|
|
+ /* Is this an Intel CPU? */
|
|
|
+
|
|
|
+ __cpuid (info, 0x00, 0);
|
|
|
+ if ( *ebx != 0x756e6547 || *ecx != 0x6c65746e || *edx != 0x49656e69 )
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Does the CPU support Intel SGX? */
|
|
|
+
|
|
|
+ __cpuid (info, 0x07, 0);
|
|
|
+
|
|
|
+ return ( *ebx & (0x1<<2) );
|
|
|
+}
|
|
|
+
|
|
|
+/* Are SGX instructions available for use? */
|
|
|
+
|
|
|
+static int _is_sgx_available ()
|
|
|
+{
|
|
|
+ unsigned int info[4];
|
|
|
+ unsigned int *eax, *ebx, *ecx, *edx;
|
|
|
+ unsigned int subleaf= 2;
|
|
|
+ unsigned int flag;
|
|
|
+
|
|
|
+ eax= &info[0];
|
|
|
+ ebx= &info[1];
|
|
|
+ ecx= &info[2];
|
|
|
+ edx= &info[3];
|
|
|
+
|
|
|
+ /* Are Intel SGX instructions available for use? */
|
|
|
+
|
|
|
+ __cpuid(info, 0x12, 0);
|
|
|
+
|
|
|
+ flag= *eax&0x3;
|
|
|
+ if ( flag == 0 ) return 0;
|
|
|
+
|
|
|
+ /* Do we have non-zero, max enclave sizes? */
|
|
|
+
|
|
|
+ if ( (*edx & 0xFFFF) == 0 ) return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Enumerate the subleafs for the EPC. At least one must be a valid
|
|
|
+ * subleaf that describes a page.
|
|
|
+ */
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ __cpuid(info, 0x12, subleaf);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Is this an invalid subleaf? If we've hit an invalid subleaf
|
|
|
+ * before finding a valid subleaf with a non-zero page size,
|
|
|
+ * then we have no EPC memory allocated, and thus no Intel SGX
|
|
|
+ * capability.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ( ! (*eax & 0x1) ) return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Is there a non-zero size for this EPC subleaf? If so, we
|
|
|
+ * have memory allocated to the EPC for Intel SGX, and are
|
|
|
+ * enabled.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (
|
|
|
+ (*eax&0xFFFFF000 || *ebx&0xFFFFF) &&
|
|
|
+ (*ecx&0xFFFFF000 || *edx&0xFFFFF)
|
|
|
+ ) return 1;
|
|
|
+
|
|
|
+ ++subleaf;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We'll never get here, but we need to keep the compiler happy */
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+sgx_status_t sgx_cap_enable_device (sgx_device_status_t *sgx_device_status)
|
|
|
+{
|
|
|
+ sgx_status_t status;
|
|
|
+ struct epcbios_stuct {
|
|
|
+ uint32_t attrs;
|
|
|
+ uint32_t sprmbins;
|
|
|
+ uint32_t maxepcsz;
|
|
|
+ /* There's more, but this is all we need */
|
|
|
+ } epcbios;
|
|
|
+ struct epcsw_struct {
|
|
|
+ uint32_t attrs;
|
|
|
+ uint32_t maxepcsz;
|
|
|
+ } epcsw;
|
|
|
+ FILE *fefivar;
|
|
|
+
|
|
|
+ if ( sgx_device_status == NULL ) return SGX_ERROR_INVALID_PARAMETER;
|
|
|
+
|
|
|
+ status= sgx_cap_get_status(sgx_device_status);
|
|
|
+ if ( status != SGX_SUCCESS ) return status;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we get back anything other than SGX_DISABLED_SCI_AVAILABLE
|
|
|
+ * then return, because there is nothing to do.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ( *sgx_device_status != SGX_DISABLED_SCI_AVAILABLE )
|
|
|
+ return SGX_SUCCESS;
|
|
|
+
|
|
|
+ /* Attempt the software enable */
|
|
|
+
|
|
|
+ /* First, read the EPCBIOS EFI variable to get the max EPC size */
|
|
|
+
|
|
|
+ fefivar= fopen(EFIVAR_EPCBIOS, "r");
|
|
|
+ if ( fefivar == NULL ) {
|
|
|
+ if ( errno == EACCES ) return SGX_ERROR_NO_PRIVILEGE;
|
|
|
+
|
|
|
+ return SGX_ERROR_UNEXPECTED;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The first 4 bytes are the EFI variable attributes. Data starts
|
|
|
+ * at offset 0x4, and the value we want is a UINT32 at offset 0x8.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ( fread(&epcbios, sizeof(epcbios), 1, fefivar) != 1 ) {
|
|
|
+ fclose(fefivar);
|
|
|
+ return SGX_ERROR_UNEXPECTED;
|
|
|
+ }
|
|
|
+
|
|
|
+ fclose(fefivar);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Now create the EPCSW EFI variable. The variable data is a
|
|
|
+ * single UINT32 specifying the requested EPC size.
|
|
|
+ */
|
|
|
+
|
|
|
+ epcsw.attrs= epcbios.attrs;
|
|
|
+ epcsw.maxepcsz= epcbios.maxepcsz;
|
|
|
+
|
|
|
+ fefivar= fopen(EFIVAR_EPCSW, "w");
|
|
|
+ if ( fefivar == NULL ) {
|
|
|
+ if ( errno == EACCES ) return SGX_ERROR_NO_PRIVILEGE;
|
|
|
+
|
|
|
+ return SGX_ERROR_UNEXPECTED;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Write out the EPCSW structure */
|
|
|
+
|
|
|
+ if ( fwrite(&epcsw, sizeof(epcsw), 1, fefivar) != 1 ) {
|
|
|
+ unlink(EFIVAR_EPCSW);
|
|
|
+ fclose(fefivar);
|
|
|
+ return SGX_ERROR_UNEXPECTED;
|
|
|
+ }
|
|
|
+
|
|
|
+ fclose(fefivar);
|
|
|
+
|
|
|
+ *sgx_device_status= SGX_DISABLED_REBOOT_REQUIRED;
|
|
|
+
|
|
|
+ return SGX_SUCCESS;
|
|
|
+}
|