OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * (C) Copyright 2010 |
| 3 * NVIDIA Corporation <www.nvidia.com> |
| 4 * |
| 5 * See file CREDITS for list of people who contributed to this |
| 6 * project. |
| 7 * |
| 8 * This program is free software; you can redistribute it and/or |
| 9 * modify it under the terms of the GNU General Public License as |
| 10 * published by the Free Software Foundation; either version 2 of |
| 11 * the License, or (at your option) any later version. |
| 12 * |
| 13 * This program is distributed in the hope that it will be useful, |
| 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 * GNU General Public License for more details. |
| 17 * |
| 18 * You should have received a copy of the GNU General Public License |
| 19 * along with this program; if not, write to the Free Software |
| 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| 21 * MA 02111-1307 USA |
| 22 */ |
| 23 |
| 24 #include <common.h> |
| 25 #include <asm/arch/nv_drf.h> |
| 26 #include <asm/arch/nvboot_error.h> |
| 27 #include <asm/arch/nvcommon.h> |
| 28 #include <asm/arch/nverror.h> |
| 29 #include <asm/arch/tegra2.h> |
| 30 #include <asm/arch/nv_hardware_access.h> |
| 31 #include "warmboot.h" |
| 32 |
| 33 NvBootError sign_data_block(NvU8 *source, |
| 34 NvU32 length, |
| 35 NvU8 *hash); |
| 36 |
| 37 /* ------------------------------------- |
| 38 * Configuration parameters |
| 39 * ------------------------------------- |
| 40 */ |
| 41 |
| 42 /* ------------------------------------- |
| 43 * Definitions |
| 44 * ------------------------------------- |
| 45 */ |
| 46 |
| 47 /* NOTE: If more than one of the following is enabled, only one of them will |
| 48 * actually be used. RANDOM takes precedence over PATTERN and ZERO, and |
| 49 * PATTERN takes precedence overy ZERO. |
| 50 */ |
| 51 #define RANDOM_AES_BLOCK_IS_RANDOM 1 /* Set nonzero to random
ize the header */ |
| 52 #define RANDOM_AES_BLOCK_IS_PATTERN 1 /* Set nonzero to patter
nize the header */ |
| 53 #define RANDOM_AES_BLOCK_IS_ZERO 1 /* Set nonzero to clear
the header */ |
| 54 |
| 55 /* Address at which AP20 WB0 code runs */ |
| 56 /* The address must not overlap the bootrom's IRAM usage */ |
| 57 #define AP20_WB0_RUN_ADDRESS 0x40020000 |
| 58 #define AP20_IRAM_BASE 0x40000000 |
| 59 |
| 60 /* ------------------------------------- |
| 61 * Global External Labels |
| 62 * ------------------------------------- |
| 63 */ |
| 64 |
| 65 void wb_start(void); /* Start of WB assembly code */ |
| 66 void wb_end(void); /* End of WB assembly code */ |
| 67 |
| 68 #define NV_ADDRESS_MAP_FUSE_BASE 0x7000F800UL |
| 69 #define FUSE_PRODUCTION_MODE_0 _MK_ADDR_CONST(0x100) |
| 70 #define FUSE_FA_0 _MK_ADDR_CONST(0x148) |
| 71 #define FUSE_SECURITY_MODE_0 _MK_ADDR_CONST(0x1a0) |
| 72 |
| 73 /* ------------------------------------- |
| 74 * Local Functions |
| 75 * ------------------------------------- |
| 76 */ |
| 77 static NvU32 get_major_version(void) |
| 78 { |
| 79 NvU32 reg = 0; |
| 80 NvU32 major_id = 0; |
| 81 |
| 82 reg = NV_READ32( NV_ADDRESS_MAP_APB_MISC_BASE + APB_MISC_GP_HIDREV_0) ; |
| 83 major_id = NV_DRF_VAL(APB_MISC, GP_HIDREV, MAJORREV, reg); |
| 84 return major_id; |
| 85 } |
| 86 |
| 87 static NvBool is_production_mode_fuse_set(void) |
| 88 { |
| 89 NvU32 reg; |
| 90 |
| 91 reg = NV_READ32(NV_ADDRESS_MAP_FUSE_BASE + FUSE_PRODUCTION_MODE_0); |
| 92 if (reg) |
| 93 return NV_TRUE; |
| 94 else |
| 95 return NV_FALSE; |
| 96 } |
| 97 |
| 98 static NvBool is_odm_production_mode_fuse_set(void) |
| 99 { |
| 100 NvU32 reg; |
| 101 |
| 102 reg = NV_READ32(NV_ADDRESS_MAP_FUSE_BASE + FUSE_SECURITY_MODE_0); |
| 103 if (reg) |
| 104 return NV_TRUE; |
| 105 else |
| 106 return NV_FALSE; |
| 107 } |
| 108 |
| 109 static NvBool ap20_is_failure_analysis_mode(void) |
| 110 { |
| 111 volatile NvU32 reg; |
| 112 |
| 113 reg = NV_READ32(NV_ADDRESS_MAP_FUSE_BASE + FUSE_FA_0); |
| 114 if (reg) |
| 115 return NV_TRUE; |
| 116 else |
| 117 return NV_FALSE; |
| 118 } |
| 119 |
| 120 static NvBool ap20_is_odm_production_mode(void) |
| 121 { |
| 122 if (!ap20_is_failure_analysis_mode() && |
| 123 is_odm_production_mode_fuse_set()) |
| 124 return NV_TRUE; |
| 125 else |
| 126 return NV_FALSE; |
| 127 } |
| 128 |
| 129 static NvBool ap20_is_production_mode(void) |
| 130 { |
| 131 if (get_major_version() == 0) |
| 132 return NV_TRUE; |
| 133 |
| 134 if (!ap20_is_failure_analysis_mode() && |
| 135 is_production_mode_fuse_set() && |
| 136 !is_odm_production_mode_fuse_set()) |
| 137 return NV_TRUE; |
| 138 else |
| 139 return NV_FALSE; |
| 140 } |
| 141 |
| 142 static NvBool fuse_get_ap20_hal(nv_fuse_hal *hal) |
| 143 { |
| 144 NvU32 reg; |
| 145 |
| 146 reg = NV_READ32(NV_ADDRESS_MAP_APB_MISC_BASE + APB_MISC_GP_HIDREV_0); |
| 147 if (NV_DRF_VAL(APB_MISC_GP, HIDREV, CHIPID, reg) == 0x20) { |
| 148 hal->is_odm_production_mode = ap20_is_odm_production_mode; |
| 149 hal->is_nv_production_mode = ap20_is_production_mode; |
| 150 return NV_TRUE; |
| 151 } |
| 152 return NV_FALSE; |
| 153 } |
| 154 |
| 155 static nvbl_operating_mode nv_fuse_get_operation_mode(void) |
| 156 { |
| 157 nv_fuse_hal hal; |
| 158 if (fuse_get_ap20_hal(&hal)) { |
| 159 if (hal.is_odm_production_mode()) { |
| 160 printf("!!! OdmProductionMode is not supported !!!\n"); |
| 161 return nvbl_mode_undefined; |
| 162 } else { |
| 163 if (hal.is_nv_production_mode()) |
| 164 return nvbl_mode_production; |
| 165 else |
| 166 return nvbl_mode_undefined; |
| 167 } |
| 168 } |
| 169 return nvbl_mode_undefined; |
| 170 } |
| 171 |
| 172 static NvU64 query_random_seed(void) |
| 173 { |
| 174 return 0; |
| 175 } |
| 176 |
| 177 static void determine_crypto_options(NvBool* is_encrypted, NvBool* is_signed, Nv
Bool* use_zero_key) |
| 178 { |
| 179 switch (nv_fuse_get_operation_mode()) { |
| 180 case nvbl_mode_production: |
| 181 *is_encrypted = NV_FALSE; |
| 182 *is_signed = NV_TRUE; |
| 183 *use_zero_key = NV_TRUE; |
| 184 break; |
| 185 |
| 186 case nvbl_mode_undefined: |
| 187 default: |
| 188 *is_encrypted = NV_FALSE; |
| 189 *is_signed = NV_FALSE; |
| 190 *use_zero_key = NV_FALSE; |
| 191 break; |
| 192 } |
| 193 } |
| 194 |
| 195 static NvBootError sign_wb_code(NvU32 start, NvU32 length, NvBool use_zero_key) |
| 196 { |
| 197 NvBootError e; |
| 198 NvU8 *source; /* Pointer to source */ |
| 199 NvU8 *hash; |
| 200 |
| 201 /* Calculate AES block parameters. */ |
| 202 source = (NvU8 *)(start + offsetof(nvboot_wb_header, random_aes_block)); |
| 203 length -= offsetof(nvboot_wb_header, random_aes_block); |
| 204 hash = (NvU8 *)(start + offsetof(nvboot_wb_header, hash)); |
| 205 e = sign_data_block(source, length, hash); |
| 206 return e; |
| 207 } |
| 208 |
| 209 /* ------------------------------------- |
| 210 * Global Functions |
| 211 * ------------------------------------- |
| 212 */ |
| 213 |
| 214 NvBootError prepare_wb_code(NvU32 seg_address, NvU32 seg_length) |
| 215 { |
| 216 NvBootError e = NvBootError_NotInitialized; /* Error code */ |
| 217 |
| 218 NvU32 start; /* start of the actual code */ |
| 219 NvU32 end; /* end of the actual code */ |
| 220 NvU32 actual_length; /* length of the actual code */ |
| 221 NvU32 length; /* length of the signed/encrypte
d code */ |
| 222 nvboot_wb_header *src_header; /* Pointer to source WB0 header
*/ |
| 223 nvboot_wb_header *dst_header; /* Pointer to destination WB0 he
ader */ |
| 224 NvBool is_encrypted; /* Segment is encrypted */ |
| 225 NvBool is_signed; /* Segment is signed */ |
| 226 NvBool use_zero_key; /* Use key of all zeros */ |
| 227 |
| 228 /* Determine crypto options. */ |
| 229 determine_crypto_options(&is_encrypted, &is_signed, &use_zero_key); |
| 230 |
| 231 /* Get the actual code limits. */ |
| 232 start = (NvU32)wb_start; |
| 233 end = (NvU32)wb_end; |
| 234 actual_length = end - start; |
| 235 length = ((actual_length + 15) >> 4) << 4; |
| 236 |
| 237 /* The region specified by seg_address must be in SDRAM and must be |
| 238 * nonzero in length. |
| 239 */ |
| 240 if ((seg_length == 0) ||(seg_address == 0) || |
| 241 (seg_address >= AP20_IRAM_BASE)) { |
| 242 goto fail; |
| 243 } |
| 244 |
| 245 /* Things must be 16-byte aligned. */ |
| 246 if ((seg_length & 0xF) || (seg_address & 0xF)) { |
| 247 goto fail; |
| 248 } |
| 249 |
| 250 /* Will the code fit? */ |
| 251 if (seg_length < length) { |
| 252 goto fail; |
| 253 } |
| 254 |
| 255 /* Get a pointers to the source and destination region header. */ |
| 256 src_header = (nvboot_wb_header*)start; |
| 257 dst_header = (nvboot_wb_header*)seg_address; |
| 258 |
| 259 /* Populate the random_aes_block as requested. */ |
| 260 #if RANDOM_AES_BLOCK_IS_RANDOM |
| 261 { |
| 262 NvU64 *pRandomAesBlock = (NvU64*)&(src_header->random_aes_bloc
k); |
| 263 NvU64 *pEnd = (NvU64*)(((NvU32)pRandomAesBlock) + |
| 264 sizeof(src_header->random_aes_block)); |
| 265 do { |
| 266 *pRandomAesBlock++ = query_random_seed(); |
| 267 } while (pRandomAesBlock < pEnd); |
| 268 } |
| 269 #elif RANDOM_AES_BLOCK_IS_PATTERN |
| 270 { |
| 271 NvU32 *pRandomAesBlock = (NvU32*)&(src_header->random_aes_bloc
k); |
| 272 NvU32 *pEnd = (NvU32*)(((NvU32)pRandomAesBlock) + |
| 273 sizeof(src_header->random_aes_block)); |
| 274 |
| 275 do { |
| 276 *pRandomAesBlock++ = RANDOM_AES_BLOCK_IS_PATTERN; |
| 277 } while (pRandomAesBlock < pEnd); |
| 278 } |
| 279 #elif RANDOM_AES_BLOCK_IS_ZERO |
| 280 { |
| 281 NvU32 *pRandomAesBlock = (NvU32*)&(src_header->random_aes_bloc
k); |
| 282 NvU32 *pEnd = (NvU32*)(((NvU32)pRandomAesBlock) + |
| 283 sizeof(src_header->random_aes_block)); |
| 284 |
| 285 do { |
| 286 *pRandomAesBlock++ = 0; |
| 287 } while (pRandomAesBlock < pEnd); |
| 288 } |
| 289 #endif |
| 290 |
| 291 /* Populate the header. */ |
| 292 src_header->length_in_secure = length; |
| 293 src_header->length_secure = length; |
| 294 src_header->destination = AP20_WB0_RUN_ADDRESS; |
| 295 src_header->entry_point = AP20_WB0_RUN_ADDRESS; |
| 296 src_header->code_length = length - sizeof(nvboot_wb_header); |
| 297 |
| 298 if (is_encrypted) { |
| 299 /* Encryption is not supported */ |
| 300 printf("!!!! No Encrypion implemented!!!!\n"); |
| 301 dst_header->length_in_secure = 0; |
| 302 e = NvBootError_Unimplemented; |
| 303 goto fail; |
| 304 } else { |
| 305 /* No, just copy the code directly. */ |
| 306 memcpy(dst_header, src_header, length); |
| 307 } |
| 308 |
| 309 /* Clear the signature in the destination code segment. */ |
| 310 memset(&(dst_header->hash), 0, sizeof(dst_header->hash)); |
| 311 |
| 312 if (is_signed) { |
| 313 NV_CHECK_ERROR_CLEANUP(sign_wb_code(seg_address, length, use_zer
o_key)); |
| 314 } |
| 315 |
| 316 fail: |
| 317 if (e != NvBootError_Success) |
| 318 printf("WB0 code is not copied to LP0 location !!! (error=0x%x)\
n", e); |
| 319 |
| 320 return e; |
| 321 } |
OLD | NEW |