/*++ Copyright (c) 2000 Microsoft Corporation Module Name: bios.c Abstract: This module implements code to make SMI BIOS calls Author: Todd Carpenter (7/20/00) - create file Environment: Kernel mode Notes: Revision History: --*/ #include #include #include "amdk6.h" LEGACY_GEMINI_SMI LegacyInterface; #if DBG VOID DisplayGBDT ( IN PGBDT GBDT ); #else #define DisplayGBDT(_x_) #endif #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FindGBDT) #pragma alloc_text(PAGE, InitializeLegacyInterface) #pragma alloc_text(PAGE, IsGeminiSupported) #endif NTSTATUS FindGBDT ( OUT PPGBDT GeminiInfo ) /*++ Description: This routine looks in the BIOS memory area for the Gemini BIOS Descriptor Table. The signature will be located on a 16-byte bountry in the area from 0C0000h to 0FFFFFh or within the first 1K of the Extended BIOS Data Area. Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_UNSUCCESSFUL; PVOID baseAddress; PGBDT newGBDTTable; ULONG_PTR address; ULONG_PTR limit; ULONG tableLength; PHYSICAL_ADDRESS PhysAddress; DebugEnter(); PAGED_CODE(); PhysAddress.HighPart = 0; PhysAddress.LowPart = GBDT_SEARCH_RANGE_BEGIN; // // Map memory to search for Gemini Bios table // baseAddress = MmMapIoSpace(PhysAddress, GBDT_SEARCH_RANGE_LENGTH, 0); if (!baseAddress) { status = STATUS_INSUFFICIENT_RESOURCES; goto FindGBDTExit; } // // Compute limit for the for loop. Do not start a scan within 16 bytes of // physical address 0xFFFFF // address = (ULONG_PTR) baseAddress; limit = address + GBDT_SEARCH_RANGE_LENGTH - GBDT_SEARCH_INTERVAL; for (; address <= limit; address += GBDT_SEARCH_INTERVAL) { if ((((PGBDT)address)->Signature == GBDT_SIGNATURE) && (GetCheckSum((PUCHAR)address, ((PGBDT)address)->Length) == 0)) { // // We found Gemini BIOS Descriptor Table // DebugPrint((TRACE, "GBDT Pointer found at: %x\n", address)); newGBDTTable = ExAllocatePoolWithTag(NonPagedPool, ((PGBDT)address)->Length, PROCESSOR_POOL_TAG); if (!newGBDTTable) { status = STATUS_INSUFFICIENT_RESOURCES; goto FindGBDTExit; } // // Copy the Gemini BIOS Descriptor Table // RtlCopyMemory(newGBDTTable, (PVOID) address, ((PGBDT)address)->Length); // // Unmap mapped memory // MmUnmapIoSpace(baseAddress, GBDT_SEARCH_RANGE_LENGTH); if (ARGUMENT_PRESENT(GeminiInfo)) { *GeminiInfo = newGBDTTable; } status = STATUS_SUCCESS; goto FindGBDTExit; } } FindGBDTExit: DebugExitStatus(status); return status; } NTSTATUS InitializeLegacyInterface ( VOID ) /*++ Description: Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status; ULONG currentState; ULONG platform = 0; DebugEnter(); PAGED_CODE(); status = FindGBDT(&LegacyInterface.GBDT); if (!NT_SUCCESS(status)) { goto InitializeLegacyInterfaceExit; } DisplayGBDT(LegacyInterface.GBDT); // // Setup LegacyInterface structure // LegacyInterface.SmiAccessType = LegacyInterface.GBDT->SmiCommandPortType & 0x1; LegacyInterface.SmiAccessSize = (LegacyInterface.GBDT->SmiCommandPortType >> 0x4) & 0x3; LegacyInterface.SmiAddress = LegacyInterface.GBDT->SmiCommandPortAddress; LegacyInterface.GeminiCode = LegacyInterface.GBDT->GeminiSmiCode; LegacyInterface.MaxSupportedStates = LegacyInterface.GBDT->MaxStateSupported + 1; LegacyInterface.CurrentlyAvailableStates = LegacyInterface.GBDT->MaxStateSupported + 1; // // No perf states, we bail // if (LegacyInterface.CurrentlyAvailableStates == 0) { status = STATUS_UNSUCCESSFUL; goto InitializeLegacyInterfaceExit; } status = IsGeminiSupported(&platform); // // if this is not a Gemini System or not of the K6 family, fail. // if (!NT_SUCCESS(status) || (platform != PLATFORM_AMDK6)) { status = STATUS_UNSUCCESSFUL; goto InitializeLegacyInterfaceExit; } status = GetCurrentStateSmm(¤tState); if (!NT_SUCCESS(status)) { goto InitializeLegacyInterfaceExit; } DebugAssert(currentState <= LegacyInterface.GBDT->MaxStateSupported); LegacyInterface.CurrentState = CONVERT_PERF_STATE_INDEX(currentState); // // Use direct transition method instead of SMI method // EnableGeminiTransitionsMSR(&LegacyInterface.EpmIoAddress); InitializeLegacyInterfaceExit: DebugExitStatus(status); return status; } NTSTATUS IsGeminiSupported ( PULONG Results ) /*++ Description: Input: CX = 0000h ESI = Gemini_Code Output: ESI = 0 SMI was entered successfully ESI <> 0 SMI was entered, but function failed. ESI = Gemini Code, SMI was never entered CF = 0 SMM Function Successful CF = 1 SMM Function Failed, ESI = error code (see below) 0x90 requested Gemini State change not supported by hardware 0x91 the attempted State change failed upon read-back 0x92 function not supported in current implementation 0x94 the input parameters were erroneous/invalid AL = 6 (K6 Gemini platform), 7 (K7 Mustang platform) Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG esiReturned; UCHAR platform; PAGED_CODE(); // // need to check whether type is I/O or memory mapped // need to check access type size // _asm { mov ecx, IS_GEMINI_SUPPORTED mov edx, LegacyInterface.SmiAddress mov esi, LegacyInterface.GeminiCode out dx, al mov esiReturned, esi test esi, esi jnz SmmFailed mov platform, al } if (ARGUMENT_PRESENT(Results)) { ASSERT((platform == PLATFORM_AMDK6) || (platform == PLATFORM_AMDK7)); *Results = (ULONG) platform; } return STATUS_SUCCESS; SmmFailed: if (esiReturned == LegacyInterface.GeminiCode) { // // SMI failed, SMM was never entered. // } else { // // SMI was successful, but SMM operation failed (esi == failure) // } DebugPrint((ERROR, "IsGeminiSupported() Failed: ESI = 0x%x\n", esiReturned)); return STATUS_UNSUCCESSFUL; } #if 0 NTSTATUS GetAvailableStatesSmm ( PULONG Results ) /*++ Description: Input: CX = 0001h ESI = Gemini_Code Output: ESI = 0 SMI was entered successfully ESI = Gemini Code, SMI was never entered CF = 0 SMM Operation Successful CF = 1 SMM Operation Failed, ESI = error code. BX = # of vaild entries Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG esiReturned, bufferSize; USHORT stateEntries; PHYSICAL_ADDRESS stateBuffer; static AVAILABLE_STATE_INFO AvailableStates[16] = {0}; TRAP(); PAGED_CODE(); // // Allocate buffer to hold state information for each state // LegacyInterface.GBDT->CurrentlyAvailableStates * sizeof(AVAILABLE_STATE_INFO) // For now we will just use a global array set to hold max size // bufferSize = sizeof(AvailableStates); stateBuffer = MmGetPhysicalAddress((PVOID)AvailableStates); ASSERT(stateBuffer.QuadPart); _asm { mov ecx, GET_GEMINI_STATES mov edx, LegacyInterface.SmiAddress mov esi, LegacyInterface.GeminiCode mov eax, stateBuffer.LowPart mov ebx, bufferSize out dx, al mov esiReturned, esi test esi, esi jnz SmmFailed mov stateEntries, bx } if (ARGUMENT_PRESENT(Results)) { ASSERT((stateEntries > 0) || (stateEntries <= 16)); *Results = (ULONG) stateEntries; } return STATUS_SUCCESS; SmmFailed: if (esiReturned == LegacyInterface.GeminiCode) { // // SMI failed, SMM was never entered. // } else { // // SMI was successful, but SMM operation failed (esi == failure) // } DebugPrint((ERROR, "GetAvailableStatesSmm() Failed: ESI = 0x%x\n", esiReturned)); return STATUS_UNSUCCESSFUL; } NTSTATUS GetMaxStateSmm ( PULONG Results ) /*++ Description: Input: CX = 0002h ESI = Gemini_Code Output: ESI = 0 SMI was entered successfully ESI = Gemini Code, SMI was never entered CF = 0 SMM Operation Successful CF = 1 SMM Operation Failed, ESI = error code. AH = BF AL = VID BL = Gemini State number Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG esiReturned; UCHAR state; TRAP(); PAGED_CODE(); _asm { mov ecx, GET_MAX_GEMINI_STATE mov edx, LegacyInterface.SmiAddress mov esi, LegacyInterface.GeminiCode out dx, al mov esiReturned, esi test esi, esi jnz SmmFailed mov state, bl } // // NOTE: currently we don't do use the VID and BF data returned in // AX as it is a bit redundant because we should already have // that data from the Gemini Bios Descriptor Table. // if (ARGUMENT_PRESENT(Results)) { *Results = (ULONG) state; } return STATUS_SUCCESS; SmmFailed: if (esiReturned == LegacyInterface.GeminiCode) { // // SMI failed, SMM was never entered. // } else { // // SMI was successful, but SMM operation failed (esi == failure) // } DebugPrint((ERROR, "GetMaxStateSmm() Failed: ESI = 0x%x\n", esiReturned)); return STATUS_UNSUCCESSFUL; } NTSTATUS SetMaxStateSmm ( VOID ) /*++ Description: Input: CX = 0003h ESI = Gemini_Code Output: ESI = 0 SMI was entered successfully ESI = Gemini Code, SMI was never entered CF = 0 SMM Operation Successful CF = 1 SMM Operation Failed, ESI = error code. Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG esiReturned; UCHAR maxState; TRAP(); PAGED_CODE(); _asm { mov ecx, SET_MAX_GEMINI_STATE mov edx, LegacyInterface.SmiAddress mov esi, LegacyInterface.GeminiCode out dx, al mov esiReturned, esi test esi, esi jnz SmmFailed } return STATUS_SUCCESS; SmmFailed: if (esiReturned == LegacyInterface.GeminiCode) { // // SMI failed, SMM was never entered. // } else { // // SMI was successful, but SMM operation failed (esi == failure) // } DebugPrint((ERROR, "SetMaxStateSmm() Failed: ESI = 0x%x\n", esiReturned)); return STATUS_UNSUCCESSFUL; } NTSTATUS GetMinStateSmm ( PULONG Results ) /*++ Description: Input: CX = 0004h ESI = Gemini_Code Output: ESI = 0 SMI was entered successfully ESI = Gemini Code, SMI was never entered CF = 0 SMM Operation Successful CF = 1 SMM Operation Failed, ESI = error code. AH = BF AL = VID BL = Gemini State number Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG esiReturned; UCHAR state; TRAP(); PAGED_CODE(); _asm { mov ecx, GET_MIN_GEMINI_STATE mov edx, LegacyInterface.SmiAddress mov esi, LegacyInterface.GeminiCode out dx, al mov esiReturned, esi test esi, esi jnz SmmFailed mov state, bl } // // NOTE: currently we don't do use the VID and BF data returned in // AX as it is a bit redundant because we should already have // that data from the Gemini Bios Descriptor Table. // if (ARGUMENT_PRESENT(Results)) { *Results = (ULONG) state; } return STATUS_SUCCESS; SmmFailed: if (esiReturned == LegacyInterface.GeminiCode) { // // SMI failed, SMM was never entered. // } else { // // SMI was successful, but SMM operation failed (esi == failure) // } DebugPrint((ERROR, "GetMinStateSmm() Failed: ESI = 0x%x\n", esiReturned)); return STATUS_UNSUCCESSFUL; } NTSTATUS SetMinStateSmm ( VOID ) /*++ Description: Input: CX = 0005h ESI = Gemini_Code Output: ESI = 0 SMI was entered successfully ESI = Gemini Code, SMI was never entered CF = 0 SMM Operation Successful CF = 1 SMM Operation Failed, ESI = error code. Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG esiReturned; UCHAR maxState; TRAP(); PAGED_CODE(); _asm { mov ecx, SET_MIN_GEMINI_STATE mov edx, LegacyInterface.SmiAddress mov esi, LegacyInterface.GeminiCode out dx, al mov esiReturned, esi test esi, esi jnz SmmFailed } return STATUS_SUCCESS; SmmFailed: if (esiReturned == LegacyInterface.GeminiCode) { // // SMI failed, SMM was never entered. // } else { // // SMI was successful, but SMM operation failed (esi == failure) // } DebugPrint((ERROR, "SetMinStateSmm() Failed: ESI = 0x%x\n", esiReturned)); return STATUS_UNSUCCESSFUL; } NTSTATUS SetCurrentStateSmm ( ULONG State ) /*++ Description: Input: CX = 0007h ESI = Gemini_Code AL = Gemini State Number Output: ESI = 0 SMI was entered successfully ESI = Gemini Code, SMI was never entered CF = 0 SMM Operation Successful CF = 1 SMM Operation Failed, ESI = error code. Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG esiReturned, geminiState; UCHAR vidANDbf; // // Convert to Gemini State // geminiState = CONVERT_PERF_STATE_INDEX(State); // // Lookup VID and BF values for geminiState // vidANDbf = (LegacyInterface.GBDT->State[geminiState].Vid) | ((LegacyInterface.GBDT->State[geminiState].Bf) << 5); _asm { mov ecx, SET_CURRENT_GEMINI_STATE mov edx, LegacyInterface.SmiAddress mov esi, LegacyInterface.GeminiCode mov al, vidANDbf out dx, al mov esiReturned, esi test esi, esi jnz SmmFailed } return STATUS_SUCCESS; SmmFailed: if (esiReturned == LegacyInterface.GeminiCode) { // // SMI failed, SMM was never entered. // } else { // // SMI was successful, but SMM operation failed (esi == failure) // } DebugPrint((ERROR, "SetCurrentStateSmm() Failed: ESI = 0x%x\n", esiReturned)); return STATUS_UNSUCCESSFUL; } NTSTATUS ConnectDisconnectGeminiInterface ( BOOLEAN Connect ) /*++ Description: Input: CX = 0200h connect, 0201h disconnect ESI = Gemini_Code Output: ESI = 0 SMI was entered successfully ESI = Gemini Code, SMI was never entered CF = 0 SMM Operation Successful CF = 1 SMM Operation Failed, ESI = error code. Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG esiReturned, function; TRAP(); PAGED_CODE(); function = Connect ? GEMINI_CONNECT : GEMINI_DISCONNECT; _asm { mov ecx, function mov edx, LegacyInterface.SmiAddress mov esi, LegacyInterface.GeminiCode out dx, al mov esiReturned, esi test esi, esi jnz SmmFailed } return STATUS_SUCCESS; SmmFailed: if (esiReturned == LegacyInterface.GeminiCode) { // // SMI failed, SMM was never entered. // } else { // // SMI was successful, but SMM operation failed (esi == failure) // } DebugPrint((ERROR, "ConnectDisconnectGeminiInterface() Failed: ESI = 0x%x\n", esiReturned)); return STATUS_UNSUCCESSFUL; } #endif NTSTATUS GetCurrentStateSmm ( PULONG Results ) /*++ Description: Input: CX = 0006h ESI = Gemini_Code Output: ESI = 0 SMI was entered successfully ESI = Gemini Code, SMI was never entered CF = 0 SMM Operation Successful CF = 1 SMM Operation Failed, ESI = error code. AH = BF value AL = VID value BL = Gemini State number BH[0] = 0 (DC power) BH[0] = 1 (AC power) Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG esiReturned, state; UCHAR vidANDbf; PAGED_CODE(); _asm { xor ebx, ebx xor eax, eax mov ecx, GET_CURRENT_GEMINI_STATE mov edx, LegacyInterface.SmiAddress mov esi, LegacyInterface.GeminiCode out dx, al mov esiReturned, esi test esi, esi jnz SmmFailed mov vidANDbf, al } // // NOTE: currently we don't do use the VID and BF data returned in // AX as it is a bit redundant because we should already have // that data from the Gemini Bios Descriptor Table. // // we are not currentingly using the AC / DC indicator either. // this information is returned in BH [0 ]. // // // Lookup VID and BF values for geminiState // status = ConvertVidBfValueToGeminiState(vidANDbf, &state); DebugAssert(status == STATUS_SUCCESS); if (ARGUMENT_PRESENT(Results)) { *Results = (ULONG) state; } return STATUS_SUCCESS; SmmFailed: if (esiReturned == LegacyInterface.GeminiCode) { // // SMI failed, SMM was never entered. // } else { // // SMI was successful, but SMM operation failed (esi == failure) // } DebugPrint((ERROR, "GetCurrentStateSmm() Failed: ESI = 0x%x\n", esiReturned)); return STATUS_UNSUCCESSFUL; } NTSTATUS GetCpuFrequency ( IN ULONG State, OUT PULONG CpuSpeed ) /*++ Routine Description: Arguments: Return Value: --*/ { ULONG geminiState; ASSERT(LegacyInterface.GBDT); geminiState = CONVERT_PERF_STATE_INDEX(State); if (ARGUMENT_PRESENT(CpuSpeed)) { *CpuSpeed = LegacyInterface.GBDT->State[geminiState].CpuFrequency; } return STATUS_SUCCESS; } NTSTATUS GetCpuVoltage ( IN ULONG State, OUT PULONG Voltage ) /*++ Routine Description: Arguments: Return Value: --*/ { ULONG geminiState; USHORT bcdVoltage; ULONG intVoltage; DebugAssert(LegacyInterface.GBDT); geminiState = CONVERT_PERF_STATE_INDEX(State); bcdVoltage = LegacyInterface.GBDT->State[geminiState].CpuVoltage; intVoltage = Bcd8ToUlong(bcdVoltage); if (ARGUMENT_PRESENT(Voltage)) { *Voltage = intVoltage; } return STATUS_SUCCESS; } NTSTATUS GetLegacyMaxProcFrequency ( OUT PULONG CpuSpeed ) /*++ Description: Arguments: Return Value: NTSTATUS --*/ { DebugAssert(CpuSpeed); if (LegacyInterface.GBDT) { *CpuSpeed = LegacyInterface.GBDT->MaxCpuSpeed; return STATUS_SUCCESS; } return STATUS_UNSUCCESSFUL; } NTSTATUS ConvertVidBfValueToGeminiState ( IN UCHAR VidBfValue, OUT PULONG State ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; ULONG state, x; UCHAR vid, bf; vid = VidBfValue & 0x1f; // vid[4:0] bf = VidBfValue >> 5; // bf[7:5] for (x = 0; x < LegacyInterface.CurrentlyAvailableStates; x++) { if ((LegacyInterface.GBDT->State[x].Vid == vid) && (LegacyInterface.GBDT->State[x].Bf == bf)) { if (ARGUMENT_PRESENT(State)) { *State = x; } return STATUS_SUCCESS; } } DebugPrint((ERROR, "Couldn't find a match for vid=0x%x and bf=0x%x\n", vid, bf)); return STATUS_UNSUCCESSFUL; } #if DBG VOID DisplayGBDT ( IN PGBDT GBDT ) { ULONG x; DebugPrint((MAXTRACE, "\n")); DebugPrint((MAXTRACE, "Gemini BIOS Descriptor Table:\n")); DebugPrint((MAXTRACE, " Signature = %.4s\n", &GBDT->Signature)); // ULONG DebugPrint((MAXTRACE, " Length = 0x%x\n", GBDT->Length)); // UCHAR DebugPrint((MAXTRACE, " Bios Revision = 0x%x\n", GBDT->Revsion)); // UCHAR DebugPrint((MAXTRACE, " Checksum = 0x%x\n", GBDT->Checksum)); // UCHAR DebugPrint((MAXTRACE, " Capabilities = 0x%x\n", GBDT->Capabilities));// UCHAR DebugPrint((MAXTRACE, " - TALERT is %ssupported\n", (GBDT->Capabilities & 0x1) ? "" : "NOT ")); DebugPrint((MAXTRACE, " - TPANIC is %ssupported\n", (GBDT->Capabilities & 0x2) ? "" : "NOT ")); DebugPrint((MAXTRACE, " Bus Speed = %u mhz\n", GBDT->BusSpeed)); // USHORT DebugPrint((MAXTRACE, " Max CPU Speed = %u mhz\n", GBDT->MaxCpuSpeed));// USHORT DebugPrint((MAXTRACE, " Max CPU State = %u\n", GBDT->MaxStateSupported)); // UCHAR DebugPrint((MAXTRACE, " SMI Port Type = 0x%x\n", GBDT->SmiCommandPortType)); // UCHAR DebugPrint((MAXTRACE, " - %s address\n", (GBDT->SmiCommandPortType & 0x1) ? "memory mapped" : "x86 I/O")); DebugPrint((MAXTRACE, " - Data Size = %u bits\n", ((GBDT->SmiCommandPortType >> 0x4) & 0x3) * 8)); DebugPrint((MAXTRACE, " SMI Port Address = 0x%x\n", GBDT->SmiCommandPortAddress)); // ULONG DebugPrint((MAXTRACE, " Gemini Code = 0x%x\n", GBDT->GeminiSmiCode)); // ULONG DebugPrint((MAXTRACE, " Perf States:\n")); for (x=0; x <= GBDT->MaxStateSupported; x++) { DebugPrint((MAXTRACE, " State %u:\n", x)); DebugPrint((MAXTRACE, " - Voltage = 0x%x\n", GBDT->State[x].CpuVoltage)); DebugPrint((MAXTRACE, " - Frequency = %u%\n", GBDT->State[x].CpuFrequency)); DebugPrint((MAXTRACE, " - VID = 0x%x\n", GBDT->State[x].Vid)); DebugPrint((MAXTRACE, " - BF = 0x%x\n", GBDT->State[x].Bf)); } DebugPrint((MAXTRACE, "\n")); } #endif