/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/ke/amd64/cpu.c * PURPOSE: Routines for CPU-level support * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Timo Kreuzer (timo.kreuzer@reactos.org) */ /* INCLUDES *****************************************************************/ #include #define NDEBUG #include /* FIXME: Local EFLAGS defines not used anywhere else */ #define EFLAGS_IOPL 0x3000 #define EFLAGS_NF 0x4000 #define EFLAGS_RF 0x10000 #define EFLAGS_ID 0x200000 /* GLOBALS *******************************************************************/ /* The Boot TSS */ KTSS64 KiBootTss; /* CPU Features and Flags */ ULONG KeI386CpuType; ULONG KeI386CpuStep; ULONG KeProcessorArchitecture; ULONG KeProcessorLevel; ULONG KeProcessorRevision; ULONG KeFeatureBits; ULONG KeI386MachineType; ULONG KeI386NpxPresent = 1; ULONG KeLargestCacheLine = 0x40; ULONG KiDmaIoCoherency = 0; CHAR KeNumberProcessors = 0; KAFFINITY KeActiveProcessors = 1; BOOLEAN KiI386PentiumLockErrataPresent; BOOLEAN KiSMTProcessorsPresent; /* Freeze data */ KIRQL KiOldIrql; ULONG KiFreezeFlag; /* Flush data */ volatile LONG KiTbFlushTimeStamp; /* CPU Signatures */ static const CHAR CmpIntelID[] = "GenuineIntel"; static const CHAR CmpAmdID[] = "AuthenticAMD"; static const CHAR CmpCyrixID[] = "CyrixInstead"; static const CHAR CmpTransmetaID[] = "GenuineTMx86"; static const CHAR CmpCentaurID[] = "CentaurHauls"; static const CHAR CmpRiseID[] = "RiseRiseRise"; /* SUPPORT ROUTINES FOR MSVC COMPATIBILITY ***********************************/ VOID NTAPI CPUID(IN ULONG InfoType, OUT PULONG CpuInfoEax, OUT PULONG CpuInfoEbx, OUT PULONG CpuInfoEcx, OUT PULONG CpuInfoEdx) { ULONG CpuInfo[4]; /* Perform the CPUID Operation */ __cpuid((int*)CpuInfo, InfoType); /* Return the results */ *CpuInfoEax = CpuInfo[0]; *CpuInfoEbx = CpuInfo[1]; *CpuInfoEcx = CpuInfo[2]; *CpuInfoEdx = CpuInfo[3]; } /* FUNCTIONS *****************************************************************/ VOID NTAPI KiSetProcessorType(VOID) { ULONG64 EFlags; INT Reg[4]; ULONG Stepping, Type; /* Start by assuming no CPUID data */ KeGetCurrentPrcb()->CpuID = 0; /* Save EFlags */ EFlags = __readeflags(); /* Do CPUID 1 now */ __cpuid(Reg, 1); /* * Get the Stepping and Type. The stepping contains both the * Model and the Step, while the Type contains the returned Type. * We ignore the family. * * For the stepping, we convert this: zzzzzzxy into this: x0y */ Stepping = Reg[0] & 0xF0; Stepping <<= 4; Stepping += (Reg[0] & 0xFF); Stepping &= 0xF0F; Type = Reg[0] & 0xF00; Type >>= 8; /* Save them in the PRCB */ KeGetCurrentPrcb()->CpuID = TRUE; KeGetCurrentPrcb()->CpuType = (UCHAR)Type; KeGetCurrentPrcb()->CpuStep = (USHORT)Stepping; /* Restore EFLAGS */ __writeeflags(EFlags); } ULONG NTAPI KiGetCpuVendor(VOID) { PKPRCB Prcb = KeGetCurrentPrcb(); INT Vendor[5]; /* Get the Vendor ID and null-terminate it */ __cpuid(Vendor, 0); /* Copy it to the PRCB and null-terminate it */ *(ULONG*)&Prcb->VendorString[0] = Vendor[1]; // ebx *(ULONG*)&Prcb->VendorString[4] = Vendor[3]; // edx *(ULONG*)&Prcb->VendorString[8] = Vendor[2]; // ecx *(ULONG*)&Prcb->VendorString[12] = 0; /* Now check the CPU Type */ if (!strcmp((PCHAR)Prcb->VendorString, CmpIntelID)) { return CPU_INTEL; } else if (!strcmp((PCHAR)Prcb->VendorString, CmpAmdID)) { return CPU_AMD; } else if (!strcmp((PCHAR)Prcb->VendorString, CmpCyrixID)) { DPRINT1("Cyrix CPUs not fully supported\n"); return 0; } else if (!strcmp((PCHAR)Prcb->VendorString, CmpTransmetaID)) { DPRINT1("Transmeta CPUs not fully supported\n"); return 0; } else if (!strcmp((PCHAR)Prcb->VendorString, CmpCentaurID)) { DPRINT1("VIA CPUs not fully supported\n"); return 0; } else if (!strcmp((PCHAR)Prcb->VendorString, CmpRiseID)) { DPRINT1("Rise CPUs not fully supported\n"); return 0; } /* Invalid CPU */ return 0; } ULONG NTAPI KiGetFeatureBits(VOID) { PKPRCB Prcb = KeGetCurrentPrcb(); ULONG Vendor; ULONG FeatureBits = KF_WORKING_PTE; INT Reg[4]; ULONG CpuFeatures = 0; /* Get the Vendor ID */ Vendor = KiGetCpuVendor(); /* Make sure we got a valid vendor ID at least. */ if (!Vendor) return FeatureBits; /* Get the CPUID Info. Features are in Reg[3]. */ __cpuid(Reg, 1); /* Set the initial APIC ID */ Prcb->InitialApicId = (UCHAR)(Reg[1] >> 24); /* Set the current features */ CpuFeatures = Reg[3]; /* Convert all CPUID Feature bits into our format */ if (CpuFeatures & 0x00000002) FeatureBits |= KF_V86_VIS | KF_CR4; if (CpuFeatures & 0x00000008) FeatureBits |= KF_LARGE_PAGE | KF_CR4; if (CpuFeatures & 0x00000010) FeatureBits |= KF_RDTSC; if (CpuFeatures & 0x00000100) FeatureBits |= KF_CMPXCHG8B; if (CpuFeatures & 0x00000800) FeatureBits |= KF_FAST_SYSCALL; if (CpuFeatures & 0x00001000) FeatureBits |= KF_MTRR; if (CpuFeatures & 0x00002000) FeatureBits |= KF_GLOBAL_PAGE | KF_CR4; if (CpuFeatures & 0x00008000) FeatureBits |= KF_CMOV; if (CpuFeatures & 0x00010000) FeatureBits |= KF_PAT; if (CpuFeatures & 0x00200000) FeatureBits |= KF_DTS; if (CpuFeatures & 0x00800000) FeatureBits |= KF_MMX; if (CpuFeatures & 0x01000000) FeatureBits |= KF_FXSR; if (CpuFeatures & 0x02000000) FeatureBits |= KF_XMMI; if (CpuFeatures & 0x04000000) FeatureBits |= KF_XMMI64; #if 0 if (Reg[2] & 0x00000001) FeatureBits |= KF_SSE3NEW; if (Reg[2] & 0x00000008) FeatureBits |= KF_MONITOR; if (Reg[2] & 0x00000200) FeatureBits |= KF_SSE3SUP; if (Reg[2] & 0x00002000) FeatureBits |= KF_CMPXCHG16B; if (Reg[2] & 0x00080000) FeatureBits |= KF_SSE41; if (Reg[2] & 0x00800000) FeatureBits |= KF_POPCNT; #endif /* Check if the CPU has hyper-threading */ if (CpuFeatures & 0x10000000) { /* Set the number of logical CPUs */ Prcb->LogicalProcessorsPerPhysicalProcessor = (UCHAR)(Reg[1] >> 16); if (Prcb->LogicalProcessorsPerPhysicalProcessor > 1) { /* We're on dual-core */ KiSMTProcessorsPresent = TRUE; } } else { /* We only have a single CPU */ Prcb->LogicalProcessorsPerPhysicalProcessor = 1; } /* Check extended cpuid features */ __cpuid(Reg, 0x80000000); if ((Reg[0] & 0xffffff00) == 0x80000000) { /* Check if CPUID 0x80000001 is supported */ if (Reg[0] >= 0x80000001) { /* Check which extended features are available. */ __cpuid(Reg, 0x80000001); /* Check if NX-bit is supported */ if (Reg[3] & 0x00100000) FeatureBits |= KF_NX_BIT; /* Now handle each features for each CPU Vendor */ switch (Vendor) { case CPU_AMD: if (Reg[3] & 0x80000000) FeatureBits |= KF_3DNOW; break; } } } /* Return the Feature Bits */ return FeatureBits; } VOID NTAPI KiGetCacheInformation(VOID) { PKIPCR Pcr = (PKIPCR)KeGetPcr(); ULONG Vendor; INT Data[4]; ULONG CacheRequests = 0, i; ULONG CurrentRegister; UCHAR RegisterByte; BOOLEAN FirstPass = TRUE; /* Set default L2 size */ Pcr->SecondLevelCacheSize = 0; /* Get the Vendor ID and make sure we support CPUID */ Vendor = KiGetCpuVendor(); if (!Vendor) return; /* Check the Vendor ID */ switch (Vendor) { /* Handle Intel case */ case CPU_INTEL: /*Check if we support CPUID 2 */ __cpuid(Data, 0); if (Data[0] >= 2) { /* We need to loop for the number of times CPUID will tell us to */ do { /* Do the CPUID call */ __cpuid(Data, 2); /* Check if it was the first call */ if (FirstPass) { /* * The number of times to loop is the first byte. Read * it and then destroy it so we don't get confused. */ CacheRequests = Data[0] & 0xFF; Data[0] &= 0xFFFFFF00; /* Don't go over this again */ FirstPass = FALSE; } /* Loop all 4 registers */ for (i = 0; i < 4; i++) { /* Get the current register */ CurrentRegister = Data[i]; /* * If the upper bit is set, then this register should * be skipped. */ if (CurrentRegister & 0x80000000) continue; /* Keep looping for every byte inside this register */ while (CurrentRegister) { /* Read a byte, skip a byte. */ RegisterByte = (UCHAR)(CurrentRegister & 0xFF); CurrentRegister >>= 8; if (!RegisterByte) continue; /* * Valid values are from 0x40 (0 bytes) to 0x49 * (32MB), or from 0x80 to 0x89 (same size but * 8-way associative. */ if (((RegisterByte > 0x40) && (RegisterByte <= 0x49)) || ((RegisterByte > 0x80) && (RegisterByte <= 0x89))) { /* Mask out only the first nibble */ RegisterByte &= 0x0F; /* Set the L2 Cache Size */ Pcr->SecondLevelCacheSize = 0x10000 << RegisterByte; } } } } while (--CacheRequests); } break; case CPU_AMD: /* Check if we support CPUID 0x80000006 */ __cpuid(Data, 0x80000000); if (Data[0] >= 6) { /* Get 2nd level cache and tlb size */ __cpuid(Data, 0x80000006); /* Set the L2 Cache Size */ Pcr->SecondLevelCacheSize = (Data[2] & 0xFFFF0000) >> 6; } break; } } VOID FASTCALL KiInitializeTss(IN PKTSS64 Tss, IN UINT64 Stack) { PKGDTENTRY64 TssEntry; /* Get pointer to the GDT entry */ TssEntry = KiGetGdtEntry(KeGetPcr()->GdtBase, KGDT64_SYS_TSS); /* Initialize the GDT entry */ KiInitGdtEntry(TssEntry, (ULONG64)Tss, sizeof(KTSS64), AMD64_TSS, 0); /* Zero out the TSS */ RtlZeroMemory(Tss, sizeof(KTSS64)); /* FIXME: I/O Map? */ Tss->IoMapBase = 0x68; /* Setup ring 0 stack pointer */ Tss->Rsp0 = Stack; /* Setup a stack for Double Fault Traps */ Tss->Ist[1] = (ULONG64)KiDoubleFaultStack; /* Setup a stack for CheckAbort Traps */ Tss->Ist[2] = (ULONG64)KiDoubleFaultStack; /* Setup a stack for NMI Traps */ Tss->Ist[3] = (ULONG64)KiDoubleFaultStack; /* Load the task register */ __ltr(KGDT64_SYS_TSS); } VOID NTAPI KeFlushCurrentTb(VOID) { /* Flush the TLB by resetting CR3 */ __writecr3(__readcr3()); } VOID NTAPI KiRestoreProcessorControlState(PKPROCESSOR_STATE ProcessorState) { /* Restore the CR registers */ __writecr0(ProcessorState->SpecialRegisters.Cr0); // __writecr2(ProcessorState->SpecialRegisters.Cr2); __writecr3(ProcessorState->SpecialRegisters.Cr3); __writecr4(ProcessorState->SpecialRegisters.Cr4); __writecr8(ProcessorState->SpecialRegisters.Cr8); /* Restore the DR registers */ __writedr(0, ProcessorState->SpecialRegisters.KernelDr0); __writedr(1, ProcessorState->SpecialRegisters.KernelDr1); __writedr(2, ProcessorState->SpecialRegisters.KernelDr2); __writedr(3, ProcessorState->SpecialRegisters.KernelDr3); __writedr(6, ProcessorState->SpecialRegisters.KernelDr6); __writedr(7, ProcessorState->SpecialRegisters.KernelDr7); /* Restore GDT, IDT, LDT and TSS */ __lgdt(&ProcessorState->SpecialRegisters.Gdtr.Limit); // __lldt(&ProcessorState->SpecialRegisters.Ldtr); // __ltr(&ProcessorState->SpecialRegisters.Tr); __lidt(&ProcessorState->SpecialRegisters.Idtr.Limit); // __ldmxcsr(&ProcessorState->SpecialRegisters.MxCsr); // FIXME // ProcessorState->SpecialRegisters.DebugControl // ProcessorState->SpecialRegisters.LastBranchToRip // ProcessorState->SpecialRegisters.LastBranchFromRip // ProcessorState->SpecialRegisters.LastExceptionToRip // ProcessorState->SpecialRegisters.LastExceptionFromRip /* Restore MSRs */ __writemsr(X86_MSR_GSBASE, ProcessorState->SpecialRegisters.MsrGsBase); __writemsr(X86_MSR_KERNEL_GSBASE, ProcessorState->SpecialRegisters.MsrGsSwap); __writemsr(X86_MSR_STAR, ProcessorState->SpecialRegisters.MsrStar); __writemsr(X86_MSR_LSTAR, ProcessorState->SpecialRegisters.MsrLStar); __writemsr(X86_MSR_CSTAR, ProcessorState->SpecialRegisters.MsrCStar); __writemsr(X86_MSR_SFMASK, ProcessorState->SpecialRegisters.MsrSyscallMask); } VOID NTAPI KiSaveProcessorControlState(OUT PKPROCESSOR_STATE ProcessorState) { /* Save the CR registers */ ProcessorState->SpecialRegisters.Cr0 = __readcr0(); ProcessorState->SpecialRegisters.Cr2 = __readcr2(); ProcessorState->SpecialRegisters.Cr3 = __readcr3(); ProcessorState->SpecialRegisters.Cr4 = __readcr4(); ProcessorState->SpecialRegisters.Cr8 = __readcr8(); /* Save the DR registers */ ProcessorState->SpecialRegisters.KernelDr0 = __readdr(0); ProcessorState->SpecialRegisters.KernelDr1 = __readdr(1); ProcessorState->SpecialRegisters.KernelDr2 = __readdr(2); ProcessorState->SpecialRegisters.KernelDr3 = __readdr(3); ProcessorState->SpecialRegisters.KernelDr6 = __readdr(6); ProcessorState->SpecialRegisters.KernelDr7 = __readdr(7); /* Save GDT, IDT, LDT and TSS */ __sgdt(&ProcessorState->SpecialRegisters.Gdtr.Limit); __sldt(&ProcessorState->SpecialRegisters.Ldtr); __str(&ProcessorState->SpecialRegisters.Tr); __sidt(&ProcessorState->SpecialRegisters.Idtr.Limit); // __stmxcsr(&ProcessorState->SpecialRegisters.MxCsr); // ProcessorState->SpecialRegisters.DebugControl = // ProcessorState->SpecialRegisters.LastBranchToRip = // ProcessorState->SpecialRegisters.LastBranchFromRip = // ProcessorState->SpecialRegisters.LastExceptionToRip = // ProcessorState->SpecialRegisters.LastExceptionFromRip = /* Save MSRs */ ProcessorState->SpecialRegisters.MsrGsBase = __readmsr(X86_MSR_GSBASE); ProcessorState->SpecialRegisters.MsrGsSwap = __readmsr(X86_MSR_KERNEL_GSBASE); ProcessorState->SpecialRegisters.MsrStar = __readmsr(X86_MSR_STAR); ProcessorState->SpecialRegisters.MsrLStar = __readmsr(X86_MSR_LSTAR); ProcessorState->SpecialRegisters.MsrCStar = __readmsr(X86_MSR_CSTAR); ProcessorState->SpecialRegisters.MsrSyscallMask = __readmsr(X86_MSR_SFMASK); } VOID NTAPI KeFlushEntireTb(IN BOOLEAN Invalid, IN BOOLEAN AllProcessors) { KIRQL OldIrql; // FIXME: halfplemented /* Raise the IRQL for the TB Flush */ OldIrql = KeRaiseIrqlToSynchLevel(); /* Flush the TB for the Current CPU, and update the flush stamp */ KeFlushCurrentTb(); /* Update the flush stamp and return to original IRQL */ InterlockedExchangeAdd(&KiTbFlushTimeStamp, 1); KeLowerIrql(OldIrql); } KAFFINITY NTAPI KeQueryActiveProcessors(VOID) { PAGED_CODE(); /* Simply return the number of active processors */ return KeActiveProcessors; } NTSTATUS NTAPI KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save) { UNIMPLEMENTED; return STATUS_UNSUCCESSFUL; } NTSTATUS NTAPI KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save) { UNIMPLEMENTED; return STATUS_UNSUCCESSFUL; } BOOLEAN NTAPI KeInvalidateAllCaches(VOID) { /* Invalidate all caches */ __wbinvd(); return TRUE; } /* * @implemented */ ULONG NTAPI KeGetRecommendedSharedDataAlignment(VOID) { /* Return the global variable */ return KeLargestCacheLine; } /* * @implemented */ VOID __cdecl KeSaveStateForHibernate(IN PKPROCESSOR_STATE State) { /* Capture the context */ RtlCaptureContext(&State->ContextFrame); /* Capture the control state */ KiSaveProcessorControlState(State); } /* * @implemented */ VOID NTAPI KeSetDmaIoCoherency(IN ULONG Coherency) { /* Save the coherency globally */ KiDmaIoCoherency = Coherency; }