/*++ 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 #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; }