Browse Source

[PAL] Fix cpuid parsing on a system with hyperthreads

Don Porter 4 years ago
parent
commit
60bed726e3

+ 4 - 1
Pal/src/db_main.c

@@ -452,7 +452,9 @@ noreturn void pal_main (
     __pal_control.alloc_align        = pal_state.alloc_align;
     __pal_control.broadcast_stream   = _DkBroadcastStreamOpen();
 
-    _DkGetCPUInfo(&__pal_control.cpu_info);
+    if (_DkGetCPUInfo(&__pal_control.cpu_info) < 0) {
+        goto out_fail;
+    }
     __pal_control.mem_info.mem_total = _DkMemoryQuota();
 
 #if PROFILING == 1
@@ -470,6 +472,7 @@ noreturn void pal_main (
     /* Now we will start the execution */
     start_execution(first_argument, arguments, environments);
 
+ out_fail:
     /* We wish we will never reached here */
     INIT_FAIL(PAL_ERROR_DENIED, "unexpected termination");
 }

+ 26 - 9
Pal/src/host/FreeBSD/db_main.c

@@ -331,7 +331,23 @@ static char * cpu_flags[]
            "pbe",    // "pending break event"
         };
 
-void _DkGetCPUInfo (PAL_CPU_INFO * ci)
+/*
+ * Returns the number of online CPUs read from sysctl hw.ncpus, -errno on failure.
+ */
+int get_cpu_count(void) {
+    int mib[2], cores;
+    size_t len = sizeof(cores);
+    mib[0] = CTL_HW;
+    mib[1] = HW_NCPU;
+
+    int rv = sysctl(mib, 2, &cores, &len, NULL, 0);
+    if (rv < 0)
+        return unix_to_pal_error(ERRNO(rv));
+
+    return cores;
+}
+
+int _DkGetCPUInfo (PAL_CPU_INFO * ci)
 {
     unsigned int words[PAL_CPUID_WORD_NUM];
 
@@ -361,14 +377,15 @@ void _DkGetCPUInfo (PAL_CPU_INFO * ci)
     ci->cpu_model    = BIT_EXTRACT_LE(words[PAL_CPUID_WORD_EAX],  4,  8);
     ci->cpu_stepping = BIT_EXTRACT_LE(words[PAL_CPUID_WORD_EAX],  0,  4);
 
-    /* According to SDM: EBX[15:0] is to enumerate processor topology
-     * of the system. However this value is intended for display/diagnostic
-     * purposes. The actual number of logical processors available to
-     * BIOS/OS/App may be different. We use this leaf for now as it's the
-     * best option we have so far to get the cpu number  */
-
-    cpuid(0xb, 1, words, 0);
-    ci->cpu_num      = BIT_EXTRACT_LE(words[WORD_EBX],  0, 16);
+    /* we cannot use CPUID(0xb) because it counts even disabled-by-BIOS cores (e.g. HT cores);
+     * instead we extract info on number of online CPUs by parsing sysfs pseudo-files */
+    int cores = get_cpu_count();
+    if (cores < 0) {
+        free(brand);
+        free(vendor_id);
+        return cores;
+    }
+    ci->cpu_num = cores;
 
     int flen = 0, fmax = 80;
     char * flags = malloc(fmax);

+ 14 - 9
Pal/src/host/Linux-SGX/db_main.c

@@ -75,6 +75,7 @@ PAL_NUM _DkGetHostId (void)
 
 #include "elf-x86_64.h"
 #include "dynamic_link.h"
+#include <asm/errno.h>
 
 void setup_pal_map (struct link_map * map);
 static struct link_map pal_map;
@@ -293,6 +294,12 @@ void pal_linux_main(char * uptr_args, uint64_t args_size,
     pal_sec.uid = sec_info.uid;
     pal_sec.gid = sec_info.gid;
 
+    int num_cpus = sec_info.num_cpus;
+    if (num_cpus >= 1 && num_cpus <= (1 << 16)) {
+        pal_sec.num_cpus = num_cpus;
+    } else {
+        return;
+    }
 
     /* set up page allocator and slab manager */
     init_slab_mgr(pagesz);
@@ -459,9 +466,11 @@ static char * cpu_flags[]
           "pbe",    // "pending break event"
         };
 
-void _DkGetCPUInfo (PAL_CPU_INFO * ci)
+
+int _DkGetCPUInfo (PAL_CPU_INFO * ci)
 {
     unsigned int words[PAL_CPUID_WORD_NUM];
+    int rv = 0;
 
     const size_t VENDOR_ID_SIZE = 13;
     char* vendor_id = malloc(VENDOR_ID_SIZE);
@@ -486,14 +495,9 @@ void _DkGetCPUInfo (PAL_CPU_INFO * ci)
     brand[BRAND_SIZE - 1] = '\0';
     ci->cpu_brand = brand;
 
-    /* According to SDM: EBX[15:0] is to enumerate processor topology
-     * of the system. However this value is intended for display/diagnostic
-     * purposes. The actual number of logical processors available to
-     * BIOS/OS/App may be different. We use this leaf for now as it's the
-     * best option we have so far to get the cpu number  */
-
-    cpuid(0xb, 1, words);
-    ci->cpu_num      = BIT_EXTRACT_LE(words[PAL_CPUID_WORD_EBX], 0, 16);
+    /* we cannot use CPUID(0xb) because it counts even disabled-by-BIOS cores (e.g. HT cores);
+     * instead, this is passed in via pal_sec at start-up time. */
+    ci->cpu_num = pal_sec.num_cpus;
 
     cpuid(1, 0, words);
     ci->cpu_family   = BIT_EXTRACT_LE(words[PAL_CPUID_WORD_EAX],  8, 12) +
@@ -526,4 +530,5 @@ void _DkGetCPUInfo (PAL_CPU_INFO * ci)
 
     flags[flen ? flen - 1 : 0] = 0;
     ci->cpu_flags = flags;
+    return rv;
 }

+ 3 - 0
Pal/src/host/Linux-SGX/pal_security.h

@@ -49,6 +49,9 @@ struct pal_sec {
     PAL_SEC_STR     pipe_prefix;
     PAL_IDX         mcast_port, mcast_srv, mcast_cli;
 
+    /* Need to pass in the number of cores */
+    PAL_NUM         num_cpus;
+
 #ifdef DEBUG
     PAL_BOL         in_gdb;
 #endif

+ 54 - 0
Pal/src/host/Linux-SGX/sgx_main.c

@@ -1,4 +1,5 @@
 #include <pal_linux.h>
+#include <pal_linux_error.h>
 #include <pal_rtld.h>
 #include "sgx_internal.h"
 #include "sgx_tls.h"
@@ -707,6 +708,54 @@ out:
     return ret;
 }
 
+/*
+ * Returns the number of online CPUs read from /sys/devices/system/cpu/online, -errno on failure.
+ * Understands complex formats like "1,3-5,6".
+ */
+int get_cpu_count(void) {
+    int fd = INLINE_SYSCALL(open, 3, "/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC, 0);
+    if (fd < 0)
+        return unix_to_pal_error(ERRNO(fd));
+
+    char buf[64];
+    int ret = INLINE_SYSCALL(read, 3, fd, buf, sizeof(buf) - 1);
+    if (ret < 0) {
+        INLINE_SYSCALL(close, 1, fd);
+        return unix_to_pal_error(ERRNO(ret));
+    }
+
+    buf[ret] = '\0'; /* ensure null-terminated buf even in partial read */
+
+    char* end;
+    char* ptr = buf;
+    int cpu_count = 0;
+    while (*ptr) {
+        while (*ptr == ' ' || *ptr == '\t' || *ptr == ',')
+            ptr++;
+
+        int firstint = (int)strtol(ptr, &end, 10);
+        if (ptr == end)
+            break;
+
+        if (*end == '\0' || *end == ',') {
+            /* single CPU index, count as one more CPU */
+            cpu_count++;
+        } else if (*end == '-') {
+            /* CPU range, count how many CPUs in range */
+            ptr = end + 1;
+            int secondint = (int)strtol(ptr, &end, 10);
+            if (secondint > firstint)
+                cpu_count += secondint - firstint + 1; // inclusive (e.g., 0-7, or 8-16)
+        }
+        ptr = end;
+    }
+
+    INLINE_SYSCALL(close, 1, fd);
+    if (cpu_count == 0)
+        return -PAL_ERROR_STREAMNOTEXIST;
+    return cpu_count;
+}
+
 static int load_enclave (struct pal_enclave * enclave,
                          char * manifest_uri,
                          char * exec_uri,
@@ -737,6 +786,11 @@ static int load_enclave (struct pal_enclave * enclave,
     pal_sec->pid = INLINE_SYSCALL(getpid, 0);
     pal_sec->uid = INLINE_SYSCALL(getuid, 0);
     pal_sec->gid = INLINE_SYSCALL(getgid, 0);
+    int num_cpus = get_cpu_count();
+    if (num_cpus < 0) {
+        return num_cpus;
+    }
+    pal_sec->num_cpus = num_cpus;
 
 #ifdef DEBUG
     size_t env_i = 0;

+ 0 - 2
Pal/src/host/Linux/db_devices.c

@@ -35,8 +35,6 @@
 typedef __kernel_pid_t pid_t;
 #include <asm/errno.h>
 #include <asm/fcntl.h>
-#include <asm/stat.h>
-#include <linux/stat.h>
 
 #define DEVICE_OPS(handle)                                                             \
     ({                                                                                 \

+ 0 - 2
Pal/src/host/Linux/db_files.c

@@ -35,8 +35,6 @@
 typedef __kernel_pid_t pid_t;
 #undef __GLIBC__
 #include <linux/stat.h>
-#include <asm/stat.h>
-#include <asm/fcntl.h>
 #include <asm/errno.h>
 
 /* 'open' operation for file streams */

+ 58 - 16
Pal/src/host/Linux/db_main.c

@@ -383,9 +383,57 @@ static char * cpu_flags[]
           "pbe",    // "pending break event"
         };
 
-void _DkGetCPUInfo (PAL_CPU_INFO * ci)
+/*
+ * Returns the number of online CPUs read from /sys/devices/system/cpu/online, -errno on failure.
+ * Understands complex formats like "1,3-5,6".
+ */
+int get_cpu_count(void) {
+    int fd = INLINE_SYSCALL(open, 3, "/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC, 0);
+    if (fd < 0)
+        return unix_to_pal_error(ERRNO(fd));
+
+    char buf[64];
+    int ret = INLINE_SYSCALL(read, 3, fd, buf, sizeof(buf) - 1);
+    INLINE_SYSCALL(close, 1, fd);
+    if (ret < 0) {
+        return unix_to_pal_error(ERRNO(ret));
+    }
+
+    buf[ret] = '\0'; /* ensure null-terminated buf even in partial read */
+
+    char* end;
+    char* ptr = buf;
+    int cpu_count = 0;
+    while (*ptr) {
+        while (*ptr == ' ' || *ptr == '\t' || *ptr == ',')
+            ptr++;
+
+        int firstint = (int)strtol(ptr, &end, 10);
+        if (ptr == end)
+            break;
+
+        if (*end == '\0' || *end == ',') {
+            /* single CPU index, count as one more CPU */
+            cpu_count++;
+        } else if (*end == '-') {
+            /* CPU range, count how many CPUs in range */
+            ptr = end + 1;
+            int secondint = (int)strtol(ptr, &end, 10);
+            if (secondint > firstint)
+                cpu_count += secondint - firstint + 1; // inclusive (e.g., 0-7, or 8-16)
+        }
+        ptr = end;
+    }
+
+    if (cpu_count == 0)
+        return -PAL_ERROR_STREAMNOTEXIST;
+    return cpu_count;
+}
+
+int _DkGetCPUInfo (PAL_CPU_INFO * ci)
 {
     unsigned int words[PAL_CPUID_WORD_NUM];
+    int rv = 0;
 
     const size_t VENDOR_ID_SIZE = 13;
     char* vendor_id = malloc(VENDOR_ID_SIZE);
@@ -408,22 +456,15 @@ void _DkGetCPUInfo (PAL_CPU_INFO * ci)
     brand[BRAND_SIZE - 1] = '\0';
     ci->cpu_brand = brand;
 
-    if (!memcmp(vendor_id, "GenuineIntel", 12)) {
-
-       /* According to SDM: EBX[15:0] is to enumerate processor topology
-        * of the system. However this value is intended for display/diagnostic
-        * purposes. The actual number of logical processors available to
-        * BIOS/OS/App may be different. We use this leaf for now as it's the
-        * best option we have so far to get the cpu number  */
-
-        cpuid(0xb, 1, words);
-        ci->cpu_num  = BIT_EXTRACT_LE(words[PAL_CPUID_WORD_EBX], 0, 16);
-    } else if (!memcmp(vendor_id, "AuthenticAMD", 12)) {
-        cpuid(0x8000008, 0, words);
-        ci->cpu_num  = BIT_EXTRACT_LE(words[PAL_CPUID_WORD_EAX], 0, 8) + 1;
-    } else {
-        ci->cpu_num  = 1;
+    /* we cannot use CPUID(0xb) because it counts even disabled-by-BIOS cores (e.g. HT cores);
+     * instead we extract info on number of online CPUs by parsing sysfs pseudo-files */
+    int cores = get_cpu_count();
+    if (cores < 0) {
+        free(vendor_id);
+        free(brand);
+        return cores;
     }
+    ci->cpu_num = cores;
 
     cpuid(1, 0, words);
     ci->cpu_family   = BIT_EXTRACT_LE(words[PAL_CPUID_WORD_EAX],  8, 12);
@@ -460,4 +501,5 @@ void _DkGetCPUInfo (PAL_CPU_INFO * ci)
 
     flags[flen ? flen - 1 : 0] = 0;
     ci->cpu_flags = flags;
+    return rv;
 }

+ 0 - 2
Pal/src/host/Linux/db_streams.c

@@ -32,12 +32,10 @@
 
 #include <linux/types.h>
 typedef __kernel_pid_t pid_t;
-#include <linux/stat.h>
 #include <linux/msg.h>
 #include <linux/socket.h>
 #include <linux/wait.h>
 #include <asm/fcntl.h>
-#include <asm/stat.h>
 #include <asm/socket.h>
 #include <asm/poll.h>
 #include <sys/signal.h>

+ 6 - 2
Pal/src/host/Linux/pal_linux.h

@@ -30,6 +30,11 @@
 #include <sys/syscall.h>
 #include <sigset.h>
 
+#include <asm/fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 #ifdef __x86_64__
 # include "sysdep-x86_64.h"
 #endif
@@ -74,7 +79,7 @@ extern struct pal_linux_state {
 #endif
 } linux_state;
 
-#include <asm/fcntl.h>
+
 #include <asm/mman.h>
 
 #ifdef INLINE_SYSCALL
@@ -153,7 +158,6 @@ int handle_deserialize (PAL_HANDLE * handle, const void * data, int size);
 #define ACCESS_W    2
 #define ACCESS_X    1
 
-struct stat;
 bool stataccess (struct stat * stats, int acc);
 
 void init_child_process (PAL_HANDLE * parent, PAL_HANDLE * exec,

+ 2 - 1
Pal/src/pal_internal.h

@@ -269,7 +269,8 @@ PAL_NUM _DkGetProcessId (void);
 PAL_NUM _DkGetHostId (void);
 unsigned long _DkMemoryQuota (void);
 unsigned long _DkMemoryAvailableQuota (void);
-void _DkGetCPUInfo (PAL_CPU_INFO * info);
+// Returns 0 on success, negative PAL code on failure
+int _DkGetCPUInfo (PAL_CPU_INFO * info);
 
 /* Internal DK calls, in case any of the internal routines needs to use them */
 /* DkStream calls */