first and last commit

This commit is contained in:
Chris Nutter committed 2023-07-08 13:03:17 -07:00
commit 1bf4efd552
2612 files changed
+636500

No files matched your search

+528
View File
@@ -0,0 +1,528 @@
/*
* Copyright (C) 2011-2013 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <debug.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <sys.h>
#include <sys/task.h>
#include <platform.h>
#include <platform/soc/hwclocks.h>
#include <platform/soc/hwregbase.h>
#include <drivers/anc_boot.h>
#include "anc_bootrom.h"
#include "anc_bootrom_private.h"
#define NAND_BOOTBLOCK_OFFSET 2
#define NAND_BOOT_MIN_WRITE_CYCLE_NANOS 50
#define NAND_BOOT_MIN_READ_CYCLE_NANOS 50
#define NAND_BOOT_MIN_CA_CYCLE_NANOS 50
#define NAND_BOOT_MIN_WRITE_SETUP_NANOS 20
#define NAND_BOOT_MIN_WRITE_HOLD_NANOS 20
#define NAND_BOOT_MIN_READ_SETUP_NANOS 20
#define NAND_BOOT_MIN_READ_HOLD_NANOS 20
#define NAND_BOOT_MIN_CA_SETUP_NANOS 20
#define NAND_BOOT_MIN_CA_HOLD_NANOS 20
#define T_CERDY_CYCLES 2675 // 50us @ 53.5MHz
#define ANC_TIMEOUT_MICROS 1000000UL // Applies to reg status waits - not tRST timeout
#define T_PURST_USEC (10 * 1000) // 10ms
anc_t g_boot_anc[ANC_BOOT_CONTROLLERS];
static bool anc_boot_init_minimal(anc_t *anc, uint32_t interface);
static void anc_configure_pins(void);
static void anc_boot_reset(anc_t *anc);
static bool anc_boot_nand_reset(int busesToReset);
static int anc_boot_read_boot_pages(uint32_t valid_controllers, void *data, size_t *size);
static void anc_boot_set_init_timings(anc_t *anc);
int anc_bootrom_init(bool reset_devices, int resetMode)
{
int i;
int busesToReset;
if (resetMode == ANC_BOOT_MODE_RESET_ALL_CONTROLLERS) {
busesToReset = ANC_BOOT_CONTROLLERS;
} else if (resetMode == ANC_BOOT_MODE_RESET_ONE_CONTROLLER) {
busesToReset = 1;
} else {
return -1;
}
anc_configure_pins();
for (i = 0; i < ANC_BOOT_CONTROLLERS; i++)
{
if (!anc_boot_init_minimal(&g_boot_anc[i], i))
{
dprintf(DEBUG_INFO, "failed: unable to initialize ANC%d\n", i);
return -1;
}
}
if (reset_devices && !anc_boot_nand_reset(busesToReset))
{
dprintf(DEBUG_INFO, "failed: unable to reset NAND devices\n");
return -1;
}
return 0;
}
static void anc_configure_pins()
{
uint32_t *npl = (uint32_t *)ANS_PPN_N_PL_BASE_ADDR;
anc_npl_wr(npl, R_PPNNPL_PPN0_CLE, M_PPNNPL_PPN0_CLE_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN0_ALE, M_PPNNPL_PPN0_ALE_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN0_REN, M_PPNNPL_PPN0_REN_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN0_WEN, M_PPNNPL_PPN0_WEN_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN0_CEN, M_PPNNPL_PPN0_CEN_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN0_IO, (V_PPNNPL_PPN0_IO_INPUT_ENABLE(0xFF) |
V_PPNNPL_PPN0_IO_PULL_ENABLE(0xFF)));
#if ANC_TOGGLE_SUPPORTED == 1
anc_npl_wr(npl, R_PPNNPL_PPN0_DQS, (M_PPNNPL_PPN0_DQS_INPUT_ENABLE |
M_PPNNPL_PPN0_DQS_PULL_ENABLE));
#endif
anc_npl_wr(npl, R_PPNNPL_PPN0_DS, V_PPNNPL_PPN0_DS_DRIVE_STRENGTH(ANC_PPNNPL_DS_DRIVE_STRENGTH));
anc_npl_wr(npl, R_PPNNPL_CONFIG, M_PPNNPL_CONFIG_AUTO_DISABLE_IO_PULLDOWN);
#if APPLICATION_SECUREROM && defined(ANC_PPNNPL_INPUT_SELECT_SCHMITT)
// <rdar://problem/15132151> N56, High power on 1V8_H7P_IO - rail - PPN_INPUT_SELECT
anc_npl_wr(npl, R_PPNNPL_PPN0_INPUT_SELECT, M_PPNNPL_PPN0_INPUT_SELECT_INPUT_SELECT |
M_PPNNPL_PPN0_INPUT_SELECT_INPUT_SELECT_SCHMITT);
#endif
#if ANC_BOOT_CONTROLLERS > 1
anc_npl_wr(npl, R_PPNNPL_PPN1_CLE, M_PPNNPL_PPN1_CLE_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN1_ALE, M_PPNNPL_PPN1_ALE_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN1_REN, M_PPNNPL_PPN1_REN_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN1_WEN, M_PPNNPL_PPN1_WEN_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN1_CEN, M_PPNNPL_PPN1_CEN_OUTPUT_ENABLE);
anc_npl_wr(npl, R_PPNNPL_PPN1_IO, (V_PPNNPL_PPN1_IO_INPUT_ENABLE(0xFF) |
V_PPNNPL_PPN1_IO_PULL_ENABLE(0xFF)));
#if ANC_TOGGLE_SUPPORTED == 1
anc_npl_wr(npl, R_PPNNPL_PPN1_DQS, (M_PPNNPL_PPN1_DQS_INPUT_ENABLE |
M_PPNNPL_PPN1_DQS_PULL_ENABLE));
#endif
anc_npl_wr(npl, R_PPNNPL_PPN1_DS, V_PPNNPL_PPN1_DS_DRIVE_STRENGTH(ANC_PPNNPL_DS_DRIVE_STRENGTH));
#if APPLICATION_SECUREROM && defined(ANC_PPNNPL_INPUT_SELECT_SCHMITT)
// <rdar://problem/15132151> N56, High power on 1V8_H7P_IO - rail - PPN_INPUT_SELECT
anc_npl_wr(npl, R_PPNNPL_PPN1_INPUT_SELECT, M_PPNNPL_PPN1_INPUT_SELECT_INPUT_SELECT |
M_PPNNPL_PPN1_INPUT_SELECT_INPUT_SELECT_SCHMITT);
#endif
#endif
}
static bool anc_boot_init_minimal(anc_t *anc, uint32_t interface)
{
#if ANC_BOOT_CONTROLLERS > 1
anc->regs = (uint32_t *)((interface == 0) ? ANS_ANC_0_BASE_ADDR : ANS_ANC_1_BASE_ADDR);
#else
anc->regs = (uint32_t *)ANS_ANC_BASE_ADDR;
if (interface > 0)
{
return false;
}
#endif
if (interface == 0)
{
clock_gate(CLK_ANS, true);
#ifdef WITH_ANS_DLL
clock_gate(CLK_ANS_DLL, true);
#endif
}
anc_boot_reset(anc);
anc_boot_set_init_timings(anc);
anc->bus_id = interface;
anc->initialized = true;
anc->pages_per_block = NAND_BOOT_PAGES_PER_BLOCK;
anc->bytes_per_meta = NAND_BOOT_BYTES_PER_META;
anc->meta_words = NAND_BOOT_BYTES_PER_META / sizeof(uint32_t);
anc->bytes_per_page = NAND_BOOT_BYTES_PER_PAGE;
anc->logical_page_size = NAND_BOOT_LOGICAL_PAGE_SIZE;
anc->lbas_per_page = NAND_BOOT_BYTES_PER_PAGE / NAND_BOOT_LOGICAL_PAGE_SIZE;
return true;
}
static void anc_boot_reset(anc_t *anc)
{
anc_wr(anc, R_ANC_LINK_CONFIG, (V_ANC_LINK_CONFIG_ENABLE_CRC(0) |
V_ANC_LINK_CONFIG_DDR_MODE(0)));
anc_wr(anc, R_ANC_LINK_CMD_TIMEOUT, (V_ANC_LINK_CMD_TIMEOUT_VALUE(60 * 1000 * 1000) |
V_ANC_LINK_CMD_TIMEOUT_ENABLE(1)));
anc_wr(anc, R_ANC_DMA_CONTROL, V_ANC_DMA_CONTROL_START(1));
anc_wr(anc, R_ANC_LINK_CONTROL, V_ANC_LINK_CONTROL_START(1));
}
static void anc_boot_set_init_timings(anc_t *anc)
{
// All timings are fixed and recommend by DV: <rdar://problem/11051297>
anc_wr(anc, R_ANC_LINK_SDR_DATA_TIMING, (V_ANC_LINK_SDR_DATA_TIMING_REN_HOLD_TIME(ANC_LINK_SDR_REN_HOLD_TIME) |
V_ANC_LINK_SDR_DATA_TIMING_REN_SETUP_TIME(ANC_LINK_SDR_REN_SETUP_TIME) |
V_ANC_LINK_SDR_DATA_TIMING_WEN_HOLD_TIME(ANC_LINK_SDR_WEN_HOLD_TIME) |
V_ANC_LINK_SDR_DATA_TIMING_WEN_SETUP_TIME(ANC_LINK_SDR_REN_SETUP_TIME)));
anc_wr(anc, R_ANC_LINK_COMMAND_ADDRESS_PULSE_TIMING,
(V_ANC_LINK_COMMAND_ADDRESS_PULSE_TIMING_CA_HOLD_TIME(ANC_LINK_CMD_ADDR_PULSE_TIMING_CA_HOLD_TIME) |
V_ANC_LINK_COMMAND_ADDRESS_PULSE_TIMING_CA_SETUP_TIME(ANC_LINK_CMD_ADDR_PULSE_TIMING_CA_SETUP_TIME)));
anc_wr(anc, R_ANC_LINK_READ_STATUS_CONFIG, (V_ANC_LINK_READ_STATUS_CONFIG_POSITION(0x40) |
V_ANC_LINK_READ_STATUS_CONFIG_POLARITY(0x40) |
V_ANC_LINK_READ_STATUS_CONFIG_STATUS_CAPTURE_DELAY(10) |
V_ANC_LINK_READ_STATUS_CONFIG_READY_SAMPLE_DELAY(8)));
anc_wr(anc, R_ANC_LINK_SDR_TIMING,
(V_ANC_LINK_SDR_TIMING_DATA_CAPTURE_DELAY(ANC_LINK_SDR_DATA_CAPTURE_DELAY) |
V_ANC_LINK_SDR_TIMING_CLE_ALE_SETUP_TIME(ANC_LINK_SDR_CLE_ALE_SETUP_TIME) |
V_ANC_LINK_SDR_TIMING_CLE_ALE_HOLD_TIME(0)));
}
static bool anc_boot_nand_reset(int busesToReset)
{
bool ret;
int controller;
anc_t *anc;
const uint32_t int_mask = (M_ANC_CHAN_INT_STATUS_LINK_CMD_FLAG |
M_ANC_CHAN_INT_STATUS_READ_STATUS_ERR_RESPONSE |
M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT);
uint32_t int_status;
utime_t current_time;
// Wait 10ms after SoC reset to ensure we don't violate tPURST
current_time = system_time();
if (current_time < T_PURST_USEC)
{
task_sleep(T_PURST_USEC - current_time);
}
// First, issue a reset on all CEs attached to each bus
for (controller = 0; controller < busesToReset; controller++)
{
anc = &g_boot_anc[controller];
anc_boot_put_link_command(anc, LINK_COMMAND__CE(1));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(T_CERDY_CYCLES));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__RESET));
anc_boot_put_link_command(anc, LINK_COMMAND__CE(0));
}
// Wait for bus 0, ce 0 to complete
anc = &g_boot_anc[0];
anc_boot_put_link_command(anc, LINK_COMMAND__CE(1));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__LOW_POWER_READ_STATUS));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(1));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_STATUS(0, PPN_LOW_POWER_STATUS__READY, PPN_LOW_POWER_STATUS__READY));
anc_boot_put_link_command(anc, LINK_COMMAND__SEND_INTERRUPT(0, 0));
int_status = anc_boot_wait_interrupt(anc, int_mask);
if (!int_status)
{
// Timeout waiting for any status bits (including timeout bit - if we hit this, the ANC hw timeout
// parts aren't working right
dprintf(DEBUG_INFO, "Timeout waiting for CHAN_INT_STATUS != 0 - this should never happen\n");
return false;
}
if (G_ANC_CHAN_INT_STATUS_LINK_CMD_FLAG(int_status))
{
// Successful status read
ret = true;
}
else
{
// Timeout or invalid status
dprintf(DEBUG_INFO, "Unexpected status 0x%08X\n", int_status);
dprintf(DEBUG_INFO, "NAND Status: 0x%02X\n", anc_rd(anc, R_ANC_LINK_NAND_STATUS));
ret = false;
}
return ret;
}
bool anc_bootrom_read_phys_page(uint32_t band,
uint32_t dip,
uint32_t page,
uint32_t num_lbas,
void *data,
uint32_t *meta)
{
anc_t *anc = &g_boot_anc[0];
const uint32_t intmask = (M_ANC_CHAN_INT_STATUS_LINK_CMD_FLAG |
M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT |
M_ANC_CHAN_INT_STATUS_READ_STATUS_ERR_RESPONSE);
uint32_t intstatus;
unsigned int lba;
addr_t paddr;
bool ret = true;
paddr = mem_static_map_physical((addr_t)data);
if (!paddr)
{
dprintf(DEBUG_INFO, "No buffer\n");
return false;
}
if ((paddr & (16 - 1)) != 0)
{
dprintf(DEBUG_INFO, "ANC buffers must be 16 byte aligned\n");
return false;
}
if (!meta)
{
dprintf(DEBUG_INFO, "No meta buffer\n");
return false;
}
if (num_lbas == 0)
{
dprintf(DEBUG_INFO, "Invalid num_lbas %d\n", num_lbas);
return false;
}
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, data, NAND_BOOT_LOGICAL_PAGE_SIZE * num_lbas);
#endif
anc_boot_put_dma_command(anc, DMA_COMMAND_CONFIG(DMA_DIRECTION_N2M, false));
anc_boot_put_link_command(anc, LINK_COMMAND__CE(1));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__READ));
// Can't use LINK_CMD_ADDR6: rdar://problem/10941493
anc_boot_put_link_command(anc, LINK_COMMAND__OPCODE(LINK_CMD_OP_ADDR6) | ((page & 0xFF) << 16));
anc_boot_put_link_command(anc, page >> 8);
anc_boot_put_link_command(anc, LINK_COMMAND__CMD2(NAND_CMD__READ_CONFIRM, NAND_CMD__READ_STATUS));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_STATUS(0x00,
(PPN_LOW_POWER_STATUS__READY |
PPN_LOW_POWER_STATUS__FAIL),
PPN_LOW_POWER_STATUS__READY));
// Wait 27 clocks for RW turnaround
anc_boot_put_link_command(anc, LINK_COMMAND__OPCODE(LINK_CMD_OP_WAIT_TIME) | 0x1b);
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__READ));
anc_boot_put_link_command(anc, LINK_COMMAND__OPCODE(LINK_CMD_OP_WAIT_TIME) | 0x1b);
for (lba = 0; lba < num_lbas; lba++)
{
anc_boot_put_link_command(anc, LINK_COMMAND__READ_PIO(NAND_BOOT_BYTES_PER_META, false, false));
if (lba == (num_lbas - 1))
{
anc_boot_put_link_command(anc, LINK_COMMAND__SEND_INTERRUPT(0, 0));
}
anc_boot_put_link_command(anc, LINK_COMMAND__READ_DMA(NAND_BOOT_LOGICAL_PAGE_SIZE, false, false));
anc_boot_put_dma_command(anc, DMA_COMMAND_BUFDESC(NAND_BOOT_LOGICAL_PAGE_SIZE,
(uint64_t)paddr + lba * NAND_BOOT_LOGICAL_PAGE_SIZE));
}
anc_boot_put_dma_command(anc, DMA_COMMAND_FLAG(0, 0));
intstatus = anc_boot_wait_interrupt(anc, intmask);
if (!intstatus)
{
dprintf(DEBUG_INFO, "Timeout waiting for CHAN_INT_STATUS != 0 - this should never happen\n");
return false;
}
// We've either timed out or gotten our status by now
if (intstatus & M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT)
{
// Timeout waiting for status: flush LINK CmdQ and return error
dprintf(DEBUG_INFO, "Timeout waiting for NAND status\n");
anc_wr(anc, R_ANC_LINK_CONTROL, M_ANC_LINK_CONTROL_RESET_CMDQ);
return false;
}
else if (intstatus & M_ANC_CHAN_INT_STATUS_READ_STATUS_ERR_RESPONSE)
{
// Invalid status (probably has error bit set). Reset CmdQ and return error
dprintf(DEBUG_INFO, "Invalid NAND status 0x%02X\n", anc_rd(anc, R_ANC_LINK_NAND_STATUS));
anc_wr(anc, R_ANC_LINK_CONTROL, M_ANC_LINK_CONTROL_RESET_CMDQ);
return false;
}
// We've got a good status and metadata should be available now.
for (lba = 0; lba < num_lbas; lba++)
{
uint32_t meta_index;
for (meta_index = 0; meta_index < (NAND_BOOT_BYTES_PER_META/ sizeof(uint32_t)); meta_index++)
{
uint32_t meta_word = anc_rd(anc, R_ANC_CHAN_LINK_PIO_READ_FIFO);
if ((meta_index == 0) && (meta_word != meta[0]))
{
dprintf(DEBUG_INFO, "Invalid meta: expected 0x%08X got 0x%08X\n", meta[0], meta_word);
ret = false;
}
}
}
#if APPLICATION_SECUREROM
if (!anc_boot_wait_reg(anc, R_ANC_CHAN_INT_STATUS,
M_ANC_CHAN_INT_STATUS_DMA_CMD_FLAG,
M_ANC_CHAN_INT_STATUS_DMA_CMD_FLAG))
{
dprintf(DEBUG_INFO, "Timeout waiting for DMA to complete\n");
return false;
}
#else // APPLICATION_SECUREROM
intstatus = anc_boot_wait_interrupt(anc, M_ANC_CHAN_INT_STATUS_DMA_CMD_FLAG | M_ANC_CHAN_INT_STATUS_DMA_CMD_TIMEOUT);
if (!intstatus || (intstatus & M_ANC_CHAN_INT_STATUS_DMA_CMD_TIMEOUT))
{
dprintf(DEBUG_INFO, "Timeout waiting for DMA to complete - 0x%08x\n", intstatus);
return false;
}
#endif // APPLICATION_SECUREROM
anc_wr(anc, R_ANC_CHAN_INT_STATUS, M_ANC_CHAN_INT_STATUS_DMA_CMD_FLAG);
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_INVALIDATE, data, NAND_BOOT_LOGICAL_PAGE_SIZE * num_lbas);
#endif
return ret;
}
void anc_boot_put_link_command(anc_t *anc, uint32_t value)
{
#if APPLICATION_SECUREROM
anc_boot_wait_reg(anc, R_ANC_CHAN_LINK_CMDQ_FIFO_STATUS, M_ANC_CHAN_LINK_CMDQ_FIFO_STATUS_FULL, 0);
anc_wr(anc, R_ANC_CHAN_LINK_CMDQ_FIFO, value);
#else // APPLICATION_SECUREROM
if (anc_boot_wait_reg_int_timeout(anc, R_ANC_CHAN_LINK_CMDQ_FIFO_STATUS, M_ANC_CHAN_LINK_CMDQ_FIFO_STATUS_FULL, 0, M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT))
{
anc_wr(anc, R_ANC_CHAN_LINK_CMDQ_FIFO, value);
}
else
{
dprintf(DEBUG_INFO, "Timeout waiting for room in link command FIFO\n");
}
#endif // APPLICATION_SECUREROM
}
void anc_boot_put_dma_command(anc_t *anc, uint64_t value)
{
#if APPLICATION_SECUREROM
anc_boot_wait_reg(anc, R_ANC_CHAN_LINK_CMDQ_FIFO_STATUS, M_ANC_CHAN_DMA_CMDQ_FIFO_STATUS_FULL, 0);
anc_wr(anc, R_ANC_CHAN_DMA_CMDQ_FIFO, value & 0xFFFFFFFFUL);
anc_wr(anc, R_ANC_CHAN_DMA_CMDQ_FIFO, (value >> 32UL));
#else // APPLICATION_SECUREROM
if (anc_boot_wait_reg_int_timeout(anc, R_ANC_CHAN_LINK_CMDQ_FIFO_STATUS, M_ANC_CHAN_DMA_CMDQ_FIFO_STATUS_FULL, 0, M_ANC_CHAN_INT_STATUS_DMA_CMD_TIMEOUT))
{
anc_wr(anc, R_ANC_CHAN_DMA_CMDQ_FIFO, value & 0xFFFFFFFFUL);
anc_wr(anc, R_ANC_CHAN_DMA_CMDQ_FIFO, (value >> 32UL));
}
else
{
dprintf(DEBUG_INFO, "Timeout waiting for room in DMA command FIFO\n");
}
#endif // APPLICATION_SECUREROM
}
// Wait up to ANC_TIMEOUT_MICROS for ((reg & mask) == value)
bool anc_boot_wait_reg(anc_t *anc, uint32_t reg, uint32_t mask, uint32_t value)
{
utime_t start_time = system_time();
uint32_t regval;
do
{
regval = anc_rd(anc, reg);
if ((regval & mask) == value)
{
return true;
}
} while (!time_has_elapsed(start_time, ANC_TIMEOUT_MICROS));
return false;
}
#if !APPLICATION_SECUREROM
// Like anc_boot_wait_reg, but waits until (R_ANC_CHAN_INT_MASK & tomask) to determine the timeout instead of using ANC_TIMEOUT_MICROS. Only used by LLB.
bool anc_boot_wait_reg_int_timeout(anc_t *anc, uint32_t reg, uint32_t mask, uint32_t value, uint32_t tomask)
{
uint32_t regval, intval;
do
{
regval = anc_rd(anc, reg);
if ((regval & mask) == value)
{
return true;
}
intval = anc_rd(anc, R_ANC_CHAN_INT_STATUS);
} while (!(intval & tomask));
anc_wr(anc, R_ANC_CHAN_INT_STATUS, (intval & tomask));
regval = anc_rd(anc, reg);
if ((regval & mask) == value)
{
return true;
}
return false;
}
#endif // !APPLICATION_SECUREROM
uint32_t anc_boot_wait_interrupt(anc_t *anc, uint32_t mask)
{
#if APPLICATION_SECUREROM
utime_t start_time = system_time();
#endif // APPLICATION_SECUREROM
uint32_t regval;
do
{
regval = anc_rd(anc, R_ANC_CHAN_INT_STATUS);
if (regval & mask)
{
// Only clear the bits we're looking for
anc_wr(anc, R_ANC_CHAN_INT_STATUS, (regval & mask));
return regval;
}
#if APPLICATION_SECUREROM
} while (!time_has_elapsed(start_time, ANC_TIMEOUT_MICROS));
#else // APPLICATION_SECUREROM
} while (1);
#endif // APPLICATION_SECUREROM
return 0;
}
static void anc_disable_uid(anc_t *anc)
{
// The DISABLE_UID bit doesn't work unless AES is enabled. Force a DMA_CONFIG to enable it
anc_boot_put_dma_command(anc, DMA_COMMAND_CONFIG(DMA_DIRECTION_N2M, true));
// Wait for AES to become enabled
anc_boot_wait_reg(anc, R_ANC_DMA_STATUS, M_ANC_DMA_STATUS_AES_ENABLED, M_ANC_DMA_STATUS_AES_ENABLED);
#ifdef M_ANC_DMA_UID_CONFIG_DISABLE_UID
// Disable UID1 (doesn't exist on M7)
anc_wr(anc, R_ANC_DMA_UID_CONFIG, M_ANC_DMA_UID_CONFIG_DISABLE_UID);
#endif
}
void anc_disable_uid_key(void)
{
int i;
if (!g_boot_anc[0].initialized)
{
// If we came in here through the DFU path, ANC isn't configured.
anc_bootrom_init(false, ANC_BOOT_MODE_RESET_ALL_CONTROLLERS);
}
for (i = 0; i < ANC_BOOT_CONTROLLERS; i++)
{
anc_disable_uid(&g_boot_anc[i]);
}
}
+24
View File
@@ -0,0 +1,24 @@
/*
* 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.
*/
#ifndef _ANC_BOOTROM_H_
#define _ANC_BOOTROM_H_
#include <stdint.h>
int anc_bootrom_init(bool reset, int resetMode);
bool anc_bootrom_read_phys_page(uint32_t band,
uint32_t dip,
uint32_t page,
uint32_t num_lbas,
void *data,
uint32_t *meta);
#endif // _ANC_BOOTROM_H_
+226
View File
@@ -0,0 +1,226 @@
/*
* Copyright (C) 2011 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.
*/
#ifndef _ANC_CMDS_H_
#define _ANC_CMDS_H_
#define LINK_CMDQ_SIZE 128
#define DMA_CMDQ_SIZE 64 // 64b words, not 32b
#define LINK_PIO_READ_FIFO_SIZE 64
////////////////////////////////////////////////////////////////////////////////
// DMA Command Definitions
#define DMA_COMMAND__OPCODE(n) (((n) & 0x7FULL) << 60)
#define DMA_COMMAND__OPCODE__CONFIG (0)
#define DMA_COMMAND__OPCODE__BUFDESC (1)
#define DMA_COMMAND__OPCODE__AES_KEY_IV (2)
#define DMA_COMMAND__OPCODE__AES_UID1_IV (3)
#define DMA_COMMAND__OPCODE__AES_IV (4)
#define DMA_COMMAND__OPCODE__FLAG (5)
#define DMA_COMMAND_CONFIG__DMA_DIRECTION(n) (((n) & 0x1ULL) << 53)
#define DMA_DIRECTION_M2N (0)
#define DMA_DIRECTION_N2M (1)
#define DMA_COMMAND_CONFIG__PROC_ENABLE_AES(n) (((n) & 0x1ULL) << 54)
#define DMA_COMMAND_BUFDESC__LENGTH(n) (((n) & 0x1FFFULL) << 40)
#define DMA_COMMAND_BUFDESC__ADDRESS(n) (((n) & 0xFFFFFFFFFFULL) << 0)
#define DMA_COMMAND_AESKEY__UNWRAP(n) (((n) & 0x1ULL) << 2)
#define DMA_COMMAND_AESKEY__KEY_LEN(n) (((n) & 0x3ULL) << 0)
#define DMA_COMMAND_AESKEY_128_BITS (0)
#define DMA_COMMAND_AESKEY_192_BITS (1)
#define DMA_COMMAND_AESKEY_256_BITS (2)
#define DMA_COMMAND_FLAG__PAUSE(n) (((n) & 0x1ULL) << 16)
#define DMA_COMMAND_FLAG__CODE(n) (((n) & 0xFFFFULL) << 0)
#define DMA_COMMAND_CONFIG(dir,aes) \
(DMA_COMMAND__OPCODE(DMA_COMMAND__OPCODE__CONFIG) | \
DMA_COMMAND_CONFIG__DMA_DIRECTION(dir) | \
DMA_COMMAND_CONFIG__PROC_ENABLE_AES(aes))
#define DMA_COMMAND_BUFDESC(data_bytes,address) \
(DMA_COMMAND__OPCODE(DMA_COMMAND__OPCODE__BUFDESC) | \
DMA_COMMAND_BUFDESC__LENGTH(data_bytes) | \
DMA_COMMAND_BUFDESC__ADDRESS(address))
#define DMA_COMMAND_AES_KEY_IV(key_len, unwrap_en) \
(DMA_COMMAND__OPCODE(DMA_COMMAND__OPCODE__AES_KEY_IV) | \
DMA_COMMAND_AESKEY__KEY_LEN(key_len) | \
DMA_COMMAND_AESKEY__UNWRAP(unwrap_en))
#define DMA_COMMAND_FLAG(code,pause) \
(DMA_COMMAND__OPCODE(DMA_COMMAND__OPCODE__FLAG) | \
DMA_COMMAND_FLAG__PAUSE(pause) | \
DMA_COMMAND_FLAG__CODE(code))
////////////////////////////////////////////////////////////////////////////////
// Link Command Definitions
#define LINK_COMMAND__YIELD (1UL << 31)
#define LINK_COMMAND__OPCODE(n) (((n) & 0x3FUL) << 24)
#define LINK_CMD_OP_DEBUG_DLL 0x00
#define LINK_CMD_OP_CMD0 0x01
#define LINK_CMD_OP_CMD1 0x02
#define LINK_CMD_OP_CMD1_OP 0x03
#define LINK_CMD_OP_CMD2 0x04
#define LINK_CMD_OP_CMD2_OP 0x05
#define LINK_CMD_OP_CMD3 0x06
#define LINK_CMD_OP_CMD3_OP 0x07
#define LINK_CMD_OP_CMD4 0x08
#define LINK_CMD_OP_CMD4_OP 0x09
#define LINK_CMD_OP_CMD5 0x0A
#define LINK_CMD_OP_CMD5_OP 0x0B
#define LINK_CMD_OP_CMD6 0x0C
#define LINK_CMD_OP_CMD6_OP 0x0D
#define LINK_CMD_OP_CMD7 0x0E
#define LINK_CMD_OP_CMD7_OP 0x0F
#define LINK_CMD_OP_CMD8_OP 0x10
#define LINK_CMD_OP_ADDR1 0x12
#define LINK_CMD_OP_ADDR1_OP 0x13
#define LINK_CMD_OP_ADDR2 0x14
#define LINK_CMD_OP_ADDR2_OP 0x15
#define LINK_CMD_OP_ADDR3 0x16
#define LINK_CMD_OP_ADDR3_OP 0x17
#define LINK_CMD_OP_ADDR4 0x18
#define LINK_CMD_OP_ADDR4_OP 0x19
#define LINK_CMD_OP_ADDR5 0x1A
#define LINK_CMD_OP_ADDR5_OP 0x1B
#define LINK_CMD_OP_ADDR6 0x1C
#define LINK_CMD_OP_ADDR6_OP 0x1D
#define LINK_CMD_OP_ADDR7 0x1E
#define LINK_CMD_OP_ADDR7_OP 0x1F
#define LINK_CMD_OP_ADDR8_OP 0x20
#define LINK_CMD_OP_READ_RELEASE 0x21
#define LINK_CMD_OP_CE 0x22
#define LINK_CMD_OP_CE_OP 0x23
#define LINK_CMD_OP_READ_DMA 0x24
#define LINK_CMD_OP_READ_DMA_OP 0x25
#define LINK_CMD_OP_READ_PIO 0x26
#define LINK_CMD_OP_READ_PIO_OP 0x27
#define LINK_CMD_OP_READ_NUL 0x28
#define LINK_CMD_OP_READ_NUL_OP 0x29
#define LINK_CMD_OP_READ_STATUS 0x2A
#define LINK_CMD_OP_READ_STATUS_ABORT_ENABLED 0x2B
#define LINK_CMD_OP_WRITE_DMA 0x2C
#define LINK_CMD_OP_WRITE_DMA_OP 0x2D
#define LINK_CMD_OP_WRITE_PIO 0x2E
#define LINK_CMD_OP_WRITE_PIO_OP 0x2F
#define LINK_CMD_OP_WRITE_PIO_IMM 0x30
#define LINK_CMD_OP_WRITE_PAD 0x32
#define LINK_CMD_OP_WRITE_PAD_OP 0x33
#define LINK_CMD_OP_READ_REGISTER 0x34
#define LINK_CMD_OP_WRITE_REGISTER 0x35
#define LINK_CMD_OP_POLL_REGISTER 0x36
#define LINK_CMD_OP_POLL_REGISTER_ABORT_ENABLED 0x37
#define LINK_CMD_OP_WAIT_TIME 0x38
#define LINK_CMD_OP_WAIT_TIME_ABORT_ENABLED 0x39
#define LINK_CMD_OP_WAIT_FOR_INTERRUPT 0x3A
#define LINK_CMD_OP_WAIT_FOR_INTERRUPT_ABORT_ENABLED 0x3B
#define LINK_CMD_OP_SEND_INTERRUPT 0x3C
#define LINK_CMD_OP_MACRO 0x3D
#define LINK_CMD_OP_DPI_OUT 0x3E
#define LINK_CMD_OP_DPI_IN 0x3F
#define LINK_COMMAND__CE__CE(ce) (((ce) & 0xFFUL) << 0)
#define LINK_COMMAND__CE(ce) (LINK_COMMAND__OPCODE(LINK_CMD_OP_CE) | LINK_COMMAND__CE__CE(ce))
#define LINK_COMMAND__CMD__CMD2(n) (((n) & 0xFFUL) << 16)
#define LINK_COMMAND__CMD__CMD1(n) (((n) & 0xFFUL) << 8)
#define LINK_COMMAND__CMD__CMD0(n) (((n) & 0xFFUL) << 0)
#define LINK_COMMAND__CMD1(cmd1) (LINK_COMMAND__OPCODE(LINK_CMD_OP_CMD1) | LINK_COMMAND__CMD__CMD0(cmd1))
#define LINK_COMMAND__CMD2(cmd1,cmd2) (LINK_COMMAND__OPCODE(LINK_CMD_OP_CMD2) | \
LINK_COMMAND__CMD__CMD0(cmd1) | \
LINK_COMMAND__CMD__CMD1(cmd2))
#define LINK_COMMAND__CMD3(cmd1,cmd2,cmd3) (LINK_COMMAND__OPCODE(LINK_CMD_OP_CMD3) | \
LINK_COMMAND__CMD__CMD0(cmd1) | \
LINK_COMMAND__CMD__CMD1(cmd2) | \
LINK_COMMAND__CMD__CMD2(cmd3))
#define LINK_COMMAND__ADDR__ADDR2(n) (((n) & 0xFFUL) << 16)
#define LINK_COMMAND__ADDR__ADDR1(n) (((n) & 0xFFUL) << 8)
#define LINK_COMMAND__ADDR__ADDR0(n) (((n) & 0xFFUL) << 0)
#define LINK_COMMAND__ADDR1(addr0) (LINK_COMMAND__OPCODE(LINK_CMD_OP_ADDR1) | \
LINK_COMMAND__ADDR__ADDR0(addr0))
#define LINK_COMMAND__ADDR2(addr0,addr1) (LINK_COMMAND__OPCODE(LINK_CMD_OP_ADDR2) | \
LINK_COMMAND__ADDR__ADDR0(addr0) | \
LINK_COMMAND__ADDR__ADDR1(addr1))
#define LINK_COMMAND__ADDR3(addr0,addr1,addr2) (LINK_COMMAND__OPCODE(LINK_CMD_OP_ADDR3) | \
LINK_COMMAND__ADDR__ADDR0(addr0) | \
LINK_COMMAND__ADDR__ADDR1(addr1) | \
LINK_COMMAND__ADDR__ADDR2(addr2))
#define LINK_COMMAND__WRITE_DMA__LEN(n) (((n) & 0x7FFFUL) << 0)
#define LINK_COMMAND__WRITE_DMA__CRC_START(n) (((n) & 0x1UL) << 22)
#define LINK_COMMAND__WRITE_DMA__CRC_STOP(n) (((n) & 0x1UL) << 23)
#define LINK_COMMAND__WRITE_DMA(len, crc_start, crc_stop) (LINK_COMMAND__OPCODE(LINK_CMD_OP_WRITE_DMA) | \
LINK_COMMAND__WRITE_DMA__LEN(len) | \
LINK_COMMAND__WRITE_DMA__CRC_START(crc_start) | \
LINK_COMMAND__WRITE_DMA__CRC_STOP(crc_stop))
#define LINK_COMMAND__WRITE_PIO__LEN(n) (((n) & 0x7FFFUL) << 0)
#define LINK_COMMAND__WRITE_PIO__CRC_START(n) (((n) & 0x1UL) << 22)
#define LINK_COMMAND__WRITE_PIO__CRC_STOP(n) (((n) & 0x1UL) << 23)
#define LINK_COMMAND__WRITE_PIO(len, crc_start, crc_stop) (LINK_COMMAND__OPCODE(LINK_CMD_OP_WRITE_PIO) | \
LINK_COMMAND__WRITE_PIO__LEN(len) | \
LINK_COMMAND__WRITE_PIO__CRC_START(crc_start) | \
LINK_COMMAND__WRITE_PIO__CRC_STOP(crc_stop))
#define LINK_COMMAND__READ_DMA__LEN(n) (((n) & 0x7FFFUL) << 0)
#define LINK_COMMAND__READ_DMA__CRC_START(n) (((n) & 0x1UL) << 22)
#define LINK_COMMAND__READ_DMA__CRC_STOP(n) (((n) & 0x1UL) << 23)
#define LINK_COMMAND__READ_DMA(len, crc_start, crc_stop) (LINK_COMMAND__OPCODE(LINK_CMD_OP_READ_DMA) | \
LINK_COMMAND__READ_DMA__LEN(len) | \
LINK_COMMAND__READ_DMA__CRC_START(crc_start) | \
LINK_COMMAND__READ_DMA__CRC_STOP(crc_stop))
#define LINK_COMMAND__READ_PIO__LEN(n) (((n) & 0x7FFFUL) << 0)
#define LINK_COMMAND__READ_PIO__CRC_START(n) (((n) & 0x1UL) << 22)
#define LINK_COMMAND__READ_PIO__CRC_STOP(n) (((n) & 0x1UL) << 23)
#define LINK_COMMAND__READ_PIO(len, crc_start, crc_stop) (LINK_COMMAND__OPCODE(LINK_CMD_OP_READ_PIO) | \
LINK_COMMAND__READ_PIO__LEN(len) | \
LINK_COMMAND__READ_PIO__CRC_START(crc_start) | \
LINK_COMMAND__READ_PIO__CRC_STOP(crc_stop))
#define LINK_COMMAND__READ_STATUS__CODE(n) (((n) & 0xFFUL) << 16)
#define LINK_COMMAND__READ_STATUS__MASK(n) (((n) & 0xFFUL) << 8)
#define LINK_COMMAND__READ_STATUS__COND(n) (((n) & 0xFFUL) << 0)
#define LINK_COMMAND__READ_STATUS(code,mask,cond) (LINK_COMMAND__OPCODE(LINK_CMD_OP_READ_STATUS) | \
LINK_COMMAND__READ_STATUS__CODE(code) | \
LINK_COMMAND__READ_STATUS__MASK(mask) | \
LINK_COMMAND__READ_STATUS__COND(cond))
#define LINK_COMMAND__SEND_INTERRUPT__PAUSE(n) (((n) & 0x1UL) << 23)
#define LINK_COMMAND__SEND_INTERRUPT__CODE(n) (((n) & 0xFFFFUL) << 0)
#define LINK_COMMAND__SEND_INTERRUPT(pause,code) (LINK_COMMAND__OPCODE(LINK_CMD_OP_SEND_INTERRUPT) | \
LINK_COMMAND__SEND_INTERRUPT__PAUSE(pause) | \
LINK_COMMAND__SEND_INTERRUPT__CODE(code))
#define LINK_COMMAND__READ_REGISTER__ADDR(n) (((n) & 0xFFFFFFUL) << 0)
#define LINK_COMMAND__READ_REGISTER(addr) (LINK_COMMAND__OPCODE(LINK_CMD_OP_READ_REGISTER) | \
LINK_COMMAND__READ_REGISTER__ADDR(addr))
#define LINK_COMMAND__WRITE_REGISTER__ADDR(n) (((n) & 0xFFFFFFUL) << 0)
#define LINK_COMMAND__WRITE_REGISTER(addr) (LINK_COMMAND__OPCODE(LINK_CMD_OP_WRITE_REGISTER) | \
LINK_COMMAND__WRITE_REGISTER__ADDR(addr))
#define LINK_COMMAND__WAIT_TIME__CYCLES(n) (((n) & 0xFFFFUL) << 0)
#define LINK_COMMAND__WAIT_TIME(link_cycles) (LINK_COMMAND__OPCODE(LINK_CMD_OP_WAIT_TIME) | \
LINK_COMMAND__WAIT_TIME__CYCLES((link_cycles)))
#define LINK_COMMAND__MACRO__LENGTH(n) (((n) & 0xFFUL) << 0)
#define LINK_COMMAND__MACRO__ADDR(n) (((n) & 0xFFUL) << 8)
#define LINK_COMMAND__MACRO__REPEAT(n) (((n) & 0xFFUL) << 16)
#define LINK_COMMAND__MACRO(addr,length,repeat) (LINK_COMMAND__OPCODE(LINK_CMD_OP_MACRO) | \
LINK_COMMAND__MACRO__LENGTH(length-1) | \
LINK_COMMAND__MACRO__ADDR(addr) | \
LINK_COMMAND__MACRO__REPEAT(repeat))
#endif // _ANC_CMDS_H_
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2011 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.
*/
#ifndef _ANC_BOOTROM_PRIVATE_H_
#define _ANC_BOOTROM_PRIVATE_H_
#if SUB_PLATFORM_S7002
# include "anc_bootrom_regs_m7.h"
#else
# include "anc_bootrom_regs.h"
#endif
#include "ppn_npl_regs.h"
#include "anc_bootrom_cmds.h"
#define ANC_MAX_CES_PER_BUS (1)
#define ANC_NAND_ID_SIZE (6)
#define NAND_BOOT_PAGES_PER_BLOCK 128
#define NAND_BOOT_BYTES_PER_META 16
#define NAND_BOOT_BYTES_PER_PAGE 8192
#define NAND_BOOT_LOGICAL_PAGE_SIZE 4096
// NAND Commands
#define NAND_CMD__RESET ((uint8_t)0xFF)
#define NAND_CMD__READ_ID ((uint8_t)0x90)
#define NAND_CMD__READ_STATUS ((uint8_t)0x70)
#define NAND_CMD__READ ((uint8_t)0x00)
#define NAND_CMD__READ_CONFIRM ((uint8_t)0x30)
#define NAND_CMD__MULTIPAGE_READ ((uint8_t)0x07)
#define NAND_CMD__MULTIPAGE_READ_LAST ((uint8_t)0x0A)
#define NAND_CMD__MULTIPAGE_READ_CONFIRM ((uint8_t)0x37)
#define NAND_CMD__MULTIPAGE_PROGRAM ((uint8_t)0x87)
#define NAND_CMD__MULTIPAGE_PROGRAM_LAST ((uint8_t)0x8A)
#define NAND_CMD__MULTIPAGE_PROGRAM_CONFIRM ((uint8_t)0x17)
#define NAND_CMD__MULTIPAGE_PREP ((uint8_t)0xB0)
#define NAND_CMD__MULTIPAGE_PREP__CONFIRM ((uint8_t)0xB8)
#define NAND_CMD__MULTIBLOCK_ERASE ((uint8_t)0x67)
#define NAND_CMD__MULTIBLOCK_ERASE_LAST ((uint8_t)0x6A)
#define NAND_CMD__MULTIBLOCK_ERASE_CONFIRM ((uint8_t)0xD7)
#define NAND_CMD__LOW_POWER_READ_STATUS ((uint8_t)0x70)
#define NAND_CMD__GET_NEXT_OPERATION_STATUS ((uint8_t)0x77)
#define NAND_CMD__OPERATION_STATUS ((uint8_t)0x7D)
#define NAND_CMD__READ_SERIAL_OUTPUT ((uint8_t)0x7A)
#define NAND_CMD__CONTROLLER_STATUS ((uint8_t)0x79)
#define NAND_CMD__READ_DEVICE_PARAMETERS ((uint8_t)0x92)
#define NAND_CMD__READ_DEVICE_PARAMETERS_CONFIRM ((uint8_t)0x97)
#define NAND_CMD__SET_FEATURES ((uint8_t)0xEF)
#define NAND_CMD__GET_FEATURES ((uint8_t)0xEE)
#define NAND_CMD__SET_GET_FEATURES_CONFIRM ((uint8_t)0xE7)
#define NAND_CMD__SET_DEBUG_DATA ((uint8_t)0xE9)
#define NAND_CMD__GET_DEBUG_DATA ((uint8_t)0xE8)
#define NAND_CMD__UPDATE_FW ((uint8_t)0xED)
#define NAND_CMD__PPN_STATUS_CONFIRM ((uint8_t)0xED)
#define PPN_LOW_POWER_STATUS__FAIL (1 << 0)
#define PPN_LOW_POWER_STATUS__READY (1 << 6)
#define PPN_LOW_POWER_STATUS__WRITE_PROTECT_BAR (1 << 7)
#define PPN_FEATURE__POWER_STATE ((uint16_t)0x0180)
#define PPN_FEATURE__POWER_STATE__LOW_POWER 0x1
#define PPN_FEATURE__POWER_STATE__NORMAL_ASYNC 0x2
#define PPN_FEATURE__POWER_STATE__NORMAL_DDR 0xA
#define PPN_FEATURE__POWER_STATE__STANDBY 0x4
#define CHIPID_ADDR (0x00)
#define PPN_BLOCK_PAIRING__NO_PAIRING (0x00)
#define PPN_BLOCK_PAIRING__EVEN_ODD (0x10)
#define PPN_BLOCK_PAIRING__FOUR_PLANE (0x20)
#define ROUNDUPTO(num, gran) ((((num) + (gran) - 1) / (gran)) * (gran))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define ANC_BOOT_MAX_TIMING_REG_VALUE (M_ANC_LINK_SDR_DATA_TIMING_REN_SETUP_TIME >> S_ANC_LINK_SDR_DATA_TIMING_REN_SETUP_TIME)
typedef uint8_t anc_chip_id_t[ANC_NAND_ID_SIZE];
typedef struct anc_t
{
uint32_t bus_id;
bool initialized;
uint32_t *regs;
uint32_t pages_per_block;
uint32_t bytes_per_meta;
uint32_t meta_words;
uint32_t bytes_per_page;
uint32_t logical_page_size;
uint32_t lbas_per_page;
anc_chip_id_t chip_id;
} anc_t;
extern anc_t g_boot_anc[ANC_BOOT_CONTROLLERS];
#define anc_wr(anc, reg, val) (*((anc)->regs + ((reg) >> 2)) = (val))
#define anc_wr64(anc, reg, val) anc_store64((volatile uint64_t *)((anc)->regs + ((reg) >> 2)), (val))
#define anc_rd(anc, reg) (*(volatile uint32_t *)((uint8_t *)((anc)->regs) + (reg)))
#define anc_npl_wr(npl, reg, val) (*(npl + ((reg) >> 2)) = (val))
void anc_boot_put_link_command(anc_t *anc, uint32_t value);
void anc_boot_put_dma_command(anc_t *anc, uint64_t value);
uint32_t anc_boot_wait_interrupt(anc_t *anc, uint32_t mask);
bool anc_boot_wait_reg(anc_t *anc, uint32_t reg, uint32_t mask, uint32_t value);
#if !APPLICATION_SECUREROM
bool anc_boot_wait_reg_int_timeout(anc_t *anc, uint32_t reg, uint32_t mask, uint32_t value, uint32_t tomask);
#endif // !APPLICATION_SECUREROM
#endif // _ANC_BOOTROM_PRIVATE_H_
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+659
View File
@@ -0,0 +1,659 @@
/*
* 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.
*/
#include <debug.h>
#include <drivers/anc_boot.h>
#include <platform.h>
#include <stdint.h>
#include <stdio.h>
#include <sys.h>
#include "anc_bootrom.h"
#include "anc_llb.h"
#include "anc_bootrom_private.h"
#include "util_boot.h"
anc_ppn_device_params_t *anc_geom;
#define NUM_DIP 256UL
#define NUM_BUS 2UL
dip_info_t dip_info[NUM_DIP];
int anc_num_channel; // number of populated channels
static bool anc_llb_set_normal_mode(void);
static bool anc_llb_set_feature(anc_t *anc, uint16_t feature, const uint8_t *data, uint32_t len);
static bool anc_llb_get_geometry(void);
static bool anc_llb_read_chip_ids(void);
static bool anc_llb_read_chip_id(anc_t *anc, anc_chip_id_t *chip_id);
static bool anc_llb_read_device_parameters(anc_t *anc,
anc_ppn_device_params_t *params);
static void anc_llb_handle_geb(anc_t *anc);
int anc_llb_init(void)
{
unsigned int dip;
unsigned int die;
unsigned int plane;
unsigned int bus;
unsigned int cau_per_die;
unsigned int numDip;
unsigned int numPlane;
unsigned int die_per_channel;
anc_geom = memalign(NUM_BUS * sizeof(anc_ppn_device_params_t), CPU_CACHELINE_SIZE);
if (!anc_geom) panic("Unable to allocate anc_geom");
// Initialize ANC, but don't reset the NANDs - they're already good to go
// in low-power mode coming out of SecureROM.
if (anc_bootrom_init(false, ANC_BOOT_MODE_RESET_ALL_CONTROLLERS) != 0)
{
dprintf(DEBUG_CRITICAL, "Failed to init ANC");
return -1;
}
if (!anc_llb_read_chip_ids())
{
dprintf(DEBUG_CRITICAL, "Failed to read chip IDs");
return -1;
}
if (!anc_llb_set_normal_mode())
{
dprintf(DEBUG_CRITICAL, "Failed to enter normal mode");
return -1;
}
if (!anc_llb_get_geometry())
{
dprintf(DEBUG_CRITICAL, "Failed to get geometry");
return -1;
}
if (anc_geom[0].caus_per_channel > (NUM_DIP / NUM_BUS))
{
dprintf(DEBUG_CRITICAL, "caus_per_channel out of range: %d max %ld\n", anc_geom[0].caus_per_channel, NUM_DIP / NUM_BUS);
return -1;
}
die_per_channel = anc_geom[0].dies_per_channel;
cau_per_die = (anc_geom[0].caus_per_channel / die_per_channel);
dip = 0;
die = 0;
bus = 0;
plane = 0;
// PAIRING_SCHEME_TWO_BY_TWO_PAIRING
if (4 == cau_per_die && PPN_BLOCK_PAIRING__FOUR_PLANE == anc_geom[0].block_pairing_scheme) {
numDip = (anc_geom[0].caus_per_channel * anc_num_channel) / 2;
numPlane = cau_per_die / 2;
} else { // PAIRING_SCHEME_ODD_EVEN_PAIRING, PAIRING_SCHEME_NO_PAIRING
numDip = anc_geom[0].caus_per_channel * anc_num_channel;
numPlane = cau_per_die;
}
while (dip < numDip) {
dip_info[dip].cau = plane + (die * cau_per_die);
dip_info[dip].bus = bus;
dip++;
plane++;
if (plane >= numPlane) {
plane = 0;
bus++;
if ((int) bus >= anc_num_channel) {
bus = 0;
die++;
}
}
}
if (4 == cau_per_die && PPN_BLOCK_PAIRING__FOUR_PLANE == anc_geom[0].block_pairing_scheme) {
die = 0;
bus = 0;
plane = 2;
numDip = anc_geom[0].caus_per_channel * anc_num_channel;
numPlane = cau_per_die;
while (dip < numDip) {
dip_info[dip].cau = plane + (die * cau_per_die);
dip_info[dip].bus = bus;
dip++;
plane++;
if (plane >= numPlane) {
plane = 2;
bus++;
if ((int) bus >= anc_num_channel) {
bus = 0;
die++;
}
}
}
}
return 0;
}
uint32_t anc_get_dip(uint32_t bus, uint32_t cau) {
uint32_t dip;
uint32_t num_dip;
num_dip = anc_geom[0].caus_per_channel * anc_num_channel;
for (dip = 0; dip < num_dip; dip++) {
if (cau == dip_info[dip].cau && bus == dip_info[dip].bus) {
break;
}
}
return dip;
}
uint32_t anc_get_dies_per_channel() {
return anc_geom[0].dies_per_channel;
}
static bool anc_llb_set_normal_mode(void)
{
int channel;
for (channel = 0; channel < anc_num_channel; channel++)
{
anc_t *anc = &g_boot_anc[channel];
uint32_t power_state = PPN_FEATURE__POWER_STATE__NORMAL_ASYNC;
if (!anc_llb_set_feature(anc,
PPN_FEATURE__POWER_STATE,
(const uint8_t *)&power_state,
sizeof(uint32_t)))
{
return false;
}
anc_wr(anc, R_ANC_LINK_SDR_DATA_TIMING, 0x04040404);
anc_wr(anc, R_ANC_LINK_SDR_TIMING, 0x00000F0F);
anc_wr(anc, R_ANC_LINK_COMMAND_ADDRESS_PULSE_TIMING, 0x00000302);
anc_wr(anc, R_ANC_LINK_READ_STATUS_CONFIG, 0x00F04040);
}
return true;
}
static bool anc_llb_get_geometry(void)
{
int channel;
for (channel = 0; channel < anc_num_channel; channel++)
{
anc_t *anc = &g_boot_anc[channel];
if (!anc_llb_read_device_parameters(anc, &anc_geom[channel]))
{
return false;
}
}
return true;
}
static bool anc_llb_read_chip_ids(void)
{
int channel;
anc_num_channel=0;
for (channel = 0; channel < ANC_BOOT_CONTROLLERS; channel++)
{
anc_t *anc = &g_boot_anc[channel];
if (!anc_llb_read_chip_id(anc, &anc->chip_id))
{
return false;
}
else if (anc->chip_id[0]=='P' && anc->chip_id[1]=='P' && anc->chip_id[2]=='N')
{
anc_num_channel++;
}
}
return true;
}
static bool anc_llb_read_chip_id(anc_t *anc, anc_chip_id_t *chip_id)
{
bool ret = false;
const uint32_t int_mask = (M_ANC_CHAN_INT_STATUS_LINK_CMD_FLAG |
M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT);
uint32_t int_status;
anc_boot_put_link_command(anc, LINK_COMMAND__CE(1));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__READ_ID));
anc_boot_put_link_command(anc, LINK_COMMAND__ADDR1(CHIPID_ADDR));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_PIO(ANC_NAND_ID_SIZE, false, false));
anc_boot_put_link_command(anc, LINK_COMMAND__SEND_INTERRUPT(0, 0));
anc_boot_put_link_command(anc, LINK_COMMAND__CE(0));
int_status = anc_boot_wait_interrupt(anc, int_mask);
if (!int_status || (int_status & M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT))
{
dprintf(DEBUG_CRITICAL, "Timeout waiting for CHAN_INT_STATUS - 0x%08x\n", int_status);
return false;
}
if (G_ANC_CHAN_INT_STATUS_LINK_CMD_FLAG(int_status))
{
uint32_t id_word;
uint8_t *ptr = (uint8_t *)chip_id;
// Chip ID is now available in Link PIO Read FIFO
id_word = anc_rd(anc, R_ANC_CHAN_LINK_PIO_READ_FIFO);
ptr[0] = (id_word >> 0) & 0xFF;
ptr[1] = (id_word >> 8) & 0xFF;
ptr[2] = (id_word >> 16) & 0xFF;
ptr[3] = (id_word >> 24) & 0xFF;
id_word = anc_rd(anc, R_ANC_CHAN_LINK_PIO_READ_FIFO);
ptr[4] = (id_word >> 0) & 0xFF;
ptr[5] = (id_word >> 8) & 0xFF;
ret = true;
}
return ret;
}
static bool anc_llb_set_feature(anc_t *anc,
uint16_t feature,
const uint8_t *data,
uint32_t len)
{
bool ret;
uint32_t i;
const uint32_t int_mask = (M_ANC_CHAN_INT_STATUS_LINK_CMD_FLAG |
M_ANC_CHAN_INT_STATUS_READ_STATUS_ERR_RESPONSE |
M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT);
uint32_t int_status;
anc_boot_put_link_command(anc, LINK_COMMAND__CE(1));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__SET_FEATURES));
anc_boot_put_link_command(anc, LINK_COMMAND__ADDR2(feature & 0xFF,
(feature & 0xFF00) >> 8));
anc_boot_put_link_command(anc, LINK_COMMAND__WRITE_PIO(len, false, false));
for (i = 0; i < len / sizeof(uint32_t); i++)
{
anc_boot_put_link_command(anc, data[i]);
}
anc_boot_put_link_command(anc, LINK_COMMAND__CMD3(NAND_CMD__SET_GET_FEATURES_CONFIRM,
NAND_CMD__GET_NEXT_OPERATION_STATUS,
NAND_CMD__OPERATION_STATUS));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(32));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_STATUS(0, 0x40, 0x40));
anc_boot_put_link_command(anc, LINK_COMMAND__SEND_INTERRUPT(0, 0));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(32));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__GET_NEXT_OPERATION_STATUS));
anc_boot_put_link_command(anc, LINK_COMMAND__CE(0));
int_status = anc_boot_wait_interrupt(anc, int_mask);
if (!int_status || (int_status & M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT))
{
panic("Timeout waiting for CHAN_INT_STATUS - 0x%08x\n", int_status);
return false;
}
if (G_ANC_CHAN_INT_STATUS_LINK_CMD_FLAG(int_status))
{
ret = true;
}
else
{
ret = false;
}
return ret;
}
static bool anc_llb_read_device_parameters(anc_t *anc,
anc_ppn_device_params_t *params)
{
const uint32_t intmask = (M_ANC_CHAN_INT_STATUS_DMA_CMD_FLAG |
M_ANC_CHAN_INT_STATUS_DMA_CMD_TIMEOUT |
M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT |
M_ANC_CHAN_INT_STATUS_READ_STATUS_ERR_RESPONSE);
uint32_t intstatus;
bool ret;
uint64_t paddr = mem_static_map_physical((uintptr_t)params);
anc_boot_put_dma_command(anc, DMA_COMMAND_CONFIG(DMA_DIRECTION_N2M, false));
anc_boot_put_link_command(anc, LINK_COMMAND__CE(1));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__READ_DEVICE_PARAMETERS));
anc_boot_put_link_command(anc, LINK_COMMAND__ADDR1(0x00));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD3(NAND_CMD__READ_DEVICE_PARAMETERS_CONFIRM,
NAND_CMD__GET_NEXT_OPERATION_STATUS,
NAND_CMD__OPERATION_STATUS));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(32));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_STATUS(0, 0x40, 0x40));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__READ_SERIAL_OUTPUT));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(32));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_DMA(sizeof(anc_ppn_device_params_t), false, false));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__GET_NEXT_OPERATION_STATUS));
anc_boot_put_link_command(anc, LINK_COMMAND__CE(0));
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN, params, sizeof(anc_ppn_device_params_t));
#endif
anc_boot_put_dma_command(anc, DMA_COMMAND_BUFDESC(sizeof(anc_ppn_device_params_t),
paddr));
anc_boot_put_dma_command(anc, DMA_COMMAND_FLAG(0, 0));
intstatus = anc_boot_wait_interrupt(anc, intmask);
if (!intstatus || (intstatus & M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT))
{
panic("Timeout waiting for CHAN_INT_STATUS - 0x%08x\n", intstatus);
return false;
}
if (intstatus & M_ANC_CHAN_INT_STATUS_DMA_CMD_TIMEOUT)
{
dprintf(DEBUG_CRITICAL, "Timeout waiting for DMA to complete");
ret = false;
}
else if (intstatus & M_ANC_CHAN_INT_STATUS_READ_STATUS_ERR_RESPONSE)
{
dprintf(DEBUG_INFO, "Unexpected NAND status\n");
ret = false;
}
else
{
ret = true;
}
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_INVALIDATE, params, sizeof(anc_ppn_device_params_t));
#endif
return ret;
}
static uint32_t make_physical_page(uint32_t dip, uint32_t band, uint16_t page)
{
const unsigned int block_bits = anc_geom[0].block_bits;
const unsigned int page_bits = anc_geom[0].page_address_bits;
const unsigned int cau_bits = anc_geom[0].cau_bits;
unsigned int mode;
if((dip_info[dip].cau == 0) && (band == 0)) {
mode = 0;
} else {
if (anc_geom[0].address_bits_bits_per_cell > 1) {
// Device supports high-endurance SLC
mode = 3;
} else {
// Device only supports regular SLC/MLC modes. Treat this high-endurance
// work as regular SLC
mode = 1;
}
}
// LLB will always use SLC (but the PPN spec requires we not set the mode bit on CAU 0, block 0)
return (page |
(band << page_bits) |
(dip_info[dip].cau << (page_bits + block_bits)) |
(mode << (page_bits + block_bits + cau_bits)));
}
uint32_t anc_llb_read_phys_page(uint32_t band, // returns # of valid pages
uint32_t dip,
uint32_t page,
uint32_t num_lbas,
void *data,
uint32_t *meta)
{
anc_t *anc = &g_boot_anc[dip_info[dip].bus];
const uint32_t intmask = (M_ANC_CHAN_INT_STATUS_LINK_CMD_FLAG |
M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT |
M_ANC_CHAN_INT_STATUS_READ_STATUS_ERR_RESPONSE);
uint32_t intstatus;
unsigned int lba;
addr_t paddr;
uint32_t ret = num_lbas;
uint32_t ppage = make_physical_page(dip, band, page);
bool encrypted = false;
uint8_t nand_status;
paddr = mem_static_map_physical((addr_t)data);
if (!paddr)
{
dprintf(DEBUG_INFO, "No buffer\n");
return 0;
}
if (!meta)
{
dprintf(DEBUG_INFO, "No meta buffer\n");
return 0;
}
if (num_lbas == 0)
{
dprintf(DEBUG_INFO, "Invalid num_lbas %d\n", num_lbas);
return 0;
}
if ((platform_get_chip_id() == 0x8960) && (platform_get_chip_revision() == 0))
{
// On Alcatraz A0, we encrypt everything but dip 0, block 0 (due to rdar://problem/11247422)
if ((dip == 0) && (band == 0))
{
encrypted = false;
}
else
{
encrypted = true;
}
}
else
{
encrypted = false;
}
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, data, num_lbas * NAND_BOOT_LOGICAL_PAGE_SIZE);
#endif
anc_boot_put_dma_command(anc, DMA_COMMAND_CONFIG(DMA_DIRECTION_N2M, encrypted));
if (encrypted)
{
anc_boot_put_dma_command(anc, DMA_COMMAND_AES_KEY_IV(DMA_COMMAND_AESKEY_256_BITS, false));
// Key of all zeroes
anc_boot_put_dma_command(anc, 0x0000000000000000ULL);
anc_boot_put_dma_command(anc, 0x0000000000000000ULL);
anc_boot_put_dma_command(anc, 0x0000000000000000ULL);
anc_boot_put_dma_command(anc, 0x0000000000000000ULL);
// IV of all zeroes
anc_boot_put_dma_command(anc, 0x0000000000000000ULL);
anc_boot_put_dma_command(anc, 0x0000000000000000ULL);
}
anc_boot_put_link_command(anc, LINK_COMMAND__CE(1));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__MULTIPAGE_READ_LAST));
if (anc_geom[dip_info[dip].bus].bytes_per_row_address != 4)
{
anc_boot_put_link_command(anc, LINK_COMMAND__OPCODE(LINK_CMD_OP_ADDR3_OP) | (page & 0xFFFFFF));
}
else
{
anc_boot_put_link_command(anc, LINK_COMMAND__OPCODE(LINK_CMD_OP_ADDR4) | (ppage & 0xFFFFFF));
anc_boot_put_link_command(anc, (ppage & 0xFF000000) >> 24);
}
anc_boot_put_link_command(anc, LINK_COMMAND__CMD3(NAND_CMD__MULTIPAGE_READ_CONFIRM,
NAND_CMD__GET_NEXT_OPERATION_STATUS,
NAND_CMD__OPERATION_STATUS));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(32));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_STATUS(0x00,
0x50,
0x40));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_REGISTER(R_ANC_LINK_NAND_STATUS - A_ANC_LINK_BASE));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__READ_SERIAL_OUTPUT));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(32));
for (lba = 0; lba < num_lbas; lba++)
{
anc_boot_put_link_command(anc, LINK_COMMAND__READ_PIO(NAND_BOOT_BYTES_PER_META, false, false));
if (lba == (num_lbas - 1))
{
anc_boot_put_link_command(anc, LINK_COMMAND__SEND_INTERRUPT(0, 0));
}
anc_boot_put_link_command(anc, LINK_COMMAND__READ_DMA(NAND_BOOT_LOGICAL_PAGE_SIZE, false, false));
if (encrypted && (lba != 0))
{
anc_boot_put_dma_command(anc, DMA_COMMAND__OPCODE(DMA_COMMAND__OPCODE__AES_IV));
anc_boot_put_dma_command(anc, 0x0000000000000000ULL);
anc_boot_put_dma_command(anc, 0x0000000000000000ULL);
}
anc_boot_put_dma_command(anc, DMA_COMMAND_BUFDESC(NAND_BOOT_LOGICAL_PAGE_SIZE,
(uint64_t)paddr + lba * NAND_BOOT_LOGICAL_PAGE_SIZE));
}
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(32));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__GET_NEXT_OPERATION_STATUS));
anc_boot_put_link_command(anc, LINK_COMMAND__CE(0));
anc_boot_put_dma_command(anc, DMA_COMMAND_FLAG(0, 0));
intstatus = anc_boot_wait_interrupt(anc, intmask);
if (!intstatus)
{
dprintf(DEBUG_CRITICAL, "Timeout waiting for CHAN_INT_STATUS != 0\n");
return 0;
}
// We've either timed out or gotten our status by now
if (intstatus & M_ANC_CHAN_INT_STATUS_LINK_CMD_TIMEOUT)
{
// Timeout waiting for status: flush LINK CmdQ and return error
dprintf(DEBUG_CRITICAL, "Timeout waiting for NAND status\n");
anc_wr(anc, R_ANC_LINK_CONTROL, M_ANC_LINK_CONTROL_RESET_CMDQ);
return 0;
}
else if (intstatus & M_ANC_CHAN_INT_STATUS_READ_STATUS_ERR_RESPONSE)
{
uint8_t status = anc_rd(anc, R_ANC_LINK_NAND_STATUS);
// Invalid status (probably has error bit set due to a clean or uncorrectable page)
if (BOOT_LBA_TOKEN_UNKNOWN != meta[0]) // scanning blocks - may be clean or uecc
{
dprintf(DEBUG_CRITICAL, "Invalid NAND status 0x%02X\n", status);
}
anc_wr(anc, R_ANC_LINK_CONTROL, M_ANC_LINK_CONTROL_START);
ret = 0;
meta[0] = BOOT_ERR_BLANK;
}
nand_status = anc_rd(anc, R_ANC_CHAN_LINK_PIO_READ_FIFO);
if (nand_status != 0x40)
{
if (BOOT_LBA_TOKEN_UNKNOWN != meta[0]) // scanning blocks - may be clean or uecc
{
dprintf(DEBUG_CRITICAL, "Invalid NAND status: 0x%02X\n", nand_status);
}
ret = 0;
meta[0] = BOOT_ERR_BLANK;
}
for (lba = 0; lba < num_lbas; lba++)
{
uint32_t meta_index;
for (meta_index = 0; meta_index < (NAND_BOOT_BYTES_PER_META / sizeof(uint32_t)); meta_index++)
{
uint32_t meta_word = anc_rd(anc, R_ANC_CHAN_LINK_PIO_READ_FIFO);
if ((meta_index == 0) && (meta_word != meta[0]))
{
if (BOOT_LBA_TOKEN_UNKNOWN == meta[0])
{
meta[0] = meta_word;
}
else if (BOOT_ERR_BLANK != meta[0])
{
if (lba==0) {
dprintf(DEBUG_CRITICAL, "Invalid meta: expected 0x%08X got 0x%08x\n", meta[0], meta_word);
}
ret = lba;
}
}
}
}
intstatus = anc_boot_wait_interrupt(anc, M_ANC_CHAN_INT_STATUS_DMA_CMD_FLAG | M_ANC_CHAN_INT_STATUS_DMA_CMD_TIMEOUT);
if (!intstatus || (intstatus & M_ANC_CHAN_INT_STATUS_DMA_CMD_TIMEOUT))
{
dprintf(DEBUG_CRITICAL, "Timeout waiting for DMA to complete - 0x%08x\n", intstatus);
ret = 0;
}
anc_wr(anc, R_ANC_CHAN_INT_STATUS, M_ANC_CHAN_INT_STATUS_DMA_CMD_FLAG);
#if WITH_NON_COHERENT_DMA
platform_cache_operation(CACHE_INVALIDATE, data, num_lbas * NAND_BOOT_LOGICAL_PAGE_SIZE);
#endif
return ret;
}
typedef struct
{
uint16_t failure_type;
uint32_t start_page;
uint8_t page_len[3];
uint8_t checksum;
} anc_ppn_failure_info_t;
static void anc_llb_handle_geb(anc_t *anc)
{
uint32_t buffer[3];
dprintf(DEBUG_CRITICAL, "General Error Dected on Bus %d, attempting to pull failure info", anc->bus_id);
anc_wr(anc, R_ANC_CHAN_INT_ENABLE, 0);
anc_wr(anc, R_ANC_DMA_CONTROL, V_ANC_DMA_CONTROL_RESET(1) | V_ANC_DMA_CONTROL_START(1));
anc_wr(anc, R_ANC_LINK_CONTROL, (V_ANC_LINK_CONTROL_RESET_CMDQ(1) |
V_ANC_LINK_CONTROL_RESET_PIO_READ(1) |
V_ANC_LINK_CONTROL_START(1)));
anc_boot_put_link_command(anc, LINK_COMMAND__CE(1));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__GET_DEBUG_DATA));
anc_boot_put_link_command(anc, LINK_COMMAND__ADDR3(0xFF, 0xFF, 0xFF));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__SET_GET_FEATURES_CONFIRM));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(129000));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__OPERATION_STATUS));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(1));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_STATUS(0, 0x40, 0x40));
anc_boot_put_link_command(anc, LINK_COMMAND__CMD1(NAND_CMD__READ_SERIAL_OUTPUT));
anc_boot_put_link_command(anc, LINK_COMMAND__WAIT_TIME(1));
anc_boot_put_link_command(anc, LINK_COMMAND__READ_PIO(10, false, false));
anc_boot_put_link_command(anc, LINK_COMMAND__CE(0));
while (G_ANC_CHAN_LINK_PIO_READ_FIFO_STATUS_LEVEL(anc_rd(anc, R_ANC_CHAN_LINK_PIO_READ_FIFO_STATUS)) < 3)
{
;
}
buffer[0] = anc_rd(anc, R_ANC_CHAN_LINK_PIO_READ_FIFO);
buffer[1] = anc_rd(anc, R_ANC_CHAN_LINK_PIO_READ_FIFO);
buffer[2] = anc_rd(anc, R_ANC_CHAN_LINK_PIO_READ_FIFO);
panic("GEB detected failure type 0x%04X start page 0x%08X",
((anc_ppn_failure_info_t *)buffer)->failure_type, ((anc_ppn_failure_info_t *)buffer)->start_page);
}
+73
View File
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2011 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.
*/
#ifndef ANC_LLB_H
#define ANC_LLB_H
typedef struct dip_info_t
{
uint32_t cau;
uint32_t bus;
} dip_info_t;
typedef struct _anc_ppn_device_params_t
{
char header[16];
uint32_t caus_per_channel;
uint32_t cau_bits;
uint32_t blocks_per_cau;
uint32_t block_bits;
uint32_t pages_per_block;
uint32_t pages_per_block_slc;
uint32_t page_address_bits;
uint32_t address_bits_bits_per_cell;
uint32_t default_bits_per_cell;
uint32_t page_size;
uint32_t dies_per_channel;
uint32_t block_pairing_scheme;
uint32_t bytes_per_row_address;
uint8_t reserved[92];
uint8_t tRC;
uint8_t tREA;
uint8_t tREH;
uint8_t tRHOH;
uint8_t tRHZ;
uint8_t tRLOH;
uint8_t tRP;
uint8_t reserved2;
uint8_t tWC;
uint8_t tWH;
uint8_t tWP;
uint8_t reserved3[53];
uint32_t read_queue_size;
uint32_t program_queue_size;
uint32_t erase_queue_size;
uint32_t prep_function_buffer_size;
uint32_t tRST;
uint32_t tPURST;
uint32_t tSCE;
uint32_t tCERDY;
uint8_t reserved4[256];
} anc_ppn_device_params_t;
int anc_llb_init(void);
uint32_t anc_get_dip(uint32_t bus, uint32_t cau);
uint32_t anc_get_dies_per_channel();
uint32_t anc_llb_read_phys_page(uint32_t band,
uint32_t dip,
uint32_t page,
uint32_t num_lbas,
void *data,
uint32_t *meta);
#endif // ANC_LLB_H
File diff suppressed because it is too large. Load diff
+46
View File
@@ -0,0 +1,46 @@
# Copyright (C) 2011-2015 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.
#
LOCAL_DIR := $(GET_LOCAL_DIR)
GLOBAL_INCLUDES += $(LOCAL_DIR)
ifeq ($(PRODUCT),LLB)
OPTIONS += WITH_ANC_FIRMWARE=1
ALL_OBJS += \
$(LOCAL_DIR)/anc_bootrom.o \
$(LOCAL_DIR)/anc_llb.o \
$(LOCAL_DIR)/util_boot.o
endif
ifeq ($(IBOOT_ANC_NVRAM), true)
ifeq ($(PRODUCT),iBoot)
# Some targets like M7 can boot into chargetrap faster
# by skipping ANS in iBoot and instead relying on ANC
# for firmware and NVRAM access. These targets set
# IBOOT_ANC_NVRAM to true and ANC support is only conditionally
# active in iBoot.
OPTIONS += WITH_ANC_FIRMWARE=1
ALL_OBJS += \
$(LOCAL_DIR)/anc_bootrom.o \
$(LOCAL_DIR)/anc_llb.o \
$(LOCAL_DIR)/util_boot.o
endif
endif
ifeq ($(APPLICATION),SecureROM)
OPTIONS += WITH_ANC_BOOT=1
ALL_OBJS += \
$(LOCAL_DIR)/anc_bootrom.o \
$(LOCAL_DIR)/util_boot.o
endif
+893
View File
@@ -0,0 +1,893 @@
/*
* Copyright (c) 2011 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <debug.h>
#include <drivers/anc_boot.h>
#include <lib/blockdev.h>
#include <platform/memmap.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "util_boot.h"
#include "anc_bootrom.h"
#include "anc_llb.h"
#define BOOT_NUM_ELEMENT (12)
#define BOOT_NUM_PSLOTS (32)
typedef struct {
uint32_t page : 8;
uint32_t slot : 8;
uint32_t pslot : 8;
uint32_t RFU : 8;
} UtilBoot_location_t;
typedef struct {
UtilBoot_location_t location[UTIL_NUM_ELEMENT];
uint32_t prevUtilDMversion;
uint32_t prevUtilDMslot;
bool BCopyPresent;
uint8_t minorVersion;
} UtilBoot_t;
typedef struct {
UtilBootDM_t s;
uint32_t version;
uint32_t RFU; // was used by mxcfg
uint16_t erases[UTIL_NUM_PSLOTS];
uint8_t clogMajor;
uint8_t clogMinor;
uint8_t pendDefect; // defect pending (shuffle in progress).
uint8_t pad[4096 - sizeof(UtilBootDM_t) - 11 - UTIL_NUM_PSLOTS*2];
}UtilDM_t;
extern anc_ppn_device_params_t *anc_geom;
#define CASSERT(x, name) typedef char __ASP_CASSERT_##name[(x) ? 1 : -1]
CASSERT(sizeof(UtilDM_t)==4096, sizeUtilDMcorrect);
extern int anc_num_channel;
#define quantup(num, gran) (((num) + (gran) - 1) / (gran))
#define ROUNDUPTO(num, gran) ((((num) + (gran) - 1) / (gran)) * (gran))
static UtilDM_t* UtilDM;
static UtilDM_t* UtilDM_backup;
static UtilBoot_t Util;
#define META_LBA_INDEX 0
static uint32_t meta[4];
// prototypes
uint32_t Boot_Slip (uint32_t slot);
void Boot_BandDip (uint32_t pslot, uint32_t* band, uint32_t* dip);
static int anc_read_fw_block (struct blockdev *_dev, void *data, block_addr req_offset, uint32_t max_size);
bool Boot_PostProcessDM(UtilDM_t *p, uint32_t slot, uint32_t page, uint32_t *meta);
#if WITH_LLB_NVRAM
bool Boot_Read(void *buffer, uint32_t page, uint32_t slot, uint32_t *meta);
uint32_t Boot_Find_Fast_Scan(void *buffer, uint32_t page, uint32_t slot);
void Boot_Find_Fast(void *buffer);
void Boot_Find_UtilDM(void *buffer);
static int anc_read_nvram_block (struct blockdev *_dev, void *data, block_addr req_offset, uint32_t max_size);
#endif
#if WITH_LLB_BLKDEV
static int anc_read_llb_block (struct blockdev *_dev, void *data, block_addr req_offset, uint32_t max_size);
#endif
// POR equivalent. Resets the Nand but does not do discovery. Zeros out the UtilDM->s.utilMajor to indicate UtilDM not loaded.
bool anc_reset(int resetMode) {
bool err;
UtilDM->s.utilMajor = 0; // indicate UtilDM not loaded.
meta[1]=0;
meta[2]=0;
meta[3]=0;
err = anc_bootrom_init(true, resetMode);
if (err != 0)
{
return false;
}
else
{
return true;
}
}
// Entry point from LLB
bool anc_firmware_init(void)
{
bool err;
struct blockdev *bdev;
#if WITH_LLB_NVRAM
struct blockdev *nv_bdev;
#endif
#if WITH_LLB_BLKDEV
struct blockdev *llb_bdev;
#endif
UtilDM = memalign(sizeof(UtilDM_t), CPU_CACHELINE_SIZE);
UtilDM_backup = memalign(sizeof(UtilDM_t), CPU_CACHELINE_SIZE);
if (!UtilDM) {
dprintf(DEBUG_CRITICAL, "Failed to malloc UtilDM\n");
return false;
}
if (!UtilDM_backup) {
dprintf(DEBUG_CRITICAL, "Failed to malloc UtilDM_backup\n");
return false;
}
bzero(UtilDM, sizeof(UtilDM_t));
bzero(UtilDM_backup, sizeof(UtilDM_t));
uint32_t block_size;
uint32_t size;
err = anc_llb_init();
if (err != 0)
{
dprintf(DEBUG_CRITICAL,"Failed to reset ANC\n");
return false;
}
// Load UtilDM
meta[META_LBA_INDEX] = BOOT_LBA_TOKEN_UTILDM;
if (anc_llb_read_phys_page(BOOT_BAND_UTILDM, BOOT_DIP_UTILDM, BOOT_PAGE_UTILDM, BOOT_SIZE_UTILDM, UtilDM_backup, &meta[0])!=BOOT_SIZE_UTILDM) {
UtilDM->s.utilMajor = 0;
return(false);
}
Boot_PostProcessDM(UtilDM_backup,0,0,meta);
dprintf(DEBUG_CRITICAL,"Loaded UtilDM revision = %2d.%2d\n",UtilDM->s.utilMajor, UtilDM->s.utilMinor);
if (UtilDM->s.eSize[BOOT_ELEMENT_FW] == 0)
{
dprintf(DEBUG_CRITICAL,"No firmware region programmed; not creating block device\n");
return false;
}
bdev = malloc(sizeof(struct blockdev));
if (!bdev)
{
dprintf(DEBUG_CRITICAL,"Failed to allocate blockdev\n");
return false;
}
block_size = BOOT_BYTES_PER_SEC * UtilDM->s.secPerPage;
size = ROUNDUPTO(UtilDM->s.eSize[BOOT_ELEMENT_FW] * BOOT_BYTES_PER_SEC, block_size);
if (construct_blockdev(bdev,
"anc_firmware",
size,
block_size) != 0)
{
dprintf(DEBUG_CRITICAL,"Failed to construct block device\n");
free(bdev);
return false;
}
bdev->read_block_hook = &anc_read_fw_block;
if (register_blockdev(bdev) != 0)
{
dprintf(DEBUG_CRITICAL,"Failed to register bdev\n");
free(bdev);
return false;
}
// ANC DMA has a minimum 16byte alignment requirement
// But if the CPU isn't coherent with ANC, we need to increase the alignment requirement
#if WITH_NON_COHERENT_DMA
blockdev_set_buffer_alignment(bdev, __max(ANC_MIN_ALIGNMENT, CPU_CACHELINE_SIZE));
#else
blockdev_set_buffer_alignment(bdev, ANC_MIN_ALIGNMENT);
#endif
#if 0
uint32_t *buf;
uint32_t i;
buf = (uint32_t *)INSECURE_MEMORY_BASE;
dprintf(DEBUG_CRITICAL,"Starting test at %p\n", buf);
memset(buf, 0, 520192);
anc_read_fw_block(NULL, buf, 0, 127);
for (i = 0; i < 520192 / 4; i++)
{
if (buf[i] != i)
{
panic("Miscompare at 0x%08X got 0x%08X", i, buf[i]);
}
}
dprintf(DEBUG_CRITICAL,"compare passed\n");
anc_read_fw_block(NULL, buf, 126, 1);
i = 126 * 1024;
uint32_t j;
for (j = 0; j < 1024; j++, i++)
{
if (buf[j] != i)
{
panic("Miscompare expected 0x%08X got 0x%08X", i, buf[j]);
}
}
#endif
#if WITH_LLB_NVRAM
// keep trying to find the latest
Boot_Find_UtilDM(UtilDM_backup);
Boot_Find_Fast(UtilDM_backup);
nv_bdev = malloc(sizeof(struct blockdev));
if (!nv_bdev)
{
dprintf(DEBUG_CRITICAL,"Failed to allocate nvram blockdev\n");
return false;
}
if (construct_blockdev(nv_bdev,
"nvram",
8192,
8192) != 0)
{
dprintf(DEBUG_CRITICAL,"Failed to construct block device\n");
free(nv_bdev);
return false;
}
nv_bdev->read_block_hook = &anc_read_nvram_block;
if (register_blockdev(nv_bdev) != 0)
{
dprintf(DEBUG_CRITICAL,"Failed to register bdev\n");
free(nv_bdev);
return false;
}
#endif
#if WITH_LLB_BLKDEV
llb_bdev = malloc(sizeof(struct blockdev));
if (!llb_bdev)
{
dprintf(DEBUG_CRITICAL,"Failed to allocate LLB blockdev\n");
return false;
}
block_size = BOOT_BYTES_PER_SEC * UtilDM->s.secPerPage;
size = ROUNDUPTO(UtilDM->s.eSize[BOOT_ELEMENT_LLB] * BOOT_BYTES_PER_SEC, block_size);
if (construct_blockdev(llb_bdev,
"anc_llb",
size,
block_size) != 0)
{
dprintf(DEBUG_CRITICAL,"Failed to construct block device\n");
free(llb_bdev);
return false;
}
llb_bdev->read_block_hook = &anc_read_llb_block;
// ANC DMA has a minimum 16byte alignment requirement
// But if the CPU isn't coherent with ANC, we need to increase the alignment requirement
#if WITH_NON_COHERENT_DMA
blockdev_set_buffer_alignment(llb_bdev, __max(ANC_MIN_ALIGNMENT, CPU_CACHELINE_SIZE));
#else
blockdev_set_buffer_alignment(llb_bdev, ANC_MIN_ALIGNMENT);
#endif
if (register_blockdev(llb_bdev) != 0)
{
dprintf(DEBUG_CRITICAL,"Failed to register bdev\n");
free(llb_bdev);
return false;
}
#endif
return true;
}
// LLB loader. Will load UtilDM on first read request which will reset the UtilDM->s.utilMajor to be current.
size_t anc_read_llb(void* data, size_t max_size) {
uint32_t page;
uint32_t size, thisSize;
uint32_t secPerPage;
bool err;
if(0 == UtilDM->s.utilMajor) {
err = anc_bootrom_init(false, 0); // do discovery
if(err) {
return(0);
}
meta[META_LBA_INDEX] = BOOT_LBA_TOKEN_UTILDM;
if (!anc_bootrom_read_phys_page(BOOT_BAND_UTILDM, BOOT_DIP_UTILDM, BOOT_PAGE_UTILDM, BOOT_SIZE_UTILDM, UtilDM, &meta[0])) {
UtilDM->s.utilMajor = 0;
return(false);
}
dprintf(DEBUG_CRITICAL,"Loaded UtilDM revision = %2d.%2d\n",UtilDM->s.utilMajor, UtilDM->s.utilMinor);
}
meta[META_LBA_INDEX] = BOOT_LBA_TOKEN_LLB;
secPerPage = UtilDM->s.secPerPage;
page = BOOT_PAGE_LLB; // LLB doesn't start at page=0
size = (uint32_t) max_size / BOOT_BYTES_PER_SEC;
dprintf(DEBUG_CRITICAL,"Reading LLB for size=%d\n", size);
if(size > UtilDM->s.eSize[BOOT_ELEMENT_LLB]) {
size = UtilDM->s.eSize[BOOT_ELEMENT_LLB];
dprintf(DEBUG_CRITICAL,"Trimmed size to written size of %5d\n", size);
}
max_size = size; // record for result to caller
while(size) {
if(size > secPerPage) {
thisSize = secPerPage;
} else {
thisSize = size;
}
if(!anc_bootrom_read_phys_page(BOOT_BAND_LLB, BOOT_DIP_LLB, page, thisSize, data, &meta[0])) {
break;
}
size -= thisSize;
page++;
data += thisSize * BOOT_BYTES_PER_SEC;
}
return( (max_size - size) * BOOT_BYTES_PER_SEC );
}
// the code below this comment is required for loading the FW element.
// clone of UtilDM_Slip from product code
uint32_t Boot_Slip(uint32_t pslot) {
uint32_t i;
i = 0;
while(i < UtilDM->s.numDefects && (pslot >= UtilDM->s.defects[i]) ) {
pslot++;
i++;
}
return(pslot);
}
// clone of UtilDM_BandDip
// Converts the physical slot to a band,dip that is consistent with Nand engine's usage. returns dip and writes band to bandPtr
void Boot_BandDip(uint32_t pslot, uint32_t* bandPtr, uint32_t* dipPtr) {
uint32_t bus;
uint32_t die;
uint32_t plane;
uint32_t cau;
uint32_t slot_itr;
uint32_t band;
uint32_t num_bus;
uint32_t die_per_bus;
uint32_t cau_per_die;
if (anc_get_dies_per_channel() * anc_num_channel == UtilDM->s.numDie) {
num_bus = anc_num_channel;
} else {
// geom_num_bus is incorrect because a bus didn't come up.
num_bus = UtilDM->s.numDie / anc_get_dies_per_channel();
}
die_per_bus = UtilDM->s.numDie / anc_num_channel;
cau_per_die = UtilDM->s.numPlanes;
bus = 0;
die = 0;
plane = 0;
band = 0;
for (slot_itr = 0; slot_itr < pslot; slot_itr++) {
bus++;
if(bus >= num_bus) {
bus = 0;
die++;
if (die >= die_per_bus){
die = 0;
plane++;
if (plane >= cau_per_die) {
plane=0;
band++;
}
}
}
}
cau = (die * cau_per_die) + plane;
*bandPtr = band;
*dipPtr = anc_get_dip(bus, cau);
}
// leveraged core UtilDM_PostProcess
bool Boot_PostProcessDM(UtilDM_t *p, uint32_t page, uint32_t slot, uint32_t *meta)
{
uint32_t wasGrownDef;
uint32_t newV;
uint32_t currV;
bool result = false;
if (meta[0] == BOOT_ERR_BLANK) {
dprintf(DEBUG_INFO,"UTIL - [%2d,%2d] BLANK! \n", slot, page);
}
else if (p->s.utilMajor != BOOT_UTIL_MAJOR) {
dprintf(DEBUG_INFO,"UTIL - [%2d,%2d] !!!!! Found UtilDM with different major version =0x%08x ", slot, page,
p->s.utilMajor);
return false;
} else if (p->s.utilMinor < Util.minorVersion) {
dprintf(DEBUG_INFO,"UTIL - [%2d,%2d] !!!!! Found UtilDM with major version 0x%08x but minor version 0x%08x (less than minimum 0x%08x)",
slot, page, p->s.utilMajor, p->s.utilMinor, Util.minorVersion);
return false;
} else {
newV = p->version;
currV = UtilDM->version;
if (newV >= currV) {
Util.prevUtilDMversion = currV;
Util.prevUtilDMslot = Util.location[BOOT_ELEMENT_UTILDM].slot;
Util.location[BOOT_ELEMENT_UTILDM].slot = slot;
Util.location[BOOT_ELEMENT_UTILDM].page = page;
dprintf(DEBUG_INFO,"UTIL - [%2d,%2d] Adopting version=0x%08x numDefects=%2d numSlots=%2d\n", slot,
page, newV, p->s.numDefects,
p->s.numSlots);
wasGrownDef = UtilDM->s.numDefects;
memcpy(UtilDM, p, sizeof(UtilDM_t));
if (wasGrownDef != UtilDM->s.numDefects) {
result = true;
}
if (slot == BOOT_SLOT_FAST_B) {
Util.BCopyPresent = true;
} else {
Util.BCopyPresent = false; // Always expect a B copy on the right of A
}
} else if (!Util.BCopyPresent && (slot == BOOT_SLOT_FAST_B) && ((currV == (newV + 1)))) {
// This is the case where we have a newer copy on the left of the older copy <rdar://problem/15261567>
// accept B copy if it's version is 1 less than A copy, consider B slot, the previous slot
Util.prevUtilDMversion = newV;
Util.prevUtilDMslot = slot;
dprintf(DEBUG_INFO,"UTIL - [%2d,%2d] Adopting B copy version=0x%08x numDefects=%2d numSlots=%2d", slot,
page, newV, p->s.numDefects,
p->s.numSlots);
Util.BCopyPresent = true;
} else {
dprintf(DEBUG_INFO,"UTIL - [%2d,%2d] Ignoring version=0x%08x numDefects=%2d numSlots=%2d", slot,
page, newV, p->s.numDefects,
p->s.numSlots);
}
}
return result;
}
static int anc_read_fw_block (struct blockdev *_dev, void *data, block_addr offset, uint32_t sectors) {
uint32_t slot;
uint32_t slotB; // offset for the B copy of FW. 0 indicates not doing retry
uint32_t pslot;
uint32_t page;
uint32_t band;
uint32_t dip;
uint32_t thisSize;
uint32_t secPerPage;
uint32_t pagePerSlot;
uint32_t err;
uint32_t max_size;
// Incoming sectors are in terms of page size (to prevent the client from
// attempting a sub-page read).
sectors *= UtilDM->s.secPerPage;
offset *= UtilDM->s.secPerPage;
dprintf(DEBUG_INFO,"Reading sectors from FW element from offset=%4d for a size=%4d\n", offset, sectors);;
if(BOOT_UTIL_MAJOR != UtilDM->s.utilMajor) {
dprintf(DEBUG_CRITICAL,"Exiting anc_read_fw because UtilDM->s.utilMajor is incorrect\n");
return(0);
}
if(offset > UtilDM->s.eSize[BOOT_ELEMENT_FW]) {
dprintf(DEBUG_CRITICAL,"Requested offset exceeds size of firmware\n");
return(0);
}
if(sectors + offset > UtilDM->s.eSize[BOOT_ELEMENT_FW]) {
dprintf(DEBUG_CRITICAL,"Attempt to read past end of firmware\n");
return(0);
}
meta[META_LBA_INDEX] = BOOT_LBA_TOKEN_FW;
secPerPage = UtilDM->s.secPerPage;
pagePerSlot = UtilDM->s.numPages;
page = BOOT_PAGE_FW; // starting location for FW in [slot,page]
slot = BOOT_SLOT_FW;
while(offset >= secPerPage) {
offset -= secPerPage;
page++;
if(page >= pagePerSlot) {
page = 0;
slot++;
}
}
// lba now converted to page/slot
err = 0;
slotB = 0;
max_size = sectors; // save for result
while(sectors) {
pslot = Boot_Slip(slot+slotB);
Boot_BandDip(pslot, &band, &dip);
thisSize = sectors;
if(thisSize > secPerPage) {
thisSize = secPerPage;
}
if (anc_llb_read_phys_page(band, dip, page, thisSize, data, &meta[0])!=secPerPage) {
dprintf(DEBUG_CRITICAL,"Failed to read page\n");
if(!slotB) {
slotB = UtilDM->s.sizeFWslots;
} else {
break;
}
} else {
slotB = 0;
sectors -= thisSize;
data += thisSize * BOOT_BYTES_PER_SEC;
page++;
if(page >= pagePerSlot) {
page = 0;
slot++;
}
}
}
if(err) {
dprintf(DEBUG_CRITICAL,"Encountered error = %d\n", err);
}
return (max_size - sectors) / secPerPage;
}
#if WITH_LLB_BLKDEV
// LLB loader. Will load UtilDM on first read request which will reset the UtilDM->s.utilMajor to be current.
static int anc_read_llb_block (struct blockdev *_dev, void *data, block_addr offset, uint32_t sectors) {
uint32_t page;
uint32_t size, thisSize, max_size, secRead;
uint32_t secPerPage;
bool err;
if(0 == UtilDM->s.utilMajor) {
err = anc_bootrom_init(false, 0); // do discovery
if(err) {
return(0);
}
meta[META_LBA_INDEX] = BOOT_LBA_TOKEN_UTILDM;
if (anc_llb_read_phys_page(BOOT_BAND_UTILDM, BOOT_DIP_UTILDM, BOOT_PAGE_UTILDM, BOOT_SIZE_UTILDM, UtilDM, &meta[0])!=BOOT_SIZE_UTILDM) {
UtilDM->s.utilMajor = 0;
return(false);
}
dprintf(DEBUG_CRITICAL,"Loaded UtilDM revision = %2d.%2d\n",UtilDM->s.utilMajor, UtilDM->s.utilMinor);
}
meta[META_LBA_INDEX] = BOOT_LBA_TOKEN_LLB;
secPerPage = UtilDM->s.secPerPage;
page = BOOT_PAGE_LLB + offset;
size = sectors * secPerPage;
dprintf(DEBUG_SPEW,"Reading LLB for size=%d, page=%u\n", sectors, (unsigned int)page);
if(size > UtilDM->s.eSize[BOOT_ELEMENT_LLB]) {
size = UtilDM->s.eSize[BOOT_ELEMENT_LLB];
dprintf(DEBUG_CRITICAL,"Trimmed size to written size of %5d\n", size);
}
max_size = size; // record for result to caller
while(size) {
if(size > secPerPage) {
thisSize = secPerPage;
} else {
thisSize = size;
}
secRead = anc_llb_read_phys_page(BOOT_BAND_LLB, BOOT_DIP_LLB, page, thisSize, data, &meta[0]);
if ((secRead == 0) || (secRead<thisSize && size>secPerPage)) { // only allow partial NAND page at the end of read
break;
}
size -= thisSize;
page++;
data += thisSize * BOOT_BYTES_PER_SEC;
}
return( (max_size - size) / secPerPage );
}
#endif
#if WITH_LLB_NVRAM
static int anc_read_nvram_block (struct blockdev *_dev, void *data, block_addr offset, uint32_t sectors) {
uint32_t slot;
uint32_t slotB; // offset for the B copy of FW. 0 indicates not doing retry
uint32_t pslot;
uint32_t page;
uint32_t band;
uint32_t dip;
uint32_t thisSize;
uint32_t secPerPage;
uint32_t pagePerSlot;
uint32_t err;
uint32_t max_size;
// Incoming sectors are in terms of page size (to prevent the client from
// attempting a sub-page read).
sectors *= 2; // blocksize is 8k not 4k
dprintf(DEBUG_INFO,"Reading sectors from NVRAM element from offset=%4d for a size=%4d\n", offset, sectors);
if(BOOT_UTIL_MAJOR != UtilDM->s.utilMajor) {
dprintf(DEBUG_CRITICAL,"Exiting anc_read_fw because UtilDM->s.utilMajor is incorrect\n");
return(0);
}
if(offset > 0) {
dprintf(DEBUG_CRITICAL,"Requested offset exceeds size of nvram\n");
return(0);
}
if(sectors + offset > UtilDM->s.eSize[BOOT_ELEMENT_NVRAM]) {
dprintf(DEBUG_CRITICAL,"Attempt to read past end of nvram\n");
return(0);
}
meta[META_LBA_INDEX] = BOOT_LBA_TOKEN_NVRAM;
secPerPage = UtilDM->s.secPerPage;
pagePerSlot = UtilDM->s.numPages;
page = Util.location[BOOT_ELEMENT_NVRAM].page; // starting location for NVRAM in [slot,page]
slot = Util.location[BOOT_ELEMENT_NVRAM].slot;
err = 0;
slotB = 0;
max_size = sectors; // save for result
if (page==0 && slot==0) {// NVRAM not populated
sectors=0;
}
while(sectors) {
pslot = Boot_Slip(slot+slotB);
Boot_BandDip(pslot, &band, &dip);
thisSize = sectors;
if(thisSize > secPerPage) {
thisSize = secPerPage;
}
if(!anc_llb_read_phys_page(band, dip, page, thisSize, data, &meta[0])) {
dprintf(DEBUG_CRITICAL,"Failed to read page\n");
if(!slotB) {
slotB = 1;
} else {
break;
}
} else {
slotB = 0;
sectors -= thisSize;
data += thisSize * BOOT_BYTES_PER_SEC;
page++;
if(page >= pagePerSlot) {
page=0;
slot++;
if (sectors) { // still more to read?
err=1;
break;
}
}
}
}
if(err) {
dprintf(DEBUG_CRITICAL,"Encountered error = %d\n", err);
}
return (max_size - sectors) / 2;
}
uint32_t Boot_Find_Fast_Scan(void *buffer, uint32_t page, uint32_t slot) {
uint32_t element;
uint32_t ElementSizePages;
if (0 == page) {
UtilDM->version = 1;
}
for (; page < UtilDM->s.numPages;) {
meta[0] = BOOT_LBA_TOKEN_UNKNOWN;
if (Boot_Read(buffer,page,slot,meta)) {
if ((meta[0] >= BOOT_LBA_TOKEN_FAST_FIRST) && (meta[0] <= BOOT_LBA_TOKEN_FAST_LAST)) {
element = meta[0] - BOOT_LBA_TOKEN_ELEMENTS;
dprintf(DEBUG_INFO,"UTIL_MINOR - [%2d,%2d] Found %d\n", slot, page, element);
if (0 == UtilDM->s.eSize[element]) {
dprintf(DEBUG_CRITICAL,"UTIL - Element of unknown size!!! \n");
page++;
} else {
if (element == BOOT_ELEMENT_UTILDM) {
Boot_PostProcessDM(buffer,page,slot,meta);
} else {
Util.location[element].slot = slot;
Util.location[element].page = page;
dprintf(DEBUG_INFO,"UTIL_MINOR - Found location [%2d,%2d] for element %d\n", Util.location[element].slot,
Util.location[element].page,element);
}
page += quantup(UtilDM->s.eSize[element], UtilDM->s.secPerPage);
}
} else if (!((meta[0] >= BOOT_LBA_TOKEN_FAST_FIRST) && (meta[0] <= BOOT_LBA_TOKEN_FAST_LAST)) && (meta[0] < BOOT_LBA_TOKEN_UNKNOWN)) {
element = meta[0] - BOOT_LBA_TOKEN_ELEMENTS;
ElementSizePages = 0;
if (element < UTIL_NUM_ELEMENT) {
ElementSizePages = quantup(UtilDM->s.eSize[element], UtilDM->s.secPerPage);
}
if ((ElementSizePages == 0) || ((page + ElementSizePages) > UtilDM->s.numPages)) {
dprintf(DEBUG_CRITICAL,"UTIL - [%2d,%2d] Something Broken, UtilDM->s.eSize[%d]=%d!!\n", slot, page, element,
ElementSizePages);
page = 0;
break;
} else {
dprintf(DEBUG_CRITICAL,"Skipping unknown element %d\n", element);
}
page += ElementSizePages;
}
}
else
{
if ( BOOT_ERR_BLANK != meta[0]) { // something ugly ?, either unknown LBA value or unc
dprintf(DEBUG_CRITICAL,"UTIL - [%2d,%2d] Something Broken !! meta=%x\n", slot, page, meta[0]);
page = 0;
} else {
dprintf(DEBUG_INFO,"UTIL_MINOR - [%2d,%2d] Blank !!\n", slot, page);
}
break;
}
}
return page;
}
// passed in a single sector seg with buffer allocated
void Boot_Find_Fast(void *buffer) {
uint32_t page;
uint32_t slot, tmpslot;
uint32_t element;
tmpslot = slot = Util.location[BOOT_ELEMENT_UTILDM].slot;// slot with the highest version UtilDM
if ((BOOT_SLOT_FAST_B == slot) && (BOOT_SLOT_FAST_A == Util.prevUtilDMslot) && (Util.prevUtilDMversion == UtilDM->version)) { // NORMAL state
slot = BOOT_SLOT_FAST_A;
page = Boot_Find_Fast_Scan(buffer, 1, slot);
if (page) {
slot = BOOT_SLOT_FAST_B; // read copy B
meta[0] = BOOT_LBA_TOKEN_UNKNOWN;
if (!Boot_Read(buffer,page-1,slot,meta)) {
dprintf(DEBUG_INFO,"UTIL - Fast Copy B last page broken!\n");
page = 0;
} else if (page < UtilDM->s.numPages) { // last written page of B is ok, check next page is blank
slot = BOOT_SLOT_FAST_B;
meta[0] = BOOT_LBA_TOKEN_UNKNOWN;
if (!Boot_Read(buffer,page,slot,meta) && meta[0]!=BOOT_ERR_BLANK) {
dprintf(DEBUG_CRITICAL,"UTIL - Fast Copy B next location not Blank!!\n");
page = 0;
}
}
}
} else {
// spew enough data to know why we failed the IF term.
dprintf(DEBUG_CRITICAL,"UTIL - FastA:%2d slot:%2d prevSlot:%2d prevV:%08x currV:%08x\n",
BOOT_SLOT_FAST_A, slot, Util.prevUtilDMslot,
Util.prevUtilDMversion, UtilDM->version);
if ((BOOT_SLOT_LLB != Util.prevUtilDMslot) && !(!Util.BCopyPresent && (BOOT_SLOT_FAST_B == Util.prevUtilDMslot))) {
/*Scan the older copy first*/
slot = Util.prevUtilDMslot;
Boot_Find_Fast_Scan(buffer, 0, slot);
}
slot = tmpslot;
Boot_Find_Fast_Scan(buffer, 0, slot);
page = 0;
}
for (element = BOOT_ELEMENT_FAST_FIRST; element <= BOOT_ELEMENT_FAST_LAST; element++) {
dprintf(DEBUG_INFO,"UTIL - Location [%2d,%2d] for element %d\n", Util.location[element].slot, Util.location[element].page,
element);
}
}
bool Boot_Read(void *buffer, uint32_t page, uint32_t slot, uint32_t *meta)
{
uint32_t band, dip;
uint32_t pslot = Boot_Slip(slot);//+slotB);
Boot_BandDip(pslot, &band, &dip);
return (anc_llb_read_phys_page(band, dip, page, 1, buffer, &meta[0]));
}
void Boot_Find_UtilDM(void *buffer)
{
uint32_t firstSlot = Util.location[BOOT_ELEMENT_UTILDM].slot;
uint32_t slot, page;
UtilDM->s.numSlots = 32; //UTIL_BLIND_SEARCH;
page=0;
for (slot = BOOT_SLOT_FAST_A; slot < UtilDM->s.numSlots;) {
if (slot == UtilDM->pendDefect) {
slot++;
continue;
}
meta[0]=BOOT_LBA_TOKEN_UNKNOWN;
Boot_Read(buffer,page,slot,meta);
if (Boot_PostProcessDM(buffer,page,slot,meta)) {
dprintf(DEBUG_INFO,"UTIL - defect growth\n"); // don't assume that we're aligned.
slot = BOOT_SLOT_FAST_A; // start over
Util.location[BOOT_ELEMENT_UTILDM].slot = firstSlot; // and assimlate again.
} else {
slot++;
}
}
if (0 == UtilDM->version) { // found nothing?
dprintf(DEBUG_CRITICAL,"UTIL - Found no UtilDM!!! Nand should be blank\n");
UtilDM->s.numSlots = 0; // then reset numSlots
UtilDM->s.numDefects = 0;
}
}
#endif
+99
View File
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2011 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.
*/
#ifndef ASPsim_utilBoot_h
#define ASPsim_utilBoot_h
#define UTIL_NUM_ELEMENT (12)
#define UTIL_NUM_PSLOTS (128)
#define UTIL_NO_DEFECT (0xff)
typedef struct {
uint8_t utilMajor;
uint8_t utilMinor;
uint16_t eSize[UTIL_NUM_ELEMENT]; // element size in sectors, 0 if never written.
uint8_t defects[UTIL_NUM_PSLOTS];
uint16_t numDefects;
uint16_t numPages;
uint8_t secPerPage;
uint8_t numPlanes;
uint8_t numDie;
uint8_t numSlots;
uint8_t sizeFWslots; // size of the firmware element in slot granularity
uint8_t RFU[8]; // reserved for future use
}UtilBootDM_t;
#define BOOT_NO_DEFECT (0xff)
#define BOOT_LBA_TOKEN_BASE 0xFF00000
#define BOOT_LBA_TOKEN_UTILNOP (BOOT_LBA_TOKEN_BASE + 1)
#define BOOT_LBA_TOKEN_ELEMENTS (BOOT_LBA_TOKEN_BASE + 2)
#define BOOT_LBA_TOKEN_LLB (BOOT_LBA_TOKEN_BASE + 2)
#define BOOT_LBA_TOKEN_FW (BOOT_LBA_TOKEN_BASE + 3)
#define BOOT_LBA_TOKEN_UTILDM (BOOT_LBA_TOKEN_BASE + 4)
#define BOOT_LBA_TOKEN_DM (BOOT_LBA_TOKEN_BASE + 0x05)
#define BOOT_LBA_TOKEN_CTRLBITS (BOOT_LBA_TOKEN_BASE + 0x06)
#define BOOT_LBA_TOKEN_EFFACEABLE (BOOT_LBA_TOKEN_BASE + 0x07)
#define BOOT_LBA_TOKEN_NVRAM (BOOT_LBA_TOKEN_BASE + 0x08)
#define BOOT_LBA_TOKEN_SYSCFG (BOOT_LBA_TOKEN_BASE + 0x09)
#define BOOT_LBA_TOKEN_PANICLOG (BOOT_LBA_TOKEN_BASE + 0x0A)
#define BOOT_LBA_TOKEN_UNKNOWN (BOOT_LBA_TOKEN_BASE + 0x10)
#define BOOT_LBA_TOKEN_FAST_FIRST BOOT_LBA_TOKEN_UTILDM
#define BOOT_LBA_TOKEN_FAST_LAST BOOT_LBA_TOKEN_PANICLOG
#define BOOT_ELEMENT_UTILDM (2)
#define BOOT_BAND_UTILDM (0)
#define BOOT_DIP_UTILDM (0)
#define BOOT_PAGE_UTILDM (0)
#define BOOT_SIZE_UTILDM (1)
#define BOOT_ELEMENT_LLB (0)
#define BOOT_BAND_LLB (0)
#define BOOT_DIP_LLB (0)
#define BOOT_PAGE_LLB (1)
#define BOOT_SIZE_LLB (UtilDM->s.eSize[BOOT_ELEMENT_LLB])
#define BOOT_ELEMENT_FW (1)
#define BOOT_SLOT_FW (1)
#define BOOT_PAGE_FW (0)
#define BOOT_SIZE_FW (UtilDM->s.eSize[BOOT_ELEMENT_FW])
#define BOOT_ELEMENT_NVRAM (6)
#define BOOT_BYTES_PER_SEC (4096)
#define BOOT_NO_ERROR (0)
#define BOOT_ERR_ABORT (10)
#define BOOT_UTIL_MAJOR (8) // MUST match AppleStorageProcessor/src/aspcore/core/format.h
#define BOOT_ELEMENT_FAST_FIRST BOOT_ELEMENT_UTILDM
#define BOOT_ELEMENT_FAST_LAST BOOT_ELEMENT_NVRAM
#define BOOT_SLOT_LLB 0
#define BOOT_SLOT_FW_A_FIRST 1
#define BOOT_SLOT_FW_A_LAST (BOOT_SLOT_FW_A_FIRST + (1 * UtilDM->s.sizeFWslots) - 1)
#define BOOT_SLOT_FW_B_LAST (BOOT_SLOT_FW_A_FIRST + (2 * UtilDM->s.sizeFWslots) - 1)
#define BOOT_SLOT_SYSCFG_A (BOOT_SLOT_FW_B_LAST + 1)
#define BOOT_SLOT_SYSCFG_B (BOOT_SLOT_SYSCFG_A + 1)
#define BOOT_SLOT_FAST_A (BOOT_SLOT_SYSCFG_B + 1)
#define BOOT_SLOT_FAST_B (BOOT_SLOT_FAST_A + 1)
#define BOOT_SLOT_PANICLOG (BOOT_SLOT_FAST_B + 1)
#define BOOT_ERR_BLANK 0xffffffff
#endif