466 lines
14 KiB
C
466 lines
14 KiB
C
/*
|
|
* COPYRIGHT (c) 2010-11 Apple Inc. All rights reserved.
|
|
*
|
|
* This document is the property of Apple Inc.
|
|
* It is considered confidential and proprietary.
|
|
*
|
|
* This document may not be reproduced or transmitted in any form,
|
|
* in whole or in part, without the express written permission of
|
|
* Apple Inc.
|
|
*/
|
|
|
|
// =============================================================================
|
|
|
|
#include <effaceable_contract.h>
|
|
#include <effaceable_debug.h>
|
|
|
|
#include "effaceable_private_types.h"
|
|
#include "effaceable_storage_dev.h"
|
|
#include "effaceable_nand_core.h"
|
|
#include "effaceable_delegation.h"
|
|
|
|
// =============================================================================
|
|
|
|
#define debug(...) dlogf(device, __VA_ARGS__ )
|
|
|
|
// =============================================================================
|
|
|
|
extern void
|
|
startEffaceableNAND(effaceable_device_t * device, effaceable_system_t * system, effaceable_nand_hal_t * nand, effaceable_storage_t * storage)
|
|
{
|
|
initContext(device, system, nand, storage);
|
|
initContract(device);
|
|
|
|
setBuf(device, allocMem(device, getUnitSize(device)));
|
|
|
|
startEffaceableStorage(storage, device, system);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static uint32_t
|
|
getGroupCount(effaceable_device_t * device)
|
|
{
|
|
return ((uint32_t)(getBankCount(device) * getBlockCount(device)));
|
|
}
|
|
|
|
static uint32_t
|
|
getUnitsPerGroup(effaceable_device_t * device)
|
|
{
|
|
return ((uint32_t)(getPageCount(device) - kAppleEffaceableNANDTotalMetaPages));
|
|
}
|
|
|
|
static uint32_t
|
|
getUnitSize(effaceable_device_t * device)
|
|
{
|
|
return getPageSize(device);
|
|
}
|
|
|
|
static EffaceableReturn
|
|
eraseGroup(effaceable_device_t * device, uint32_t group)
|
|
{
|
|
const uint32_t bank = mapGroupToBank(device, group);
|
|
const uint32_t block = mapGroupToBlock(device, group);
|
|
|
|
EffaceableReturn ok = kEffaceableReturnError;
|
|
|
|
if (isBlockBad(device, bank, block)) {
|
|
// XXX remap block
|
|
ok = kEffaceableReturnBadMedia;
|
|
} else if (kEffaceableReturnSuccess == (ok = eraseBlock(device, bank, block))) {
|
|
openBlock(device, bank, block);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static void
|
|
openBlock(effaceable_device_t * device, uint32_t bank, uint32_t block)
|
|
{
|
|
uint32_t page;
|
|
const uint32_t start = 0;
|
|
const uint32_t after = kAppleEffaceableNANDOpeningMetaPages;
|
|
void * buf = 0;
|
|
|
|
buf = getBuf(device);
|
|
prepOpenCloseMeta(device, kAppleEffaceableNANDMetaOpenMagic, buf);
|
|
for (page = start; page < after; page++) {
|
|
writePage(device, bank, block, page, buf, getPageSize(device));
|
|
}
|
|
}
|
|
|
|
static void
|
|
closeBlock(effaceable_device_t * device, uint32_t bank, uint32_t block)
|
|
{
|
|
uint32_t page;
|
|
const uint32_t start = (getPageCount(device) - kAppleEffaceableNANDClosingMetaPages);
|
|
const uint32_t after = getPageCount(device);
|
|
void * buf = 0;
|
|
|
|
buf = getBuf(device);
|
|
prepOpenCloseMeta(device, kAppleEffaceableNANDMetaCloseMagic, buf);
|
|
prepCloseContent(device, buf);
|
|
for (page = start; page < after; page++) {
|
|
writePage(device, bank, block, page, buf, getPageSize(device));
|
|
}
|
|
}
|
|
|
|
static void
|
|
prepOpenCloseMeta(effaceable_device_t * device, uint32_t magic, void * buf)
|
|
{
|
|
AppleEffaceableNANDMeta * meta = (AppleEffaceableNANDMeta*)buf;
|
|
|
|
whitenMeta(device, meta);
|
|
meta->header.magic = magic;
|
|
meta->header.version_major = kAppleEffaceableNANDMetaVersionMajorCurrent;
|
|
meta->header.version_minor = kAppleEffaceableNANDMetaVersionMinorCurrent;
|
|
meta->header.flags = kAppleEffaceableNANDMetaFlagsNone;
|
|
}
|
|
|
|
static void
|
|
prepCloseContent(effaceable_device_t * device, void * buf)
|
|
{
|
|
AppleEffaceableNANDMeta * meta = (AppleEffaceableNANDMeta*)buf;
|
|
|
|
if (requestPartitionTableDiff(device, meta->content, getMetaContentSize(device))) {
|
|
meta->header.flags |= kAppleEffaceableNANDMetaFlagsHasPTabDiff;
|
|
debug(INFO, "successfully requested PTabDiff");
|
|
} else {
|
|
debug(WARN, "unable to request PTabDiff");
|
|
}
|
|
}
|
|
|
|
static EffaceableReturn
|
|
writeUnit(effaceable_device_t * device, const void * buf, uint32_t group, uint32_t unit)
|
|
{
|
|
const uint32_t bank = mapGroupToBank(device, group);
|
|
const uint32_t block = mapGroupToBlock(device, group);
|
|
const uint32_t page = mapUnitToPage(device, unit);
|
|
|
|
EffaceableReturn ok = kEffaceableReturnError;
|
|
|
|
if (isBlockBad(device, bank, block)) {
|
|
// XXX replace block iff passing below critical good block count threshold
|
|
ok = kEffaceableReturnBadMedia;
|
|
} else {
|
|
if (kEffaceableReturnSuccess == (ok = writePage(device, bank, block, page, buf, getPageSize(device)))) {
|
|
// if that was final content page, write block closing meta pages
|
|
if ((getUnitsPerGroup(device) - 1) == unit) {
|
|
closeBlock(device, bank, block);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static EffaceableReturn
|
|
readUnit(effaceable_device_t * device, void * buf, uint32_t group, uint32_t unit)
|
|
{
|
|
const uint32_t bank = mapGroupToBank(device, group);
|
|
const uint32_t block = mapGroupToBlock(device, group);
|
|
const uint32_t page = mapUnitToPage(device, unit);
|
|
|
|
EffaceableReturn ok;
|
|
|
|
if (isBlockBad(device, bank, block)) {
|
|
// XXX replace block iff passing below critical good block count threshold
|
|
ok = kEffaceableReturnBadMedia;
|
|
} else {
|
|
ok = readPage(device, bank, block, page, buf, getPageSize(device));
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static uint32_t
|
|
mapGroupToBank(effaceable_device_t * device, uint32_t group)
|
|
{
|
|
const uint32_t bank = ((uint32_t)group) % getBankCount(device);
|
|
// XXX check bounds
|
|
return bank;
|
|
}
|
|
|
|
static uint32_t
|
|
mapGroupToBlock(effaceable_device_t * device, uint32_t group)
|
|
{
|
|
const uint32_t block = ((uint32_t)group) / getBankCount(device);
|
|
// XXX check bounds
|
|
return block;
|
|
}
|
|
|
|
static uint32_t
|
|
mapUnitToPage(effaceable_device_t * device, uint32_t unit)
|
|
{
|
|
const uint32_t page = ((uint32_t)unit) + kAppleEffaceableNANDOpeningMetaPages;
|
|
// XXX check bounds
|
|
return page;
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
static uint32_t
|
|
getCopiesSize(effaceable_device_t * device)
|
|
{
|
|
return getCopySize(device) * getCopiesPerUnit(device);
|
|
}
|
|
|
|
static void
|
|
cleanBuffers(effaceable_device_t * device)
|
|
{
|
|
setMem(device, getBuf(device), 0, getCopiesSize(device));
|
|
}
|
|
|
|
static void
|
|
whitenExtra(effaceable_device_t * device, void * buf)
|
|
{
|
|
// XXX seems like this should be getting called; why isn't it?
|
|
const uint32_t copies_size = getCopiesSize(device);
|
|
const uint32_t extra_size = getUnitSize(device) - copies_size;
|
|
readRandom(device, ((uint8_t*)buf) + copies_size, extra_size);
|
|
}
|
|
|
|
static void
|
|
whitenMeta(effaceable_device_t * device, void * buf)
|
|
{
|
|
readRandom(device, buf, getPageSize(device));
|
|
}
|
|
|
|
static void
|
|
foundCurrentCloneInGroup(effaceable_device_t * device, uint32_t group)
|
|
{
|
|
const uint32_t magic = kAppleEffaceableNANDMetaCloseMagic;
|
|
const uint32_t major = kAppleEffaceableNANDMetaVersionMajorPTabDiff;
|
|
const uint32_t minor = kAppleEffaceableNANDMetaVersionMinorPTabDiff;
|
|
const uint32_t flag = kAppleEffaceableNANDMetaFlagsHasPTabDiff;
|
|
|
|
const uint32_t bank = mapGroupToBank(device, group);
|
|
const uint32_t block = mapGroupToBlock(device, group);
|
|
const uint32_t start = (getPageCount(device) - kAppleEffaceableNANDClosingMetaPages);
|
|
const uint32_t after = getPageCount(device);
|
|
|
|
void * buf = getBuf(device);
|
|
|
|
uint32_t page;
|
|
AppleEffaceableNANDMeta * meta;
|
|
|
|
for (page = start; page < after; page++) {
|
|
debug(INFO, "scanning %lu:%lu:%lu for ptab diff", bank, block, page);
|
|
if (kEffaceableReturnSuccess == readPage(device, bank, block, page, buf, getPageSize(device))) {
|
|
|
|
meta = (AppleEffaceableNANDMeta *)buf;
|
|
if (magic != meta->header.magic) {
|
|
debug(ERR, "bad magic for closing page");
|
|
} else if (0 > compareMetaVersions(device, major, minor, meta->header.version_major, meta->header.version_minor)) {
|
|
debug(INFO, "meta version precedes ptab diff implementation");
|
|
} else if (flag != (flag & meta->header.flags)) {
|
|
debug(INFO, "meta doesn't have ptab diff content");
|
|
} else if (!providePartitionTableDiff(device, meta->content, getMetaContentSize(device))) {
|
|
debug(WARN, "ptab diff content rejected");
|
|
} else {
|
|
debug(INFO, "successfully provided ptab diff");
|
|
}
|
|
|
|
/* readable closing pages should all be identical; no reason to continue */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
compareMetaVersions(effaceable_device_t * device, uint32_t major1, uint32_t minor1, uint32_t major2, uint32_t minor2)
|
|
{
|
|
if (major1 < major2) {
|
|
return -1;
|
|
} else if (major1 > major2) {
|
|
return 1;
|
|
} else if (minor1 < minor2) {
|
|
return -1;
|
|
} else if (minor1 > minor2) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t
|
|
getMetaContentSize(effaceable_device_t * device)
|
|
{
|
|
return (getPageSize(device) - kAppleEffaceableNANDMetaHeaderSize);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static void
|
|
initContract(effaceable_device_t * device)
|
|
{
|
|
// hook required operations
|
|
device->getGroupCount = getGroupCount;
|
|
device->getUnitsPerGroup = getUnitsPerGroup;
|
|
device->getUnitSize = getUnitSize;
|
|
device->eraseGroup = eraseGroup;
|
|
device->writeUnit = writeUnit;
|
|
device->readUnit = readUnit;
|
|
|
|
// hook optional operations
|
|
device->cleanBuffers = cleanBuffers;
|
|
device->foundCurrentCloneInGroup = foundCurrentCloneInGroup;
|
|
device->updateDynamicProperties = (void *)0;
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
static inline effaceable_nand_context_t * context(effaceable_device_t * device)
|
|
{
|
|
return (effaceable_nand_context_t *)device->opaque;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static inline void * getBuf(effaceable_device_t * device)
|
|
{
|
|
return context(device)->buf;
|
|
}
|
|
|
|
static inline void * setBuf(effaceable_device_t * device, void * buf)
|
|
{
|
|
return context(device)->buf = buf;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static inline uint32_t getCopiesPerUnit(effaceable_device_t * device)
|
|
{
|
|
return delegateFn(device, storage, getCopiesPerUnit);
|
|
}
|
|
|
|
static inline uint32_t getCopySize(effaceable_device_t * device)
|
|
{
|
|
return delegateFn(device, storage, getCopySize);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static inline uint32_t getPageSize(effaceable_device_t * device)
|
|
{
|
|
return delegateFn(device, nand, getPageSize);
|
|
}
|
|
|
|
static inline uint32_t getPageCount(effaceable_device_t * device)
|
|
{
|
|
return delegateFn(device, nand, getPageCount);
|
|
}
|
|
|
|
static inline uint32_t getBlockCount(effaceable_device_t * device)
|
|
{
|
|
return delegateFn(device, nand, getBlockCount);
|
|
}
|
|
|
|
static inline uint32_t getBankCount(effaceable_device_t * device)
|
|
{
|
|
return delegateFn(device, nand, getBankCount);
|
|
}
|
|
|
|
static inline bool isBlockBad(effaceable_device_t * device, uint32_t bank, uint32_t block)
|
|
{
|
|
return delegateFn(device, nand, isBlockBad, bank, block);
|
|
}
|
|
|
|
static inline EffaceableReturn eraseBlock(effaceable_device_t * device, uint32_t bank, uint32_t block)
|
|
{
|
|
return delegateFn(device, nand, eraseBlock, bank, block);
|
|
}
|
|
|
|
static inline EffaceableReturn writePage(effaceable_device_t * device, uint32_t bank, uint32_t block, uint32_t page, const void * buf, uint32_t length)
|
|
{
|
|
return delegateFn(device, nand, writePage, bank, block, page, buf, length);
|
|
}
|
|
|
|
static inline EffaceableReturn readPage(effaceable_device_t * device, uint32_t bank, uint32_t block, uint32_t page, void * buf, uint32_t length)
|
|
{
|
|
return delegateFn(device, nand, readPage, bank, block, page, buf, length);
|
|
}
|
|
|
|
static inline bool requestPartitionTableDiff(effaceable_device_t * device, void * buf, uint32_t length)
|
|
{
|
|
return delegateFn(device, nand, requestPartitionTableDiff, buf, length);
|
|
}
|
|
|
|
static inline bool providePartitionTableDiff(effaceable_device_t * device, void * buf, uint32_t length)
|
|
{
|
|
return delegateFn(device, nand, providePartitionTableDiff, buf, length);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static inline void * allocMem(effaceable_device_t * device, uint32_t size)
|
|
{
|
|
return delegateFn(device, system, allocMem, size);
|
|
}
|
|
|
|
static inline void freeMem(effaceable_device_t * device, void * buf, uint32_t size)
|
|
{
|
|
delegateFn(device, system, freeMem, buf, size);
|
|
}
|
|
|
|
static inline void * setMem(effaceable_device_t * device, void * buf, uint8_t val, uint32_t size)
|
|
{
|
|
return delegateFn(device, system, setMem, buf, val, size);
|
|
}
|
|
|
|
static inline void * moveMem(effaceable_device_t * device, void * dst, const void * src, uint32_t size)
|
|
{
|
|
return delegateFn(device, system, moveMem, dst, src, size);
|
|
}
|
|
|
|
static inline int cmpMem(effaceable_device_t * device, const void * lhs, const void * rhs, uint32_t size)
|
|
{
|
|
return delegateFn(device, system, cmpMem, lhs, rhs, size);
|
|
}
|
|
|
|
static inline void readRandom(effaceable_device_t * device, void * buf, uint32_t size)
|
|
{
|
|
delegateFn(device, system, readRandom, buf, size);
|
|
}
|
|
|
|
static inline uint32_t crc32Sys(effaceable_device_t * device, uint32_t crc, const void * buf, uint32_t size, bool finish)
|
|
{
|
|
return delegateFn(device, system, crc32, crc, buf, size, finish);
|
|
}
|
|
|
|
static inline bool setPropertySys(effaceable_device_t * device, const char * key, uint32_t value)
|
|
{
|
|
return delegateFn(device, system, setProperty, key, value);
|
|
}
|
|
|
|
static inline void panicSys(effaceable_device_t * device, const char * msg)
|
|
{
|
|
delegateFn(device, system, panicSys, msg);
|
|
}
|
|
|
|
static int logf(effaceable_device_t * device, const char * fmt, ...)
|
|
{
|
|
int err;
|
|
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
err = delegateFn(device, system, vlogf, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return err;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static void
|
|
initContext(effaceable_device_t * device, effaceable_system_t * system, effaceable_nand_hal_t * nand, effaceable_storage_t * storage)
|
|
{
|
|
device->opaque = system->allocMem(system, sizeof(effaceable_nand_context_t));
|
|
system->setMem(system, context(device), 0, sizeof(effaceable_nand_context_t));
|
|
|
|
context(device)->storage = storage;
|
|
context(device)->system = system;
|
|
context(device)->nand = nand;
|
|
}
|
|
|
|
// =============================================================================
|