1 /* 2 * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <string.h> 9 10 #include <platform_def.h> 11 12 #include <arch_helpers.h> 13 #include <common/debug.h> 14 #include <drivers/io/io_block.h> 15 #include <drivers/io/io_driver.h> 16 #include <drivers/io/io_dummy.h> 17 #include <drivers/io/io_mtd.h> 18 #include <drivers/io/io_storage.h> 19 #include <drivers/mmc.h> 20 #include <drivers/partition/partition.h> 21 #include <drivers/raw_nand.h> 22 #include <drivers/spi_nand.h> 23 #include <drivers/spi_nor.h> 24 #include <drivers/st/io_mmc.h> 25 #include <drivers/st/io_stm32image.h> 26 #include <drivers/st/stm32_fmc2_nand.h> 27 #include <drivers/st/stm32_qspi.h> 28 #include <drivers/st/stm32_sdmmc2.h> 29 #include <lib/mmio.h> 30 #include <lib/utils.h> 31 #include <plat/common/platform.h> 32 33 /* IO devices */ 34 #ifndef AARCH32_SP_OPTEE 35 static const io_dev_connector_t *dummy_dev_con; 36 static uintptr_t dummy_dev_handle; 37 static uintptr_t dummy_dev_spec; 38 #endif 39 40 static uintptr_t image_dev_handle; 41 static uintptr_t storage_dev_handle; 42 43 #if STM32MP_SDMMC || STM32MP_EMMC 44 static struct mmc_device_info mmc_info; 45 static io_block_spec_t gpt_block_spec = { 46 .offset = 0, 47 .length = 34 * MMC_BLOCK_SIZE, /* Size of GPT table */ 48 }; 49 50 static uint32_t block_buffer[MMC_BLOCK_SIZE] __aligned(MMC_BLOCK_SIZE); 51 52 static const io_block_dev_spec_t mmc_block_dev_spec = { 53 /* It's used as temp buffer in block driver */ 54 .buffer = { 55 .offset = (size_t)&block_buffer, 56 .length = MMC_BLOCK_SIZE, 57 }, 58 .ops = { 59 .read = mmc_read_blocks, 60 .write = NULL, 61 }, 62 .block_size = MMC_BLOCK_SIZE, 63 }; 64 65 #if STM32MP_EMMC_BOOT 66 static io_block_spec_t emmc_boot_ssbl_block_spec = { 67 .offset = PLAT_EMMC_BOOT_SSBL_OFFSET, 68 .length = MMC_BLOCK_SIZE, /* We are interested only in first 4 bytes */ 69 }; 70 71 static const io_block_dev_spec_t mmc_block_dev_boot_part_spec = { 72 /* It's used as temp buffer in block driver */ 73 .buffer = { 74 .offset = (size_t)&block_buffer, 75 .length = MMC_BLOCK_SIZE, 76 }, 77 .ops = { 78 .read = mmc_boot_part_read_blocks, 79 .write = NULL, 80 }, 81 .block_size = MMC_BLOCK_SIZE, 82 }; 83 #endif 84 85 static struct io_mmc_dev_spec mmc_device_spec = { 86 .use_boot_part = false, 87 }; 88 89 static const io_dev_connector_t *mmc_dev_con; 90 #endif /* STM32MP_SDMMC || STM32MP_EMMC */ 91 92 #if STM32MP_SPI_NOR 93 static io_mtd_dev_spec_t spi_nor_dev_spec = { 94 .ops = { 95 .init = spi_nor_init, 96 .read = spi_nor_read, 97 }, 98 }; 99 #endif 100 101 #if STM32MP_RAW_NAND 102 static io_mtd_dev_spec_t nand_dev_spec = { 103 .ops = { 104 .init = nand_raw_init, 105 .read = nand_read, 106 }, 107 }; 108 109 static const io_dev_connector_t *nand_dev_con; 110 #endif 111 112 #if STM32MP_SPI_NAND 113 static io_mtd_dev_spec_t spi_nand_dev_spec = { 114 .ops = { 115 .init = spi_nand_init, 116 .read = nand_read, 117 }, 118 }; 119 #endif 120 121 #if STM32MP_SPI_NAND || STM32MP_SPI_NOR 122 static const io_dev_connector_t *spi_dev_con; 123 #endif 124 125 #ifdef AARCH32_SP_OPTEE 126 static const struct stm32image_part_info optee_header_partition_spec = { 127 .name = OPTEE_HEADER_IMAGE_NAME, 128 .binary_type = OPTEE_HEADER_BINARY_TYPE, 129 }; 130 131 static const struct stm32image_part_info optee_core_partition_spec = { 132 .name = OPTEE_CORE_IMAGE_NAME, 133 .binary_type = OPTEE_CORE_BINARY_TYPE, 134 }; 135 136 static const struct stm32image_part_info optee_paged_partition_spec = { 137 .name = OPTEE_PAGED_IMAGE_NAME, 138 .binary_type = OPTEE_PAGED_BINARY_TYPE, 139 }; 140 #else 141 static const io_block_spec_t bl32_block_spec = { 142 .offset = BL32_BASE, 143 .length = STM32MP_BL32_SIZE 144 }; 145 #endif 146 147 static const struct stm32image_part_info bl33_partition_spec = { 148 .name = BL33_IMAGE_NAME, 149 .binary_type = BL33_BINARY_TYPE, 150 }; 151 152 enum { 153 IMG_IDX_BL33, 154 #ifdef AARCH32_SP_OPTEE 155 IMG_IDX_OPTEE_HEADER, 156 IMG_IDX_OPTEE_CORE, 157 IMG_IDX_OPTEE_PAGED, 158 #endif 159 IMG_IDX_NUM 160 }; 161 162 static struct stm32image_device_info stm32image_dev_info_spec __unused = { 163 .lba_size = MMC_BLOCK_SIZE, 164 .part_info[IMG_IDX_BL33] = { 165 .name = BL33_IMAGE_NAME, 166 .binary_type = BL33_BINARY_TYPE, 167 }, 168 #ifdef AARCH32_SP_OPTEE 169 .part_info[IMG_IDX_OPTEE_HEADER] = { 170 .name = OPTEE_HEADER_IMAGE_NAME, 171 .binary_type = OPTEE_HEADER_BINARY_TYPE, 172 }, 173 .part_info[IMG_IDX_OPTEE_CORE] = { 174 .name = OPTEE_CORE_IMAGE_NAME, 175 .binary_type = OPTEE_CORE_BINARY_TYPE, 176 }, 177 .part_info[IMG_IDX_OPTEE_PAGED] = { 178 .name = OPTEE_PAGED_IMAGE_NAME, 179 .binary_type = OPTEE_PAGED_BINARY_TYPE, 180 }, 181 #endif 182 }; 183 184 static io_block_spec_t stm32image_block_spec = { 185 .offset = 0, 186 .length = 0, 187 }; 188 189 static const io_dev_connector_t *stm32image_dev_con __unused; 190 191 #ifndef AARCH32_SP_OPTEE 192 static int open_dummy(const uintptr_t spec); 193 #endif 194 static int open_image(const uintptr_t spec); 195 static int open_storage(const uintptr_t spec); 196 197 struct plat_io_policy { 198 uintptr_t *dev_handle; 199 uintptr_t image_spec; 200 int (*check)(const uintptr_t spec); 201 }; 202 203 static const struct plat_io_policy policies[] = { 204 #ifdef AARCH32_SP_OPTEE 205 [BL32_IMAGE_ID] = { 206 .dev_handle = &image_dev_handle, 207 .image_spec = (uintptr_t)&optee_header_partition_spec, 208 .check = open_image 209 }, 210 [BL32_EXTRA1_IMAGE_ID] = { 211 .dev_handle = &image_dev_handle, 212 .image_spec = (uintptr_t)&optee_core_partition_spec, 213 .check = open_image 214 }, 215 [BL32_EXTRA2_IMAGE_ID] = { 216 .dev_handle = &image_dev_handle, 217 .image_spec = (uintptr_t)&optee_paged_partition_spec, 218 .check = open_image 219 }, 220 #else 221 [BL32_IMAGE_ID] = { 222 .dev_handle = &dummy_dev_handle, 223 .image_spec = (uintptr_t)&bl32_block_spec, 224 .check = open_dummy 225 }, 226 #endif 227 [BL33_IMAGE_ID] = { 228 .dev_handle = &image_dev_handle, 229 .image_spec = (uintptr_t)&bl33_partition_spec, 230 .check = open_image 231 }, 232 #if STM32MP_SDMMC || STM32MP_EMMC 233 [GPT_IMAGE_ID] = { 234 .dev_handle = &storage_dev_handle, 235 .image_spec = (uintptr_t)&gpt_block_spec, 236 .check = open_storage 237 }, 238 #endif 239 [STM32_IMAGE_ID] = { 240 .dev_handle = &storage_dev_handle, 241 .image_spec = (uintptr_t)&stm32image_block_spec, 242 .check = open_storage 243 } 244 }; 245 246 #ifndef AARCH32_SP_OPTEE 247 static int open_dummy(const uintptr_t spec) 248 { 249 return io_dev_init(dummy_dev_handle, 0); 250 } 251 #endif 252 253 static int open_image(const uintptr_t spec) 254 { 255 return io_dev_init(image_dev_handle, 0); 256 } 257 258 static int open_storage(const uintptr_t spec) 259 { 260 return io_dev_init(storage_dev_handle, 0); 261 } 262 263 #if STM32MP_EMMC_BOOT 264 static uint32_t get_boot_part_ssbl_header(void) 265 { 266 uint32_t magic = 0; 267 int io_result; 268 size_t bytes_read; 269 270 io_result = register_io_dev_block(&mmc_dev_con); 271 if (io_result != 0) { 272 panic(); 273 } 274 275 io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_block_dev_boot_part_spec, 276 &storage_dev_handle); 277 assert(io_result == 0); 278 279 io_result = io_open(storage_dev_handle, (uintptr_t) &emmc_boot_ssbl_block_spec, 280 &image_dev_handle); 281 assert(io_result == 0); 282 283 io_result = io_read(image_dev_handle, (uintptr_t) &magic, sizeof(magic), 284 &bytes_read); 285 assert(io_result == 0); 286 assert(bytes_read == sizeof(magic)); 287 288 io_result = io_dev_close(storage_dev_handle); 289 assert(io_result == 0); 290 291 return magic; 292 } 293 #endif 294 295 static void print_boot_device(boot_api_context_t *boot_context) 296 { 297 switch (boot_context->boot_interface_selected) { 298 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD: 299 INFO("Using SDMMC\n"); 300 break; 301 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: 302 INFO("Using EMMC\n"); 303 break; 304 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI: 305 INFO("Using QSPI NOR\n"); 306 break; 307 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: 308 INFO("Using FMC NAND\n"); 309 break; 310 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI: 311 INFO("Using SPI NAND\n"); 312 break; 313 default: 314 ERROR("Boot interface not found\n"); 315 panic(); 316 break; 317 } 318 319 if (boot_context->boot_interface_instance != 0U) { 320 INFO(" Instance %d\n", boot_context->boot_interface_instance); 321 } 322 } 323 324 #if STM32MP_SDMMC || STM32MP_EMMC 325 static void boot_mmc(enum mmc_device_type mmc_dev_type, 326 uint16_t boot_interface_instance) 327 { 328 int io_result __unused; 329 uint8_t idx; 330 struct stm32image_part_info *part; 331 struct stm32_sdmmc2_params params; 332 const partition_entry_t *entry __unused; 333 uint32_t magic __unused; 334 335 zeromem(¶ms, sizeof(struct stm32_sdmmc2_params)); 336 337 mmc_info.mmc_dev_type = mmc_dev_type; 338 339 switch (boot_interface_instance) { 340 case 1: 341 params.reg_base = STM32MP_SDMMC1_BASE; 342 break; 343 case 2: 344 params.reg_base = STM32MP_SDMMC2_BASE; 345 break; 346 case 3: 347 params.reg_base = STM32MP_SDMMC3_BASE; 348 break; 349 default: 350 WARN("SDMMC instance not found, using default\n"); 351 if (mmc_dev_type == MMC_IS_SD) { 352 params.reg_base = STM32MP_SDMMC1_BASE; 353 } else { 354 params.reg_base = STM32MP_SDMMC2_BASE; 355 } 356 break; 357 } 358 359 params.device_info = &mmc_info; 360 if (stm32_sdmmc2_mmc_init(¶ms) != 0) { 361 ERROR("SDMMC%u init failed\n", boot_interface_instance); 362 panic(); 363 } 364 365 stm32image_dev_info_spec.device_size = 366 stm32_sdmmc2_mmc_get_device_size(); 367 368 #if STM32MP_EMMC_BOOT 369 magic = get_boot_part_ssbl_header(); 370 371 if (magic == BOOT_API_IMAGE_HEADER_MAGIC_NB) { 372 VERBOSE("%s, header found, jump to emmc load\n", __func__); 373 idx = IMG_IDX_BL33; 374 part = &stm32image_dev_info_spec.part_info[idx]; 375 part->part_offset = PLAT_EMMC_BOOT_SSBL_OFFSET; 376 part->bkp_offset = 0U; 377 mmc_device_spec.use_boot_part = true; 378 379 goto emmc_boot; 380 } else { 381 WARN("%s: Can't find STM32 header on a boot partition\n", __func__); 382 } 383 #endif 384 385 /* Open MMC as a block device to read GPT table */ 386 io_result = register_io_dev_block(&mmc_dev_con); 387 if (io_result != 0) { 388 panic(); 389 } 390 391 io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_block_dev_spec, 392 &storage_dev_handle); 393 assert(io_result == 0); 394 395 partition_init(GPT_IMAGE_ID); 396 397 io_result = io_dev_close(storage_dev_handle); 398 assert(io_result == 0); 399 400 for (idx = 0U; idx < IMG_IDX_NUM; idx++) { 401 part = &stm32image_dev_info_spec.part_info[idx]; 402 entry = get_partition_entry(part->name); 403 if (entry == NULL) { 404 ERROR("Partition %s not found\n", part->name); 405 panic(); 406 } 407 408 part->part_offset = entry->start; 409 part->bkp_offset = 0U; 410 } 411 412 #if STM32MP_EMMC_BOOT 413 emmc_boot: 414 #endif 415 /* 416 * Re-open MMC with io_mmc, for better perfs compared to 417 * io_block. 418 */ 419 io_result = register_io_dev_mmc(&mmc_dev_con); 420 assert(io_result == 0); 421 422 io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_device_spec, 423 &storage_dev_handle); 424 assert(io_result == 0); 425 426 io_result = register_io_dev_stm32image(&stm32image_dev_con); 427 assert(io_result == 0); 428 429 io_result = io_dev_open(stm32image_dev_con, 430 (uintptr_t)&stm32image_dev_info_spec, 431 &image_dev_handle); 432 assert(io_result == 0); 433 } 434 #endif /* STM32MP_SDMMC || STM32MP_EMMC */ 435 436 #if STM32MP_SPI_NOR 437 static void boot_spi_nor(boot_api_context_t *boot_context) 438 { 439 int io_result __unused; 440 uint8_t idx; 441 struct stm32image_part_info *part; 442 443 io_result = stm32_qspi_init(); 444 assert(io_result == 0); 445 446 io_result = register_io_dev_mtd(&spi_dev_con); 447 assert(io_result == 0); 448 449 /* Open connections to device */ 450 io_result = io_dev_open(spi_dev_con, 451 (uintptr_t)&spi_nor_dev_spec, 452 &storage_dev_handle); 453 assert(io_result == 0); 454 455 stm32image_dev_info_spec.device_size = spi_nor_dev_spec.device_size; 456 457 idx = IMG_IDX_BL33; 458 part = &stm32image_dev_info_spec.part_info[idx]; 459 part->part_offset = STM32MP_NOR_BL33_OFFSET; 460 part->bkp_offset = 0U; 461 462 #ifdef AARCH32_SP_OPTEE 463 idx = IMG_IDX_OPTEE_HEADER; 464 part = &stm32image_dev_info_spec.part_info[idx]; 465 part->part_offset = STM32MP_NOR_TEEH_OFFSET; 466 part->bkp_offset = 0U; 467 468 idx = IMG_IDX_OPTEE_PAGED; 469 part = &stm32image_dev_info_spec.part_info[idx]; 470 part->part_offset = STM32MP_NOR_TEED_OFFSET; 471 part->bkp_offset = 0U; 472 473 idx = IMG_IDX_OPTEE_CORE; 474 part = &stm32image_dev_info_spec.part_info[idx]; 475 part->part_offset = STM32MP_NOR_TEEX_OFFSET; 476 part->bkp_offset = 0U; 477 #endif 478 479 io_result = register_io_dev_stm32image(&stm32image_dev_con); 480 assert(io_result == 0); 481 482 io_result = io_dev_open(stm32image_dev_con, 483 (uintptr_t)&stm32image_dev_info_spec, 484 &image_dev_handle); 485 assert(io_result == 0); 486 } 487 #endif /* STM32MP_SPI_NOR */ 488 489 #if STM32MP_RAW_NAND 490 static void boot_fmc2_nand(boot_api_context_t *boot_context) 491 { 492 int io_result __unused; 493 uint8_t idx; 494 struct stm32image_part_info *part; 495 496 io_result = stm32_fmc2_init(); 497 assert(io_result == 0); 498 499 /* Register the IO device on this platform */ 500 io_result = register_io_dev_mtd(&nand_dev_con); 501 assert(io_result == 0); 502 503 /* Open connections to device */ 504 io_result = io_dev_open(nand_dev_con, (uintptr_t)&nand_dev_spec, 505 &storage_dev_handle); 506 assert(io_result == 0); 507 508 stm32image_dev_info_spec.device_size = nand_dev_spec.device_size; 509 510 idx = IMG_IDX_BL33; 511 part = &stm32image_dev_info_spec.part_info[idx]; 512 part->part_offset = STM32MP_NAND_BL33_OFFSET; 513 part->bkp_offset = nand_dev_spec.erase_size; 514 515 #ifdef AARCH32_SP_OPTEE 516 idx = IMG_IDX_OPTEE_HEADER; 517 part = &stm32image_dev_info_spec.part_info[idx]; 518 part->part_offset = STM32MP_NAND_TEEH_OFFSET; 519 part->bkp_offset = nand_dev_spec.erase_size; 520 521 idx = IMG_IDX_OPTEE_PAGED; 522 part = &stm32image_dev_info_spec.part_info[idx]; 523 part->part_offset = STM32MP_NAND_TEED_OFFSET; 524 part->bkp_offset = nand_dev_spec.erase_size; 525 526 idx = IMG_IDX_OPTEE_CORE; 527 part = &stm32image_dev_info_spec.part_info[idx]; 528 part->part_offset = STM32MP_NAND_TEEX_OFFSET; 529 part->bkp_offset = nand_dev_spec.erase_size; 530 #endif 531 532 io_result = register_io_dev_stm32image(&stm32image_dev_con); 533 assert(io_result == 0); 534 535 io_result = io_dev_open(stm32image_dev_con, 536 (uintptr_t)&stm32image_dev_info_spec, 537 &image_dev_handle); 538 assert(io_result == 0); 539 } 540 #endif /* STM32MP_RAW_NAND */ 541 542 #if STM32MP_SPI_NAND 543 static void boot_spi_nand(boot_api_context_t *boot_context) 544 { 545 int io_result __unused; 546 uint8_t idx; 547 struct stm32image_part_info *part; 548 549 io_result = stm32_qspi_init(); 550 assert(io_result == 0); 551 552 io_result = register_io_dev_mtd(&spi_dev_con); 553 assert(io_result == 0); 554 555 /* Open connections to device */ 556 io_result = io_dev_open(spi_dev_con, 557 (uintptr_t)&spi_nand_dev_spec, 558 &storage_dev_handle); 559 assert(io_result == 0); 560 561 stm32image_dev_info_spec.device_size = 562 spi_nand_dev_spec.device_size; 563 564 idx = IMG_IDX_BL33; 565 part = &stm32image_dev_info_spec.part_info[idx]; 566 part->part_offset = STM32MP_NAND_BL33_OFFSET; 567 part->bkp_offset = spi_nand_dev_spec.erase_size; 568 569 #ifdef AARCH32_SP_OPTEE 570 idx = IMG_IDX_OPTEE_HEADER; 571 part = &stm32image_dev_info_spec.part_info[idx]; 572 part->part_offset = STM32MP_NAND_TEEH_OFFSET; 573 part->bkp_offset = spi_nand_dev_spec.erase_size; 574 575 idx = IMG_IDX_OPTEE_PAGED; 576 part = &stm32image_dev_info_spec.part_info[idx]; 577 part->part_offset = STM32MP_NAND_TEED_OFFSET; 578 part->bkp_offset = spi_nand_dev_spec.erase_size; 579 580 idx = IMG_IDX_OPTEE_CORE; 581 part = &stm32image_dev_info_spec.part_info[idx]; 582 part->part_offset = STM32MP_NAND_TEEX_OFFSET; 583 part->bkp_offset = spi_nand_dev_spec.erase_size; 584 #endif 585 586 io_result = register_io_dev_stm32image(&stm32image_dev_con); 587 assert(io_result == 0); 588 589 io_result = io_dev_open(stm32image_dev_con, 590 (uintptr_t)&stm32image_dev_info_spec, 591 &image_dev_handle); 592 assert(io_result == 0); 593 } 594 #endif /* STM32MP_SPI_NAND */ 595 596 void stm32mp_io_setup(void) 597 { 598 int io_result __unused; 599 boot_api_context_t *boot_context = 600 (boot_api_context_t *)stm32mp_get_boot_ctx_address(); 601 602 print_boot_device(boot_context); 603 604 if ((boot_context->boot_partition_used_toboot == 1U) || 605 (boot_context->boot_partition_used_toboot == 2U)) { 606 INFO("Boot used partition fsbl%d\n", 607 boot_context->boot_partition_used_toboot); 608 } 609 610 #ifndef AARCH32_SP_OPTEE 611 io_result = register_io_dev_dummy(&dummy_dev_con); 612 assert(io_result == 0); 613 614 io_result = io_dev_open(dummy_dev_con, dummy_dev_spec, 615 &dummy_dev_handle); 616 assert(io_result == 0); 617 #endif 618 619 switch (boot_context->boot_interface_selected) { 620 #if STM32MP_SDMMC 621 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD: 622 dmbsy(); 623 boot_mmc(MMC_IS_SD, boot_context->boot_interface_instance); 624 break; 625 #endif 626 #if STM32MP_EMMC 627 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: 628 dmbsy(); 629 boot_mmc(MMC_IS_EMMC, boot_context->boot_interface_instance); 630 break; 631 #endif 632 #if STM32MP_SPI_NOR 633 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI: 634 dmbsy(); 635 boot_spi_nor(boot_context); 636 break; 637 #endif 638 #if STM32MP_RAW_NAND 639 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: 640 dmbsy(); 641 boot_fmc2_nand(boot_context); 642 break; 643 #endif 644 #if STM32MP_SPI_NAND 645 case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI: 646 dmbsy(); 647 boot_spi_nand(boot_context); 648 break; 649 #endif 650 651 default: 652 ERROR("Boot interface %d not supported\n", 653 boot_context->boot_interface_selected); 654 break; 655 } 656 } 657 658 /* 659 * Return an IO device handle and specification which can be used to access 660 * an image. Use this to enforce platform load policy. 661 */ 662 int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, 663 uintptr_t *image_spec) 664 { 665 int rc; 666 const struct plat_io_policy *policy; 667 668 assert(image_id < ARRAY_SIZE(policies)); 669 670 policy = &policies[image_id]; 671 rc = policy->check(policy->image_spec); 672 if (rc == 0) { 673 *image_spec = policy->image_spec; 674 *dev_handle = *(policy->dev_handle); 675 } 676 677 return rc; 678 } 679