diff options
Diffstat (limited to 'arch/x86/cpuid')
| -rw-r--r-- | arch/x86/cpuid/Cpuid.cpp | 345 | ||||
| -rw-r--r-- | arch/x86/cpuid/Cpuid.h | 236 | ||||
| -rw-r--r-- | arch/x86/cpuid/CpuidCheck.s | 21 |
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 ® = 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: |
