From 4fe6a7aaa6e1b0e7f7678de2bbc548e9abe96bbc Mon Sep 17 00:00:00 2001 From: Julio Faracco Date: Sun, 25 Feb 2018 23:38:40 -0300 Subject: [PATCH] cpuid: Adding cpuid to detect x86 CPU information and features. This is a minimal implementation of cpuid module to detect some CPU features and other information useful in the future. Signed-off-by: Julio Faracco --- kernel/arch/include/x86/cpuid.h | 135 +++++++++++++ kernel/arch/x86/Build.mk | 2 +- kernel/arch/x86/cpuid.c | 329 ++++++++++++++++++++++++++++++++ 3 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 kernel/arch/include/x86/cpuid.h create mode 100644 kernel/arch/x86/cpuid.c diff --git a/kernel/arch/include/x86/cpuid.h b/kernel/arch/include/x86/cpuid.h new file mode 100644 index 0000000..3d70319 --- /dev/null +++ b/kernel/arch/include/x86/cpuid.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2009 RenĂª de Souza Pinto + * Tempos - Tempos is an Educational and multi purpose Operating System + * + * File: cpuid.h + * Written by: Julio Faracco + * This file is part of TempOS. + * + * TempOS is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * TempOS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef CPU_H + + #define CPU_H + + #include + + #define NCAPINTS 19 /* N 32-bit words worth of info */ + #define NBUGINTS 1 /* N 32-bit bug flags */ + + /* CPU Vendor-strings. */ + #define CPUID_VENDOR_OLDAMD "AMDisbetter!" /* early engineering samples of AMD K5 processor */ + #define CPUID_VENDOR_AMD "AuthenticAMD" + #define CPUID_VENDOR_INTEL "GenuineIntel" + #define CPUID_VENDOR_CENTAUR "CentaurHauls" + #define CPUID_VENDOR_OLDTRANSMETA "TransmetaCPU" + #define CPUID_VENDOR_TRANSMETA "GenuineTMx86" + #define CPUID_VENDOR_CYRIX "CyrixInstead" + #define CPUID_VENDOR_CENTAUR "CentaurHauls" + #define CPUID_VENDOR_NEXGEN "NexGenDriven" + #define CPUID_VENDOR_UMC "UMC UMC UMC " + #define CPUID_VENDOR_SIS "SiS SiS SiS " + #define CPUID_VENDOR_NSC "Geode by NSC" + #define CPUID_VENDOR_RISE "RiseRiseRise" + #define CPUID_VENDOR_VORTEX "Vortex86 SoC" + #define CPUID_VENDOR_VIA "VIA VIA VIA " + + /* CPU Vendor-strings from Virtual Machines.*/ + #define CPUID_VENDOR_KVM "KVMKVMKVM" + #define CPUID_VENDOR_VMWARE "VMwareVMware" + #define CPUID_VENDOR_XENHVM "XenVMMXenVMM" + #define CPUID_VENDOR_MICROSOFT_HV "Microsoft Hv" + #define CPUID_VENDOR_PARALLELS " lrpepyh vr" + #define CPUID_VENDOR_BHYVE "bhyve bhyve " + + enum cpuid_leafs { + CPUID_1_EDX=0, + CPUID_8000_0001_EDX, + CPUID_8086_0001_EDX, + CPUID_LNX_1, + CPUID_1_ECX, + CPUID_C000_0001_EDX, + CPUID_8000_0001_ECX, + CPUID_LNX_2, + CPUID_LNX_3, + + CPUID_7_0_EBX, + CPUID_D_1_EAX, + CPUID_F_0_EDX, + CPUID_F_1_EDX, + CPUID_8000_0008_EBX, + + CPUID_6_EAX, + CPUID_8000_000A_EDX, + CPUID_7_ECX, + CPUID_8000_0007_EBX, + CPUID_7_EDX, + }; + + enum cpuid_requests { + CPUID_GETVENDORSTRING=0, + CPUID_GETFEATURES, + CPUID_GETTLB, + CPUID_GETSERIAL, + CPUID_ADDITIONALCACHE, + CPUID_MWAIT, + CPUID_GETPOWER, + CPUID_INTELADDITIONAL, + + CPUID_EXTENDED=0x80000000, + CPUID_FEATURES, + CPUID_BRANDSTRING, + CPUID_BRANDSTRINGMORE, + CPUID_BRANDSTRINGEND, + CPUID_L1TLB, + CPUID_EXTENDEDL2, + CPUID_ADVANCEDPOWER, + CPUID_VIRTPHYADDR + }; + + struct cpuinfo_x86 { + uchar8_t x86; /* CPU family */ + uchar8_t x86_vendor; /* CPU vendor */ + uchar8_t x86_model; /* CPU model */ + uchar8_t x86_stepping; + /* Number of 4K pages in DTLB/ITLB combined(in pages): */ + /* For 64 bits processors. */ + //int x86_tlbsize; + uchar8_t x86_virt_bits; + uchar8_t x86_phys_bits; + uchar8_t x86_coreid_bits; + uchar8_t cu_id; + uint32_t extended_cpuid_level; + int cpuid_level; + uint32_t x86_capability[NCAPINTS + NBUGINTS]; + char x86_vendor_id[16]; + char x86_model_id[64]; + unsigned int x86_cache_size; + int x86_cache_alignment; + int x86_power; + uint16_t x86_clflush_size; + }; + + int cpuid (uint32_t leaf, uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx); + + int cpuid_count (uint32_t leaf, uint32_t subleaf, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx); + + void cpuinfo(void); + +#endif /* CPU_H */ diff --git a/kernel/arch/x86/Build.mk b/kernel/arch/x86/Build.mk index a3ed122..62c3ee5 100644 --- a/kernel/arch/x86/Build.mk +++ b/kernel/arch/x86/Build.mk @@ -5,7 +5,7 @@ # TBS - Build configuration file # -obj-y += exceptions.o gdt.o idt.o io.o dump_cpu.o +obj-y += exceptions.o gdt.o idt.o io.o dump_cpu.o cpuid.o obj-x86asm += isr.o task.o diff --git a/kernel/arch/x86/cpuid.c b/kernel/arch/x86/cpuid.c new file mode 100644 index 0000000..87c680c --- /dev/null +++ b/kernel/arch/x86/cpuid.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2009 RenĂª de Souza Pinto + * Tempos - Tempos is an Educational and multi purpose Operating System + * + * File: cpuid.c + * Written by: Julio Faracco + * Desc: Get CPU information and features + * + * This file is part of TempOS. + * + * TempOS is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * TempOS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#define __cpuid(level, a, b, c, d) \ + asm("cpuid\n\t" \ + : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \ + : "0" (level)) + +#define __cpuid_count(level, count, a, b, c, d) \ + asm("cpuid\n\t" \ + : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \ + : "0" (level), "2" (count)) + +static uint32_t +__get_cpuid_max (uint32_t ext, uint32_t *sig) +{ + uint32_t eax, ebx, ecx, edx; + asm("pushf{l|d}\n\t" + "pushf{l|d}\n\t" + "pop{l}\t%0\n\t" + "mov{l}\t{%0, %1|%1, %0}\n\t" + "xor{l}\t{%2, %0|%0, %2}\n\t" + "push{l}\t%0\n\t" + "popf{l|d}\n\t" + "pushf{l|d}\n\t" + "pop{l}\t%0\n\t" + "popf{l|d}\n\t" + : "=&r" (eax), "=&r" (ebx) + : "i" (0x00200000)); + + if (!((eax ^ ebx) & 0x00200000)) + return 0; + + __cpuid(ext, eax, ebx, ecx, edx); + + if (sig) + *sig = ebx; + + return eax; +} + +int +cpuid (uint32_t leaf, uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + uint32_t ext = leaf & 0x80000000; + uint32_t maxlevel = __get_cpuid_max(ext, 0); + + if (maxlevel == 0 || maxlevel < leaf) + return 0; + + __cpuid(leaf, *eax, *ebx, *ecx, *edx); + return 1; +} + +int +cpuid_count (uint32_t leaf, uint32_t subleaf, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + uint32_t ext = leaf & 0x80000000; + uint32_t maxlevel = __get_cpuid_max (ext, 0); + + if (maxlevel == 0 || maxlevel < leaf) + return 0; + + __cpuid_count(leaf, subleaf, *eax, *ebx, *ecx, *edx); + + return 1; +} + +uint32_t cpuid_eax(unsigned int leaf) +{ + uint32_t eax, ebx, ecx, edx; + + cpuid(leaf, &eax, &ebx, &ecx, &edx); + + return eax; +} + +uint32_t cpuid_ebx(unsigned int leaf) +{ + uint32_t eax, ebx, ecx, edx; + + cpuid(leaf, &eax, &ebx, &ecx, &edx); + + return edx; +} + +uint32_t cpuid_ecx(unsigned int leaf) +{ + uint32_t eax, ebx, ecx, edx; + + cpuid(leaf, &eax, &ebx, &ecx, &edx); + + return ecx; +} + +uint32_t cpuid_edx(unsigned int leaf) +{ + uint32_t eax, ebx, ecx, edx; + + cpuid(leaf, &eax, &ebx, &ecx, &edx); + + return edx; +} + + +void __u32_to_str(uint32_t word, char *buffer) +{ + buffer[0] = word & 0xFF; + buffer[1] = (word >> 0x08) & 0xFF; + buffer[2] = (word >> 0x10) & 0xFF; + buffer[3] = (word >> 0x18) & 0xFF; +} + +void __regs_to_str(uint32_t eax, uint32_t ebx, uint32_t ecx, + uint32_t edx, char *buffer) +{ + __u32_to_str(eax, &buffer[0]); + __u32_to_str(ebx, &buffer[4]); + __u32_to_str(ecx, &buffer[8]); + __u32_to_str(edx, &buffer[12]); +} + +unsigned int x86_family(unsigned int sig) +{ + unsigned int x86; + + x86 = (sig >> 8) & 0xf; + if (x86 == 0xf) + x86 += (sig >> 20) & 0xff; + return x86; +} + +unsigned int x86_model(unsigned int sig) +{ + unsigned int fam, model; + + fam = x86_family(sig); + model = (sig >> 4) & 0xf; + + if (fam >= 0x6) + model += ((sig >> 16) & 0xf) << 4; + return model; +} + +unsigned int x86_stepping(unsigned int sig) +{ + return sig & 0xf; +} + +void get_cpu_cap(struct cpuinfo_x86 *c) +{ + uint32_t eax, ebx, ecx, edx; + + if (c->cpuid_level >= CPUID_GETFEATURES) { + cpuid(CPUID_GETFEATURES, &eax, &ebx, &ecx, &edx); + c->x86_capability[CPUID_1_ECX] = ecx; + c->x86_capability[CPUID_1_EDX] = edx; + } + + if (c->cpuid_level >= CPUID_GETPOWER) + c->x86_capability[CPUID_6_EAX] = cpuid_eax(CPUID_GETPOWER); + + if (c->cpuid_level >= CPUID_INTELADDITIONAL) { + cpuid_count(CPUID_INTELADDITIONAL, 0, &eax, &ebx, &ecx, &edx); + c->x86_capability[CPUID_7_0_EBX] = ebx; + c->x86_capability[CPUID_7_ECX] = ecx; + c->x86_capability[CPUID_7_EDX] = edx; + } + + if (c->cpuid_level >= 0x0000000d) { + cpuid_count(0x0000000d, 1, &eax, &ebx, &ecx, &edx); + c->x86_capability[CPUID_D_1_EAX] = eax; + } + + if (c->cpuid_level >= 0x0000000F) { + cpuid_count(0x0000000F, 0, &eax, &ebx, &ecx, &edx); + c->x86_capability[CPUID_F_0_EDX] = edx; + } + + eax = cpuid_eax(CPUID_EXTENDED); + c->extended_cpuid_level = eax; + + if ((eax & 0xffff0000) == CPUID_EXTENDED) { + if (eax >= CPUID_FEATURES) { + cpuid(CPUID_FEATURES, &eax, &ebx, &ecx, &edx); + c->x86_capability[CPUID_8000_0001_ECX] = ecx; + c->x86_capability[CPUID_8000_0001_EDX] = edx; + } + } + + if (c->extended_cpuid_level >= CPUID_ADVANCEDPOWER) { + cpuid(CPUID_ADVANCEDPOWER, &eax, &ebx, &ecx, &edx); + c->x86_capability[CPUID_8000_0007_EBX] = ebx; + c->x86_power = edx; + } + + if (c->extended_cpuid_level >= CPUID_VIRTPHYADDR) { + cpuid(CPUID_VIRTPHYADDR, &eax, &ebx, &ecx, &edx); + c->x86_virt_bits = (eax >> 8) & 0xff; + c->x86_phys_bits = eax & 0xff; + c->x86_capability[CPUID_8000_0008_EBX] = ebx; + } + + if (c->extended_cpuid_level >= 0x8000000a) + c->x86_capability[CPUID_8000_000A_EDX] = cpuid_edx(0x8000000a); + +} + +void cpu_detect_cache_sizes(struct cpuinfo_x86 *c) +{ + unsigned int n, dummy, ebx, ecx, edx, l2size; + + n = c->extended_cpuid_level; + + if (n >= 0x80000005) { + cpuid(0x80000005, &dummy, &ebx, &ecx, &edx); + c->x86_cache_size = (ecx>>24) + (edx>>24); + + /* For 64 bits processor. See cpuid.h. */ + //c->x86_tlbsize = 0; + } + + if (n < 0x80000006) /* Some chips just has a large L1. */ + return; + + cpuid(0x80000006, &dummy, &ebx, &ecx, &edx); + l2size = ecx >> 16; + + /* For 64 bits processor. See cpuid.h. */ + //c->x86_tlbsize += ((ebx >> 16) & 0xfff) + (ebx & 0xfff); + + if (l2size == 0) + return; /* Again, no L2 cache is possible */ + + c->x86_cache_size = l2size; +} + +void cpu_detect(struct cpuinfo_x86 *c) +{ + cpuid(CPUID_GETVENDORSTRING, + (unsigned int *)&c->cpuid_level, + (unsigned int *)&c->x86_vendor_id[0], + (unsigned int *)&c->x86_vendor_id[8], + (unsigned int *)&c->x86_vendor_id[4]); + + c->x86 = 4; + if (c->cpuid_level >= CPUID_GETFEATURES) { + uint32_t junk, tfms, cap0, misc; + + cpuid(CPUID_GETFEATURES, &tfms, &misc, &junk, &cap0); + c->x86 = x86_family(tfms); + c->x86_model = x86_model(tfms); + c->x86_stepping = x86_stepping(tfms); + + if (cap0 & (1<<19)) { + c->x86_clflush_size = ((misc >> 8) & 0xff) * 8; + c->x86_cache_alignment = c->x86_clflush_size; + } + } +} + +static void get_model_name(struct cpuinfo_x86 *c) +{ + char *p, *q, *s; + uint32_t eax, ebx, ecx, edx; + + if (c->extended_cpuid_level < CPUID_BRANDSTRINGEND) + return; + cpuid(CPUID_BRANDSTRING, &eax, &ebx, &ecx, &edx); + __regs_to_str(eax, ebx, ecx, edx, &c->x86_model_id[0]); + cpuid(CPUID_BRANDSTRINGMORE, &eax, &ebx, &ecx, &edx); + __regs_to_str(eax, ebx, ecx, edx, &c->x86_model_id[16]); + cpuid(CPUID_BRANDSTRINGEND, &eax, &ebx, &ecx, &edx); + __regs_to_str(eax, ebx, ecx, edx, &c->x86_model_id[32]); + c->x86_model_id[48] = '\0'; + + /* Trim whitespace */ + p = q = s = &c->x86_model_id[0]; + + while (*p == ' ') + p++; + + while (*p) { + /* Note the last non-whitespace index */ + if (*p != ' ') + s = q; + *q++ = *p++; + } + + *(s + 1) = '\0'; +} + +void cpuinfo(void) +{ + struct cpuinfo_x86 cpu; + cpu_detect(&cpu); + cpu_detect_cache_sizes(&cpu); + get_cpu_cap(&cpu); + get_model_name(&cpu); + kprintf("Processor: %s\n", cpu.x86_model_id); +}