From: Tulio A M Mendes Date: Tue, 10 Feb 2026 06:53:50 +0000 (-0300) Subject: feat: implement CPUID feature detection + HAL wrapper X-Git-Url: https://projects.tadryanom.me/docs/static/gitweb.css?a=commitdiff_plain;h=aa28c4b8e4379da7048134890baed5fbb70668ee;p=AdrOS.git feat: implement CPUID feature detection + HAL wrapper Add x86 CPUID detection (src/arch/x86/cpuid.c) that queries: - Leaf 0: vendor string, max leaf - Leaf 1: feature flags (PAE, NX, APIC, SEP, SSE, SSE2, HTT, etc) - Leaf 1 EBX: topology (APIC ID, logical CPUs) - Extended leaves: NX, Long Mode, SYSCALL, brand string HAL wrapper (include/hal/cpu_features.h): - hal_cpu_detect_features() / hal_cpu_get_features() / hal_cpu_print_features() - x86 impl in src/hal/x86/cpu_features.c - Weak default stub in src/kernel/cpu_features.c Called from kernel_main() right after console_init(). QEMU output: GenuineIntel, PAE APIC SEP SSE SSE2 FXSR HYPERVISOR. Passes: make, cppcheck, QEMU smoke test. --- diff --git a/include/arch/x86/cpuid.h b/include/arch/x86/cpuid.h new file mode 100644 index 0000000..f033db9 --- /dev/null +++ b/include/arch/x86/cpuid.h @@ -0,0 +1,59 @@ +#ifndef ARCH_X86_CPUID_H +#define ARCH_X86_CPUID_H + +#include + +struct x86_cpu_features { + /* CPUID leaf 0 */ + uint32_t max_leaf; + char vendor[13]; /* "GenuineIntel" / "AuthenticAMD" */ + + /* CPUID leaf 1 — ECX */ + uint8_t sse3 : 1; + uint8_t ssse3 : 1; + uint8_t sse41 : 1; + uint8_t sse42 : 1; + uint8_t x2apic : 1; + uint8_t avx : 1; + uint8_t hypervisor : 1; + + /* CPUID leaf 1 — EDX */ + uint8_t fpu : 1; + uint8_t tsc : 1; + uint8_t msr : 1; + uint8_t pae : 1; + uint8_t cx8 : 1; /* CMPXCHG8B */ + uint8_t apic : 1; + uint8_t sep : 1; /* SYSENTER/SYSEXIT */ + uint8_t mtrr : 1; + uint8_t pge : 1; /* Page Global Enable */ + uint8_t cmov : 1; + uint8_t pat : 1; + uint8_t pse36 : 1; + uint8_t mmx : 1; + uint8_t fxsr : 1; /* FXSAVE/FXRSTOR */ + uint8_t sse : 1; + uint8_t sse2 : 1; + uint8_t htt : 1; /* Hyper-Threading */ + + /* CPUID leaf 0x80000001 — EDX */ + uint8_t nx : 1; /* No-Execute (NX / XD) */ + uint8_t lm : 1; /* Long Mode (64-bit) */ + uint8_t syscall : 1; /* SYSCALL/SYSRET */ + + /* Extended info */ + uint32_t max_ext_leaf; + char brand[49]; /* CPU brand string (leaves 0x80000002-4) */ + + /* Topology (from leaf 1 EBX) */ + uint8_t initial_apic_id; + uint8_t logical_cpus; /* max logical CPUs per package */ +}; + +/* Detect CPU features. Call once during early boot. */ +void x86_cpuid_detect(struct x86_cpu_features* out); + +/* Print detected features to UART. */ +void x86_cpuid_print(const struct x86_cpu_features* f); + +#endif diff --git a/include/hal/cpu_features.h b/include/hal/cpu_features.h new file mode 100644 index 0000000..10ad173 --- /dev/null +++ b/include/hal/cpu_features.h @@ -0,0 +1,38 @@ +#ifndef HAL_CPU_FEATURES_H +#define HAL_CPU_FEATURES_H + +#include + +/* Architecture-independent CPU feature flags */ +struct cpu_features { + char vendor[13]; + char brand[49]; + + uint8_t has_apic : 1; + uint8_t has_x2apic : 1; + uint8_t has_pae : 1; + uint8_t has_nx : 1; + uint8_t has_sse : 1; + uint8_t has_sse2 : 1; + uint8_t has_fxsr : 1; + uint8_t has_sysenter : 1; /* x86 SEP / ARM SVC / RISC-V ECALL */ + uint8_t has_syscall : 1; /* x86-64 SYSCALL/SYSRET */ + uint8_t has_htt : 1; /* Hyper-Threading / SMT */ + uint8_t has_tsc : 1; + uint8_t has_msr : 1; + uint8_t is_hypervisor : 1; + + uint8_t logical_cpus; /* max logical CPUs per package */ + uint8_t initial_cpu_id; /* BSP APIC ID or equivalent */ +}; + +/* Detect and cache CPU features. Call once during early boot. */ +void hal_cpu_detect_features(void); + +/* Get pointer to the cached feature struct (valid after hal_cpu_detect_features). */ +const struct cpu_features* hal_cpu_get_features(void); + +/* Print detected features (UART). */ +void hal_cpu_print_features(void); + +#endif diff --git a/src/arch/x86/cpuid.c b/src/arch/x86/cpuid.c new file mode 100644 index 0000000..fa19226 --- /dev/null +++ b/src/arch/x86/cpuid.c @@ -0,0 +1,137 @@ +#include "arch/x86/cpuid.h" +#include "uart_console.h" +#include "utils.h" + +#include + +static inline void cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, + uint32_t* ecx, uint32_t* edx) { + __asm__ volatile("cpuid" + : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) + : "a"(leaf), "c"(0)); +} + +void x86_cpuid_detect(struct x86_cpu_features* out) { + uint32_t eax, ebx, ecx, edx; + + /* Zero everything */ + for (size_t i = 0; i < sizeof(*out); i++) + ((uint8_t*)out)[i] = 0; + + /* Leaf 0: vendor string + max standard leaf */ + cpuid(0, &eax, &ebx, &ecx, &edx); + out->max_leaf = eax; + + /* Vendor: EBX-EDX-ECX */ + *(uint32_t*)&out->vendor[0] = ebx; + *(uint32_t*)&out->vendor[4] = edx; + *(uint32_t*)&out->vendor[8] = ecx; + out->vendor[12] = '\0'; + + if (out->max_leaf < 1) return; + + /* Leaf 1: feature flags */ + cpuid(1, &eax, &ebx, &ecx, &edx); + + /* ECX features */ + out->sse3 = (ecx >> 0) & 1; + out->ssse3 = (ecx >> 9) & 1; + out->sse41 = (ecx >> 19) & 1; + out->sse42 = (ecx >> 20) & 1; + out->x2apic = (ecx >> 21) & 1; + out->avx = (ecx >> 28) & 1; + out->hypervisor = (ecx >> 31) & 1; + + /* EDX features */ + out->fpu = (edx >> 0) & 1; + out->tsc = (edx >> 4) & 1; + out->msr = (edx >> 5) & 1; + out->pae = (edx >> 6) & 1; + out->cx8 = (edx >> 8) & 1; + out->apic = (edx >> 9) & 1; + out->sep = (edx >> 11) & 1; + out->mtrr = (edx >> 12) & 1; + out->pge = (edx >> 13) & 1; + out->cmov = (edx >> 15) & 1; + out->pat = (edx >> 16) & 1; + out->pse36 = (edx >> 17) & 1; + out->mmx = (edx >> 23) & 1; + out->fxsr = (edx >> 24) & 1; + out->sse = (edx >> 25) & 1; + out->sse2 = (edx >> 26) & 1; + out->htt = (edx >> 28) & 1; + + /* Topology from EBX */ + out->initial_apic_id = (uint8_t)(ebx >> 24); + out->logical_cpus = (uint8_t)(ebx >> 16); + + /* Extended leaves */ + cpuid(0x80000000, &eax, &ebx, &ecx, &edx); + out->max_ext_leaf = eax; + + if (out->max_ext_leaf >= 0x80000001) { + cpuid(0x80000001, &eax, &ebx, &ecx, &edx); + out->nx = (edx >> 20) & 1; + out->lm = (edx >> 29) & 1; + out->syscall = (edx >> 11) & 1; + } + + /* Brand string (leaves 0x80000002 - 0x80000004) */ + if (out->max_ext_leaf >= 0x80000004) { + uint32_t* b = (uint32_t*)out->brand; + for (uint32_t leaf = 0x80000002; leaf <= 0x80000004; leaf++) { + cpuid(leaf, &eax, &ebx, &ecx, &edx); + *b++ = eax; + *b++ = ebx; + *b++ = ecx; + *b++ = edx; + } + out->brand[48] = '\0'; + } +} + +void x86_cpuid_print(const struct x86_cpu_features* f) { + uart_print("[CPUID] Vendor: "); + uart_print(f->vendor); + uart_print("\n"); + + if (f->brand[0]) { + uart_print("[CPUID] Brand: "); + uart_print(f->brand); + uart_print("\n"); + } + + uart_print("[CPUID] Features:"); + if (f->fpu) uart_print(" FPU"); + if (f->tsc) uart_print(" TSC"); + if (f->msr) uart_print(" MSR"); + if (f->pae) uart_print(" PAE"); + if (f->apic) uart_print(" APIC"); + if (f->sep) uart_print(" SEP"); + if (f->pge) uart_print(" PGE"); + if (f->mmx) uart_print(" MMX"); + if (f->fxsr) uart_print(" FXSR"); + if (f->sse) uart_print(" SSE"); + if (f->sse2) uart_print(" SSE2"); + if (f->sse3) uart_print(" SSE3"); + if (f->ssse3) uart_print(" SSSE3"); + if (f->sse41) uart_print(" SSE4.1"); + if (f->sse42) uart_print(" SSE4.2"); + if (f->avx) uart_print(" AVX"); + if (f->htt) uart_print(" HTT"); + if (f->nx) uart_print(" NX"); + if (f->lm) uart_print(" LM"); + if (f->x2apic) uart_print(" x2APIC"); + if (f->hypervisor) uart_print(" HYPERVISOR"); + if (f->syscall) uart_print(" SYSCALL"); + uart_print("\n"); + + uart_print("[CPUID] APIC ID: "); + char tmp[4]; + itoa(f->initial_apic_id, tmp, 10); + uart_print(tmp); + uart_print(", Logical CPUs: "); + itoa(f->logical_cpus, tmp, 10); + uart_print(tmp); + uart_print("\n"); +} diff --git a/src/hal/x86/cpu_features.c b/src/hal/x86/cpu_features.c new file mode 100644 index 0000000..3a561f5 --- /dev/null +++ b/src/hal/x86/cpu_features.c @@ -0,0 +1,45 @@ +#include "hal/cpu_features.h" +#include "arch/x86/cpuid.h" + +#include + +static struct cpu_features g_features; +static struct x86_cpu_features g_x86_features; + +void hal_cpu_detect_features(void) { + x86_cpuid_detect(&g_x86_features); + + /* Copy to generic struct */ + for (size_t i = 0; i < 12; i++) + g_features.vendor[i] = g_x86_features.vendor[i]; + g_features.vendor[12] = '\0'; + + for (size_t i = 0; i < 48; i++) + g_features.brand[i] = g_x86_features.brand[i]; + g_features.brand[48] = '\0'; + + g_features.has_apic = g_x86_features.apic; + g_features.has_x2apic = g_x86_features.x2apic; + g_features.has_pae = g_x86_features.pae; + g_features.has_nx = g_x86_features.nx; + g_features.has_sse = g_x86_features.sse; + g_features.has_sse2 = g_x86_features.sse2; + g_features.has_fxsr = g_x86_features.fxsr; + g_features.has_sysenter = g_x86_features.sep; + g_features.has_syscall = g_x86_features.syscall; + g_features.has_htt = g_x86_features.htt; + g_features.has_tsc = g_x86_features.tsc; + g_features.has_msr = g_x86_features.msr; + g_features.is_hypervisor = g_x86_features.hypervisor; + + g_features.logical_cpus = g_x86_features.logical_cpus; + g_features.initial_cpu_id = g_x86_features.initial_apic_id; +} + +const struct cpu_features* hal_cpu_get_features(void) { + return &g_features; +} + +void hal_cpu_print_features(void) { + x86_cpuid_print(&g_x86_features); +} diff --git a/src/kernel/cpu_features.c b/src/kernel/cpu_features.c new file mode 100644 index 0000000..c50a7cd --- /dev/null +++ b/src/kernel/cpu_features.c @@ -0,0 +1,23 @@ +#include "hal/cpu_features.h" +#include "uart_console.h" + +#include + +static struct cpu_features g_default_features; + +__attribute__((weak)) +void hal_cpu_detect_features(void) { + for (size_t i = 0; i < sizeof(g_default_features); i++) + ((uint8_t*)&g_default_features)[i] = 0; + uart_print("[CPU] No arch-specific feature detection.\n"); +} + +__attribute__((weak)) +const struct cpu_features* hal_cpu_get_features(void) { + return &g_default_features; +} + +__attribute__((weak)) +void hal_cpu_print_features(void) { + uart_print("[CPU] Feature detection not available.\n"); +} diff --git a/src/kernel/main.c b/src/kernel/main.c index 6876c4a..41ff983 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -21,6 +21,7 @@ #include "arch/arch_platform.h" #include "hal/cpu.h" +#include "hal/cpu_features.h" /* Check if the compiler thinks we are targeting the wrong operating system. */ @@ -35,6 +36,9 @@ void kernel_main(const struct boot_info* bi) { console_init(); + hal_cpu_detect_features(); + hal_cpu_print_features(); + kprintf("[AdrOS] Initializing PMM...\n"); // 2. Initialize Physical Memory Manager