#if defined(JAZZ) /*++ Copyright (c) 1991 Microsoft Corporation Module Name: jxconfig.c Abstract: This module implements the ARC firmware Configuration Query functions as described in the Advanced Risc Computing Specification (Revision 1.00), section 3.3.3.4, for a MIPS R3000 or R4000 Jazz system. Author: David M. Robinson (davidro) 13-June-1991 Revision History: --*/ #include "fwp.h" #include "string.h" #include "selftest.h" extern ULONG end; // // Define the ARC pathname mnemonics. // PCHAR MnemonicTable[] = { "arc", "cpu", "fpu", "pic", "pdc", "sic", "sdc", "sc", "eisa", "tc", "scsi", "dti", "multi", "disk", "tape", "cdrom", "worm", "serial", "net", "video", "par", "point", "key", "audio", "other", "rdisk", "fdisk", "tape", "modem", "monitor", "print", "pointer", "keyboard", "term", "other" }; // // Function prototypes. // ARC_STATUS FwRestoreConfiguration ( VOID ); VOID FwConfigurationSetChecksum( VOID ); ULONG FwZeroCompressLength ( IN ULONG DataLength, IN PVOID ConfigurationData ); ULONG FwZeroCompress ( IN ULONG DataLength, IN PVOID ConfigurationData, OUT PVOID OutputBuffer ); VOID FwZeroDecompress ( IN PVOID InBuffer, IN ULONG Index, OUT PVOID ConfigurationData, IN ULONG Length ); // // IdentifierIndex and DataIndex identify the next free locations in the // configuration identifier and data areas. Configuration points to the // allocated configuration area. // ULONG IdentifierIndex; ULONG DataIndex; ULONG EisaDataIndex; PCONFIGURATION Configuration; // // Boolean to keep checksum status of the NVRAM. // BOOLEAN NvramValid = FALSE; // // External data. // extern MONITOR_CONFIGURATION_DATA DefaultMonitor; VOID FwConfigurationInitialize ( VOID ) /*++ Routine Description: This routine initializes the configuration area in memory, and the configuration routine addresses. Note: This routine is called at phase 1 initialization and at this time nothing is available. Arguments: None. Return Value: None. --*/ { PCONFIGURATION_COMPONENT Processor, Child; ULONG ProcessorNumber; CHAR CpuPath[10]; // // Initialize the configuration routine addresses in the system // parameter block. // (PARC_GET_CHILD_ROUTINE)SYSTEM_BLOCK->FirmwareVector[GetChildRoutine] = FwGetChild; (PARC_GET_PARENT_ROUTINE)SYSTEM_BLOCK->FirmwareVector[GetParentRoutine] = FwGetParent; (PARC_GET_PEER_ROUTINE)SYSTEM_BLOCK->FirmwareVector[GetPeerRoutine] = FwGetPeer; (PARC_ADD_CHILD_ROUTINE)SYSTEM_BLOCK->FirmwareVector[AddChildRoutine] = FwAddChild; (PARC_DELETE_COMPONENT_ROUTINE)SYSTEM_BLOCK->FirmwareVector[DeleteComponentRoutine] = FwDeleteComponent; (PARC_GET_COMPONENT_ROUTINE)SYSTEM_BLOCK->FirmwareVector[GetComponentRoutine] = FwGetComponent; (PARC_GET_DATA_ROUTINE)SYSTEM_BLOCK->FirmwareVector[GetDataRoutine] = FwGetConfigurationData; (PARC_SAVE_CONFIGURATION_ROUTINE)SYSTEM_BLOCK->FirmwareVector[SaveConfigurationRoutine] = FwSaveConfiguration; // // Allocate a region to store the volatile configuration database. // Configuration = (PCONFIGURATION)FwAllocatePool(sizeof(CONFIGURATION)); // // Initialize other static data. // IdentifierIndex = 0; DataIndex = 0; EisaDataIndex = 0; if (FwRestoreConfiguration() == ESUCCESS) { // // Delete processor components. // for (ProcessorNumber = 0; ProcessorNumber < 2 ; ProcessorNumber++ ) { sprintf(CpuPath,"cpu(%1d)", ProcessorNumber); Processor = FwGetComponent(CpuPath); if ((Processor != NULL) && (Processor->Type == CentralProcessor)) { while ((Child = FwGetChild(Processor)) != NULL) { FwDeleteComponent(Child); } FwDeleteComponent(Processor); } } // // Add the first processor. // JzAddProcessor(0); #ifdef DUO // // Add the second processor // ExecuteOnProcessorB((PPROCESSOR_TASK_ROUTINE)JzAddProcessor,1); #endif FwSaveConfiguration(); } return; } PCONFIGURATION_COMPONENT FwAddChild ( IN PCONFIGURATION_COMPONENT Component, IN PCONFIGURATION_COMPONENT NewComponent, IN PVOID ConfigurationData OPTIONAL ) /*++ Routine Description: This routine adds a new component entry as a child of Component, including an identifier string if the IdentifierLength field of NewComponent is non-zero, and configuration data if the ConfigurationDataLength field of NewComponent is non-zero and the ConfigurationData parameter is present. If Component is NULL, the root component is being added. Arguments: Component - Supplies a pointer to a configuration component. NewComponent - Supplies a pointer to a new configuration component to be added as a child of Component. ConfigurationData - Supplies an optional pointer to a configuration data buffer. Return Value: Returns a pointer to the new configuration component entry. If the create operation was unsuccessful, NULL is returned. --*/ { PCONFIGURATION_PACKET Packet; PCONFIGURATION_PACKET ParentPacket; ULONG Index; PUCHAR String; PUCHAR Data; BOOLEAN Eisa; ULONG DataLength; // // If Component is NULL and the new Class is system, the root component is // being added, otherwise find the first free component entry. // if ((Component == NULL) && (NewComponent->Class == SystemClass)) { Packet = &Configuration->Packet[0]; // // TEMPTEMP If the root component is being added, clear all of the // configuration area. This is a Hack, should be replaced by // a good way to do this. // RtlZeroMemory(Configuration, sizeof(CONFIGURATION)); IdentifierIndex = 0; DataIndex = 0; EisaDataIndex = 0; } else { // // If the configuration is not valid, return NULL. // if (!NvramValid) { return(NULL); } Packet = &Configuration->Packet[1]; for ( Index = 1 ; Packet->Parent != NULL ; Index++ ) { // // If no more entries, return NULL. Since Index is 0 based // subtract one from NUMBER_OF_ENTRIES for end check. // if (Index >= (NUMBER_OF_ENTRIES - 1)) { return NULL; } Packet++; } } // // Check to see if the parent component is the eisa bus. // if ((Component != NULL) && (Component->Type == EisaAdapter)) { Eisa = TRUE; } else { Eisa = FALSE; } // // If there is not enough space for the new identifier string or the // configuration data, return NULL. // if (IdentifierIndex + NewComponent->IdentifierLength >= LENGTH_OF_IDENTIFIER) { return(NULL); } if (Eisa) { DataLength = FwZeroCompressLength(NewComponent->ConfigurationDataLength, ConfigurationData); if (EisaDataIndex + DataLength >= LENGTH_OF_EISA_DATA) { return(NULL); } } else { if (DataIndex + NewComponent->ConfigurationDataLength >= LENGTH_OF_DATA) { return(NULL); } } // // There is space for everything. Fill in new configuration entry first. // Packet->Component.Class = NewComponent->Class; Packet->Component.Type = NewComponent->Type; Packet->Component.Flags = NewComponent->Flags; Packet->Component.Version = NewComponent->Version; Packet->Component.Revision = NewComponent->Revision; Packet->Component.Key = NewComponent->Key; Packet->Component.AffinityMask = 0xffffffff; Packet->Component.IdentifierLength = NewComponent->IdentifierLength; Packet->Component.Identifier = &Configuration->Identifier[IdentifierIndex]; // // If Component is NULL, this is the root component so the parent is NULL, // otherwise find the parent packet. // if (Component == NULL) { ParentPacket = NULL; } else { ParentPacket = CONTAINING_RECORD(Component, CONFIGURATION_PACKET, Component); } // // Only copy configuration data length if configuration data is supplied. // if (ConfigurationData != NULL) { Packet->Component.ConfigurationDataLength = NewComponent->ConfigurationDataLength; } else { Packet->Component.ConfigurationDataLength = 0; } Packet->Parent = ParentPacket; Packet->Child = NULL; // // Add identifer string. // String = NewComponent->Identifier; for ( Index = 0 ; Index < NewComponent->IdentifierLength ; Index++ ) { Configuration->Identifier[IdentifierIndex++] = *String++; } // // Make sure identifier is zero terminated, if not add one. // if (Configuration->Identifier[IdentifierIndex - 1] != 0) { Configuration->Identifier[IdentifierIndex++] = 0; Packet->Component.IdentifierLength += 1; } // // Copy configuration data. // if (Eisa) { Packet->ConfigurationData = &Configuration->EisaData[EisaDataIndex]; EisaDataIndex += FwZeroCompress(NewComponent->ConfigurationDataLength, ConfigurationData, &Configuration->EisaData[EisaDataIndex]); } else { Data = (PUCHAR)ConfigurationData; Packet->ConfigurationData = &Configuration->Data[DataIndex]; for ( Index = 0 ; Index < NewComponent->ConfigurationDataLength ; Index++ ) { Configuration->Data[DataIndex++] = *Data++; } } // // Add the new component as the first child of Component, unless this is // the root component. // if (Component == NULL) { Packet->Peer = NULL; } else { Packet->Peer = ParentPacket->Child; ParentPacket->Child = Packet; } return (&Packet->Component); } ARC_STATUS FwDeleteComponent ( IN PCONFIGURATION_COMPONENT Component ) /*++ Routine Description: This function deletes a component entry. If the entry has one or more children, an error is returned, otherwise the entry is deleted. Deleting the entry will implicitly delete the identifier string and the configuration data. Note that no attempt is made to compress the entry, identifier, or the configuration data areas after an entry is deleted, as doing so would potentially invalidate outstanding pointers. Arguments: Component - Supplies a pointer to a configuration component. Return Value: Returns ESUCCESS if the entry was successfully deleted, otherwise one of the following error codes is returned. EINVAL Component is not a valid configuration component, or the configuration is not valid. EACCES Component has children, and cannot be freed until they are deleted. --*/ { PCONFIGURATION_PACKET Packet; PCONFIGURATION_PACKET SearchPacket; if (!NvramValid || (Component == NULL)) { return EINVAL; } Packet = CONTAINING_RECORD(Component, CONFIGURATION_PACKET, Component); // // If Component's Parent field is NULL, return EINVAL. // if (Packet->Parent == NULL) { return EINVAL; } // // If Component has children, return EACCES. // if (Packet->Child != NULL) { return EACCES; } // // Find the entry that points to Component, and point it to // Component's peer. If this is Component's parent, update the child // pointer, otherwise this is a peer and update the peer pointer. // SearchPacket = Packet->Parent; if (SearchPacket->Child == Packet) { SearchPacket->Child = Packet->Peer; } else { SearchPacket = SearchPacket->Child; while (SearchPacket->Peer != Packet) { SearchPacket = SearchPacket->Peer; } SearchPacket->Peer = Packet->Peer; } // // Delete Component by zeroing the parent pointer. // Packet->Parent = NULL; return ESUCCESS; } PCONFIGURATION_COMPONENT FwGetComponent ( IN PCHAR Pathname ) /*++ Routine Description: This routine searches the configuration tree for the component that best matches the Pathname string. Arguments: Pathname - Supplies a string containing the pathname to search. Return Value: Returns a pointer to the configuration component that best matches pathname. The algorithm is to search for each component starting with the first. When the string has been exhausted or no component matches the current string section, then a pointer to the last successfully matched component is returned. If the configuration information is not valid, NULL is returned. --*/ { PCONFIGURATION_COMPONENT Component; PCONFIGURATION_COMPONENT MatchComponent; PCHAR PathString; PCHAR MatchString; PCHAR Token; ULONG Key; // // If the configuration is not valid, return NULL. // if (!NvramValid) { return(NULL); } PathString = Pathname; // // Get the the root component. // MatchComponent = FwGetChild(NULL); // // Repeat search for each new match component. // do { // // Get the first child of the current match component. // Component = FwGetChild( MatchComponent ); // // Search each child of the current match component for the next match. // while ( Component != NULL ) { // // Reset Token to be the current position on the pathname. // Token = PathString; MatchString = MnemonicTable[Component->Type]; // // Compare strings. // while (*MatchString == tolower(*Token)) { MatchString++; Token++; } // // Strings compare if the first mismatch is the terminator for // each. // if ((*MatchString == 0) && (*Token == '(')) { // // Form key. // Key = 0; Token++; while ((*Token != ')') && (*Token != 0)) { Key = (Key * 10) + *Token++ - '0'; } // // If the key matches the component matches, so update // pointers and break. // if (Component->Key == Key) { PathString = Token + 1; MatchComponent = Component; break; } } NextPeer: Component = FwGetPeer( Component ); } } while ((Component != NULL) && (*PathString != 0)); return MatchComponent; } PCONFIGURATION_COMPONENT FwGetChild ( IN PCONFIGURATION_COMPONENT Component OPTIONAL ) /*++ Routine Description: Returns a pointer to the configuration component for the first child of Component. If Component is NULL, a pointer to the root configuration component is returned. Arguments: Component - Supplies an optional pointer to a configuration component. Return Value: Returns a pointer to the configuration component for the first child of Component. If Component has no children, this pointer will be NULL. If Component is NULL, a pointer to the root configuration component is returned. If the configuration is not valid, NULL is returned. --*/ { PCONFIGURATION_PACKET Packet; // // If the configuration is not valid, return NULL. // if (!NvramValid) { return(NULL); } if (Component == NULL) { return &Configuration->Packet[0].Component; } else { Packet = CONTAINING_RECORD(Component, CONFIGURATION_PACKET, Component); return &((PCONFIGURATION_PACKET)(Packet->Child))->Component; } } PCONFIGURATION_COMPONENT FwGetParent ( IN PCONFIGURATION_COMPONENT Component ) /*++ Routine Description: This function returns the parent of the named component. Arguments: Component - Supplies a pointer to a configuration component. Return Value: Returns a pointer to the configuration component for the parent of Component. If Component has no parent NULL is returned (this is only true for the root configuration component). If the configuration is not valid, NULL is returned. --*/ { PCONFIGURATION_PACKET Packet; // // If the configuration is not valid, return NULL. // if (!NvramValid) { return(NULL); } Packet = CONTAINING_RECORD(Component, CONFIGURATION_PACKET, Component); if (Packet->Parent == NULL) { return NULL; } else { return &((PCONFIGURATION_PACKET)(Packet->Parent))->Component; } } PCONFIGURATION_COMPONENT FwGetPeer ( IN PCONFIGURATION_COMPONENT Component ) /*++ Routine Description: This function returns the peer of the named component. Arguments: Component - Supplies a pointer to a configuration component. Return Value: Returns a pointer to the configuration component for the next peer of Component. If Component has no next peer, NULL is returned. If the configuration is not valid, NULL is returned. --*/ { PCONFIGURATION_PACKET Packet; // // If the configuration is not valid, return NULL. // if (!NvramValid) { return(NULL); } Packet = CONTAINING_RECORD(Component, CONFIGURATION_PACKET, Component); if (Packet->Peer == NULL) { return NULL; } else { return &((PCONFIGURATION_PACKET)(Packet->Peer))->Component; } } ARC_STATUS FwGetConfigurationDataIndex ( OUT PVOID ConfigurationData, IN PCONFIGURATION_COMPONENT Component, IN ULONG Index, IN ULONG Length ) /*++ Routine Description: This functions returns the specified configuration data associated with Component in the buffer supplied by ConfigurationData. The max length of the data is stored in the Component structure. Arguments: ConfigurationData - Supplies a pointer to a buffer to receive the configuration data. Component - Supplies a pointer to a configuration component. Index - Supplies an index within the configuration data. Length - Supplies the number of bytes to read (see the ConfigurationDataLength field within the Component for the max value). Return Value: If the configuration data is successfully copied into the buffer provided by ConfigurationData, ESUCCESS is returned. Otherwise one of the following error codes is returned. EINVAL Component is not a valid configuration component or the other arguments are invalid or the configuration is not valid. --*/ { PCONFIGURATION_PACKET Packet; ULONG DataSize; PUCHAR SourceData; PUCHAR DestinationData; // // If the configuration is not valid, return EINVAL. // if (!NvramValid) { return(EINVAL); } DataSize = Component->ConfigurationDataLength; // // check the passing parameters // if ( DataSize == 0 || Index >= DataSize || DataSize - Index < Length ) { return EINVAL; } Packet = CONTAINING_RECORD( Component, CONFIGURATION_PACKET, Component ); // // If Component's Parent field is NULL, return EINVAL. // if (Packet->Parent == NULL) { return EINVAL; } // // If this is an eisa component, decompress the data, otherwise just copy it. // if (Packet->Parent->Component.Type == EisaAdapter) { FwZeroDecompress(Packet->ConfigurationData, Index, ConfigurationData, Length); } else { SourceData = (PUCHAR)Packet->ConfigurationData + Index; DestinationData = ConfigurationData; while ( Length-- ) { *DestinationData++ = *SourceData++; } } return ESUCCESS; } ARC_STATUS FwGetConfigurationData ( OUT PVOID ConfigurationData, IN PCONFIGURATION_COMPONENT Component ) /*++ Routine Description: This functions returns the configuration data associated with Component in the buffer supplied by ConfigurationData. The length of the data is stored in the Component structure. Arguments: ConfigurationData - Supplies a pointer to a buffer to receive the configuration data. Component - Supplies a pointer to a configuration component. Return Value: If the configuration data is successfully copied into the buffer provided by ConfigurationData, ESUCCESS is returned. Otherwise one of the following error codes is returned. EINVAL Component is not a valid configuration component, or the configuration is invalid. --*/ { return(FwGetConfigurationDataIndex(ConfigurationData, Component, 0, Component->ConfigurationDataLength)); } ARC_STATUS FwSaveConfiguration ( VOID ) /*++ Routine Description: This routine stores all of the configuration entries into NVRAM, including the associated identifier strings and configuration data. Arguments: None. Return Value: Returns ESUCCESS if the save completed successfully, otherwise one of the following error codes is returned. ENOSPC Not enough space in the NVRAM to save all of the data. --*/ { ULONG EntryIndex; ULONG Index; PCONFIGURATION_PACKET Packet; PCONFIGURATION_COMPONENT Component; PNV_CONFIGURATION NvConfiguration; COMPRESSED_CONFIGURATION_PACKET CompressedPacket; USHORT NvIdentifierIndex; USHORT NvDataIndex; USHORT NvEisaDataIndex; PUCHAR CompressedChars, NvChars, Data; NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION; NvIdentifierIndex = 0; NvDataIndex = 0; NvEisaDataIndex = 0; // // Write each volatile packet into a compressed non-volatile packet, // including the identifier string and the configuration data. // for ( EntryIndex = 0 ; EntryIndex < NUMBER_OF_ENTRIES ; EntryIndex++ ) { // // Get pointers to the volatile data. // Packet = (PCONFIGURATION_PACKET)&Configuration->Packet[EntryIndex]; Component = &Packet->Component; // // If this is not the root entry and the parent field is NULL, zero // entry and skip to next. // if ((EntryIndex != 0) && (Packet->Parent == NULL)) { NvChars = (PUCHAR)&NvConfiguration->Packet[EntryIndex]; for ( Index = 0 ; Index < sizeof(COMPRESSED_CONFIGURATION_PACKET) ; Index++ ) { WRITE_REGISTER_UCHAR( NvChars++, 0); } continue; } // // Determine the parent and store as an index. Note that the index // (Packet->Parent) is 1 based, to reserve the value 0 to mean no // parent. // if (EntryIndex != 0) { CompressedPacket.Parent = (UCHAR)(Packet->Parent - &Configuration->Packet[0]) + 1; } else { CompressedPacket.Parent = 0; } // // Fill in the rest of the fields. Version and ConfigurationDataLength will // never be larger than USHORTS. // CompressedPacket.Class = (UCHAR)Component->Class; CompressedPacket.Type = (UCHAR)Component->Type; CompressedPacket.Version = (UCHAR)Component->Version; CompressedPacket.Revision = (UCHAR)Component->Revision; CompressedPacket.Key = Component->Key; CompressedPacket.ConfigurationDataLength = (USHORT)Component->ConfigurationDataLength; CompressedPacket.ConfigurationData = 0; // // Make sure the top bit of the flag field is zero unless it's set // to be eisa below. // CompressedPacket.Flags = *(PUCHAR)(&Component->Flags) & 0x7f; // // If the component has an identifier string, copy it to NVRAM, // otherwise set the index to indicate no identifier. // if (Component->IdentifierLength != 0) { CompressedPacket.Identifier = NvIdentifierIndex; for ( Index = 0 ; Index < Component->IdentifierLength ; Index++ ) { WRITE_REGISTER_UCHAR( &NvConfiguration->Identifier[NvIdentifierIndex++], Component->Identifier[Index]); } } else { CompressedPacket.Identifier = NO_CONFIGURATION_IDENTIFIER; } // // If the component has configuration data, copy it to NVRAM. // if (Component->ConfigurationDataLength != 0) { // // If the parent component is the eisa bus, copy until the end // of the compressed data. // if (Packet->Parent->Component.Type == EisaAdapter) { CompressedPacket.ConfigurationData = NvEisaDataIndex; Data = (PUCHAR)Packet->ConfigurationData; for ( Index = 0 ; TRUE ; Index++ ) { WRITE_REGISTER_UCHAR( &NvConfiguration->EisaData[NvEisaDataIndex++], *Data++); // // If we've written at least two bytes and the last two // bytes were zero we're at the end. // if ((Index > 1) && (!*(Data - 1) && !*(Data - 2))) { break; } } // // Set a flag to make it easier to determine that this is an // Eisa component. // CompressedPacket.Flags |= 0x80; } else { CompressedPacket.ConfigurationData = NvDataIndex; Data = (PUCHAR)Packet->ConfigurationData; for ( Index = 0 ; Index < Component->ConfigurationDataLength ; Index++ ) { WRITE_REGISTER_UCHAR( &NvConfiguration->Data[NvDataIndex++], *Data++); } } } // // Write compressed packet to NVRAM. // CompressedChars = (PUCHAR)&CompressedPacket; NvChars = (PUCHAR)&NvConfiguration->Packet[EntryIndex]; for ( Index = 0 ; Index < sizeof(COMPRESSED_CONFIGURATION_PACKET) ; Index++ ) { WRITE_REGISTER_UCHAR( NvChars++, *CompressedChars++); } } // // Zero the rest of the identifier and configuration data areas. // for ( Index = NvIdentifierIndex ; Index < LENGTH_OF_IDENTIFIER ; Index++ ) { WRITE_REGISTER_UCHAR( &NvConfiguration->Identifier[Index], 0); } for ( Index = NvDataIndex ; Index < LENGTH_OF_DATA ; Index++ ) { WRITE_REGISTER_UCHAR( &NvConfiguration->Data[Index] ,0); } for ( Index = NvEisaDataIndex ; Index < LENGTH_OF_EISA_DATA ; Index++ ) { WRITE_REGISTER_UCHAR( &NvConfiguration->EisaData[Index] ,0); } // // Write configuration data checksum. // FwConfigurationSetChecksum(); // // Restore configuration information out of NVRAM. This acts to compress // the identifier and configuration data areas if any deletes have been // performed. // return FwRestoreConfiguration(); } ARC_STATUS FwRestoreConfiguration ( VOID ) /*++ Routine Description: This routine restores all of the configuration entries from NVRAM, including the associated identifier strings and configuration data. Arguments: None. Return Value: Returns ESUCCESS if the restore completed successfully, otherwise one of the following error codes is returned. EIO Invalid NVRAM checksum. --*/ { ULONG EntryIndex; ULONG Index; PCONFIGURATION_PACKET Packet; PCONFIGURATION_COMPONENT Component; PNV_CONFIGURATION NvConfiguration; COMPRESSED_CONFIGURATION_PACKET CompressedPacket; USHORT NvIdentifierIndex; USHORT NvDataIndex; USHORT NvEisaDataIndex; PUCHAR CompressedChars, NvChars; PCONFIGURATION_PACKET SearchPacket; ULONG Long; NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION; NvIdentifierIndex = 0; NvDataIndex = 0; NvEisaDataIndex = 0; IdentifierIndex = 0; DataIndex = 0; EisaDataIndex = 0; // // Check the checksum, return error if invalid. // if (FwConfigurationCheckChecksum() != ESUCCESS) { return EIO; } // // Clear the configuration area. // RtlZeroMemory(Configuration, sizeof(CONFIGURATION)); // // Read each non-volatile compressed packet into a volatile packet, // including the identifier string and the configuration data. // for ( EntryIndex = 0 ; EntryIndex < NUMBER_OF_ENTRIES ; EntryIndex++ ) { // // Read compressed packet from NVRAM. // CompressedChars = (PUCHAR)&CompressedPacket; NvChars = (PUCHAR)&NvConfiguration->Packet[EntryIndex]; for ( Index = 0 ; Index < sizeof(COMPRESSED_CONFIGURATION_PACKET) ; Index++ ) { *CompressedChars++ = READ_REGISTER_UCHAR( NvChars++ ); } // // If this is not the root entry and the parent field is NULL, // go to the next. // if ((EntryIndex != 0) && (CompressedPacket.Parent == 0)) { continue; } // // Get pointers to the volatile area. // Packet = (PCONFIGURATION_PACKET)&Configuration->Packet[EntryIndex]; Component = &Packet->Component; // // If not the root entry and the parent field is within range, fill // in parent field (note that the parent index // is 1 based, subtract 1 to get correct index). If the parent's child // pointer is NULL, fill in with the current entry, otherwise follow // the links and add the current entry as the last peer. // if ((EntryIndex != 0) && (CompressedPacket.Parent <= NUMBER_OF_ENTRIES)) { Packet->Parent = &Configuration->Packet[CompressedPacket.Parent - 1]; SearchPacket = Packet->Parent; if (SearchPacket->Child == NULL) { SearchPacket->Child = Packet; } else { SearchPacket = SearchPacket->Child; while ( SearchPacket->Peer != NULL ) { SearchPacket = SearchPacket->Peer; } SearchPacket->Peer = Packet; } } else { Packet->Parent = NULL; } // // NULL current packet's child and peer pointers. // Packet->Child = NULL; Packet->Peer = NULL; // // Fill in the rest of the fields. // Component->Class = (CONFIGURATION_CLASS)CompressedPacket.Class; Component->Type = (CONFIGURATION_TYPE)CompressedPacket.Type; Component->Flags.Failed = (CompressedPacket.Flags & 0x01) ? 1 : 0; Component->Flags.ReadOnly = (CompressedPacket.Flags & 0x02) ? 1 : 0; Component->Flags.Removable = (CompressedPacket.Flags & 0x04) ? 1 : 0; Component->Flags.ConsoleIn = (CompressedPacket.Flags & 0x08) ? 1 : 0; Component->Flags.ConsoleOut = (CompressedPacket.Flags & 0x10) ? 1 : 0; Component->Flags.Input = (CompressedPacket.Flags & 0x20) ? 1 : 0; Component->Flags.Output = (CompressedPacket.Flags & 0x40) ? 1 : 0; Component->Version = (USHORT)CompressedPacket.Version; Component->Revision = (USHORT)CompressedPacket.Revision; Component->Key = CompressedPacket.Key; Component->AffinityMask = 0xffffffff; Component->ConfigurationDataLength = (ULONG)CompressedPacket.ConfigurationDataLength; // // If the component has an identifier string, copy it to memory. // Index = 0; if (CompressedPacket.Identifier != NO_CONFIGURATION_IDENTIFIER) { Component->Identifier = &Configuration->Identifier[IdentifierIndex]; do { Configuration->Identifier[IdentifierIndex++] = READ_REGISTER_UCHAR( &NvConfiguration->Identifier[NvIdentifierIndex] ); Index++; } while ( READ_REGISTER_UCHAR(&NvConfiguration->Identifier[NvIdentifierIndex++] ) ); } // // Set identifier length field. // Component->IdentifierLength = Index; // // If the component has configuration data, copy it to memory. // if (Component->ConfigurationDataLength != 0) { // // If the eisa flag is set, only copy the compressed data. // if (CompressedPacket.Flags & 0x80) { Packet->ConfigurationData = &Configuration->EisaData[EisaDataIndex]; for ( Index = 0 ; TRUE ; Index++ ) { Configuration->EisaData[EisaDataIndex++] = READ_REGISTER_UCHAR( &NvConfiguration->EisaData[NvEisaDataIndex++] ); // // If at least two bytes have been written and the last // two bytes are zero, we're at the end. // if ((Index > 1) && (!Configuration->EisaData[EisaDataIndex - 1] & !Configuration->EisaData[EisaDataIndex - 2])) { break; } } } else { Packet->ConfigurationData = &Configuration->Data[DataIndex]; for ( Index = 0 ; Index < Component->ConfigurationDataLength ; Index++ ) { Configuration->Data[DataIndex++] = READ_REGISTER_UCHAR( &NvConfiguration->Data[NvDataIndex++] ); } } } } return(ESUCCESS); } ARC_STATUS FwConfigurationCheckChecksum ( VOID ) /*++ Routine Description: This routine checks the configuration checksum. Arguments: None. Return Value: If the checksum is good, ESUCCESS is returned, otherwise EIO is returned. --*/ { PUCHAR NvChars; PNV_CONFIGURATION NvConfiguration; ULONG Index; ULONG Checksum1, Checksum2; NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION; // // Form checksum from NVRAM data. // Checksum1 = 0; NvChars = (PUCHAR)NvConfiguration; for ( Index = 0 ; Index < sizeof(COMPRESSED_CONFIGURATION_PACKET) * NUMBER_OF_ENTRIES + LENGTH_OF_IDENTIFIER + LENGTH_OF_DATA; Index++ ) { Checksum1 += READ_REGISTER_UCHAR( NvChars++ ); } // // Reconstitute checksum and return error if no compare. // Checksum2 = (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum1[0] ) | (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum1[1] ) << 8 | (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum1[2] ) << 16 | (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum1[3] ) << 24 ; if (Checksum1 != Checksum2) { NvramValid = FALSE; return EIO; } // // Repeat for the eisa data area. // Checksum1 = 0; NvChars = (PUCHAR)NvConfiguration->EisaData; for ( Index = 0 ; Index < LENGTH_OF_EISA_DATA; Index++ ) { Checksum1 += READ_REGISTER_UCHAR( NvChars++ ); } // // Reconstitute checksum and return error if no compare. // Checksum2 = (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum3[0] ) | (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum3[1] ) << 8 | (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum3[2] ) << 16 | (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum3[3] ) << 24 ; if (Checksum1 != Checksum2) { NvramValid = FALSE; return EIO; } NvramValid = TRUE; return(ESUCCESS); } VOID FwConfigurationSetChecksum ( VOID ) /*++ Routine Description: This routine sets the configuration checksum. Arguments: None. Return Value: None. --*/ { PUCHAR NvChars; PNV_CONFIGURATION NvConfiguration; ULONG Index; ULONG Checksum1; NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION; // // Form checksum from NVRAM data. // Checksum1 = 0; NvChars = (PUCHAR)NvConfiguration; for ( Index = 0 ; Index < sizeof(COMPRESSED_CONFIGURATION_PACKET) * NUMBER_OF_ENTRIES + LENGTH_OF_IDENTIFIER + LENGTH_OF_DATA; Index++ ) { Checksum1 += READ_REGISTER_UCHAR( NvChars++ ); } // // Set checksum. // WRITE_REGISTER_UCHAR( &NvConfiguration->Checksum1[0], Checksum1); WRITE_REGISTER_UCHAR( &NvConfiguration->Checksum1[1], Checksum1 >> 8); WRITE_REGISTER_UCHAR( &NvConfiguration->Checksum1[2], Checksum1 >> 16); WRITE_REGISTER_UCHAR( &NvConfiguration->Checksum1[3], Checksum1 >> 24); // // Repeat for the eisa data area. // Checksum1 = 0; NvChars = (PUCHAR)NvConfiguration->EisaData; for ( Index = 0 ; Index < LENGTH_OF_EISA_DATA; Index++ ) { Checksum1 += READ_REGISTER_UCHAR( NvChars++ ); } // // Set checksum. // WRITE_REGISTER_UCHAR( &NvConfiguration->Checksum3[0], Checksum1); WRITE_REGISTER_UCHAR( &NvConfiguration->Checksum3[1], Checksum1 >> 8); WRITE_REGISTER_UCHAR( &NvConfiguration->Checksum3[2], Checksum1 >> 16); WRITE_REGISTER_UCHAR( &NvConfiguration->Checksum3[3], Checksum1 >> 24); NvramValid = TRUE; return; } ARC_STATUS FwGetVideoData ( OUT PMONITOR_CONFIGURATION_DATA MonitorData ) /*++ Routine Description: This routine finds the video board and monitor configuration data in the NVRAM. Normally this information would be accessed through FwGetConfigurationData, but the initialization code needs the video information before the firmware routines have been initialized. If no monitor data is found, then default data is returned. Arguments: MonitorData - Supplies a pointer to a structure to receive the monitor configuration data. Return Value: Returns ESUCCESS if data was found and restored, otherwise returns one of the following error codes. EIO Invalid NVRAM checksum. ENODEV Monitor data not found. Note that in any event valid data is returned. --*/ { ARC_STATUS Status; ULONG EntryIndex; ULONG Index; COMPRESSED_CONFIGURATION_PACKET CompressedPacket; PUCHAR CompressedChars; PUCHAR NvChars; PUCHAR Data; PNV_CONFIGURATION NvConfiguration; NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION; // // Check configuration checksum, return error if not valid. // if (FwConfigurationCheckChecksum() != ESUCCESS){ Status = EIO; } else { // // Search the NVRAM configuration entries for the monitor, skip // the root entry. // for ( EntryIndex = 1 ; EntryIndex < NUMBER_OF_ENTRIES ; EntryIndex++ ) { // // Read compressed packet from NVRAM. // CompressedChars = (PUCHAR)&CompressedPacket; NvChars = (PUCHAR)&NvConfiguration->Packet[EntryIndex]; for ( Index = 0 ; Index < sizeof(COMPRESSED_CONFIGURATION_PACKET) ; Index++ ) { *CompressedChars++ = READ_REGISTER_UCHAR( NvChars++ ); } // // If the parent field is not Null, and the packet is class Peripheral, // type Monitor, this is the display. // if ((CompressedPacket.Parent != 0) && (CompressedPacket.Class == PeripheralClass) && (CompressedPacket.Type == MonitorPeripheral)) { break; } } // // If we've fallen out of the loop the monitor was not found, return // error. // if (EntryIndex == NUMBER_OF_ENTRIES) { Status = ENODEV; } else { // // If the configuration data length is the correct value, copy the data // into the monitor data structure, otherwise return an error. // if (CompressedPacket.ConfigurationDataLength == sizeof(MONITOR_CONFIGURATION_DATA)) { Data = (PUCHAR)MonitorData; for ( DataIndex = CompressedPacket.ConfigurationData ; DataIndex < (CompressedPacket.ConfigurationData + CompressedPacket.ConfigurationDataLength) ; DataIndex++ ) { *Data++ = READ_REGISTER_UCHAR( &NvConfiguration->Data[DataIndex] ); } Status = ESUCCESS; } else { Status = ENODEV; } } } if (Status != ESUCCESS) { MonitorData->HorizontalResolution = DefaultMonitor.HorizontalResolution; MonitorData->HorizontalDisplayTime = DefaultMonitor.HorizontalDisplayTime; MonitorData->HorizontalBackPorch = DefaultMonitor.HorizontalBackPorch; MonitorData->HorizontalFrontPorch = DefaultMonitor.HorizontalFrontPorch; MonitorData->HorizontalSync = DefaultMonitor.HorizontalSync; MonitorData->VerticalResolution = DefaultMonitor.VerticalResolution; MonitorData->VerticalBackPorch = DefaultMonitor.VerticalBackPorch; MonitorData->VerticalFrontPorch = DefaultMonitor.VerticalFrontPorch; MonitorData->VerticalSync = DefaultMonitor.VerticalSync; MonitorData->HorizontalScreenSize = DefaultMonitor.HorizontalScreenSize; MonitorData->VerticalScreenSize = DefaultMonitor.VerticalScreenSize; } return(Status); } VOID FwSetVideoData ( IN PMONITOR_CONFIGURATION_DATA MonitorData ) /*++ Routine Description: This routine stores the monitor configuration data in the NVRAM. Normally this information would be accessed through FwAddChild, but this routine allows this data to be set before the configuration routines have been initialized. N.B. This routine assumes the NVRAM has already been checked by FwGetVideoData and is valid. Arguments: MonitorData - Supplies a pointer to a structure containing the monitor configuration data. Return Value: None. --*/ { ARC_STATUS Status; ULONG EntryIndex; ULONG Index; COMPRESSED_CONFIGURATION_PACKET CompressedPacket; PUCHAR CompressedChars; PUCHAR NvChars; PUCHAR Data; PNV_CONFIGURATION NvConfiguration; NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION; // // Search the NVRAM configuration entries for the monitor, skip // the root entry. // for ( EntryIndex = 1 ; EntryIndex < NUMBER_OF_ENTRIES ; EntryIndex++ ) { // // Read compressed packet from NVRAM. // CompressedChars = (PUCHAR)&CompressedPacket; NvChars = (PUCHAR)&NvConfiguration->Packet[EntryIndex]; for ( Index = 0 ; Index < sizeof(COMPRESSED_CONFIGURATION_PACKET) ; Index++ ) { *CompressedChars++ = READ_REGISTER_UCHAR( NvChars++ ); } // // If the parent field is not Null, and the packet is class Peripheral, // type Monitor, this is the display. // if ((CompressedPacket.Parent != 0) && (CompressedPacket.Class == PeripheralClass) && (CompressedPacket.Type == MonitorPeripheral)) { break; } } // // Copy the data into the monitor data structure. // Data = (PUCHAR)MonitorData; for ( DataIndex = CompressedPacket.ConfigurationData ; DataIndex < (CompressedPacket.ConfigurationData + CompressedPacket.ConfigurationDataLength) ; DataIndex++ ) { WRITE_REGISTER_UCHAR( &NvConfiguration->Data[DataIndex], *Data++); } // // Fix the checksum. // FwConfigurationSetChecksum(); return; } ULONG FwZeroCompressLength ( IN ULONG DataLength, IN PVOID ConfigurationData ) /*++ Routine Description: This routine returns the compressed length of a configuration data sample. Arguments: DataLength - Supplies the uncompressed length of the data. ConfigurationData - Supplies a pointer to the uncompressed configuration data. Return Value: Returns the compressed length of the configuration data. --*/ { ULONG Index; ULONG CompressedLength; ULONG Zero; PUCHAR In; CompressedLength = 2; Zero = 0; In = ConfigurationData; for (Index = 0; Index < DataLength ; Index++ ) { if (*In++) { CompressedLength++; Zero = 0; } else { if (Zero++) { if (Zero == 0x100) { Zero = 1; CompressedLength += 2; } } else { CompressedLength += 2; } } } return(CompressedLength); } ULONG FwZeroCompress ( IN ULONG DataLength, IN PVOID ConfigurationData, OUT PVOID OutputBuffer ) /*++ Routine Description: This routine compresses configuration data. Arguments: DataLength - Supplies the uncompressed length of the data. ConfigurationData - Supplies a pointer to the uncompressed configuration data. OutputBuffer - Supplies a pointer to the buffer to receive the compressed data. Return Value: Returns the compressed length of the configuration data. --*/ { ULONG Index; ULONG CompressedLength; ULONG Zero; PUCHAR In, Out; In = (PUCHAR)ConfigurationData; Out = (PUCHAR)OutputBuffer; CompressedLength = 2; Zero = 0; for (Index = 0; Index < DataLength ; Index++ ) { if (*In) { if (Zero) { Out++; Zero = 0; } *Out++ = *In; CompressedLength++; } else { if (Zero++) { if (Zero == 0x100) { *Out++ = 0xFF; *Out++ = 0; *Out = 1; Zero = 1; CompressedLength += 2; } else { *Out += 1; } } else { *Out++ = 0; *Out = 1; CompressedLength += 2; } } In++; } if (Zero) { Out++; } *Out++ = 0; *Out = 0; return(CompressedLength); } VOID FwZeroDecompress ( IN PVOID InBuffer, IN ULONG Index, OUT PVOID ConfigurationData, IN ULONG Length ) /*++ Routine Description: This routine compresses configuration data. Arguments: InBuffer - Supplies a pointer to the compressed configuration data. Index - Supplies the index into the uncompressed data to start returning. ConfigurationData - Supplies a pointer to the output buffer. Length - Supplies the length of data to uncompress. Return Value: None. --*/ { ULONG DecompressedLength; ULONG Zero; PUCHAR In, Out; UCHAR OutChar; if (InBuffer == NULL) { return; } In = (PUCHAR)InBuffer; Out = (PUCHAR)ConfigurationData; DecompressedLength = 0; Zero = 0; while (DecompressedLength++ < Index + Length) { if (Zero) { Zero--; } else if (*In) { OutChar = *In++; } else { OutChar = 0; Zero = *(++In) - 1; In++; } if (DecompressedLength > Index) { *Out++ = OutChar; } } } #endif