iBoot/drivers/apple/asp/debug.c

4208 lines
135 KiB
C

/*
* Copyright (c) 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.
*/
#define ASP_TARGET_RTXC 1
#define CASSERT(x, name) typedef char __ASP_CASSERT_##name[(x) ? 1 : -1]
#include <debug.h>
#include <sys/menu.h>
#include <lib/env.h>
#include <platform/memmap.h>
#include <platform/soc/hwclocks.h>
#include <stdint.h>
#include <assert.h>
#include <csi_ipc_protocol.h>
#include <arch.h>
#include <aspcore_mbox.h>
#include <aspcore_protocol.h>
#include <aspcore_nand_debug.h>
#include <debug.h>
#include <drivers/asp.h>
#include <drivers/csi.h>
#include <lib/env.h>
#include <lib/heap.h>
#include <lib/partition.h>
#include <libDER/DER_Keys.h>
#include <libDER/DER_Decode.h>
#include <libDER/asn1Types.h>
#include <platform.h>
#include <stdio.h>
#include <sys.h>
#include <platform/timer.h> // ticks
#include "common_util.h"
#include <drivers/usb/usb_debug.h>
#if DEBUG_BUILD && WITH_MENU
#define METADATA_PER_LBA (16)
#define LBA_TOKEN_SIZE (4)
#define TOKEN_TERMINATOR (0xFFFFFFFF)
#define LBA_TOKEN_BASE (0x0FF00000)
#define LBA_TOKEN_BLOG (LBA_TOKEN_BASE + 0x13)
#define BYTES_PER_SEC (4096)
#define BLOG_NUM_ENTRY (BYTES_PER_SEC / sizeof(BlogE_t))
#define MAX_RMA_CHUNK_PAGES (1024)
#define PERFECT_PAGE_SIZE (4112)
#define VENDOR_CODE__MICRON (0x2C)
#define VENDOR_CODE__HYNIX (0xAD)
#define VENDOR_CODE__SANDISK (0x45)
#define SIZE_OF_WFALL_TBL_ENTRY (4)
#define TEST_DATA_PATTERN (0xA5)
#define WATERMARK_PATTERN (0xFF)
#define FLOW_DEAD 12
#define FLOW_UTIL 13
extern aspproto_nand_geom_t nand_info;
extern asp_t asp;
extern struct blockdev *asp_nand_dev;
typedef enum {
NOB_TOKEN_UNMAPPED = 0,
NOB_TOKEN_W_UNC = 1,
NOB_TOKEN_W_UNC_LOG = 2,
NOB_TOKEN_GC_UNC = 3,
NOB_TOKEN_GC_BLANK = 4,
NOB_TOKEN_TRANSLATE = 5,
NUM_NOB_TOKENS = 6,
NOB_TOKEN_MAX = 7
} NOB_TOKEN_e;
#define NOB_BAND_BITS 12
#define NOB_BOFF_BITS (32 - NOB_BAND_BITS)
typedef union {
struct {
uint32_t boff : NOB_BOFF_BITS;
uint32_t band : NOB_BAND_BITS;
};
uint32_t all; // note that the all field is used to acclerate math on the boff field which is assumed to be the least significant bits without risk of roll over.
NOB_TOKEN_e token;
} nob_t;
typedef struct {
uint32_t lba;
uint32_t size;
} BlogE_t;
typedef enum {
BONFIRE_SLC_STATE_NONE = 0,
BONFIRE_SLC_STATE_A = 1,
BONFIRE_SLC_STATE_B = 2
} BONFIRE_SLC_STATE_e;
BONFIRE_SLC_STATE_e bonfire_slc_state = BONFIRE_SLC_STATE_NONE;
typedef enum {
ASP_USER_PARTITION,
ASP_INTERMEDIATE_PARTITION,
NUM_ASP_PARITIONS
} asp_partitions_e;
char partition_names[NUM_ASP_PARITIONS][30] = {
"USER PARTITION",
"INTERMEDIATE PARTITION"
};
#define ROUNDUPTO(num, gran) ((((num) + (gran) - 1) / (gran)) * (gran))
static void usage(void)
{
printf("usage:\n");
printf("asp sync\n");
#if defined(ASP_ENABLE_NEURALIZE) && ASP_ENABLE_NEURALIZE
printf("asp neuralize\n");
#endif
printf("asp readid\n");
printf("asp info\n");
printf("asp debug_counter_supported <channel>\n");
printf("asp get_debug_counter <channel>\n");
printf("asp reset_debug_counter <channel>\n");
printf("asp dies_in_parallel <total dies>\n");
printf("asp dies_in_parallel (for MLC device: <slc_write> <read> <erase> <mlc_write>) (for TLC device: <slc_write> <read> <erase> <tlc_write>)\n");
printf("asp test_scratchpad\n");
printf("asp bonfirereadband <band> <mode(%d for MLC, %d for SLC, %d for TLC)> <display stats (0/1)\n", CELL_TYPE_IS_MLC, CELL_TYPE_IS_SLC, CELL_TYPE_IS_TLC);
printf("asp readband <band> <mode(%d for MLC, %d for SLC, %d for TLC)> < no. of stripes to be issued during 1 read transaction> <Optional: time in seconds to repeatedly read. if 0, then read only once>\n", CELL_TYPE_IS_MLC, CELL_TYPE_IS_SLC, CELL_TYPE_IS_TLC);
printf("asp readrandom <mode(%d for MLC, %d for SLC, %d for TLC, %d for mixed)> <Num of reads (each read is 4k/8k/16k)> <no. of stripes to be issued during 1 read transaction> <num sectors_per_page> <if reads in a stripe should be on same_die (1 for same die, 2 for diff die, 3 for random)> <Optional: time in seconds to repeatedly read. if 0, then read only once>\n", CELL_TYPE_IS_MLC, CELL_TYPE_IS_SLC, CELL_TYPE_IS_TLC, CELL_TYPE_IS_MIXED);
printf("asp readmixedsuperpage <Num stripes> < no. of stripes to be issued during 1 read transaction> <Optional: time in seconds to repeatedly read. if 0, then read only once>\n");
printf("asp secperband <band>\n");
printf("asp v2p <vba>\n");
printf("asp getburnincode\n");
printf("asp cbp2r <cau> <blk> <page>\n");
printf("asp dbp2r <dip> <bork> <page>\n");
printf("asp r2cbp <row_address>\n");
printf("asp l2dbp <lba>\n");
printf("asp getlastfailure\n");
printf("asp set_photoflow_slc\n");
printf("asp set_photoflow_mlc\n");
printf("asp enable_bg\n");
printf("asp disable_bg\n");
printf("asp bbt\n");
printf("asp dm\n");
printf("asp dipinfo\n");
printf("asp read <bus> <ce> <row address> <sector_offset> <no. of %d byte sectors> <0/1/2 for bit flips or in-depth health monitoring> <mode(%d for MLC, %d for SLC, %d for TLC)>\n", ASP_NAND_BLKSZ, CELL_TYPE_IS_MLC, CELL_TYPE_IS_SLC, CELL_TYPE_IS_TLC);
printf("asp readpagemeta <bus> <ce> <row address> <no. of pages>\n");
printf("asp readbandmeta <band> <verbose (0/1)\n");
printf("asp bandstat\n");
printf("asp disableuid\n");
printf("asp dumpblog <band> <audit (0/1)> <verbose (0/1)>\n");
printf("asp vthsweep <channel> <cau> <block> <file_path>\n");
printf("asp rma_configure <channel> <mask>\n");
printf("asp rma_set <channel>\n");
printf("asp rma_get <channel> [file_path]\n");
printf("asp rma_delete <channel>\n");
printf("asp ppn_recover\n");
printf("asp ppn_get_calibration <file_path>\n");
printf("asp waterfall_size\n");
printf("asp waterfall\n");
printf("asp devparam\n");
printf("asp testbdevread\n");
printf("asp setTLCwritestripes <number of stripes that can be programmed together>\n");
printf("asp readverify <band>\n");
printf("asp istlc\n");
printf("asp getlinkclkfreq\n");
printf("asp resetperfticks\n");
printf("asp getperfticks\n");
printf("asp printslcbonfirebands\n");
#if ASP_ENABLE_WRITES
printf("asp setwritable\n");
printf("asp utilFormat\n");
printf("asp lbaFormat\n");
printf("asp register\n");
printf("asp bonfireeraseband <band>\n");
printf("asp bonfirewriteband <band> <mode(%d for MLC, %d for SLC, %d for TLC)> <Optional: 4 byte data pattern. otherwise random>\n", CELL_TYPE_IS_MLC, CELL_TYPE_IS_SLC, CELL_TYPE_IS_TLC);
printf("asp setburnincode <code>\n");
printf("asp eraseband <band> <mode(%d for MLC, %d for SLC, %d for TLC)>\n", CELL_TYPE_IS_MLC, CELL_TYPE_IS_SLC, CELL_TYPE_IS_TLC);
printf("asp writeband <band> <mode(%d for MLC, %d for SLC, %d for TLC)> <Optional: 4 byte data pattern. otherwise random>\n", CELL_TYPE_IS_MLC, CELL_TYPE_IS_SLC, CELL_TYPE_IS_TLC);
printf("asp eploop <band> <mode(%d for MLC, %d for SLC, %d for TLC) <time in seconds to repeatedly Erase+Prog. if 0, then only once> <Optional: 4 byte data pattern. otherwise random>", CELL_TYPE_IS_MLC, CELL_TYPE_IS_SLC, CELL_TYPE_IS_TLC);
printf("asp testwaterfall\n");
printf("asp testbdevwrite\n");
printf("asp groupaslc <seed>\n");
printf("asp groupbslc\n");
printf("asp ungroup\n");
#endif //ASP_ENABLE_WRITES
}
static int asp_nand_info(void)
{
printf("ce_per_bus = %d\n", nand_info.ce_per_bus);
printf("cau_per_die = %d\n", nand_info.cau_per_die);
printf("num_bus = %d\n", nand_info.num_bus);
printf("die_per_bus = %d\n", nand_info.die_per_bus);
printf("num_dip = %d\n", nand_info.num_dip);
printf("num_bands = %d\n", nand_info.num_bands);
printf("sec_per_page = %d\n", nand_info.sec_per_page);
printf("sec_per_full_band = %d\n", nand_info.sec_per_full_band);
printf("sec_per_full_band_slc = %d\n", nand_info.sec_per_full_band_slc);
printf("bytes_per_sec_meta = %d\n", nand_info.bytes_per_sec_meta);
printf("pages_per_block = %d\n", nand_info.pages_per_block);
printf("pages_per_block_slc = %d\n", nand_info.pages_per_block_slc);
printf("cell type = %d. (%d for MLC, %d for TLC)\n", nand_info.cell_type, CELL_TYPE_IS_MLC, CELL_TYPE_IS_TLC);
printf("lastUserBand = %d\n", asp.lastUserBand);
printf("lastBand = %d\n", asp.lastBand);
printf("numVirtualSLCBonfireBands = %d\n", asp.numVirtualSLCBonfireBands);
printf("firstIntermediateBand = %d\n", asp.firstIntermediateBand);
printf("lastIntermediateBand = %d\n", asp.lastIntermediateBand);
return 0;
}
static void asp_print_status_key() {
printf("\nType key: \n0:Success, \n3:UECC/Efail/Pfail, \n5:Refresh, \n6: clean, \n8:Unknown\n\n");
}
static bool asp_format(uint8_t type)
{
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
if(!asp.writable)
{
printf("Need to make system writable. Execute 'asp setwritable' first\n");
return false;
}
if ((asp.state != ASP_STATE_INITIALIZED)
&& (asp.state != ASP_STATE_READY)
&& (asp.state != ASP_STATE_ERROR_NOT_REGISTERED))
{
dprintf(DEBUG_CRITICAL, "ASP is not initialized/ready! Current state is %d\n", asp.state);
return false;
}
if ((type == ASP_FORMAT_UTIL)
|| (type == ASP_FORMAT_ALL))
{
command->op = ASPPROTO_CMD_UTIL_FORMAT;
}
else
{
command->op = ASPPROTO_CMD_LBA_FORMAT;
}
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
// NOTE: If format fails, we maintain the last formatted
// status from asp_get_geomtry()
dprintf(DEBUG_CRITICAL, "ASP Format Failed\n");
return false;
}
if (command->op == ASPPROTO_CMD_UTIL_FORMAT)
{
asp.util_formatted = true;
}
asp.lba_formatted = true;
return true;
}
static void output_nand_die_uid(uint8_t *buffer)
{
uint32_t die_idx, bus_idx, byteIdx;
for(bus_idx = 0; bus_idx < nand_info.num_bus; bus_idx++)
{
for(die_idx = 0; die_idx < nand_info.die_per_bus; die_idx++)
{
printf("CE %d: ", bus_idx);
for(byteIdx = 0; byteIdx < ASPPROTO_DEV_ID_LEN_PPN; byteIdx++)
{
printf("%02X ", buffer[(ASPPROTO_DEV_ID_LEN_PPN*(die_idx + bus_idx * nand_info.die_per_bus)) + byteIdx]);
}
printf("\n");
}
}
}
static void output_nand_uid (uint8_t *buffer, uint32_t size, uint32_t parameter)
{
uint32_t Idx, byteIdx;
for(Idx = 0; Idx < size/ASPPROTO_DEV_ID_LEN_PPN; Idx++)
{
printf("CE %d: ", Idx);
for(byteIdx = 0; byteIdx < ASPPROTO_DEV_ID_LEN_PPN; byteIdx++)
{
if(parameter == ASPPROTO_CMD_FW_VERSION)
{
printf("%c", buffer[(ASPPROTO_DEV_ID_LEN_PPN*Idx) + byteIdx]);
}
else
{
printf("%02X ", buffer[(ASPPROTO_DEV_ID_LEN_PPN*Idx) + byteIdx]);
}
}
printf("\n");
}
}
static int asp_nand_get_uid()
{
aspproto_cmd_t * command;
int status;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_PKG_ASSEMBLY_CODE;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get package assembly code: %d\n", status);
return -1;
}
printf("\nPackage Assembly Code:\n");
output_nand_uid(command->device_id.id, command->device_id.size, ASPPROTO_CMD_PKG_ASSEMBLY_CODE);
command->op = ASPPROTO_CMD_CONTROLLER_UID;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get controller uid: %d\n", status);
return -1;
}
printf("\nController UID:\n");
output_nand_uid(command->device_id.id, command->device_id.size, ASPPROTO_CMD_CONTROLLER_UID);
command->op = ASPPROTO_CMD_CONTROLLER_HWID;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get controller hwid: %d\n", status);
return -1;
}
printf("\nController HWID:\n");
output_nand_uid(command->device_id.id, command->device_id.size, ASPPROTO_CMD_CONTROLLER_HWID);
command->op = ASPPROTO_CMD_FW_VERSION;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get fw version: %d\n", status);
return -1;
}
printf("\nFW Version:\n");
output_nand_uid(command->device_id.id, command->device_id.size, ASPPROTO_CMD_FW_VERSION);
command->op = ASPPROTO_CMD_DIE_UID;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get die uid: %d\n", status);
return -1;
}
printf("\nDie UID:\n");
output_nand_die_uid(command->device_id.id);
return 0;
}
static uint32_t asp_get_link_clk_freq(bool verbose, uint64_t *freq_ptr) {
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
uint8_t *bounce_buffer;
uint32_t paddr32;
bounce_buffer = memalign(sizeof(uint64_t), ASP_NAND_BLK_ALIGNMENT);
if(!bounce_buffer)
{
printf("unable to allocate bounce buffer\n");
return -1;
}
paddr32 = VADDR_TO_PADDR32(bounce_buffer);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_LINK_CLK_FREQ;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = sizeof(uint64_t);
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, bounce_buffer, CPU_CACHELINE_SIZE);
#endif
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)){
dprintf(DEBUG_CRITICAL, "Unable to get link clk freq\n");
return -1;
} else {
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN, bounce_buffer, CPU_CACHELINE_SIZE);
#endif
if(verbose)
{
printf("link clk freq: %lld\n", *(uint64_t *)bounce_buffer);
}
}
if(freq_ptr)
{
*freq_ptr = *(uint64_t *)bounce_buffer;
}
return 0;
}
static uint32_t asp_get_perf_ticks(bool verbose, uint64_t *ticks_ptr, uint32_t channel) {
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
uint8_t *bounce_buffer;
uint32_t paddr32;
bounce_buffer = memalign(sizeof(uint64_t), ASP_NAND_BLK_ALIGNMENT);
if(!bounce_buffer)
{
printf("unable to allocate bounce buffer\n");
return -1;
}
paddr32 = VADDR_TO_PADDR32(bounce_buffer);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_PERF_TICKS;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = sizeof(uint64_t);
command->tunnel.options.mask = 1 << channel;
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, bounce_buffer, CPU_CACHELINE_SIZE);
#endif
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)){
dprintf(DEBUG_CRITICAL, "Unable to get perf ticks\n");
return -1;
} else {
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN, bounce_buffer, CPU_CACHELINE_SIZE);
#endif
if(verbose)
{
printf("For channel %d, ticks: %lld\n", channel, *(uint64_t *)bounce_buffer);
}
if(verbose && (0 == (*(uint64_t *)bounce_buffer)))
{
printf("Got 0 ticks. Is ANC_ENABLE_TUNNEL_PERF_COUNTER turned ON?\n");
}
}
if(ticks_ptr)
{
*ticks_ptr = *(uint64_t *)bounce_buffer;
}
return 0;
}
static int asp_get_bonfire_get_phy_band_num(uint32_t virtual_band) {
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_BONFIRE_GET_PHY_BAND_NUM;
command->tunnel.options.value = virtual_band;
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)){
dprintf(DEBUG_CRITICAL, "Unable to get actual band number\n");
return -1;
}
return command->tunnel.options.value;
}
static uint32_t asp_print_slc_bonfire_bands() {
uint32_t i;
for(i = 0; i < asp.numVirtualSLCBonfireBands; i++) {
printf("virtual band: %d -> actual band: %d\n", i, asp_get_bonfire_get_phy_band_num(i));
}
return 0;
}
static uint32_t asp_reset_perf_ticks() {
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_RESET_PERF_TICKS;
command->tunnel.options.mask = (1 << 0) | (1 << 1);
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)){
dprintf(DEBUG_CRITICAL, "Unable to reset perf ticks\n");
return -1;
}
return 0;
}
static int asp_cbp2r(uint32_t cau, uint32_t block, uint32_t page)
{
aspproto_cmd_t * command;
int status;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CBP_TO_ROW_ADDR;
command->addr_xlate.cau = cau;
command->addr_xlate.block = block;
command->addr_xlate.page = page;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get row address: %d\n", status);
return -1;
}
printf("cau: %d, blk: %d, page: %d --> row_addr: 0x%X\n", cau, block, page, command->addr_xlate.row_addr);
return 0;
}
static int asp_l2dbp(uint32_t lba)
{
nand_debug_lba_map_t *lbaMap;
uint32_t paddr32;
uint32_t lbaMapSize;
aspproto_cmd_t * command;
int status;
lbaMap = NULL;
command = NULL;
lbaMapSize = sizeof(nand_debug_lba_map_t);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
if(!command)
{
return -1;
}
lbaMap = memalign(lbaMapSize, ASP_NAND_BLK_ALIGNMENT);
if(!lbaMap)
{
dprintf(DEBUG_CRITICAL, "Unable to allocate data_buffer\n");
return -1;
}
memset(lbaMap, 0x00, lbaMapSize);
paddr32 = VADDR_TO_PADDR32(lbaMap);
lbaMap->lba = lba;
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, lbaMap, ROUNDUPTO(lbaMapSize,CPU_CACHELINE_SIZE));
#endif
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_LBA_TO_PADDR;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = lbaMapSize;
status = asp_send_command(command->tag);
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_INVALIDATE, lbaMap, ROUNDUPTO(lbaMapSize,CPU_CACHELINE_SIZE));
#endif
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get Physical Address for lba: %d\n", lbaMap->lba);
return -1;
}
printf("lba: 0x%x --> band: 0x%x dip: %d bork: 0x%x page: 0x%x\n", lbaMap->lba, lbaMap->band, lbaMap->dip, lbaMap->bork, lbaMap->page);
return 0;
}
static int asp_r2cbp(uint32_t row_addr)
{
aspproto_cmd_t * command;
int status;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_ROW_ADDR_TO_CBP;
command->addr_xlate.row_addr = row_addr;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get row address: %d\n", status);
return -1;
}
printf("row_addr: 0x%X --> cau: %d, block: %d, page: %d\n", row_addr, command->addr_xlate.cau, command->addr_xlate.block, command->addr_xlate.page);
return 0;
}
static int asp_set_burnin_code(uint32_t burnin_code)
{
aspproto_cmd_t * command;
int status;
if(!asp.writable)
{
printf("Need to make system writable. Execute 'asp setwritable' first\n");
return -1;
}
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_SET_BURNIN_CODE;
command->lba = burnin_code;
status = asp_send_command(command->tag);
return 0;
}
static int asp_set_tlcwritestripes(uint32_t write_stripes)
{
aspproto_cmd_t * command;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_SETOPTIONS;
command->opts.TLCwriteStripes = write_stripes;
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)){
dprintf(DEBUG_CRITICAL, "Unable to set tlc writes stripes\n");
return -1;
}
return 0;
}
static int asp_get_burnin_code()
{
aspproto_cmd_t * command;
int status;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_GET_BURNIN_CODE;
status = asp_send_command(command->tag);
printf("Burnin Code: %d. Status: 0x%X\n", command->lba, status);
return 0;
}
static int asp_get_last_failure()
{
aspproto_cmd_t * command;
int status;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_GET_LAST_FAILURE;
status = asp_send_command(command->tag);
printf("Type: %d CE: %d, page: 0x%X\n\n", command->last_failure.failure_mode, command->last_failure.ce, command->last_failure.physical_page);
return 0;
}
static int asp_sectors_per_band(uint32_t band, bool verbose)
{
aspproto_cmd_t * command;
int status;
if((band < 1) || (band > asp.lastBand))
{
printf("Band %d not allowed. Please choose a band between 1 and %d\n", band, asp.lastBand);
return -1;
}
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_SECTORS_PER_BAND;
command->lba = band;
status = asp_send_command(command->tag);
if(verbose)
{
printf("Band: %d has %d sectors.\n", band, command->count);
}
return (int)command->count;
}
static int asp_nand_get_addr(uint32_t vba, uint32_t *bus, uint32_t *row_addr, bool verbose)
{
aspproto_cmd_t * command;
int status;
if(vba < nand_info.sec_per_full_band)
{
printf("Cannot decode vba in band 0: should be >= %d\n", nand_info.sec_per_full_band);
return -1;
}
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_GET_ADDR;
command->addr.vba = vba;
status = asp_send_command(command->tag);
if(verbose)
{
printf("vba: %d -> ce: %d, row_addr: 0x%X, cau: %d, column: %d\n", vba, command->addr.ce, command->addr.row_addr, command->addr.cau, command->addr.column);
}
if(bus)
{
*bus = command->addr.ce;
}
if(row_addr)
{
*row_addr = command->addr.row_addr;
}
return 0;
}
static uint32_t get_num_pages_for_sectors(uint32_t sector_offset, uint32_t num_sectors)
{
uint32_t last_sector = (sector_offset + num_sectors);
uint32_t leading_sectors = 0;
uint32_t trailing_sectors = 0;
uint32_t intermediate_sectors = 0;
if(last_sector >= nand_info.sec_per_page)
{
leading_sectors = sector_offset ? (nand_info.sec_per_page - sector_offset) : 0;
}
else
{
leading_sectors = sector_offset ? (last_sector - sector_offset) : 0;
}
if((!sector_offset) || (last_sector > nand_info.sec_per_page))
{
trailing_sectors = last_sector % nand_info.sec_per_page;
}
intermediate_sectors = num_sectors - (leading_sectors + trailing_sectors);
assert(!(intermediate_sectors % nand_info.sec_per_page));
return (leading_sectors ? 1 : 0) + (trailing_sectors ? 1 : 0) + (intermediate_sectors / nand_info.sec_per_page);
}
/*
Description:
Does a physical NAND read without encryption.
Reads are done one physical page at a time.
Output data is arranged as first all data followed by all metadata.
Metadata will be available only at a full page boundary - even for partial page reads.
For partial page reads, output data is arranged as follows:
Example - For a 16k page with a read of 2 4k sectors starting from offset 1,
data will be available as the first 8k, meta will be available at a 16k offset.
*/
static int asp_physical_read(uint32_t bus,
uint32_t ce,
uint32_t row_addr,
uint32_t sector_offset,
uint32_t num_sectors,
nand_debug_health_monitoring_e health_monitoring,
aspproto_cell_type_t cell_type,
uint8_t * buffer,
bool verbose,
int *status_ptr)
{
aspproto_cmd_t *command = 0;
int status = 0;
uint8_t *bounce_buffer = NULL;
uint32_t paddr32 = 0;
const uint32_t main_size = ASP_NAND_BLKSZ * nand_info.sec_per_page;
const uint32_t meta_size = nand_info.sec_per_page * METADATA_PER_LBA;
const uint32_t num_pages = get_num_pages_for_sectors(sector_offset, num_sectors);
const uint32_t kB_sectors_per_page = main_size / 1024;
uint32_t bytes_per_read = main_size + meta_size;
uint32_t sectors_remaining = num_sectors;
uint32_t offset_cursor = sector_offset;
uint32_t sectors_for_current_page;
uint8_t *data_cursor = buffer;
uint8_t *meta_cursor = buffer + (num_pages * main_size);
uint32_t current_row_addr = row_addr;
uint8_t *buf_stats_cursor = NULL;
uint32_t idx = 0;
uint8_t *value_ptr;
uint64_t perf_ticks;
uint64_t link_clk_freq;
if(0 != asp_reset_perf_ticks())
{
return -1;
}
if((sector_offset > (nand_info.sec_per_page - 1)))
{
printf("sector offset allowed to be only between 0 and %d\n", nand_info.sec_per_page - 1);
return -1;
}
if((NAND_DEBUG_HEALTH_MONITORING_NONE != health_monitoring) && ((sector_offset != 0) || (num_sectors % nand_info.sec_per_page)))
{
printf("health monitoring reads are allowed only for full pages\n");
return -1;
}
if(NAND_DEBUG_HEALTH_MONITORING_COUNT <= health_monitoring)
{
printf("invalid health monitoring option\n");
return -1;
}
if(NAND_DEBUG_HEALTH_MONITORING_NONE != health_monitoring)
{
if (!security_allow_memory(buffer, num_pages * kB_sectors_per_page))
{
printf("Permission Denied\n");
return -1;
}
memset(buffer, 0xA5, num_pages * kB_sectors_per_page);
bytes_per_read = kB_sectors_per_page;
}
else
{
if (!security_allow_memory(buffer, num_pages * bytes_per_read))
{
printf("Permission Denied\n");
return -1;
}
memset(buffer, 0xA5, num_pages * bytes_per_read);
}
bounce_buffer = memalign(bytes_per_read, 4096);
if(!bounce_buffer)
{
dprintf(DEBUG_CRITICAL, "Failed to allocate data buffer\n");
return -1;
}
paddr32 = VADDR_TO_PADDR32(bounce_buffer);
while(sectors_remaining)
{
if (NAND_DEBUG_HEALTH_MONITORING_IN_DEPTH == health_monitoring) {
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_ENABLE_IN_DEPTH_HEALTH;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << bus);
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)) {
dprintf(DEBUG_CRITICAL, "Error enabling depth health monitoring for channel %d rowAddr: 0x%x\n", bus, current_row_addr);
}
}
if((offset_cursor % nand_info.sec_per_page) && ((sector_offset + sectors_remaining) > nand_info.sec_per_page))
{
sectors_for_current_page = (offset_cursor % nand_info.sec_per_page) ? (nand_info.sec_per_page - sector_offset) : 0;
}
else
{
sectors_for_current_page = ((sectors_remaining < nand_info.sec_per_page) ? sectors_remaining : nand_info.sec_per_page);
}
memset(bounce_buffer, 0xC7, bytes_per_read);
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, bounce_buffer, ROUNDUPTO(bytes_per_read,CPU_CACHELINE_SIZE));
#endif
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_READ;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = bytes_per_read;
command->tunnel.options.debug_epr_info.bus = bus;
command->tunnel.options.debug_epr_info.ce = ce;
command->tunnel.options.debug_epr_info.row_addr = current_row_addr;
command->tunnel.options.debug_epr_info.num_sectors = sectors_for_current_page;
command->tunnel.options.debug_epr_info.sector_offset = offset_cursor % nand_info.sec_per_page;
command->tunnel.options.debug_epr_info.health_monitoring = health_monitoring;
command->tunnel.options.debug_epr_info.cell_type = cell_type;
command->tunnel.options.mask = 1 << bus;
command->flags.all = 0;
command->flags.noAesKey = 1;
status = asp_send_command(command->tag);
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_INVALIDATE, bounce_buffer, ROUNDUPTO(bytes_per_read,CPU_CACHELINE_SIZE));
#endif
if(verbose)
{
printf("Bus: %01d, ce: %02d, row_addr: 0x%08X, num_sectors: %02d, sector_offset: %02d. Status: %02d\t", bus, ce, current_row_addr, sectors_for_current_page, (offset_cursor % nand_info.sec_per_page), status);
}
if(status_ptr)
{
*status_ptr = status;
}
if(NAND_DEBUG_HEALTH_MONITORING_SECTOR_STATS == health_monitoring)
{
// display sector stats
printf("Bitflips: ");
buf_stats_cursor = bounce_buffer;
for(idx = 0; idx < kB_sectors_per_page; idx++)
{
printf("%02X ", *buf_stats_cursor);
buf_stats_cursor++;
}
memcpy(data_cursor, bounce_buffer, kB_sectors_per_page);
data_cursor += kB_sectors_per_page;
}
else if (NAND_DEBUG_HEALTH_MONITORING_IN_DEPTH == health_monitoring)
{
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_IN_DEPTH_HEALTH;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << bus);
command->tunnel.options.value = 0;
if ((asp_send_command(command->tag) == ASPPROTO_CMD_STATUS_SUCCESS)) {
value_ptr = (uint8_t*) &(command->tunnel.options.value);
printf("Correctability: 0x%x tR-Status: 0x%x",value_ptr[0], value_ptr[1]);
} else {
dprintf(DEBUG_CRITICAL, "Error pulling in depth health monitoring for channel %d rowAddr: 0x%x\n", bus, current_row_addr);
}
}
else
{
memcpy(data_cursor, bounce_buffer, main_size);
memcpy(meta_cursor, (bounce_buffer + main_size), meta_size);
data_cursor += main_size;
meta_cursor += meta_size;
}
if(verbose)
{
printf("\n");
}
sectors_remaining -= sectors_for_current_page;
offset_cursor += sectors_for_current_page;
current_row_addr++;
}
if(verbose)
{
asp_print_status_key();
}
if(0 != asp_get_perf_ticks(false, &perf_ticks, bus))
{
return -1;
}
if(0 != asp_get_link_clk_freq(false, &link_clk_freq))
{
return -1;
}
printf("%u bytes transferred in %llu ticks @ %llu Hz\n\n", (num_sectors * ASP_NAND_BLKSZ),
perf_ticks,
link_clk_freq);
printf("To calculate total throughput in MiB/s: (%u * %llu) / (%llu * 1024 * 1024)\n\n", (num_sectors * ASP_NAND_BLKSZ),
link_clk_freq,
perf_ticks);
printf("Total time taken in us: (%llu) / (%llu / 1000000)\n\n", perf_ticks, link_clk_freq);
printf("Note: This is actual bus utilization with very minimal SW overhead. This command uses only 1 channel at a time\n");
free(bounce_buffer);
return 0;
}
static int asp_read_pagemeta(uint32_t bus, uint32_t ce, uint32_t row_addr, uint32_t num_pages, uint8_t * buffer)
{
uint32_t page_idx = 0;
const uint32_t main_size = ASP_NAND_BLKSZ * nand_info.sec_per_page;
const uint32_t meta_size = nand_info.sec_per_page * METADATA_PER_LBA;
uint8_t *meta_cursor = buffer + (num_pages * main_size);
asp_physical_read(bus, ce, row_addr, 0, (num_pages * nand_info.sec_per_page), false, CELL_TYPE_IS_MLC, buffer, false, NULL);
printf("LBA tokens:\n");
for(page_idx = row_addr; page_idx < row_addr + num_pages; page_idx++)
{
printf("Bus:%01d, ce:%02d, row_addr:0x%08X\t", bus, ce, page_idx);
printf("Meta: %08X %08X %08X %08X\n", *((uint32_t*)meta_cursor), *((uint32_t*)meta_cursor + 4), *((uint32_t*)meta_cursor + 8), *((uint32_t*)meta_cursor + 12));
meta_cursor += meta_size;
}
return 0;
}
static int asp_read_bandmeta(uint32_t band, uint8_t * buffer, bool verbose)
{
uint32_t page_idx = 0;
const uint32_t main_size = ASP_NAND_BLKSZ * nand_info.sec_per_page;
const uint32_t meta_size = nand_info.sec_per_page * METADATA_PER_LBA;
const uint32_t sectors_per_band = asp_sectors_per_band(band, false);
const uint32_t pages_per_band = sectors_per_band / nand_info.sec_per_page;
uint32_t vba = band * nand_info.sec_per_full_band;
uint8_t *meta_cursor = buffer;
uint32_t row_addr = 0;
uint32_t bus = 0;
uint8_t *page_buffer = NULL;
if (!security_allow_memory(buffer, meta_size * pages_per_band))
{
printf("Permission Denied\n");
return -1;
}
memset(buffer, 0xA5, meta_size * pages_per_band);
page_buffer = memalign((main_size + meta_size), 4096);
if(!page_buffer)
{
printf("could not allocate page buffer\n");
return -1;
}
if(verbose)
{
printf("LBA tokens:\n");
}
for(page_idx = 0; page_idx < pages_per_band; page_idx++)
{
asp_nand_get_addr (vba, &bus, &row_addr, false);
asp_physical_read(bus, 0, row_addr, 0, nand_info.sec_per_page, false, CELL_TYPE_IS_MLC, page_buffer, false, NULL);
memcpy(meta_cursor, page_buffer + main_size, meta_size);
if(verbose)
{
printf("Bus:%01d, ce:%02d, row_addr:0x%08X\t", bus, 0, row_addr);
printf("Meta: %08X %08X %08X %08X\n", *((uint32_t*)meta_cursor), *((uint32_t*)meta_cursor + (1 * LBA_TOKEN_SIZE)), *((uint32_t*)meta_cursor + (2 * LBA_TOKEN_SIZE)), *((uint32_t*)meta_cursor + (3 * LBA_TOKEN_SIZE)));
}
vba += nand_info.sec_per_page;
meta_cursor += meta_size;
}
free(page_buffer);
return 0;
}
static int audit_blog(uint32_t band, uint8_t *buffer, uint32_t num_leafs, bool verbose)
{
const uint32_t sectors_per_band = asp_sectors_per_band(band, false);
const uint32_t pages_per_band = sectors_per_band / nand_info.sec_per_page;
BlogE_t *blog_buffer = (BlogE_t *)buffer;
uint32_t current_lba_token;
uint32_t current_size;
uint32_t *lba_token_buffer = NULL;
uint32_t max_blog_entries = 0;
uint32_t blog_entry_idx = 0;
uint32_t *lba_token_cursor = NULL;
uint32_t size_idx;
uint32_t boff_idx = 0;
int status = 0;
assert(buffer);
printf("Auditing Blog entries against LBA's in NAND...\n");
lba_token_buffer = (uint32_t *)malloc(nand_info.sec_per_page * METADATA_PER_LBA * pages_per_band);
if(!lba_token_buffer)
{
printf("Unable to allocate memory for lba token buffer\n");
return -1;
}
if(asp_read_bandmeta(band, (uint8_t *)lba_token_buffer, false))
{
free(lba_token_buffer);
return -1;
}
lba_token_cursor = lba_token_buffer;
max_blog_entries = (num_leafs * ASP_NAND_BLKSZ) / sizeof(BlogE_t);
for(blog_entry_idx = 0; blog_entry_idx < max_blog_entries; blog_entry_idx++)
{
current_lba_token = blog_buffer[blog_entry_idx].lba;
current_size = blog_buffer[blog_entry_idx].size;
if((blog_entry_idx % (ASP_NAND_BLKSZ / sizeof(BlogE_t))) == ((ASP_NAND_BLKSZ / sizeof(BlogE_t)) - 1))
{
if(verbose)
{
printf("End of leaf.\n");
}
assert(current_lba_token == TOKEN_TERMINATOR);
continue;
}
else if(current_lba_token == TOKEN_TERMINATOR)
{
if(verbose)
{
printf("End of blog.\n");
}
break;
}
assert(current_size <= sectors_per_band);
for(size_idx = 0; size_idx < current_size; size_idx++)
{
if(verbose)
{
printf("Blog Entry idx: %04d, Blog Entry LBA: 0x%08X, LBA in NAND: 0x%08X. Boff: %04d\n", blog_entry_idx, current_lba_token, *lba_token_cursor, boff_idx);
}
if((current_lba_token >= LBA_TOKEN_BASE) && (current_lba_token != LBA_TOKEN_BLOG))
{
//Page pads are written slightly differently in NAND and Blog
current_lba_token = LBA_TOKEN_BASE;
}
if(current_lba_token != *lba_token_cursor)
{
printf("Audit failed. Blog says LBA: 0x%08X. NAND says LBA: 0x%08X. boff: %d (vba: %d)\n", current_lba_token, *lba_token_cursor, boff_idx, ((band * nand_info.sec_per_full_band) + boff_idx));
status = -1;
}
lba_token_cursor += LBA_TOKEN_SIZE;
current_lba_token++;
boff_idx++;
}
}
if(status)
{
printf("Audit failed\n");
}
else
{
printf("Audit passed\n");
}
free(lba_token_buffer);
return status;
}
//Dumps the blog and optionally audits if all LBA's in band correspond to blog entries.
static int asp_dump_blog(uint32_t band, uint8_t * buffer, bool audit, bool verbose)
{
const uint32_t main_size = ASP_NAND_BLKSZ * nand_info.sec_per_page;
const uint32_t meta_size = nand_info.sec_per_page * METADATA_PER_LBA;
const uint32_t sectors_per_band = asp_sectors_per_band(band, true);
const uint32_t max_blog_size = sectors_per_band * sizeof(BlogE_t);
const uint32_t max_possible_leafs = (max_blog_size / ASP_NAND_BLKSZ) + 1;
uint32_t *leaf_ptr;
uint32_t vba = band * nand_info.sec_per_full_band;
uint32_t blogdir_vba;
uint32_t row_addr = 0;
uint32_t bus = 0;
uint32_t lba_token;
uint32_t expected_lba_offset = 0;
uint32_t num_leafs = 0;
uint32_t status = 0;
uint8_t *page_buffer = NULL;
bool audit_allowed = true;
int lba_status = ASPPROTO_CMD_STATUS_SUCCESS;
if (!security_allow_memory(buffer, max_blog_size))
{
printf("Permission Denied\n");
return -1;
}
memset(buffer, 0xA5, max_blog_size);
page_buffer = memalign((main_size + meta_size), 4096);
if(!page_buffer)
{
printf("Could not allocate page buffer\n");
return -1;
}
leaf_ptr = malloc(max_possible_leafs * sizeof(uint32_t));
if(!leaf_ptr)
{
printf("Could not allocate leaf pointer array\n");
return -1;
}
//Blog always starts at last sector of band.
vba = vba + sectors_per_band - 1;
blogdir_vba = vba;
asp_nand_get_addr(vba , &bus, &row_addr, false);
expected_lba_offset = vba % nand_info.sec_per_page;
//Physical read of the Blog sector
asp_physical_read(bus, 0, row_addr, expected_lba_offset, 1, false, CELL_TYPE_IS_MLC, page_buffer, false, &lba_status);
if(lba_status != ASPPROTO_CMD_STATUS_SUCCESS)
{
printf("Received a non-success status: %d when reading bus: %d, ce: %d, row_addr: 0x%X, lba offset: %d\n", lba_status, bus, 0, row_addr, expected_lba_offset);
asp_print_status_key();
audit_allowed = false;
goto FreeMemory;
}
lba_token = *((uint32_t*)(page_buffer + main_size));
if(lba_token < LBA_TOKEN_BASE)
{
BlogE_t *ptr = (BlogE_t *)page_buffer;
ptr[0].lba = lba_token - (sectors_per_band - 1);
ptr[0].size = sectors_per_band;
ptr[1].lba = TOKEN_TERMINATOR;
ptr[1].size = 0;
num_leafs = 1;
printf("No blog. The entrire band was written sequentially. lba 0x%x size 0x%x\n ", ptr[0].lba, ptr[0].size);
memcpy(buffer, page_buffer, ASP_NAND_BLKSZ);
}
else
{
if (LBA_TOKEN_BLOG == lba_token)
{
BlogE_t *ptr = (BlogE_t *)page_buffer;
nob_t *nobPtr;
uint32_t i, j;
ptr += BLOG_NUM_ENTRY;
nobPtr = (nob_t*)ptr;
nobPtr--;
i = nobPtr->all;
nobPtr -= i;
printf("Number of leafs is %d\n",i);
num_leafs = i;
if(i == 1)
{
ptr -= BLOG_NUM_ENTRY;
memcpy(buffer, page_buffer, ASP_NAND_BLKSZ);
j = 0;
while (( ptr[j].lba != TOKEN_TERMINATOR) && (ptr[j].size != 0))
{
printf("lba 0x%x count 0x%x\n", ptr[j].lba,ptr[j].size);
j++;
}
}
else
{
nob_t *dir = malloc(sizeof(nob_t) * i);
uint32_t dir_no = i;
if(dir)
{
memcpy(dir, nobPtr, sizeof(nob_t) * dir_no);
}
else
{
printf("cannot allocate memory . getting out\n");
goto FreeMemory;
}
for(j = 0; j < num_leafs; j ++)
{
vba = (dir[j].band * nand_info.sec_per_full_band) + dir[j].boff;
asp_nand_get_addr(vba , &bus, &row_addr, false);
expected_lba_offset = vba % nand_info.sec_per_page;
//Physical read of the Blog sector
asp_physical_read(bus, 0, row_addr, expected_lba_offset, 1, false, CELL_TYPE_IS_MLC, page_buffer, false, &lba_status);
if(lba_status != ASPPROTO_CMD_STATUS_SUCCESS)
{
printf("Received a non-success status: %d when reading bus: %d, ce: %d, row_addr: 0x%X, lba offset: %d\n", lba_status, bus, 0, row_addr, expected_lba_offset);
asp_print_status_key();
audit_allowed = false;
free(dir);
goto FreeMemory;
}
memcpy(buffer + (j * ASP_NAND_BLKSZ), page_buffer, ASP_NAND_BLKSZ);
ptr = (BlogE_t *)page_buffer;
while (( ptr[i].lba != TOKEN_TERMINATOR) && (ptr[i].size != 0))
{
printf("lba 0x%x count 0x%x\n", ptr[i].lba,ptr[i].size);
i++;
}
}
free(dir);
}
}
else
{
goto FreeMemory;
}
}
printf("%d bytes of Blog dumped to %p\n", (num_leafs * ASP_NAND_BLKSZ), buffer);
if(audit && audit_allowed)
{
status = audit_blog(band, buffer, num_leafs, verbose);
}
FreeMemory :
free(page_buffer);
free(leaf_ptr);
return status;
}
static uint32_t asp_istlc(bool verbose) {
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_ISTLC;
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)){
dprintf(DEBUG_CRITICAL, "Unable to get if device is TLC\n");
return -1;
} else {
if(verbose)
{
printf("istlc: %d\n", command->tunnel.options.value);
}
return command->tunnel.options.value;
}
}
static uint32_t get_rma_chunk(uint32_t channel, uint32_t startPage, uint8_t *bounceBuffer, uint32_t buffLen) {
uint32_t paddr32;
paddr32 = VADDR_TO_PADDR32(bounceBuffer);
memset(bounceBuffer, 0xA5, buffLen);
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_RMA_DATA;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = buffLen;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << channel);
command->tunnel.options.value = startPage;
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, bounceBuffer, ROUNDUPTO(buffLen,CPU_CACHELINE_SIZE));
#endif
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)){
dprintf(DEBUG_CRITICAL, "Unable to pull rma data for channel %d\n",channel);
return -1;
}
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_INVALIDATE, bounceBuffer, ROUNDUPTO(buffLen,CPU_CACHELINE_SIZE));
#endif
return 0;
}
static uint32_t asp_recover() {
aspproto_cmd_t * command;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_RECOVER;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)) {
dprintf(DEBUG_CRITICAL, "Unable to recover NAND\n");
return -1;
}
printf("NAND recovered\n");
return 0;
}
static uint32_t asp_rma_delete(uint32_t channel) {
aspproto_cmd_t * command;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_DELETE_RMA_DATA;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << channel);
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)) {
dprintf(DEBUG_CRITICAL, "Unable to delete rma data for channel %d\n",channel);
return -1;
}
printf("RMA deleted from channel %d\n",channel);
return 0;
}
static uint32_t asp_rma_configure(uint32_t channel, uint32_t mask) {
aspproto_cmd_t * command;
mask &= 0x0F;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_CONFIGURE_RMA_DATA;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << channel);
command->tunnel.options.value = mask;
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)) {
dprintf(DEBUG_CRITICAL, "Unable to configure rma data for channel %d with mask 0x%x\n", channel, mask);
return -1;
}
printf("RMA configured on channel %d with mask 0x%x\n",channel,mask);
return 0;
}
static uint32_t asp_rma_set(uint32_t channel) {
aspproto_cmd_t * command;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_TRIGGER_RMA_DATA;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << channel);
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)) {
dprintf(DEBUG_CRITICAL, "Unable to set RMA data for channel %d\n", channel);
return -1;
}
printf("Set RMA data with address FFh FFh FFh on channel %d\n",channel);
return 0;
}
static uint32_t asp_rma_get(uint32_t channel, char *destFile, uint32_t *failure_type) {
aspproto_cmd_t * command;
uint8_t *bounceBuffer;
uint32_t buffLen;
uint32_t startPage;
uint32_t totalPages;
uint32_t pagesRemaining;
uint32_t numPages;
uint32_t chunkSize;
uint32_t paddr32;
uint32_t status;
buffLen = sizeof(nand_debug_failure_info_t);
bounceBuffer = memalign(buffLen, 4096);
if(!bounceBuffer)
{
dprintf(DEBUG_CRITICAL, "Failed to allocate data buffer\n");
return -1;
}
paddr32 = VADDR_TO_PADDR32(bounceBuffer);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_FAILURE_INFO;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = buffLen;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << channel);
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, bounceBuffer, ROUNDUPTO(buffLen,CPU_CACHELINE_SIZE));
#endif
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)) {
dprintf(DEBUG_CRITICAL, "Unable to trigger Debug Data for channel %d\n",channel);
free(bounceBuffer);
return -1;
}
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_INVALIDATE, bounceBuffer, ROUNDUPTO(buffLen,CPU_CACHELINE_SIZE));
#endif
if (NULL != destFile) {
startPage = ((nand_debug_failure_info_t *)bounceBuffer)->start_page;
totalPages = ((nand_debug_failure_info_t *)bounceBuffer)->page_len;
if (NULL != failure_type) {
*failure_type = ((nand_debug_failure_info_t *)bounceBuffer)->failure_type;
}
//pull the debug data pages
buffLen = MAX_RMA_CHUNK_PAGES * PERFECT_PAGE_SIZE;
free(bounceBuffer);
bounceBuffer = memalign(buffLen, 4096);
if(!bounceBuffer) {
dprintf(DEBUG_CRITICAL, "Failed to allocate data buffer\n");
return -1;
}
printf("\033[1;31m"); // change font color to red & bold
for (pagesRemaining = totalPages; pagesRemaining > 0; pagesRemaining -= numPages) {
printf("Status: %d%%, Pages: %d/%d ", (totalPages - pagesRemaining) * 100 / totalPages, totalPages - pagesRemaining, totalPages);
numPages = MIN(pagesRemaining,MAX_RMA_CHUNK_PAGES);
chunkSize = numPages * PERFECT_PAGE_SIZE;
if (get_rma_chunk(channel, startPage, bounceBuffer, chunkSize) != 0) {
break;
}
addr_t loadAddr = env_get_uint("loadaddr", DEFAULT_LOAD_ADDRESS);
memcpy( ((void*)loadAddr),bounceBuffer,chunkSize);
status = usb_send_data_to_file(destFile, chunkSize, loadAddr, 0);
if (status) {
printf("usb_cmd_put failed ret = 0x%x\n", status);
printf("\033[0m\n"); // change font color back to default
free(bounceBuffer);
return -1;
}
startPage += numPages;
printf("\033[0E\033[2K"); // go to start of line and erase the line respectively
}
printf("Status: %d%%, Pages: %d/%d", (totalPages - pagesRemaining) * 100 / totalPages, totalPages - pagesRemaining, totalPages);
printf("\033[0m\n"); // change font color back to default
}
asp_recover();
free(bounceBuffer);
return 0;
}
static uint32_t asp_ppn_get_calibration(char *destFile) {
aspproto_cmd_t * command;
uint8_t *bounceBuffer;
uint32_t buffLen;
uint32_t paddr32;
uint32_t status;
buffLen = PERFECT_PAGE_SIZE;
bounceBuffer = memalign(buffLen, 4096);
if(!bounceBuffer)
{
dprintf(DEBUG_CRITICAL, "Failed to allocate data buffer\n");
return -1;
}
paddr32 = VADDR_TO_PADDR32(bounceBuffer);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_CALIBRATION;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = buffLen;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << 0);
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, bounceBuffer, buffLen);
#endif
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)) {
dprintf(DEBUG_CRITICAL, "Unable to get calibration data\n");
free(bounceBuffer);
return -1;
}
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_INVALIDATE, bounceBuffer, buffLen);
#endif
addr_t loadAddr = env_get_uint("loadaddr", DEFAULT_LOAD_ADDRESS);
memcpy( ((void*)loadAddr),bounceBuffer,buffLen);
if (NULL != destFile) {
status = usb_send_data_to_file(destFile, buffLen, loadAddr, 0);
if (status) {
printf("usb_cmd_put failed ret = 0x%x\n", status);
free(bounceBuffer);
return -1;
}
} else {
printf("PPN calibration data extraced to address %p. Save to host by running 'usb put <filename> %u'\n",
(void *)loadAddr, buffLen);
}
free(bounceBuffer);
return 0;
}
static uint32_t asp_vth_sweep(uint32_t channel, uint32_t cau, uint32_t block, char *destFile)
{
aspproto_cmd_t * command;
uint32_t rowAddr;
uint32_t status;
uint32_t failure_type;
// bounds check the parameters
if (channel >= nand_info.num_bus || cau >= (nand_info.cau_per_die * nand_info.die_per_bus) || block >= nand_info.num_bands) {
printf("Invalid parameter. Max bus=%d \n", nand_info.num_bus);
return -1;
}
// get manufID to check for supported vendor
if ((asp.mfg_id[0][0] != VENDOR_CODE__HYNIX) && (asp.mfg_id[0][0] != VENDOR_CODE__SANDISK) && (asp.mfg_id[0][0] != VENDOR_CODE__MICRON)) {
printf("Vendor 0x%x not supported!\n", asp.mfg_id[0][0]);
return -1;
}
// get the row address
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CBP_TO_ROW_ADDR;
command->addr_xlate.cau = cau;
command->addr_xlate.block = block;
command->addr_xlate.page = 0;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS) {
dprintf(DEBUG_CRITICAL, "Failed to get row address: %d\n", status);
return -1;
}
rowAddr = command->addr_xlate.row_addr;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_VTH_SWEEP;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << channel);
command->tunnel.options.value = rowAddr;
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)) {
dprintf(DEBUG_CRITICAL, "Unable to perform Vth sweep for channel %d\n",channel);
return -1;
}
status = asp_rma_get(channel, destFile, &failure_type);
if (0 != status) {
printf("Vth sweep failed to get RMA data\n");
return -1;
}
if (PPN_GEB_TYPE_VTHSWEEP != failure_type) {
printf("Vth sweep expected failure type 0x%X, received 0x%X\n", PPN_GEB_TYPE_VTHSWEEP, failure_type);
return -1;
}
return 0;
}
static uint32_t find_sectors_per_stripe (uint32_t sectors_per_band)
{
const uint32_t sec_per_dip = nand_info.sec_per_full_band / nand_info.num_dip;
return (sectors_per_band / sec_per_dip) * nand_info.sec_per_page;
}
#define DEBUG_LARGE_HEAP_HACK (0)
#if DEBUG_LARGE_HEAP_HACK
//***HACK: iBoot does not support such a large heap.
// However it has a large hole of free unmanaged memory below IBOOT_BASE (defined in memmap.h)
// Define IBOOT_HACK_ADDR well below IBOOT_BASE and use it.
#define IBOOT_HACK_ADDR (0x8397FB000) //currently set to 64MB below IBOOT_BASE
#endif // #if DEBUG_LARGE_HEAP_HACK
static int asp_bonfire_write_band(uint32_t band, aspproto_cell_type_t cell_type, bool random_pattern, uint32_t data_pattern)
{
uint8_t *dataBuf = NULL;
uint32_t paddr32;
aspproto_cmd_t * command;
int status;
uint32_t sectors_per_band;
uint32_t band_size;
uint32_t i;
if((band < 1) || (band > asp.lastBand))
{
printf("Band %d not allowed. Please choose a band between 1 and %d\n", band, asp.lastBand);
return -1;
}
sectors_per_band = (uint32_t) asp_sectors_per_band(band, false);
if(0 == sectors_per_band)
{
printf("Cannot program band - It has 0 good sectors.\n");
return -1;
}
band_size = sectors_per_band * ASP_NAND_BLKSZ;
#if DEBUG_LARGE_HEAP_HACK
dataBuf = (uint8_t *)IBOOT_HACK_ADDR;
#else
// Provide a 4k buffer which will be written for the entire band.
band_size = ASP_NAND_BLKSZ;
dataBuf = memalign(band_size, ASP_NAND_BLK_ALIGNMENT);
#endif //#if DEBUG_LARGE_HEAP_HACK
if(!dataBuf)
{
printf("could not allocate stats buffer\n");
return -1;
}
assert((band_size % sizeof(data_pattern)) == 0);
for(i = 0; i < band_size / sizeof(data_pattern); i++)
{
if(!random_pattern)
{
((uint32_t *)dataBuf)[i] = data_pattern;
}
else
{
((uint32_t *)dataBuf)[i] = rand();
}
}
paddr32 = VADDR_TO_PADDR32(dataBuf);
command = asp_get_cmd_for_tag(ASP_TAG_NAND);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_WRITE_BAND;
command->tunnel.buffer_paddr = (uint64_t) paddr32;
command->tunnel.bufferLen = band_size;
command->tunnel.options.outputLen = 0;
command->tunnel.options.debug_epr_info.band = band;
command->tunnel.options.debug_epr_info.cell_type = cell_type;
command->flags.all = 0;
command->flags.noAesKey = 1;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not write band. Error: %d\n\n", status);
asp_print_status_key();
return -1;
}
#if !DEBUG_LARGE_HEAP_HACK
free(dataBuf);
#endif //#if DEBUG_LARGE_HEAP_HACK
return 0;
}
static int asp_bonfire_erase_band(uint32_t band)
{
aspproto_cmd_t * command;
int status;
if((band < 1) || (band > asp.lastBand))
{
printf("Band %d not allowed. Please choose a band between 1 and %d\n", band, asp.lastBand);
return -1;
}
if(!asp.writable)
{
printf("Need to make system writable. Execute 'asp setwritable' first\n");
return -1;
}
command = asp_get_cmd_for_tag(ASP_TAG_NAND);
command->op = ASPPROTO_CMD_BONFIRE_ERASE;
command->lba = band;
status = asp_send_command(command->tag);
printf("Band: %d erase status: 0x%X. \n", band, status);
return 0;
}
static aspproto_nand_col_t asp_get_single_dip_info(uint32_t dip)
{
aspproto_cmd_t * command;
int status;
command = asp_get_cmd_for_tag(ASP_TAG_NAND);
command->op = ASPPROTO_CMD_GET_DIP_INFO;
command->lba = dip;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
printf("Error getting info for dip %d. status: 0x%X\n", dip, status);
}
return command->dip_info;
}
static int asp_get_all_dip_info(void)
{
uint32_t i = 0;
aspproto_nand_col_t dip_info;
for(i = 0; i < nand_info.num_dip; i++)
{
dip_info = asp_get_single_dip_info(i);
printf("dip: %d - bus: %d, ce: %d, cau: %d\n", i, dip_info.bus, dip_info.ce, dip_info.cau);
}
return 0;
}
static int asp_dbp2r(uint32_t dip, uint32_t bork, uint32_t page)
{
aspproto_nand_col_t dip_info;
aspproto_cmd_t * command;
int status;
if(dip < nand_info.num_dip)
{
dip_info = asp_get_single_dip_info(dip);
}
else
{
dprintf(DEBUG_CRITICAL, "dip: %d exceeds maximum number of dips: %d\n", dip, nand_info.num_dip);
return -1;
}
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CBP_TO_ROW_ADDR;
command->addr_xlate.cau = dip_info.cau;
command->addr_xlate.block = bork;
command->addr_xlate.page = page;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get row address: %d\n", status);
printf("ce: %d cau: %d, blk: %d, page: %d\n", dip_info.ce, dip_info.cau, bork, page);
return -1;
}
printf("dip: %d, bork: %d, page: %d --> bus: %d row_addr: 0x%X\n",
dip, bork, page, dip_info.bus, command->addr_xlate.row_addr);
return 0;
}
static int asp_get_waterfall_table_size()
{
uint32_t table_size = 0;
aspproto_cmd_t * command;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_WATERFALL_TBL_SIZE;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not get waterfall table size.\n\n");
return -1;
}
table_size = command->tunnel.options.outputLen;
assert(!(table_size % SIZE_OF_WFALL_TBL_ENTRY));
printf("Waterfall table size: %d bytes\n", table_size);
return table_size;
}
static int asp_erase_band(uint32_t band, aspproto_cell_type_t cell_type, bool verbose)
{
aspproto_cmd_t * command;
int status;
if((band < 1) || (band > asp.lastBand))
{
printf("Band %d not allowed. Please choose a band between 1 and %d\n", band, asp.lastBand);
return -1;
}
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_ERASE_BAND;
command->tunnel.options.debug_epr_info.band = band;
command->tunnel.options.debug_epr_info.cell_type = cell_type;
status = asp_send_command(command->tag);
if(verbose)
{
printf("Band: %d erase status: 0x%X. \n", band, status);
}
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
asp_print_status_key();
dprintf(DEBUG_CRITICAL, "ERROR: Could not erase band %d\n\n", band);
return -1;
}
return 0;
}
static int asp_read_verify(uint32_t band)
{
aspproto_cmd_t * command;
uint64_t perf_ticks0;
uint64_t perf_ticks1;
uint64_t link_clk_freq;
uint32_t pages_verified = 0;
if((band < 1) || (band > asp.lastBand))
{
printf("Band %d not allowed. Please choose a band between 1 and %d\n", band, asp.lastBand);
return -1;
}
if(0 != asp_reset_perf_ticks())
{
return -1;
}
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_READ_VERIFY_BAND;
command->tunnel.options.debug_epr_info.band = band;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not read verify band.\n\n");
return -1;
}
pages_verified += command->tunnel.options.debug_epr_info.output_value;
if(0 != asp_get_perf_ticks(false, &perf_ticks0, 0))
{
return -1;
}
if(0 != asp_get_perf_ticks(false, &perf_ticks1, 1))
{
return -1;
}
if(0 != asp_get_link_clk_freq(false, &link_clk_freq))
{
return -1;
}
printf("Ch0: %u bytes verified in %llu ticks @ %llu Hz\n\n", (pages_verified * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
perf_ticks0,
link_clk_freq);
printf("Ch1: %u bytes verified in %llu ticks @ %llu Hz\n\n", (pages_verified * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
perf_ticks1,
link_clk_freq);
printf("Ch0: To calculate total throughput in MiB/s: (%u * %llu) / (%llu * 1024 * 1024)\n\n", (pages_verified * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks0);
printf("Ch1: To calculate total throughput in MiB/s: (%u * %llu) / (%llu * 1024 * 1024)\n\n", (pages_verified * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks1);
printf("Note: This is actual bus utilization with negligible overhead.\n");
return 0;
}
static int asp_get_timing_parameters(aspproto_nand_timing_params_t *buf)
{
uint32_t paddr32;
aspproto_nand_timing_params_t *timing_params = NULL;
aspproto_cmd_t *command;
timing_params = memalign(sizeof(aspproto_nand_timing_params_t), ASP_NAND_BLK_ALIGNMENT);
if(!timing_params)
{
dprintf(DEBUG_CRITICAL, "Unable to allocate data_buffer\n");
return -1;
}
memset(timing_params, 0x00, sizeof(aspproto_nand_timing_params_t));
paddr32 = VADDR_TO_PADDR32(timing_params);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_PPN_TIMING;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = sizeof(aspproto_nand_timing_params_t);
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not get timing parameters.\n\n");
free(timing_params);
return -1;
}
memcpy(buf, timing_params, sizeof(aspproto_nand_timing_params_t));
free(timing_params);
return 0;
}
static int asp_get_device_parameters(aspproto_ppn_device_params_t *buf)
{
uint32_t paddr32;
aspproto_ppn_device_params_t *device_params = NULL;
aspproto_cmd_t * command;
device_params = memalign(sizeof(aspproto_ppn_device_params_t), ASP_NAND_BLK_ALIGNMENT);
if(!device_params)
{
dprintf(DEBUG_CRITICAL, "Unable to allocate data_buffer\n");
return -1;
}
memset(device_params, 0x00, sizeof(aspproto_ppn_device_params_t));
paddr32 = VADDR_TO_PADDR32(device_params);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_PPN_GEOM;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = sizeof(aspproto_ppn_device_params_t);
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not get waterfall table.\n\n");
free(device_params);
return -1;
}
memcpy(buf, device_params, sizeof(aspproto_ppn_device_params_t));
free(device_params);
return 0;
}
static int asp_print_ppn_parameters(void)
{
aspproto_ppn_device_params_t device_params;
aspproto_nand_timing_params_t timing_params;
int status;
status = asp_get_device_parameters(&device_params);
status |= asp_get_timing_parameters(&timing_params);
if(!status)
{
printf("Device parameters:\n");
printf("caus_per_channel: %d\n", device_params.caus_per_channel);
printf("cau_bits: %d\n", device_params.cau_bits);
printf("blocks_per_cau: %d\n", device_params.blocks_per_cau);
printf("block_bits: %d\n", device_params.block_bits);
printf("pages_per_block: %d\n", device_params.pages_per_block);
printf("pages_per_block_slc: %d\n", device_params.pages_per_block_slc);
printf("page_address_bits: %d\n", device_params.page_address_bits);
printf("address_bits_bits_per_cell: %d\n", device_params.address_bits_bits_per_cell);
printf("default_bits_per_cell: %d\n", device_params.default_bits_per_cell);
printf("page_size: %d\n", device_params.page_size);
printf("dies_per_channel: %d\n", device_params.dies_per_channel);
printf("block_pairing_scheme: %d\n", device_params.block_pairing_scheme);
printf("bytes_per_row_address: %d\n", device_params.bytes_per_row_address);
printf("pages_in_block0: %d\n", device_params.pages_in_block0);
printf("pages_in_read_verify: %d\n", device_params.pages_in_read_verify);
printf("multi_cau_bits: %d\n", device_params.multi_cau_bits);
printf("pattern_bits: %d\n", device_params.pattern_bits);
printf("channel0_die: %d\n",device_params.channel0_die);
printf("package_blocks_at_EOL: %d\n", device_params.package_blocks_at_EOL);
printf("tRC: %d\n", timing_params.tRC_ns);
printf("tREA: %d\n", timing_params.tREA_ns);
printf("tREH: %d\n", timing_params.tREH_ns);
printf("tRHOH: %d\n", timing_params.tRHOH_ns);
printf("tRHZ: %d\n", timing_params.tRHZ_ns);
printf("tRLOH: %d\n", timing_params.tRLOH_ns);
printf("tRP: %d\n", timing_params.tRP_ns);
printf("tWC: %d\n", timing_params.tWC_ns);
printf("tWH: %d\n", timing_params.tWH_ns);
printf("tWP: %d\n", timing_params.tWP_ns);
printf("read_queue_size: %d\n", device_params.read_queue_size);
printf("program_queue_size: %d\n", device_params.program_queue_size);
printf("erase_queue_size: %d\n", device_params.erase_queue_size);
printf("prep_function_buffer_size: %d\n", device_params.prep_function_buffer_size);
printf("tRST: %d\n", timing_params.tRST_ms);
printf("tPURST: %d\n", timing_params.tPURST_ms);
printf("tSCE: %d\n", timing_params.tSCE_ms);
printf("tCERDY: %d\n", timing_params.tCERDY_us);
printf("caus_per_package: %d\n", device_params.caus_per_package);
printf("dies_per_package: %d\n", device_params.dies_per_package);
printf("tRC_ddr: %d\n", timing_params.tRC_ddr_ns);
printf("tREH_ddr: %d\n", timing_params.tREH_ddr_ns);
printf("tRP_ddr: %d\n", timing_params.tRP_ddr_ns);
printf("tDQSL: %d\n", timing_params.tDQSL_ps);
printf("tDQSH: %d\n", timing_params.tDQSH_ps);
printf("tDSC: %d\n", timing_params.tDSC_ps);
printf("tDQSRE: %d\n", timing_params.tDQSRE_ps);
printf("tDQSQ: %d\n", timing_params.tDQSQ_ps);
printf("tDVW: %d\n", timing_params.tDVW_ps);
printf("max_interface_speed: %d\n", timing_params.max_interface_speed_mhz);
printf("\n");
printf("num_bus: %d\n", device_params.num_bus);
printf("ces_per_bus: %d\n", device_params.ces_per_bus);
printf("logical_page_size: %d\n", device_params.logical_page_size);
}
return status;
}
static uint32_t tlc_pattern = 0;
static int get_tlc_pattern(void)
{
aspproto_ppn_device_params_t *device_params;
device_params = NULL;
device_params = (aspproto_ppn_device_params_t *) malloc(sizeof(aspproto_ppn_device_params_t));
assert(device_params);
asp_get_device_parameters(device_params);
if((tlc_pattern >= (uint32_t)((1 << device_params->pattern_bits) - 1)))
{
tlc_pattern = 0;
} else {
tlc_pattern++;
}
return tlc_pattern;
}
static uint32_t test_pattern = 0;
static int asp_bonfire_group_a_slc(uint32_t seed)
{
aspproto_cmd_t * command;
printf("Opening ASP\n");
if (!asp_send_open())
{
dprintf(DEBUG_CRITICAL, "Unable to open ASP\n");
return false;
}
printf("Setting writable\n");
if (!asp_set_writable())
{
asp.state = ASP_STATE_ERROR_NOT_WRITABLE;
printf("Unable to set writable\n");
return false;
}
assert(bonfire_slc_state == BONFIRE_SLC_STATE_NONE);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_BONFIRE_SLC_GROUP_A;
command->tunnel.options.value = seed;
command->flags.all = 0;
command->flags.noAesKey = 1;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not group A for SLC Bonfire.\n\n");
return -1;
}
bonfire_slc_state = BONFIRE_SLC_STATE_A;
asp_get_geometry();
return 0;
}
static int asp_bonfire_group_b_slc(void)
{
aspproto_cmd_t * command;
assert(bonfire_slc_state == BONFIRE_SLC_STATE_A);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_BONFIRE_SLC_GROUP_B;
command->flags.all = 0;
command->flags.noAesKey = 1;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not group A for SLC Bonfire.\n\n");
return -1;
}
bonfire_slc_state = BONFIRE_SLC_STATE_B;
asp_get_geometry();
return 0;
}
static int asp_bonfire_ungroup(void)
{
aspproto_cmd_t * command;
assert(bonfire_slc_state == BONFIRE_SLC_STATE_B);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_BONFIRE_SLC_UNGROUP;
command->flags.all = 0;
command->flags.noAesKey = 1;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not group A for SLC Bonfire.\n\n");
return -1;
}
bonfire_slc_state = BONFIRE_SLC_STATE_NONE;
asp_get_geometry();
return 0;
}
static int asp_write_band(uint32_t band, aspproto_cell_type_t cell_type, bool verbose, bool random_pattern, uint32_t data_pattern)
{
aspproto_cmd_t * command;
uint8_t * dataBuf;
uint32_t paddr32;
uint64_t perf_ticks0;
uint64_t perf_ticks1;
uint64_t link_clk_freq;
uint32_t pages_written = 0;
uint32_t pages_in_block;
uint32_t join_program_pages;
aspproto_cmd_status_e status;
uint32_t i;
uint32_t band_size = 0;
uint32_t buffer_length = 0;
uint32_t sectors_per_band;
if((band < 1) || (band > asp.lastBand))
{
printf("Band %d not allowed. Please choose a band between 1 and %d\n", band, asp.lastBand);
return -1;
}
sectors_per_band = (uint32_t) asp_sectors_per_band(band, false);
if(0 == sectors_per_band)
{
printf("Cannot program band - It has 0 good sectors.\n");
return -1;
}
band_size = sectors_per_band * ASP_NAND_BLKSZ;
join_program_pages = (cell_type == CELL_TYPE_IS_TLC) ? 3 : 1;
pages_in_block = (cell_type == CELL_TYPE_IS_SLC) ? nand_info.pages_per_block_slc : nand_info.pages_per_block;
if(0 != asp_reset_perf_ticks())
{
return -1;
}
#if DEBUG_LARGE_HEAP_HACK
buffer_length = band_size;
dataBuf = (uint8_t *)IBOOT_HACK_ADDR;
#else
buffer_length = ASP_NAND_BLKSZ;
dataBuf = memalign(buffer_length, ASP_NAND_BLK_ALIGNMENT);
#endif //#if DEBUG_LARGE_HEAP_HACK
if(!dataBuf)
{
printf("could not allocate data buffer\n");
return -1;
}
assert((buffer_length % sizeof(data_pattern)) == 0);
for(i = 0; i < buffer_length / sizeof(data_pattern); i++)
{
if(!random_pattern)
{
((uint32_t *)dataBuf)[i] = data_pattern;
}
else
{
((uint32_t *)dataBuf)[i] = rand();
}
}
paddr32 = VADDR_TO_PADDR32(dataBuf);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_WRITE_BAND;
command->tunnel.options.debug_epr_info.band = band;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = buffer_length;
command->tunnel.options.debug_epr_info.cell_type = cell_type;
command->flags.all = 0;
command->flags.noAesKey = 1;
if(cell_type == CELL_TYPE_IS_TLC)
{
command->tunnel.options.debug_epr_info.tlc_pattern = test_pattern++;
}
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not write band. Error: %d\n\n", status);
asp_print_status_key();
return -1;
}
pages_written = command->tunnel.options.debug_epr_info.output_value;
if(0 != asp_get_perf_ticks(false, &perf_ticks0, 0))
{
return -1;
}
if(0 != asp_get_perf_ticks(false, &perf_ticks1, 1))
{
return -1;
}
if(0 != asp_get_link_clk_freq(false, &link_clk_freq))
{
return -1;
}
if(verbose)
{
printf("Ch0: %u bytes transferred in %llu ticks @ %llu Hz\n\n", (pages_written * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
perf_ticks0,
link_clk_freq);
printf("Ch1: %u bytes transferred in %llu ticks @ %llu Hz\n\n", (pages_written * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
perf_ticks1,
link_clk_freq);
printf("Ch0: To calculate total throughput in MiB/s: (%u * %llu) / (%llu * 1024 * 1024)\n\n", (pages_written * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks0);
printf("Ch1: To calculate total throughput in MiB/s: (%u * %llu) / (%llu * 1024 * 1024)\n\n", (pages_written * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks1);
printf("Ch0: Time for each superstripe in us: (%llu / %d) / (%llu / 1000000)\n\n", perf_ticks0, (pages_in_block / join_program_pages), link_clk_freq);
printf("Ch1: Time for each superstripe in us: (%llu / %d) / (%llu / 1000000)\n\n", perf_ticks1, (pages_in_block / join_program_pages), link_clk_freq);
printf("Note: This is actual bus utilization with negligible overhead.\n");
}
return 0;
}
static int asp_eploop_band(uint32_t band, aspproto_cell_type_t cell_type, uint32_t seconds, bool random_pattern, uint32_t data_pattern)
{
uint64_t start_ticks;
uint32_t iterations;
start_ticks = system_time();
if((band < 1) || (band > asp.lastBand))
{
printf("Band %d not allowed. Please choose a band between 1 and %d\n", band, asp.lastBand);
return -1;
}
if(seconds)
{
printf("Going to erase+program band: %d for %d seconds. cell_type: %d\n", band, seconds, cell_type);
}
else
{
printf("Going to erase+program band: %d once. cell_type: %d\n", band, cell_type);
}
iterations = 0;
while(1)
{
if(-1 == asp_erase_band(band, cell_type, false))
{
printf("error erasing band. returning\n");
return -1;
}
if(-1 == asp_write_band(band, cell_type, false, random_pattern, data_pattern))
{
printf("error programming band. returning\n");
return -1;
}
iterations++;
if(0 == seconds)
{
break;
}
else if(time_has_elapsed(start_ticks, (uint64_t)seconds * 1000000))
{
break;
}
}
printf("Completed %d iterations.\n", iterations);
return 0;
}
static int asp_get_waterfall_table(uint8_t *buffer)
{
uint32_t paddr32;
uint32_t table_size = 0;
uint8_t *table = NULL;
aspproto_cmd_t * command;
table_size = asp_get_waterfall_table_size();
if (!security_allow_memory(buffer, table_size))
{
printf("Permission Denied\n");
return -1;
}
memset(buffer, 0xA5, table_size);
table = memalign(table_size, ASP_NAND_BLK_ALIGNMENT);
if(!table)
{
dprintf(DEBUG_CRITICAL, "Unable to allocate data_buffer\n");
return -1;
}
memset(table, 0x00, table_size);
paddr32 = VADDR_TO_PADDR32(table);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_WATERFALL_TABLE;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = table_size;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not get waterfall table.\n\n");
return -1;
}
memcpy(buffer, table, table_size);
printf("Waterfall table available at address: %p\n", buffer);
free(table);
return 0;
}
static int asp_get_dm(void)
{
uint8_t *dmBuf = NULL;
ExportDefects_t *defects = NULL;
uint32_t dmBufSize;
uint32_t paddr32;
aspproto_cmd_t * command;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_EXPORT_BAND_DEFECTS;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.outputLen = 0;
command->tunnel.options.flags.forceExport = 1;
command->tunnel.options.flags.sizeOnly = 1;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: no defect management list found\n\n");
asp.state = ASP_STATE_ERROR_INVALID_PPNFW;
return -1;
}
dmBufSize = command->tunnel.options.outputLen;
if (dmBufSize == 0){
dprintf(DEBUG_CRITICAL, "ERROR: defect management list returned size 0");
return -1;
}
//get the defect list
dmBuf = memalign(dmBufSize, ASP_NAND_BLK_ALIGNMENT);
if(!dmBuf)
{
dprintf(DEBUG_CRITICAL, "Unable to allocate data_buffer\n");
return -1;
}
memset(dmBuf, 0x00, dmBufSize);
paddr32 = VADDR_TO_PADDR32(dmBuf);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_EXPORT_BAND_DEFECTS;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = dmBufSize;
command->tunnel.options.flags.forceExport = 1;
command->tunnel.options.flags.sizeOnly = 0;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS){
dprintf(DEBUG_CRITICAL, "ERROR: failed to get the defect management list\n\n");
asp.state = ASP_STATE_ERROR_INVALID_PPNFW;
free(dmBuf);
return -1;
}
defects = (ExportDefects_t *)dmBuf;
//ensure that grownBadCount doesn't exceed the buffersize
if(defects->grownBadCount * sizeof(ExportDefectEntry_t) + sizeof(ExportDefects_t) - sizeof(uint32_t) > dmBufSize){
dprintf(DEBUG_CRITICAL, "ERROR: number of defects, %d, exceeds buffersize of %d bytes\n",defects->grownBadCount, dmBufSize);
free(dmBuf);
return -1;
}
//print the dm list
if (defects->grownBadCount == 0){
printf("No Grown Bad Blocks Found\n");
} else {
printf("Grown Bad Blocks:\n");
ExportDefectEntry_t * exportDefectEntries = (ExportDefectEntry_t *) &defects->exportDefectEntriesPtr;
for(uint32_t i=0; i < defects->grownBadCount; i++)
{
printf("bus: %d, ce: %d, cau: %d -> block: %d cycles: %d reason: 0x%x\n",
exportDefectEntries[i].bus,
exportDefectEntries[i].ce,
exportDefectEntries[i].cau,
exportDefectEntries[i].band,
exportDefectEntries[i].cycles,
exportDefectEntries[i].reason);
}
}
free(dmBuf);
return 0;
}
static int asp_get_bbt(void)
{
uint8_t *bbtBuf = NULL;
uint32_t paddr32;
uint32_t numSec = 0;
uint32_t numDip = 0;
uint32_t numBand = 0;
uint32_t dip = 0;
uint32_t band = 0;
uint32_t mask = 0;
uint32_t statsBufSize = 0;
uint8_t *dipPtr = NULL;
uint8_t *bandPtr = NULL;
aspproto_nand_col_t dip_info;
aspproto_cmd_t * command;
int status;
numSec = (nand_info.num_bands + 7) / 8; // bit per band round up to number of bytes.
numSec = (numSec + (ASP_NAND_BLKSZ-1)) / ASP_NAND_BLKSZ; // then round number of bytes up to number of sector
numDip = nand_info.num_dip;
numBand = nand_info.num_bands;
statsBufSize = numDip * numSec * ASP_NAND_BLKSZ;
bbtBuf = memalign(statsBufSize, ASP_NAND_BLK_ALIGNMENT);
if(!bbtBuf)
{
printf("could not allocate BBT buffer\n");
return -1;
}
memset(bbtBuf, 0x00, statsBufSize);
paddr32 = VADDR_TO_PADDR32(bbtBuf);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_GET_BBT;
command->count = numDip * numSec;
command->sglAndIv[0] = paddr32;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get BBT: %d\n", status);
return -1;
}
printf("BBT:\n");
for(dip = 0; dip < numDip; dip++)
{
dip_info = asp_get_single_dip_info(dip);
dipPtr = bbtBuf + (dip *numSec * ASP_NAND_BLKSZ);
for(band=0; band < numBand; band++)
{
bandPtr = dipPtr + (band/8);
mask = (0x80 >> (band % 8));
if (0 == (*bandPtr & mask))
{
printf("bus: %d, ce: %d, cau: %d -> block: %d\n", dip_info.bus, dip_info.ce, dip_info.cau, band);
}
}
printf("\n");
}
free(bbtBuf);
return 0;
}
static void output_band_stats(uint32_t *buf32)
{
uint32_t numBands;
uint32_t band;
uint32_t flags;
uint32_t flow;
uint32_t valid;
uint32_t eraseCycles;
uint32_t age;
uint32_t GCcan;
uint32_t GCmust;
uint32_t GCdone;
uint32_t pFail;
uint32_t mode;
uint32_t maxEraseCnt[NUM_ASP_PARITIONS] = {0, 0};
uint32_t minEraseCnt[NUM_ASP_PARITIONS] = {~0, ~0};
uint32_t avgEraseCnt[NUM_ASP_PARITIONS] = {0, 0};
uint32_t maxAge[NUM_ASP_PARITIONS] = {0, 0};
uint32_t firstBand[NUM_ASP_PARITIONS];
uint32_t lastBand[NUM_ASP_PARITIONS];
uint32_t * statsPtr;
uint32_t partitions = 0;
uint32_t partition;
printf ( "Band stats:\n" );
printf ( "===========\n" );
numBands = *buf32++;
partitions = *buf32++;
assert(partitions <= NUM_ASP_PARITIONS);
for(partition = 0; partition < partitions; partition++) {
firstBand[partition] = *buf32++;
lastBand[partition] = *buf32++;
}
statsPtr = buf32;
for (band = 0; band < numBands; band++)
{
flags = *buf32++;
flow = *buf32++;
valid = *buf32++;
eraseCycles = *buf32++;
age = *buf32++;
for(partition = 0; partition < partitions; partition++) {
if((band >= firstBand[partition]) && (band <= lastBand[partition]))
{
if(band == firstBand[partition])
{
printf("\n%s:\n", partition_names[partition]);
}
break;
}
}
if(partition < partitions)
{
maxAge[partition] = MAX(maxAge[partition], age);
maxEraseCnt[partition] = MAX(maxEraseCnt[partition], eraseCycles);
minEraseCnt[partition] = MIN(minEraseCnt[partition], eraseCycles);
avgEraseCnt[partition] += eraseCycles;
}
GCcan = flags & 1;
GCmust = (flags >> 1) & 1;
GCdone = (flags >> 2) & 1;
pFail = (flags >> 3) & 1;
mode = (flags >> 4) & 0x3;
if (~0U != valid)
{
printf("band:%4d\tflow:%2d\tvalid:%5d\terases:%5d\tage:%5d\tGCcan:%d GCmust:%d GCDone: %d pFail: %d mode:%d\n",
band, flow, valid, eraseCycles, age, GCcan, GCmust, GCdone, pFail, mode);
}
else
{
printf("band:%4d\tflow:%2d\tvalid: NA\terases:%5d\tage:%5d\tGCcan:%d GCmust:%d GCDone: %d pFail: %d mode:%d\n",
band, flow, eraseCycles, age, GCcan, GCmust, GCdone, pFail, mode);
}
}
for(partition = 0; partition < partitions; partition++) {
if(lastBand[partition] >= firstBand[partition]) {
avgEraseCnt[partition] = avgEraseCnt[partition] / (lastBand[partition] + 1 - firstBand[partition]);
printf("%s : Erase Cycles: Max ( %d ) Min ( %d ) Avg ( %d )\n", partition_names[partition],
maxEraseCnt[partition], minEraseCnt[partition], avgEraseCnt[partition]);
printf(" Max band age: ( %d )\n", maxAge[partition]);
}
}
}
static int asp_get_band_stats(void)
{
uint8_t *buffer = NULL;
uint32_t paddr32;
uint32_t statsBufSize = 0;
aspproto_cmd_t * command = NULL;
int status;
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
if(!command)
{
return -1;
}
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_READ_BAND_STATS;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.outputLen = 0;
command->tunnel.options.flags.forceExport = 0;
command->tunnel.options.flags.sizeOnly = 1;
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get Band Stats 0x%08X\n", status);
return -1;
}
statsBufSize = command->tunnel.options.outputLen;
statsBufSize = ALIGN_UP(statsBufSize, ASP_NAND_BLKSZ);
buffer = memalign(statsBufSize, ASP_NAND_BLK_ALIGNMENT);
if(!buffer)
{
printf("could not allocate Band Stats buffer\n");
return -1;
}
memset(buffer, 0x00, statsBufSize);
paddr32 = VADDR_TO_PADDR32(buffer);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_READ_BAND_STATS;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = statsBufSize;
command->tunnel.options.flags.forceExport = 0;
command->tunnel.options.flags.sizeOnly = 0;
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, buffer, statsBufSize);
#endif
status = asp_send_command(command->tag);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Failed to get Band Stats 0x%08X\n", status);
free(buffer);
return -1;
}
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_INVALIDATE, buffer, statsBufSize);
#endif
output_band_stats((uint32_t *)buffer);
free(buffer);
return 0;
}
static bool debug_counter_supported(uint32_t channel)
{
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_DEBUG_COUNTER_SUPPORTED;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << channel);
if (nand_info.num_bus==0)
{
return false;
}
command->tunnel.options.value = 0;
if ((asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS) || (command->tunnel.options.value == 0))
{
dprintf(DEBUG_CRITICAL, "Unable to get debug counter for channel %d\n",channel);
return false;
}
else
{
dprintf(DEBUG_CRITICAL, "channel %d debug counter supported: %d\n", channel, command->tunnel.options.value);
}
return true;
}
static bool get_debug_counter(uint32_t channel, uint8_t * buffer)
{
uint8_t *bounce_buffer = NULL;
uint32_t buffer_size = ASP_NAND_BLKSZ;
bool status = false;
if (nand_info.num_bus==0)
{
return false;
}
if (!security_allow_memory(buffer, buffer_size))
{
printf("Permission Denied\n");
return -1;
}
memset(buffer, 0xA5, buffer_size);
bounce_buffer = memalign(buffer_size, ASP_NAND_BLK_ALIGNMENT);
if(!bounce_buffer)
{
dprintf(DEBUG_CRITICAL, "Failed to allocate data buffer\n");
return -1;
}
memset(bounce_buffer, 0xC7, buffer_size);
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_GET_DEBUG_COUNTER;
command->tunnel.buffer_paddr = VADDR_TO_PADDR32(bounce_buffer);
command->tunnel.bufferLen = buffer_size;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << channel);
command->tunnel.options.value = 0;
if (asp_send_command(command->tag) == ASPPROTO_CMD_STATUS_SUCCESS)
{
memcpy(buffer, bounce_buffer, buffer_size);
status = true;
printf("Debug Counter Buffer extracted for ch %d. Save to host by running 'usb put <filename> %d'\n",
channel, ASP_NAND_BLKSZ);
}
else
{
dprintf(DEBUG_CRITICAL, "Unable to get debug counter for channel %d\n",channel);
}
free(bounce_buffer);
return status;
}
static bool reset_debug_counter(uint32_t channel)
{
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_NAND_DEBUG;
command->tunnel.opcode = NAND_DEBUG_RESET_DEBUG_COUNTER;
command->tunnel.buffer_paddr = 0;
command->tunnel.bufferLen = 0;
command->tunnel.options.mask = ((1<<ANC_MAX_BUSSES)-1) & (1 << channel);
if (nand_info.num_bus==0)
{
return false;
}
command->tunnel.options.value = 0;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "Unable to reset debug counter for channel %d\n",channel);
return false;
}
else
{
dprintf(DEBUG_CRITICAL, "debug counter reset for channel %d\n",channel);
}
return true;
}
/*NOTE: This command does not read actual data to host buffer.
It just overwrites a placeholder sector data buffer.
If you need actual data, use 'asp read'
If you need bitflips, use 'asp bonfirereadband'
*/
static int asp_read_band(uint32_t band, aspproto_cell_type_t cell_type, uint32_t num_stripes, uint32_t seconds)
{
aspproto_cmd_t * command;
uint8_t * scratch_buffer; //placeholder for putting in page data. every sector.
uint32_t paddr32;
uint64_t start_ticks;
uint64_t perf_ticks0;
uint64_t perf_ticks1;
uint64_t link_clk_freq;
uint64_t pages_read = 0;
uint32_t pages_in_block;
uint32_t join_program_pages;
int status;
if(band > asp.lastBand)
{
printf("Band %d not allowed. Please choose a band between 1 and %d\n", band, asp.lastBand);
return -1;
}
join_program_pages = (cell_type == CELL_TYPE_IS_TLC) ? 3 : 1;
pages_in_block = (cell_type == CELL_TYPE_IS_SLC) ? nand_info.pages_per_block_slc : nand_info.pages_per_block;
if(0 != asp_reset_perf_ticks())
{
return -1;
}
scratch_buffer = memalign(ASP_NAND_BLKSZ, ASP_NAND_BLK_ALIGNMENT);
paddr32 = VADDR_TO_PADDR32(scratch_buffer);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_READ_BAND;
command->tunnel.options.debug_epr_info.band = band;
command->tunnel.options.debug_epr_info.cell_type = cell_type;
command->tunnel.options.debug_epr_info.num_stripes = num_stripes;
command->flags.all = 0;
command->flags.noAesKey = 1;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = ASP_NAND_BLKSZ;
start_ticks = system_time();
while(1)
{
status = asp_send_command(command->tag);
printf("Finished Band read with status: %d\n", status);
if (status != ASPPROTO_CMD_STATUS_SUCCESS)
{
asp_print_status_key();
dprintf(DEBUG_CRITICAL, "ERROR: Could not read band.\n\n");
return -1;
}
pages_read += command->tunnel.options.debug_epr_info.output_value;
if(0 == seconds)
{
break;
}
else if(time_has_elapsed(start_ticks, (uint64_t)seconds * 1000000))
{
break;
}
}
if(0 != asp_get_perf_ticks(false, &perf_ticks0, 0))
{
return -1;
}
if(0 != asp_get_perf_ticks(false, &perf_ticks1, 1))
{
return -1;
}
if(0 != asp_get_link_clk_freq(false, &link_clk_freq))
{
return -1;
}
printf("Ch0: %llu bytes transferred in %llu ticks @ %llu Hz\n\n", (pages_read * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
perf_ticks0,
link_clk_freq);
printf("Ch1: %llu bytes transferred in %llu ticks @ %llu Hz\n\n", (pages_read * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
perf_ticks1,
link_clk_freq);
printf("Ch0: To calculate total throughput in MiB/s: (%llu * %llu) / (%llu * 1024 * 1024)\n\n", (pages_read * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks0);
printf("Ch1: To calculate total throughput in MiB/s: (%llu * %llu) / (%llu * 1024 * 1024)\n\n", (pages_read * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks1);
printf("Ch0: Time for each superstripe in us: (%llu / %d) / (%llu / 1000000)\n\n", perf_ticks0, (pages_in_block / join_program_pages), link_clk_freq);
printf("Ch1: Time for each superstripe in us: (%llu / %d) / (%llu / 1000000)\n\n", perf_ticks1, (pages_in_block / join_program_pages), link_clk_freq);
printf("Note: This is actual bus utilization with negligible overhead.\n");
return 0;
}
//Make sure these macros are the same in AppleStorageProcessor/src/aspcore/core/cmd.c
#define SLC_TEST_BAND_START 101
#define SLC_TEST_BAND_END 110
#define MBC_TEST_BAND_START 111
#define MBC_TEST_BAND_END 120
static bool prefill_complete_slc = false;
static bool prefill_complete_mbc = false;
static void asp_prefill_bands(aspproto_cell_type_t cell_type)
{
uint32_t band_idx;
uint32_t start_band;
uint32_t end_band;
if(cell_type == CELL_TYPE_IS_SLC)
{
if (prefill_complete_slc)
{
return;
}
start_band = SLC_TEST_BAND_START;
end_band = SLC_TEST_BAND_END;
}
else
{
if (prefill_complete_mbc)
{
return;
}
start_band = MBC_TEST_BAND_START;
end_band = MBC_TEST_BAND_END;
}
printf("Prefilling band: %d to band: %d\n", start_band, end_band);
for(band_idx = start_band; band_idx <= end_band; band_idx++)
{
if(-1 == asp_erase_band(band_idx, cell_type, false))
{
panic("error erasing band");
}
if(-1 == (asp_write_band(band_idx, cell_type, false, true, 0)))
{
panic("error writing band");
}
}
//Do prefill only once
if(cell_type == CELL_TYPE_IS_SLC)
{
prefill_complete_slc = true;
}
else
{
prefill_complete_mbc = true;
}
printf("Prefill complete\n");
}
/* This command does superpage reads in a hard coded range of bands.
It goes dip-by-dip and enqueues a page read. Each of that read may be either SLC or TLC/MLC.
NOTE: This command does not read actual data to host buffer.
It just overwrites a placeholder data buffer.
If you need actual data, use 'asp read'
If you need bitflips, use 'asp bonfirereadband'
The band range is first erassed and programmed.
*/
static int asp_read_mixed_superpage(uint32_t num_superpages, uint32_t num_stripes, uint32_t seconds)
{
aspproto_cmd_t * command;
uint8_t * scratch_buffer; //placeholder for putting in page data. every sector.
uint32_t paddr32;
uint64_t start_ticks;
uint32_t iterations = 0;
uint64_t perf_ticks0;
uint64_t perf_ticks1;
uint64_t link_clk_freq;
uint32_t pages_read = 0;
asp_prefill_bands(CELL_TYPE_IS_SLC);
if(asp_istlc(false))
{
asp_prefill_bands(CELL_TYPE_IS_TLC);
}
else
{
asp_prefill_bands(CELL_TYPE_IS_MLC);
}
printf("Will now start the reads\n");
if(0 != asp_reset_perf_ticks())
{
return -1;
}
scratch_buffer = memalign(ASP_NAND_BLKSZ, ASP_NAND_BLK_ALIGNMENT);
paddr32 = VADDR_TO_PADDR32(scratch_buffer);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_READ_MIXED_SUPERPAGE;
command->tunnel.options.debug_epr_info.num_sectors = num_superpages;
command->tunnel.options.debug_epr_info.num_stripes = num_stripes;
command->flags.all = 0;
command->flags.noAesKey = 1;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = ASP_NAND_BLKSZ;
start_ticks = system_time();
while(1)
{
iterations++;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not read band.\n\n");
return -1;
}
pages_read += command->tunnel.options.debug_epr_info.output_value;
if(0 == seconds)
{
break;
}
else if(time_has_elapsed(start_ticks, (uint64_t)seconds * 1000000))
{
break;
}
}
if(0 != asp_get_perf_ticks(false, &perf_ticks0, 0))
{
return -1;
}
if(0 != asp_get_perf_ticks(false, &perf_ticks1 , 1))
{
return -1;
}
if(0 != asp_get_link_clk_freq(false, &link_clk_freq))
{
return -1;
}
printf("Ch0: %u bytes transferred in %llu ticks @ %llu Hz\n\n", (pages_read * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
perf_ticks0,
link_clk_freq);
printf("Ch1: %u bytes transferred in %llu ticks @ %llu Hz\n\n", (pages_read * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
perf_ticks1,
link_clk_freq);
printf("Ch0: To calculate total throughput in MiB/s: (%u * %llu) / (%llu * 1024 * 1024)\n\n", (pages_read * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks0);
printf("Ch1: To calculate total throughput in MiB/s: (%u * %llu) / (%llu * 1024 * 1024)\n\n", (pages_read * nand_info.sec_per_page * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks1);
printf("Note: This is actual bus utilization with negligible overhead.\n");
return 0;
}
/* This command does random reads in a hard coded range of bands.
For every page address, 'sectors_per_page' is the number of sectors of that page that is read.
'num_sectors' is the number of page addresses sent to the NAND.
'num_stripes' is the number of stripes done in one read transaction.
'same_die' determines if in a stripe, pages on the same die are read, or not, or if its random.
NOTE: This command does not read actual data to host buffer.
It just overwrites a placeholder sector data buffer.
If you need actual data, use 'asp read'
If you need bitflips, use 'asp bonfirereadband'
The band range is first erassed and programmed.
*/
static int asp_read_random(aspproto_cell_type_t cell_type, uint32_t num_sectors, uint32_t num_stripes, uint32_t sectors_per_page, uint32_t same_die ,uint32_t seconds)
{
aspproto_cmd_t * command;
uint8_t * scratch_buffer; //placeholder for putting in page data. every sector.
uint32_t paddr32;
uint64_t start_ticks;
uint32_t iterations = 0;
uint64_t perf_ticks0;
uint64_t perf_ticks1;
uint64_t link_clk_freq;
if(sectors_per_page > nand_info.sec_per_page)
{
printf("unit_size (%d) should be < sectors per page (%d)\n", sectors_per_page, nand_info.sec_per_page);
return -1;
}
asp_prefill_bands(cell_type);
printf("Will now start the reads\n");
if(0 != asp_reset_perf_ticks())
{
return -1;
}
scratch_buffer = memalign(ASP_NAND_BLKSZ, ASP_NAND_BLK_ALIGNMENT);
paddr32 = VADDR_TO_PADDR32(scratch_buffer);
command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
command->op = ASPPROTO_CMD_CORE_DEBUG;
command->tunnel.core_opcode = CORE_DEBUG_READ_RANDOM;
command->tunnel.options.debug_epr_info.cell_type = cell_type;
command->tunnel.options.debug_epr_info.num_sectors = num_sectors;
command->tunnel.options.debug_epr_info.num_stripes = num_stripes;
command->tunnel.options.debug_epr_info.same_die = same_die;
command->tunnel.options.debug_epr_info.sectors_per_page = sectors_per_page;
command->flags.all = 0;
command->flags.noAesKey = 1;
command->tunnel.buffer_paddr = paddr32;
command->tunnel.bufferLen = ASP_NAND_BLKSZ;
if(cell_type == CELL_TYPE_IS_MIXED)
{
command->tunnel.options.flags.mixedMode = 1;
}
start_ticks = system_time();
while(1)
{
iterations++;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
dprintf(DEBUG_CRITICAL, "ERROR: Could not read band.\n\n");
return -1;
}
if(0 == seconds)
{
break;
}
else if(time_has_elapsed(start_ticks, (uint64_t)seconds * 1000000))
{
break;
}
}
if(0 != asp_get_perf_ticks(false, &perf_ticks0, 0))
{
return -1;
}
if(0 != asp_get_perf_ticks(false, &perf_ticks1, 1))
{
return -1;
}
if(0 != asp_get_link_clk_freq(false, &link_clk_freq))
{
return -1;
}
printf("Ch 0: %u bytes transferred in %llu ticks @ %llu Hz\n\n", (iterations * num_sectors * ASP_NAND_BLKSZ) / 2,
perf_ticks0,
link_clk_freq);
printf("Ch 1: %u bytes transferred in %llu ticks @ %llu Hz\n\n", (iterations * num_sectors * ASP_NAND_BLKSZ) / 2,
perf_ticks1,
link_clk_freq);
printf("Ch 0: To calculate total throughput in MiB/s: (%u * %llu) / (%llu * 1024 * 1024)\n\n", (iterations * num_sectors * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks0);
printf("Ch 1: To calculate total throughput in MiB/s: (%u * %llu) / (%llu * 1024 * 1024)\n\n", (iterations * num_sectors * ASP_NAND_BLKSZ) / 2,
link_clk_freq,
perf_ticks1);
printf("Ch 0: Time taken for %d 4k sector in us: (%llu / %d) / (%llu / 1000000)\n\n", sectors_per_page, perf_ticks0, ((iterations * num_sectors) / 2), link_clk_freq); // Divide by 2 is because 2 buses are active at a time
printf("Ch 1: Time taken for %d 4k sector in us: (%llu / %d) / (%llu / 1000000)\n\n", sectors_per_page, perf_ticks1, ((iterations * num_sectors) / 2), link_clk_freq); // Divide by 2 is because 2 buses are active at a time
printf("Note: This is actual bus utilization with negligible overhead.\n");
return 0;
}
static int asp_bonfire_read_band(uint32_t band, aspproto_cell_type_t cell_type, bool stats)
{
uint8_t *statsBuf = NULL;
uint32_t paddr32 = 0;
aspproto_cmd_t * command;
int status;
uint32_t sectors_per_band;
if((band > asp.lastBand))
{
printf("Enter valid band: <= %d.\n", asp.lastBand);
return -1;
}
sectors_per_band = (uint32_t) asp_sectors_per_band(band, false);
if(0 == sectors_per_band)
{
printf("Cannot program band - It has 0 good sectors.\n");
return -1;
}
statsBuf = memalign(sectors_per_band * nand_info.bytes_per_sec_meta, ASP_NAND_BLK_ALIGNMENT);
if(!statsBuf)
{
printf("could not allocate stats buffer\n");
return -1;
}
memset(statsBuf, 0xFF, sectors_per_band * nand_info.bytes_per_sec_meta);
if(stats)
{
paddr32 = VADDR_TO_PADDR32(statsBuf);
}
command = asp_get_cmd_for_tag(ASP_TAG_NAND);
command->flags.all = 0;
command->flags.noAesKey = 1;
command->op = ASPPROTO_CMD_BONFIRE_READ;
command->bonfire_info.num_vpn = sectors_per_band / nand_info.sec_per_page;
if(cell_type == CELL_TYPE_IS_SLC) {
command->bonfire_info.start_vpn = band * (nand_info.sec_per_full_band_slc / nand_info.sec_per_page);
} else {
command->bonfire_info.start_vpn = band * (nand_info.sec_per_full_band / nand_info.sec_per_page);
}
command->bonfire_info.stats_buf32 = paddr32;
command->bonfire_info.cell_type = cell_type;
status = asp_send_command(command->tag);
if(stats)
{
printf("Sector Stats:\n");
uint8_t *page_cursor = statsBuf;
uint8_t *buf_stats_cursor = page_cursor;
bool zero_flips = true;
uint32_t idx = 0, pageIdx = 0;
uint32_t sectors_per_page = 16;
for(pageIdx = 0; pageIdx < command->bonfire_info.num_vpn; pageIdx++)
{
buf_stats_cursor = page_cursor;
for(idx = 0; idx < sectors_per_page; idx++)
{
if(*buf_stats_cursor != 0)
{
zero_flips = false;
break;
}
buf_stats_cursor++;
}
if(!zero_flips)
{
buf_stats_cursor = page_cursor ;
printf("vpn: %d: ", pageIdx);
for(idx = 0; idx < sectors_per_page; idx++)
{
printf("%02X ", *buf_stats_cursor);
buf_stats_cursor++;
}
printf("\n");
}
page_cursor = page_cursor + 16;
}
}
printf("Finished Band read with status: %d\n", status);
asp_print_status_key();
free(statsBuf);
return 0;
}
static int asp_nand_read_id(void)
{
uint32_t i = 0;
printf("\nChip ID:\n");
for(i = 0; i < nand_info.num_bus; i++)
{
printf("Bus %d: %02X %02X %02X %02X %02X %02X\n",
i,
asp.chip_id[i][0], asp.chip_id[i][1], asp.chip_id[i][2],
asp.chip_id[i][3], asp.chip_id[i][4], asp.chip_id[i][5]);
}
printf("\nMfg ID:\n");
for(i = 0; i < nand_info.num_bus; i++)
{
printf("Bus %d: %02X %02X %02X %02X %02X %02X\n",
i,
asp.mfg_id[i][0], asp.mfg_id[i][1], asp.mfg_id[i][2],
asp.mfg_id[i][3], asp.mfg_id[i][4], asp.mfg_id[i][5]);
}
asp_nand_get_uid();
return 0;
}
void asp_test_write_bdev(uint32_t start_lba, int num_lbas)
{
uint8_t *write_ptr;
int result;
if(!asp.writable)
{
panic("Need to make system writable. Execute 'asp setwritable' first\n");
}
printf("Going to do a bdev write\n");
write_ptr = malloc(num_lbas * ASP_NAND_BLKSZ);
if(!write_ptr)
{
panic("Could not allocate memory\n");
}
asp.state = ASP_STATE_READY;
memset(write_ptr, TEST_DATA_PATTERN, num_lbas * ASP_NAND_BLKSZ);
result = asp_write_block(asp_nand_dev, write_ptr, start_lba, num_lbas);
printf("bdev write returned %d\n", result);
if(num_lbas != result)
{
panic("bdev write returned %d instead of %d\n", result, num_lbas);
}
free(write_ptr);
}
void asp_test_read_bdev(uint32_t start_lba, int num_lbas)
{
int result;
uint8_t *read_ptr = (uint8_t *) env_get_uint("loadaddr", DEFAULT_LOAD_ADDRESS);
int i;
printf("Going to do a bdev read\n");
if (!security_allow_memory(read_ptr, num_lbas * ASP_NAND_BLKSZ))
{
panic("Permission Denied\n");
}
asp.state = ASP_STATE_READY;
memset(read_ptr, WATERMARK_PATTERN, num_lbas * ASP_NAND_BLKSZ);
result = asp_read_block(asp_nand_dev, read_ptr, start_lba, num_lbas);
if(num_lbas != result)
{
panic("bdev read returned %d instead of %d\n", result, num_lbas);
}
for(i = 0; i < num_lbas * ASP_NAND_BLKSZ; i++)
{
if(TEST_DATA_PATTERN != read_ptr[i])
{
panic("expected data: 0x%X, read data: 0x%X, byte offset in buffer: %d\n", TEST_DATA_PATTERN, read_ptr[i], i);
}
}
printf("bdev read returned %d\n", result);
}
void asp_test_waterfall_start(void)
{
aspproto_cmd_t * command = asp_get_cmd_for_tag(ASP_TAG_GENERIC);
printf("Going to start waterfall\n");
command->op = ASPPROTO_CMD_WATERFALL_START;
if (asp_send_command(command->tag) != ASPPROTO_CMD_STATUS_SUCCESS)
{
panic("ASP waterfall table start Failed\n");
}
}
bool asp_test_waterfall(void)
{
uint32_t lba = 0;
int count = 256;
printf("Opening ASP\n");
if (!asp_send_open())
{
dprintf(DEBUG_CRITICAL, "Unable to open ASP\n");
return false;
}
printf("Setting writable\n");
if (!asp_set_writable())
{
asp.state = ASP_STATE_ERROR_NOT_WRITABLE;
printf("Unable to set writable\n");
return false;
}
printf("Going to util format\n");
if (!asp_format(ASP_FORMAT_UTIL))
{
printf("Failed to do util format\n");
return false;
}
asp_get_geometry();
asp_test_write_bdev(lba, count);
asp_test_waterfall_start();
asp_test_read_bdev(lba, count);
lba += count;
asp_test_write_bdev(lba, count);
asp_test_waterfall_start();
asp_test_read_bdev(lba, count);
lba += count;
asp_test_write_bdev(lba, count);
asp_test_waterfall_start();
asp_test_read_bdev(lba, count);
return true;
}
static int do_asp(int argc, struct cmd_arg *args)
{
void *buffer;
size_t buffer_len;
int err = 0;
const char *cmd;
if (argc < 2)
{
printf("Not enough arguments\n");
usage();
return 0;
}
buffer = (void *) env_get_uint("loadaddr", DEFAULT_LOAD_ADDRESS);
buffer_len = (size_t) env_get_uint("filesize", 0);
cmd = args[1].str;
if (asp.state == ASP_STATE_ERROR_CRITICAL)
{
printf("Error - ASP has failed to initialize successfully!\n");
return -1;
}
if (!strcmp(args[1].str, "sync"))
{
if (!asp_sync())
{
printf("Failed to sync ASP\n");
return -1;
}
}
#if defined(ASP_ENABLE_NEURALIZE) && ASP_ENABLE_NEURALIZE
else if (!strcmp(args[1].str, "neuralize"))
{
if (!asp_set_writable())
{
asp.state = ASP_STATE_ERROR_NOT_WRITABLE;
printf("Unable to neuralize device; media is not writable\n");
return -1;
}
if (!asp_neuralize())
{
printf("Failed to neuralize\n");
asp.state = ASP_STATE_ERROR_NEURALIZE;
return -1;
}
}
#endif
else if (!strcmp(cmd, "info"))
{
printf("nand info: \n");
err = asp_nand_info();
}
else if (!strcmp(cmd, "readid"))
{
err = asp_nand_read_id();
}
else if (!strcmp(cmd, "istlc"))
{
err = asp_istlc(true);
}
else if(!strcmp(cmd, "getlinkclkfreq"))
{
err = asp_get_link_clk_freq(true, NULL);
}
else if(!strcmp(cmd, "resetperfticks"))
{
err = asp_reset_perf_ticks();
}
else if(!strcmp(cmd, "getperfticks"))
{
err = asp_get_perf_ticks(true, NULL, 0);
err = asp_get_perf_ticks(true, NULL, 1);
}
else if(!strcmp(cmd, "printslcbonfirebands"))
{
err = asp_print_slc_bonfire_bands();
}
else if (!strcmp(cmd, "test_scratchpad"))
{
err = asp_test_scratchpad();
}
else if (!strcmp(cmd, "dies_in_parallel") && argc>=3)
{
if (argc==3)
{
err = asp_set_dies_in_parallel(args[2].u,args[2].u,
args[2].u,args[2].u,
args[2].u,args[2].u,
args[2].u,args[2].u,
CORE_POWERSTATE_HIGH_POWER);
asp_set_power_state(CORE_POWERSTATE_HIGH_POWER);
}
else if (argc == 10)
{
err = asp_set_dies_in_parallel(args[2].u,args[3].u,
args[4].u,args[5].u,
args[6].u,args[7].u,
args[8].u,args[9].u,
CORE_POWERSTATE_HIGH_POWER);
asp_set_power_state(CORE_POWERSTATE_HIGH_POWER);
}
else
{
usage();
return 0;
}
}
else if (!strcmp(cmd, "set_photoflow_slc"))
{
err = asp_set_photoflow_mode(CORE_FLOW_MODE_SLC);
}
else if (!strcmp(cmd, "set_photoflow_mlc"))
{
err = asp_set_photoflow_mode(CORE_FLOW_MODE_MLC);
}
else if (!strcmp(cmd, "enable_bg"))
{
err = asp_enable_bg();
}
else if (!strcmp(cmd, "disable_bg"))
{
err = asp_disable_bg();
}
else if (!strcmp(cmd, "debug_counter_supported"))
{
if(argc < 3)
{
usage();
return 0;
}
err = debug_counter_supported(args[2].u);
}
else if (!strcmp(cmd, "get_debug_counter"))
{
if(argc < 3)
{
usage();
return 0;
}
err = get_debug_counter(args[2].u, (uint8_t *)buffer);
}
else if (!strcmp(cmd, "reset_debug_counter"))
{
if(argc < 3)
{
usage();
return 0;
}
err = reset_debug_counter(args[2].u);
}
else if (!strcmp(cmd, "bonfirereadband"))
{
if(argc < 5)
{
usage();
return 0;
}
err = asp_bonfire_read_band(args[2].u, args[3].u, args[4].u);
}
else if (!strcmp(cmd, "readband"))
{
if(argc < 5)
{
usage();
return 0;
}
if(argc == 5)
{
err = asp_read_band(args[2].u, args[3].u, args[4].u, 0);
}
else
{
err = asp_read_band(args[2].u, args[3].u, args[4].u, args[5].u);
}
}
else if (!strcmp(cmd, "readrandom"))
{
if(argc < 7)
{
usage();
return 0;
}
if(argc == 7)
{
err = asp_read_random(args[2].u, args[3].u, args[4].u, args[5].u, args[6].u, 0);
}
else
{
err = asp_read_random(args[2].u, args[3].u, args[4].u, args[5].u, args[6].u, args[7].u);
}
}
else if (!strcmp(cmd, "readmixedsuperpage"))
{
if(argc < 4)
{
usage();
return 0;
}
if(argc == 4)
{
err = asp_read_mixed_superpage(args[2].u, args[3].u, 0);
}
else
{
err = asp_read_mixed_superpage(args[2].u, args[3].u, args[4].u);
}
}
else if (!strcmp(cmd, "v2p"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_nand_get_addr(args[2].u, NULL, NULL, true);
}
else if (!strcmp(cmd, "l2dbp"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_l2dbp(args[2].u);
}
else if (!strcmp(cmd, "r2cbp"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_r2cbp(args[2].u);
}
else if (!strcmp(cmd, "cbp2r"))
{
if(argc < 5)
{
usage();
return 0;
}
err = asp_cbp2r(args[2].u, args[3].u, args[4].u);
}
else if (!strcmp(cmd, "dbp2r"))
{
if(argc < 5)
{
usage();
return 0;
}
err = asp_dbp2r(args[2].u, args[3].u, args[4].u);
}
else if (!strcmp(cmd, "secperband"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_sectors_per_band(args[2].u, true);
}
else if (!strcmp(cmd, "getburnincode"))
{
err = asp_get_burnin_code();
}
else if (!strcmp(cmd, "setTLCwritestripes"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_set_tlcwritestripes(args[2].u);
}
else if (!strcmp(cmd, "getlastfailure"))
{
err = asp_get_last_failure();
}
else if (!strcmp(cmd, "bbt"))
{
err = asp_get_bbt();
}
else if (!strcmp(cmd, "dm"))
{
err = asp_get_dm();
}
else if (!strcmp(cmd, "waterfall_size"))
{
err = asp_get_waterfall_table_size();
}
else if (!strcmp(cmd, "waterfall"))
{
err = asp_get_waterfall_table((uint8_t *)buffer);
}
else if (!strcmp(cmd, "devparam"))
{
err = asp_print_ppn_parameters();
}
else if (!strcmp(cmd, "readverify"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_read_verify(args[2].u);
}
else if (!strcmp(cmd, "dipinfo"))
{
err = asp_get_all_dip_info();
}
else if (!strcmp(cmd, "read"))
{
if(argc < 9)
{
usage();
return 0;
}
err = asp_physical_read(args[2].u, args[3].u, args[4].u, args[5].u, args[6].u, args[7].u, args[8].u, (uint8_t *)buffer, true, NULL);
}
else if (!strcmp(cmd, "readpagemeta"))
{
if(argc < 6)
{
usage();
return 0;
}
err = asp_read_pagemeta(args[2].u, args[3].u, args[4].u, args[5].u, (uint8_t *)buffer);
}
else if (!strcmp(cmd, "readbandmeta"))
{
if(argc < 4)
{
usage();
return 0;
}
err = asp_read_bandmeta(args[2].u, (uint8_t *)buffer, args[3].u);
}
else if (!strcmp(cmd, "bandstat"))
{
err = asp_get_band_stats();
}
else if (!strcmp(cmd, "disableuid"))
{
err = asp_disable_uid();
}
else if (!strcmp(cmd, "dumpblog"))
{
if(argc < 5)
{
usage();
return 0;
}
err = asp_dump_blog(args[2].u, (uint8_t *)buffer, args[3].u, args[4].u);
}
else if (!strcmp(cmd, "vthsweep"))
{
if(argc < 6)
{
usage();
return 0;
}
err = asp_vth_sweep(args[2].u, args[3].u, args[4].u, args[5].str);
}
else if (!strcmp(cmd, "rma_delete"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_rma_delete(args[2].u);
}
else if (!strcmp(cmd, "rma_configure"))
{
if(argc < 4)
{
usage();
return 0;
}
err = asp_rma_configure(args[2].u, args[3].u);
}
else if (!strcmp(cmd, "rma_set"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_rma_set(args[2].u);
err &= asp_recover();
}
else if (!strcmp(cmd, "rma_get"))
{
if(argc < 3)
{
usage();
return 0;
} else if(argc == 3) {
err = asp_rma_get(args[2].u, NULL, NULL);
} else {
err = asp_rma_get(args[2].u, args[3].str, NULL);
}
}
else if (!strcmp(cmd, "ppn_recover"))
{
err = asp_recover();
}
else if (!strcmp(cmd, "ppn_get_calibration"))
{
if(argc < 2)
{
usage();
return 0;
} else if(argc == 2) {
err = asp_ppn_get_calibration(NULL);
} else {
err = asp_ppn_get_calibration(args[2].str);
}
}
else if (!strcmp(cmd, "testbdevread"))
{
if(argc < 4)
{
usage();
return 0;
}
asp_test_read_bdev(args[2].u, args[3].u);
}
#if ASP_ENABLE_WRITES
else if (!strcmp(args[1].str, "utilFormat"))
{
if (!asp_format(ASP_FORMAT_UTIL))
{
printf("Failed to format media\n");
return -1;
}
return 0;
}
else if (!strcmp(args[1].str, "lbaFormat"))
{
if (!asp_format(ASP_FORMAT_LBA))
{
printf("Failed to format media\n");
return -1;
}
return 0;
}
else if (!strcmp(args[1].str, "register"))
{
if(!asp.writable)
{
printf("Need to make system writable. Execute 'asp setwritable' first\n");
return -1;
}
if (!asp_get_geometry())
{
asp.state = ASP_STATE_ERROR_CRITICAL;
printf("Unable to register device; media is unformatted\n");
return -1;
}
if (!asp_create_block_device(ASP_NVRAM))
{
asp.state = ASP_STATE_ERROR_NOT_REGISTERED;
printf("Failed to create ASP_NVRAM block device\n");
return -1;
}
if (!asp_create_block_device(ASP_NAND))
{
asp.state = ASP_STATE_ERROR_NOT_REGISTERED;
printf("Failed to create ASP_NAND block device\n");
return -1;
}
if (!asp_create_block_device(ASP_FIRMWARE))
{
asp.state = ASP_STATE_ERROR_NOT_REGISTERED;
printf("Failed to create ASP_FIRMWARE block device\n");
return -1;
}
if (!asp_create_block_device(ASP_LLB))
{
asp.state = ASP_STATE_ERROR_NOT_REGISTERED;
printf("Failed to create ASP_LLB block device\n");
return -1;
}
if (!asp_create_block_device(ASP_EFFACEABLE))
{
asp.state = ASP_STATE_ERROR_NOT_REGISTERED;
printf("Failed to create ASP_EFFACEABLE block device\n");
return -1;
}
if (!asp_create_block_device(ASP_SYSCFG))
{
asp.state = ASP_STATE_ERROR_NOT_REGISTERED;
printf("Failed to create ASP_SYSCFG block device\n");
return -1;
}
if (!asp_create_block_device(ASP_PANICLOG))
{
asp.state = ASP_STATE_ERROR_NOT_REGISTERED;
printf("Failed to create ASP_PANICLOG block device\n");
return -1;
}
asp.state = ASP_STATE_READY;
partition_scan_and_publish_subdevices("asp_nand");
return 0;
}
else if (!strcmp(cmd, "bonfirewriteband"))
{
bool random_pattern = true;
uint32_t data_pattern = 0;
if(argc < 4)
{
usage();
return 0;
}
if(argc > 4)
{
data_pattern = args[4].u;
random_pattern = false;
}
err = asp_bonfire_write_band(args[2].u, args[3].u, random_pattern, data_pattern);
}
else if (!strcmp(cmd, "bonfireeraseband"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_bonfire_erase_band(args[2].u);
}
else if (!strcmp(cmd, "groupaslc"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_bonfire_group_a_slc(args[2].u);
}
else if (!strcmp(cmd, "groupbslc"))
{
err = asp_bonfire_group_b_slc();
}
else if (!strcmp(cmd, "ungroup"))
{
err = asp_bonfire_ungroup();
}
else if (!strcmp(cmd, "writeband"))
{
bool random_pattern = true;
uint32_t data_pattern = 0;
if(argc < 4)
{
usage();
return 0;
}
if(argc > 4)
{
data_pattern = args[4].u;
random_pattern = false;
}
err = asp_write_band(args[2].u, args[3].u, true, random_pattern, data_pattern);
}
else if (!strcmp(cmd, "eploop"))
{
bool random_pattern = true;
uint32_t data_pattern = 0;
if(argc < 5)
{
usage();
return 0;
}
if(argc > 5)
{
data_pattern = args[5].u;
random_pattern = false;
}
err = asp_eploop_band(args[2].u, args[3].u, args[4].u, random_pattern, data_pattern);
}
else if (!strcmp(cmd, "eraseband"))
{
if(argc < 4)
{
usage();
return 0;
}
err = asp_erase_band(args[2].u, args[3].u, true);
}
else if (!strcmp(cmd, "testwaterfall"))
{
err = asp_test_waterfall();
}
else if (!strcmp(cmd, "testbdevwrite"))
{
if(argc < 4)
{
usage();
return 0;
}
asp_test_write_bdev(args[2].u, args[3].u);
}
else if (!strcmp(cmd, "setwritable"))
{
err = asp_set_writable();
}
else if (!strcmp(cmd, "setburnincode"))
{
if(argc < 3)
{
usage();
return 0;
}
err = asp_set_burnin_code(args[2].u);
}
#endif // ASP_ENABLE_WRITES
else
{
printf("invalid command\n");
usage();
err = -1;
}
return err;
}
MENU_COMMAND_DEVELOPMENT(asp, do_asp, "ASP maintenance and failure analysis commands", NULL);
#endif // DEBUG_BUILD && WITH_MENU
#if !RELEASE_BUILD && WITH_MENU
static bool find_blobs(void *src_buffer, UInt32 src_length, void **fw, UInt32 *fw_len)
{
DERReturn drtn;
DERSequence rdn;
DERItem seq, top = { src_buffer, src_length };
DERDecodedInfo topDecode, keyC, valC;
if (DR_Success != DERDecodeItem(&top, &topDecode)) {
printf("couldn't crack top level\n");
return false;
}
if ((ASN1_CONSTRUCTED|ASN1_APPLICATION) != ((ASN1_CONSTRUCTED|ASN1_APPLICATION) & topDecode.tag)) {
printf("top should be cons app\n");
return false;
}
seq.length = topDecode.content.length;
seq.data = topDecode.content.data;
if (DR_Success != DERDecodeSeqContentInit(&seq, &rdn)) {
printf("could not initialize DER decode\n");
return false;
}
while ((drtn = DERDecodeSeqNext(&rdn, &keyC)) == DR_Success) {
// Got key UTF8-str, check
if (ASN1_UTF8_STRING != keyC.tag) {
printf("key invalid: %lld\n", keyC.tag);
return false;
}
// Get value Octet-str
if (DR_Success != ((drtn = DERDecodeSeqNext(&rdn, &valC)))) {
printf("no value found for key %lld\n", keyC.tag);
return false;
}
// Check value tag
if (ASN1_OCTET_STRING != valC.tag) {
printf("value invalid: %lld\n", valC.tag);
return false;
}
// Decode
if ((6 == keyC.content.length) && (0 == strncmp((char*)keyC.content.data, "ppn-fw", 6))) {
*fw = valC.content.data;
*fw_len = valC.content.length;
} else {
printf("unknown key\n");
return false;
}
}
return true;
}
static int do_ppnfw(int argc, struct cmd_arg *args)
{
addr_t buf_ptr;
size_t buf_len;
void *fw_buffer = NULL;
uint32_t fw_length;
if (argc > 3)
{
return -1;
}
buf_ptr = (argc > 1) ? args[1].u : env_get_uint("loadaddr", DEFAULT_LOAD_ADDRESS);
buf_len = (argc > 2) ? args[2].u : env_get_uint("filesize", 0);
if (!security_allow_memory((void*)buf_ptr, buf_len))
{
return -2;
}
fw_buffer = NULL;
fw_length = 0;
find_blobs((void*)buf_ptr, buf_len, &fw_buffer, &fw_length);
if ((NULL == fw_buffer))
{
printf("couldn't find the fw payload\n");
return -3;
}
return asp_update_ppn_firmware(fw_buffer, fw_length);
}
MENU_COMMAND_DEVELOPMENT(ppnfw, do_ppnfw, "Update PPN controller firmware", NULL);
#endif // #if !RELEASE_BUILD && WITH_MENU