NT4/private/ntos/nthals/halsgi/mips/sxenvirv.c
2020-09-30 17:12:29 +02:00

611 lines
14 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Copyright (c) 1992 Silicon Graphics, Inc.
Module Name:
s3envirv.c
Abstract:
This module implements the HAL get and set environment variable
routines for the SGI Indigo system.
Author:
Kevin Meier (o-kevinm) 16-Sept-1992
Environment:
Kernel mode
Revision History:
--*/
#include "halp.h"
#include "arccodes.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define NVLEN_MAX 256 // Number of bytes on the 93cs56
// Control opcodes for nonvolatile ram
#define SER_READ 0xc000 // serial memory read
#define SER_WEN 0x9800 // write enable before prog modes
#define SER_WRITE 0xa000 // serial memory write
#define SER_WRALL 0x8800 // write all registers
#define SER_WDS 0x8000 // disable all programming
#define SER_PRREAD 0xc000 // read protect register
#define SER_PREN 0x9800 // enable protect register mode
#define SER_PRCLEAR 0xffff // clear protect register
#define SER_PRWRITE 0xa000 // write protect register
#define SER_PRDS 0x8000 // disable protect register, forever
#define NVRAM_ENTRIES 26 // max number of nvram entries
#define MAXNVNAMELEN 32 // max length of variable name
#define MAX_ENTRY_LEN 64 // max length of variable value
#define NVOFF_CHECKSUM 0 // Checksum offset in nvram
#define NVLEN_CHECKSUM 1 // Checksum length in nvram
//
// Format used to store nvram table information
//
typedef struct _NvramEntry {
char NvName[MAXNVNAMELEN]; // string name of entry
char *NvValue; // PROM: string for default value
int NvAddr; // offset to entry in nvram
int NvLen; // length of entry in nvram
} NvramEntry;
volatile UCHAR *CpuAuxControl;
NvramEntry NvramTable[NVRAM_ENTRIES]; // local storage for nvram
char NvramValues[NVRAM_ENTRIES * MAX_ENTRY_LEN];
typedef
LONG
(*PVEN_NVRAM_TABLE_ROUTINE) (
IN PVOID Table,
IN ULONG Size
);
#define NvramTableRoutine 1
#define VenNvramTable(x,y) \
((PVEN_NVRAM_TABLE_ROUTINE)(SYSTEM_BLOCK->VendorVector[NvramTableRoutine])) \
((x), (y))
static USHORT NvramReadRegister(int);
static void NvramReadString(int, int, char *);
//
// Fetch the nvram table from the PROM through the arcs private vector.
// Must be called before the DSP is enabled.
// We know if the table has grown by the return value.
//
void
HalpInitNvram(void)
{
ULONG Size;
int Entry;
CpuAuxControl = (volatile UCHAR *)SGI_AUX_BASE;
Size = VenNvramTable(NvramTable, sizeof(NvramTable));
if(Size) {
#ifdef DBG
char buf[64];
sprintf(buf, "Nvram table has grown (%d).\n", Size);
HalDisplayString(buf);
DbgBreakPoint();
#endif
return;
}
RtlZeroMemory (NvramValues, sizeof NvramValues);
for (Entry = 0; Entry < NVRAM_ENTRIES; ++Entry) {
if (NvramTable[Entry].NvLen == 0) {
// DbgPrint ("Nvram has %d entries\n", Entry);
break;
}
NvramTable[Entry].NvValue = &NvramValues[Entry * MAX_ENTRY_LEN];
if (NvramTable[Entry].NvLen <= MAX_ENTRY_LEN) {
NvramReadString(NvramTable[Entry].NvAddr, NvramTable[Entry].NvLen,
NvramTable[Entry].NvValue);
}
else {
#ifdef DBG
DbgPrint ("NvEntry %s too large\n", NvramTable[Entry].NvName);
#endif
NvramTable[Entry].NvName[0] = 0;
}
}
#ifdef DBG
if (Entry >= NVRAM_ENTRIES)
DbgPrint("Nvram: too many entries\n");
#endif
}
static UCHAR ConsoleLedState;
//
// Enable the serial memory by setting the console chip select
//
static void
ChipSelOn(void)
{
ConsoleLedState = *CpuAuxControl & CONSOLE_LED;
*CpuAuxControl &= ~CPU_TO_SER;
*CpuAuxControl &= ~SERCLK;
*CpuAuxControl &= ~NVRAM_PRE;
KeStallExecutionProcessor(1);
*CpuAuxControl |= CONSOLE_CS;
*CpuAuxControl |= SERCLK;
}
//
// Turn off the chip select
//
static void
ChipSelOff(void)
{
*CpuAuxControl &= ~SERCLK;
*CpuAuxControl &= ~CONSOLE_CS;
*CpuAuxControl |= NVRAM_PRE;
*CpuAuxControl |= SERCLK;
*CpuAuxControl = (*CpuAuxControl & ~CONSOLE_LED) | ConsoleLedState;
}
#define BITS_IN_COMMAND 11
//
// Clock in the nvram command and the register number. For the National
// nvram chip the op code is 3 bits and the address is 6/8 bits.
//
static void
NvramCommand(ULONG Command, ULONG Register)
{
USHORT SerialCmd;
int BitCount;
SerialCmd = (USHORT)(Command | (Register << (16 - BITS_IN_COMMAND)));
for (BitCount = 0; BitCount < BITS_IN_COMMAND; BitCount++) {
if (SerialCmd & 0x8000) /* if high order bit set */
*CpuAuxControl |= CPU_TO_SER;
else
*CpuAuxControl &= ~CPU_TO_SER;
*CpuAuxControl &= ~SERCLK;
*CpuAuxControl |= SERCLK;
SerialCmd <<= 1;
}
*CpuAuxControl &= ~CPU_TO_SER; /* see data sheet timing diagram */
}
//
// After write/erase commands, we must wait for the command to complete.
// Write cycle time is 10 ms max (~5 ms nom); we timeout after ~20 ms.
// NVDELAY_TIME * NVDELAY_LIMIT = 20 ms
//
#define NVDELAY_TIME 100 // 100 us delay times
#define NVDELAY_LIMIT 200 // 200 delay limit
static int
NvramHold(void)
{
int Error, Timeout = NVDELAY_LIMIT;
ChipSelOn();
while (!(*CpuAuxControl & SER_TO_CPU) && Timeout--)
KeStallExecutionProcessor(NVDELAY_TIME);
Error = (*CpuAuxControl & SER_TO_CPU) ? ESUCCESS : EIO;
ChipSelOff();
return Error;
}
static char
NvramChecksum(void)
{
int Register;
USHORT Value;
char Checksum;
// Seed the checksum so all-zeroes (all-ones) nvram doesn't have a zero
// (all-ones) checksum.
//
Checksum = '\xA5';
// Checksum all of the nvram, but skip the checksum byte.
//
for (Register = 0; Register < NVLEN_MAX / 2; Register++) {
Value = NvramReadRegister(Register);
if(Register == (NVOFF_CHECKSUM / 2))
#if NVOFF_CHECKSUM & 0x01
Checksum ^= Value >> 8;
#else
Checksum ^= Value & 0xff;
#endif
else
Checksum ^= (Value >> 8) ^ (Value & 0xff);
// following is a tricky way to rotate
Checksum = (Checksum << 1) | (Checksum < 0);
}
return Checksum;
}
//
// NvramReadRegister -- read a 16 bit register from non-volatile memory.
// Bytes are stored in this string in big-endian order in each 16 bit word.
//
static USHORT
NvramReadRegister(int Register)
{
USHORT ReadBits = 0;
int BitCount;
UCHAR ConsoleLedOff = *CpuAuxControl & CONSOLE_LED;
*CpuAuxControl &= ~NVRAM_PRE;
ChipSelOn(); /* enable chip select */
NvramCommand(SER_READ, Register);
/* clock the data ouf of serial mem */
for (BitCount = 0; BitCount < 16; BitCount++) {
*CpuAuxControl &= ~SERCLK;
*CpuAuxControl |= SERCLK;
ReadBits <<= 1;
ReadBits |= (*CpuAuxControl & SER_TO_CPU) ? 1 : 0;
}
ChipSelOff();
*CpuAuxControl = (*CpuAuxControl & ~CONSOLE_LED) | ConsoleLedOff;
return ReadBits;
}
//
// NvramWriteRegister -- writes a 16 bit word into non-volatile memory. Bytes
// are stored in this register in big-endian order in each 16 bit word.
//
static ARC_STATUS
NvramWriteRegister(int Register, USHORT Value)
{
int Error, BitCount;
UCHAR ConsoleLedOff = *CpuAuxControl & CONSOLE_LED;
*CpuAuxControl &= ~NVRAM_PRE;
ChipSelOn();
NvramCommand(SER_WEN, 0);
ChipSelOff();
ChipSelOn();
NvramCommand(SER_WRITE, Register);
//
// clock the data into serial mem
//
for (BitCount = 0; BitCount < 16; BitCount++) {
if (Value & 0x8000) // get the high bit
*CpuAuxControl |= CPU_TO_SER;
else
*CpuAuxControl &= ~CPU_TO_SER;
*CpuAuxControl &= ~SERCLK;
*CpuAuxControl |= SERCLK;
Value <<= 1;
}
*CpuAuxControl &= ~CPU_TO_SER;
ChipSelOff();
Error = NvramHold();
ChipSelOn();
NvramCommand(SER_WDS, 0);
ChipSelOff();
*CpuAuxControl = (*CpuAuxControl & ~CONSOLE_LED) | ConsoleLedOff;
return Error;
}
//
// NvramReadString -- read nvram contents into a buffer
//
static void
NvramReadString(int Offset, int Length, char *Buffer)
{
char *BufPtr;
int LenCount;
USHORT Contents;
BufPtr = Buffer;
if (Offset % 2 == 1) {
Contents = NvramReadRegister(Offset / 2);
*BufPtr++ = Contents & 0xff;
Offset++;
Length--;
}
for (LenCount = 0; LenCount < Length / 2; LenCount++) {
Contents = NvramReadRegister(Offset / 2 + LenCount);
*BufPtr++ = (char)(Contents >> 8);
*BufPtr++ = (char)(Contents & 0xff);
}
if (Length % 2 == 1) {
Contents = NvramReadRegister((Offset + Length) / 2);
*BufPtr++ = Contents >> 8;
}
*BufPtr = 0;
}
//
// NvramWriteString -- write string to non-volatile memory
//
static ARC_STATUS
NvramWriteString(int Offset, int Length, char *String)
{
USHORT CurrentVal;
char Checksum[2];
int OffsetSave;
int LenCount;
OffsetSave = Offset;
if (Offset % 2) {
CurrentVal = NvramReadRegister(Offset / 2);
CurrentVal &= 0xff00;
CurrentVal |= *String;
if (NvramWriteRegister(Offset / 2, CurrentVal))
return EIO;
if (*String)
String++;
Offset++;
Length--;
}
for (LenCount = 0; LenCount < Length / 2; LenCount++) {
if (*String) {
CurrentVal = (USHORT) *String++ << 8;
CurrentVal |= *String;
if (*String)
*String++;
} else
CurrentVal = 0;
if (NvramWriteRegister(Offset / 2 + LenCount, CurrentVal))
return EIO;
}
if (Length % 2 == 1) {
CurrentVal = NvramReadRegister((Offset + Length) / 2);
CurrentVal &= 0x00ff;
CurrentVal |= (USHORT) *String << 8;
if (NvramWriteRegister((Offset + Length) / 2, CurrentVal))
return EIO;
}
if (OffsetSave != NVOFF_CHECKSUM) {
Checksum[0] = NvramChecksum();
Checksum[1] = 0;
return NvramWriteString(NVOFF_CHECKSUM, NVLEN_CHECKSUM, Checksum);
}
else
return ESUCCESS;
}
#ifdef MFG_EADDR
//
// NvramSetEaddr - set ethernet address in nvram
//
#define TOHEX(c) ((('0'<=(c))&&((c)<='9')) ? ((c)-'0') : ((c)-'a'+10))
static ARC_STATUS
NvramSetEaddr (char *Value)
{
char Digit[6], *cp;
int DigitCount;
// Expect value to be the address of an ethernet address string
// of the form xx:xx:xx:xx:xx:xx (lower case only)
//
for (DigitCount = 0, cp = Value; *cp ; ) {
if (*cp == ':') {
cp++;
continue;
} else if (!ISXDIGIT(*cp) || !ISXDIGIT(*(cp+1))) {
return EINVAL;
} else {
if (DigitCount >= 6)
return EINVAL;
Digit[DigitCount++] = (TOHEX(*cp)<<4) + TOHEX(*(cp+1));
cp += 2;
}
}
for (DigitCount = 0; DigitCount < NVLEN_ENET; ++DigitCount)
if (NvramWriteString(NVOFF_ENET+DigitCount, 1, &Digit[DigitCount]))
return EIO;
return ESUCCESS;
}
#endif /* MFG_EADDR */
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,
EACCES is returned if the variable is read-only, ENOSPC is returned
if the buffer is too large, or ENOENT is returned if an entry for
this variable is not found.
--*/
{
NvramEntry *NvEntry;
int ValueLen = strlen(Value);
char _value[20];
int _valuelen;
if (!strcmp("eaddr", Variable))
#ifdef MFG_EADDR
return NvramSetEaddr(Value);
#else
return EACCES;
#endif /* MFG_EADDR */
// Don't allow setting the password from the OS, only clearing.
//
if (!strcmp(Variable, "passwd_key") && ValueLen)
return EACCES;
// Change the netaddr to binary for the nvram
//
if (strcmp(Variable, "netaddr") == 0) {
char buf[4];
char *ptr = Value;
int i;
strcpy(_value, Value);
_valuelen = ValueLen;
while (*ptr) // to the end of the string
ptr++;
/* convert string to number, one at a time */
for (i = 3; i >= 0; i--) {
while (*ptr != '.' && ptr >= Value)
ptr--;
buf[i] = atoi(ptr + 1);
if (ptr > Value)
*ptr = 0;
}
Value[0] = buf[0];
Value[1] = buf[1];
Value[2] = buf[2];
Value[3] = buf[3];
ValueLen = 4;
}
// check to see if it is a valid nvram name
//
for (NvEntry = NvramTable; NvEntry->NvLen; NvEntry++)
if (!strcmp(NvEntry->NvName, Variable)) {
int Error;
if(ValueLen > NvEntry->NvLen)
return ENOSPC;
if(ValueLen < NvEntry->NvLen)
++ValueLen; /* write out NULL */
Error = NvramWriteString(NvEntry->NvAddr, ValueLen, Value);
if (strcmp(Variable, "netaddr") == 0) {
strcpy(Value, _value);
ValueLen = _valuelen;
}
if (!Error)
strncpy (NvEntry->NvValue, Value, ValueLen);
return Error;
}
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 if the entry is not found, or ENOSPC is returned
if the buffer isn't large enough.
--*/
{
NvramEntry *NvEntry;
ULONG VarLength;
int Count;
if (!Variable)
return ENOENT;
VarLength = strlen(Variable);
// check to see if it is a valid nvram name
//
for (NvEntry = NvramTable; NvEntry->NvLen; NvEntry++) {
if (!strncmp(NvEntry->NvName, Variable, VarLength)) {
if(NvEntry->NvLen > Length)
return ENOSPC;
for (Count = 0; Count < NvEntry->NvLen; Count++)
Buffer[Count] = NvEntry->NvValue[Count];
return ESUCCESS;
}
}
return ENOENT;
}