/*++ Copyright (c) 1989 Microsoft Corporation Module Name: cyrix.c Abstract: Detects and initializes Cryix processors Author: Ken Reneris (kenr) 24-Feb-1994 Environment: Kernel mode only. --*/ #include "ki.h" #define Cx486_SLC 0x0 #define Cx486_DLC 0x1 #define Cx486_SLC2 0x2 #define Cx486_DLC2 0x3 #define Cx486_SRx 0x4 // Retail Upgrade Cx486SLC #define Cx486_DRx 0x5 // Retail Upgrade Cx486DLC #define Cx486_SRx2 0x6 // Retail Upgrade 2x Cx486SLC #define Cx486_DRx2 0x7 // Retail Upgrade 2x Cx486DLC #define Cx486DX 0x1a #define Cx486DX2 0x1b #define M1 0x30 #define CCR0 0xC0 #define CCR1 0xC1 #define CCR2 0xC2 #define CCR3 0xC3 #define DIR0 0xFE #define DIR1 0xFF // SRx & DRx flags #define CCR0_NC0 0x01 // No cache 64k @ 1M boundaries #define CCR0_NC1 0x02 // No cache 640k - 1M #define CCR0_A20M 0x04 // Enables A20M# #define CCR0_KEN 0x08 // Enables KEN# #define CCR0_FLUSH 0x10 // Enables FLUSH# // DX flags #define CCR1_NO_LOCK 0x10 // Ignore lock prefixes ULONG Ke386CyrixId (VOID); UCHAR ReadCyrixRegister (IN UCHAR Register); VOID WriteCyrixRegister (IN UCHAR Register, IN UCHAR Value); VOID Ke386ConfigureCyrixProcessor (VOID); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,Ke386CyrixId) #pragma alloc_text(PAGELK,Ke386ConfigureCyrixProcessor) #endif extern UCHAR CmpCyrixID[]; ULONG Ke386CyrixId ( VOID ) /*++ Routine Description: Detects and returns the Cyrix ID of the processor. This function only detects Cyrix processors which have internal cache support. Arguments: Configure - If TRUE, causes this function to alter the Cyrix CCR registers for the optimal NT performance. If FALSE, the processors configuration is not altered. Return Value: Cyrix ID of the processor 0 if not a Cyrix processor --*/ { ULONG CyrixID; UCHAR r3, c; UCHAR flags; PKPRCB Prcb; CyrixID = 0; Prcb = KeGetCurrentPrcb(); if (Prcb->CpuID && strcmp (Prcb->VendorString, CmpCyrixID)) { // Not a Cyrix processor return 0; } // Test Div instruction to see if the flags // do not get altered _asm { xor eax, eax sahf ; flags = ah lahf ; ah = flags mov flags, ah ; save flags mov eax, 5 mov ecx, 2 div cl ; 5 / 2 = ? lahf sub flags, ah ; flags = orig_flags - new_flags } if (flags == 0) { // See if the Cyrix CCR3 register bit 0x80 can be editted. r3 = ReadCyrixRegister(CCR3); // Read CCR3 c = r3 ^ 0x80; // flip bit 80 WriteCyrixRegister(CCR3, c); // Write CCR3 ReadCyrixRegister(CCR0); // select new register c = ReadCyrixRegister(CCR3); // Read new CCR3 value if (ReadCyrixRegister(CCR3) != r3) { // Read the Cyrix ID type register CyrixID = ReadCyrixRegister(DIR0) + 1; } WriteCyrixRegister(CCR3, r3); // restore original CCR3 value } if (CyrixID > 0x7f) { // invalid setting CyrixID = 0; } return CyrixID; } static UCHAR ReadCyrixRegister ( IN UCHAR Register ) /*++ Routine Description: Reads an internal Cyrix ID register. Note the internal register space is accessed via I/O addresses which are hooked internally to the processor. The caller is responsible for only calling this function on a Cyrix processor. Arguments: Register - Which Cyrix register to read Return Value: The registers value --*/ { UCHAR Value; _asm { mov al, Register cli out 22h, al in al, 23h sti mov Value, al } return Value; } static VOID WriteCyrixRegister ( IN UCHAR Register, IN UCHAR Value ) /*++ Routine Description: Write an internal Cyrix ID register. Note the internal register space is accessed via I/O addresses which are hooked internally to the processor. The caller is responsible for only calling this function on a Cyrix processor. Arguments: Register - Which Cyrix register to written Value - Value to write into the register Return Value: The registers value --*/ { _asm { mov al, Register mov cl, Value cli out 22h, al mov al, cl out 23h, al sti } } VOID Ke386ConfigureCyrixProcessor (VOID) { UCHAR r0, r1; ULONG id, rev; PVOID LockHandle; PAGED_CODE(); id = Ke386CyrixId(); if (id) { LockHandle = MmLockPagableCodeSection (&Ke386ConfigureCyrixProcessor); id = id - 1; rev = ReadCyrixRegister(DIR1); if ((id >= 0x20 && id <= 0x27) || ((id & 0xF0) == M1 && rev < 0x17)) { // These steppings have a write-back cache problem. // On these chips the L1 w/b cache can be disabled by // setting only the NW bit. _asm { cli mov eax, cr0 or eax, CR0_NW mov cr0, eax sti } } switch (id) { case Cx486_SRx: case Cx486_DRx: case Cx486_SRx2: case Cx486_DRx2: // These processors have an internal cache feature // let's turn it on. r0 = ReadCyrixRegister(CCR0); r0 |= CCR0_NC1 | CCR0_FLUSH; r0 &= ~CCR0_NC0; WriteCyrixRegister(CCR0, r0); // Clear Non-Cacheable Region 1 WriteCyrixRegister(0xC4, 0); WriteCyrixRegister(0xC5, 0); WriteCyrixRegister(0xC6, 0); break; case Cx486DX: case Cx486DX2: // Set NO_LOCK flag on these processors according to // the number of booted processors r1 = ReadCyrixRegister(CCR1); r1 |= CCR1_NO_LOCK; if (KeNumberProcessors > 1) { r1 &= ~CCR1_NO_LOCK; } WriteCyrixRegister(CCR1, r1); break; } MmUnlockPagableImageSection (LockHandle); } }