1 /* 2 * Copyright (c) 2014-2015, Linaro Ltd and Contributors. All rights reserved. 3 * Copyright (c) 2014-2015, Hisilicon Ltd and Contributors. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * Neither the name of ARM nor the names of its contributors may be used 16 * to endorse or promote products derived from this software without specific 17 * prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <arch_helpers.h> 33 #include <assert.h> 34 #include <debug.h> 35 #include <dw_mmc.h> 36 #include <fastboot.h> 37 #include <io_block.h> 38 #include <io_driver.h> 39 #include <io_fip.h> 40 #include <io_memmap.h> 41 #include <io_storage.h> 42 #include <mmio.h> 43 #include <partitions.h> 44 #include <platform_def.h> 45 #include <semihosting.h> /* For FOPEN_MODE_... */ 46 #include <string.h> 47 #include "hikey_private.h" 48 49 #define LOADER_MAX_ENTRIES 2 50 #define PTABLE_MAX_ENTRIES 3 51 #define USER_MAX_ENTRIES 2 52 53 #define FLUSH_BASE (DDR_BASE + 0x100000) 54 55 struct entry_head { 56 unsigned char magic[8]; 57 unsigned char name[8]; 58 unsigned int start; /* lba */ 59 unsigned int count; /* lba */ 60 unsigned int flag; 61 }; 62 63 static const io_dev_connector_t *bl1_mem_dev_con; 64 static uintptr_t bl1_mem_dev_spec; 65 static uintptr_t loader_mem_dev_handle; 66 static uintptr_t bl1_mem_init_params; 67 static const io_dev_connector_t *fip_dev_con; 68 static uintptr_t fip_dev_spec; 69 static uintptr_t fip_dev_handle; 70 static const io_dev_connector_t *dw_mmc_dev_con; 71 static struct block_ops dw_mmc_ops; 72 static uintptr_t emmc_dev_handle; 73 74 #define SPARSE_FILL_BUFFER_ADDRESS 0x18000000 75 #define SPARSE_FILL_BUFFER_SIZE 0x08000000 76 77 /* Page 1024, since only a few pages before 2048 are used as partition table */ 78 #define SERIALNO_OFFSET (1024 * 512) 79 80 static const io_block_spec_t loader_mem_spec = { 81 /* l-loader.bin that contains bl1.bin */ 82 .offset = LOADER_RAM_BASE, 83 .length = BL1_RO_LIMIT - LOADER_RAM_BASE, 84 }; 85 86 static const io_block_spec_t boot_emmc_spec = { 87 .offset = MMC_LOADER_BASE, 88 .length = BL1_RO_LIMIT - LOADER_RAM_BASE, 89 }; 90 91 static const io_block_spec_t normal_emmc_spec = { 92 .offset = MMC_BASE, 93 .length = MMC_SIZE, 94 }; 95 96 static io_block_spec_t fip_block_spec = { 97 .offset = 0, 98 .length = 0, 99 }; 100 101 static const io_file_spec_t bl2_file_spec = { 102 .path = BL2_IMAGE_NAME, 103 .mode = FOPEN_MODE_RB 104 }; 105 106 static const io_file_spec_t bl30_file_spec = { 107 .path = BL30_IMAGE_NAME, 108 .mode = FOPEN_MODE_RB 109 }; 110 111 static const io_file_spec_t bl31_file_spec = { 112 .path = BL31_IMAGE_NAME, 113 .mode = FOPEN_MODE_RB 114 }; 115 116 static const io_file_spec_t bl32_file_spec = { 117 .path = BL32_IMAGE_NAME, 118 .mode = FOPEN_MODE_RB 119 }; 120 121 static const io_file_spec_t bl33_file_spec = { 122 .path = BL33_IMAGE_NAME, 123 .mode = FOPEN_MODE_RB 124 }; 125 126 static int open_loader_mem(const uintptr_t spec); 127 static int open_fip(const uintptr_t spec); 128 static int open_dw_mmc(const uintptr_t spec); 129 static int open_dw_mmc_boot(const uintptr_t spec); 130 131 struct plat_io_policy { 132 const char *image_name; 133 uintptr_t *dev_handle; 134 uintptr_t image_spec; 135 int (*check)(const uintptr_t spec); 136 }; 137 138 static const struct plat_io_policy policies[] = { 139 { 140 LOADER_MEM_NAME, 141 &loader_mem_dev_handle, 142 (uintptr_t)&loader_mem_spec, 143 open_loader_mem 144 }, { 145 BOOT_EMMC_NAME, 146 &emmc_dev_handle, 147 (uintptr_t)&boot_emmc_spec, 148 open_dw_mmc_boot 149 }, { 150 NORMAL_EMMC_NAME, 151 &emmc_dev_handle, 152 (uintptr_t)&normal_emmc_spec, 153 open_dw_mmc 154 }, { 155 FIP_IMAGE_NAME, 156 &emmc_dev_handle, 157 (uintptr_t)&fip_block_spec, 158 open_dw_mmc 159 }, { 160 BL2_IMAGE_NAME, 161 &fip_dev_handle, 162 (uintptr_t)&bl2_file_spec, 163 open_fip 164 }, { 165 BL30_IMAGE_NAME, 166 &fip_dev_handle, 167 (uintptr_t)&bl30_file_spec, 168 open_fip 169 }, { 170 BL31_IMAGE_NAME, 171 &fip_dev_handle, 172 (uintptr_t)&bl31_file_spec, 173 open_fip 174 }, { 175 BL32_IMAGE_NAME, 176 &fip_dev_handle, 177 (uintptr_t)&bl32_file_spec, 178 open_fip 179 }, { 180 BL33_IMAGE_NAME, 181 &fip_dev_handle, 182 (uintptr_t)&bl33_file_spec, 183 open_fip 184 }, { 185 0, 0, 0, 0 186 } 187 }; 188 189 static int open_loader_mem(const uintptr_t spec) 190 { 191 int result = IO_FAIL; 192 uintptr_t image_handle; 193 194 result = io_dev_init(loader_mem_dev_handle, bl1_mem_init_params); 195 if (result == IO_SUCCESS) { 196 result = io_open(loader_mem_dev_handle, spec, &image_handle); 197 if (result == IO_SUCCESS) { 198 io_close(image_handle); 199 } 200 } 201 return result; 202 } 203 204 static int open_fip(const uintptr_t spec) 205 { 206 int result = IO_FAIL; 207 208 /* See if a Firmware Image Package is available */ 209 result = io_dev_init(fip_dev_handle, (uintptr_t)FIP_IMAGE_NAME); 210 if (result == IO_SUCCESS) { 211 INFO("Using FIP\n"); 212 /*TODO: Check image defined in spec is present in FIP. */ 213 } 214 return result; 215 } 216 217 218 static int open_dw_mmc(const uintptr_t spec) 219 { 220 int result = IO_FAIL; 221 uintptr_t image_handle; 222 223 /* indicate to select normal partition in eMMC */ 224 result = io_dev_init(emmc_dev_handle, 0); 225 if (result == IO_SUCCESS) { 226 result = io_open(emmc_dev_handle, spec, &image_handle); 227 if (result == IO_SUCCESS) { 228 /* INFO("Using DW MMC IO\n"); */ 229 io_close(image_handle); 230 } 231 } 232 return result; 233 } 234 235 static int open_dw_mmc_boot(const uintptr_t spec) 236 { 237 int result = IO_FAIL; 238 uintptr_t image_handle; 239 240 /* indicate to select boot partition in eMMC */ 241 result = io_dev_init(emmc_dev_handle, 1); 242 if (result == IO_SUCCESS) { 243 result = io_open(emmc_dev_handle, spec, &image_handle); 244 if (result == IO_SUCCESS) { 245 /* INFO("Using DW MMC IO\n"); */ 246 io_close(image_handle); 247 } 248 } 249 return result; 250 } 251 252 void io_setup(void) 253 { 254 int io_result = IO_FAIL; 255 256 /* Register the IO devices on this platform */ 257 io_result = register_io_dev_fip(&fip_dev_con); 258 assert(io_result == IO_SUCCESS); 259 260 io_result = register_io_dev_block(&dw_mmc_dev_con); 261 assert(io_result == IO_SUCCESS); 262 263 io_result = register_io_dev_memmap(&bl1_mem_dev_con); 264 assert(io_result == IO_SUCCESS); 265 266 /* Open connections to devices and cache the handles */ 267 io_result = io_dev_open(fip_dev_con, fip_dev_spec, &fip_dev_handle); 268 assert(io_result == IO_SUCCESS); 269 270 dw_mmc_ops.init = init_mmc; 271 dw_mmc_ops.read = mmc0_read; 272 dw_mmc_ops.write = mmc0_write; 273 io_result = io_dev_open(dw_mmc_dev_con, (uintptr_t)&dw_mmc_ops, 274 &emmc_dev_handle); 275 assert(io_result == IO_SUCCESS); 276 277 io_result = io_dev_open(bl1_mem_dev_con, bl1_mem_dev_spec, 278 &loader_mem_dev_handle); 279 assert(io_result == IO_SUCCESS); 280 281 /* Ignore improbable errors in release builds */ 282 (void)io_result; 283 } 284 285 /* Return an IO device handle and specification which can be used to access 286 * an image. Use this to enforce platform load policy */ 287 int plat_get_image_source(const char *image_name, uintptr_t *dev_handle, 288 uintptr_t *image_spec) 289 { 290 int result = IO_FAIL; 291 const struct plat_io_policy *policy; 292 293 if ((image_name != NULL) && (dev_handle != NULL) && 294 (image_spec != NULL)) { 295 policy = policies; 296 while (policy->image_name != NULL) { 297 if (strcmp(policy->image_name, image_name) == 0) { 298 result = policy->check(policy->image_spec); 299 if (result == IO_SUCCESS) { 300 *image_spec = policy->image_spec; 301 *dev_handle = *(policy->dev_handle); 302 break; 303 } 304 } 305 policy++; 306 } 307 } else { 308 result = IO_FAIL; 309 } 310 return result; 311 } 312 313 int update_fip_spec(void) 314 { 315 struct ptentry *ptn; 316 317 ptn = find_ptn("fastboot"); 318 if (!ptn) { 319 WARN("failed to find partition fastboot\n"); 320 ptn = find_ptn("bios"); 321 if (!ptn) { 322 WARN("failed to find partition bios\n"); 323 return IO_FAIL; 324 } 325 } 326 VERBOSE("%s: name:%s, start:%llx, length:%llx\n", 327 __func__, ptn->name, ptn->start, ptn->length); 328 fip_block_spec.offset = ptn->start; 329 fip_block_spec.length = ptn->length; 330 return IO_SUCCESS; 331 } 332 333 static int fetch_entry_head(void *buf, int num, struct entry_head *hd) 334 { 335 unsigned char magic[8] = "ENTRYHDR"; 336 if (hd == NULL) 337 return IO_FAIL; 338 memcpy((void *)hd, buf, sizeof(struct entry_head) * num); 339 if (!strncmp((void *)hd->magic, (void *)magic, 8)) 340 return IO_SUCCESS; 341 return IO_NOT_SUPPORTED; 342 } 343 344 static int flush_loader(void) 345 { 346 struct entry_head entries[5]; 347 uintptr_t img_handle, spec; 348 int result = IO_FAIL; 349 size_t bytes_read, length; 350 ssize_t offset; 351 int i, fp; 352 353 result = fetch_entry_head((void *)(FLUSH_BASE + 28), 354 LOADER_MAX_ENTRIES, entries); 355 if (result) { 356 WARN("failed to parse entries in loader image\n"); 357 return result; 358 } 359 360 spec = 0; 361 for (i = 0, fp = 0; i < LOADER_MAX_ENTRIES; i++) { 362 if (entries[i].flag != 1) { 363 WARN("Invalid flag in entry:0x%x\n", entries[i].flag); 364 return IO_NOT_SUPPORTED; 365 } 366 result = plat_get_image_source(BOOT_EMMC_NAME, &emmc_dev_handle, 367 &spec); 368 if (result) { 369 WARN("failed to open emmc boot area\n"); 370 return result; 371 } 372 /* offset in Boot Area1 */ 373 offset = MMC_LOADER_BASE + entries[i].start * 512; 374 375 result = io_open(emmc_dev_handle, spec, &img_handle); 376 if (result != IO_SUCCESS) { 377 WARN("Failed to open memmap device\n"); 378 return result; 379 } 380 length = entries[i].count * 512; 381 382 result = io_seek(img_handle, IO_SEEK_SET, offset); 383 if (result) 384 goto exit; 385 386 if (i == 1) 387 fp = (entries[1].start - entries[0].start) * 512; 388 result = io_write(img_handle, FLUSH_BASE + fp, length, 389 &bytes_read); 390 if ((result != IO_SUCCESS) || (bytes_read < length)) { 391 WARN("Failed to write '%s' file (%i)\n", 392 LOADER_MEM_NAME, result); 393 goto exit; 394 } 395 io_close(img_handle); 396 } 397 return result; 398 exit: 399 io_close(img_handle); 400 return result; 401 } 402 403 /* 404 * Flush l-loader.bin (loader & bl1.bin) into Boot Area1 of eMMC. 405 */ 406 int flush_loader_image(void) 407 { 408 uintptr_t bl1_image_spec; 409 int result = IO_FAIL; 410 size_t bytes_read, length; 411 uintptr_t img_handle; 412 413 result = plat_get_image_source(LOADER_MEM_NAME, &loader_mem_dev_handle, 414 &bl1_image_spec); 415 416 result = io_open(loader_mem_dev_handle, bl1_image_spec, &img_handle); 417 if (result != IO_SUCCESS) { 418 WARN("Failed to open memmap device\n"); 419 goto exit; 420 } 421 length = loader_mem_spec.length; 422 result = io_read(img_handle, FLUSH_BASE, length, &bytes_read); 423 if ((result != IO_SUCCESS) || (bytes_read < length)) { 424 WARN("Failed to load '%s' file (%i)\n", LOADER_MEM_NAME, result); 425 goto exit; 426 } 427 io_close(img_handle); 428 429 result = flush_loader(); 430 if (result != IO_SUCCESS) { 431 io_dev_close(loader_mem_dev_handle); 432 return result; 433 } 434 exit: 435 io_close(img_handle); 436 io_dev_close(loader_mem_dev_handle); 437 return result; 438 } 439 440 static int flush_single_image(const char *mmc_name, unsigned long img_addr, 441 ssize_t offset, size_t length) 442 { 443 uintptr_t img_handle, spec = 0; 444 size_t bytes_read; 445 int result = IO_FAIL; 446 447 result = plat_get_image_source(mmc_name, &emmc_dev_handle, 448 &spec); 449 if (result) { 450 NOTICE("failed to open emmc user data area\n"); 451 return result; 452 } 453 454 result = io_open(emmc_dev_handle, spec, &img_handle); 455 if (result != IO_SUCCESS) { 456 NOTICE("Failed to open memmap device\n"); 457 return result; 458 } 459 460 result = io_seek(img_handle, IO_SEEK_SET, offset); 461 if (result) { 462 NOTICE("Failed to seek at offset:0x%x\n", offset); 463 goto exit; 464 } 465 466 result = io_write(img_handle, img_addr, length, 467 &bytes_read); 468 if ((result != IO_SUCCESS) || (bytes_read < length)) { 469 NOTICE("Failed to write file (%i)\n", result); 470 goto exit; 471 } 472 exit: 473 io_close(img_handle); 474 return result; 475 } 476 477 static int is_sparse_image(unsigned long img_addr) 478 { 479 if (*(uint32_t *)img_addr == SPARSE_HEADER_MAGIC) 480 return 1; 481 return 0; 482 } 483 484 static int do_unsparse(char *cmdbuf, unsigned long img_addr, unsigned long img_length) 485 { 486 sparse_header_t *header = (sparse_header_t *)img_addr; 487 chunk_header_t *chunk = NULL; 488 struct ptentry *ptn; 489 void *data = (void *)img_addr; 490 uint64_t out_blks = 0, out_length = 0; 491 uint64_t length; 492 uint32_t fill_value; 493 uint64_t left, count; 494 int i, result; 495 496 ptn = find_ptn(cmdbuf); 497 if (!ptn) { 498 NOTICE("failed to find partition %s\n", cmdbuf); 499 return IO_FAIL; 500 } 501 length = (uint64_t)(header->total_blks) * (uint64_t)(header->blk_sz); 502 if (length > ptn->length) { 503 NOTICE("Unsparsed image length is %lld, pentry length is %lld.\n", 504 length, ptn->length); 505 return IO_FAIL; 506 } 507 508 data = (void *)((unsigned long)data + header->file_hdr_sz); 509 for (i = 0; i < header->total_chunks; i++) { 510 chunk = (chunk_header_t *)data; 511 data = (void *)((unsigned long)data + sizeof(chunk_header_t)); 512 length = (uint64_t)chunk->chunk_sz * (uint64_t)header->blk_sz; 513 514 switch (chunk->chunk_type) { 515 case CHUNK_TYPE_RAW: 516 result = flush_single_image(NORMAL_EMMC_NAME, 517 (unsigned long)data, 518 ptn->start + out_length, length); 519 if (result < 0) { 520 NOTICE("sparse: failed to flush raw chunk\n"); 521 return result; 522 } 523 out_blks += length / 512; 524 out_length += length; 525 /* next chunk is just after the raw data */ 526 data = (void *)((unsigned long)data + length); 527 break; 528 case CHUNK_TYPE_FILL: 529 if (chunk->total_sz != (sizeof(unsigned int) + sizeof(chunk_header_t))) { 530 NOTICE("sparse: bad chunk size\n"); 531 return IO_FAIL; 532 } 533 fill_value = *(unsigned int *)data; 534 if (fill_value != 0) { 535 NOTICE("sparse: filled value shouldn't be zero.\n"); 536 } 537 memset((void *)SPARSE_FILL_BUFFER_ADDRESS, 538 0, SPARSE_FILL_BUFFER_SIZE); 539 left = length; 540 while (left > 0) { 541 if (left < SPARSE_FILL_BUFFER_SIZE) 542 count = left; 543 else 544 count = SPARSE_FILL_BUFFER_SIZE; 545 result = flush_single_image(NORMAL_EMMC_NAME, 546 SPARSE_FILL_BUFFER_ADDRESS, 547 ptn->start + out_length, count); 548 if (result < 0) { 549 WARN("sparse: failed to flush fill chunk\n"); 550 return result; 551 } 552 out_blks += count / 512; 553 out_length += count; 554 left = left - count; 555 } 556 /* next chunk is just after the filled data */ 557 data = (void *)((unsigned long)data + sizeof(unsigned int)); 558 break; 559 case CHUNK_TYPE_DONT_CARE: 560 if (chunk->total_sz != sizeof(chunk_header_t)) { 561 NOTICE("sparse: unmatched chunk size\n"); 562 return IO_FAIL; 563 } 564 out_blks += length / 512; 565 out_length += length; 566 break; 567 default: 568 NOTICE("sparse: unrecognized type 0x%x\n", chunk->chunk_type); 569 break; 570 } 571 } 572 return 0; 573 } 574 575 /* Page 1024 is used to store serial number */ 576 int flush_random_serialno(unsigned long addr, unsigned long length) 577 { 578 int result; 579 580 memset((void *)SPARSE_FILL_BUFFER_ADDRESS, 0, 512); 581 memcpy((void *)SPARSE_FILL_BUFFER_ADDRESS, (void *)addr, length); 582 result = flush_single_image(NORMAL_EMMC_NAME, SPARSE_FILL_BUFFER_ADDRESS, 583 SERIALNO_OFFSET, 512); 584 return result; 585 } 586 587 char *load_serialno(void) 588 { 589 uintptr_t img_handle, spec = 0; 590 size_t bytes_read; 591 struct random_serial_num *random = NULL; 592 int result; 593 594 result = plat_get_image_source(NORMAL_EMMC_NAME, &emmc_dev_handle, 595 &spec); 596 if (result) { 597 NOTICE("failed to open emmc user data area\n"); 598 return NULL; 599 } 600 601 result = io_open(emmc_dev_handle, spec, &img_handle); 602 if (result != IO_SUCCESS) { 603 NOTICE("Failed to open memmap device\n"); 604 return NULL; 605 } 606 607 result = io_seek(img_handle, IO_SEEK_SET, SERIALNO_OFFSET); 608 if (result) { 609 NOTICE("Failed to seek at offset 0\n"); 610 goto exit; 611 } 612 result = io_read(img_handle, SPARSE_FILL_BUFFER_ADDRESS, 512, &bytes_read); 613 if ((result != IO_SUCCESS) || (bytes_read < 512)) { 614 NOTICE("Failed to load '%s' file (%i)\n", LOADER_MEM_NAME, result); 615 goto exit; 616 } 617 io_close(img_handle); 618 619 random = (struct random_serial_num *)SPARSE_FILL_BUFFER_ADDRESS; 620 if (random->magic != RANDOM_MAGIC) 621 return NULL; 622 623 return random->serialno; 624 exit: 625 io_close(img_handle); 626 return NULL; 627 } 628 629 /* 630 * Flush bios.bin into User Data Area in eMMC 631 */ 632 int flush_user_images(char *cmdbuf, unsigned long img_addr, 633 unsigned long img_length) 634 { 635 struct entry_head entries[5]; 636 struct ptentry *ptn; 637 size_t length; 638 ssize_t offset; 639 int result = IO_FAIL; 640 int i, fp; 641 642 result = fetch_entry_head((void *)img_addr, USER_MAX_ENTRIES, entries); 643 switch (result) { 644 case IO_NOT_SUPPORTED: 645 if (!strncmp(cmdbuf, "fastboot", 8) || 646 !strncmp(cmdbuf, "bios", 4)) { 647 update_fip_spec(); 648 } 649 if (is_sparse_image(img_addr)) { 650 result = do_unsparse(cmdbuf, img_addr, img_length); 651 } else { 652 ptn = find_ptn(cmdbuf); 653 if (!ptn) { 654 WARN("failed to find partition %s\n", cmdbuf); 655 return IO_FAIL; 656 } 657 img_length = (img_length + 512 - 1) / 512 * 512; 658 result = flush_single_image(NORMAL_EMMC_NAME, img_addr, 659 ptn->start, img_length); 660 } 661 break; 662 case IO_SUCCESS: 663 if (strncmp(cmdbuf, "ptable", 6)) { 664 WARN("it's not for ptable\n"); 665 return IO_FAIL; 666 } 667 /* currently it's for partition table */ 668 /* the first block is for entry headers */ 669 fp = 512; 670 671 for (i = 0; i < USER_MAX_ENTRIES; i++) { 672 if (entries[i].flag != 0) { 673 WARN("Invalid flag in entry:0x%x\n", 674 entries[i].flag); 675 return IO_NOT_SUPPORTED; 676 } 677 if (entries[i].count == 0) 678 continue; 679 length = entries[i].count * 512; 680 offset = MMC_BASE + entries[i].start * 512; 681 VERBOSE("i:%d, start:%x, count:%x\n", 682 i, entries[i].start, entries[i].count); 683 result = flush_single_image(NORMAL_EMMC_NAME, 684 img_addr + fp, offset, length); 685 fp += entries[i].count * 512; 686 } 687 get_partition(); 688 break; 689 case IO_FAIL: 690 WARN("failed to parse entries in user image.\n"); 691 return result; 692 } 693 return result; 694 } 695