|
|
1.1 ! root 1: /* ELF Boot loader ! 2: * As we have seek, this implementation can be straightforward. ! 3: * 2003-07 by SONE Takeshi ! 4: */ ! 5: ! 6: #include "config.h" ! 7: #include "kernel/kernel.h" ! 8: #include "libc/diskio.h" ! 9: #include "arch/common/elf_boot.h" ! 10: #include "libopenbios/elf_load.h" ! 11: #include "libopenbios/sys_info.h" ! 12: #include "libopenbios/ipchecksum.h" ! 13: #include "libopenbios/bindings.h" ! 14: #include "libopenbios/ofmem.h" ! 15: #define printf printk ! 16: #define debug printk ! 17: ! 18: #define DEBUG 0 ! 19: ! 20: #define MAX_HEADERS 0x20 ! 21: #define BS 0x100 /* smallest step used when looking for the ELF header */ ! 22: ! 23: #ifdef CONFIG_PPC ! 24: extern void flush_icache_range( char *start, char *stop ); ! 25: #endif ! 26: ! 27: /* FreeBSD and possibly others mask the high 8 bits */ ! 28: #define addr_fixup(addr) ((addr) & 0x00ffffff) ! 29: ! 30: static char *image_name, *image_version; ! 31: static int fd; ! 32: ! 33: /* Note: avoid name collision with platforms which have their own version of calloc() */ ! 34: static void *ob_calloc(size_t nmemb, size_t size) ! 35: { ! 36: size_t alloc_size = nmemb * size; ! 37: void *mem; ! 38: ! 39: if (alloc_size < nmemb || alloc_size < size) { ! 40: printf("calloc overflow: %u, %u\n", nmemb, size); ! 41: return NULL; ! 42: } ! 43: ! 44: mem = malloc(alloc_size); ! 45: memset(mem, 0, alloc_size); ! 46: ! 47: return mem; ! 48: } ! 49: ! 50: static int check_mem_ranges(struct sys_info *info, ! 51: Elf_phdr *phdr, int phnum) ! 52: { ! 53: int i, j; ! 54: unsigned long start, end; ! 55: unsigned long prog_start, prog_end; ! 56: struct memrange *mem; ! 57: ! 58: prog_start = virt_to_phys(&_start); ! 59: prog_end = virt_to_phys(&_end); ! 60: ! 61: for (i = 0; i < phnum; i++) { ! 62: if (phdr[i].p_type != PT_LOAD) ! 63: continue; ! 64: start = addr_fixup(phdr[i].p_paddr); ! 65: end = start + phdr[i].p_memsz; ! 66: if (start < prog_start && end > prog_start) ! 67: goto conflict; ! 68: if (start < prog_end && end > prog_end) ! 69: goto conflict; ! 70: mem=info->memrange; ! 71: for (j = 0; j < info->n_memranges; j++) { ! 72: if (mem[j].base <= start && mem[j].base + mem[j].size >= end) ! 73: break; ! 74: } ! 75: if (j >= info->n_memranges) ! 76: goto badseg; ! 77: } ! 78: return 1; ! 79: ! 80: conflict: ! 81: printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end); ! 82: ! 83: badseg: ! 84: printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1); ! 85: return 0; ! 86: } ! 87: ! 88: static unsigned long process_image_notes(Elf_phdr *phdr, int phnum, ! 89: unsigned short *sum_ptr, ! 90: unsigned int offset) ! 91: { ! 92: int i; ! 93: char *buf = NULL; ! 94: int retval = 0; ! 95: unsigned long addr, end; ! 96: Elf_Nhdr *nhdr; ! 97: const char *name; ! 98: void *desc; ! 99: ! 100: for (i = 0; i < phnum; i++) { ! 101: if (phdr[i].p_type != PT_NOTE) ! 102: continue; ! 103: buf = malloc(phdr[i].p_filesz); ! 104: seek_io(fd, offset + phdr[i].p_offset); ! 105: if ((size_t)read_io(fd, buf, phdr[i].p_filesz) != phdr[i].p_filesz) { ! 106: printf("Can't read note segment\n"); ! 107: goto out; ! 108: } ! 109: addr = (unsigned long) buf; ! 110: end = addr + phdr[i].p_filesz; ! 111: while (addr < end) { ! 112: nhdr = (Elf_Nhdr *) addr; ! 113: addr += sizeof(Elf_Nhdr); ! 114: name = (const char *) addr; ! 115: addr += (nhdr->n_namesz+3) & ~3; ! 116: desc = (void *) addr; ! 117: addr += (nhdr->n_descsz+3) & ~3; ! 118: ! 119: if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT) ! 120: && memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) { ! 121: if (nhdr->n_type == EIN_PROGRAM_NAME) { ! 122: image_name = ob_calloc(1, nhdr->n_descsz + 1); ! 123: memcpy(image_name, desc, nhdr->n_descsz); ! 124: } ! 125: if (nhdr->n_type == EIN_PROGRAM_VERSION) { ! 126: image_version = ob_calloc(1, nhdr->n_descsz + 1); ! 127: memcpy(image_version, desc, nhdr->n_descsz); ! 128: } ! 129: if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) { ! 130: *sum_ptr = *(unsigned short *) desc; ! 131: debug("Image checksum: %#04x\n", *sum_ptr); ! 132: /* Where in the file */ ! 133: retval = phdr[i].p_offset ! 134: + (unsigned long) desc - (unsigned long) buf; ! 135: } ! 136: } ! 137: } ! 138: } ! 139: out: ! 140: close_io(fd); ! 141: if (buf) ! 142: free(buf); ! 143: return retval; ! 144: } ! 145: ! 146: static int load_segments(Elf_phdr *phdr, int phnum, ! 147: unsigned long checksum_offset, ! 148: unsigned int offset, unsigned long *bytes) ! 149: { ! 150: //unsigned int start_time, time; ! 151: int i; ! 152: ! 153: *bytes = 0; ! 154: // start_time = currticks(); ! 155: for (i = 0; i < phnum; i++) { ! 156: if (phdr[i].p_type != PT_LOAD) ! 157: continue; ! 158: debug("segment %d addr:" FMT_elf " file:" FMT_elf " mem:" FMT_elf " ", ! 159: i, addr_fixup(phdr[i].p_paddr), phdr[i].p_filesz, phdr[i].p_memsz); ! 160: seek_io(fd, offset + phdr[i].p_offset); ! 161: debug("loading... "); ! 162: if ((size_t)read_io(fd, phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_filesz) ! 163: != phdr[i].p_filesz) { ! 164: printf("Can't read program segment %d\n", i); ! 165: return 0; ! 166: } ! 167: bytes += phdr[i].p_filesz; ! 168: debug("clearing... "); ! 169: memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + phdr[i].p_filesz), 0, ! 170: phdr[i].p_memsz - phdr[i].p_filesz); ! 171: if (phdr[i].p_offset <= checksum_offset ! 172: && phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) { ! 173: debug("clearing checksum... "); ! 174: memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + checksum_offset ! 175: - phdr[i].p_offset), 0, 2); ! 176: } ! 177: debug("ok\n"); ! 178: ! 179: } ! 180: // time = currticks() - start_time; ! 181: //debug("Loaded %lu bytes in %ums (%luKB/s)\n", bytes, time, ! 182: // time? bytes/time : 0); ! 183: debug("Loaded %lu bytes \n", *bytes); ! 184: ! 185: return 1; ! 186: } ! 187: ! 188: static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum, ! 189: unsigned short image_sum) ! 190: { ! 191: unsigned short sum, part_sum; ! 192: unsigned long offset; ! 193: int i; ! 194: ! 195: sum = 0; ! 196: offset = 0; ! 197: ! 198: part_sum = ipchksum(ehdr, sizeof *ehdr); ! 199: sum = add_ipchksums(offset, sum, part_sum); ! 200: offset += sizeof *ehdr; ! 201: ! 202: part_sum = ipchksum(phdr, phnum * sizeof(*phdr)); ! 203: sum = add_ipchksums(offset, sum, part_sum); ! 204: offset += phnum * sizeof(*phdr); ! 205: ! 206: for (i = 0; i < phnum; i++) { ! 207: if (phdr[i].p_type != PT_LOAD) ! 208: continue; ! 209: part_sum = ipchksum(phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_memsz); ! 210: sum = add_ipchksums(offset, sum, part_sum); ! 211: offset += phdr[i].p_memsz; ! 212: } ! 213: ! 214: if (sum != image_sum) { ! 215: printf("Verify FAILED (image:%#04x vs computed:%#04x)\n", ! 216: image_sum, sum); ! 217: return 0; ! 218: } ! 219: return 1; ! 220: } ! 221: ! 222: static inline unsigned padded(unsigned s) ! 223: { ! 224: return (s + 3) & ~3; ! 225: } ! 226: ! 227: static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name, ! 228: unsigned type, const char *desc, unsigned descsz) ! 229: { ! 230: Elf_Nhdr nhdr; ! 231: unsigned ent_size, new_size, pad; ! 232: char *addr; ! 233: ! 234: if (!bhdr) ! 235: return NULL; ! 236: ! 237: nhdr.n_namesz = name? strlen(name)+1 : 0; ! 238: nhdr.n_descsz = descsz; ! 239: nhdr.n_type = type; ! 240: ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz); ! 241: if (bhdr->b_size + ent_size > 0xffff) { ! 242: printf("Boot notes too big\n"); ! 243: free(bhdr); ! 244: return NULL; ! 245: } ! 246: if (bhdr->b_size + ent_size > bhdr->b_checksum) { ! 247: do { ! 248: new_size = bhdr->b_checksum * 2; ! 249: } while (new_size < bhdr->b_size + ent_size); ! 250: if (new_size > 0xffff) ! 251: new_size = 0xffff; ! 252: debug("expanding boot note size to %u\n", new_size); ! 253: #ifdef HAVE_REALLOC ! 254: bhdr = realloc(bhdr, new_size); ! 255: bhdr->b_checksum = new_size; ! 256: #else ! 257: printf("Boot notes too big\n"); ! 258: free(bhdr); ! 259: return NULL; ! 260: #endif ! 261: } ! 262: ! 263: addr = (char *) bhdr; ! 264: addr += bhdr->b_size; ! 265: memcpy(addr, &nhdr, sizeof(nhdr)); ! 266: addr += sizeof(nhdr); ! 267: ! 268: if (name && nhdr.n_namesz) { ! 269: memcpy(addr, name, nhdr.n_namesz); ! 270: addr += nhdr.n_namesz; ! 271: pad = padded(nhdr.n_namesz) - nhdr.n_namesz; ! 272: memset(addr, 0, pad); ! 273: addr += pad; ! 274: } ! 275: ! 276: memcpy(addr, desc, nhdr.n_descsz); ! 277: addr += nhdr.n_descsz; ! 278: pad = padded(nhdr.n_descsz) - nhdr.n_descsz; ! 279: memset(addr, 0, pad); ! 280: ! 281: bhdr->b_size += ent_size; ! 282: bhdr->b_records++; ! 283: return bhdr; ! 284: } ! 285: ! 286: static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name, ! 287: unsigned type, const char *desc) ! 288: { ! 289: return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1); ! 290: } ! 291: ! 292: static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline) ! 293: { ! 294: Elf_Bhdr *bhdr; ! 295: ! 296: bhdr = malloc(256); ! 297: bhdr->b_signature = ELF_BHDR_MAGIC; ! 298: bhdr->b_size = sizeof *bhdr; ! 299: bhdr->b_checksum = 256; /* XXX cache the current buffer size here */ ! 300: bhdr->b_records = 0; ! 301: ! 302: if (info->firmware) ! 303: bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware); ! 304: bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name); ! 305: bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version); ! 306: if (cmdline) ! 307: bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline); ! 308: if (!bhdr) ! 309: return bhdr; ! 310: bhdr->b_checksum = 0; ! 311: bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size); ! 312: return bhdr; ! 313: } ! 314: ! 315: int ! 316: is_elf(Elf_ehdr *ehdr) ! 317: { ! 318: return (ehdr->e_ident[EI_MAG0] == ELFMAG0 ! 319: && ehdr->e_ident[EI_MAG1] == ELFMAG1 ! 320: && ehdr->e_ident[EI_MAG2] == ELFMAG2 ! 321: && ehdr->e_ident[EI_MAG3] == ELFMAG3 ! 322: && ehdr->e_ident[EI_CLASS] == ARCH_ELF_CLASS ! 323: && ehdr->e_ident[EI_DATA] == ARCH_ELF_DATA ! 324: && ehdr->e_ident[EI_VERSION] == EV_CURRENT ! 325: && ehdr->e_type == ET_EXEC ! 326: && ARCH_ELF_MACHINE_OK(ehdr->e_machine) ! 327: && ehdr->e_version == EV_CURRENT ! 328: && ehdr->e_phentsize == sizeof(Elf_phdr)); ! 329: } ! 330: ! 331: int ! 332: find_elf(Elf_ehdr *ehdr) ! 333: { ! 334: int offset; ! 335: ! 336: for (offset = 0; offset < MAX_HEADERS * BS; offset += BS) { ! 337: if ((size_t)read_io(fd, ehdr, sizeof ehdr) != sizeof ehdr) { ! 338: debug("Can't read ELF header\n"); ! 339: return 0; ! 340: } ! 341: ! 342: if (is_elf(ehdr)) { ! 343: debug("Found ELF header at offset %d\n", offset); ! 344: return offset; ! 345: } ! 346: ! 347: seek_io(fd, offset); ! 348: } ! 349: ! 350: debug("Not a bootable ELF image\n"); ! 351: return 0; ! 352: } ! 353: ! 354: Elf_phdr * ! 355: elf_readhdrs(int offset, Elf_ehdr *ehdr) ! 356: { ! 357: unsigned long phdr_size; ! 358: Elf_phdr *phdr; ! 359: ! 360: phdr_size = ehdr->e_phnum * sizeof(Elf_phdr); ! 361: phdr = malloc(phdr_size); ! 362: seek_io(fd, offset + ehdr->e_phoff); ! 363: if ((size_t)read_io(fd, phdr, phdr_size) != phdr_size) { ! 364: printf("Can't read program header\n"); ! 365: return NULL; ! 366: } ! 367: ! 368: return phdr; ! 369: } ! 370: ! 371: int ! 372: elf_load(struct sys_info *info, ihandle_t dev, const char *cmdline, void **boot_notes) ! 373: { ! 374: Elf_ehdr ehdr; ! 375: Elf_phdr *phdr = NULL; ! 376: unsigned long checksum_offset, file_size; ! 377: unsigned short checksum = 0; ! 378: int retval = -1; ! 379: unsigned int offset; ! 380: ! 381: image_name = image_version = NULL; ! 382: ! 383: /* Mark the saved-program-state as invalid */ ! 384: feval("0 state-valid !"); ! 385: ! 386: fd = open_ih(dev); ! 387: if (fd == -1) { ! 388: goto out; ! 389: } ! 390: ! 391: offset = find_elf(&ehdr); ! 392: if (!offset) { ! 393: retval = LOADER_NOT_SUPPORT; ! 394: goto out; ! 395: } ! 396: ! 397: #if DEBUG ! 398: printk("ELF header:\n"); ! 399: printk(" ehdr.e_type = %d\n", (int)ehdr.e_type); ! 400: printk(" ehdr.e_machine = %d\n", (int)ehdr.e_machine); ! 401: printk(" ehdr.e_version = %d\n", (int)ehdr.e_version); ! 402: printk(" ehdr.e_entry = 0x%08x\n", (int)ehdr.e_entry); ! 403: printk(" ehdr.e_phoff = 0x%08x\n", (int)ehdr.e_phoff); ! 404: printk(" ehdr.e_shoff = 0x%08x\n", (int)ehdr.e_shoff); ! 405: printk(" ehdr.e_flags = %d\n", (int)ehdr.e_flags); ! 406: printk(" ehdr.e_ehsize = 0x%08x\n", (int)ehdr.e_ehsize); ! 407: printk(" ehdr.e_phentsize = 0x%08x\n", (int)ehdr.e_phentsize); ! 408: printk(" ehdr.e_phnum = %d\n", (int)ehdr.e_phnum); ! 409: #endif ! 410: ! 411: if (ehdr.e_phnum > MAX_HEADERS) { ! 412: printk ("elfload: too many program headers (MAX_HEADERS)\n"); ! 413: retval = 0; ! 414: goto out; ! 415: } ! 416: ! 417: phdr = elf_readhdrs(offset, &ehdr); ! 418: if (!phdr) ! 419: goto out; ! 420: ! 421: if (!check_mem_ranges(info, phdr, ehdr.e_phnum)) ! 422: goto out; ! 423: ! 424: checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum, offset); ! 425: ! 426: printf("Loading %s", image_name ? image_name : "image"); ! 427: if (image_version) ! 428: printf(" version %s", image_version); ! 429: printf("...\n"); ! 430: ! 431: if (!load_segments(phdr, ehdr.e_phnum, checksum_offset, offset, &file_size)) ! 432: goto out; ! 433: ! 434: if (checksum_offset) { ! 435: if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum)) ! 436: goto out; ! 437: } ! 438: ! 439: /* If we are attempting an ELF boot image, we pass a non-NULL pointer ! 440: into boot_notes and mark the image as elf-boot rather than standard ! 441: ELF */ ! 442: if (boot_notes) { ! 443: *boot_notes = (void *)virt_to_phys(build_boot_notes(info, cmdline)); ! 444: feval("elf-boot saved-program-state >sps.file-type !"); ! 445: } else { ! 446: feval("elf saved-program-state >sps.file-type !"); ! 447: } ! 448: ! 449: //debug("current time: %lu\n", currticks()); ! 450: ! 451: debug("entry point is " FMT_elf "\n", addr_fixup(ehdr.e_entry)); ! 452: ! 453: // Initialise saved-program-state ! 454: PUSH(addr_fixup(ehdr.e_entry)); ! 455: feval("saved-program-state >sps.entry !"); ! 456: PUSH(file_size); ! 457: feval("saved-program-state >sps.file-size !"); ! 458: ! 459: feval("-1 state-valid !"); ! 460: ! 461: out: ! 462: close_io(fd); ! 463: if (phdr) ! 464: free(phdr); ! 465: if (image_name) ! 466: free(image_name); ! 467: if (image_version) ! 468: free(image_version); ! 469: return retval; ! 470: } ! 471: ! 472: void ! 473: elf_init_program(void) ! 474: { ! 475: char *base; ! 476: int i; ! 477: Elf_ehdr *ehdr; ! 478: Elf_phdr *phdr; ! 479: size_t size, total_size = 0; ! 480: char *addr; ! 481: uintptr_t tmp; ! 482: ! 483: /* TODO: manage ELF notes section */ ! 484: feval("0 state-valid !"); ! 485: feval("load-base"); ! 486: base = (char*)cell2pointer(POP()); ! 487: ! 488: ehdr = (Elf_ehdr *)base; ! 489: ! 490: if (!is_elf(ehdr)) { ! 491: debug("Not a valid ELF memory image\n"); ! 492: return; ! 493: } ! 494: ! 495: phdr = (Elf_phdr *)(base + ehdr->e_phoff); ! 496: ! 497: for (i = 0; i < ehdr->e_phnum; i++) { ! 498: ! 499: #if DEBUG ! 500: debug("filesz: %08lX memsz: %08lX p_offset: %08lX " ! 501: "p_vaddr %08lX\n", ! 502: (unsigned long)phdr[i].p_filesz, (unsigned long)phdr[i].p_memsz, ! 503: (unsigned long)phdr[i].p_offset, (unsigned long)phdr[i].p_vaddr ); ! 504: #endif ! 505: ! 506: size = MIN(phdr[i].p_filesz, phdr[i].p_memsz); ! 507: if (!size) ! 508: continue; ! 509: #if !defined(CONFIG_SPARC32) && !defined(CONFIG_X86) ! 510: if( ofmem_claim( phdr[i].p_vaddr, phdr[i].p_memsz, 0 ) == -1 ) { ! 511: printk("Ignoring failed claim for va %lx memsz %lx!\n", ! 512: (unsigned long)phdr[i].p_vaddr, ! 513: (unsigned long)phdr[i].p_memsz); ! 514: } ! 515: #endif ! 516: /* Workaround for archs where sizeof(int) != pointer size */ ! 517: tmp = phdr[i].p_vaddr; ! 518: addr = (char *)tmp; ! 519: ! 520: memcpy(addr, base + phdr[i].p_offset, size); ! 521: ! 522: total_size += size; ! 523: ! 524: #ifdef CONFIG_PPC ! 525: flush_icache_range( addr, addr + size ); ! 526: #endif ! 527: } ! 528: ! 529: // Initialise saved-program-state ! 530: PUSH(ehdr->e_entry); ! 531: feval("saved-program-state >sps.entry !"); ! 532: PUSH(total_size); ! 533: feval("saved-program-state >sps.file-size !"); ! 534: feval("elf saved-program-state >sps.file-type !"); ! 535: ! 536: feval("-1 state-valid !"); ! 537: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.