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 <asm/arch/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 /* to randomize the header */ |
| 52 #define RANDOM_AES_BLOCK_IS_PATTERN 1 /* to patternize the header */ |
| 53 #define RANDOM_AES_BLOCK_IS_ZERO 1 /* 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, |
| 178 NvBool* use_zero_key) |
| 179 { |
| 180 switch (nv_fuse_get_operation_mode()) { |
| 181 case nvbl_mode_production: |
| 182 *is_encrypted = NV_FALSE; |
| 183 *is_signed = NV_TRUE; |
| 184 *use_zero_key = NV_TRUE; |
| 185 break; |
| 186 |
| 187 case nvbl_mode_undefined: |
| 188 default: |
| 189 *is_encrypted = NV_FALSE; |
| 190 *is_signed = NV_FALSE; |
| 191 *use_zero_key = NV_FALSE; |
| 192 break; |
| 193 } |
| 194 } |
| 195 |
| 196 static NvBootError sign_wb_code(NvU32 start, NvU32 length, NvBool use_zero_key) |
| 197 { |
| 198 NvBootError e; |
| 199 NvU8 *source; /* Pointer to source */ |
| 200 NvU8 *hash; |
| 201 |
| 202 /* Calculate AES block parameters. */ |
| 203 source = (NvU8 *)(start + offsetof(nvboot_wb_header, random_aes_block)); |
| 204 length -= offsetof(nvboot_wb_header, random_aes_block); |
| 205 hash = (NvU8 *)(start + offsetof(nvboot_wb_header, hash)); |
| 206 e = sign_data_block(source, length, hash); |
| 207 return e; |
| 208 } |
| 209 |
| 210 /* ------------------------------------- |
| 211 * Global Functions |
| 212 * ------------------------------------- |
| 213 */ |
| 214 |
| 215 NvBootError prepare_wb_code(NvU32 seg_address, NvU32 seg_length) |
| 216 { |
| 217 NvBootError e = NvBootError_NotInitialized; /* Error code */ |
| 218 |
| 219 NvU32 start; /* start of the actual code */ |
| 220 NvU32 end; /* end of the actual code */ |
| 221 NvU32 actual_length; /* length of the actual code */ |
| 222 NvU32 length; /* length of the |
| 223 signed/encrypted code */ |
| 224 nvboot_wb_header *src_header; /* Pointer to src WB header */ |
| 225 nvboot_wb_header *dst_header; /* Pointer to dest WB header */ |
| 226 NvBool is_encrypted; /* Segment is encrypted */ |
| 227 NvBool is_signed; /* Segment is signed */ |
| 228 NvBool use_zero_key; /* Use key of all zeros */ |
| 229 |
| 230 /* Determine crypto options. */ |
| 231 determine_crypto_options(&is_encrypted, &is_signed, &use_zero_key); |
| 232 |
| 233 /* Get the actual code limits. */ |
| 234 start = (NvU32)wb_start; |
| 235 end = (NvU32)wb_end; |
| 236 actual_length = end - start; |
| 237 length = ((actual_length + 15) >> 4) << 4; |
| 238 |
| 239 /* The region specified by seg_address must be in SDRAM and must be |
| 240 * nonzero in length. |
| 241 */ |
| 242 if ((seg_length == 0) ||(seg_address == 0) || |
| 243 (seg_address >= AP20_IRAM_BASE)) { |
| 244 goto fail; |
| 245 } |
| 246 |
| 247 /* Things must be 16-byte aligned. */ |
| 248 if ((seg_length & 0xF) || (seg_address & 0xF)) { |
| 249 goto fail; |
| 250 } |
| 251 |
| 252 /* Will the code fit? */ |
| 253 if (seg_length < length) { |
| 254 goto fail; |
| 255 } |
| 256 |
| 257 /* Get a pointers to the source and destination region header. */ |
| 258 src_header = (nvboot_wb_header*)start; |
| 259 dst_header = (nvboot_wb_header*)seg_address; |
| 260 |
| 261 /* Populate the random_aes_block as requested. */ |
| 262 #if RANDOM_AES_BLOCK_IS_RANDOM |
| 263 { |
| 264 NvU64 *pAesBlock = (NvU64*)&(src_header->random_aes_block); |
| 265 NvU64 *pEnd = (NvU64*)(((NvU32)pAesBlock) + |
| 266 sizeof(src_header->random_aes_block)); |
| 267 do { |
| 268 *pAesBlock++ = query_random_seed(); |
| 269 } while (pAesBlock < pEnd); |
| 270 } |
| 271 #elif RANDOM_AES_BLOCK_IS_PATTERN |
| 272 { |
| 273 NvU32 *pAesBlock = (NvU32*)&(src_header->random_aes_block); |
| 274 NvU32 *pEnd = (NvU32*)(((NvU32)pAesBlock) + |
| 275 sizeof(src_header->random_aes_block)); |
| 276 |
| 277 do { |
| 278 *pAesBlock++ = RANDOM_AES_BLOCK_IS_PATTERN; |
| 279 } while (pAesBlock < pEnd); |
| 280 } |
| 281 #elif RANDOM_AES_BLOCK_IS_ZERO |
| 282 { |
| 283 NvU32 *pAesBlock = (NvU32*)&(src_header->random_aes_block); |
| 284 NvU32 *pEnd = (NvU32*)(((NvU32)pAesBlock) + |
| 285 sizeof(src_header->random_aes_block)); |
| 286 |
| 287 do { |
| 288 *pAesBlock++ = 0; |
| 289 } while (pRandomAesBlock < pEnd); |
| 290 } |
| 291 #endif |
| 292 |
| 293 /* Populate the header. */ |
| 294 src_header->length_in_secure = length; |
| 295 src_header->length_secure = length; |
| 296 src_header->destination = AP20_WB0_RUN_ADDRESS; |
| 297 src_header->entry_point = AP20_WB0_RUN_ADDRESS; |
| 298 src_header->code_length = length - sizeof(nvboot_wb_header); |
| 299 |
| 300 if (is_encrypted) { |
| 301 /* Encryption is not supported */ |
| 302 printf("!!!! No Encrypion implemented!!!!\n"); |
| 303 dst_header->length_in_secure = 0; |
| 304 e = NvBootError_Unimplemented; |
| 305 goto fail; |
| 306 } else { |
| 307 /* No, just copy the code directly. */ |
| 308 memcpy(dst_header, src_header, length); |
| 309 } |
| 310 |
| 311 /* Clear the signature in the destination code segment. */ |
| 312 memset(&(dst_header->hash), 0, sizeof(dst_header->hash)); |
| 313 |
| 314 if (is_signed) { |
| 315 NV_CHECK_ERROR_CLEANUP(sign_wb_code(seg_address, length, |
| 316 use_zero_key)); |
| 317 } |
| 318 |
| 319 fail: |
| 320 if (e != NvBootError_Success) |
| 321 printf("WB code not copied to LP0 location! (error=0x%x)\n", e); |
| 322 |
| 323 return e; |
| 324 } |
OLD | NEW |