summaryrefslogtreecommitdiffstats
path: root/arch/x86/cpuid
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/cpuid')
-rw-r--r--arch/x86/cpuid/Cpuid.cpp345
-rw-r--r--arch/x86/cpuid/Cpuid.h236
-rw-r--r--arch/x86/cpuid/CpuidCheck.s21
3 files changed, 602 insertions, 0 deletions
diff --git a/arch/x86/cpuid/Cpuid.cpp b/arch/x86/cpuid/Cpuid.cpp
new file mode 100644
index 0000000..b246ef7
--- /dev/null
+++ b/arch/x86/cpuid/Cpuid.cpp
@@ -0,0 +1,345 @@
+#include "Cpuinfo.h"
+
+
+inline Cpuinfo::cpuidFunctionIndex operator++(Cpuinfo::cpuidFunctionIndex& command)
+{
+ command = Cpuinfo::cpuidFunctionIndex(int(command) + 1);
+ switch(int(command))
+ {
+ case 0x8: command = Cpuinfo::DirectCacheAccessInfo; break;
+ case 0xC: command = Cpuinfo::ExtendedStateInfo; break;
+ case 0xE: command = Cpuinfo::RDTMonitoring; break;
+ case 0x11: command = Cpuinfo::SGXInfo; break;
+ case 0x13: command = Cpuinfo::TraceInfo; break;
+ default:
+ {
+ uint32 commandInt(command);
+ if(commandInt > Cpuinfo::SOCInfo
+ and commandInt < Cpuinfo::UnimplementedStart)
+ {
+ command = Cpuinfo::UnimplementedStart;
+ }
+ else if(commandInt > Cpuinfo::UnimplementedStart
+ and commandInt < Cpuinfo::UnimplementedEnd)
+ {
+ command = Cpuinfo::UnimplementedEnd;
+ }
+ else if(commandInt > Cpuinfo::UnimplementedEnd
+ and commandInt < Cpuinfo::ExtendedCpuidStart)
+ {
+ command = Cpuinfo::ExtendedCpuidStart;
+ }
+ else if(commandInt > Cpuinfo::ExtendedCpuidEnd)
+ {
+ command = Cpuinfo::BasicInfo;
+ }
+ break;
+ }
+ };
+
+ return command;
+};
+
+Cpuinfo::Cpuinfo()
+{
+ eax = ebx = ecx = edx = 0;
+ cpuidAvailable = CPUIDCHK();
+ isBrandStringSupported = false;
+ maximumInputValue = maximumInputValueExtended = 0;
+ displayModel = displayFamily = stepping = 0;
+ brandIndex = clflushLineSize = maxNumberApicsIds = initialApicId = 0;
+ maxNumberProcessorsIds = 0;
+ currentCpuType = processorType::NotRead;
+ serialNumber = 0;
+
+ MemoryOperations::set(vendorString, '\0', vendorStringSize);
+ MemoryOperations::set(brandString, '\0', brandStringSize);
+
+ dataFilling(Cpuinfo::BasicInfo, Cpuinfo::ExtendedCpuidEnd);
+};
+
+bool Cpuinfo::isEaxValueCorrect(const cpuidFunctionIndex eaxInitValue)
+{
+ if(!cpuidAvailable) return false;
+
+ else if(eaxInitValue > maximumInputValue
+ and eaxInitValue < ExtendedCpuidStart) return false;
+
+ else if(eaxInitValue > maximumInputValueExtended
+ and eaxInitValue > ExtendedCpuidStart) return false;
+
+ else if(eaxInitValue == 0x8
+ or eaxInitValue == 0xC
+ or eaxInitValue == 0xE
+ or eaxInitValue == 0x11
+ or eaxInitValue == 0x13) return false;
+
+ else return true;
+};
+
+void Cpuinfo::setBasicData()
+{
+ maximumInputValue = eax;
+
+ uint32 vendorStringInt[3] = {ebx, edx, ecx};
+
+ MemoryOperations::copy(vendorStringInt, vendorString, sizeof(uint32) * 3);
+};
+
+void Cpuinfo::setVersionData()
+{
+ uint8 steppingId, modelId, familyId, modelIdExt, familyIdExt;
+
+ steppingId = eax & 0xf;
+ modelId = (eax >> 4) & 0xf;
+ familyId = (eax >> 8) & 0xf;
+ modelIdExt = (eax >> 16) & 0xf;
+ familyIdExt = (eax >> 20) & 0xf;
+
+ stepping = steppingId;
+ currentCpuType = (processorType)((eax >> 12) & 0x3);
+
+ if(familyId != 0xf)
+ {
+ displayFamily = familyId;
+ }
+ else displayFamily = familyIdExt + familyId;
+
+ if(familyId == 0x6 or familyId == 0xf)
+ {
+ displayModel = (modelIdExt << 4) + modelId;
+ }
+ else displayModel = modelId;
+
+ brandIndex = (uint8)ebx;
+ clflushLineSize = ((uint8)(ebx >> 8)) << 3;
+ maxNumberProcessorsIds = (ebx >> 16) & 0x0F;
+ initialApicId = ebx >> 24;
+
+ if(edx & Cpuinfo::HTT)
+ {
+ uint8 powerOfTwo = 1;
+ for(; powerOfTwo < maxNumberProcessorsIds; powerOfTwo *= 2);
+ if(maxNumberProcessorsIds == NULL) maxNumberApicsIds = NULL;
+ else maxNumberApicsIds = powerOfTwo;
+ }
+};
+
+void Cpuinfo::setInternalData()
+{
+ uint32 regs[4] = {eax, ebx, ecx, edx};
+ uint8 idxDesc = 0;
+
+ for(uint8 idx = 0; idx < 4; idx++)
+ {
+ uint32 &reg = regs[idx];
+ for(uint8 bytes = 0; bytes < 4; bytes++)
+ {
+ internalInfoDescriptors[idxDesc++] = reg & 0xff;
+ reg >>= 8;
+ }
+ }
+};
+
+void Cpuinfo::setSerialData()
+{
+ if(cpuid(VersionInfo)
+ || !(edx & Cpuinfo::PSN)
+ || cpuid(SerialNumberInfo)) return;
+
+ serialNumber = ((uint64)ecx) | (((uint64)edx) << 32);
+};
+
+void Cpuinfo::setExtendedBasicData()
+{
+ if(eax & Cpuinfo::ExtendedBasicInfo)
+ {
+ maximumInputValueExtended = eax;
+ isBrandStringSupported = true;
+ }
+ else isBrandStringSupported = false;
+};
+
+void Cpuinfo::setBrandString(brandStringPart part)
+{
+ if((part == Cpuinfo::brandString_low or
+ part == Cpuinfo::brandString_middle or
+ part == Cpuinfo::brandString_hight)
+ and isBrandStringSupported)
+ {
+ uint8 partUint = uint8(part);
+ uint32 regs[4] = {eax, ebx, ecx, edx};
+
+ MemoryOperations::copy(regs, &brandString[partUint], sizeof(uint32) * 4);
+ }
+};
+
+bool Cpuinfo::decodeCpuidResult(const cpuidFunctionIndex eaxValue)
+{
+ switch(eaxValue)
+ {
+ case Cpuinfo::BasicInfo: setBasicData(); break;
+ case Cpuinfo::VersionInfo: setVersionData(); break;
+ case Cpuinfo::InternalInfo: setInternalData(); break;
+ case Cpuinfo::SerialNumberInfo: setSerialData(); break;
+ case Cpuinfo::MonitorMwaitInfo: break;
+ case Cpuinfo::ThermalPowerInfo: break;
+ case Cpuinfo::DirectCacheAccessInfo: break;
+ case Cpuinfo::PerformanceMonitoring: break;
+ case Cpuinfo::ClockInfo: break;
+ case Cpuinfo::FrequencyInfo: break;
+ case Cpuinfo::ExtendedBasicInfo: setExtendedBasicData(); break;
+ case Cpuinfo::MoreFeatureInfo: break;
+ case Cpuinfo::BrandStringLow: setBrandString(brandString_low); break;
+ case Cpuinfo::BrandStringMiddle: setBrandString(brandString_middle); break;
+ case Cpuinfo::BrandStringHight: setBrandString(brandString_hight); break;
+ case Cpuinfo::CacheInfo: break;
+ case Cpuinfo::MiscFeatureInfo: break;
+ case Cpuinfo::AddressSize: break;
+ default: return true; break;
+ };
+
+ return false;
+};
+
+bool Cpuinfo::decodeCpuidCommand(const cpuidFunctionIndex eaxValue)
+{
+ if(cpuid(eaxValue))
+ {
+ return true;
+ }
+ else
+ {
+ return decodeCpuidResult(eaxValue);
+ }
+};
+
+bool Cpuinfo::cpuid(const cpuidFunctionIndex eaxValue)
+{
+ // Clear registers
+ eax = ebx = ecx = edx = 0;
+
+ if(!isEaxValueCorrect(eaxValue)
+ or isCommandSupportSubLeaf(eaxValue)) return true;
+
+ asm("movl %[eaxValue], %%eax\n\t"
+ "movl %0, %%eax\n\t"
+ "cpuid\n\t"
+ "movl %%eax, %0\n\t"
+ "movl %%ebx, %1\n\t"
+ "movl %%ecx, %2\n\t"
+ "movl %%edx, %3\n\t"
+ : "=r" (eax), "=r" (ebx), "=r" (ecx), "=r" (edx)
+ : [eaxValue] "r" (eaxValue));
+
+ return false;
+};
+
+bool Cpuinfo::cpuid(const cpuidFunctionIndex eaxValue, uint32 ecxValue)
+{
+ // Clear registers
+ eax = ebx = ecx = edx = 0;
+
+ if(!isEaxValueCorrect(eaxValue)
+ or !isCommandSupportSubLeaf(eaxValue)) return true;
+
+ asm("movl %[eaxValue], %%eax\n\t"
+ "movl %[ecxValue], %%ecx\n\t"
+ "cpuid\n\t"
+ "movl %%eax, %0\n\t"
+ "movl %%ebx, %1\n\t"
+ "movl %%ecx, %2\n\t"
+ "movl %%edx, %3\n\t"
+ : "=r" (eax), "=r" (ebx), "=r" (ecx), "=r" (edx)
+ : [eaxValue] "r" (eaxValue), [ecxValue] "r" (ecxValue));
+
+ return false;
+};
+
+bool Cpuinfo::isCommandSupportSubLeaf(const cpuidFunctionIndex eaxValue)
+{
+ switch(eaxValue)
+ {
+ case DeterministicCacheInfo: return true; break;
+ case FeatureInfo: return true; break;
+ case ExtendedTopologyInfo: return true; break;
+ case ExtendedStateInfo: return true; break;
+ case RDTMonitoring: return true; break;
+ case AllocationInfo: return true; break;
+ case SGXInfo: return true; break;
+ case TraceInfo: return true; break;
+ case SOCInfo: return true; break;
+ default: return false; break;
+ };
+};
+
+const char* Cpuinfo::getVendorString()
+{
+ const char* vendorStringInvalid = "VendorStringInvalid";
+
+ if(maximumInputValue != NULL) return (const char *)vendorString;
+ else return vendorStringInvalid;
+};
+
+void Cpuinfo::dataFilling(cpuidFunctionIndex startValue,
+ cpuidFunctionIndex endValue)
+{
+ cpuidFunctionIndex command = startValue;
+ uint32 ecx = 0;
+
+ do{
+ if(isEaxValueCorrect(command))
+ {
+ if(isCommandSupportSubLeaf(command))
+ {
+ cpuid(command, ecx);
+ }
+ else decodeCpuidCommand(command);
+ }
+ }while(++command <= endValue and command != Cpuinfo::BasicInfo);
+};
+
+cpuidOutputRegisters Cpuinfo::getRegs()
+{
+ return {eax, ebx, ecx, edx};
+};
+
+uint8 Cpuinfo::getDisplayFamily()
+{
+ return displayFamily;
+};
+
+uint8 Cpuinfo::getDisplayModel()
+{
+ return displayModel;
+};
+
+uint8 Cpuinfo::getStepping()
+{
+ return stepping;
+};
+
+Cpuinfo::processorType Cpuinfo::getProcessorType()
+{
+ return currentCpuType;
+};
+
+uint8 Cpuinfo::getMaxFunctionIndex()
+{
+ return maximumInputValue;
+};
+
+uint32 Cpuinfo::getMaxFunctionIndexExtended()
+{
+ return maximumInputValueExtended;
+};
+
+const char* Cpuinfo::getBrandString()
+{
+ return (const char*)brandString;
+};
+
+uint8* Cpuinfo::getInternalDescriptors()
+{
+ return internalInfoDescriptors;
+};
diff --git a/arch/x86/cpuid/Cpuid.h b/arch/x86/cpuid/Cpuid.h
new file mode 100644
index 0000000..42fb692
--- /dev/null
+++ b/arch/x86/cpuid/Cpuid.h
@@ -0,0 +1,236 @@
+#ifndef CPUID_H
+#define CPUID_H
+#include <types.h>
+#include <MemoryOperations.h>
+
+// To declare cpuid::Executor::Executor()
+#include "../../../kernel/details/Platform.h"
+
+
+namespace cpuid
+{
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+ /**
+ * Existence check on cpuid command
+ *
+ * @return true if cpuid available, false otherwise
+ */
+ bool CPUIDCHK();
+#ifdef __cplusplus
+};
+#endif
+
+/**
+ * Structure to keep results of cpuid execution command
+ */
+struct cpuidOutRegs
+{
+ uint32 eax;
+ uint32 ebx;
+ uint32 ecx;
+ uint32 edx;
+ /**
+ * Check on null
+ *
+ * @return True if all fields are null, false otherwise
+ */
+ bool isNull();
+};
+
+/**
+ * Perform cpuid instruction with only leaf
+ *
+ * @param initial value passes to eax register
+ *
+ * @return output registers (eax, ebx, ecx, edx)
+ */
+inline cpuidOutRegs cpuid(uint32 eax);
+
+
+/**
+ * Perform cpuid instruction with leaf and subleaf
+ *
+ * @param initial value passes to eax register
+ * @param number of subleaf passes to ecx register
+ *
+ * @return output registers (eax, ebx, ecx, edx)
+ */
+inline cpuidOutRegs cpuid(uint32 eax, uint32 ecx);
+
+/**
+ * Data representation for cpuid command.
+ * Used to inherits from classes which commands are passing only initial value
+ *
+ * @note Only for inheritance
+ */
+struct CmdWithNoSubleaf
+{
+ const uint32 initVal;
+ // This field is always handle by validator function. See `valid`
+ // function description below
+ bool isValid;
+
+ CmdWithNoSubleaf() = delete;
+
+protected:
+ CmdWithNoSubleaf(uint32 cmdPass) : initVal(cmdPass), isValid(true) {};
+};
+
+/**
+ * This class used for additional value for subleaf
+ *
+ * @note Also for inheritance purposes only
+ */
+struct CmdWithSubleaf : public CmdWithNoSubleaf
+{
+ const uint32 subleaf;
+
+ CmdWithSubleaf() = delete;
+
+protected:
+ CmdWithSubleaf(uint32 cmdp, uint32 subleafp) : CmdWithNoSubleaf(cmdp),
+ subleaf(subleafp){};
+};
+
+/**
+ * Provides details for cpuid(0h) instruction
+ */
+struct BasicInfo : public CmdWithNoSubleaf
+{
+ constexpr static uint32 initialValue = 0;
+ uint32 maxCmdForBasicInfo;
+ char manufacturerString[13];
+
+ BasicInfo() : CmdWithNoSubleaf(initialValue) {};
+};
+
+/**
+ * Provides details for cpuid(1h) instruction
+ */
+struct VersionInfo : public CmdWithNoSubleaf
+{
+ constexpr static uint32 initialValue = 0;
+ VersionInfo() : CmdWithNoSubleaf(initialValue) {};
+
+ inline uint8 getFamilyId();
+ inline uint8 getModelId();
+private:
+ struct eaxVersionInformation
+ {
+ uint8 steppingId : 4,
+ modelId : 4,
+ familyId : 4,
+ processorType : 2,
+ extendedModelId : 4,
+ extendedFamilyId : 4;
+ };
+
+ struct ebxVersionInformation
+ {
+ uint8 brandIndex,
+ lineSize,
+ maxNumberId,
+ initialApicId;
+ };
+
+public:
+ eaxVersionInformation eax;
+ ebxVersionInformation ebx;
+ uint32 ecx, edx;
+
+ // ECX register flags
+ enum class EcxFlags : uint32
+ {
+ SSE3 = 1,
+ TablePCLMULQDQ = (1 << 1),
+ DTES64 = (1 << 2),
+ MONITOR = (1 << 3),
+ DS_CPL = (1 << 4),
+ VMX = (1 << 5),
+ SMX = (1 << 6),
+ EIST = (1 << 7),
+ TM2 = (1 << 8),
+ SSSE3 = (1 << 9),
+ SNXT_ID = (1 << 10),
+ SDBG = (1 << 11),
+ FMA = (1 << 12),
+ CMPXCHG16B = (1 << 13),
+ xTPR_UPDATECONTROL = (1 << 14),
+ PDCM = (1 << 15),
+ PCID = (1 << 17),
+ DCA = (1 << 18),
+ SSE4_1 = (1 << 19),
+ SSE4_2 = (1 << 20),
+ x2APIC = (1 << 21),
+ MOVBE = (1 << 22),
+ POPCNT = (1 << 23),
+ TSC_DEADLINE = (1 << 24),
+ AESNI = (1 << 25),
+ XSAVE = (1 << 26),
+ OSXSAVE = (1 << 27),
+ AVX = (1 << 28),
+ F16C = (1 << 29),
+ RDRAND = (1 << 30),
+ };
+
+ // EDX register flags
+ enum class EdxFlags : uint32
+ {
+ FPU = 1,
+ VME = (1 << 1),
+ DE = (1 << 2),
+ PSE = (1 << 3),
+ TSC = (1 << 4),
+ MSR = (1 << 5),
+ PAE = (1 << 6),
+ MCE = (1 << 7),
+ CX8 = (1 << 8),
+ APIC = (1 << 9),
+ SEP = (1 << 11),
+ MTRR = (1 << 12),
+ PGE = (1 << 13),
+ MCA = (1 << 14),
+ CMOV = (1 << 15),
+ PAT = (1 << 16),
+ PSE_36 = (1 << 17),
+ PSN = (1 << 18),
+ CLFSH = (1 << 19),
+ DS = (1 << 21),
+ ACPI = (1 << 22),
+ MMX = (1 << 23),
+ FXSR = (1 << 24),
+ SSE = (1 << 25),
+ SSE2 = (1 << 26),
+ SS = (1 << 27),
+ HTT = (1 << 28),
+ TM = (1 << 29),
+ PBE = (1 << 31),
+ };
+};
+
+
+/**
+ * Getters are acquire information from cpuid instruction
+ * and then put in some cpuid command instance
+ *
+ * @param cpuid command which needs to acquire processor details by one of some
+ * commands
+ */
+void acquireInformation(BasicInfo &cpuidRes);
+void acquireInformation(VersionInfo &cpuidRes);
+
+/**
+ * Validators are check filling correctness for some cpuid command instance
+ *
+ * @param[out] cpuid command instance (as ins)
+ *
+ * @result precised `ins.isValid` value
+ */
+bool validate(/*Reference command type*/);
+
+
+};
+#endif
diff --git a/arch/x86/cpuid/CpuidCheck.s b/arch/x86/cpuid/CpuidCheck.s
new file mode 100644
index 0000000..67bc9eb
--- /dev/null
+++ b/arch/x86/cpuid/CpuidCheck.s
@@ -0,0 +1,21 @@
+global CPUIDCHK:function
+CPUIDCHK:
+ push ebx
+ pushfd
+ pop eax
+ mov ebx, eax
+ xor eax, 0x200000
+ push eax
+ popfd
+ pushfd
+ pop eax
+ xor eax, ebx
+ je nocpuid
+ mov eax, 1
+ jmp restore
+nocpuid:
+ mov eax, 0
+restore:
+ pop ebx
+ ret
+.endchk: