| Index: arch/arm/cpu/arm_cortexa9/tegra2/warmboot.c
|
| diff --git a/arch/arm/cpu/arm_cortexa9/tegra2/warmboot.c b/arch/arm/cpu/arm_cortexa9/tegra2/warmboot.c
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..6d0a47249fce3b995f1e1f3fadda707a077013b1
|
| --- /dev/null
|
| +++ b/arch/arm/cpu/arm_cortexa9/tegra2/warmboot.c
|
| @@ -0,0 +1,321 @@
|
| +/*
|
| + * (C) Copyright 2010
|
| + * NVIDIA Corporation <www.nvidia.com>
|
| + *
|
| + * See file CREDITS for list of people who contributed to this
|
| + * project.
|
| + *
|
| + * This program is free software; you can redistribute it and/or
|
| + * modify it under the terms of the GNU General Public License as
|
| + * published by the Free Software Foundation; either version 2 of
|
| + * the License, or (at your option) any later version.
|
| + *
|
| + * This program is distributed in the hope that it will be useful,
|
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| + * GNU General Public License for more details.
|
| + *
|
| + * You should have received a copy of the GNU General Public License
|
| + * along with this program; if not, write to the Free Software
|
| + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
| + * MA 02111-1307 USA
|
| + */
|
| +
|
| +#include <common.h>
|
| +#include <asm/arch/nv_drf.h>
|
| +#include <asm/arch/nvboot_error.h>
|
| +#include <asm/arch/nvcommon.h>
|
| +#include <asm/arch/nverror.h>
|
| +#include <asm/arch/tegra2.h>
|
| +#include <asm/arch/nv_hardware_access.h>
|
| +#include "warmboot.h"
|
| +
|
| +NvBootError sign_data_block(NvU8 *source,
|
| + NvU32 length,
|
| + NvU8 *hash);
|
| +
|
| +/* -------------------------------------
|
| + * Configuration parameters
|
| + * -------------------------------------
|
| + */
|
| +
|
| +/* -------------------------------------
|
| + * Definitions
|
| + * -------------------------------------
|
| + */
|
| +
|
| +/* NOTE: If more than one of the following is enabled, only one of them will
|
| + * actually be used. RANDOM takes precedence over PATTERN and ZERO, and
|
| + * PATTERN takes precedence overy ZERO.
|
| + */
|
| +#define RANDOM_AES_BLOCK_IS_RANDOM 1 /* Set nonzero to randomize the header */
|
| +#define RANDOM_AES_BLOCK_IS_PATTERN 1 /* Set nonzero to patternize the header */
|
| +#define RANDOM_AES_BLOCK_IS_ZERO 1 /* Set nonzero to clear the header */
|
| +
|
| +/* Address at which AP20 WB0 code runs */
|
| +/* The address must not overlap the bootrom's IRAM usage */
|
| +#define AP20_WB0_RUN_ADDRESS 0x40020000
|
| +#define AP20_IRAM_BASE 0x40000000
|
| +
|
| +/* -------------------------------------
|
| + * Global External Labels
|
| + * -------------------------------------
|
| + */
|
| +
|
| +void wb_start(void); /* Start of WB assembly code */
|
| +void wb_end(void); /* End of WB assembly code */
|
| +
|
| +#define NV_ADDRESS_MAP_FUSE_BASE 0x7000F800UL
|
| +#define FUSE_PRODUCTION_MODE_0 _MK_ADDR_CONST(0x100)
|
| +#define FUSE_FA_0 _MK_ADDR_CONST(0x148)
|
| +#define FUSE_SECURITY_MODE_0 _MK_ADDR_CONST(0x1a0)
|
| +
|
| +/* -------------------------------------
|
| + * Local Functions
|
| + * -------------------------------------
|
| + */
|
| +static NvU32 get_major_version(void)
|
| +{
|
| + NvU32 reg = 0;
|
| + NvU32 major_id = 0;
|
| +
|
| + reg = NV_READ32( NV_ADDRESS_MAP_APB_MISC_BASE + APB_MISC_GP_HIDREV_0) ;
|
| + major_id = NV_DRF_VAL(APB_MISC, GP_HIDREV, MAJORREV, reg);
|
| + return major_id;
|
| +}
|
| +
|
| +static NvBool is_production_mode_fuse_set(void)
|
| +{
|
| + NvU32 reg;
|
| +
|
| + reg = NV_READ32(NV_ADDRESS_MAP_FUSE_BASE + FUSE_PRODUCTION_MODE_0);
|
| + if (reg)
|
| + return NV_TRUE;
|
| + else
|
| + return NV_FALSE;
|
| +}
|
| +
|
| +static NvBool is_odm_production_mode_fuse_set(void)
|
| +{
|
| + NvU32 reg;
|
| +
|
| + reg = NV_READ32(NV_ADDRESS_MAP_FUSE_BASE + FUSE_SECURITY_MODE_0);
|
| + if (reg)
|
| + return NV_TRUE;
|
| + else
|
| + return NV_FALSE;
|
| +}
|
| +
|
| +static NvBool ap20_is_failure_analysis_mode(void)
|
| +{
|
| + volatile NvU32 reg;
|
| +
|
| + reg = NV_READ32(NV_ADDRESS_MAP_FUSE_BASE + FUSE_FA_0);
|
| + if (reg)
|
| + return NV_TRUE;
|
| + else
|
| + return NV_FALSE;
|
| +}
|
| +
|
| +static NvBool ap20_is_odm_production_mode(void)
|
| +{
|
| + if (!ap20_is_failure_analysis_mode() &&
|
| + is_odm_production_mode_fuse_set())
|
| + return NV_TRUE;
|
| + else
|
| + return NV_FALSE;
|
| +}
|
| +
|
| +static NvBool ap20_is_production_mode(void)
|
| +{
|
| + if (get_major_version() == 0)
|
| + return NV_TRUE;
|
| +
|
| + if (!ap20_is_failure_analysis_mode() &&
|
| + is_production_mode_fuse_set() &&
|
| + !is_odm_production_mode_fuse_set())
|
| + return NV_TRUE;
|
| + else
|
| + return NV_FALSE;
|
| +}
|
| +
|
| +static NvBool fuse_get_ap20_hal(nv_fuse_hal *hal)
|
| +{
|
| + NvU32 reg;
|
| +
|
| + reg = NV_READ32(NV_ADDRESS_MAP_APB_MISC_BASE + APB_MISC_GP_HIDREV_0);
|
| + if (NV_DRF_VAL(APB_MISC_GP, HIDREV, CHIPID, reg) == 0x20) {
|
| + hal->is_odm_production_mode = ap20_is_odm_production_mode;
|
| + hal->is_nv_production_mode = ap20_is_production_mode;
|
| + return NV_TRUE;
|
| + }
|
| + return NV_FALSE;
|
| +}
|
| +
|
| +static nvbl_operating_mode nv_fuse_get_operation_mode(void)
|
| +{
|
| + nv_fuse_hal hal;
|
| + if (fuse_get_ap20_hal(&hal)) {
|
| + if (hal.is_odm_production_mode()) {
|
| + printf("!!! OdmProductionMode is not supported !!!\n");
|
| + return nvbl_mode_undefined;
|
| + } else {
|
| + if (hal.is_nv_production_mode())
|
| + return nvbl_mode_production;
|
| + else
|
| + return nvbl_mode_undefined;
|
| + }
|
| + }
|
| + return nvbl_mode_undefined;
|
| +}
|
| +
|
| +static NvU64 query_random_seed(void)
|
| +{
|
| + return 0;
|
| +}
|
| +
|
| +static void determine_crypto_options(NvBool* is_encrypted, NvBool* is_signed, NvBool* use_zero_key)
|
| +{
|
| + switch (nv_fuse_get_operation_mode()) {
|
| + case nvbl_mode_production:
|
| + *is_encrypted = NV_FALSE;
|
| + *is_signed = NV_TRUE;
|
| + *use_zero_key = NV_TRUE;
|
| + break;
|
| +
|
| + case nvbl_mode_undefined:
|
| + default:
|
| + *is_encrypted = NV_FALSE;
|
| + *is_signed = NV_FALSE;
|
| + *use_zero_key = NV_FALSE;
|
| + break;
|
| + }
|
| +}
|
| +
|
| +static NvBootError sign_wb_code(NvU32 start, NvU32 length, NvBool use_zero_key)
|
| +{
|
| + NvBootError e;
|
| + NvU8 *source; /* Pointer to source */
|
| + NvU8 *hash;
|
| +
|
| + /* Calculate AES block parameters. */
|
| + source = (NvU8 *)(start + offsetof(nvboot_wb_header, random_aes_block));
|
| + length -= offsetof(nvboot_wb_header, random_aes_block);
|
| + hash = (NvU8 *)(start + offsetof(nvboot_wb_header, hash));
|
| + e = sign_data_block(source, length, hash);
|
| + return e;
|
| +}
|
| +
|
| +/* -------------------------------------
|
| + * Global Functions
|
| + * -------------------------------------
|
| + */
|
| +
|
| +NvBootError prepare_wb_code(NvU32 seg_address, NvU32 seg_length)
|
| +{
|
| + NvBootError e = NvBootError_NotInitialized; /* Error code */
|
| +
|
| + NvU32 start; /* start of the actual code */
|
| + NvU32 end; /* end of the actual code */
|
| + NvU32 actual_length; /* length of the actual code */
|
| + NvU32 length; /* length of the signed/encrypted code */
|
| + nvboot_wb_header *src_header; /* Pointer to source WB0 header */
|
| + nvboot_wb_header *dst_header; /* Pointer to destination WB0 header */
|
| + NvBool is_encrypted; /* Segment is encrypted */
|
| + NvBool is_signed; /* Segment is signed */
|
| + NvBool use_zero_key; /* Use key of all zeros */
|
| +
|
| + /* Determine crypto options. */
|
| + determine_crypto_options(&is_encrypted, &is_signed, &use_zero_key);
|
| +
|
| + /* Get the actual code limits. */
|
| + start = (NvU32)wb_start;
|
| + end = (NvU32)wb_end;
|
| + actual_length = end - start;
|
| + length = ((actual_length + 15) >> 4) << 4;
|
| +
|
| + /* The region specified by seg_address must be in SDRAM and must be
|
| + * nonzero in length.
|
| + */
|
| + if ((seg_length == 0) ||(seg_address == 0) ||
|
| + (seg_address >= AP20_IRAM_BASE)) {
|
| + goto fail;
|
| + }
|
| +
|
| + /* Things must be 16-byte aligned. */
|
| + if ((seg_length & 0xF) || (seg_address & 0xF)) {
|
| + goto fail;
|
| + }
|
| +
|
| + /* Will the code fit? */
|
| + if (seg_length < length) {
|
| + goto fail;
|
| + }
|
| +
|
| + /* Get a pointers to the source and destination region header. */
|
| + src_header = (nvboot_wb_header*)start;
|
| + dst_header = (nvboot_wb_header*)seg_address;
|
| +
|
| + /* Populate the random_aes_block as requested. */
|
| +#if RANDOM_AES_BLOCK_IS_RANDOM
|
| + {
|
| + NvU64 *pRandomAesBlock = (NvU64*)&(src_header->random_aes_block);
|
| + NvU64 *pEnd = (NvU64*)(((NvU32)pRandomAesBlock) +
|
| + sizeof(src_header->random_aes_block));
|
| + do {
|
| + *pRandomAesBlock++ = query_random_seed();
|
| + } while (pRandomAesBlock < pEnd);
|
| + }
|
| +#elif RANDOM_AES_BLOCK_IS_PATTERN
|
| + {
|
| + NvU32 *pRandomAesBlock = (NvU32*)&(src_header->random_aes_block);
|
| + NvU32 *pEnd = (NvU32*)(((NvU32)pRandomAesBlock) +
|
| + sizeof(src_header->random_aes_block));
|
| +
|
| + do {
|
| + *pRandomAesBlock++ = RANDOM_AES_BLOCK_IS_PATTERN;
|
| + } while (pRandomAesBlock < pEnd);
|
| + }
|
| +#elif RANDOM_AES_BLOCK_IS_ZERO
|
| + {
|
| + NvU32 *pRandomAesBlock = (NvU32*)&(src_header->random_aes_block);
|
| + NvU32 *pEnd = (NvU32*)(((NvU32)pRandomAesBlock) +
|
| + sizeof(src_header->random_aes_block));
|
| +
|
| + do {
|
| + *pRandomAesBlock++ = 0;
|
| + } while (pRandomAesBlock < pEnd);
|
| + }
|
| +#endif
|
| +
|
| + /* Populate the header. */
|
| + src_header->length_in_secure = length;
|
| + src_header->length_secure = length;
|
| + src_header->destination = AP20_WB0_RUN_ADDRESS;
|
| + src_header->entry_point = AP20_WB0_RUN_ADDRESS;
|
| + src_header->code_length = length - sizeof(nvboot_wb_header);
|
| +
|
| + if (is_encrypted) {
|
| + /* Encryption is not supported */
|
| + printf("!!!! No Encrypion implemented!!!!\n");
|
| + dst_header->length_in_secure = 0;
|
| + e = NvBootError_Unimplemented;
|
| + goto fail;
|
| + } else {
|
| + /* No, just copy the code directly. */
|
| + memcpy(dst_header, src_header, length);
|
| + }
|
| +
|
| + /* Clear the signature in the destination code segment. */
|
| + memset(&(dst_header->hash), 0, sizeof(dst_header->hash));
|
| +
|
| + if (is_signed) {
|
| + NV_CHECK_ERROR_CLEANUP(sign_wb_code(seg_address, length, use_zero_key));
|
| + }
|
| +
|
| +fail:
|
| + if (e != NvBootError_Success)
|
| + printf("WB0 code is not copied to LP0 location !!! (error=0x%x)\n", e);
|
| +
|
| + return e;
|
| +}
|
|
|