From bf028daa6cb5ddd952ff2b46fe863b41bcf5d14e Mon Sep 17 00:00:00 2001 From: Ryan Breen Date: Sat, 7 Feb 2026 04:35:43 -0500 Subject: [PATCH] feat: replace fake /proc/cpuinfo with real CPU detection Add CPUID-based detection on x86_64 and system register reads (MIDR_EL1, ID_AA64ISAR0_EL1, ID_AA64PFR0_EL1, etc.) on ARM64 to populate /proc/cpuinfo with real hardware data instead of hardcoded placeholder values. x86_64 now reports: vendor, family, model, stepping, brand string, and feature flags derived from CPUID leaves 0/1/0x80000001-4. ARM64 now reports: implementer, part name, variant, revision, and feature flags derived from ID register fields. Co-Authored-By: Claude Opus 4.6 --- kernel/src/arch_impl/aarch64/cpuinfo.rs | 291 ++++++++++++++++++++++++ kernel/src/arch_impl/aarch64/mod.rs | 1 + kernel/src/arch_impl/x86_64/cpuinfo.rs | 234 +++++++++++++++++++ kernel/src/arch_impl/x86_64/mod.rs | 1 + kernel/src/fs/procfs/mod.rs | 84 +++++-- kernel/src/main.rs | 6 + kernel/src/main_aarch64.rs | 10 + 7 files changed, 608 insertions(+), 19 deletions(-) create mode 100644 kernel/src/arch_impl/aarch64/cpuinfo.rs create mode 100644 kernel/src/arch_impl/x86_64/cpuinfo.rs diff --git a/kernel/src/arch_impl/aarch64/cpuinfo.rs b/kernel/src/arch_impl/aarch64/cpuinfo.rs new file mode 100644 index 00000000..2cbeaf48 --- /dev/null +++ b/kernel/src/arch_impl/aarch64/cpuinfo.rs @@ -0,0 +1,291 @@ +//! AArch64 CPU identification via system registers. +//! +//! Reads real hardware information from ARM64 system registers +//! and presents it in a format suitable for /proc/cpuinfo. + +use alloc::string::String; +use alloc::vec::Vec; +use spin::Once; + +/// Cached CPU information, populated once at boot. +pub struct CpuInfo { + /// MIDR_EL1: Main ID Register + pub midr: u64, + /// MPIDR_EL1: Multiprocessor Affinity Register + pub mpidr: u64, + /// ID_AA64ISAR0_EL1: Instruction Set Attribute Register 0 + pub isar0: u64, + /// ID_AA64ISAR1_EL1: Instruction Set Attribute Register 1 + pub isar1: u64, + /// ID_AA64PFR0_EL1: Processor Feature Register 0 + pub pfr0: u64, + /// ID_AA64MMFR0_EL1: Memory Model Feature Register 0 + pub mmfr0: u64, +} + +static CPU_INFO: Once = Once::new(); + +/// Initialize CPU detection. Must be called once during boot. +pub fn init() { + CPU_INFO.call_once(detect_cpu); +} + +/// Get a reference to the cached CPU info. +pub fn get() -> Option<&'static CpuInfo> { + CPU_INFO.get() +} + +fn detect_cpu() -> CpuInfo { + let midr: u64; + let mpidr: u64; + let isar0: u64; + let isar1: u64; + let pfr0: u64; + let mmfr0: u64; + + unsafe { + core::arch::asm!("mrs {}, midr_el1", out(reg) midr, options(nomem, nostack)); + core::arch::asm!("mrs {}, mpidr_el1", out(reg) mpidr, options(nomem, nostack)); + core::arch::asm!("mrs {}, id_aa64isar0_el1", out(reg) isar0, options(nomem, nostack)); + core::arch::asm!("mrs {}, id_aa64isar1_el1", out(reg) isar1, options(nomem, nostack)); + core::arch::asm!("mrs {}, id_aa64pfr0_el1", out(reg) pfr0, options(nomem, nostack)); + core::arch::asm!("mrs {}, id_aa64mmfr0_el1", out(reg) mmfr0, options(nomem, nostack)); + } + + CpuInfo { + midr, + mpidr, + isar0, + isar1, + pfr0, + mmfr0, + } +} + +impl CpuInfo { + /// CPU implementer code from MIDR_EL1[31:24] + pub fn implementer(&self) -> u8 { + ((self.midr >> 24) & 0xFF) as u8 + } + + /// CPU variant from MIDR_EL1[23:20] + pub fn variant(&self) -> u8 { + ((self.midr >> 20) & 0xF) as u8 + } + + /// CPU architecture from MIDR_EL1[19:16] + pub fn architecture(&self) -> u8 { + ((self.midr >> 16) & 0xF) as u8 + } + + /// CPU part number from MIDR_EL1[15:4] + pub fn part_number(&self) -> u16 { + ((self.midr >> 4) & 0xFFF) as u16 + } + + /// CPU revision from MIDR_EL1[3:0] + pub fn revision(&self) -> u8 { + (self.midr & 0xF) as u8 + } + + /// Human-readable implementer name. + pub fn implementer_name(&self) -> &'static str { + match self.implementer() { + 0x41 => "ARM", + 0x42 => "Broadcom", + 0x43 => "Cavium", + 0x44 => "DEC", + 0x46 => "Fujitsu", + 0x48 => "HiSilicon", + 0x49 => "Infineon", + 0x4D => "Motorola/Freescale", + 0x4E => "NVIDIA", + 0x50 => "Applied Micro", + 0x51 => "Qualcomm", + 0x53 => "Samsung", + 0x56 => "Marvell", + 0x61 => "Apple", + 0x66 => "Faraday", + 0x69 => "Intel", + 0xC0 => "Ampere", + _ => "Unknown", + } + } + + /// Human-readable part name for known cores. + pub fn part_name(&self) -> &'static str { + let imp = self.implementer(); + let part = self.part_number(); + match (imp, part) { + // ARM cores + (0x41, 0xD03) => "Cortex-A53", + (0x41, 0xD04) => "Cortex-A35", + (0x41, 0xD05) => "Cortex-A55", + (0x41, 0xD07) => "Cortex-A57", + (0x41, 0xD08) => "Cortex-A72", + (0x41, 0xD09) => "Cortex-A73", + (0x41, 0xD0A) => "Cortex-A75", + (0x41, 0xD0B) => "Cortex-A76", + (0x41, 0xD0C) => "Neoverse-N1", + (0x41, 0xD0D) => "Cortex-A77", + (0x41, 0xD40) => "Neoverse-V1", + (0x41, 0xD41) => "Cortex-A78", + (0x41, 0xD44) => "Cortex-X1", + (0x41, 0xD46) => "Cortex-A510", + (0x41, 0xD47) => "Cortex-A710", + (0x41, 0xD48) => "Cortex-X2", + (0x41, 0xD49) => "Neoverse-N2", + (0x41, 0xD4A) => "Neoverse-E1", + // Apple cores (QEMU may report as these) + (0x61, 0x022) => "Icestorm (M1 efficiency)", + (0x61, 0x023) => "Firestorm (M1 performance)", + (0x61, 0x024) => "Icestorm (M1 Pro efficiency)", + (0x61, 0x025) => "Firestorm (M1 Pro performance)", + (0x61, 0x028) => "Blizzard (M2 efficiency)", + (0x61, 0x029) => "Avalanche (M2 performance)", + (0x61, 0x032) => "Sawtooth (M3 efficiency)", + (0x61, 0x033) => "Everest (M3 performance)", + // Qualcomm + (0x51, 0x800) => "Kryo 260 (A73)", + (0x51, 0x801) => "Kryo 260 (A53)", + (0x51, 0xC00) => "Falkor", + _ => "Unknown", + } + } + + /// Generate the features string from ID register fields. + pub fn features_string(&self) -> String { + let mut features: Vec<&str> = Vec::new(); + + // Check PFR0 for basic feature presence + let fp = (self.pfr0 >> 16) & 0xF; + let advsimd = (self.pfr0 >> 20) & 0xF; + + if fp < 0xF { + features.push("fp"); + } + if advsimd < 0xF { + features.push("asimd"); + } + + // ID_AA64ISAR0_EL1 fields + let aes = (self.isar0 >> 4) & 0xF; + if aes >= 1 { features.push("aes"); } + if aes >= 2 { features.push("pmull"); } + + let sha1 = (self.isar0 >> 8) & 0xF; + if sha1 >= 1 { features.push("sha1"); } + + let sha2 = (self.isar0 >> 12) & 0xF; + if sha2 >= 1 { features.push("sha2"); } + if sha2 >= 2 { features.push("sha512"); } + + let crc32 = (self.isar0 >> 16) & 0xF; + if crc32 >= 1 { features.push("crc32"); } + + let atomic = (self.isar0 >> 20) & 0xF; + if atomic >= 2 { features.push("atomics"); } + + let rdm = (self.isar0 >> 28) & 0xF; + if rdm >= 1 { features.push("asimdrdm"); } + + let sha3 = (self.isar0 >> 32) & 0xF; + if sha3 >= 1 { features.push("sha3"); } + + let sm3 = (self.isar0 >> 36) & 0xF; + if sm3 >= 1 { features.push("sm3"); } + + let sm4 = (self.isar0 >> 40) & 0xF; + if sm4 >= 1 { features.push("sm4"); } + + let dp = (self.isar0 >> 44) & 0xF; + if dp >= 1 { features.push("asimddp"); } + + let fhm = (self.isar0 >> 48) & 0xF; + if fhm >= 1 { features.push("asimdfhm"); } + + let ts = (self.isar0 >> 52) & 0xF; + if ts >= 1 { features.push("flagm"); } + if ts >= 2 { features.push("flagm2"); } + + let rndr = (self.isar0 >> 60) & 0xF; + if rndr >= 1 { features.push("rng"); } + + // ID_AA64ISAR1_EL1 fields + let dpb = self.isar1 & 0xF; + if dpb >= 1 { features.push("dcpop"); } + if dpb >= 2 { features.push("dcpodp"); } + + let jscvt = (self.isar1 >> 12) & 0xF; + if jscvt >= 1 { features.push("jscvt"); } + + let fcma = (self.isar1 >> 16) & 0xF; + if fcma >= 1 { features.push("fcma"); } + + let lrcpc = (self.isar1 >> 20) & 0xF; + if lrcpc >= 1 { features.push("lrcpc"); } + if lrcpc >= 2 { features.push("ilrcpc"); } + + let frintts = (self.isar1 >> 32) & 0xF; + if frintts >= 1 { features.push("frint"); } + + let sb = (self.isar1 >> 36) & 0xF; + if sb >= 1 { features.push("sb"); } + + let specres = (self.isar1 >> 40) & 0xF; + if specres >= 1 { features.push("specres"); } + + let bf16 = (self.isar1 >> 44) & 0xF; + if bf16 >= 1 { features.push("bf16"); } + + let i8mm = (self.isar1 >> 52) & 0xF; + if i8mm >= 1 { features.push("i8mm"); } + + // PFR0: SVE, EL levels, etc. + let sve = (self.pfr0 >> 32) & 0xF; + if sve >= 1 { features.push("sve"); } + + let dit = (self.pfr0 >> 48) & 0xF; + if dit >= 1 { features.push("dit"); } + + // MMFR0: physical address size + let parange = self.mmfr0 & 0xF; + let pa_bits = match parange { + 0 => 32, + 1 => 36, + 2 => 40, + 3 => 42, + 4 => 44, + 5 => 48, + 6 => 52, + _ => 0, + }; + if pa_bits > 0 { + // Not reported as a feature flag, but useful info + } + + let mut result = String::new(); + for (i, feat) in features.iter().enumerate() { + if i > 0 { + result.push(' '); + } + result.push_str(feat); + } + result + } + + /// Physical address bits supported. + pub fn pa_bits(&self) -> u8 { + let parange = self.mmfr0 & 0xF; + match parange { + 0 => 32, + 1 => 36, + 2 => 40, + 3 => 42, + 4 => 44, + 5 => 48, + 6 => 52, + _ => 0, + } + } +} diff --git a/kernel/src/arch_impl/aarch64/mod.rs b/kernel/src/arch_impl/aarch64/mod.rs index adf27548..87669b0f 100644 --- a/kernel/src/arch_impl/aarch64/mod.rs +++ b/kernel/src/arch_impl/aarch64/mod.rs @@ -12,6 +12,7 @@ pub mod boot; #[allow(unused_imports)] pub mod constants; pub mod cpu; +pub mod cpuinfo; pub mod elf; pub mod exception; pub mod exception_frame; diff --git a/kernel/src/arch_impl/x86_64/cpuinfo.rs b/kernel/src/arch_impl/x86_64/cpuinfo.rs new file mode 100644 index 00000000..fd0eda31 --- /dev/null +++ b/kernel/src/arch_impl/x86_64/cpuinfo.rs @@ -0,0 +1,234 @@ +//! x86_64 CPU identification via CPUID instruction. +//! +//! Reads real hardware information from the CPU using the CPUID instruction +//! and presents it in a format suitable for /proc/cpuinfo. + +use alloc::string::String; +use alloc::vec::Vec; +use core::arch::x86_64::__cpuid; +use spin::Once; + +/// Cached CPU information, populated once at boot. +pub struct CpuInfo { + /// Vendor ID string (e.g., "GenuineIntel", "AuthenticAMD") + pub vendor_id: [u8; 12], + /// Brand string from extended CPUID leaves (e.g., "Intel(R) Core(TM)...") + pub brand_string: Option<[u8; 48]>, + /// CPU family (adjusted) + pub family: u32, + /// CPU model (adjusted) + pub model: u32, + /// CPU stepping + pub stepping: u32, + /// Max standard CPUID leaf + pub max_leaf: u32, + /// Feature flags from leaf 1 ECX + pub features_ecx: u32, + /// Feature flags from leaf 1 EDX + pub features_edx: u32, + /// Extended feature flags from leaf 0x80000001 ECX + pub ext_features_ecx: u32, + /// Extended feature flags from leaf 0x80000001 EDX + pub ext_features_edx: u32, + /// Number of logical processors (from leaf 1 EBX[23:16]) + pub logical_processors: u32, + /// CLFLUSH line size (from leaf 1 EBX[15:8]) * 8 + pub clflush_size: u32, +} + +static CPU_INFO: Once = Once::new(); + +/// Initialize CPU detection. Must be called once during boot. +pub fn init() { + CPU_INFO.call_once(detect_cpu); +} + +/// Get a reference to the cached CPU info. +pub fn get() -> Option<&'static CpuInfo> { + CPU_INFO.get() +} + +fn detect_cpu() -> CpuInfo { + // Leaf 0: vendor ID and max standard leaf + let leaf0 = unsafe { __cpuid(0) }; + let max_leaf = leaf0.eax; + + let mut vendor_id = [0u8; 12]; + vendor_id[0..4].copy_from_slice(&leaf0.ebx.to_le_bytes()); + vendor_id[4..8].copy_from_slice(&leaf0.edx.to_le_bytes()); + vendor_id[8..12].copy_from_slice(&leaf0.ecx.to_le_bytes()); + + // Leaf 1: family/model/stepping and feature flags + let (family, model, stepping, features_ecx, features_edx, logical_processors, clflush_size) = + if max_leaf >= 1 { + let leaf1 = unsafe { __cpuid(1) }; + let raw_stepping = leaf1.eax & 0xF; + let raw_model = (leaf1.eax >> 4) & 0xF; + let raw_family = (leaf1.eax >> 8) & 0xF; + let ext_model = (leaf1.eax >> 16) & 0xF; + let ext_family = (leaf1.eax >> 20) & 0xFF; + + // Adjusted family/model per Intel CPUID spec + let adj_family = if raw_family == 0xF { + raw_family + ext_family + } else { + raw_family + }; + let adj_model = if raw_family == 0x6 || raw_family == 0xF { + (ext_model << 4) | raw_model + } else { + raw_model + }; + + let logical = (leaf1.ebx >> 16) & 0xFF; + let clflush = ((leaf1.ebx >> 8) & 0xFF) * 8; + + ( + adj_family, + adj_model, + raw_stepping, + leaf1.ecx, + leaf1.edx, + logical, + clflush, + ) + } else { + (0, 0, 0, 0, 0, 1, 0) + }; + + // Extended leaves + let leaf_ext0 = unsafe { __cpuid(0x80000000) }; + let max_ext_leaf = leaf_ext0.eax; + + let (ext_features_ecx, ext_features_edx) = if max_ext_leaf >= 0x80000001 { + let leaf_ext1 = unsafe { __cpuid(0x80000001) }; + (leaf_ext1.ecx, leaf_ext1.edx) + } else { + (0, 0) + }; + + // Brand string (leaves 0x80000002-0x80000004) + let brand_string = if max_ext_leaf >= 0x80000004 { + let mut brand = [0u8; 48]; + for i in 0..3u32 { + let leaf = unsafe { __cpuid(0x80000002 + i) }; + let offset = (i as usize) * 16; + brand[offset..offset + 4].copy_from_slice(&leaf.eax.to_le_bytes()); + brand[offset + 4..offset + 8].copy_from_slice(&leaf.ebx.to_le_bytes()); + brand[offset + 8..offset + 12].copy_from_slice(&leaf.ecx.to_le_bytes()); + brand[offset + 12..offset + 16].copy_from_slice(&leaf.edx.to_le_bytes()); + } + Some(brand) + } else { + None + }; + + CpuInfo { + vendor_id, + max_leaf, + brand_string, + family, + model, + stepping, + features_ecx, + features_edx, + ext_features_ecx, + ext_features_edx, + logical_processors, + clflush_size, + } +} + +impl CpuInfo { + /// Get vendor ID as a string slice. + pub fn vendor_str(&self) -> &str { + core::str::from_utf8(&self.vendor_id).unwrap_or("Unknown") + } + + /// Get brand/model name string. + pub fn brand_str(&self) -> &str { + if let Some(ref brand) = self.brand_string { + // Find the null terminator or end of array + let len = brand.iter().position(|&b| b == 0).unwrap_or(48); + core::str::from_utf8(&brand[..len]) + .unwrap_or("Unknown") + .trim() + } else { + "Unknown" + } + } + + /// Generate the flags string from CPUID feature bits. + pub fn flags_string(&self) -> String { + let mut flags: Vec<&str> = Vec::new(); + + // EDX feature flags (leaf 1) + let edx = self.features_edx; + if edx & (1 << 0) != 0 { flags.push("fpu"); } + if edx & (1 << 1) != 0 { flags.push("vme"); } + if edx & (1 << 2) != 0 { flags.push("de"); } + if edx & (1 << 3) != 0 { flags.push("pse"); } + if edx & (1 << 4) != 0 { flags.push("tsc"); } + if edx & (1 << 5) != 0 { flags.push("msr"); } + if edx & (1 << 6) != 0 { flags.push("pae"); } + if edx & (1 << 7) != 0 { flags.push("mce"); } + if edx & (1 << 8) != 0 { flags.push("cx8"); } + if edx & (1 << 9) != 0 { flags.push("apic"); } + if edx & (1 << 11) != 0 { flags.push("sep"); } + if edx & (1 << 12) != 0 { flags.push("mtrr"); } + if edx & (1 << 13) != 0 { flags.push("pge"); } + if edx & (1 << 14) != 0 { flags.push("mca"); } + if edx & (1 << 15) != 0 { flags.push("cmov"); } + if edx & (1 << 16) != 0 { flags.push("pat"); } + if edx & (1 << 17) != 0 { flags.push("pse36"); } + if edx & (1 << 19) != 0 { flags.push("clflush"); } + if edx & (1 << 23) != 0 { flags.push("mmx"); } + if edx & (1 << 24) != 0 { flags.push("fxsr"); } + if edx & (1 << 25) != 0 { flags.push("sse"); } + if edx & (1 << 26) != 0 { flags.push("sse2"); } + if edx & (1 << 28) != 0 { flags.push("ht"); } + + // ECX feature flags (leaf 1) + let ecx = self.features_ecx; + if ecx & (1 << 0) != 0 { flags.push("sse3"); } + if ecx & (1 << 1) != 0 { flags.push("pclmulqdq"); } + if ecx & (1 << 3) != 0 { flags.push("monitor"); } + if ecx & (1 << 9) != 0 { flags.push("ssse3"); } + if ecx & (1 << 12) != 0 { flags.push("fma"); } + if ecx & (1 << 13) != 0 { flags.push("cx16"); } + if ecx & (1 << 19) != 0 { flags.push("sse4_1"); } + if ecx & (1 << 20) != 0 { flags.push("sse4_2"); } + if ecx & (1 << 21) != 0 { flags.push("x2apic"); } + if ecx & (1 << 22) != 0 { flags.push("movbe"); } + if ecx & (1 << 23) != 0 { flags.push("popcnt"); } + if ecx & (1 << 25) != 0 { flags.push("aes"); } + if ecx & (1 << 26) != 0 { flags.push("xsave"); } + if ecx & (1 << 28) != 0 { flags.push("avx"); } + if ecx & (1 << 29) != 0 { flags.push("f16c"); } + if ecx & (1 << 30) != 0 { flags.push("rdrand"); } + if ecx & (1u32 << 31) != 0 { flags.push("hypervisor"); } + + // Extended EDX features (leaf 0x80000001) + let ext_edx = self.ext_features_edx; + if ext_edx & (1 << 11) != 0 { flags.push("syscall"); } + if ext_edx & (1 << 20) != 0 { flags.push("nx"); } + if ext_edx & (1 << 26) != 0 { flags.push("pdpe1gb"); } + if ext_edx & (1 << 27) != 0 { flags.push("rdtscp"); } + if ext_edx & (1 << 29) != 0 { flags.push("lm"); } + + // Extended ECX features (leaf 0x80000001) + let ext_ecx = self.ext_features_ecx; + if ext_ecx & (1 << 0) != 0 { flags.push("lahf_lm"); } + if ext_ecx & (1 << 5) != 0 { flags.push("abm"); } + if ext_ecx & (1 << 6) != 0 { flags.push("sse4a"); } + + let mut result = String::new(); + for (i, flag) in flags.iter().enumerate() { + if i > 0 { + result.push(' '); + } + result.push_str(flag); + } + result + } +} diff --git a/kernel/src/arch_impl/x86_64/mod.rs b/kernel/src/arch_impl/x86_64/mod.rs index 3fb0cb03..069f4019 100644 --- a/kernel/src/arch_impl/x86_64/mod.rs +++ b/kernel/src/arch_impl/x86_64/mod.rs @@ -16,6 +16,7 @@ #[allow(unused_imports)] pub mod constants; pub mod cpu; +pub mod cpuinfo; pub mod interrupt_frame; pub mod paging; pub mod percpu; diff --git a/kernel/src/fs/procfs/mod.rs b/kernel/src/fs/procfs/mod.rs index 92ccc1c3..16178028 100644 --- a/kernel/src/fs/procfs/mod.rs +++ b/kernel/src/fs/procfs/mod.rs @@ -473,29 +473,75 @@ fn generate_cpuinfo() -> String { #[cfg(target_arch = "x86_64")] { - format!( - "processor\t: 0\n\ - vendor_id\t: GenuineBreenix\n\ - cpu family\t: 6\n\ - model\t\t: 0\n\ - model name\t: Breenix Virtual CPU\n\ - flags\t\t: fpu vme de pse tsc msr pae mce cx8\n\ - bogomips\t: 3000.00\n\n" - ) + if let Some(info) = crate::arch_impl::x86_64::cpuinfo::get() { + format!( + "processor\t: 0\n\ + vendor_id\t: {}\n\ + cpu family\t: {}\n\ + model\t\t: {}\n\ + model name\t: {}\n\ + stepping\t: {}\n\ + cpu MHz\t\t: 0.000\n\ + cache size\t: 0 KB\n\ + physical id\t: 0\n\ + siblings\t: {}\n\ + core id\t\t: 0\n\ + cpu cores\t: 1\n\ + fpu\t\t: {}\n\ + fpu_exception\t: {}\n\ + cpuid level\t: {}\n\ + clflush size\t: {}\n\ + flags\t\t: {}\n\ + bogomips\t: 0.00\n\n", + info.vendor_str(), + info.family, + info.model, + info.brand_str(), + info.stepping, + info.logical_processors, + if info.features_edx & 1 != 0 { "yes" } else { "no" }, + if info.features_edx & 1 != 0 { "yes" } else { "no" }, + info.max_leaf, + info.clflush_size, + info.flags_string(), + ) + } else { + format!("processor\t: 0\nmodel name\t: Unknown (CPUID not initialized)\n\n") + } } #[cfg(target_arch = "aarch64")] { - format!( - "processor\t: 0\n\ - BogoMIPS\t: 100.00\n\ - Features\t: fp asimd\n\ - CPU implementer\t: 0x00\n\ - CPU architecture\t: 8\n\ - CPU variant\t: 0x0\n\ - CPU part\t: 0x000\n\ - CPU revision\t: 0\n\n" - ) + if let Some(info) = crate::arch_impl::aarch64::cpuinfo::get() { + let part_name = info.part_name(); + let impl_name = info.implementer_name(); + let model_name = if part_name != "Unknown" { + format!("{} {}", impl_name, part_name) + } else { + format!("{} (part 0x{:03x})", impl_name, info.part_number()) + }; + format!( + "processor\t: 0\n\ + BogoMIPS\t: 0.00\n\ + Features\t: {}\n\ + CPU implementer\t: 0x{:02x}\n\ + CPU architecture\t: 8\n\ + CPU variant\t: 0x{:x}\n\ + CPU part\t: 0x{:03x}\n\ + CPU revision\t: {}\n\ + Model name\t: {}\n\ + Address sizes\t: {} bits physical\n\n", + info.features_string(), + info.implementer(), + info.variant(), + info.part_number(), + info.revision(), + model_name, + info.pa_bits(), + ) + } else { + format!("processor\t: 0\nmodel name\t: Unknown (CPU detection not initialized)\n\n") + } } } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 786783e3..e16ad35c 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -359,6 +359,12 @@ fn kernel_main(boot_info: &'static mut bootloader_api::BootInfo) -> ! { crate::fs::devptsfs::init(); log::info!("devptsfs initialized at /dev/pts"); + // Detect CPU features (must be before procfs so /proc/cpuinfo has real data) + crate::arch_impl::x86_64::cpuinfo::init(); + log::info!("CPU detected: {}", crate::arch_impl::x86_64::cpuinfo::get() + .map(|c| c.brand_str()) + .unwrap_or("Unknown")); + // Initialize procfs (/proc virtual filesystem) crate::fs::procfs::init(); log::info!("procfs initialized at /proc"); diff --git a/kernel/src/main_aarch64.rs b/kernel/src/main_aarch64.rs index a883a1a9..bdf06d1b 100644 --- a/kernel/src/main_aarch64.rs +++ b/kernel/src/main_aarch64.rs @@ -327,6 +327,16 @@ pub extern "C" fn kernel_main() -> ! { kernel::fs::devptsfs::init(); serial_println!("[boot] devptsfs initialized at /dev/pts"); + // Detect CPU features (must be before procfs so /proc/cpuinfo has real data) + kernel::arch_impl::aarch64::cpuinfo::init(); + serial_println!("[boot] CPU detected: {} {}", + kernel::arch_impl::aarch64::cpuinfo::get() + .map(|c| c.implementer_name()) + .unwrap_or("Unknown"), + kernel::arch_impl::aarch64::cpuinfo::get() + .map(|c| c.part_name()) + .unwrap_or("Unknown")); + // Initialize procfs (/proc virtual filesystem) kernel::fs::procfs::init(); serial_println!("[boot] procfs initialized at /proc");