Source to iokit/Drivers/platform/drvAppleCore99NVRAM/Core99NVRAM.cpp
/*
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* DRI: Josh de Cesare
*
*/
#include <IOKit/IOLib.h>
#include <IOKit/IODeviceMemory.h>
#include "Core99NVRAM.h"
// Note: Since the storage for NVRAM is inside the BootROM's high
// logevity flash section, it should be good for a lifetime
// of erase and write cycles. However, all care should be
// exersized when writing. Writes should be done only once
// per boot, and only if the NVRAM has changed. If periodic
// syncing is desired it should have a period no less than
// five minutes.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#define super IONVRAMController
OSDefineMetaClassAndStructors(Core99NVRAM, IONVRAMController);
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool Core99NVRAM::start(IOService *provider)
{
IOMemoryMap *nvramMemoryMap;
unsigned long gen1, gen2;
// Get the base address for the nvram.
nvramMemoryMap = provider->mapDeviceMemoryWithIndex(0);
if (nvramMemoryMap == 0) return false;
nvramBaseAddress = (unsigned char *)nvramMemoryMap->getVirtualAddress();
// Allocte the nvram shadow.
nvramShadow = (unsigned char *)IOMalloc(kCore99NVRAMSize);
if (nvramShadow == 0) return false;
// Find the current nvram partition and set the next.
gen1 = validateGeneration(nvramBaseAddress + kCore99NVRAMAreaAOffset);
gen2 = validateGeneration(nvramBaseAddress + kCore99NVRAMAreaBOffset);
if (gen1 > gen2) {
generation = gen1;
nvramCurrent = nvramBaseAddress + kCore99NVRAMAreaAOffset;
nvramNext = nvramBaseAddress + kCore99NVRAMAreaBOffset;
} else {
generation = gen2;
nvramCurrent = nvramBaseAddress + kCore99NVRAMAreaBOffset;
nvramNext = nvramBaseAddress + kCore99NVRAMAreaAOffset;
}
// Copy the nvram into the shadow.
bcopy(nvramCurrent, nvramShadow, kCore99NVRAMSize);
return super::start(provider);
}
IOReturn Core99NVRAM::openNVRAM(void)
{
if (nvramShadow == 0) return kNoNVRAM;
return kNoError;
}
IOReturn Core99NVRAM::syncNVRAM(void)
{
Core99NVRAMHeader *header;
unsigned char *tmpBuffer;
IOReturn error;
if (nvramShadow == 0) return kNoNVRAM;
// Don't write the BootROM if nothing has changed.
if (!bcmp(nvramShadow, nvramCurrent, kCore99NVRAMSize)) return kNoError;
header = (Core99NVRAMHeader *)nvramShadow;
header->generation = ++generation;
header->checksum = chrpCheckSum(nvramShadow);
header->adler32 = adler32(nvramShadow + kCore99NVRAMAdlerStart,
kCore99NVRAMAdlerSize);
error = eraseBlock();
if (error != kIOReturnSuccess) return error;
error = writeBlock(nvramShadow);
if (error != kIOReturnSuccess) return error;
tmpBuffer = nvramCurrent;
nvramCurrent = nvramNext;
nvramNext = tmpBuffer;
return kNoError;
}
IOReturn Core99NVRAM::readNVRAM(UInt32 Offset, IOByteCount *Length,
UInt8 *Buffer)
{
if (nvramShadow == 0) return kNoNVRAM;
if ((Buffer == 0) || (Offset < 0) || (*Length <= 0) ||
((Offset + *Length) > kCore99NVRAMSize)) return kParameterError;
bcopy(nvramShadow + Offset, Buffer, *Length);
return kNoError;
}
IOReturn Core99NVRAM::writeNVRAM(UInt32 Offset, IOByteCount *Length,
UInt8 *Buffer)
{
if (nvramShadow == 0) return kNoNVRAM;
if ((Buffer == 0) || (Offset < 0) || (*Length <= 0) ||
((Offset + *Length) > kCore99NVRAMSize)) return kParameterError;
bcopy(Buffer, nvramShadow + Offset, *Length);
return kNoError;
}
IOReturn Core99NVRAM::eraseBlock(void)
{
IOReturn error;
// Write the Erase Setup Command.
*nvramNext = kCore99NVRAMEraseSetupCmd;
eieio();
// Write the Erase Confirm Command.
*nvramNext = kCore99NVRAMEraseConfirmCmd;
eieio();
error = waitForCommandDone();
// Write the Reset Command.
*nvramNext = kCore99NVRAMResetDeviceCmd;
eieio();
if (error == kIOReturnSuccess) {
error = verifyEraseBlock();
}
return error;
}
IOReturn Core99NVRAM::verifyEraseBlock(void)
{
long cnt;
for (cnt = 0; cnt < kCore99NVRAMSize; cnt++) {
if (nvramNext[cnt] != 0xFF) return kIOReturnInvalid;
}
return kIOReturnSuccess;
}
IOReturn Core99NVRAM::writeBlock(unsigned char *sourceAddress)
{
long cnt;
IOReturn error;
// Write the data byte by byte.
for (cnt = 0; cnt < kCore99NVRAMSize; cnt++) {
nvramNext[cnt] = kCore99NVRAMWriteSetupCmd;
eieio();
nvramNext[cnt] = sourceAddress[cnt];
eieio();
error = waitForCommandDone();
if (error != kIOReturnSuccess) break;
}
// Write the Reset Command.
*nvramNext = kCore99NVRAMResetDeviceCmd;
eieio();
if (error == kIOReturnSuccess) {
error = verifyWriteBlock(sourceAddress);
}
return error;
}
IOReturn Core99NVRAM::verifyWriteBlock(unsigned char *sourceAddress)
{
long cnt;
for (cnt = 0; cnt < kCore99NVRAMSize; cnt++) {
if (nvramNext[cnt] != sourceAddress[cnt]) return kIOReturnInvalid;
}
return kIOReturnSuccess;
}
IOReturn Core99NVRAM::waitForCommandDone(void)
{
unsigned char status;
// There should be a time out in here...
do {
status = *nvramNext;
eieio();
} while ((status & kCore99NVRAMStatusRegCompletionMask) == 0);
// Check for errors.
if (status & kCore99NVRAMStatusRegErrorMask) return kIOReturnInvalid;
return kIOReturnSuccess;
}
unsigned long Core99NVRAM::validateGeneration(unsigned char *nvramBuffer)
{
Core99NVRAMHeader *header = (Core99NVRAMHeader *)nvramBuffer;
// First validate the signature.
if (header->signature != kCore99NVRAMSignature) return 0;
// Next make sure the header's checksum matches.
if (header->checksum != chrpCheckSum(nvramBuffer)) return 0;
// Make sure the adler checksum matches.
if (header->adler32 != adler32(nvramBuffer + kCore99NVRAMAdlerStart,
kCore99NVRAMAdlerSize))
return 0;
return header->generation;
}
unsigned char Core99NVRAM::chrpCheckSum(unsigned char *buffer)
{
long cnt;
unsigned char i_sum, c_sum;
c_sum = 0;
for (cnt = 0; cnt < 16; cnt++) {
// Skip the checksum.
if (cnt == 1) continue;
i_sum = c_sum + buffer[cnt];
if (i_sum < c_sum) i_sum += 1;
c_sum = i_sum;
}
return c_sum;
}
unsigned long Core99NVRAM::adler32(unsigned char *buffer, long length)
{
long cnt;
unsigned long result, lowHalf, highHalf;
lowHalf = 1;
highHalf = 0;
for (cnt = 0; cnt < length; cnt++) {
if ((cnt % 5000) == 0) {
lowHalf %= 65521L;
highHalf %= 65521L;
}
lowHalf += buffer[cnt];
highHalf += lowHalf;
}
lowHalf %= 65521L;
highHalf %= 65521L;
result = (highHalf << 16) | lowHalf;
return result;
}