/*++ Copyright (c) 1991 Microsoft Corporation Module Name: jxenvirv.c Abstract: This module implements the HAL get and set environment variable routines for a MIPS system. Author: Environment: Kernel mode Revision History: --*/ #include "halp.h" #include "arccodes.h" #include "jazznvr.h" #include "string.h" // // Define base address at which to map NVRAM. // #define NVRAM_MEMORY_BASE PTE_BASE // // Define local upcase macro. // #define UpCase(c) ((c) >= 'a' && (c) <= 'z' ? (c) - ('a' - 'A') : (c)) KIRQL HalpMapNvram ( IN PENTRYLO SavedPte ) /*++ Routine Description: This function is called to map the NVRAM into the address space of the current process. Arguments: SavedPte - Supplies a pointer to an array which receives the old NVRAM PTE values. Return Value: The previous IRQL is returned as the function value. --*/ { KIRQL OldIrql; ENTRYLO NvramPte; PENTRYLO PtePointer; // // Construct a PTE to map NVRAM into the address space of the current // process. // NvramPte.X1 = 0; NvramPte.PFN = NVRAM_PHYSICAL_BASE >> PAGE_SHIFT; NvramPte.G = 0; NvramPte.V = 1; NvramPte.D = 1; #if defined(R3000) NvramPte.N = 1; #endif #if defined(R4000) NvramPte.C = UNCACHED_POLICY; #endif // // Raise IRQL to the highest level, flush the TB, map the NVRAM into // the address space of the current process, and return the previous // IRQL. // PtePointer = (PENTRYLO)PDE_BASE; KeRaiseIrql(HIGH_LEVEL, &OldIrql); SavedPte[0] = PtePointer[0]; SavedPte[1] = PtePointer[1]; KeFlushCurrentTb(); PtePointer[0] = NvramPte; NvramPte.PFN += 1; PtePointer[1] = NvramPte; return OldIrql; } VOID HalpUnmapNvram ( IN PENTRYLO SavedPte, IN KIRQL OldIrql ) /*++ Routine Description: This function is called to unmap the NVRAM from the address space of the current process. Arguments: SavedPte - Supplies a pointer to an array which contains the old NVRAM PTE values. OldIrql - Supplies the previous IRQL value. Return Value: None. --*/ { PENTRYLO PtePointer; // // Restore the previous mapping for the current process, flush the TB, // and lower IRQL to its previous level. // PtePointer = (PENTRYLO)PDE_BASE; KeFlushCurrentTb(); PtePointer[0] = SavedPte[0]; PtePointer[1] = SavedPte[1]; KeLowerIrql(OldIrql); return; } ARC_STATUS HalpEnvironmentCheckChecksum ( VOID ) /*++ Routine Description: This routine checks the NVRAM environment area checksum. N.B. The NVRAM must be mapped before this routine is called. Arguments: None. Return Value: ESUCCESS is returned if the checksum matches. Otherwise, EIO is returned. --*/ { ULONG Checksum1; ULONG Checksum2; PUCHAR Environment; ULONG Index; PNV_CONFIGURATION NvConfiguration; // // Compute the NVRAM environment area checksum. // NvConfiguration = (PNV_CONFIGURATION)NVRAM_MEMORY_BASE; Environment = &NvConfiguration->Environment[0]; Checksum1 = 0; for (Index = 0; Index < LENGTH_OF_ENVIRONMENT; Index += 1) { Checksum1 += (ULONG)READ_REGISTER_UCHAR(&Environment[Index]); } // // Merge the checksum bytes from the NVRAM and compare to computed value. // Checksum2 = (ULONG)READ_REGISTER_UCHAR(&NvConfiguration->Checksum2[0]) | (ULONG)READ_REGISTER_UCHAR(&NvConfiguration->Checksum2[1]) << 8 | (ULONG)READ_REGISTER_UCHAR(&NvConfiguration->Checksum2[2]) << 16 | (ULONG)READ_REGISTER_UCHAR(&NvConfiguration->Checksum2[3]) << 24; // // If the checksum mismatches, then return an I/O error. Otherwise, // return a success status. // if (Checksum1 != Checksum2) { return EIO; } else { return ESUCCESS; } } VOID HalpEnvironmentSetChecksum ( VOID ) /*++ Routine Description: This routine sets the NVRAM environment area checksum. N.B. The NVRAM must be mapped before this routine is called. Arguments: None. Return Value: None. --*/ { ULONG Checksum; PUCHAR Environment; ULONG Index; PNV_CONFIGURATION NvConfiguration; // // Compute the NVRAM environment area checksum. // NvConfiguration = (PNV_CONFIGURATION)NVRAM_MEMORY_BASE; Environment = &NvConfiguration->Environment[0]; Checksum = 0; for (Index = 0; Index < LENGTH_OF_ENVIRONMENT; Index += 1) { Checksum += (ULONG)READ_REGISTER_UCHAR(&Environment[Index]); } // // Write the NVRAM environment area checksum. // WRITE_REGISTER_UCHAR(&NvConfiguration->Checksum2[0], (UCHAR)(Checksum & 0xFF)); WRITE_REGISTER_UCHAR(&NvConfiguration->Checksum2[1], (UCHAR)((Checksum >> 8) & 0xFF)); WRITE_REGISTER_UCHAR(&NvConfiguration->Checksum2[2], (UCHAR)((Checksum >> 16) & 0xFF)); WRITE_REGISTER_UCHAR(&NvConfiguration->Checksum2[3], (UCHAR)(Checksum >> 24)); return; } ARC_STATUS HalpFindEnvironmentVariable ( IN PCHAR Variable, OUT PULONG VariableIndex, OUT PULONG ValueIndex ) /*++ Routine Description: This routine performs a case insensitive search of the NVRAM environment area for the specified variable name. N.B. The NVRAM must be mapped before this routine is called. Arguments: Variable - Supplies a pointer to a zero terminated string containing an environment variable name. Return Value: ESUCCESS is returned if the specified variable name is located. Otherwise, ENOENT is returned. --*/ { PUCHAR Environment; ULONG Index; PUCHAR Name; // // If the variable name is null, then return no entry found. // if (*Variable == 0) { return ENOENT; } // // Search the environment section of the NVRAM for a variable name match. // Environment = &((PNV_CONFIGURATION)NVRAM_MEMORY_BASE)->Environment[0]; Index = 0; do { // // Set name to the beginning of the variable name and record the // current index value. // Name = Variable; *VariableIndex = Index; // // Search until the end of the current environment variable, the // end of the specified variable name, or the end of the NVRAM is // reached. // while ((Index < LENGTH_OF_ENVIRONMENT) && (READ_REGISTER_UCHAR(&Environment[Index]) != 0) && (*Name != 0)) { if (READ_REGISTER_UCHAR(&Environment[Index]) != UpCase(*Name)) { break; } Name += 1; Index += 1; } // // Check for a match which is signified by the end of the variable // name and the equal separator in the current environment variable. // if ((*Name == 0) && (READ_REGISTER_UCHAR(&Environment[Index]) == '=')) { *ValueIndex = Index + 1; return ESUCCESS; } // // Advance to the start of the next variable. // while ((Index < LENGTH_OF_ENVIRONMENT) && (READ_REGISTER_UCHAR(&Environment[Index]) != 0)) { Index += 1; } Index += 1; } while (Index < LENGTH_OF_ENVIRONMENT); return ENOENT; } ARC_STATUS HalGetEnvironmentVariable ( IN PCHAR Variable, IN USHORT Length, OUT PCHAR Buffer ) /*++ Routine Description: This function locates an environment variable and returns its value. Arguments: Variable - Supplies a pointer to a zero terminated environment variable name. Length - Supplies the length of the value buffer in bytes. Buffer - Supplies a pointer to a buffer that receives the variable value. Return Value: ESUCCESS is returned if the enviroment variable is located. Otherwise, ENOENT is returned. --*/ { PUCHAR Environment; ULONG Index; KIRQL OldIrql; ENTRYLO SavedPte[2]; ARC_STATUS Status; ULONG ValueIndex; ULONG VariableIndex; // // Map the NVRAM into the address space of the current process. // OldIrql = HalpMapNvram(&SavedPte[0]); // // If the checksum does not match or the specified variable cannot // be located, then set the status to no entry found. Otherwise, copy // the respective variable value to the specified output buffer. // Environment = &((PNV_CONFIGURATION)NVRAM_MEMORY_BASE)->Environment[0]; if ((HalpEnvironmentCheckChecksum() != ESUCCESS) || (HalpFindEnvironmentVariable(Variable, &VariableIndex, &ValueIndex) != ESUCCESS)) { Status = ENOENT; } else { // // Copy the specified value to the output buffer. // for (Index = 0; Index < Length; Index += 1) { *Buffer = READ_REGISTER_UCHAR(&Environment[ValueIndex]); if (*Buffer == 0) { break; } Buffer += 1; ValueIndex += 1; } // // If the length terminated the loop, then return not enough memory. // Otherwise, return success. // if (Index == Length) { Status = ENOMEM; } else { Status = ESUCCESS; } } // // Unmap the NVRAM from the address space of the current process and // return the function status. // HalpUnmapNvram(&SavedPte[0], OldIrql); return Status; } ARC_STATUS HalSetEnvironmentVariable ( IN PCHAR Variable, IN PCHAR Value ) /*++ Routine Description: This function creates an environment variable with the specified value. Arguments: Variable - Supplies a pointer to an environment variable name. Value - Supplies a pointer to the environment variable value. Return Value: ESUCCESS is returned if the environment variable is created. Otherwise, ENOMEM is returned. --*/ { UCHAR Character; PUCHAR Environment; KIRQL OldIrql; ENTRYLO SavedPte[2]; ARC_STATUS Status; ULONG TopIndex; ULONG VariableIndex; ULONG VariableLength; ULONG ValueEnd; ULONG ValueIndex; ULONG ValueLength; // // Map the NVRAM into the address space of the current process. // OldIrql = HalpMapNvram(&SavedPte[0]); Environment = &((PNV_CONFIGURATION)NVRAM_MEMORY_BASE)->Environment[0]; // // If the checksum does not match, then set status to an I/O error. // if (HalpEnvironmentCheckChecksum() != ESUCCESS) { Status = EIO; goto Unmap; } // // Determine the top of the environment area by scanning backwards until // the a non-null character is found or the beginning of the environment // area is reached. // for (TopIndex = (LENGTH_OF_ENVIRONMENT - 1); TopIndex > 0; TopIndex -= 1) { if (READ_REGISTER_UCHAR(&Environment[TopIndex]) != '\0') { break; } } // // If the environment area contains any data, then adjust the top index // to the first free byte. // if (TopIndex != 0) { TopIndex += 2; } // // Compute the length of the variable name and the variable value. // VariableLength = strlen(Variable) + 1; ValueLength = strlen(Value) + 1; // // Check to determine if the specified variable is currently defined. // if (HalpFindEnvironmentVariable(Variable, &VariableIndex, &ValueIndex) == ESUCCESS) { // // The specified variable is currently defined. Determine the end // of the variable value by scanning forward to the zero termination // byte. // ValueEnd = ValueIndex; while (READ_REGISTER_UCHAR(&Environment[ValueEnd]) != '\0') { ValueEnd += 1; } ValueEnd += 1; // // If there is enough free space for the new variable value, then // remove the current variable name and value from the environment // area, insert the new variable value at the end of the environment // if it is not null, and set the status to success. Otherwise, set // the status to no space available. // if ((ValueEnd - ValueIndex + LENGTH_OF_ENVIRONMENT - TopIndex) >= ValueLength) { while (ValueEnd != TopIndex) { Character = READ_REGISTER_UCHAR(&Environment[ValueEnd]); WRITE_REGISTER_UCHAR(&Environment[VariableIndex], Character); ValueEnd += 1; VariableIndex += 1; } ValueIndex = VariableIndex; while (ValueIndex != TopIndex) { WRITE_REGISTER_UCHAR(&Environment[ValueIndex], '\0'); ValueIndex += 1; } // // If the new variable value is not null, then copy the variable // name and the variable value into the enviroment area. // if (*Value != '\0') { // // copy the variable name to the environment area. // do { WRITE_REGISTER_UCHAR(&Environment[VariableIndex], UpCase(*Variable)); VariableIndex += 1; Variable += 1; } while (*Variable != '\0'); // // Insert separator character and copy variable value to the // environment area. // WRITE_REGISTER_UCHAR(&Environment[VariableIndex], '='); VariableIndex += 1; do { WRITE_REGISTER_UCHAR(&Environment[VariableIndex], *Value); VariableIndex += 1; Value += 1; } while (*Value != '\0'); } Status = ESUCCESS; } else { Status = ENOSPC; } } else { // // The specified variable does not currently have a value. If the // specified variable is null or has no value, then set the status // to success. Otherwise, if the free area is not large enough to // hold the new variable name and its value, then set the status to // no space available. Otherwise, insert the variable name and value // at the end of the environment area and set the status to success. // if ((*Variable == '\0') || (*Value == '\0')) { Status = ESUCCESS; } else if ((LENGTH_OF_ENVIRONMENT - TopIndex) < (VariableLength + ValueLength)) { Status = ENOSPC; } else { // // copy the variable name to the environment area. // do { WRITE_REGISTER_UCHAR(&Environment[TopIndex], UpCase(*Variable)); TopIndex += 1; Variable += 1; } while (*Variable != '\0'); // // Insert separator character and copy variable value to the // environment area. // WRITE_REGISTER_UCHAR(&Environment[TopIndex], '='); TopIndex += 1; do { WRITE_REGISTER_UCHAR(&Environment[TopIndex], *Value); TopIndex += 1; Value += 1; } while (*Value != '\0'); Status = ESUCCESS; } } // // Compute the new checksum and write to the environment area. // HalpEnvironmentSetChecksum(); // // Unmap the NVRAM from the address space of the current process. // Unmap: HalpUnmapNvram(&SavedPte[0], OldIrql); return Status; }