/*++ Copyright (c) 1991 Microsoft Corporation Copyright (c) 1993 Digital Equipment 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 an Alpha/Jensen machine. Author: David M. Robinson (davidro) 13-June-1991 Revision History: 30-April-1992 John DeRosa [DEC] Added Alpha/Jensen hooks. --*/ #include "fwp.h" #if !defined(FAILSAFE_BOOTER) // // 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", "line", "netper", "memory" }; #endif // !defined(FAILSAFE_BOOTER) // // Function prototypes. // ARC_STATUS FwRestoreConfiguration ( VOID ); ARC_STATUS FwConfigurationCheckChecksum ( VOID ); ARC_STATUS 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 ); #if !defined(FAILSAFE_BOOTER) // // 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; #endif // !defined(FAILSAFE_BOOTER) 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. --*/ { // // 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; #ifndef FAILSAFE_BOOTER (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 and restore the configuration from ROM. // FwRestoreConfiguration(); #endif 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. --*/ { #ifndef FAILSAFE_BOOTER 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 { 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); #else // FAILSAFE_BOOTER return NULL; #endif // ndef FAILSAFE_BOOTER } 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. EACCES Component has children, and cannot be freed until they are deleted. --*/ { #ifndef FAILSAFE_BOOTER PCONFIGURATION_PACKET Packet; PCONFIGURATION_PACKET SearchPacket; if (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; #else // FAILSAFE_BOOTER return EINVAL; #endif // ndef FAILSAFE_BOOTER } #ifndef FAILSAFE_BOOTER PCONFIGURATION_COMPONENT FwCoreGetComponent ( IN PCHAR Pathname, IN PCONFIGURATION_COMPONENT MatchComponent ) /*++ Routine Description: This function does the work for FwGetComponent. Arguments: Pathname - Supplies a string containing the pathname to search. MatchComponent - A pointer to the current node in the tree in the recursive search. Return Value: Returns a pointer to the configuration component that best matches pathname. Algorithm: If MatchComponent == NULL, return NULL. else.. Check this node. If a successful match, then: - if we are at the end of the Pathname, return this node. - otherwise, check the children of this node for the remainder of the pathname. If there is a better match in one of the children, return that pointer. Else, return the pointer to this node. else.. Check the peers of this node. If a successful match is returned back, return the pointer returned. else.. return NULL. --*/ { PCONFIGURATION_COMPONENT Component; PCHAR MatchString; PCHAR Token; ULONG Key; BOOLEAN NodeMatch = FALSE; if (MatchComponent == NULL) { return NULL; } // // Check this node. // Token = Pathname; MatchString = MnemonicTable[MatchComponent->Type]; // // Compare strings. // while ((*MatchString == tolower(*Token)) && (*MatchString != 0)){ MatchString++; Token++; } // // Strings compare if the first mismatch is the terminator for each. // if ((*MatchString == 0) && ((*Token == '(') || (*Token == 0))) { // // Form key. // Key = 0; if (*Token == '(') { Token++; } while ((*Token != ')') && (*Token != 0)) { Key = (Key * 10) + *Token++ - '0'; } // // If the key matches the component matches, we have a match. // if (MatchComponent->Key == Key) { NodeMatch = TRUE; } } if (NodeMatch) { // // This node matches the first component specified in Pathname. // Token points at either a ')' or the NULL terminating the // Pathname string. // if ((*Token == 0) || ((*Token == ')' && (*(Token+1) == 0))) ) { return MatchComponent; } else { Component = FwCoreGetComponent (Token+1, FwGetChild(MatchComponent) ); if (Component == NULL) { return MatchComponent; } else { return Component; } } } else { // // This node does not match our current location in the String. // Check the peers. // // If the search fails with the peers of this node, NULL will be // returned, which is what we want to return from this call as well. // return FwCoreGetComponent(Pathname, FwGetPeer(MatchComponent)); } } #endif // ndef FAILSAFE_BOOTER 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. --*/ { #ifndef FAILSAFE_BOOTER PCONFIGURATION_COMPONENT Component; // // Start searching from the first child of the root component. // if ((Component = FwCoreGetComponent(Pathname, FwGetChild(FwGetChild(NULL)))) != NULL) { return Component; } else { return FwGetChild(NULL); } #else // FAILSAFE_BOOTER return NULL; #endif // ndef FAILSAFE_BOOTER } 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. --*/ { #ifndef FAILSAFE_BOOTER PCONFIGURATION_PACKET Packet; if (Component == NULL) { return &Configuration->Packet[0].Component; } else { Packet = CONTAINING_RECORD(Component, CONFIGURATION_PACKET, Component); return &((PCONFIGURATION_PACKET)(Packet->Child))->Component; } #else // FAILSAFE_BOOTER return NULL; #endif // ndef FAILSAFE_BOOTER } 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). --*/ { #ifndef FAILSAFE_BOOTER PCONFIGURATION_PACKET Packet; Packet = CONTAINING_RECORD(Component, CONFIGURATION_PACKET, Component); if (Packet->Parent == NULL) { return NULL; } else { return &((PCONFIGURATION_PACKET)(Packet->Parent))->Component; } #else // FAILSAFE_BOOTER return NULL; #endif // FAILSAFE_BOOTER } 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. --*/ { #ifndef FAILSAFE_BOOTER PCONFIGURATION_PACKET Packet; Packet = CONTAINING_RECORD(Component, CONFIGURATION_PACKET, Component); if (Packet->Peer == NULL) { return NULL; } else { return &((PCONFIGURATION_PACKET)(Packet->Peer))->Component; } #else // FAILSAFE_BOOTER return NULL; #endif // ndef FAILSAFE_BOOTER } #ifndef FAILSAFE_BOOTER ARC_STATUS FwGetConfigurationDataIndex( OUT PVOID ConfigurationData, IN PCONFIGURATION_COMPONENT Component, IN ULONG Index, IN ULONG Length ) /*++ Routine Description: This function 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. --*/ { PCONFIGURATION_PACKET Packet; ULONG DataSize; PUCHAR SourceData; PUCHAR DestinationData; 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; } #endif // ndef FAILSAFE_BOOTER 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. --*/ { #ifndef FAILSAFE_BOOTER return(FwGetConfigurationDataIndex(ConfigurationData, Component, 0, Component->ConfigurationDataLength)); #else return EINVAL; #endif } #if !defined(FAILSAFE_BOOTER) ARC_STATUS FwEnvironmentStore ( VOID ) /*++ Routine Description: This loads the entire environment into the non-volatile environment area. Alpha/Jensen uses a segmented block-erase PROM. When the code wants to store one environment variable, it must store all of them. This routine must *only* be called from FwSaveConfiguration, which does the block-erase and the store of the other part of the non-volatile configuration information. Arguments: None. Return Value: ESUCCESS if the writes were OK. EIO otherwise. --*/ { ULONG Index; PNV_CONFIGURATION NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION; PUCHAR NvChars, VChars; VChars = VolatileEnvironment; NvChars = (PUCHAR)&NvConfiguration->Environment[0]; for (Index = 0; Index < LENGTH_OF_ENVIRONMENT; Index++) { if (FwROMByteWrite(NvChars++, *VChars++) != ESUCCESS) { return EIO; } } if (FwEnvironmentSetChecksum() != ESUCCESS) { return EIO; } else { return ESUCCESS; } } #endif // !defined(FAILSAFE_BOOTER) ARC_STATUS FwSaveConfiguration ( VOID ) /*++ Routine Description: This routine stores all of the configuration entries into NVRAM, including the associated identifier strings and configuration data. Alpha/Jensen saves the entire configuration structure, i.e. including the environment variables, because the ARC CDS + environment variables are all in one structure and Jensen has a segmented block-erase PROM. Doing a complete save changes the least code. 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. EIO Some write error happened in the PROM. --*/ { #ifndef FAILSAFE_BOOTER 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; #ifdef JENSEN // // Erase the PROM block we are going to update. // // N.B. This call erases one block in the underlying ROM set. Therefore, // the block size of the ROM had better be at least the size of the // ARC data to be written. // if (FwROMEraseBlock((PUCHAR)NVRAM_CONFIGURATION) != ESUCCESS) { return ENOSPC; } #endif // // 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++ ) { if (FwROMByteWrite(NvChars++, 0) != ESUCCESS) { FwROMSetARCDataToReadMode(); return EIO; } } 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 is 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++ ) { if (FwROMByteWrite(&NvConfiguration->Identifier[NvIdentifierIndex++], Component->Identifier[Index]) != ESUCCESS) { FwROMSetARCDataToReadMode(); return EIO; } } } 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++ ) { if (FwROMByteWrite(&NvConfiguration->EisaData[NvEisaDataIndex++], *Data++) != ESUCCESS) { FwROMSetARCDataToReadMode(); return EIO; } // // If we have written at least two bytes and the last two // bytes were zero we are 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++ ) { if (FwROMByteWrite(&NvConfiguration->Data[NvDataIndex++], *Data++) != ESUCCESS) { FwROMSetARCDataToReadMode(); return EIO; } } } } // // Write compressed packet to NVRAM. // CompressedChars = (PUCHAR)&CompressedPacket; NvChars = (PUCHAR)&NvConfiguration->Packet[EntryIndex]; for ( Index = 0 ; Index < sizeof(COMPRESSED_CONFIGURATION_PACKET) ; Index++ ) { if (FwROMByteWrite(NvChars++, *CompressedChars++) != ESUCCESS) { FwROMSetARCDataToReadMode(); return EIO; } } } // // Zero the rest of the identifier and configuration data areas. // for ( Index = NvIdentifierIndex ; Index < LENGTH_OF_IDENTIFIER ; Index++ ) { if (FwROMByteWrite(&NvConfiguration->Identifier[Index], 0) != ESUCCESS) { FwROMSetARCDataToReadMode(); return EIO; } } for ( Index = NvDataIndex ; Index < LENGTH_OF_DATA ; Index++ ) { if (FwROMByteWrite(&NvConfiguration->Data[Index] ,0) != ESUCCESS) { FwROMSetARCDataToReadMode(); return EIO; } } for ( Index = NvEisaDataIndex ; Index < LENGTH_OF_EISA_DATA ; Index++ ) { if (FwROMByteWrite(&NvConfiguration->EisaData[Index] ,0) != ESUCCESS) { FwROMSetARCDataToReadMode(); return EIO; } } // // Write configuration data checksum. // #ifdef ALPHA if ((FwConfigurationSetChecksum() != ESUCCESS) || (FwEnvironmentStore() != ESUCCESS) ) { FwROMSetARCDataToReadMode(); return EIO; } FwROMSetARCDataToReadMode(); #else FwConfigurationSetChecksum(); #endif // // Restore configuration information out of NVRAM. This acts to compress // the identifier and configuration data areas if any deletes have been // performed. // return FwRestoreConfiguration(); #else // !defined(FAILSAFE_BOOTER) // // The FailSafe Booter needs this function defined for a call in // the scsidisk.c module. // return ESUCCESS; #endif // !defined(FAILSAFE_BOOTER) } #if !defined(FAILSAFE_BOOTER) 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++ = HalpReadNVByte( 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 parents 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 packets 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++] = HalpReadNVByte( &NvConfiguration->Identifier[NvIdentifierIndex] ); Index++; } while ( HalpReadNVByte(&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++] = HalpReadNVByte( &NvConfiguration->EisaData[NvEisaDataIndex++] ); // // If at least two bytes have been written and the last // two bytes are zero, we are 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++] = HalpReadNVByte( &NvConfiguration->Data[NvDataIndex++] ); } } } } return(ESUCCESS); } #endif // !defined(FAILSAFE_BOOTER) #if !defined(FAILSAFE_BOOTER) 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)NVRAM_CONFIGURATION; for ( Index = 0 ; Index < sizeof(COMPRESSED_CONFIGURATION_PACKET) * NUMBER_OF_ENTRIES + LENGTH_OF_IDENTIFIER + LENGTH_OF_DATA; Index++ ) { Checksum1 += HalpReadNVByte( NvChars++ ); } // // Reconstitute checksum and return error if no compare. // Checksum2 = (ULONG)HalpReadNVByte( &NvConfiguration->Checksum1[0] ) | (ULONG)HalpReadNVByte( &NvConfiguration->Checksum1[1] ) << 8 | (ULONG)HalpReadNVByte( &NvConfiguration->Checksum1[2] ) << 16 | (ULONG)HalpReadNVByte( &NvConfiguration->Checksum1[3] ) << 24 ; if (Checksum1 != Checksum2) { return EIO; } // // Repeat for the eisa data area. // Checksum1 = 0; NvChars = (PUCHAR)NvConfiguration->EisaData; for ( Index = 0 ; Index < LENGTH_OF_EISA_DATA; Index++ ) { Checksum1 += HalpReadNVByte( NvChars++ ); } // // Reconstitute checksum and return error if no compare. // Checksum2 = (ULONG)HalpReadNVByte( &NvConfiguration->Checksum3[0] ) | (ULONG)HalpReadNVByte( &NvConfiguration->Checksum3[1] ) << 8 | (ULONG)HalpReadNVByte( &NvConfiguration->Checksum3[2] ) << 16 | (ULONG)HalpReadNVByte( &NvConfiguration->Checksum3[3] ) << 24 ; if (Checksum1 != Checksum2) { return EIO; } return(ESUCCESS); } #endif // !defined(FAILSAFE_BOOTER) #if !defined(FAILSAFE_BOOTER) ARC_STATUS FwConfigurationSetChecksum ( VOID ) /*++ Routine Description: This routine sets the configuration checksum. This has been coded for Alpha/Jensen. It assumes that the block containing the checksum has already been erased and written to, and that the status of these previous operations has already been checked. Arguments: None. Return Value: ESUCCESS if the checksum was written OK. EIO otherwise. --*/ { PUCHAR NvChars; PNV_CONFIGURATION NvConfiguration; ULONG Index; ULONG Checksum1; NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION; FwROMSetARCDataToReadMode(); // // 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 += HalpReadNVByte( NvChars++ ); } // // Set checksum. // FwROMResetStatus(&NvConfiguration->Checksum1[0]); if ((FwROMByteWrite(&NvConfiguration->Checksum1[0], (UCHAR)(Checksum1 & 0xFF)) != ESUCCESS) || (FwROMByteWrite(&NvConfiguration->Checksum1[1], (UCHAR)((Checksum1 >> 8) & 0xFF)) != ESUCCESS) || (FwROMByteWrite(&NvConfiguration->Checksum1[2], (UCHAR)((Checksum1 >> 16) & 0xFF)) != ESUCCESS) || (FwROMByteWrite(&NvConfiguration->Checksum1[3], (UCHAR)(Checksum1 >> 24)) != ESUCCESS)) { return EIO; } // // Repeat for the eisa data area. // Checksum1 = 0; NvChars = (PUCHAR)NvConfiguration->EisaData; for ( Index = 0 ; Index < LENGTH_OF_EISA_DATA; Index++ ) { Checksum1 += HalpReadNVByte( NvChars++ ); } // // Set checksum. // FwROMResetStatus(&NvConfiguration->Checksum3[0]); if ((FwROMByteWrite(&NvConfiguration->Checksum3[0], (UCHAR)(Checksum1 & 0xFF)) != ESUCCESS) || (FwROMByteWrite(&NvConfiguration->Checksum3[1], (UCHAR)((Checksum1 >> 8) & 0xFF)) != ESUCCESS) || (FwROMByteWrite(&NvConfiguration->Checksum3[2], (UCHAR)((Checksum1 >> 16) & 0xFF)) != ESUCCESS) || (FwROMByteWrite(&NvConfiguration->Checksum3[3], (UCHAR)(Checksum1 >> 24)) != ESUCCESS)) { return EIO; } else { return ESUCCESS; } } #endif // !defined(FAILSAFE_BOOTER) #if !defined(FAILSAFE_BOOTER) 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); } #endif // !defined(FAILSAFE_BOOTER) #if !defined(FAILSAFE_BOOTER) 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); } #endif // !defined(FAILSAFE_BOOTER) #if !defined(FAILSAFE_BOOTER) VOID FwZeroDecompress ( IN PVOID InBuffer, IN ULONG Index, OUT PVOID ConfigurationData, IN ULONG Length ) /*++ Routine Description: This routine decompresses 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 // !defined(FAILSAFE_BOOTER)