iBoot/drivers/flash_nand/boot/nand_export.c

344 lines
11 KiB
C

/*
* Copyright (c) 2010-2012 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 <stdarg.h>
#include "nand_part.h"
#include "nand_part_interface.h"
#include "nand_export.h"
#include <FIL.h>
#include <debug.h>
#include <AssertMacros.h>
#include "ANDTypes.h"
#include "WMRFeatures.h"
// =============================================================================
#define _log(fac, line, fmt, args...) dprintf(DEBUG_CRITICAL, "[nand_export.c : %s @ %4d] " fmt "\n", kNandPartLogTag##fac, line, ##args)
#define error(args...) _log(Error, __LINE__, ##args)
#define info(args...) _log(Info, __LINE__, ##args)
#if NAND_PART_EXTENDED_LOGGING
# define warn(args...) _log(Warn, __LINE__, ##args)
# define debug(args...) _log(Debug, __LINE__, ##args)
#else
# define warn(args...) /* do nothing */
# define debug(args...) /* do nothing */
#endif
#if NAND_PART_LOCAL_DEBUG_ONLY
# define spew(args...) _log(Spew, __LINE__, ##args)
#else
# define spew(args...) /* do nothing */
#endif
/* ========================================================================== */
static bool _read_boot_page(void * context, uint32_t ce, uint32_t addr, void * buf, bool * is_clean);
static bool _read_full_page(void * context, uint32_t ce, uint32_t addr, void * buf, void * meta, bool * is_clean);
static bool _write_boot_page(void * context, uint32_t ce, uint32_t addr, void * buf);
static bool _write_full_page(void * context, uint32_t ce, uint32_t addr, void * buf, void * meta);
static bool _erase_block(void * context, uint32_t ce, uint32_t addr, uint32_t block);
static bool _is_block_factory_bad(void * context, uint32_t ce, uint32_t block);
static bool _validate_spare_list(void * context, uint32_t * ce_list, uint32_t * block_list, unsigned count);
static bool _request_spare_blocks(void * context, uint32_t * ce_list, uint32_t * block_list, unsigned count, unsigned * quantity);
static void _init_nand_export_geometry(NandPartProvider *npp);
static void _init_nand_export_callbacks(NandPartProvider *npp);
static int failVal[] = {0, 0, 0};
static int numOps[] = {0, 0, 0};
/* ========================================================================== */
static bool
_read_boot_page(void * context, uint32_t ce, uint32_t addr, void * buf, bool * is_clean)
{
Int32 ret;
nand_boot_context_t * cxt = withContext(context);
require(cxt->is_fil_available, no_fil);
require(0 != FIL_GetFuncTbl()->ReadBLPage, no_op);
ret = FIL_GetFuncTbl()->ReadBLPage(ce, addr, buf);
if (NULL != is_clean) {
*is_clean = (ANDErrorCodeCleanOk == ret);
}
numOps[READ_OP]++;
if (failVal[READ_OP] > 0 && numOps[READ_OP] >= failVal[READ_OP]) {
debug("*** injecting error ***\n");
numOps[READ_OP] = 0;
return false;
}
return((ANDErrorCodeOk == ret) ? true : false);
no_op:
#if RELEASE_BUILD
error("no FIL ReadBLPage");
#else
panic("no FIL ReadBLPage");
#endif
no_fil:
return false;
}
static bool
_read_full_page(void * context, uint32_t ce, uint32_t addr, void * buf, void * meta, bool * is_clean)
{
Int32 ret;
nand_boot_context_t * cxt = withContext(context);
require(cxt->is_fil_available, no_fil);
require(0 != FIL_GetFuncTbl()->ReadWithECC, no_op);
ret = FIL_GetFuncTbl()->ReadWithECC(ce, addr, buf, meta, NULL, NULL, false);
if (NULL != is_clean) {
*is_clean = (ANDErrorCodeCleanOk == ret);
}
numOps[READ_OP]++;
if (failVal[READ_OP] > 0 && numOps[READ_OP] >= failVal[READ_OP]) {
debug("*** injecting error ***\n");
numOps[READ_OP] = 0;
return false;
}
return((ANDErrorCodeOk == ret) ? true : false);
no_op:
#if RELEASE_BUILD
error("no FIL ReadWithECC");
#else
panic("no FIL ReadWithECC");
#endif
no_fil:
return false;
}
static bool
_write_boot_page(void * context, uint32_t ce, uint32_t addr, void * buf)
{
Int32 ret;
nand_boot_context_t * cxt = withContext(context);
require(cxt->is_fil_available, no_fil);
require(0 != FIL_GetFuncTbl()->WriteBLPage, no_op);
ret = FIL_GetFuncTbl()->WriteBLPage(ce, addr, buf);
numOps[WRITE_OP]++;
if (failVal[WRITE_OP] > 0 && numOps[WRITE_OP] >= failVal[WRITE_OP]) {
debug("*** injecting error ***\n");
numOps[WRITE_OP] = 0;
return false;
}
return((ANDErrorCodeOk == ret) ? true : false);
no_op:
#if RELEASE_BUILD
error("no FIL WriteBLPage");
#else
panic("no FIL WriteBLPage");
#endif
no_fil:
return false;
}
static bool
_write_full_page(void * context, uint32_t ce, uint32_t addr, void * buf, void * meta)
{
Int32 ret;
nand_boot_context_t * cxt = withContext(context);
require(cxt->is_fil_available, no_fil);
require(0 != FIL_GetFuncTbl()->Write, no_op);
ret = FIL_GetFuncTbl()->Write(ce, addr, buf, meta, false);
numOps[WRITE_OP]++;
if (failVal[WRITE_OP] > 0 && numOps[WRITE_OP] >= failVal[WRITE_OP]) {
debug("*** injecting error ***\n");
numOps[WRITE_OP] = 0;
return false;
}
return((ANDErrorCodeOk == ret) ? true : false);
no_op:
#if RELEASE_BUILD
error("no FIL Write");
#else
panic("no FIL Write");
#endif
no_fil:
return false;
}
static bool
_erase_block(void * context, uint32_t ce, uint32_t addr, uint32_t block)
{
Int32 ret;
nand_boot_context_t * cxt = withContext(context);
require(cxt->is_fil_available, no_fil);
require(0 != FIL_GetFuncTbl()->Erase, no_op);
// In iBoot, the FIL erase command expects an block offset;
// however, other environments may expect an address instead.
ret = FIL_GetFuncTbl()->Erase(ce, block);
numOps[ERASE_OP]++;
if (failVal[ERASE_OP] > 0 && numOps[ERASE_OP] >= failVal[ERASE_OP]) {
debug("*** injecting error ***\n");
numOps[ERASE_OP] = 0;
return false;
}
return((ANDErrorCodeOk == ret) ? true : false);
no_op:
#if RELEASE_BUILD
error("no FIL Erase");
#else
panic("no FIL Erase");
#endif
no_fil:
return false;
}
/* ========================================================================== */
static bool
_is_block_factory_bad(void * context, uint32_t ce, uint32_t block)
{
// XXX - Access to factory bad block table not yet supported
// in iBoot context. This probably should be fixed from an
// academic perspective; however, by the time we've finished
// formatting during first restore in iOS, the problem of
// factory bad blocks should be moot for nand_part_core.
return false;
}
static bool
_validate_spare_list(void * context, uint32_t * ce_list, uint32_t * block_list, unsigned count)
{
// XXX - Double-checking spare block list against what VFL
// believes it should be is not yet supported in iBoot; therefore,
// for the moment, simply lie when asked and say that all is well.
return true;
}
static bool
_request_spare_blocks(void * context, uint32_t * ce_list, uint32_t * block_list, unsigned count, unsigned * quantity)
{
// Requesting spare blocks should only happen as a result of
// initial format (in iOS); therefore, it's a bug if this
// callback is ever in invoked in iBoot.
#if RELEASE_BUILD
error("unexpected callback requesting spare blocks\n");
#else
panic("unexpected callback requesting spare blocks\n");
#endif
return false;
}
/* ========================================================================== */
static void
_init_nand_export_geometry(NandPartProvider *npp)
{
npp->geometry.isPpn = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_PPN_DEVICE);
npp->geometry.ceCount = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_NUM_OF_CS);
/* The value of AND_DEVINFO_BLOCKS_PER_CS should NOT be used for PPN */
npp->geometry.blocksPerCE = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_BLOCKS_PER_CS);
npp->geometry.blockStride = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_BLOCK_STRIDE);
npp->geometry.pagesPerMLCBlock = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_MLC_PAGES_PER_BLOCK);
npp->geometry.pagesPerSLCBlock = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_SLC_PAGES_PER_BLOCK);
npp->geometry.bytesPerBootPage = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_BYTES_PER_BL_PAGE);
npp->geometry.bytesPerFullPage = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_BYTES_PER_PAGE);
npp->geometry.bytesPerFullPageMeta = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_FIL_META_BUFFER_BYTES) * FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_FIL_LBAS_PER_PAGE);
npp->geometry.bitsPerCauAddr = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_BITS_PER_CAU_ADDRESS);
npp->geometry.bitsPerPageAddr = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_BITS_PER_PAGE_ADDRESS);
npp->geometry.bitsPerBlockAddr = FIL_GetFuncTbl()->GetDeviceInfo(AND_DEVINFO_BITS_PER_BLOCK_ADDRESS);
}
static void
_init_nand_export_callbacks(NandPartProvider *npp)
{
npp->read_boot_page = _read_boot_page;
npp->read_full_page = _read_full_page;
npp->write_boot_page = _write_boot_page;
npp->write_full_page = _write_full_page;
npp->erase_block = _erase_block;
npp->is_block_factory_bad = _is_block_factory_bad;
npp->validate_spare_list = _validate_spare_list;
npp->request_spare_blocks = _request_spare_blocks;
}
/* ========================================================================== */
bool
nand_export_init(nand_boot_context_t * cxt)
{
bool ok = false;
Int32 ret = 0;
NandPartProvider *npp = &(cxt->npp);
ret = FIL_Init();
// Always export callbacks. Even if FIL failed to init, they
// will be protected internally by 'cxt->is_fil_available'.
_init_nand_export_callbacks(npp);
if (ANDErrorCodeOk == ret) {
_init_nand_export_geometry(npp);
ok = true;
} else {
dprintf(DEBUG_SPEW, "unable to init nand FIL\n");
ok = false;
}
cxt->is_fil_available = ok;
return ok;
}
/* ========================================================================== */
extern void
nand_failval_set(int op_num, int fail_val)
{
int i;
failVal[op_num] = fail_val;
/* print out the failure values set for each operation
* where i = 0 = READ, i = 1 = WRITE, and i = 2 = ERASE
*/
for (i = 0; i < 3; i++)
debug("%d %d\n", i, failVal[i]);
}