windows-nt/Source/XPSP1/NT/base/boot/detect/i386/hwpbiosc.c
2020-09-26 16:20:57 +08:00

762 lines
24 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990, 1991 Microsoft Corporation
Module Name:
hwpbiosc.c
Abstract:
This modules contains PnP BIOS C supporting routines
Author:
Shie-Lin Tzong (shielint) 20-Apr-1995
Environment:
Real mode.
Revision History:
Doug Fritz (dfritz) 02-Oct-1997
- Add: get docking station info from PnP BIOS and pass to NTLDR
Alan Warwick (alanwar) 10-Feb-1998
- Add: get SMBIOS tables from PnP BIOS and pass to NTLDR
--*/
#include "hwdetect.h"
#include <string.h>
#include "smbios.h"
#include "pnpbios.h"
//
// Some global variables referenced by other routines
//
BOOLEAN SystemHas8259 = FALSE;
BOOLEAN SystemHas8253 = FALSE;
USHORT HwSMBIOSStructureLength(
FPSMBIOS_STRUCT_HEADER StructHeader,
USHORT MaxStructLen
)
{
USHORT length;
UCHAR type;
FPUCHAR stringPtr;
type = StructHeader->Type;
length = StructHeader->Length;
//
// The length of an SMBIOS structure can be computed by adding the size
// specified in the structure header plus the space used by the string
// table that immediately follows the structure header. The size of the
// string table is determined by scanning for a double NUL. A problem is
// that those structures that do not contain strings do not have a
// double NUL to indicate an empty string table. However since we do
// initialize the entire buffer to 0 before calling the bios there
// will always be a double nul at the end regardless of how the bios
// fills writes the structure.
stringPtr = (FPUCHAR)StructHeader + StructHeader->Length;
//
// Loop one byte at a time until double NUL is found
while ((*((FPUSHORT)stringPtr) != 0) && (length < MaxStructLen))
{
stringPtr++;
length++;
}
#if DBG
if (length == MaxStructLen)
{
BlPrint("HwSMBIOSStructureLength: structure overflow 0x%x\n", length);
}
#endif
return(length);
}
USHORT HwGetSMBIOSInfo(
ENTRY_POINT BiosEntry,
USHORT RealModeDataBaseAddress,
USHORT StructBufferSize,
FPUCHAR StructBuffer
)
/*++
Routine Description:
This routine determine if SMBIOS information is available in the system
and if so then collect the size needed for all of the information and
actually collect the information.
The SMBIOS tables are packed into a buffer end to end. The length of each
SMBIOS table is determined by the length in the structure header plus
any memory used by the stirng space immediately after the fixed portion
of the structure. The string space is terminated by a double NUL. However
some structure types do not contain strings and thus do not have a
string space so the length of the structure is simply the length specified
in the structure header. However this routine will append a double NUL
to those structures anyway so that the total length of each structure
within the buffer can be determined by finding the first double NUL after
the length declared in the structure header.
Arguments:
BiosEntry is the real mode entrypoint to the PnP bios
RealModeDataBaseAddress
StructBufferSize is the maximum number of bytes available to write in
StructBuffer
StructBuffer is the buffer in which to write the SMBIOS data. If this is
NULL then only the size needed to write the data is determined.
Return Value:
Size of SMBIOS structures
--*/
{
USHORT retCode;
USHORT numberStructures;
USHORT maxStructSize;
ULONG dmiStorageBase;
USHORT dmiStorageSize;
UCHAR dmiBiosRevision;
ULONG romAddr, romEnd;
FPSMBIOS_EPS_HEADER header;
FPDMIBIOS_EPS_HEADER dmiHeader;
FPUCHAR current;
UCHAR sum;
USHORT j;
USHORT structCount;
USHORT structNumber;
USHORT dmiStorageSegment;
USHORT totalStructSize = 0;
USHORT checkLength;
FPSMBIOS_STRUCT_HEADER structHeader;
USHORT length, lengthNeeded;
FPUCHAR tempStructBuffer;
#if DBG
BlPrint("GetSMBIOSInfo: Determining SMBIOS - Try for table\n");
#endif
MAKE_FP(current, PNP_BIOS_START);
romAddr = PNP_BIOS_START;
romEnd = PNP_BIOS_END;
checkLength = 0;
while (romAddr < romEnd) {
header = (FPSMBIOS_EPS_HEADER)current;
dmiHeader = (FPDMIBIOS_EPS_HEADER)current;
if ((dmiHeader->Signature2[0] == '_') &&
(dmiHeader->Signature2[1] == 'D') &&
(dmiHeader->Signature2[2] == 'M') &&
(dmiHeader->Signature2[3] == 'I') &&
(dmiHeader->Signature2[4] == '_')) {
#if DBG
BlPrint("GetSMBIOSInfo: found _DMI_ anchor string installation %lx\n",
dmiHeader);
#endif
checkLength = sizeof(DMIBIOS_EPS_HEADER);
} else if (header->Signature[0] == '_' &&
header->Signature[1] == 'S' &&
header->Signature[2] == 'M' &&
header->Signature[3] == '_' &&
header->Length >= sizeof(SMBIOS_EPS_HEADER) &&
header->Signature2[0] == '_' &&
header->Signature2[1] == 'D' &&
header->Signature2[2] == 'M' &&
header->Signature2[3] == 'I' &&
header->Signature2[4] == '_' ) {
#if DBG
BlPrint("GetSMBIOSInfo: found _SM_ anchor string installation %lx\n",
header);
#endif
checkLength = header->Length;
dmiHeader = (FPDMIBIOS_EPS_HEADER)&header->Signature2[0];
}
if (checkLength != 0)
{
sum = 0;
for (j = 0; j < checkLength; j++) {
sum += current[j];
}
if (sum == 0) {
break;
}
#if DBG
BlPrint("GetSMBIOSInfo: Checksum fails\n");
#endif
checkLength = 0;
}
romAddr += PNP_BIOS_HEADER_INCREMENT;
MAKE_FP(current, romAddr);
}
if (romAddr >= romEnd) {
//
// We could not find the table so try the calling method
dmiBiosRevision = 0;
numberStructures = 0;
retCode = BiosEntry(GET_DMI_INFORMATION,
(FPUCHAR)&dmiBiosRevision,
(FPUSHORT)&numberStructures,
(FPUSHORT)&maxStructSize,
(FPULONG)&dmiStorageBase,
(FPUSHORT)&dmiStorageSize,
RealModeDataBaseAddress);
if ((retCode != DMI_SUCCESS) ||
(dmiBiosRevision < 0x20))
{
#if DBG
BlPrint("GetSMBIOSInfo: GET_DMI_INFORMATION failed %x\n", retCode);
#endif
return(0);
#if DBG
} else {
BlPrint("GetSMBIOSInfo: GET_DMI_INFORMATION\n");
BlPrint(" BiosRevision %x Number Structures %x Structure Size %x\n", dmiBiosRevision, numberStructures, maxStructSize);
BlPrint(" StorageBase %lx StorageSize %x\n", dmiStorageBase, dmiStorageSize);
#endif
}
maxStructSize += 3;
tempStructBuffer = HwAllocateHeap(maxStructSize, FALSE);
if (tempStructBuffer == NULL)
{
#if DBG
BlPrint("GetSMBIOSInfo: HwAllocateHeap(structureSize = 0x%x\n",
maxStructSize);
#endif
return(0);
}
//
// Loop calling Get_DMI_STRUCTURE to get next structure until we
// hit the end of structure or receive an error.
structCount = 0;
structNumber = 0;
dmiStorageSegment = (USHORT)(dmiStorageBase >> 4);
while ((structCount < numberStructures) &&
(retCode == DMI_SUCCESS) &&
(structNumber != 0xffff))
{
_fmemset(tempStructBuffer, 0, maxStructSize);
retCode = BiosEntry(GET_DMI_STRUCTURE,
(FPUSHORT)&structNumber,
(FPUCHAR)tempStructBuffer,
dmiStorageSegment,
RealModeDataBaseAddress
);
#if DBG
BlPrint("GetSMBIOSInfo: GET_DMI_STRUCTURE --> %x\n", retCode);
#endif
if (retCode == DMI_SUCCESS)
{
structCount++;
structHeader = (FPSMBIOS_STRUCT_HEADER)tempStructBuffer;
length = HwSMBIOSStructureLength(structHeader, maxStructSize);
lengthNeeded = length + 2;
if (StructBuffer != NULL)
{
//
// if caller wants the data then lets copy into it buffer
if (StructBufferSize >= lengthNeeded)
{
_fmemcpy(StructBuffer,
tempStructBuffer,
length);
*((FPUSHORT)&StructBuffer[length]) = 0;
StructBufferSize -= lengthNeeded;
StructBuffer += lengthNeeded;
totalStructSize += lengthNeeded;
#if DBG
} else {
BlPrint("GetSMBIOSInfo: Struct too large 0x%x bytes left\n",
StructBufferSize);
#endif
}
} else {
//
// Caller is only interested in length required
totalStructSize += lengthNeeded;
}
#if DBG
BlPrint("GetSMBIOSInfo: Number 0x%x Type 0x%x Length 0x%x/0x%x Handle 0x%x\n",
structNumber,
structHeader->Type,
structHeader->Length,
length,
structHeader->Handle);
for (j = 0; j < structHeader->Length; j = j + 16)
{
BlPrint(" %x %x %x %x %x %x %x %x\n %x %x %x %x %x %x %x %x\n",
structHeader->Data[j],
structHeader->Data[j+1],
structHeader->Data[j+2],
structHeader->Data[j+3],
structHeader->Data[j+4],
structHeader->Data[j+5],
structHeader->Data[j+6],
structHeader->Data[j+7],
structHeader->Data[j+8],
structHeader->Data[j+9],
structHeader->Data[j+10],
structHeader->Data[j+11],
structHeader->Data[j+12],
structHeader->Data[j+13],
structHeader->Data[j+14],
structHeader->Data[j]+15);
}
for (j = structHeader->Length; j < length; j++)
{
BlPrint("%c", structHeader->Data[j-sizeof(SMBIOS_STRUCT_HEADER)]);
if (structHeader->Data[j-sizeof(SMBIOS_STRUCT_HEADER)] == 0)
{
BlPrint("\n");
}
}
BlPrint("\n");
#endif
}
#if DBG
while ( !HwGetKey() ) ; // wait until key pressed to continue
#endif
}
HwFreeHeap(maxStructSize);
#if DBG
BlPrint("GetSMBIOSInfo: %x/%x structures read, total size 0x%x\n",
structCount, numberStructures, totalStructSize);
#endif
#if DBG
} else {
if ((FPVOID)dmiHeader != (FPVOID)header)
{
BlPrint("GetSMBIOSInfo: _SM_ Structure Table\n");
BlPrint(" Length %x MajorVersion %x MinorVersion %x\n",
header->Length, header->MajorVersion, header->MinorVersion);
BlPrint(" MaximumStructureSize %x EntryPointRevision %x StructureTableLength %x\n",
header->MaximumStructureSize, header->EntryPointRevision, header->StructureTableLength);
BlPrint(" StructureTableAddress %x NumberStructures %x Revision %x\n",
header->StructureTableAddress, header->NumberStructures, header->Revision);
} else {
BlPrint("GetSMBIOSInfo: _DMI_ Structure Table\n");
BlPrint(" StructureTableLength %x\n",
dmiHeader->StructureTableLength);
BlPrint(" StructureTableAddress %x NumberStructures %x Revision %x\n",
dmiHeader->StructureTableAddress, dmiHeader->NumberStructures, dmiHeader->Revision);
}
#endif
}
#if DBG
while ( !HwGetKey() ) ; // wait until key pressed to continue
#endif
return(totalStructSize);
}
#if 0
VOID
HwDisablePnPBiosDevnode(
ENTRY_POINT biosEntry,
FPPNP_BIOS_INSTALLATION_CHECK header,
UCHAR node,
FPPNP_BIOS_DEVICE_NODE deviceNode
)
{
USHORT control = GET_CURRENT_CONFIGURATION;
USHORT retCode;
FPUCHAR buffer;
USHORT i;
UCHAR code;
#if 0
BlPrint("DisablePnPBiosDevnode: found it\n");
while ( !HwGetKey() ) ; // wait until key pressed to continue
buffer = (FPUCHAR)deviceNode;
for (i = 0; i < deviceNode->Size; i++) {
BlPrint("%x ", *buffer++);
if ( ((i+1)%16) == 0) {
BlPrint("\n");
}
}
BlPrint("\n");
#endif
//
// Zero out allocated resources
//
buffer = (FPUCHAR)(deviceNode+1);
if (deviceNode->Size <= sizeof(PNP_BIOS_DEVICE_NODE)) {
return;
}
for (i = 0; i < (deviceNode->Size - sizeof(PNP_BIOS_DEVICE_NODE)); i++) {
code = *buffer;
#define PNP_BIOS_END_TAG 0x79
if (code == PNP_BIOS_END_TAG) {
//
// found END TAG
// write checksum
//
*(++buffer) = (UCHAR) (0 - PNP_BIOS_END_TAG);
break;
}
*buffer++ = 0;
}
#if 0
buffer = (FPUCHAR)deviceNode;
for (i = 0; i < deviceNode->Size; i++) {
BlPrint("%x ", *buffer++);
if ( ((i+1)%16) == 0) {
BlPrint("\n");
}
}
BlPrint("\n");
while ( !HwGetKey() ) ; // wait until key pressed to continue
#endif
retCode = biosEntry(PNP_BIOS_SET_DEVICE_NODE,
node,
deviceNode,
control,
header->RealModeDataBaseAddress
);
#if DBG
if (retCode != 0) {
BlPrint("HwDisablePnPBiosDevnode: PnP Bios func 2 returns failure = %x.\n", retCode);
}
#endif
}
#endif
//
// Global Variable within NTDETECT
// - structure definition in dockinfo.h
// - extern declaration in hwdetect.h
// - used in hwpbios.c and hwdetect.c
//
BOOLEAN
HwGetPnpBiosSystemData(
IN FPUCHAR *Configuration,
OUT PUSHORT PnPBiosLength,
OUT PUSHORT SMBIOSLength,
IN OUT FPDOCKING_STATION_INFO DockInfo
)
/*++
Routine Description:
This routine checks if PNP BIOS is present in the machine. If yes, it
also create a registry descriptor to collect the BIOS data.
Arguments:
Configuration - Supplies a variable to receive the PNP BIOS data.
PnPBiosLength - Supplies a variable to receive the size of the PnP Bios
data (Does not include HEADER)
SMBIOSBiosLength - Supplies a variable to receive the size of the SMBIOS
data (Does not include HEADER). Total size of buffer
returned is in *Configuration is (*PnPBiosLength +
*SMBIOSLength + 2 * DATA_HEADER_SIZE)
DockInfo -
Return Value:
A value of TRUE is returned if success. Otherwise, a value of
FALSE is returned.
--*/
{
ULONG romAddr, romEnd;
FPUCHAR current;
FPPNP_BIOS_INSTALLATION_CHECK header;
UCHAR sum, node = 0;
UCHAR currentNode;
USHORT i, totalSize = 0, nodeSize, numberNodes, retCode;
ENTRY_POINT biosEntry;
FPPNP_BIOS_DEVICE_NODE deviceNode;
USHORT control = GET_CURRENT_CONFIGURATION;
USHORT sMBIOSBufferSize;
FPUCHAR sMBIOSBuffer;
//
// Perform PNP BIOS installation Check
//
MAKE_FP(current, PNP_BIOS_START);
romAddr = PNP_BIOS_START;
romEnd = PNP_BIOS_END;
while (romAddr < romEnd) {
header = (FPPNP_BIOS_INSTALLATION_CHECK)current;
if (header->Signature[0] == '$' && header->Signature[1] == 'P' &&
header->Signature[2] == 'n' && header->Signature[3] == 'P' &&
header->Length >= sizeof(PNP_BIOS_INSTALLATION_CHECK)) {
#if DBG
BlPrint("GetPnpBiosData: find Pnp installation\n");
#endif
sum = 0;
for (i = 0; i < header->Length; i++) {
sum += current[i];
}
if (sum == 0) {
break;
}
#if DBG
BlPrint("GetPnpBiosData: Checksum fails\n");
#endif
}
romAddr += PNP_BIOS_HEADER_INCREMENT;
MAKE_FP(current, romAddr);
}
if (romAddr >= romEnd) {
return FALSE;
}
#if DBG
BlPrint("PnP installation check at %lx\n", romAddr);
#endif
//
// Determine how much space we will need and allocate heap space
//
totalSize += sizeof(PNP_BIOS_INSTALLATION_CHECK);
biosEntry = *(ENTRY_POINT far *)&header->RealModeEntryOffset;
//
// Determine size needed for SMBIOS data
sMBIOSBufferSize = HwGetSMBIOSInfo(biosEntry,
header->RealModeDataBaseAddress,
0,
NULL);
if (sMBIOSBufferSize > MAXSMBIOS20SIZE)
{
#if DBG
BlPrint("GetPnpBiosData: SMBIOS data structures are too large 0x%x bytes\n",
sMBIOSBufferSize);
while ( !HwGetKey() ) ; // wait until key pressed to continue
#endif
sMBIOSBufferSize = 0;
}
retCode = biosEntry(PNP_BIOS_GET_NUMBER_DEVICE_NODES,
(FPUSHORT)&numberNodes,
(FPUSHORT)&nodeSize,
header->RealModeDataBaseAddress
);
if (retCode != 0) {
#if DBG
BlPrint("GetPnpBiosData: PnP Bios GetNumberNodes func returns failure %x.\n", retCode);
#endif
return FALSE;
}
#if DBG
BlPrint("GetPnpBiosData: Pnp Bios GetNumberNodes returns %x nodes\n", numberNodes);
#endif
deviceNode = (FPPNP_BIOS_DEVICE_NODE) HwAllocateHeap(nodeSize, FALSE);
if (!deviceNode) {
#if DBG
BlPrint("GetPnpBiosData: Out of heap space.\n");
#endif
return FALSE;
}
while (node != 0xFF) {
retCode = biosEntry(PNP_BIOS_GET_DEVICE_NODE,
(FPUCHAR)&node,
deviceNode,
control,
header->RealModeDataBaseAddress
);
if (retCode != 0) {
#if DBG
BlPrint("GetPnpBiosData: PnP Bios GetDeviceNode func returns failure = %x.\n", retCode);
#endif
HwFreeHeap((ULONG)nodeSize);
return FALSE;
}
#if DBG
BlPrint("GetPnpBiosData: PnpBios GetDeviceNode returns nodesize %x for node %x\n", deviceNode->Size, node);
#endif
totalSize += deviceNode->Size;
}
#if DBG
BlPrint("GetPnpBiosData: PnpBios total size of nodes %x\n", totalSize);
#endif
HwFreeHeap((ULONG)nodeSize); // Free temporary buffer
*PnPBiosLength = totalSize;
*SMBIOSLength = sMBIOSBufferSize;
//
// Allocate enough room for 2 HWPARTIAL_RESOURCE_DESCRIPTORS (one for
// PnP bios and one for SMBios) plus room to keep the data.
totalSize += sMBIOSBufferSize + DATA_HEADER_SIZE + sizeof(HWPARTIAL_RESOURCE_DESCRIPTOR);
current = (FPUCHAR) HwAllocateHeap(totalSize, FALSE);
if (!current) {
#if DBG
BlPrint("GetPnpBiosData: Out of heap space.\n");
#endif
return FALSE;
}
//
// Collect PnP Bios installation check data and device node data.
//
_fmemcpy (current + DATA_HEADER_SIZE,
(FPUCHAR)header,
sizeof(PNP_BIOS_INSTALLATION_CHECK)
);
deviceNode = (FPPNP_BIOS_DEVICE_NODE)(current + DATA_HEADER_SIZE +
sizeof(PNP_BIOS_INSTALLATION_CHECK));
node = 0;
while (node != 0xFF) {
currentNode = node;
retCode = biosEntry(PNP_BIOS_GET_DEVICE_NODE,
(FPUCHAR)&node,
deviceNode,
control,
header->RealModeDataBaseAddress
);
if (retCode != 0) {
#if DBG
BlPrint("GetPnpBiosData: PnP Bios func 1 returns failure = %x.\n", retCode);
#endif
HwFreeHeap((ULONG)totalSize);
return FALSE;
}
//
// Record the existance of certain devices for the benefit of other
// routines in ntdetect. For example, the pccard irq detection code
// uses the PIC and an 8237... this insures that we actually have
// those devices.
//
if (deviceNode->ProductId == 0xd041) { // PNP0000
SystemHas8259 = TRUE;
} else if (deviceNode->ProductId == 0x1d041) { // PNP0100
SystemHas8253 = TRUE;
}
deviceNode = (FPPNP_BIOS_DEVICE_NODE)((FPUCHAR)deviceNode + deviceNode->Size);
}
//
// Collect SMBIOS Data, skipping over PartialDescriptor which is filled in
// by the caller of this routine
if (sMBIOSBufferSize != 0)
{
sMBIOSBuffer = (FPUCHAR)deviceNode;
sMBIOSBuffer += sizeof(HWPARTIAL_RESOURCE_DESCRIPTOR);
retCode = HwGetSMBIOSInfo(biosEntry,
header->RealModeDataBaseAddress,
sMBIOSBufferSize,
sMBIOSBuffer);
#if DBG
BlPrint("SMBIOS asked for 0x%x bytes and filled 0x%x bytes into %lx\n",
sMBIOSBufferSize, retCode, sMBIOSBuffer);
#endif
}
*Configuration = current;
//
// call PnP BIOS to get docking station information
//
DockInfo->ReturnCode = biosEntry(PNP_BIOS_GET_DOCK_INFORMATION,
(FPUCHAR) DockInfo,
header->RealModeDataBaseAddress
);
#if DBG
BlPrint("\npress any key to continue...\n");
while ( !HwGetKey() ) ; // wait until key pressed to continue
clrscrn();
BlPrint("*** DockInfo - BEGIN ***\n\n");
BlPrint("ReturnCode= 0x%x (Other fields undefined if ReturnCode != 0)\n",
DockInfo->ReturnCode
);
BlPrint(" 0x0000 = SUCCESS (docked)\n");
BlPrint(" 0x0082 = FUNCTION_NOT_SUPPORTED\n");
BlPrint(" 0x0087 = SYSTEM_NOT_DOCKED\n");
BlPrint(" 0x0089 = UNABLE_TO_DETERMINE_DOCK_CAPABILITIES\n\n");
BlPrint("DockID = 0x%lx\n", DockInfo->DockID);
BlPrint(" 0xFFFFFFFF if product has no identifier (DockID)\n\n");
BlPrint("SerialNumber = 0x%lx\n", DockInfo->SerialNumber);
BlPrint(" 0 if docking station has no SerialNumber\n\n");
BlPrint("Capabilities = 0x%x\n" , DockInfo->Capabilities);
BlPrint(" Bits 15:3 - reserved (0)\n");
BlPrint(" Bits 2:1 - docking: 00=cold, 01=warm, 10=hot, 11=reserved\n");
BlPrint(" Bit 0 - docking/undocking: 0=surprise style, 1=vcr style\n\n");
BlPrint("*** DockInfo - END ***\n\n");
BlPrint("press any key to continue...\n");
while ( !HwGetKey() ) ; // wait until key pressed to continue
clrscrn();
#endif // DBG
return TRUE;
}