From 0585eff9a2a964ab53baa96f92b3b6b2153936f4 Mon Sep 17 00:00:00 2001 From: Matheus Date: Tue, 24 Sep 2024 23:08:58 -0300 Subject: [PATCH] x86_64 cpu: find topology --- kernel-src/arch/x86-64/cpu.c | 163 ++++++++++++++++++++++++++- kernel-src/include/x86-64/arch/cpu.h | 38 +++++-- 2 files changed, 189 insertions(+), 12 deletions(-) diff --git a/kernel-src/arch/x86-64/cpu.c b/kernel-src/arch/x86-64/cpu.c index 64e649aa..0bfa46da 100644 --- a/kernel-src/arch/x86-64/cpu.c +++ b/kernel-src/arch/x86-64/cpu.c @@ -3,7 +3,11 @@ #include #include +#define CPUID_VENDOR_AMD "AuthenticAMD" +#define CPUID_VENDOR_INTEL "GenuineIntel" + #define CPUID_SYSCALL (1 << 11) +#define CPUID_LEAF_1_EDX_HTT (1 << 28) #define EFER_SYSCALLENABLE 1 void arch_syscall_entry(); @@ -39,11 +43,144 @@ static void x87isr(isr_t *self, context_t *ctx) { } } +static bool cpuid_leaf_0xb_available(void) { + if (current_cpu()->cpuid_max < 0xb) + return false; + + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; + __get_cpuid_count(0xb, 0, &eax, &ebx, &ecx, &edx); + return ebx != 0; +} + +static bool cpuid_leaf_0x80000008_available(void) { + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; + __get_cpuid(0x80000000, &eax, &ebx, &ecx, &edx); + + if (eax < 0x80000008) + return false; + + return true; +} + +static bool cpuid_leaf_0x4_available(void) { + if (current_cpu()->cpuid_max < 0x4) + return false; + + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; + __get_cpuid_count(0xb, 0, &eax, &ebx, &ecx, &edx); + return eax != 0; +} + +#define TOPOLOGY_TYPE_THREAD 1 +#define TOPOLOGY_TYPE_CORE 2 +#define TOPOLOGY_TYPE_PACKAGE 0xffffffff + +static size_t get_topology_depth(void) { + bool is_amd = strcmp(current_cpu()->vendor, CPUID_VENDOR_AMD) == 0; + bool is_intel = strcmp(current_cpu()->vendor, CPUID_VENDOR_INTEL) == 0; + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; + + __get_cpuid(1, &eax, &ebx, &ecx, &edx); + bool has_package_bits = edx & CPUID_LEAF_1_EDX_HTT; + + if (is_intel && cpuid_leaf_0xb_available()) { + int current_type; + size_t depth = 0; + for (;;) { + __get_cpuid_count(0xb, depth, &eax, &ebx, &ecx, &edx); + current_type = (ecx >> 8) & 0xff; + if (current_type == 0) + break; + depth += 1; + } + + __assert(depth >= 2); // intel guarantees that at least the thread and cpu level will exist + return depth + 1; // account for package level + } else if ((is_intel || is_amd) && has_package_bits) { + return 3; // use the package -> core -> thread topology for this CPU + } else { + return 1; // it will be a leaf node in the root of the topology tree + } +} + +// last value in topology is supposed to be the leaf +static void get_topology(long *topology_ids, long *topology_types) { + bool is_amd = strcmp(current_cpu()->vendor, CPUID_VENDOR_AMD) == 0; + bool is_intel = strcmp(current_cpu()->vendor, CPUID_VENDOR_INTEL) == 0; + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; + size_t depth = get_topology_depth(); + + __get_cpuid(1, &eax, &ebx, &ecx, &edx); + bool has_package_bits = edx & CPUID_LEAF_1_EDX_HTT; + + uint8_t initial_apic_id = (ebx >> 24) & 0xff; + + size_t bits_below_package = 32 - __builtin_clz(((ebx >> 16) & 0xff) - 1); + + if (is_intel && cpuid_leaf_0xb_available()) { + uint32_t bits = 0; + for (int i = 0; i < depth - 1; ++i) { + __get_cpuid_count(0xb, i, &eax, &ebx, &ecx, &edx); + initial_apic_id = edx; + uint32_t current_bits = eax & 0x1f; + topology_ids[depth - 1 - i] = (initial_apic_id >> bits) & ((1 << current_bits) - 1); + bits += current_bits; + } + + topology_ids[0] = (long)(initial_apic_id >> bits) & ((1l << (32 - bits)) - 1); + topology_types[0] = TOPOLOGY_TYPE_PACKAGE; + } else if (is_intel && has_package_bits && cpuid_leaf_0x4_available()) { + __get_cpuid_count(0x4, 0, &eax, &ebx, &ecx, &edx); + + size_t bits_for_core = 32 - __builtin_clz(((eax >> 26) & 0x3f)); + size_t bits_for_thread = bits_below_package - bits_for_core; + uint32_t core_mask = (1 << bits_for_core) - 1; + uint32_t thread_mask = (1 << bits_for_thread) - 1; + + topology_ids[0] = initial_apic_id >> bits_below_package; + topology_types[0] = TOPOLOGY_TYPE_PACKAGE; + + topology_ids[1] = (initial_apic_id >> bits_for_thread) & core_mask; + topology_types[1] = TOPOLOGY_TYPE_CORE; + + topology_ids[2] = initial_apic_id & thread_mask; + topology_types[2] = TOPOLOGY_TYPE_THREAD; + } else if (is_amd && has_package_bits && cpuid_leaf_0x80000008_available()) { + __get_cpuid(0x80000008, &eax, &ebx, &ecx, &edx); + size_t apic_id_size = (ecx >> 12) & 0xf; + size_t bits_for_core = apic_id_size ? apic_id_size : 32 - __builtin_clz(ecx & 0xff); + size_t bits_for_thread = bits_below_package - bits_for_core; + uint32_t core_mask = (1 << bits_for_core) - 1; + uint32_t thread_mask = (1 << bits_for_thread) - 1; + + topology_ids[0] = initial_apic_id >> bits_below_package; + topology_types[0] = TOPOLOGY_TYPE_PACKAGE; + + topology_ids[1] = (initial_apic_id >> bits_for_thread) & core_mask; + topology_types[1] = TOPOLOGY_TYPE_CORE; + + topology_ids[2] = initial_apic_id & thread_mask; + topology_types[2] = TOPOLOGY_TYPE_THREAD; + } else if (has_package_bits) { + topology_ids[0] = initial_apic_id >> bits_below_package; + topology_types[0] = TOPOLOGY_TYPE_PACKAGE; + + topology_ids[1] = initial_apic_id & ((1 << bits_below_package) - 1); + topology_types[1] = TOPOLOGY_TYPE_CORE; + + topology_ids[2] = 0; + topology_types[2] = TOPOLOGY_TYPE_THREAD; + } else { + *topology_ids = initial_apic_id; + *topology_types = TOPOLOGY_TYPE_THREAD; + } +} + void cpu_initstate() { arch_apic_initap(); // syscall instruction - unsigned int eax = 0,ebx = 0,ecx = 0,edx = 0; + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; __get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx); __assert(edx & CPUID_SYSCALL); @@ -80,8 +217,32 @@ void cpu_initstate() { : : : "rax" ); + // register some exception handlers that give out signals interrupt_register(0, div0isr, NULL, IPL_IGNORE); interrupt_register(6, illisr, NULL, IPL_IGNORE); interrupt_register(16, x87isr, NULL, IPL_IGNORE); interrupt_register(19, simdisr, NULL, IPL_IGNORE); + + // get vendor string for cpu and max cpuid eax + __get_cpuid(0, &eax, &ebx, &ecx, &edx); + int *vendor_ptr = (int *)current_cpu()->vendor; + *vendor_ptr++ = ebx; + *vendor_ptr++ = edx; + *vendor_ptr++ = ecx; + current_cpu()->vendor[12] = '\0'; + current_cpu()->cpuid_max = eax; + + size_t topology_depth = get_topology_depth(); + long topology_ids[topology_depth]; + long topology_types[topology_depth]; + get_topology(topology_ids, topology_types); + + char str_buf[128]; + size_t done = 0; + + done += snprintf(str_buf, 128, "cpu%d: %s@", current_cpu()->id, current_cpu()->vendor); + for (int i = 0; i < topology_depth; ++i) + done += snprintf(str_buf + done, 128 - done, "%d%c", topology_ids[i], i == topology_depth - 1 ? 0 : ':'); + str_buf[done] = '\n'; + printf("%s\n", str_buf); } diff --git a/kernel-src/include/x86-64/arch/cpu.h b/kernel-src/include/x86-64/arch/cpu.h index 2ca21953..17d6c408 100644 --- a/kernel-src/include/x86-64/arch/cpu.h +++ b/kernel-src/include/x86-64/arch/cpu.h @@ -16,28 +16,44 @@ #define ARCH_EOI arch_apic_eoi typedef struct cpu_t { - thread_t *thread; - struct cpu_t *self; - vmmcontext_t *vmmctx; + // expected to be exposed to the rest of the kernel + thread_t *thread; // expected to be here by other code + + struct cpu_t *self; // expected to be here by other code + + vmmcontext_t *vmmctx; // expected to be here by other code + long id; - uint64_t gdt[7]; - ist_t ist; + isr_t isr[MAX_ISR_COUNT]; - int acpiid; - timer_t *timer; bool intstatus; long ipl; - thread_t *idlethread; - timerentry_t schedtimerentry; - void *schedulerstack; - isr_t *isrqueue; + timer_t *timer; + isr_t *dpcisr; dpc_t *dpcqueue; + thread_t *idlethread; + timerentry_t schedtimerentry; + void *schedulerstack; + dpc_t reschedule_dpc; isr_t *reschedule_isr; + + // architecture specific, does not need to be exposed + + uint64_t gdt[7]; + char vendor[13]; + ist_t ist; + int acpiid; + + uint32_t cpuid_max; + + int topology_thread; + int topology_core; + int topology_package; } cpu_t; #define CPU_HALT() asm volatile("hlt")