1 /* 2 * Copyright (C) 2016 Michal Simek <michals@xilinx.com> 3 * Copyright (C) 2015 Nathan Rossi <nathan@nathanrossi.com> 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 * 7 * The following Boot Header format/structures and values are defined in the 8 * following documents: 9 * * ug1085 ZynqMP TRM doc v1.4 (Chapter 11, Table 11-4) 10 * 11 * Expected Header Size = 0x9C0 12 * Forced as 'little' endian, 32-bit words 13 * 14 * 0x 0 - Interrupt table (8 words) 15 * ... (Default value = 0xeafffffe) 16 * 0x 1f 17 * 0x 20 - Width detection 18 * * DEFAULT_WIDTHDETECTION 0xaa995566 19 * 0x 24 - Image identifier 20 * * DEFAULT_IMAGEIDENTIFIER 0x584c4e58 21 * 0x 28 - Encryption 22 * * 0x00000000 - None 23 * * 0xa5c3c5a3 - eFuse 24 * * 0xa5c3c5a7 - obfuscated key in eFUSE 25 * * 0x3a5c3c5a - bbRam 26 * * 0xa35c7ca5 - obfuscated key in boot header 27 * 0x 2C - Image load 28 * 0x 30 - Image offset 29 * 0x 34 - PFW image length 30 * 0x 38 - Total PFW image length 31 * 0x 3C - Image length 32 * 0x 40 - Total image length 33 * 0x 44 - Image attributes 34 * 0x 48 - Header checksum 35 * 0x 4c - Obfuscated key 36 * ... 37 * 0x 68 38 * 0x 6c - Reserved 39 * 0x 70 - User defined 40 * ... 41 * 0x 9c 42 * 0x a0 - Secure header initialization vector 43 * ... 44 * 0x a8 45 * 0x ac - Obfuscated key initialization vector 46 * ... 47 * 0x b4 48 * 0x b8 - Register Initialization, 511 Address and Data word pairs 49 * * List is terminated with an address of 0xffffffff or 50 * ... * at the max number of entries 51 * 0x8b4 52 * 0x8b8 - Reserved 53 * ... 54 * 0x9bf 55 * 0x9c0 - Data/Image starts here or above 56 */ 57 58 #include "imagetool.h" 59 #include "mkimage.h" 60 #include <image.h> 61 62 #define HEADER_INTERRUPT_DEFAULT (cpu_to_le32(0xeafffffe)) 63 #define HEADER_REGINIT_NULL (cpu_to_le32(0xffffffff)) 64 #define HEADER_WIDTHDETECTION (cpu_to_le32(0xaa995566)) 65 #define HEADER_IMAGEIDENTIFIER (cpu_to_le32(0x584c4e58)) 66 67 enum { 68 ENCRYPTION_EFUSE = 0xa5c3c5a3, 69 ENCRYPTION_OEFUSE = 0xa5c3c5a7, 70 ENCRYPTION_BBRAM = 0x3a5c3c5a, 71 ENCRYPTION_OBBRAM = 0xa35c7ca5, 72 ENCRYPTION_NONE = 0x0, 73 }; 74 75 struct zynqmp_reginit { 76 uint32_t address; 77 uint32_t data; 78 }; 79 80 #define HEADER_INTERRUPT_VECTORS 8 81 #define HEADER_REGINITS 256 82 83 struct zynqmp_header { 84 uint32_t interrupt_vectors[HEADER_INTERRUPT_VECTORS]; /* 0x0 */ 85 uint32_t width_detection; /* 0x20 */ 86 uint32_t image_identifier; /* 0x24 */ 87 uint32_t encryption; /* 0x28 */ 88 uint32_t image_load; /* 0x2c */ 89 uint32_t image_offset; /* 0x30 */ 90 uint32_t pfw_image_length; /* 0x34 */ 91 uint32_t total_pfw_image_length; /* 0x38 */ 92 uint32_t image_size; /* 0x3c */ 93 uint32_t image_stored_size; /* 0x40 */ 94 uint32_t image_attributes; /* 0x44 */ 95 uint32_t checksum; /* 0x48 */ 96 uint32_t __reserved1[27]; /* 0x4c */ 97 struct zynqmp_reginit register_init[HEADER_REGINITS]; /* 0xb8 */ 98 uint32_t __reserved4[66]; /* 0x9c0 */ 99 }; 100 101 static struct zynqmp_header zynqmpimage_header; 102 static void *dynamic_header; 103 static FILE *fpmu; 104 105 static uint32_t zynqmpimage_checksum(struct zynqmp_header *ptr) 106 { 107 uint32_t checksum = 0; 108 109 if (ptr == NULL) 110 return 0; 111 112 checksum += le32_to_cpu(ptr->width_detection); 113 checksum += le32_to_cpu(ptr->image_identifier); 114 checksum += le32_to_cpu(ptr->encryption); 115 checksum += le32_to_cpu(ptr->image_load); 116 checksum += le32_to_cpu(ptr->image_offset); 117 checksum += le32_to_cpu(ptr->pfw_image_length); 118 checksum += le32_to_cpu(ptr->total_pfw_image_length); 119 checksum += le32_to_cpu(ptr->image_size); 120 checksum += le32_to_cpu(ptr->image_stored_size); 121 checksum += le32_to_cpu(ptr->image_attributes); 122 checksum = ~checksum; 123 124 return cpu_to_le32(checksum); 125 } 126 127 static void zynqmpimage_default_header(struct zynqmp_header *ptr) 128 { 129 int i; 130 131 if (ptr == NULL) 132 return; 133 134 ptr->width_detection = HEADER_WIDTHDETECTION; 135 ptr->image_attributes = 0x800; 136 ptr->image_identifier = HEADER_IMAGEIDENTIFIER; 137 ptr->encryption = cpu_to_le32(ENCRYPTION_NONE); 138 139 /* Setup not-supported/constant/reserved fields */ 140 for (i = 0; i < HEADER_INTERRUPT_VECTORS; i++) 141 ptr->interrupt_vectors[i] = HEADER_INTERRUPT_DEFAULT; 142 143 for (i = 0; i < HEADER_REGINITS; i++) { 144 ptr->register_init[i].address = HEADER_REGINIT_NULL; 145 ptr->register_init[i].data = 0; 146 } 147 148 /* 149 * Certain reserved fields are required to be set to 0, ensure they are 150 * set as such. 151 */ 152 ptr->pfw_image_length = 0x0; 153 ptr->total_pfw_image_length = 0x0; 154 } 155 156 /* mkimage glue functions */ 157 static int zynqmpimage_verify_header(unsigned char *ptr, int image_size, 158 struct image_tool_params *params) 159 { 160 struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr; 161 162 if (image_size < sizeof(struct zynqmp_header)) 163 return -1; 164 165 if (zynqhdr->width_detection != HEADER_WIDTHDETECTION) 166 return -1; 167 if (zynqhdr->image_identifier != HEADER_IMAGEIDENTIFIER) 168 return -1; 169 170 if (zynqmpimage_checksum(zynqhdr) != zynqhdr->checksum) 171 return -1; 172 173 return 0; 174 } 175 176 static void zynqmpimage_print_header(const void *ptr) 177 { 178 struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr; 179 int i; 180 181 printf("Image Type : Xilinx Zynq Boot Image support\n"); 182 printf("Image Offset : 0x%08x\n", le32_to_cpu(zynqhdr->image_offset)); 183 printf("Image Size : %lu bytes (%lu bytes packed)\n", 184 (unsigned long)le32_to_cpu(zynqhdr->image_size), 185 (unsigned long)le32_to_cpu(zynqhdr->image_stored_size)); 186 187 if (zynqhdr->pfw_image_length) 188 printf("PMUFW Size : %lu bytes (%lu bytes packed)\n", 189 (unsigned long)le32_to_cpu(zynqhdr->pfw_image_length), 190 (unsigned long)le32_to_cpu( 191 zynqhdr->total_pfw_image_length)); 192 193 printf("Image Load : 0x%08x\n", le32_to_cpu(zynqhdr->image_load)); 194 printf("Checksum : 0x%08x\n", le32_to_cpu(zynqhdr->checksum)); 195 196 for (i = 0; i < HEADER_INTERRUPT_VECTORS; i++) { 197 if (zynqhdr->interrupt_vectors[i] == HEADER_INTERRUPT_DEFAULT) 198 continue; 199 200 printf("Modified Interrupt Vector Address [%d]: 0x%08x\n", i, 201 le32_to_cpu(zynqhdr->interrupt_vectors[i])); 202 } 203 204 for (i = 0; i < HEADER_REGINITS; i++) { 205 if (zynqhdr->register_init[i].address == HEADER_REGINIT_NULL) 206 break; 207 208 if (i == 0) 209 printf("Custom Register Initialization:\n"); 210 211 printf(" @ 0x%08x -> 0x%08x\n", 212 le32_to_cpu(zynqhdr->register_init[i].address), 213 le32_to_cpu(zynqhdr->register_init[i].data)); 214 } 215 216 free(dynamic_header); 217 } 218 219 static int zynqmpimage_check_params(struct image_tool_params *params) 220 { 221 if (!params) 222 return 0; 223 224 if (params->addr != 0x0) { 225 fprintf(stderr, "Error: Load Address cannot be specified.\n"); 226 return -1; 227 } 228 229 /* 230 * If the entry point is specified ensure it is 64 byte aligned. 231 */ 232 if (params->eflag && (params->ep % 64 != 0)) { 233 fprintf(stderr, 234 "Error: Entry Point must be aligned to a 64-byte boundary.\n"); 235 return -1; 236 } 237 238 return !(params->lflag || params->dflag); 239 } 240 241 static int zynqmpimage_check_image_types(uint8_t type) 242 { 243 if (type == IH_TYPE_ZYNQMPIMAGE) 244 return EXIT_SUCCESS; 245 return EXIT_FAILURE; 246 } 247 248 static uint32_t fsize(FILE *fp) 249 { 250 int size, ret, origin; 251 252 origin = ftell(fp); 253 if (origin < 0) { 254 fprintf(stderr, "Incorrect file size\n"); 255 fclose(fp); 256 exit(2); 257 } 258 259 ret = fseek(fp, 0L, SEEK_END); 260 if (ret) { 261 fprintf(stderr, "Incorrect file SEEK_END\n"); 262 fclose(fp); 263 exit(3); 264 } 265 266 size = ftell(fp); 267 if (size < 0) { 268 fprintf(stderr, "Incorrect file size\n"); 269 fclose(fp); 270 exit(4); 271 } 272 273 /* going back */ 274 ret = fseek(fp, origin, SEEK_SET); 275 if (ret) { 276 fprintf(stderr, "Incorrect file SEEK_SET to %d\n", origin); 277 fclose(fp); 278 exit(3); 279 } 280 281 return size; 282 } 283 284 static void zynqmpimage_pmufw(struct zynqmp_header *zynqhdr, 285 const char *filename) 286 { 287 uint32_t size; 288 289 /* Setup PMU fw size */ 290 zynqhdr->pfw_image_length = fsize(fpmu); 291 zynqhdr->total_pfw_image_length = zynqhdr->pfw_image_length; 292 293 zynqhdr->image_size -= zynqhdr->pfw_image_length; 294 zynqhdr->image_stored_size -= zynqhdr->total_pfw_image_length; 295 296 /* Read the whole PMUFW to the header */ 297 size = fread(&zynqhdr->__reserved4[66], 1, 298 zynqhdr->pfw_image_length, fpmu); 299 if (size != zynqhdr->pfw_image_length) { 300 fprintf(stderr, "Cannot read PMUFW file: %s\n", filename); 301 fclose(fpmu); 302 exit(1); 303 } 304 305 fclose(fpmu); 306 } 307 308 static void zynqmpimage_parse_initparams(struct zynqmp_header *zynqhdr, 309 const char *filename) 310 { 311 FILE *fp; 312 struct zynqmp_reginit reginit; 313 unsigned int reg_count = 0; 314 int r, err; 315 struct stat path_stat; 316 317 /* Expect a table of register-value pairs, e.g. "0x12345678 0x4321" */ 318 fp = fopen(filename, "r"); 319 if (!fp) { 320 fprintf(stderr, "Cannot open initparams file: %s\n", filename); 321 exit(1); 322 } 323 324 err = fstat(fileno(fp), &path_stat); 325 if (err) { 326 fclose(fp); 327 return; 328 } 329 330 if (!S_ISREG(path_stat.st_mode)) { 331 fclose(fp); 332 return; 333 } 334 335 do { 336 r = fscanf(fp, "%x %x", ®init.address, ®init.data); 337 if (r == 2) { 338 zynqhdr->register_init[reg_count] = reginit; 339 ++reg_count; 340 } 341 r = fscanf(fp, "%*[^\n]\n"); /* Skip to next line */ 342 } while ((r != EOF) && (reg_count < HEADER_REGINITS)); 343 fclose(fp); 344 } 345 346 static void zynqmpimage_set_header(void *ptr, struct stat *sbuf, int ifd, 347 struct image_tool_params *params) 348 { 349 struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr; 350 zynqmpimage_default_header(zynqhdr); 351 352 /* place image directly after header */ 353 zynqhdr->image_offset = 354 cpu_to_le32((uint32_t)sizeof(struct zynqmp_header)); 355 zynqhdr->image_size = cpu_to_le32(params->file_size - 356 sizeof(struct zynqmp_header)); 357 zynqhdr->image_stored_size = zynqhdr->image_size; 358 zynqhdr->image_load = 0xfffc0000; 359 if (params->eflag) 360 zynqhdr->image_load = cpu_to_le32((uint32_t)params->ep); 361 362 /* PMUFW */ 363 if (fpmu) 364 zynqmpimage_pmufw(zynqhdr, params->imagename); 365 366 /* User can pass in text file with init list */ 367 if (strlen(params->imagename2)) 368 zynqmpimage_parse_initparams(zynqhdr, params->imagename2); 369 370 zynqhdr->checksum = zynqmpimage_checksum(zynqhdr); 371 } 372 373 static int zynqmpimage_vrec_header(struct image_tool_params *params, 374 struct image_type_params *tparams) 375 { 376 struct stat path_stat; 377 char *filename = params->imagename; 378 int err; 379 380 /* Handle static case without PMUFW */ 381 tparams->header_size = sizeof(struct zynqmp_header); 382 tparams->hdr = (void *)&zynqmpimage_header; 383 384 /* PMUFW name is passed via params->imagename */ 385 if (strlen(filename) == 0) 386 return EXIT_SUCCESS; 387 388 fpmu = fopen(filename, "r"); 389 if (!fpmu) { 390 fprintf(stderr, "Cannot open PMUFW file: %s\n", filename); 391 return EXIT_FAILURE; 392 } 393 394 err = fstat(fileno(fpmu), &path_stat); 395 if (err) { 396 fclose(fpmu); 397 fpmu = NULL; 398 return EXIT_FAILURE; 399 } 400 401 if (!S_ISREG(path_stat.st_mode)) { 402 fclose(fpmu); 403 fpmu = NULL; 404 return EXIT_FAILURE; 405 } 406 407 /* Increase header size by PMUFW file size */ 408 tparams->header_size += fsize(fpmu); 409 410 /* Allocate buffer with space for PMUFW */ 411 dynamic_header = calloc(1, tparams->header_size); 412 tparams->hdr = dynamic_header; 413 414 return EXIT_SUCCESS; 415 } 416 417 U_BOOT_IMAGE_TYPE( 418 zynqmpimage, 419 "Xilinx ZynqMP Boot Image support", 420 sizeof(struct zynqmp_header), 421 (void *)&zynqmpimage_header, 422 zynqmpimage_check_params, 423 zynqmpimage_verify_header, 424 zynqmpimage_print_header, 425 zynqmpimage_set_header, 426 NULL, 427 zynqmpimage_check_image_types, 428 NULL, 429 zynqmpimage_vrec_header 430 ); 431