1 /* $NetBSD: kloader.c,v 1.32 2021/10/11 14:25:05 rin Exp $ */ 2 3 /*- 4 * Copyright (c) 2001, 2002, 2004 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: kloader.c,v 1.32 2021/10/11 14:25:05 rin Exp $"); 31 32 #include "debug_kloader.h" 33 34 #include <sys/param.h> 35 #include <sys/fcntl.h> 36 #include <sys/kmem.h> 37 #include <sys/namei.h> 38 #include <sys/proc.h> 39 #include <sys/systm.h> 40 #include <sys/vnode.h> 41 42 #define ELFSIZE 32 43 #include <sys/exec_elf.h> 44 45 #include <uvm/uvm_extern.h> 46 47 #include <machine/kloader.h> 48 49 #define PRINTF(fmt, args...) printf("kloader: " fmt, ##args) 50 51 #ifdef KLOADER_DEBUG 52 int kloader_debug = 1; 53 #define DPRINTF(fmt, args...) \ 54 if (kloader_debug) \ 55 printf("%s: " fmt, __func__ , ##args) 56 #define _DPRINTF(fmt, args...) \ 57 if (kloader_debug) \ 58 printf(fmt, ##args) 59 #define DPRINTFN(n, fmt, args...) \ 60 if (kloader_debug > (n)) \ 61 printf("%s: " fmt, __func__ , ##args) 62 #define _DPRINTFN(n, fmt, args...) \ 63 if (kloader_debug > (n)) \ 64 printf(fmt, ##args) 65 #define STATIC 66 #else 67 #define DPRINTF(fmt, args...) ((void)0) 68 #define _DPRINTF(fmt, args...) ((void)0) 69 #define DPRINTFN(n, fmt, args...) ((void)0) 70 #define _DPRINTFN(n, fmt, args...) ((void)0) 71 #define STATIC static 72 #endif 73 74 struct kloader { 75 struct pglist pg_head; 76 struct vm_page *cur_pg; /* XXX use bus_dma(9) */ 77 struct kloader_page_tag *cur_tag; 78 struct vnode *vp; 79 struct kloader_page_tag *tagstart; 80 struct kloader_bootinfo *bootinfo; 81 struct kloader_bootinfo *rebootinfo; 82 vaddr_t loader_sp; 83 kloader_bootfunc_t *loader; 84 int setuped; 85 int called; 86 struct kloader_ops *ops; 87 }; 88 89 #define BUCKET_SIZE (PAGE_SIZE - sizeof(struct kloader_page_tag)) 90 #define KLOADER_LWP (&lwp0) 91 STATIC struct kloader kloader; 92 93 #define ROUND4(x) (((x) + 3) & ~3) 94 95 STATIC int kloader_load(void); 96 97 STATIC int kloader_alloc_memory(size_t); 98 STATIC struct kloader_page_tag *kloader_get_tag(vaddr_t); 99 STATIC void kloader_from_file(vaddr_t, off_t, size_t); 100 STATIC void kloader_copy(vaddr_t, const void *, size_t); 101 STATIC void kloader_zero(vaddr_t, size_t); 102 103 STATIC void kloader_load_segment(Elf_Phdr *); 104 105 STATIC struct vnode *kloader_open(const char *); 106 STATIC void kloader_close(void); 107 STATIC int kloader_read(size_t, size_t, void *); 108 109 #ifdef KLOADER_DEBUG 110 STATIC void kloader_pagetag_dump(void); 111 #endif 112 113 void 114 __kloader_reboot_setup(struct kloader_ops *ops, const char *filename) 115 { 116 117 if (kloader.bootinfo == NULL) { 118 PRINTF("No bootinfo.\n"); 119 return; 120 } 121 122 if (ops == NULL || ops->jump == NULL || ops->boot == NULL) { 123 PRINTF("No boot operations.\n"); 124 return; 125 } 126 kloader.ops = ops; 127 128 if (kloader.called++ == 0) { 129 PRINTF("kernel file name: %s\n", filename); 130 kloader.vp = kloader_open(filename); 131 if (kloader.vp == NULL) 132 return; 133 134 if (kloader_load() == 0) { 135 kloader.setuped = TRUE; 136 #ifdef KLOADER_DEBUG 137 kloader_pagetag_dump(); 138 #endif 139 } 140 kloader_close(); 141 } else { 142 /* Fatal case. reboot from DDB etc. */ 143 kloader_reboot(); 144 } 145 } 146 147 148 void 149 kloader_reboot(void) 150 { 151 152 if (kloader.setuped) { 153 PRINTF("Rebooting...\n"); 154 (*kloader.ops->jump)(kloader.loader, kloader.loader_sp, 155 kloader.rebootinfo, kloader.tagstart); 156 } 157 158 if (kloader.ops->reset != NULL) { 159 PRINTF("Resetting...\n"); 160 (*kloader.ops->reset)(); 161 } 162 while (/*CONSTCOND*/1) 163 continue; 164 /* NOTREACHED */ 165 } 166 167 168 int 169 kloader_load(void) 170 { 171 Elf_Ehdr eh; 172 Elf_Phdr *ph, *p; 173 Elf_Shdr *sh; 174 Elf_Addr entry; 175 vaddr_t kv; 176 size_t sz; 177 size_t phsz, shsz, shstrsz; 178 char *shstrtab; 179 int symndx, strndx; 180 size_t ksymsz; 181 struct kloader_bootinfo nbi; /* new boot info */ 182 char *oldbuf, *newbuf; 183 char **ap; 184 int i; 185 186 ph = NULL; 187 sh = NULL; 188 shstrtab = NULL; 189 190 /* read kernel's ELF header */ 191 kloader_read(0, sizeof(Elf_Ehdr), &eh); 192 193 if (eh.e_ident[EI_MAG0] != ELFMAG0 || 194 eh.e_ident[EI_MAG1] != ELFMAG1 || 195 eh.e_ident[EI_MAG2] != ELFMAG2 || 196 eh.e_ident[EI_MAG3] != ELFMAG3) { 197 PRINTF("not an ELF file\n"); 198 goto err; 199 } 200 201 /* read program headers */ 202 phsz = eh.e_phentsize * eh.e_phnum; 203 if ((ph = kmem_alloc(phsz, KM_NOSLEEP)) == NULL) { 204 PRINTF("can't allocate program header table.\n"); 205 goto err; 206 } 207 if (kloader_read(eh.e_phoff, phsz, ph) != 0) { 208 PRINTF("program header read error.\n"); 209 goto err; 210 } 211 212 /* read section headers */ 213 shsz = eh.e_shentsize * eh.e_shnum; 214 if ((sh = kmem_alloc(shsz, KM_NOSLEEP)) == NULL) { 215 PRINTF("can't allocate section header table.\n"); 216 goto err; 217 } 218 if (kloader_read(eh.e_shoff, shsz, sh) != 0) { 219 PRINTF("section header read error.\n"); 220 goto err; 221 } 222 223 /* read section names */ 224 shstrsz = ROUND4(sh[eh.e_shstrndx].sh_size); 225 shstrtab = kmem_alloc(shstrsz, KM_NOSLEEP); 226 if (shstrtab == NULL) { 227 PRINTF("unable to allocate memory for .shstrtab\n"); 228 goto err; 229 } 230 DPRINTF("reading 0x%x bytes of .shstrtab at 0x%x\n", 231 sh[eh.e_shstrndx].sh_size, sh[eh.e_shstrndx].sh_offset); 232 kloader_read(sh[eh.e_shstrndx].sh_offset, sh[eh.e_shstrndx].sh_size, 233 shstrtab); 234 235 /* save entry point, code to construct symbol table overwrites it */ 236 entry = eh.e_entry; 237 238 /* 239 * Calculate memory size 240 */ 241 sz = 0; 242 243 /* loadable segments */ 244 for (i = 0; i < eh.e_phnum; i++) { 245 if (ph[i].p_type == PT_LOAD) { 246 DPRINTF("segment %d size = file 0x%x memory 0x%x\n", 247 i, ph[i].p_filesz, ph[i].p_memsz); 248 #ifdef KLOADER_ZERO_BSS 249 sz += round_page(ph[i].p_memsz); 250 #else 251 sz += round_page(ph[i].p_filesz); 252 #endif 253 sz += PAGE_SIZE; /* compensate for partial last tag */ 254 } 255 } 256 257 if (sz == 0) /* nothing to load? */ 258 goto err; 259 260 /* symbols/strings sections */ 261 symndx = strndx = -1; 262 for (i = 0; i < eh.e_shnum; i++) { 263 if (strcmp(shstrtab + sh[i].sh_name, ".symtab") == 0) 264 symndx = i; 265 else if (strcmp(shstrtab + sh[i].sh_name, ".strtab") == 0) 266 strndx = i; 267 else if (i != eh.e_shstrndx) { 268 /* while here, mark all other sections as unused */ 269 sh[i].sh_type = SHT_NULL; 270 sh[i].sh_offset = 0; 271 } 272 } 273 274 if (symndx < 0 || strndx < 0) { 275 if (symndx < 0) 276 PRINTF("no .symtab section\n"); 277 if (strndx < 0) 278 PRINTF("no .strtab section\n"); 279 ksymsz = SELFMAG; /* just a bad magic */ 280 } else { 281 ksymsz = sizeof(Elf_Ehdr) 282 + eh.e_shentsize * eh.e_shnum 283 + shstrsz /* rounded to 4 bytes */ 284 + sh[symndx].sh_size 285 + sh[strndx].sh_size; 286 DPRINTF("ksyms size = 0x%zx\n", ksymsz); 287 } 288 sz += ROUND4(ksymsz); 289 290 /* boot info for the new kernel */ 291 sz += sizeof(struct kloader_bootinfo); 292 293 /* get memory for new kernel */ 294 if (kloader_alloc_memory(sz) != 0) 295 goto err; 296 297 /* 298 * Copy new kernel in. 299 */ 300 kv = 0; /* XXX: -Wuninitialized */ 301 for (i = 0, p = ph; i < eh.e_phnum; i++, p++) { 302 if (p->p_type == PT_LOAD) { 303 kloader_load_segment(p); 304 kv = p->p_vaddr + ROUND4(p->p_memsz); 305 } 306 } 307 308 /* 309 * Construct symbol table for ksyms. 310 */ 311 if (symndx < 0 || strndx < 0) { 312 kloader_zero(kv, SELFMAG); 313 kv += SELFMAG; 314 } else { 315 Elf_Off eoff; 316 off_t symoff, stroff; 317 318 /* save offsets of .symtab and .strtab before we change them */ 319 symoff = sh[symndx].sh_offset; 320 stroff = sh[strndx].sh_offset; 321 322 /* no loadable segments */ 323 eh.e_entry = 0; 324 eh.e_phnum = 0; 325 eh.e_phoff = 0; 326 327 /* change offsets to reflect new layout */ 328 eoff = sizeof(Elf_Ehdr); 329 eh.e_shoff = eoff; 330 331 eoff += eh.e_shentsize * eh.e_shnum; 332 sh[eh.e_shstrndx].sh_offset = eoff; 333 334 eoff += shstrsz; 335 sh[symndx].sh_offset = eoff; 336 337 eoff += sh[symndx].sh_size; 338 sh[strndx].sh_offset = eoff; 339 340 /* local copies massaged, can serve them now */ 341 DPRINTF("ksyms ELF header\n"); 342 kloader_copy(kv, &eh, sizeof(Elf_Ehdr)); 343 kv += sizeof(Elf_Ehdr); 344 345 DPRINTF("ksyms section headers\n"); 346 kloader_copy(kv, sh, eh.e_shentsize * eh.e_shnum); 347 kv += eh.e_shentsize * eh.e_shnum; 348 349 DPRINTF("ksyms .shstrtab\n"); 350 kloader_copy(kv, shstrtab, shstrsz); 351 kv += shstrsz; 352 353 DPRINTF("ksyms .symtab\n"); 354 kloader_from_file(kv, symoff, sh[symndx].sh_size); 355 kv += sh[symndx].sh_size; 356 357 DPRINTF("ksyms .strtab\n"); 358 kloader_from_file(kv, stroff, ROUND4(sh[strndx].sh_size)); 359 kv += ROUND4(sh[strndx].sh_size); 360 } 361 362 /* 363 * Create boot info to pass to the new kernel. 364 * All pointers in it are *not* valid until the new kernel runs! 365 */ 366 367 /* get a private copy of current bootinfo to vivisect */ 368 memcpy(&nbi, kloader.bootinfo, sizeof(struct kloader_bootinfo)); 369 370 /* new kernel entry point */ 371 nbi.entry = entry; 372 373 /* where args currently are, see kloader_bootinfo_set() */ 374 oldbuf = &kloader.bootinfo->_argbuf[0]; 375 376 /* where args *will* be after boot code copied them */ 377 newbuf = (char *)(void *)kv 378 + offsetof(struct kloader_bootinfo, _argbuf); 379 380 DPRINTF("argv: old %p -> new %p\n", oldbuf, newbuf); 381 382 /* not a valid pointer in this kernel! */ 383 nbi.argv = (void *)newbuf; 384 385 /* local copy that we populate with new (not yet valid) pointers */ 386 ap = (char **)(void *)nbi._argbuf; 387 388 for (i = 0; i < kloader.bootinfo->argc; ++i) { 389 DPRINTFN(1, " [%d]: %p -> ", i, kloader.bootinfo->argv[i]); 390 ap[i] = newbuf + 391 (kloader.bootinfo->argv[i] - oldbuf); 392 _DPRINTFN(1, "%p\n", ap[i]); 393 } 394 395 /* arrange for the new bootinfo to get copied */ 396 DPRINTF("bootinfo\n"); 397 kloader_copy(kv, &nbi, sizeof(struct kloader_bootinfo)); 398 399 /* will be valid by the time the new kernel starts */ 400 kloader.rebootinfo = (void *)kv; 401 /* kv += sizeof(struct kloader_bootinfo); */ 402 403 /* 404 * Copy loader code 405 */ 406 KDASSERT(kloader.cur_pg); 407 kloader.loader = (void *)PG_VADDR(kloader.cur_pg); 408 memcpy(kloader.loader, kloader.ops->boot, PAGE_SIZE); 409 410 /* loader stack starts at the bottom of that page */ 411 kloader.loader_sp = (vaddr_t)kloader.loader + PAGE_SIZE; 412 413 DPRINTF("[loader] addr=%p sp=%p [kernel] entry=%p\n", 414 kloader.loader, (void *)kloader.loader_sp, (void *)nbi.entry); 415 416 return (0); 417 err: 418 if (ph != NULL) 419 kmem_free(ph, phsz); 420 if (sh != NULL) 421 kmem_free(sh, shsz); 422 if (shstrtab != NULL) 423 kmem_free(shstrtab, shstrsz); 424 425 return 1; 426 } 427 428 429 int 430 kloader_alloc_memory(size_t sz) 431 { 432 int n, error; 433 434 n = (sz + BUCKET_SIZE - 1) / BUCKET_SIZE /* kernel &co */ 435 + 1; /* 2nd loader */ 436 437 error = uvm_pglistalloc(n * PAGE_SIZE, avail_start, avail_end, 438 PAGE_SIZE, 0, &kloader.pg_head, n, 0); 439 if (error) { 440 PRINTF("can't allocate memory.\n"); 441 return (1); 442 } 443 DPRINTF("allocated %d pages.\n", n); 444 445 kloader.cur_pg = TAILQ_FIRST(&kloader.pg_head); 446 kloader.tagstart = (void *)PG_VADDR(kloader.cur_pg); 447 kloader.cur_tag = NULL; 448 449 return (0); 450 } 451 452 453 struct kloader_page_tag * 454 kloader_get_tag(vaddr_t dst) 455 { 456 struct vm_page *pg; 457 vaddr_t addr; 458 struct kloader_page_tag *tag; 459 460 tag = kloader.cur_tag; 461 if (tag != NULL /* has tag */ 462 && tag->sz < BUCKET_SIZE /* that has free space */ 463 && tag->dst + tag->sz == dst) /* and new data are contiguous */ 464 { 465 DPRINTFN(1, "current tag %x/%x ok\n", tag->dst, tag->sz); 466 return (tag); 467 } 468 469 pg = kloader.cur_pg; 470 KDASSERT(pg != NULL); 471 kloader.cur_pg = TAILQ_NEXT(pg, pageq.queue); 472 473 addr = PG_VADDR(pg); 474 tag = (void *)addr; 475 476 /* 477 * 2nd loader uses simple word-by-word copy, so destination 478 * address of a tag must be properly aligned. 479 */ 480 KASSERT(ALIGNED_POINTER(dst, register_t)); 481 482 tag->src = addr + sizeof(struct kloader_page_tag); 483 tag->dst = dst; 484 tag->sz = 0; 485 tag->next = 0; /* Terminate. this member may overwrite after. */ 486 if (kloader.cur_tag) 487 kloader.cur_tag->next = addr; 488 kloader.cur_tag = tag; 489 490 return (tag); 491 } 492 493 494 /* 495 * Operations to populate kloader_page_tag's with data. 496 */ 497 498 void 499 kloader_from_file(vaddr_t dst, off_t ofs, size_t sz) 500 { 501 struct kloader_page_tag *tag; 502 size_t freesz; 503 504 while (sz > 0) { 505 tag = kloader_get_tag(dst); 506 KDASSERT(tag != NULL); 507 freesz = BUCKET_SIZE - tag->sz; 508 if (freesz > sz) 509 freesz = sz; 510 511 DPRINTFN(1, "0x%08"PRIxVADDR" + 0x%zx <- 0x%lx\n", dst, freesz, 512 (unsigned long)ofs); 513 kloader_read(ofs, freesz, (void *)(tag->src + tag->sz)); 514 515 tag->sz += freesz; 516 sz -= freesz; 517 ofs += freesz; 518 dst += freesz; 519 } 520 } 521 522 523 void 524 kloader_copy(vaddr_t dst, const void *src, size_t sz) 525 { 526 struct kloader_page_tag *tag; 527 size_t freesz; 528 529 while (sz > 0) { 530 tag = kloader_get_tag(dst); 531 KDASSERT(tag != NULL); 532 freesz = BUCKET_SIZE - tag->sz; 533 if (freesz > sz) 534 freesz = sz; 535 536 DPRINTFN(1, "0x%08"PRIxVADDR" + 0x%zx <- %p\n", dst, freesz, src); 537 memcpy((void *)(tag->src + tag->sz), src, freesz); 538 539 tag->sz += freesz; 540 sz -= freesz; 541 src = (const char *)src + freesz; 542 dst += freesz; 543 } 544 } 545 546 547 void 548 kloader_zero(vaddr_t dst, size_t sz) 549 { 550 struct kloader_page_tag *tag; 551 size_t freesz; 552 553 while (sz > 0) { 554 tag = kloader_get_tag(dst); 555 KDASSERT(tag != NULL); 556 freesz = BUCKET_SIZE - tag->sz; 557 if (freesz > sz) 558 freesz = sz; 559 560 DPRINTFN(1, "0x%08"PRIxVADDR" + 0x%zx\n", dst, freesz); 561 memset((void *)(tag->src + tag->sz), 0, freesz); 562 563 tag->sz += freesz; 564 sz -= freesz; 565 dst += freesz; 566 } 567 } 568 569 570 void 571 kloader_load_segment(Elf_Phdr *p) 572 { 573 574 DPRINTF("memory 0x%08x 0x%x <- file 0x%x 0x%x\n", 575 p->p_vaddr, p->p_memsz, p->p_offset, p->p_filesz); 576 577 kloader_from_file(p->p_vaddr, p->p_offset, p->p_filesz); 578 #ifdef KLOADER_ZERO_BSS 579 kloader_zero(p->p_vaddr + p->p_filesz, p->p_memsz - p->p_filesz); 580 #endif 581 } 582 583 584 /* 585 * file access 586 */ 587 struct vnode * 588 kloader_open(const char *filename) 589 { 590 struct pathbuf *pb; 591 struct nameidata nid; 592 struct vnode *vp; 593 int error; 594 595 pb = pathbuf_create(filename); 596 if (pb == NULL) { 597 PRINTF("%s: pathbuf_create failed\n", filename); 598 return (NULL); 599 } 600 601 /* 602 * XXX why does this call both namei and vn_open? 603 */ 604 605 NDINIT(&nid, LOOKUP, FOLLOW, pb); 606 error = namei(&nid); 607 if (error != 0) { 608 PRINTF("%s: namei failed, errno=%d\n", filename, error); 609 pathbuf_destroy(pb); 610 return (NULL); 611 } 612 613 error = vn_open(NULL, pb, 0, FREAD, 0, &vp, NULL, NULL); 614 if (error != 0) { 615 PRINTF("%s: open failed, errno=%d\n", filename, error); 616 pathbuf_destroy(pb); 617 return (NULL); 618 } 619 620 pathbuf_destroy(pb); 621 return vp; 622 } 623 624 void 625 kloader_close(void) 626 { 627 struct lwp *l = KLOADER_LWP; 628 struct vnode *vp = kloader.vp; 629 630 VOP_UNLOCK(vp); 631 vn_close(vp, FREAD, l->l_cred); 632 } 633 634 int 635 kloader_read(size_t ofs, size_t size, void *buf) 636 { 637 struct lwp *l = KLOADER_LWP; 638 struct vnode *vp = kloader.vp; 639 size_t resid; 640 int error; 641 642 error = vn_rdwr(UIO_READ, vp, buf, size, ofs, UIO_SYSSPACE, 643 IO_NODELOCKED | IO_SYNC, l->l_cred, &resid, NULL); 644 645 if (error) 646 PRINTF("read error.\n"); 647 648 return (error); 649 } 650 651 652 /* 653 * bootinfo 654 */ 655 void 656 kloader_bootinfo_set(struct kloader_bootinfo *kbi, int argc, char *argv[], 657 struct bootinfo *bi, int printok) 658 { 659 char *p, *pend, *buf; 660 int i; 661 662 kloader.bootinfo = kbi; 663 buf = kbi->_argbuf; 664 if (bi != NULL) 665 memcpy(&kbi->bootinfo, bi, sizeof(struct bootinfo)); 666 kbi->argc = argc; 667 kbi->argv = (char **)buf; 668 669 p = &buf[argc * sizeof(char **)]; 670 pend = &buf[KLOADER_KERNELARGS_MAX - 1]; 671 672 for (i = 0; i < argc; i++) { 673 char *q = argv[i]; 674 int len = strlen(q) + 1; 675 if ((p + len) > pend) { 676 kloader.bootinfo = NULL; 677 if (printok) 678 PRINTF("buffer insufficient.\n"); 679 return; 680 } 681 kbi->argv[i] = p; 682 memcpy(p, q, len); 683 p += len; 684 } 685 } 686 687 688 #ifdef KLOADER_DEBUG 689 void 690 kloader_pagetag_dump(void) 691 { 692 struct kloader_page_tag *tag = kloader.tagstart; 693 struct kloader_page_tag *p, *op; 694 bool print; 695 int i, n; 696 697 p = tag; 698 op = NULL; 699 i = 0, n = 15; 700 701 PRINTF("[page tag chain]\n"); 702 do { 703 print = FALSE; 704 if (i < n) 705 print = TRUE; 706 if ((uint32_t)p & 3) { 707 printf("tag alignment error\n"); 708 break; 709 } 710 if ((p->src & 3) || (p->dst & 3)) { 711 printf("data alignment error.\n"); 712 print = TRUE; 713 } 714 715 if (print) { 716 printf("[%2d] next 0x%08x src 0x%08x dst 0x%08x" 717 " sz 0x%x\n", i, p->next, p->src, p->dst, p->sz); 718 } else if (i == n) { 719 printf("[...]\n"); 720 } 721 op = p; 722 i++; 723 } while ((p = (struct kloader_page_tag *)(p->next)) != 0); 724 725 if (op != NULL) 726 printf("[%d(last)] next 0x%08x src 0x%08x dst 0x%08x sz 0x%x\n", 727 i - 1, op->next, op->src, op->dst, op->sz); 728 } 729 730 #endif /* KLOADER_DEBUG */ 731