]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: implement CPUID feature detection + HAL wrapper
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 06:53:50 +0000 (03:53 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 06:53:50 +0000 (03:53 -0300)
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.

include/arch/x86/cpuid.h [new file with mode: 0644]
include/hal/cpu_features.h [new file with mode: 0644]
src/arch/x86/cpuid.c [new file with mode: 0644]
src/hal/x86/cpu_features.c [new file with mode: 0644]
src/kernel/cpu_features.c [new file with mode: 0644]
src/kernel/main.c

diff --git a/include/arch/x86/cpuid.h b/include/arch/x86/cpuid.h
new file mode 100644 (file)
index 0000000..f033db9
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef ARCH_X86_CPUID_H
+#define ARCH_X86_CPUID_H
+
+#include <stdint.h>
+
+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 (file)
index 0000000..10ad173
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef HAL_CPU_FEATURES_H
+#define HAL_CPU_FEATURES_H
+
+#include <stdint.h>
+
+/* 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 (file)
index 0000000..fa19226
--- /dev/null
@@ -0,0 +1,137 @@
+#include "arch/x86/cpuid.h"
+#include "uart_console.h"
+#include "utils.h"
+
+#include <stddef.h>
+
+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 (file)
index 0000000..3a561f5
--- /dev/null
@@ -0,0 +1,45 @@
+#include "hal/cpu_features.h"
+#include "arch/x86/cpuid.h"
+
+#include <stddef.h>
+
+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 (file)
index 0000000..c50a7cd
--- /dev/null
@@ -0,0 +1,23 @@
+#include "hal/cpu_features.h"
+#include "uart_console.h"
+
+#include <stddef.h>
+
+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");
+}
index 6876c4a29693dba1a952742389d98e96b1774766..41ff983c75ca64c18763834f5f46f7d138748db3 100644 (file)
@@ -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