#include <IOKit/IOLib.h>
#include <IOKit/IODeviceMemory.h>
#include "Core99NVRAM.h"
#define super IONVRAMController
OSDefineMetaClassAndStructors(Core99NVRAM, IONVRAMController);
bool Core99NVRAM::start(IOService *provider)
{
IOMemoryMap *nvramMemoryMap;
unsigned long gen1, gen2;
nvramMemoryMap = provider->mapDeviceMemoryWithIndex(0);
if (nvramMemoryMap == 0) return false;
nvramBaseAddress = (unsigned char *)nvramMemoryMap->getVirtualAddress();
nvramShadow = (unsigned char *)IOMalloc(kCore99NVRAMSize);
if (nvramShadow == 0) return false;
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;
}
bcopy(nvramCurrent, nvramShadow, kCore99NVRAMSize);
return super::start(provider);
}
void Core99NVRAM::sync(void)
{
Core99NVRAMHeader *header;
unsigned char *tmpBuffer;
if (!bcmp(nvramShadow, nvramCurrent, kCore99NVRAMSize)) return;
header = (Core99NVRAMHeader *)nvramShadow;
header->generation = ++generation;
header->checksum = chrpCheckSum(nvramShadow);
header->adler32 = adler32(nvramShadow + kCore99NVRAMAdlerStart,
kCore99NVRAMAdlerSize);
if (eraseBlock() != kIOReturnSuccess) return;
if (writeBlock(nvramShadow) != kIOReturnSuccess) return;
tmpBuffer = (unsigned char *)nvramCurrent;
nvramCurrent = nvramNext;
nvramNext = tmpBuffer;
}
IOReturn Core99NVRAM::read(IOByteCount offset, UInt8 *buffer,
IOByteCount length)
{
if (nvramShadow == 0) return kIOReturnNotReady;
if ((buffer == 0) || (length <= 0) || (offset < 0) ||
((offset + length) > kCore99NVRAMSize))
return kIOReturnBadArgument;
bcopy(nvramShadow + offset, buffer, length);
return kIOReturnSuccess;
}
IOReturn Core99NVRAM::write(IOByteCount offset, UInt8 *buffer,
IOByteCount length)
{
if (nvramShadow == 0) return kIOReturnSuccess;
if ((buffer == 0) || (length <= 0) || (offset < 0) ||
((offset + length) > kCore99NVRAMSize))
return kIOReturnBadArgument;
bcopy(buffer, nvramShadow + offset, length);
return kIOReturnSuccess;
}
IOReturn Core99NVRAM::eraseBlock(void)
{
IOReturn error;
*nvramNext = kCore99NVRAMEraseSetupCmd;
eieio();
*nvramNext = kCore99NVRAMEraseConfirmCmd;
eieio();
error = waitForCommandDone();
*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;
for (cnt = 0; cnt < kCore99NVRAMSize; cnt++) {
nvramNext[cnt] = kCore99NVRAMWriteSetupCmd;
eieio();
nvramNext[cnt] = sourceAddress[cnt];
eieio();
error = waitForCommandDone();
if (error != kIOReturnSuccess) break;
}
*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;
do {
status = *nvramNext;
eieio();
} while ((status & kCore99NVRAMStatusRegCompletionMask) == 0);
if (status & kCore99NVRAMStatusRegErrorMask) return kIOReturnInvalid;
return kIOReturnSuccess;
}
unsigned long Core99NVRAM::validateGeneration(unsigned char *nvramBuffer)
{
Core99NVRAMHeader *header = (Core99NVRAMHeader *)nvramBuffer;
if (header->signature != kCore99NVRAMSignature) return 0;
if (header->checksum != chrpCheckSum(nvramBuffer)) return 0;
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++) {
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;
}