1263 lines
33 KiB
C
1263 lines
33 KiB
C
// Copyright (C) 2010 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 "WhimoryBoot.h"
|
|
#include "WhimoryBootTypes.h"
|
|
#include "WMRConfig.h"
|
|
#include "WMROAM.h"
|
|
#include "VFLBuffer.h"
|
|
#include "FPart.h"
|
|
#include "VFL.h"
|
|
#include "FTL.h"
|
|
#include "FIL.h"
|
|
#include "FILTypes.h"
|
|
#include "WMRFeatures.h"
|
|
#include "ANDStats.h"
|
|
#if WMR_UNIT_TEST_ENABLED
|
|
#include "WMRTest.h"
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Static variables
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static struct WhimorySignature _current_signature;
|
|
#ifndef AND_READONLY
|
|
static struct WhimorySignature _signature_at_boot; // keep around for debug
|
|
#endif //AND_READONLY
|
|
|
|
static FTLFunctions *_ftl = NULL;
|
|
static VFLFunctions *_vfl = NULL;
|
|
static FPartFunctions *_fpart = NULL;
|
|
static LowFuncTbl *_fil = NULL;
|
|
|
|
static struct WhimoryDeviceInfo _device_info;
|
|
|
|
extern Boot_FTL_Registry_t g_ftl_registry[];
|
|
extern Boot_VFL_Registry_t g_vfl_registry[];
|
|
|
|
Boot_FPart_Registry_t g_fpart_registry[] =
|
|
{
|
|
{ PPN_FPart_MAJOR_VER, PPN_FPart_GetFunctions },
|
|
{ 0, NULL },
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Static function declarations
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if !(AND_FPART_ONLY)
|
|
static FTLFunctions *
|
|
_LookupFTL
|
|
(UInt32 major_ver);
|
|
|
|
static VFLFunctions *
|
|
_LookupVFL
|
|
(UInt32 major_ver);
|
|
#endif //!(AND_FPART_ONLY)
|
|
|
|
static FPartFunctions *
|
|
_LookupFPart
|
|
(UInt32 major_ver);
|
|
|
|
static void
|
|
_GetDeviceInfo
|
|
(LowFuncTbl *fil);
|
|
|
|
#if (!AND_READONLY) && !(AND_FPART_ONLY)
|
|
|
|
static WhimoryBootCode
|
|
_AllocateInitialSignature
|
|
(void);
|
|
|
|
static BOOL32
|
|
_FillNewSignature
|
|
(struct WhimorySignature *sig,
|
|
UInt32 fpart_major_ver,
|
|
UInt32 fpart_options,
|
|
UInt32 fpart_format_options,
|
|
UInt32 vfl_major_ver,
|
|
UInt32 vfl_options,
|
|
UInt32 ftl_major_ver,
|
|
UInt32 ftl_options,
|
|
UInt32 num_keepout_blocks);
|
|
|
|
static WhimoryBootCode
|
|
_DestructiveFormat
|
|
(struct WhimorySignature *sig,
|
|
UInt32 megabytes_for_ftl);
|
|
|
|
static BOOL32
|
|
_SetFTLPartition
|
|
(UInt32 megabytes_for_ftl);
|
|
|
|
#endif //(!AND_READONLY) && !(AND_FPART_ONLY)
|
|
|
|
static BOOL32
|
|
_SignatureHasCorrectGeometry
|
|
(struct WhimorySignature *sig);
|
|
|
|
static BOOL32
|
|
_andGetStruct
|
|
(UInt32 selector,
|
|
void * data,
|
|
UInt32 * size);
|
|
|
|
static BOOL32
|
|
_andPerformFunction
|
|
(UInt32 selector,
|
|
void *buffer,
|
|
UInt32 *size);
|
|
|
|
#if !(AND_FPART_ONLY)
|
|
static void
|
|
_ClearFILStatistics
|
|
(void);
|
|
#endif //!(AND_FPART_ONLY)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Public function implementations
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
WhimoryBootCode
|
|
WMR_Start
|
|
(UInt32 fpart_major_ver,
|
|
UInt32 fpart_options,
|
|
UInt32 boot_options) // low-power, etc
|
|
{
|
|
ANDStatus status;
|
|
WhimoryBootCode ret_val = kWhimoryOk;
|
|
BOOL32 found_any_signature = FALSE32;
|
|
|
|
if (!OAM_Init())
|
|
{
|
|
ret_val = kWhimoryErrSetup;
|
|
goto start_fail;
|
|
}
|
|
|
|
WMR_ASSERT(sizeof(struct WhimorySignature) == WHIMORY_SIGNATURE_SIZE);
|
|
|
|
#ifndef AND_READONLY
|
|
WMR_PRINT(INIT, "Apple PPN NAND Driver, Read/Write\n");
|
|
#else
|
|
WMR_PRINT(INIT, "Apple PPN NAND Driver, Read-Only\n");
|
|
#endif //AND_READONLY
|
|
|
|
status = FIL_Init();
|
|
if (status != FIL_SUCCESS)
|
|
{
|
|
ret_val = kWhimoryErrMissingHardware;
|
|
goto start_fail;
|
|
}
|
|
|
|
_fil = FIL_GetFuncTbl();
|
|
|
|
// Populate relevant device info
|
|
_GetDeviceInfo(_fil);
|
|
|
|
// Verify that there is a device
|
|
if (0 == _device_info.num_ce)
|
|
{
|
|
WMR_PRINT(ERROR, "No NAND device found\n");
|
|
ret_val = kWhimoryErrMissingHardware;
|
|
goto start_fail;
|
|
}
|
|
|
|
WMR_PRINT(INIT, "FIL_Init [OK]\n");
|
|
|
|
#if WMR_UNIT_TEST_ENABLED
|
|
// FIL Test Hook
|
|
if (!WMR_FIL_Test(_fil))
|
|
{
|
|
ret_val = kWhimoryErrUnitTestFailed;
|
|
goto start_fail;
|
|
}
|
|
#endif //WMR_UNIT_TEST_ENABLED
|
|
|
|
#if AND_LOW_POWER_MOUNT
|
|
// Determine if we need to minimize power
|
|
if (boot_options & kWhimoryBootOptionLowPower)
|
|
{
|
|
UInt32 power_mode = NAND_POWER_MODE_SINGLE_CE;
|
|
FIL_SetStruct(AND_STRUCT_FIL_POWER_MODE,
|
|
&power_mode,
|
|
sizeof(power_mode));
|
|
}
|
|
#endif //AND_LOW_POWER_MOUNT
|
|
|
|
status = BUF_Init(_device_info.main_bytes_per_page,
|
|
_device_info.total_meta_per_page,
|
|
WMR_NUM_BUFFERS);
|
|
|
|
if (status != BUF_SUCCESS)
|
|
{
|
|
ret_val = kWhimoryErrOutOfMemory;
|
|
goto start_fail;
|
|
}
|
|
|
|
_fpart = _LookupFPart(fpart_major_ver);
|
|
if (NULL == _fpart)
|
|
{
|
|
ret_val = kWhimoryErrUnsupportedVersion;
|
|
goto start_fail;
|
|
}
|
|
|
|
WMR_ASSERT(NULL != _fpart->Init);
|
|
if (!_fpart->Init(_fil, fpart_options))
|
|
{
|
|
ret_val = kWhimoryErrSetup;
|
|
goto start_fail;
|
|
}
|
|
|
|
#if WMR_UNIT_TEST_ENABLED
|
|
// FPart Test Hook
|
|
if (!WMR_FPart_Test(_fpart))
|
|
{
|
|
ret_val = kWhimoryErrUnitTestFailed;
|
|
goto start_fail;
|
|
}
|
|
#endif //WMR_UNIT_TEST_ENABLED
|
|
|
|
// Read the signature to see if we're allowed to talk to this NAND
|
|
WMR_MEMSET(&_current_signature, 0xA5, sizeof(_current_signature));
|
|
found_any_signature = _fpart->ReadSpecialBlock(&_current_signature,
|
|
sizeof(_current_signature),
|
|
AND_SB_TYPE_DRIVER_SIGNATURE);
|
|
|
|
if (!found_any_signature)
|
|
{
|
|
WMR_PRINT(INIT, "No signature found.\n");
|
|
ret_val = kWhimoryWarningUnformatted;
|
|
goto start_fail;
|
|
}
|
|
|
|
if (WHIMORY_SIGNATURE_MAGIC != _current_signature.magic)
|
|
{
|
|
WMR_PRINT(ERROR, "Signature has invalid magic: 0x%08x\n",
|
|
_current_signature.magic);
|
|
ret_val = kWhimoryErrCorruptedFormat;
|
|
goto start_fail;
|
|
}
|
|
|
|
if (fpart_major_ver != _current_signature.fpart_major_ver)
|
|
{
|
|
WMR_PRINT(ERROR, "FPart major ver in signature is %d\n",
|
|
_current_signature.fpart_major_ver);
|
|
ret_val = kWhimoryErrCorruptedFormat;
|
|
goto start_fail;
|
|
}
|
|
|
|
if (!_SignatureHasCorrectGeometry(&_current_signature))
|
|
{
|
|
WMR_PRINT(ERROR, "Geometry does not match signature\n");
|
|
ret_val = kWhimoryErrCorruptedFormat;
|
|
goto start_fail;
|
|
}
|
|
|
|
#ifndef AND_READONLY
|
|
// Copy the found signature to a scratch buffer so we can modify it later
|
|
WMR_MEMCPY(&_signature_at_boot,
|
|
&_current_signature,
|
|
sizeof(_current_signature));
|
|
#endif //AND_READONLY
|
|
|
|
if (WHIMORY_SIGNATURE_VER_PPN_CUR < _current_signature.whimory_ver_at_birth)
|
|
{
|
|
// This device was born from a newer driver and cannot be opened
|
|
WMR_PRINT(ERROR, "Device version %d unsupported\n",
|
|
_current_signature.whimory_ver_at_birth);
|
|
ret_val = kWhimoryBirthVersionTooNew;
|
|
goto start_fail;
|
|
}
|
|
|
|
#if !(AND_FPART_ONLY)
|
|
// Make sure that the required FTL and VFL are available
|
|
if (NULL == _LookupVFL(_current_signature.vfl_major_ver))
|
|
{
|
|
ret_val = kWhimoryMajorVersionNotAvailable;
|
|
goto start_fail;
|
|
}
|
|
|
|
if (NULL == _LookupFTL(_current_signature.ftl_major_ver))
|
|
{
|
|
ret_val = kWhimoryMajorVersionNotAvailable;
|
|
goto start_fail;
|
|
}
|
|
#endif //!AND_FPART_ONLY
|
|
|
|
return kWhimoryOk;
|
|
|
|
start_fail:
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
#if !(AND_FPART_ONLY)
|
|
WhimoryBootCode
|
|
WMR_Open
|
|
(UInt32 *total_sectors,
|
|
UInt32 *sector_size,
|
|
UInt32 *index_cache,
|
|
UInt32 boot_options)
|
|
{
|
|
WhimoryBootCode ret_val = kWhimoryOk;
|
|
Int32 status;
|
|
|
|
WMR_ASSERT(NULL != _fil);
|
|
WMR_ASSERT(NULL != _fpart);
|
|
WMR_ASSERT(_current_signature.magic == WHIMORY_SIGNATURE_MAGIC);
|
|
WMR_ASSERT(WHIMORY_SIGNATURE_VER_PPN_CUR >= _current_signature.whimory_ver_at_birth);
|
|
|
|
// Check VFL and FTL software availability first
|
|
_vfl = _LookupVFL(_current_signature.vfl_major_ver);
|
|
if (NULL == _vfl)
|
|
{
|
|
ret_val = kWhimoryErrUnsupportedVersion;
|
|
goto open_fail;
|
|
}
|
|
|
|
WMR_ASSERT(NULL != _vfl->GetMinorVersion);
|
|
if (_current_signature.vfl_minor_ver != _vfl->GetMinorVersion())
|
|
{
|
|
WMR_PRINT(ERROR, "VFL minor version - disk %d sw %d\n",
|
|
_current_signature.vfl_minor_ver, _vfl->GetMinorVersion());
|
|
ret_val = kWhimoryMinorVersionMismatch;
|
|
goto open_fail;
|
|
}
|
|
|
|
_ftl = _LookupFTL(_current_signature.ftl_major_ver);
|
|
if (NULL == _ftl)
|
|
{
|
|
ret_val = kWhimoryErrUnsupportedVersion;
|
|
goto open_fail;
|
|
}
|
|
|
|
WMR_ASSERT(NULL != _ftl->GetMinorVersion);
|
|
if (_current_signature.ftl_minor_ver != _ftl->GetMinorVersion())
|
|
{
|
|
WMR_PRINT(ERROR, "FTL minor version - disk %d sw %d\n",
|
|
_current_signature.ftl_minor_ver, _ftl->GetMinorVersion());
|
|
ret_val = kWhimoryMinorVersionMismatch;
|
|
goto open_fail;
|
|
}
|
|
|
|
status = _vfl->Init(_fpart);
|
|
if (status != VFL_SUCCESS)
|
|
{
|
|
WMR_PRINT(ERROR, "VFL_Init [Failed]\n");
|
|
ret_val = kWhimoryErrSetup;
|
|
goto open_fail;
|
|
}
|
|
|
|
status = _vfl->Open(_current_signature.num_keepout_blocks,
|
|
_current_signature.vfl_minor_ver,
|
|
_current_signature.vfl_options);
|
|
|
|
if (status != VFL_SUCCESS)
|
|
{
|
|
WMR_PRINT(ERROR, "VFL_Open [Failed]\n");
|
|
ret_val = kWhimoryErrCorruptedFormat;
|
|
goto open_fail;
|
|
}
|
|
|
|
WMR_PRINT(INIT, "VFL_Open [OK]\n");
|
|
|
|
_ClearFILStatistics();
|
|
|
|
#if WMR_UNIT_TEST_ENABLED
|
|
// VFL Test Hook
|
|
if (!WMR_VFL_Test(_vfl))
|
|
{
|
|
ret_val = kWhimoryErrUnitTestFailed;
|
|
goto open_fail;
|
|
}
|
|
#endif //WMR_UNIT_TEST_ENABLED
|
|
|
|
status = _ftl->Init(_vfl);
|
|
if (status != FTL_SUCCESS)
|
|
{
|
|
WMR_PRINT(ERROR, "FTL_Init [Failed]\n");
|
|
ret_val = kWhimoryErrSetup;
|
|
goto open_fail;
|
|
}
|
|
|
|
if ((boot_options & kWhimoryBootOptionDisableGCInTrim) == kWhimoryBootOptionDisableGCInTrim)
|
|
{
|
|
_current_signature.ftl_options |= WMR_INIT_DISABLE_GC_IN_TRIM;
|
|
}
|
|
|
|
if ((boot_options & kWhimorySetIndexCacheSize) == kWhimorySetIndexCacheSize)
|
|
{
|
|
_current_signature.ftl_options |= WMR_INIT_SET_INDEX_CACHE_SIZE;
|
|
_current_signature.ftl_options |= ((*index_cache) << 20);
|
|
}
|
|
#ifdef AND_SIMULATOR
|
|
if(boot_options & (WMR_INIT_IGNORE_ERASE_GAP))
|
|
{
|
|
_current_signature.ftl_options |= WMR_INIT_IGNORE_ERASE_GAP;
|
|
}
|
|
#endif
|
|
status = _ftl->Open(total_sectors,
|
|
sector_size,
|
|
((boot_options & kWhimoryBootOptionForceFtlRecovery) ? TRUE32 : FALSE32),
|
|
((boot_options & kWhimoryBootOptionFreshOffFTLFormat) ? TRUE32 : FALSE32),
|
|
_current_signature.ftl_minor_ver,
|
|
_current_signature.ftl_options|((boot_options & kWhimoryBootOptionReadOnly) ? kWhimoryBootOptionReadOnly : 0));
|
|
if (status != FTL_SUCCESS)
|
|
{
|
|
WMR_PRINT(ERROR, "FTL_Open [Failed]\n");
|
|
ret_val = kWhimoryErrCorruptedFormat;
|
|
goto open_fail;
|
|
}
|
|
WMR_PRINT(INIT, "FTL_Open [OK]\n");
|
|
|
|
#if WMR_UNIT_TEST_ENABLED
|
|
// FTL Test Hook
|
|
if (!WMR_FTL_Test(_ftl))
|
|
{
|
|
ret_val = kWhimoryErrUnitTestFailed;
|
|
goto open_fail;
|
|
}
|
|
#endif //WMR_UNIT_TEST_ENABLED
|
|
|
|
return kWhimoryOk;
|
|
|
|
open_fail:
|
|
WMR_PRINT(ERROR, "FAILED: 0x%08x\n", ret_val);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
#ifndef AND_READONLY
|
|
WhimoryBootCode
|
|
WMR_Initial_Format
|
|
(UInt32 fpart_major_ver,
|
|
UInt32 fpart_options,
|
|
UInt32 fpart_format_options,
|
|
UInt32 vfl_major_ver,
|
|
UInt32 vfl_options,
|
|
UInt32 ftl_major_ver,
|
|
UInt32 ftl_options,
|
|
UInt32 num_keepout_blocks,
|
|
UInt32 megabytes_for_ftl)
|
|
{
|
|
WhimoryBootCode boot_code = kWhimoryErrSetup;
|
|
|
|
// Should have previously attempted to FIL_Init
|
|
_fil = FIL_GetFuncTbl();
|
|
WMR_ASSERT(NULL != _fil);
|
|
|
|
// Populate relevant device info
|
|
_GetDeviceInfo(_fil);
|
|
|
|
// Fill-out a signature and get function pointers
|
|
if (_FillNewSignature(&_current_signature,
|
|
fpart_major_ver,
|
|
fpart_options,
|
|
fpart_format_options,
|
|
vfl_major_ver,
|
|
vfl_options,
|
|
ftl_major_ver,
|
|
ftl_options,
|
|
num_keepout_blocks))
|
|
{
|
|
// Write that format without maintaining user data
|
|
boot_code = _DestructiveFormat(&_current_signature, megabytes_for_ftl);
|
|
}
|
|
|
|
return boot_code;
|
|
}
|
|
|
|
WhimoryBootCode
|
|
WMR_Update
|
|
(const BOOL32 keep_backward_compatibility,
|
|
const BOOL32 save_data,
|
|
UInt32 vfl_major_ver,
|
|
UInt32 vfl_options,
|
|
UInt32 ftl_major_ver,
|
|
UInt32 ftl_options,
|
|
UInt32 num_keepout_blocks)
|
|
{
|
|
// This function is like destructive format, but carries forward some
|
|
// signature information instead of generating a new signature
|
|
|
|
WhimoryBootCode boot_code = kWhimoryOk;
|
|
|
|
WMR_ASSERT(WHIMORY_SIGNATURE_MAGIC == _current_signature.magic);
|
|
WMR_ASSERT(WHIMORY_SIGNATURE_VER_PPN_CUR >= _current_signature.whimory_ver_at_birth);
|
|
|
|
// We don't support these types of graceful transitions yet
|
|
WMR_ASSERT(!keep_backward_compatibility);
|
|
WMR_ASSERT(!save_data);
|
|
|
|
_vfl = _LookupVFL(vfl_major_ver);
|
|
if (NULL == _vfl)
|
|
{
|
|
boot_code = kWhimoryErrUnsupportedVersion;
|
|
goto update_error;
|
|
}
|
|
|
|
_current_signature.vfl_major_ver = vfl_major_ver;
|
|
_current_signature.vfl_minor_ver = _vfl->GetMinorVersion();
|
|
_current_signature.vfl_options = vfl_options;
|
|
|
|
_ftl = _LookupFTL(ftl_major_ver);
|
|
if (NULL == _ftl)
|
|
{
|
|
boot_code = kWhimoryErrUnsupportedVersion;
|
|
goto update_error;
|
|
}
|
|
|
|
_current_signature.ftl_major_ver = ftl_major_ver;
|
|
_current_signature.ftl_minor_ver = _ftl->GetMinorVersion();
|
|
_current_signature.ftl_options = ftl_options;
|
|
|
|
if (_current_signature.num_keepout_blocks != num_keepout_blocks)
|
|
{
|
|
WMR_PRINT(ERROR, "Warning: changing keepout from %d to %d\n",
|
|
_current_signature.num_keepout_blocks, num_keepout_blocks);
|
|
_current_signature.num_keepout_blocks = num_keepout_blocks;
|
|
}
|
|
|
|
boot_code = _DestructiveFormat(&_current_signature, 0);
|
|
|
|
if (kWhimoryOk != boot_code)
|
|
{
|
|
// On failure, revert to old setting
|
|
WMR_MEMCPY(&_current_signature, &_signature_at_boot, sizeof(_current_signature));
|
|
}
|
|
|
|
return boot_code;
|
|
|
|
update_error:
|
|
|
|
WMR_MEMCPY(&_current_signature, &_signature_at_boot, sizeof(_current_signature));
|
|
return boot_code;
|
|
}
|
|
|
|
WhimoryBootCode
|
|
WMR_Partition
|
|
(UInt32 megabytes_for_ftl)
|
|
{
|
|
WhimoryBootCode boot_code = kWhimoryOk;
|
|
Int32 nand_status;
|
|
|
|
WMR_ASSERT(NULL != _fpart);
|
|
// Assert that we have a valid signature in memory
|
|
WMR_ASSERT(_current_signature.magic == WHIMORY_SIGNATURE_MAGIC);
|
|
|
|
_vfl = _LookupVFL(_current_signature.vfl_major_ver);
|
|
if (NULL == _vfl)
|
|
{
|
|
boot_code = kWhimoryErrUnsupportedVersion;
|
|
goto partition_fail;
|
|
}
|
|
|
|
_ftl = _LookupFTL(_current_signature.ftl_major_ver);
|
|
if (NULL == _ftl)
|
|
{
|
|
boot_code = kWhimoryErrUnsupportedVersion;
|
|
goto partition_fail;
|
|
}
|
|
|
|
// Assume VFL is already formatted
|
|
if (ANDErrorCodeOk != (nand_status = _vfl->Init(_fpart)))
|
|
{
|
|
boot_code = kWhimoryErrSetup;
|
|
goto partition_fail;
|
|
}
|
|
|
|
nand_status = _vfl->Open(_current_signature.num_keepout_blocks,
|
|
_current_signature.vfl_minor_ver,
|
|
_current_signature.vfl_options);
|
|
if (ANDErrorCodeOk != nand_status)
|
|
{
|
|
WMR_PRINT(ERROR, "VFL Open failed\n");
|
|
boot_code = kWhimoryErrCorruptedFormat;
|
|
goto partition_fail;
|
|
}
|
|
|
|
_ClearFILStatistics();
|
|
|
|
if (!_SetFTLPartition(megabytes_for_ftl))
|
|
{
|
|
boot_code = kWhimoryErrUnsupportedVersion;
|
|
goto partition_fail;
|
|
}
|
|
|
|
// Format the FTL with the new size, but don't open it
|
|
if (ANDErrorCodeOk != (nand_status = _ftl->Init(_vfl)))
|
|
{
|
|
boot_code = kWhimoryErrSetup;
|
|
goto partition_fail;
|
|
}
|
|
nand_status = _ftl->Format(_current_signature.ftl_options);
|
|
if (ANDErrorCodeOk != nand_status)
|
|
{
|
|
WMR_PRINT(ERROR, "FTL Format failed\n");
|
|
boot_code = kWhimoryErrCorruptedFormat;
|
|
goto partition_fail;
|
|
}
|
|
|
|
if (0 != (WHIMORY_FTL_PARTITION_VALID_BIT & _current_signature.megabytes_for_ftl))
|
|
{
|
|
// if there's a pending partition flag in the signature, clear it
|
|
_current_signature.megabytes_for_ftl &= ~WHIMORY_FTL_PARTITION_VALID_BIT;
|
|
|
|
WMR_PRINT(INIT, "Updating signature\n");
|
|
if (!_fpart->WriteSpecialBlock(&_current_signature,
|
|
sizeof(_current_signature),
|
|
AND_SB_TYPE_DRIVER_SIGNATURE))
|
|
{
|
|
WMR_PRINT(ERROR, "Failed to write signature\n");
|
|
boot_code = kWhimoryErrCorruptedFormat;
|
|
goto partition_fail;
|
|
}
|
|
WMR_MEMCPY(&_signature_at_boot, &_current_signature, sizeof(_current_signature));
|
|
}
|
|
|
|
// Tear down so we can call WMR_Open()
|
|
_ftl->Close();
|
|
_vfl->Close();
|
|
|
|
return kWhimoryOk;
|
|
|
|
partition_fail:
|
|
|
|
return boot_code;
|
|
}
|
|
|
|
#endif // ! AND_READONLY
|
|
#endif // ! AND_FPART_ONLY
|
|
|
|
void
|
|
WMR_Close
|
|
(void)
|
|
{
|
|
if (NULL != _ftl)
|
|
{
|
|
_ftl->Close();
|
|
}
|
|
_ftl = NULL;
|
|
|
|
if (NULL != _vfl)
|
|
{
|
|
_vfl->Close();
|
|
}
|
|
_vfl = NULL;
|
|
|
|
if (NULL != _fpart)
|
|
{
|
|
_fpart->Close();
|
|
}
|
|
_fpart = NULL;
|
|
|
|
BUF_Close();
|
|
|
|
// Clear static variables
|
|
|
|
#ifndef AND_READONLY
|
|
WMR_MEMSET(&_signature_at_boot, 0, sizeof(_signature_at_boot));
|
|
#endif //!AND_READONLY
|
|
WMR_MEMSET(&_current_signature, 0, sizeof(_current_signature));
|
|
WMR_MEMSET(&_device_info, 0, sizeof(_device_info));
|
|
}
|
|
|
|
BOOL32
|
|
_andGetStruct
|
|
(UInt32 selector,
|
|
void *data,
|
|
UInt32 *size)
|
|
{
|
|
BOOL32 result = FALSE32;
|
|
|
|
if (LOCKDOWN_EXTRACT_ALL == selector)
|
|
{
|
|
// Mobile Lockdown - Get All Structures
|
|
#if AND_COLLECT_STATISTICS
|
|
result = ANDExportAllStructs(data, size);
|
|
#endif //AND_COLLECT_STATISTICS
|
|
}
|
|
else if (LOCKDOWN_GET_ALL_SIZE == selector)
|
|
{
|
|
#if AND_COLLECT_STATISTICS
|
|
UInt32 *target_size = (UInt32*)data;
|
|
if ((NULL != target_size) &&
|
|
(NULL != size) &&
|
|
(*size >= sizeof(*target_size)))
|
|
{
|
|
result = ANDExportAllStructs(NULL, target_size);
|
|
*size = sizeof(*target_size);
|
|
}
|
|
#endif //AND_COLLECT_STATISTICS
|
|
}
|
|
else
|
|
{
|
|
switch (selector & AND_STRUCT_LAYER_MASK)
|
|
{
|
|
case AND_STRUCT_LAYER_FTL:
|
|
{
|
|
if ((NULL != _ftl) && (NULL != _ftl->GetStruct))
|
|
{
|
|
result = _ftl->GetStruct(selector, data, size);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AND_STRUCT_LAYER_VFL:
|
|
{
|
|
if ((NULL != _vfl) && (NULL != _vfl->GetStruct))
|
|
{
|
|
result = _vfl->GetStruct(selector, data, size);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AND_STRUCT_LAYER_FIL:
|
|
{
|
|
result = FIL_GetStruct(selector, data, size);
|
|
break;
|
|
}
|
|
|
|
case AND_STRUCT_LAYER_GETALL:
|
|
{
|
|
#if AND_COLLECT_STATISTICS
|
|
if (AND_STRUCT_WMR_EXPORT_ALL == selector)
|
|
{
|
|
result = ANDExportAllStructs(data, size);
|
|
}
|
|
else if (AND_STRUCT_WMR_EXPORT_ALL_GET_SIZE == selector)
|
|
{
|
|
UInt32 *target_size = (UInt32*)data;
|
|
if ((NULL != target_size) &&
|
|
(NULL != size) &&
|
|
(*size >= sizeof(*target_size)))
|
|
{
|
|
result = ANDExportAllStructs(NULL, target_size);
|
|
*size = sizeof(*target_size);
|
|
}
|
|
}
|
|
#endif //AND_COLLECT_STATISTICS
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
WMR_PRINT(ERROR, "not implemented: 0x%08x\n", selector);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
BOOL32
|
|
_andPerformFunction
|
|
(UInt32 selector,
|
|
void *buffer,
|
|
UInt32 *size)
|
|
{
|
|
BOOL32 result = FALSE32;
|
|
|
|
switch (selector)
|
|
{
|
|
#ifndef AND_READONLY
|
|
case AND_FUNCTION_INDEX_CACHE_UPDATE:
|
|
{
|
|
if ((NULL != _ftl) &&
|
|
(NULL != _ftl->GetStruct) &&
|
|
(NULL != size))
|
|
{
|
|
result = _ftl->GetStruct(selector, NULL, size);
|
|
*size = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AND_FUNCTION_SET_BURNIN_CODE:
|
|
{
|
|
if ((NULL != _vfl) &&
|
|
(NULL != _vfl->SetStruct) &&
|
|
(NULL != buffer) &&
|
|
(NULL != size))
|
|
{
|
|
result = _vfl->SetStruct(AND_STRUCT_VFL_BURNIN_CODE, buffer, *size);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AND_FUNCTION_SET_POWER_MODE:
|
|
{
|
|
if ((NULL != buffer) &&
|
|
(NULL != size))
|
|
{
|
|
result = FIL_SetStruct(AND_STRUCT_FIL_POWER_MODE, buffer, *size);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AND_FUNCTION_NEURALIZE:
|
|
{
|
|
if (NULL != _fpart)
|
|
{
|
|
result = _fpart->Neuralize();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AND_FUNCTION_SET_TIMINGS:
|
|
{
|
|
if ((NULL != buffer) &&
|
|
(NULL != size))
|
|
{
|
|
result = FIL_SetStruct(AND_STRUCT_FIL_SET_TIMINGS, buffer, *size);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AND_FUNCTION_SAVE_STATS:
|
|
{
|
|
if(_ftl->WriteStats)
|
|
{
|
|
result = _ftl->WriteStats();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _AND_FUNCTION_COMPLETE_EPOCH_UPDATE:
|
|
{
|
|
WMR_PRINT(ERROR, "Epoch update is deprecated\n");
|
|
result = FALSE32;
|
|
|
|
}
|
|
break;
|
|
|
|
#endif // ! AND_READONLY
|
|
default:
|
|
{
|
|
WMR_PANIC("Unknown selector: 0x%08x\n", selector);
|
|
result = FALSE32;
|
|
}
|
|
}
|
|
|
|
if (!result)
|
|
{
|
|
WMR_PRINT(ERROR, "selector 0x%08x failed\n", selector);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOL32
|
|
WMR_PPN_CtrlIO
|
|
(UInt32 selector,
|
|
void *data,
|
|
UInt32 *size)
|
|
{
|
|
if ((selector & AND_STRUCT_LAYER_MASK) == AND_STRUCT_LAYER_FUNCTIONAL)
|
|
{
|
|
return _andPerformFunction(selector, data, size);
|
|
}
|
|
else
|
|
{
|
|
return _andGetStruct(selector, data, size);
|
|
}
|
|
}
|
|
|
|
BOOL32
|
|
WMR_PPNGetStruct
|
|
(UInt32 struct_type,
|
|
void *data,
|
|
UInt32 *size)
|
|
{
|
|
BOOL32 result = FALSE32;
|
|
|
|
switch (struct_type)
|
|
{
|
|
case AND_STRUCT_WMR_VERSION:
|
|
{
|
|
UInt32 dwSignature = WHIMORY_SIGNATURE_MAGIC;
|
|
result = WMR_FILL_STRUCT(data,
|
|
size,
|
|
&dwSignature,
|
|
sizeof(dwSignature));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
WMR_PRINT(ERROR, "unknown struct id: 0x%08x\n", struct_type);
|
|
}
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
FTLFunctions *
|
|
WMR_PPN_GetFTL
|
|
(void)
|
|
{
|
|
WMR_ASSERT(NULL != _ftl);
|
|
|
|
return _ftl;
|
|
}
|
|
|
|
VFLFunctions *
|
|
WMR_PPN_GetVFL
|
|
(void)
|
|
{
|
|
WMR_ASSERT(NULL != _vfl);
|
|
|
|
return _vfl;
|
|
}
|
|
|
|
FPartFunctions *
|
|
WMR_PPN_GetFPart
|
|
(void)
|
|
{
|
|
WMR_ASSERT(NULL != _fpart);
|
|
|
|
return _fpart;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Static function implementations
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if !defined(AND_READONLY) && !(AND_FPART_ONLY)
|
|
BOOL32
|
|
_FillNewSignature
|
|
(struct WhimorySignature *sig,
|
|
UInt32 fpart_major_ver,
|
|
UInt32 fpart_options,
|
|
UInt32 fpart_format_options,
|
|
UInt32 vfl_major_ver,
|
|
UInt32 vfl_options,
|
|
UInt32 ftl_major_ver,
|
|
UInt32 ftl_options,
|
|
UInt32 num_keepout_blocks)
|
|
{
|
|
WMR_MEMSET(sig, 0, sizeof(*sig));
|
|
sig->magic = WHIMORY_SIGNATURE_MAGIC;
|
|
sig->_deprecated_epoch = 0;
|
|
sig->whimory_ver_at_birth = WHIMORY_SIGNATURE_VER_PPN_CUR;
|
|
|
|
// Get an FPart
|
|
sig->fpart_major_ver = fpart_major_ver;
|
|
_fpart = _LookupFPart(fpart_major_ver);
|
|
if (NULL == _fpart)
|
|
{
|
|
return FALSE32;
|
|
}
|
|
WMR_ASSERT(NULL != _fpart->GetMinorVersion);
|
|
sig->fpart_minor_ver = _fpart->GetMinorVersion();
|
|
sig->fpart_options = fpart_options;
|
|
sig->fpart_format_options = fpart_format_options;
|
|
|
|
// Get a VFL
|
|
sig->vfl_major_ver = vfl_major_ver;
|
|
_vfl = _LookupVFL(vfl_major_ver);
|
|
if (NULL == _vfl)
|
|
{
|
|
return FALSE32;
|
|
}
|
|
WMR_ASSERT(NULL != _vfl->GetMinorVersion);
|
|
sig->vfl_minor_ver = _vfl->GetMinorVersion();
|
|
sig->vfl_options = vfl_options;
|
|
|
|
// Get an FTL
|
|
sig->ftl_major_ver = ftl_major_ver;
|
|
_ftl = _LookupFTL(ftl_major_ver);
|
|
if (NULL == _ftl)
|
|
{
|
|
return FALSE32;
|
|
}
|
|
WMR_ASSERT(NULL != _ftl->GetMinorVersion);
|
|
sig->ftl_minor_ver = _ftl->GetMinorVersion();
|
|
|
|
// Stash the number of keepout blocks
|
|
sig->num_keepout_blocks = num_keepout_blocks;
|
|
|
|
// Keep critical geometry information for devices with
|
|
// multiple removable packages
|
|
sig->num_chip_enables = _device_info.num_ce;
|
|
|
|
// For debugging, record what software version formatted this device
|
|
WMR_FILL_SW_VERSION(sig->sw_ver_str, sizeof(sig->sw_ver_str));
|
|
|
|
return TRUE32;
|
|
}
|
|
|
|
WhimoryBootCode
|
|
_AllocateInitialSignature
|
|
(void)
|
|
{
|
|
// if there is no block allocated to signature, allocate one
|
|
if (!_fpart->IsSpecialBlockTypeAllocated(AND_SB_TYPE_DRIVER_SIGNATURE))
|
|
{
|
|
SpecialBlockAddress signature_block;
|
|
if (!_vfl->AllocateSpecialBlock(&signature_block,
|
|
AND_SB_TYPE_DRIVER_SIGNATURE))
|
|
{
|
|
WMR_PRINT(ERROR, "AllocateSpecialBlock for signature failed\n");
|
|
return kWhimoryErrSetup;
|
|
}
|
|
if (!_fpart->AllocateSpecialBlockType(&signature_block,
|
|
1,
|
|
AND_SB_TYPE_DRIVER_SIGNATURE))
|
|
{
|
|
WMR_PRINT(ERROR, "AllocateSpecialBlockType failed\n");
|
|
return kWhimoryErrSetup;
|
|
}
|
|
}
|
|
return kWhimoryOk;
|
|
}
|
|
|
|
WhimoryBootCode
|
|
_DestructiveFormat
|
|
(struct WhimorySignature *sig,
|
|
UInt32 megabytes_for_ftl)
|
|
{
|
|
Int32 nand_status;
|
|
|
|
// Init and format FPart
|
|
if (!_fpart->Init(_fil, sig->fpart_options))
|
|
{
|
|
return kWhimoryErrOutOfMemory;
|
|
}
|
|
|
|
// Format VFL and open it so FTL can use it
|
|
if (ANDErrorCodeOk != (nand_status = _vfl->Init(_fpart)))
|
|
{
|
|
WMR_PRINT(LOG, "_vfl->Init: 0x%08X\n", nand_status);
|
|
return kWhimoryErrOutOfMemory;
|
|
}
|
|
|
|
WMR_PRINT(INIT, "Starting VFL Format\n");
|
|
if (ANDErrorCodeOk != (nand_status = _vfl->Format(sig->num_keepout_blocks,
|
|
sig->vfl_options)))
|
|
{
|
|
WMR_PRINT(LOG, "_vfl->Format: 0x%08X\n", nand_status);
|
|
return kWhimoryErrCorruptedFormat;
|
|
}
|
|
|
|
if (ANDErrorCodeOk != (nand_status = _vfl->Open(sig->num_keepout_blocks,
|
|
sig->vfl_minor_ver,
|
|
sig->vfl_options)))
|
|
{
|
|
WMR_PRINT(LOG, "_vfl->Open: 0x%08X\n", nand_status);
|
|
return kWhimoryErrCorruptedFormat;
|
|
}
|
|
|
|
// Allocate a signature before any persistent FPart blocks
|
|
// so that subsequent searches will find it first
|
|
WMR_PRINT(INIT, "Allocating signature\n");
|
|
if (ANDErrorCodeOk != _AllocateInitialSignature())
|
|
{
|
|
return kWhimoryErrCorruptedFormat;
|
|
}
|
|
|
|
WMR_PRINT(INIT, "Starting FPart Format\n");
|
|
// Allocate persistent special blocks owned by FPart
|
|
if (!_fpart->Format(_vfl, sig->fpart_format_options))
|
|
{
|
|
WMR_PRINT(ERROR, "Failed to format FPart\n");
|
|
return kWhimoryErrSetup;
|
|
}
|
|
|
|
_ClearFILStatistics();
|
|
|
|
// If there is a request for non-zero burnin-size,
|
|
// set that before formatting FTL
|
|
if ((megabytes_for_ftl > 0) &&
|
|
!_SetFTLPartition(megabytes_for_ftl))
|
|
{
|
|
return kWhimoryErrCorruptedFormat;
|
|
}
|
|
|
|
// Format the FTL, but don't open it
|
|
if (ANDErrorCodeOk != (nand_status = _ftl->Init(_vfl)))
|
|
{
|
|
WMR_PRINT(LOG, "_ftl->Init: 0x%08X\n", nand_status);
|
|
return kWhimoryErrOutOfMemory;
|
|
}
|
|
WMR_PRINT(INIT, "Starting FTL Format\n");
|
|
if (ANDErrorCodeOk != (nand_status = _ftl->Format((sig->ftl_options) | (WMR_INIT_RUN_PRODUCTION_FORMAT))))
|
|
{
|
|
WMR_PRINT(LOG, "_ftl->Format: 0x%08X\n", nand_status);
|
|
return kWhimoryErrCorruptedFormat;
|
|
}
|
|
|
|
// Write signature to tell WMR_Open() about the details
|
|
WMR_PRINT(INIT, "Writing final signature\n");
|
|
if (!_fpart->WriteSpecialBlock(sig,
|
|
sizeof(*sig),
|
|
AND_SB_TYPE_DRIVER_SIGNATURE))
|
|
{
|
|
WMR_PRINT(ERROR, "Failed to write signature\n");
|
|
return kWhimoryErrCorruptedFormat;
|
|
}
|
|
WMR_MEMCPY(&_signature_at_boot, sig, sizeof(*sig));
|
|
|
|
// Close so that init can be called in WMR_Open()
|
|
_vfl->Close();
|
|
_ftl->Close();
|
|
return kWhimoryOk;
|
|
}
|
|
|
|
BOOL32
|
|
_SetFTLPartition
|
|
(UInt32 megabytes_for_ftl)
|
|
{
|
|
UInt32 num_superblocks = 0;
|
|
UInt32 num_superblocks_data_size = sizeof(num_superblocks);
|
|
UInt16 superblocks_for_ftl;
|
|
BOOL32 result = FALSE32;
|
|
|
|
if (!_vfl->GetStruct(AND_STRUCT_VFL_NUM_OF_SUBLKS,
|
|
&num_superblocks,
|
|
&num_superblocks_data_size))
|
|
{
|
|
WMR_PRINT(ERROR, "VFL parameters unavailable\n");
|
|
return FALSE32;
|
|
}
|
|
|
|
if (megabytes_for_ftl > 0)
|
|
{
|
|
WMR_ASSERT(num_superblocks > 0);
|
|
|
|
WMR_ASSERT(NULL != _ftl);
|
|
superblocks_for_ftl = _ftl->ConvertUserMBtoFTLSuperblocks(_vfl, megabytes_for_ftl);
|
|
|
|
WMR_PRINT(INIT, "Set to %dMB using %d virtual blocks\n",
|
|
megabytes_for_ftl,
|
|
superblocks_for_ftl);
|
|
WMR_ASSERT(superblocks_for_ftl <= num_superblocks);
|
|
}
|
|
else
|
|
{
|
|
WMR_ASSERT(num_superblocks > 0);
|
|
// All the superblocks go to FTL
|
|
superblocks_for_ftl = num_superblocks;
|
|
|
|
WMR_PRINT(INIT, "full size FTL\n");
|
|
}
|
|
|
|
result = _vfl->SetStruct(AND_STRUCT_VFL_NUM_OF_FTL_SUBLKS,
|
|
&superblocks_for_ftl,
|
|
sizeof(superblocks_for_ftl));
|
|
|
|
if (!result)
|
|
{
|
|
WMR_PRINT(ERROR, "unsupported\n");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif //!defined(AND_READONLY) && !(AND_FPART_ONLY)
|
|
|
|
const struct WhimorySignature *
|
|
WMR_PPNGetSignature
|
|
(void)
|
|
{
|
|
return &_current_signature;
|
|
}
|
|
|
|
static BOOL32
|
|
_SignatureHasCorrectGeometry
|
|
(struct WhimorySignature *sig)
|
|
{
|
|
if (sig->num_chip_enables != _device_info.num_ce)
|
|
{
|
|
return FALSE32;
|
|
}
|
|
|
|
return TRUE32;
|
|
}
|
|
|
|
#if !(AND_FPART_ONLY)
|
|
|
|
FTLFunctions *
|
|
_LookupFTL
|
|
(UInt32 major_ver)
|
|
{
|
|
UInt32 idx;
|
|
|
|
for (idx = 0; NULL != g_ftl_registry[idx].registrar; idx++)
|
|
{
|
|
if (major_ver == g_ftl_registry[idx].major_ver)
|
|
{
|
|
return g_ftl_registry[idx].registrar();
|
|
}
|
|
}
|
|
WMR_PRINT(ERROR, "Unsupported FTL major ver: %d\n", major_ver);
|
|
return NULL;
|
|
}
|
|
|
|
VFLFunctions *
|
|
_LookupVFL
|
|
(UInt32 major_ver)
|
|
{
|
|
UInt32 idx;
|
|
|
|
for (idx = 0; NULL != g_vfl_registry[idx].registrar; idx++)
|
|
{
|
|
if (major_ver == g_vfl_registry[idx].major_ver)
|
|
{
|
|
return g_vfl_registry[idx].registrar();
|
|
}
|
|
}
|
|
WMR_PRINT(ERROR, "Unsupported VFL major ver: %d\n", major_ver);
|
|
return NULL;
|
|
}
|
|
|
|
#endif // ! AND_FPART_ONLY
|
|
|
|
FPartFunctions *
|
|
_LookupFPart
|
|
(UInt32 major_ver)
|
|
{
|
|
UInt32 idx;
|
|
|
|
for (idx = 0; NULL != g_fpart_registry[idx].registrar; idx++)
|
|
{
|
|
if (major_ver == g_fpart_registry[idx].major_ver)
|
|
{
|
|
return g_fpart_registry[idx].registrar();
|
|
}
|
|
}
|
|
WMR_PRINT(ERROR, "Unsupported FPart major ver: %d\n", major_ver);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
_GetDeviceInfo
|
|
(LowFuncTbl *fil)
|
|
{
|
|
_device_info.num_ce = fil->GetDeviceInfo(AND_DEVINFO_NUM_OF_CS);
|
|
_device_info.main_bytes_per_page = fil->GetDeviceInfo(AND_DEVINFO_BYTES_PER_PAGE);
|
|
_device_info.total_meta_per_page = fil->GetDeviceInfo(AND_DEVINFO_BYTES_PER_SPARE);
|
|
_device_info.lba_per_page = fil->GetDeviceInfo(AND_DEVINFO_FIL_LBAS_PER_PAGE);
|
|
}
|
|
|
|
|
|
#if !(AND_FPART_ONLY)
|
|
static void
|
|
_ClearFILStatistics(void)
|
|
{
|
|
FILStatistics fil_stats;
|
|
|
|
WMR_MEMSET(&fil_stats, 0, sizeof(fil_stats));
|
|
FIL_SetStruct(AND_STRUCT_FIL_STATISTICS, &fil_stats, sizeof(fil_stats));
|
|
}
|
|
#endif //!(AND_FPART_ONLY)
|