iBoot/drivers/apple/h2fmi/H2fmi_iop.c

903 lines
34 KiB
C
Raw Normal View History

2023-07-08 13:03:17 -07:00
// *****************************************************************************
//
// File: H2fmi_iop.c
//
// *****************************************************************************
//
// Notes:
//
// *****************************************************************************
//
// Copyright (C) 2010 Apple, Inc. All rights reserved.
//
// This document is the property of Apple Computer, 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 Computer, Inc.
//
// *****************************************************************************
#include "H2fmi_iop.h"
#include "H2fmi_private.h"
#include "H2fmi_ppn.h"
#include "fmiss.h"
#include <platform/soc/hwclocks.h>
#include <platform/soc/pmgr.h>
#include <platform/memmap.h>
#include <platform.h>
#include <drivers/dma.h>
#include <drivers/aes.h>
#include <debug.h>
// =============================================================================
// external global variable definitions
// =============================================================================
extern UInt8 nand_whitening_key[16];
// =============================================================================
// private function declarations
// =============================================================================
bool h2fmi_setup_aes(h2fmi_t* fmi, IOPFMI* cmd, hfmi_aes_iv* aes_iv_array, UInt32 aes_chain_size, UInt32 aes_key_type, UInt32* aes_key, UInt32* seeds)
{
if (cmd->flags & (kIOPFMI_FLAGS_USE_AES | kIOPFMI_FLAGS_HOMOGENIZE))
{
switch (cmd->opcode)
{
case kIOPFMI_OPCODE_WRITE_SINGLE:
case kIOPFMI_OPCODE_WRITE_MULTIPLE:
case kIOPFMI_OPCODE_PPN_WRITE:
fmi->aes_cxt.command = AES_CMD_ENC;
break;
case kIOPFMI_OPCODE_READ_SINGLE:
case kIOPFMI_OPCODE_READ_MULTIPLE:
case kIOPFMI_OPCODE_PPN_READ:
fmi->aes_cxt.command = AES_CMD_DEC;
break;
default:
ASSERT(false);
return false;
break;
}
if (cmd->flags & kIOPFMI_FLAGS_USE_AES)
{
fmi->current_aes_iv_array = (hfmi_aes_iv*)aes_iv_array;
switch (aes_key_type)
{
case kIOPFMI_AES_KEY_TYPE_USER128:
fmi->aes_cxt.keying = AES_KEY_TYPE_USER | AES_KEY_SIZE_128;
fmi->aes_cxt.key = aes_key;
WMR_PRINT(CRYPTO, "Using HW key IV: 0x%08x 0x%08x 0x%08x 0x%08x\n",
aes_key[0], aes_key[1], aes_key[2], aes_key[3]);
break;
case kIOPFMI_AES_KEY_TYPE_USER192:
fmi->aes_cxt.keying = AES_KEY_TYPE_USER | AES_KEY_SIZE_192;
fmi->aes_cxt.key = aes_key;
WMR_PRINT(CRYPTO, "Using HW key IV: 0x%08x 0x%08x 0x%08x 0x%08x 0X%08x 0x%08x\n",
aes_key[0], aes_key[1], aes_key[2], aes_key[3], aes_key[4], aes_key[5]);
break;
case kIOPFMI_AES_KEY_TYPE_USER256:
fmi->aes_cxt.keying = AES_KEY_TYPE_USER | AES_KEY_SIZE_256;
fmi->aes_cxt.key = aes_key;
WMR_PRINT(CRYPTO, "Using HW key IV: 0x%08x 0x%08x 0x%08x 0x%08x 0X%08x 0x%08x 0X%08x 0x%08x\n",
aes_key[0], aes_key[1], aes_key[2], aes_key[3], aes_key[4], aes_key[5], aes_key[6], aes_key[7]);
break;
case kIOPFMI_AES_KEY_TYPE_UID0:
fmi->aes_cxt.keying = AES_KEY_TYPE_UID0;
fmi->aes_cxt.key = NULL;
WMR_PRINT(CRYPTO, "Using HW UID0\n");
break;
case kIOPFMI_AES_KEY_TYPE_GID0:
fmi->aes_cxt.keying = AES_KEY_TYPE_GID0;
fmi->aes_cxt.key = NULL;
WMR_PRINT(CRYPTO, "Using HW GID0\n");
break;
case kIOPFMI_AES_KEY_TYPE_GID1:
fmi->aes_cxt.keying = AES_KEY_TYPE_GID1;
fmi->aes_cxt.key = NULL;
WMR_PRINT(CRYPTO, "Using HW GID1\n");
break;
default:
ASSERT(false);
return false;
break;
}
fmi->aes_cxt.chunk_size = aes_chain_size;
}
else
{
fmi->aes_cxt.keying = (AES_KEY_TYPE_USER | AES_KEY_SIZE_128);
fmi->aes_cxt.key = nand_whitening_key;
fmi->current_aes_iv_array = NULL;
fmi->aes_cxt.chunk_size = fmi->logical_page_size;
WMR_PRINT(CRYPTO, "Using constant AES for whitening\n");
}
fmi->aes_cxt.chunk_size = aes_chain_size;
fmi->aes_cxt.iv_func = h2fmi_aes_iv;
fmi->aes_cxt.iv_func_arg = fmi;
fmi->iv_seed_array = seeds;
fmi->current_aes_cxt = &fmi->aes_cxt;
}
else
{
//WMR_PRINT(CRYPTO, "Disabling AES\n");
fmi->current_aes_cxt = NULL;
fmi->current_aes_iv_array = NULL;
}
return true;
}
void h2fmi_restoreFmiRegs(h2fmi_t *fmi)
{
// Restore any register state that may have been lost
// due to power gating
#if FMI_VERSION > 2
restoreTimingRegs(fmi);
turn_on_fmc(fmi);
#endif
#if FMISS_ENABLED
if (!fmi->is_ppn)
{
fmiss_raw_init_sequences(fmi);
}
else
{
fmiss_init_sequences(fmi);
}
#endif
}
// =============================================================================
// implementation function declarations
// =============================================================================
void h2fmi_iop_set_config(h2fmi_t* fmi, IOPFMI_SetConfig* cmd)
{
// record which interface is being used
dprintf(DEBUG_SPEW, "configuring FMI%d\n", cmd->fmi);
fmi->regs = (cmd->fmi == 0) ? FMI0 : FMI1;
fmi->bus_id = cmd->fmi;
// record necessary device geometry information
fmi->bytes_per_spare = cmd->bytes_per_spare;
fmi->num_of_ce = cmd->num_of_ce;
fmi->pages_per_block = cmd->pages_per_block;
fmi->blocks_per_ce = cmd->blocks_per_ce;
fmi->total_bytes_per_meta = cmd->total_bytes_per_meta;
fmi->valid_bytes_per_meta = cmd->valid_bytes_per_meta;
fmi->bytes_per_page = cmd->bytes_per_page;
fmi->sectors_per_page = fmi->bytes_per_page / H2FMI_BYTES_PER_SECTOR;
fmi->valid_ces = cmd->valid_ces;
fmi->is_ppn = cmd->ppn;
fmi->logical_page_size = cmd->logical_page_size;
fmi->clock_speed_khz = cmd->clock_speed_khz;
// TODO: decide on appropriate infrastructure in support of
// vendor-specific superblock formats and command sequences
fmi->banks_per_ce = 1;
// calculate and store bus interface timing control
WMR_PRINT(INF, "cmd->read_sample_cycles : %d\n", cmd->read_sample_cycles);
WMR_PRINT(INF, "cmd->read_setup_cycles : %d\n", cmd->read_setup_cycles);
WMR_PRINT(INF, "cmd->read_hold_cycles : %d\n", cmd->read_hold_cycles);
WMR_PRINT(INF, "cmd->write_setup_cycles : %d\n", cmd->write_setup_cycles);
WMR_PRINT(INF, "cmd->write_hold_cycles : %d\n", cmd->write_hold_cycles);
fmi->if_ctrl = (FMC_IF_CTRL__DCCYCLE(cmd->read_sample_cycles) |
FMC_IF_CTRL__REB_SETUP(cmd->read_setup_cycles) |
FMC_IF_CTRL__REB_HOLD(cmd->read_hold_cycles) |
FMC_IF_CTRL__WEB_SETUP(cmd->write_setup_cycles) |
FMC_IF_CTRL__WEB_HOLD(cmd->write_hold_cycles));
WMR_PRINT(INF, "cmd->retire_on_invalid_refresh : %d\n", cmd->retire_on_invalid_refresh);
fmi->retire_on_invalid_refresh = cmd->retire_on_invalid_refresh;
#if SUPPORT_TOGGLE_NAND
fmi->is_toggle_system = cmd->toggle_system;
fmi->is_toggle = cmd->toggle;
if (fmi->is_toggle)
{
WMR_PRINT(INF, "cmd->ce_hold_cycles : %d\n", cmd->ce_hold_cycles);
WMR_PRINT(INF, "cmd->ce_setup_cycles : %d\n", cmd->ce_setup_cycles);
WMR_PRINT(INF, "cmd->adl_cycles : %d\n", cmd->adl_cycles);
WMR_PRINT(INF, "cmd->whr_cycles : %d\n", cmd->whr_cycles);
WMR_PRINT(INF, "cmd->read_pre_cycles : %d\n", cmd->read_pre_cycles);
WMR_PRINT(INF, "cmd->read_post_cycles : %d\n", cmd->read_post_cycles);
WMR_PRINT(INF, "cmd->write_pre_cycles : %d\n", cmd->write_pre_cycles);
WMR_PRINT(INF, "cmd->write_post_cycles : %d\n", cmd->write_post_cycles);
WMR_PRINT(INF, "cmd->enable_diff_DQS : %d\n", cmd->enable_diff_DQS);
WMR_PRINT(INF, "cmd->enable_diff_RE : %d\n", cmd->enable_diff_RE);
WMR_PRINT(INF, "cmd->enable_VREF : %d\n", cmd->enable_VREF);
fmi->dqs_ctrl = cmd->reg_dqs_delay;
fmi->timing_ctrl_1 = (FMC_TOGGLE_CTRL_1_DDR_RD_PRE_TIME(cmd->read_pre_cycles) |
FMC_TOGGLE_CTRL_1_DDR_RD_POST_TIME(cmd->read_post_cycles) |
FMC_TOGGLE_CTRL_1_DDR_WR_PRE_TIME(cmd->write_pre_cycles) |
FMC_TOGGLE_CTRL_1_DDR_WR_POST_TIME(cmd->write_post_cycles));
fmi->timing_ctrl_2 = (FMC_TOGGLE_CTRL_2_CE_SETUP_TIME(cmd->ce_setup_cycles) |
FMC_TOGGLE_CTRL_2_CE_HOLD_TIME(cmd->ce_hold_cycles) |
FMC_TOGGLE_CTRL_2_NAND_TIMING_ADL(cmd->adl_cycles) |
FMC_TOGGLE_CTRL_2_NAND_TIMING_WHR(cmd->whr_cycles));
fmi->toggle_if_ctrl = (FMC_IF_CTRL__DCCYCLE(0) |
FMC_IF_CTRL__REB_SETUP(cmd->dqs_half_cycles) |
FMC_IF_CTRL__REB_HOLD(0) |
FMC_IF_CTRL__WEB_SETUP(cmd->write_setup_cycles) |
FMC_IF_CTRL__WEB_HOLD(cmd->write_hold_cycles));
fmi->useDiffDQS = cmd->enable_diff_DQS;
fmi->useDiffRE = cmd->enable_diff_RE;
fmi->useVREF = cmd->enable_VREF;
}
#endif
h2fmi_restoreFmiRegs(fmi);
// always report config information
WMR_PRINT(INF, "fmi->regs : %p\n", fmi->regs);
WMR_PRINT(INF, "fmi->valid_ces : %p\n", fmi->valid_ces);
WMR_PRINT(INF, "fmi->num_of_ce : %p\n", fmi->num_of_ce);
WMR_PRINT(INF, "fmi->banks_per_ce : %p\n", fmi->banks_per_ce);
WMR_PRINT(INF, "fmi->pages_per_block : %p\n", fmi->pages_per_block);
WMR_PRINT(INF, "fmi->sectors_per_page : %p\n", fmi->sectors_per_page);
WMR_PRINT(INF, "fmi->bytes_per_spare : %p\n", fmi->bytes_per_spare);
WMR_PRINT(INF, "fmi->blocks_per_ce : %p\n", fmi->blocks_per_ce);
WMR_PRINT(INF, "fmi->valid_bytes_per_meta : %p\n", fmi->valid_bytes_per_meta);
WMR_PRINT(INF, "fmi->total_bytes_per_meta : %p\n", fmi->total_bytes_per_meta);
WMR_PRINT(INF, "fmi->if_ctrl : %p\n", fmi->if_ctrl);
#if SUPPORT_TOGGLE_NAND
if (fmi->is_toggle)
{
WMR_PRINT(INF, "fmi->dqs_ctrl : %p\n", fmi->dqs_ctrl);
WMR_PRINT(INF, "fmi->timing_ctrl_1 : %p\n", fmi->timing_ctrl_1);
WMR_PRINT(INF, "fmi->timing_ctrl_2 : %p\n", fmi->timing_ctrl_2);
WMR_PRINT(INF, "fmi->toggle_if_ctrl : %p\n", fmi->toggle_if_ctrl);
WMR_PRINT(INF, "fmi->is_toggle : %p\n", fmi->is_toggle);
}
#endif
WMR_PRINT(INF, "fmi->bytes_per_page : %p\n", fmi->bytes_per_page);
WMR_PRINT(INF, "fmi->is_ppn : %p\n", fmi->is_ppn);
// these steps are only taken when fmi is first configured
if (!fmi->initialized)
{
// Make certain the FMC is enabled upon init.
turn_on_fmc(fmi);
// Perform system-specific initialization.
h2fmi_init_sys(fmi);
// indicate that fmi is initialized
fmi->initialized = TRUE32;
fmi->stateMachine.currentMode = fmiNone;
fmi->wMaxOutstandingCEWriteOperations = cmd->wMaxOutstandingCEWriteOperations;
}
fmi->activeCe = ~0;
if (fmi->is_ppn && (!fmi->is_ppn_channel_init))
{
#if SUPPORT_PPN
if (!h2fmi_ppn_init_channel(fmi,
cmd->ppn_debug_flags_valid,
cmd->ppn_debug_flags,
cmd->ppn_vs_debug_flags_valid,
cmd->ppn_vs_debug_flags,
cmd->ppn_allow_saving_debug_data,
cmd->ppn_version))
{
WMR_PANIC("[FIL:ERR] Unable to initialize PPN device\n");
cmd->iopfmi.status = kIOPFMI_STATUS_FAILURE;
return;
}
fmi->is_ppn_channel_init = TRUE32;
#else
WMR_PANIC("!SUPPORT_PPN");
#endif
}
// Cache page config registers
if (fmi->is_ppn)
{
#if SUPPORT_PPN
fmi->fmi_config_reg = h2fmi_ppn_calculate_fmi_config(fmi);
#if FMI_VERSION > 0
fmi->fmi_data_size_reg = h2fmi_ppn_calculate_fmi_data_size(fmi);
#endif /* FMI_VERSION > 0*/
#else //SUPPORT_PPN
WMR_PANIC("!SUPPORT_PPN");
#endif //SUPPORT_PPN
}
else
{
// Cache ECC configuration
fmi->correctable_bits = h2fmi_calculate_ecc_bits(fmi);
fmi->fmi_config_reg = h2fmi_calculate_fmi_config(fmi);
#if FMI_VERSION > 0
fmi->fmi_data_size_reg = h2fmi_calculate_fmi_data_size(fmi);
#endif /* FMI_VERSION > 0*/
}
// record success status in command structure
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
}
void h2fmi_iop_read_chip_ids(h2fmi_t* fmi, IOPFMI_ReadChipIDs* cmd)
{
IOPFMI_chipid_t* ids_buf = (IOPFMI_chipid_t*)mem_static_map_cached(cmd->chip_id_buffer);
IOPFMI_status_t status = kIOPFMI_STATUS_SUCCESS;
UInt32 valid_ces;
UInt32 ce;
WMR_ASSERT(ids_buf != MAP_FAILED);
valid_ces = fmi->valid_ces;
fmi->valid_ces = 0xffff;
for (ce = 0; ce < H2FMI_MAX_CE_TOTAL; ce++)
{
h2fmi_chipid_t* chipid = (h2fmi_chipid_t*)&ids_buf[ce];
UInt8 *ptr = (UInt8 *)&ids_buf[ce];
if (!h2fmi_nand_read_id(fmi, ce, chipid, CHIPID_ADDR))
{
status = kIOPFMI_STATUS_FAILED_READ_ID;
break;
}
WMR_PRINT(INF, "Read Chip ID %02X %02X %02X %02X %02X %02X to %p\n",
ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], chipid);
}
fmi->valid_ces = valid_ces;
cmd->iopfmi.status = status;
WMR_PREPARE_WRITE_BUFFER(ids_buf, H2FMI_MAX_CE_TOTAL * sizeof(IOPFMI_chipid_t));
}
void h2fmi_iop_reset_everything(h2fmi_t* fmi, IOPFMI_ResetEverything* cmd)
{
IOPFMI_status_t status = kIOPFMI_STATUS_SUCCESS;
BOOL32 ret = TRUE32;
h2fmi_reset(fmi);
h2fmi_init_sys(fmi);
ret = h2fmi_nand_reset_all(fmi);
if (!ret)
{
status = kIOPFMI_STATUS_FAILED_RESET_ALL;
}
}
void h2fmi_iop_erase_multiple(h2fmi_t* fmi, IOPFMI_EraseMultiple* cmd)
{
BOOL32 erase_failed = FALSE32;
WMR_PRINT(ERASE, "IOP received Multi-Erase command %d elements\n", cmd->number_of_elements);
if (!h2fmi_erase_blocks(fmi,
cmd->number_of_elements,
cmd->ce,
cmd->block_number,
&erase_failed))
{
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_ERASE_BLOCK;
}
else if (erase_failed)
{
cmd->iopfmi.status = kIOPFMI_STATUS_DEVICE_ERROR;
}
else
{
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
}
/**
* Copy erase status back out ...
*/
cmd->failure_details.wNumCE_Executed = fmi->failureDetails.wNumCE_Executed;
cmd->failure_details.wFirstFailingCE = fmi->failureDetails.wFirstFailingCE;
}
void h2fmi_iop_erase_single(h2fmi_t* fmi, IOPFMI_EraseSingle* cmd)
{
BOOL32 erase_failed = FALSE32;
WMR_PRINT(ERASE, "IOP received Erase command CE %d Block %d\n", cmd->ce, cmd->block_number);
fmi->failureDetails.wCEStatusArray = &fmi->failureDetails.wSingleCEStatus;
fmi->failureDetails.wCEStatusArrayModulusMask = 0;
if (!h2fmi_erase_blocks(fmi,
1,
&cmd->ce,
&cmd->block_number,
&erase_failed))
{
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_ERASE_BLOCK;
}
else if (erase_failed)
{
cmd->iopfmi.status = kIOPFMI_STATUS_DEVICE_ERROR;
}
else
{
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
}
/**
* Copy erase status back out ...
*/
cmd->failure_details.wNumCE_Executed = fmi->failureDetails.wNumCE_Executed;
cmd->failure_details.wSingleCEStatus = fmi->failureDetails.wSingleCEStatus;
}
void h2fmi_iop_read_single(h2fmi_t* fmi, IOPFMI_IOSingle* cmd)
{
BOOL32 gather_corrections = (0 != cmd->correction_address);
UInt8* correct_buf = (gather_corrections
? (UInt8*)mem_static_map_cached(cmd->correction_address)
: NULL);
UInt8* data_buf = (UInt8*)cmd->data_address;
UInt8* meta_buf = (UInt8*)cmd->meta_address;
hfmi_aes_iv* aes_iv_array = NULL;
WMR_ASSERT(correct_buf != MAP_FAILED);
WMR_PRINT(READ, "IOP received Read Single command CE %d Block %d\n", cmd->ce, cmd->page_number);
if (cmd->aes_iv_array)
{
aes_iv_array = (hfmi_aes_iv*)mem_static_map_cached(cmd->aes_iv_array);
WMR_ASSERT(aes_iv_array != MAP_FAILED);
WMR_PREPARE_READ_BUFFER(aes_iv_array, cmd->aes_num_chains * sizeof(hfmi_aes_iv));
}
h2fmi_setup_aes(fmi, &cmd->iopfmi, aes_iv_array, cmd->aes_chain_size, cmd->aes_key_type, (UInt32*)cmd->aes_key_bytes, &cmd->page_number);
cmd->iopfmi.status = h2fmi_read_page(fmi,
cmd->ce,
cmd->page_number,
data_buf,
meta_buf,
correct_buf,
NULL);
// clean corrections buffer, if not NULL
if (gather_corrections)
{
WMR_PREPARE_WRITE_BUFFER(correct_buf,
fmi->sectors_per_page * sizeof(IOPFMI_correction_t));
}
}
void h2fmi_iop_read_multiple(h2fmi_t* fmi, IOPFMI_IOMultiple* cmd)
{
const UInt32 page_count = cmd->page_count;
BOOL32 gather_corrections = (0 != cmd->corrections_array);
UInt8* corrections_array = (gather_corrections
? (UInt8*)mem_static_map_cached(cmd->corrections_array)
: NULL);
h2fmi_ce_t* chip_enable_array = (h2fmi_ce_t*)mem_static_map_cached(cmd->chip_enable_array);
UInt32* page_number_array = (UInt32*)mem_static_map_cached(cmd->page_number_array);
hfmi_aes_iv* aes_iv_array = NULL;
WMR_ASSERT(corrections_array != MAP_FAILED);
WMR_ASSERT(chip_enable_array != MAP_FAILED);
WMR_ASSERT(page_number_array != MAP_FAILED);
if (cmd->aes_iv_array)
{
aes_iv_array = (hfmi_aes_iv*)mem_static_map_cached(cmd->aes_iv_array);
WMR_ASSERT(aes_iv_array != MAP_FAILED);
WMR_PREPARE_READ_BUFFER(aes_iv_array, cmd->aes_num_chains * sizeof(hfmi_aes_iv));
}
// Invalidate control arrays, ensuring that these values are
// re-read from physical memory.
WMR_PREPARE_READ_BUFFER(chip_enable_array, page_count * sizeof(UInt32));
WMR_PREPARE_READ_BUFFER(page_number_array, page_count * sizeof(UInt32));
UInt32* data_segments_array = (UInt32*)mem_static_map_cached(cmd->data_segments_array);
UInt32* meta_segments_array = (UInt32*)mem_static_map_cached(cmd->meta_segments_array);
WMR_ASSERT(data_segments_array != MAP_FAILED);
WMR_ASSERT(meta_segments_array != MAP_FAILED);
WMR_PREPARE_READ_BUFFER(data_segments_array, cmd->data_segment_array_length_in_bytes);
WMR_PREPARE_READ_BUFFER(meta_segments_array, cmd->meta_segment_array_length_in_bytes);
h2fmi_setup_aes(fmi, &cmd->iopfmi, aes_iv_array, cmd->aes_chain_size, cmd->aes_key_type, (UInt32*)cmd->aes_key_bytes, page_number_array);
WMR_PRINT(READ, "IOP received Read Multiple command NumPages %d\n", page_count);
// perform multi-read
cmd->iopfmi.status = h2fmi_read_multi(fmi,
page_count,
chip_enable_array,
page_number_array,
(void*)data_segments_array,
(void*)meta_segments_array,
corrections_array,
NULL);
// clean corrections buffer, if not NULL
if (gather_corrections)
{
WMR_PREPARE_WRITE_BUFFER(corrections_array,
page_count * fmi->sectors_per_page * sizeof(IOPFMI_correction_t));
}
/**
* Copy failure data.
*/
cmd->failure_details.wNumCE_Executed = fmi->failureDetails.wNumCE_Executed;
cmd->failure_details.wOverallOperationFailure = fmi->failureDetails.wOverallOperationFailure;
cmd->failure_details.wFirstFailingCE = fmi->failureDetails.wFirstFailingCE;
#if (defined (WMR_DEBUG) && WMR_DEBUG)
if ( (kIOPFMI_STATUS_SUCCESS != cmd->iopfmi.status) &&
(kIOPFMI_STATUS_BLANK != cmd->iopfmi.status) &&
(kIOPFMI_STATUS_FUSED != cmd->iopfmi.status))
{
unsigned k = fmi->failureDetails.wCEStatusArrayModulusMask;
WMR_PRINT(ERROR, "IOP --> err 0x%08x, %d completed on %d\n",cmd->iopfmi.status, fmi->failureDetails.wNumCE_Executed,fmi->bus_id);
while ( k>0 )
{
WMR_PRINT(ERROR, "Status[%d] = 0x%x\n",k,fmi->failureDetails.wCEStatusArray[k]);
k--;
}
}
#endif
}
void h2fmi_iop_write_single(h2fmi_t* fmi, IOPFMI_IOSingle* cmd)
{
UInt8* data_buf = (UInt8*)cmd->data_address;
UInt8* meta_buf = (UInt8*)cmd->meta_address;
BOOL32 write_failed = FALSE32;
hfmi_aes_iv* aes_iv_array = NULL;
if (cmd->aes_iv_array)
{
aes_iv_array = (hfmi_aes_iv*)mem_static_map_cached(cmd->aes_iv_array);
WMR_ASSERT(aes_iv_array != MAP_FAILED);
WMR_PREPARE_READ_BUFFER(aes_iv_array, cmd->aes_num_chains * sizeof(hfmi_aes_iv));
}
WMR_PRINT(WRITE, "IOP received Write Single command CE %d Block %d\n", cmd->ce, cmd->page_number);
h2fmi_setup_aes(fmi, &cmd->iopfmi, aes_iv_array, cmd->aes_chain_size, cmd->aes_key_type, (UInt32*)cmd->aes_key_bytes, &cmd->page_number);
if (!h2fmi_write_page(fmi,
cmd->ce,
cmd->page_number,
data_buf,
meta_buf,
&write_failed))
{
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_WRITE_PAGE;
}
else if (write_failed)
{
cmd->iopfmi.status = kIOPFMI_STATUS_DEVICE_ERROR;
}
else
{
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
}
}
void h2fmi_iop_write_multiple(h2fmi_t* fmi, IOPFMI_IOMultiple* cmd)
{
const UInt32 page_count = cmd->page_count;
h2fmi_ce_t* chip_enable_array = (h2fmi_ce_t*)mem_static_map_cached(cmd->chip_enable_array);
UInt32* page_number_array = (UInt32*)mem_static_map_cached(cmd->page_number_array);
BOOL32 write_failed = FALSE32;
hfmi_aes_iv* aes_iv_array = NULL;
WMR_ASSERT(chip_enable_array != MAP_FAILED);
WMR_ASSERT(page_number_array != MAP_FAILED);
if (cmd->aes_iv_array)
{
aes_iv_array = (hfmi_aes_iv*)mem_static_map_cached(cmd->aes_iv_array);
WMR_ASSERT(aes_iv_array != MAP_FAILED);
WMR_PREPARE_READ_BUFFER(aes_iv_array, cmd->aes_num_chains * sizeof(hfmi_aes_iv));
}
// Invalidate control arrays, ensuring that these values are
// re-read from physical memory.
WMR_PREPARE_READ_BUFFER(chip_enable_array, page_count * sizeof(UInt32));
WMR_PREPARE_READ_BUFFER(page_number_array, page_count * sizeof(UInt32));
UInt32* data_segments_array = (UInt32*)mem_static_map_cached(cmd->data_segments_array);
UInt32* meta_segments_array = (UInt32*)mem_static_map_cached(cmd->meta_segments_array);
WMR_ASSERT(data_segments_array != MAP_FAILED);
WMR_ASSERT(meta_segments_array != MAP_FAILED);
WMR_PREPARE_READ_BUFFER(data_segments_array, cmd->data_segment_array_length_in_bytes);
WMR_PREPARE_READ_BUFFER(meta_segments_array, cmd->meta_segment_array_length_in_bytes);
WMR_PRINT(WRITE, "IOP received Write Multiple command NumPages %d\n", page_count);
h2fmi_setup_aes(fmi, &cmd->iopfmi, aes_iv_array, cmd->aes_chain_size, cmd->aes_key_type, (UInt32*)cmd->aes_key_bytes, page_number_array);
// perform multi-write
if (!h2fmi_write_multi(fmi,
page_count,
chip_enable_array,
page_number_array,
(void*)data_segments_array,
(void*)meta_segments_array,
&write_failed,
cmd->vendorProtocol))
{
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_WRITE_MULTI;
}
else if (write_failed)
{
cmd->iopfmi.status = kIOPFMI_STATUS_DEVICE_ERROR;
}
else
{
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
}
/**
* Copy failure data.
*/
cmd->failure_details.wNumCE_Executed = fmi->failureDetails.wNumCE_Executed;
cmd->failure_details.wFirstFailingCE = fmi->failureDetails.wFirstFailingCE;
}
void h2fmi_iop_read_raw(h2fmi_t* fmi, IOPFMI_IORaw* cmd)
{
UInt8* page_buf = (UInt8*)(cmd->buffer_address);
UInt8* data_buf = page_buf + 0;
UInt8* spare_buf = page_buf + fmi->bytes_per_page;
fmi->current_aes_cxt = NULL;
WMR_PRINT(READ, "IOP received Read Raw command CE %d Page %d\n", cmd->ce, cmd->page_number);
if (!h2fmi_read_raw_page(fmi,
cmd->ce,
cmd->page_number,
data_buf,
spare_buf))
{
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_READ_RAW;
}
else
{
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
}
}
void h2fmi_iop_write_raw(h2fmi_t* fmi, IOPFMI_IORaw* cmd)
{
const UInt32 data_bytes_per_page = (fmi->sectors_per_page * H2FMI_BYTES_PER_SECTOR);
const UInt32 bytes_per_page = data_bytes_per_page + fmi->bytes_per_spare;
UInt8* page_buf = (UInt8*)cmd->buffer_address;
UInt8* data_buf = page_buf;
// deal with cache coherency issues
const UInt32 data_clean_size = bytes_per_page;
fmi->current_aes_cxt = NULL;
WMR_PRINT(WRITE, "IOP sending write raw ce %d page %d data_buf %08x size %d\n",
cmd->ce,
cmd->page_number,
data_buf,
bytes_per_page);
// Invalidate data and meta buffers, ensuring their
// contents are re-read from physical memory
WMR_PREPARE_READ_BUFFER(data_buf, data_clean_size);
cmd->iopfmi.status = h2fmi_write_raw_page(fmi,
cmd->ce,
cmd->page_number,
data_buf);
}
void h2fmi_iop_read_bootpage(h2fmi_t* fmi, IOPFMI_IOBootPage* cmd)
{
BOOL32 gather_corrections = (0 != cmd->corrections_array);
UInt8* corrections_array = (gather_corrections
? (UInt8*)mem_static_map_cached(cmd->corrections_array)
: NULL);
UInt8* page_buf = (UInt8*)cmd->buffer_address;
WMR_ASSERT(corrections_array != MAP_FAILED);
fmi->current_aes_cxt = NULL;
WMR_PRINT(READ, "IOP received Read Boot Page command CE %d Page %d\n", cmd->ce, cmd->page_number);
cmd->iopfmi.status = h2fmi_read_bootpage(fmi,
cmd->ce,
cmd->page_number,
page_buf,
corrections_array);
// clean corrections buffer, if not NULL
if (gather_corrections)
{
WMR_PREPARE_WRITE_BUFFER(corrections_array,
fmi->sectors_per_page * sizeof(IOPFMI_correction_t));
}
}
void h2fmi_iop_write_bootpage(h2fmi_t* fmi, IOPFMI_IOBootPage* cmd)
{
UInt8* page_buf = (UInt8*)mem_static_map_cached(cmd->buffer_address);
WMR_ASSERT(page_buf != MAP_FAILED);
WMR_PREPARE_READ_BUFFER(page_buf, H2FMI_BOOT_BYTES_PER_PAGE);
fmi->current_aes_cxt = NULL;
WMR_PRINT(WRITE, "IOP received Write Boot Page command CE %d Page %d\n", cmd->ce, cmd->page_number);
cmd->iopfmi.status = h2fmi_write_bootpage(fmi,
cmd->ce,
cmd->page_number,
page_buf);
}
void h2fmi_iop_sleep(h2fmi_t* fmi)
{
dprintf(DEBUG_CRITICAL, "sleeping FMI%d\n", h2fmi_bus_index(fmi));
}
void h2fmi_iop_wake(h2fmi_t* fmi)
{
dprintf(DEBUG_CRITICAL, "waking FMI%d\n", h2fmi_bus_index(fmi));
}
void dump_fmi_state(h2fmi_t *pFMI, uint32_t index, BOOL32 withHWRegs, BOOL32 withECC)
{
/**
* Dump 'important' parts of state machine ...
*/
printf("FMI[%u] - stateMachine{mode:%d, state: %d, CEsav: %d, page_idx: %d, page_count: %d, currentCE: %d, currentPage: 0x%x, fSuccessful: %d, lastCE: %d, needsPrepare: %d, cleanPages: %d, uecc_pages: %d, cmdStartTick: 0x%llx, wVendorType: %d}\n",
index,
(int)pFMI->stateMachine.currentMode,
(int)pFMI->stateMachine.state.rd,
pFMI->stateMachine.savedCurrentCE,
pFMI->stateMachine.page_idx,
pFMI->stateMachine.page_count,
pFMI->stateMachine.currentCE,
pFMI->stateMachine.currentPage,
(int)pFMI->stateMachine.fSuccessful,
pFMI->stateMachine.lastCE,
pFMI->stateMachine.needs_prepare,
pFMI->stateMachine.clean_pages,
pFMI->stateMachine.uecc_pages,
pFMI->stateMachine.wCmdStartTicks,
pFMI->stateMachine.wVendorType);
/**
* Dump failure details.
*/
printf("FMI[%u] - failureDetails{wNumCEexec:%d, hldoff: %d, outstd: %d/%d, overall: 0x%x, firstFailingCE: 0x%x, wSingleCEStatus: 0x%x}\n",
index,
pFMI->failureDetails.wNumCE_Executed,
pFMI->stateMachine.wNumTimesHoldoffExecuted,
pFMI->stateMachine.wOutstandingCEWriteOperations,
pFMI->stateMachine.wMaxOutstandingCEWriteOperations,
pFMI->failureDetails.wOverallOperationFailure,
pFMI->failureDetails.wFirstFailingCE,
pFMI->failureDetails.wSingleCEStatus
);
if (withHWRegs)
{
/**
* Dump important FMI registers ...
*/
#if FMI_VERSION <=2
printf("FMI[%u] - fmi{bus: %d, CFG: 0x%x, CTRL: 0x%x, STAT: 0x%x, INTPND: 0x%x, INT_EN: 0x%x, DBG0: 0x%x, DBG1: 0x%x, DBG2: 0x%x}\n",
index,
pFMI->bus_id,
h2fmi_rd(pFMI,FMI_CONFIG),
h2fmi_rd(pFMI,FMI_CONTROL),
h2fmi_rd(pFMI,FMI_STATUS),
h2fmi_rd(pFMI,FMI_INT_PEND),
h2fmi_rd(pFMI,FMI_INT_EN),
h2fmi_rd(pFMI,FMI_DEBUG0),
h2fmi_rd(pFMI,FMI_DEBUG1),
h2fmi_rd(pFMI,FMI_DEBUG2)
);
#else
printf("FMI[%u] - fmi{bus: %d, CFG: 0x%x, CTRL: 0x%x, STAT: 0x%x, INTPND: 0x%x, INT_EN: 0x%x, DBG0: 0x%x, DATSIZ: 0x%x}\n",
index,
pFMI->bus_id,
h2fmi_rd(pFMI,FMI_CONFIG),
h2fmi_rd(pFMI,FMI_CONTROL),
h2fmi_rd(pFMI,FMI_STATUS),
h2fmi_rd(pFMI,FMI_INT_PEND),
h2fmi_rd(pFMI,FMI_INT_EN),
h2fmi_rd(pFMI,FMI_DEBUG0),
h2fmi_rd(pFMI,FMI_DATA_SIZE)
);
#endif
#if ((FMI_VERSION > 0) && (FMI_VERSION <= 2))
printf("FMI[%u] - fmi{DBG3: 0x%x, DATSIZ: 0x%x}\n",index,h2fmi_rd(pFMI,FMI_DEBUG3),h2fmi_rd(pFMI,FMI_DATA_SIZE));
#endif
/**
* Dump important FMC registers ...
*/
printf("FMI[%u] - fmc{ON: 0x%x, IFCTRL: 0x%x, CECTRL: 0x%x, RWCTRL: 0x%x, CMD: 0x%x, ADDR0: 0x%x, ADDR1: 0x%x, ANUM: 0x%x, DNUM: 0x%x, FSTATUS: 0x%x, NSTATUS: 0x%x, RBB_CFG: 0x%x}\n",
index,
h2fmi_rd(pFMI,FMC_ON),
h2fmi_rd(pFMI,FMC_IF_CTRL),
h2fmi_rd(pFMI,FMC_CE_CTRL),
h2fmi_rd(pFMI,FMC_RW_CTRL),
h2fmi_rd(pFMI,FMC_CMD),
h2fmi_rd(pFMI,FMC_ADDR0),
h2fmi_rd(pFMI,FMC_ADDR1),
h2fmi_rd(pFMI,FMC_ADDRNUM),
h2fmi_rd(pFMI,FMC_DATANUM),
h2fmi_rd(pFMI,FMC_STATUS),
h2fmi_rd(pFMI,FMC_NAND_STATUS),
h2fmi_rd(pFMI,FMC_RBB_CONFIG)
);
#if FMI_VERSION > 2
printf("FMI[%u] - fmc{DQST: 0x%x, NANDT1: 0x%x, NANDT2: 0x%x, NANDT3: 0x%x}\n",
index,
h2fmi_rd(pFMI,FMC_DQS_TIMING_CTRL),
h2fmi_rd(pFMI,FMC_TOGGLE_CTRL_1),
h2fmi_rd(pFMI,FMC_TOGGLE_CTRL_2),
h2fmi_rd(pFMI,FMC_TOGGLE_CTRL_3)
);
#endif // FMI_VERSION > 2
// ECC might be clocked off for PPN
if (withECC)
{
/**
* Dump important ECC registers ...
*/
printf("FMI[%u] - ecc{PND: 0x%x, MASK: 0x%x}\n",
index,
h2fmi_rd(pFMI,ECC_PND),
h2fmi_rd(pFMI,ECC_MASK)
);
}
else
{
printf("FMI[%u} - omitting ecc registers for ppn device\n", index);
}
#if FMISS_DUMP_ENABLED
// FMISS
if (0 != (FMI_CONTROL__SEQUENCER_TIMEOUT_ENABLE & h2fmi_rd(pFMI, FMI_CONTROL)))
{
fmiss_dump(pFMI);
}
#endif // FMISS_DUMP_ENABLED
}
}