coreboot/util/mkelfImage/linux-i386/mkelf-linux-i386.c
<<
>>
Prefs
   1#include <stdio.h>
   2#include <errno.h>
   3#include <stdlib.h>
   4#include <stdint.h>
   5#include <string.h>
   6#define _GNU_SOURCE
   7#include <getopt.h>
   8#include "elf.h"
   9#include "elf_boot.h"
  10#include "convert.h"
  11#include "x86-linux.h"
  12#include "mkelfImage.h"
  13
  14static unsigned char payload[] = {
  15#include "convert.bin.c"
  16};
  17
  18struct kernel_info;
  19static void (*parse_kernel_type)(struct kernel_info *info, char *kernel_buf, size_t kernel_size);
  20static void parse_bzImage_kernel(struct kernel_info *info, char *kernel_buf, size_t kernel_size);
  21static void parse_elf32_kernel(struct kernel_info *info, char *kernel_buf, size_t kernel_size);
  22static void parse_elf64_kernel(struct kernel_info *info, char *kernel_buf, size_t kernel_size);
  23
  24char *vmlinux_x86_64_probe(char *kernel_buf, off_t kernel_size);
  25
  26char *vmlinux_i386_probe(char *kernel_buf, off_t kernel_size)
  27{
  28        Elf32_Ehdr *ehdr;
  29        Elf32_Phdr *phdr;
  30        int i;
  31        int phdrs;
  32        ehdr = (Elf32_Ehdr *)kernel_buf;
  33        if (
  34                (ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
  35                (ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
  36                (ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
  37                (ehdr->e_ident[EI_MAG3] != ELFMAG3)) {
  38                return "No ELF signature found on kernel\n";
  39        }
  40        if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) {
  41                return vmlinux_x86_64_probe(kernel_buf, kernel_size);
  42//              return "Not a 32bit ELF kernel\n";
  43        }
  44        if (ehdr->e_ident[EI_DATA]  != ELFDATA2LSB) {
  45                return "Not a little endian ELF kernel\n";
  46        }
  47        if (le16_to_cpu(ehdr->e_type) != ET_EXEC) {
  48                return "Not an executable kernel\n";
  49        }
  50        if (le16_to_cpu(ehdr->e_machine) != EM_386) {
  51                return "Not an i386 kernel\n";
  52        }
  53        if (    (ehdr->e_ident[EI_VERSION] != EV_CURRENT) ||
  54                (le32_to_cpu(ehdr->e_version) != EV_CURRENT)) {
  55                return "Kernel not using ELF version 1.\n";
  56        }
  57        if (le16_to_cpu(ehdr->e_phentsize) != sizeof(*phdr)) {
  58                return "Kernel uses bad program header size.\n";
  59        }
  60        phdr = (Elf32_Phdr *)(kernel_buf + le32_to_cpu(ehdr->e_phoff));
  61        phdrs = 0;
  62        for(i = 0; i < le16_to_cpu(ehdr->e_phnum); i++) {
  63                if (le32_to_cpu(phdr[i].p_type) != PT_LOAD)
  64                        continue;
  65                phdrs++;
  66        }
  67        if (phdrs == 0) {
  68                return "No PT_LOAD segments!\n";
  69        }
  70        parse_kernel_type = parse_elf32_kernel;
  71        return 0;
  72}
  73char *vmlinux_x86_64_probe(char *kernel_buf, off_t kernel_size)
  74{
  75        Elf64_Ehdr *ehdr;
  76        Elf64_Phdr *phdr;
  77        int i;
  78        int phdrs = 0;
  79        ehdr = (Elf64_Ehdr *)kernel_buf;
  80        if (
  81                (ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
  82                (ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
  83                (ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
  84                (ehdr->e_ident[EI_MAG3] != ELFMAG3)) {
  85                return "No ELF signature found on kernel\n";
  86        }
  87        if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
  88                return "Not a 64bit ELF kernel\n";
  89        }
  90        if (ehdr->e_ident[EI_DATA]  != ELFDATA2LSB) {
  91                return "Not a little endian ELF kernel\n";
  92        }
  93        if (le16_to_cpu(ehdr->e_type) != ET_EXEC) {
  94                return "Not an executable kernel\n";
  95        }
  96        if (le16_to_cpu(ehdr->e_machine) != EM_X86_64) {
  97                return "Not an x86_64 kernel\n";
  98        }
  99        if (    (ehdr->e_ident[EI_VERSION] != EV_CURRENT) ||
 100                (le32_to_cpu(ehdr->e_version) != EV_CURRENT)) {
 101                return "Kernel not using ELF version 1.\n";
 102        }
 103        if (le16_to_cpu(ehdr->e_phentsize) != sizeof(*phdr)) {
 104                return "Kernel uses bad program header size.\n";
 105        }
 106        phdr = (Elf64_Phdr *)(kernel_buf + le64_to_cpu(ehdr->e_phoff));
 107        phdrs = 0;
 108        for(i = 0; i < le16_to_cpu(ehdr->e_phnum); i++) {
 109                if (le32_to_cpu(phdr[i].p_type) != PT_LOAD)
 110                        continue;
 111                phdrs++;
 112        }
 113        if (phdrs == 0) {
 114                return "No PT_LOAD segments!\n";
 115        }
 116        parse_kernel_type = parse_elf64_kernel;
 117        return 0;
 118}
 119
 120char *bzImage_i386_probe(char *kernel_buf, off_t kernel_size)
 121{
 122        struct x86_linux_header *hdr;
 123        unsigned long offset;
 124        int setup_sects;
 125        hdr = (struct x86_linux_header *)kernel_buf;
 126
 127        if (le16_to_cpu(hdr->boot_sector_magic) != 0xaa55) {
 128                return "No bootsector magic";
 129        }
 130        if (memcmp(hdr->header_magic, "HdrS", 4) != 0) {
 131                return "Not a linux kernel";
 132        }
 133
 134        if (le16_to_cpu(hdr->protocol_version) < 0x202) {
 135                return "Kernel protcols version before 2.02 not supported";
 136        }
 137
 138        setup_sects = hdr->setup_sects;
 139        if (setup_sects == 0) {
 140                setup_sects = 4;
 141        }
 142        offset = 512 + (512 *setup_sects);
 143        if (offset > kernel_size) {
 144                return "Not enough bytes";
 145        }
 146        parse_kernel_type = parse_bzImage_kernel;
 147        return 0;
 148}
 149
 150char *linux_i386_probe(char *kernel_buf, off_t kernel_size)
 151{
 152        char *result;
 153        result = "";
 154        if (result) result = bzImage_i386_probe(kernel_buf, kernel_size);
 155        if (result) result = vmlinux_i386_probe(kernel_buf, kernel_size);
 156        if (result) result = bzImage_i386_probe(kernel_buf, kernel_size);
 157        return result;
 158}
 159
 160#define NR_SECTIONS 16
 161
 162struct kernel_info
 163{
 164        int phdrs;
 165        void *kernel[NR_SECTIONS];
 166        size_t filesz[NR_SECTIONS];
 167        size_t memsz[NR_SECTIONS];
 168        size_t paddr[NR_SECTIONS];
 169        size_t vaddr[NR_SECTIONS];
 170        size_t entry;
 171        size_t switch_64;
 172        char *version;
 173};
 174
 175static void parse_elf32_kernel(struct kernel_info *info, char *kernel_buf, size_t kernel_size)
 176{
 177        Elf32_Ehdr *ehdr;
 178        Elf32_Phdr *phdr;
 179        int i;
 180        int phdrs;
 181        ehdr = (Elf32_Ehdr *)kernel_buf;
 182        phdr = (Elf32_Phdr *)(kernel_buf + ehdr->e_phoff);
 183        phdrs = 0;
 184        for(i = 0; i < le16_to_cpu(ehdr->e_phnum); i++) {
 185                if (le32_to_cpu(phdr[i].p_type) != PT_LOAD)
 186                        continue;
 187                if(phdrs == NR_SECTIONS)
 188                        die("NR_SECTIONS is too small\n");
 189                info->kernel[phdrs]  = kernel_buf + le32_to_cpu(phdr[i].p_offset);
 190                info->filesz[phdrs]  = le32_to_cpu(phdr[i].p_filesz);
 191                info->memsz[phdrs]   = le32_to_cpu(phdr[i].p_memsz);
 192                info->paddr[phdrs]   = le32_to_cpu(phdr[i].p_paddr) & 0xfffffff;
 193                info->vaddr[phdrs]   = le32_to_cpu(phdr[i].p_vaddr);
 194                phdrs++;
 195        }
 196
 197        if(!phdrs)
 198                die("We need at least one phdr\n");
 199
 200        info->phdrs = phdrs;
 201        info->entry   = le32_to_cpu(ehdr->e_entry);
 202        info->switch_64   = 0; //not convert from elf64
 203        info->version = "unknown";
 204}
 205
 206static void parse_elf64_kernel(struct kernel_info *info, char *kernel_buf, size_t kernel_size)
 207{
 208        Elf64_Ehdr *ehdr;
 209        Elf64_Phdr *phdr;
 210        int i;
 211        int phdrs;
 212        ehdr = (Elf64_Ehdr *)kernel_buf;
 213        phdr = (Elf64_Phdr *)(kernel_buf + le64_to_cpu(ehdr->e_phoff));
 214
 215        phdrs = 0;
 216        for(i = 0; i < le16_to_cpu(ehdr->e_phnum); i++) {
 217                if (le32_to_cpu(phdr[i].p_type) != PT_LOAD)
 218                        continue;
 219                if(phdrs == NR_SECTIONS)
 220                        die("NR_SECTIONS is too small\n");
 221                info->kernel[phdrs]  = kernel_buf + le64_to_cpu(phdr[i].p_offset);
 222                info->filesz[phdrs]  = le64_to_cpu(phdr[i].p_filesz);
 223                info->memsz[phdrs]   = le64_to_cpu(phdr[i].p_memsz);
 224                info->paddr[phdrs]   = le64_to_cpu(phdr[i].p_paddr) & 0xfffffff;
 225                info->vaddr[phdrs]   = le64_to_cpu(phdr[i].p_vaddr);
 226                phdrs++;
 227        }
 228
 229        if(!phdrs)
 230                die("We need at least one phdr\n");
 231
 232        info->phdrs = phdrs;
 233        info->entry   = le64_to_cpu(ehdr->e_entry);
 234#if  0
 235        if (info->entry != info->paddr[0]) {
 236                info->entry = info->paddr[0]; // we still have startup_32 there
 237                info->switch_64   = 0; //not convert from elf64
 238        } else
 239#endif
 240                info->switch_64   = 1; //convert from elf64
 241
 242        info->version = "unknown";
 243}
 244
 245
 246static void parse_bzImage_kernel(struct kernel_info *info, char *kernel_buf, size_t kernel_size)
 247{
 248        struct x86_linux_header *hdr;
 249        unsigned long offset;
 250        int setup_sects;
 251        hdr = (struct x86_linux_header *)kernel_buf;
 252        setup_sects = hdr->setup_sects;
 253        if (setup_sects == 0) {
 254                setup_sects = 4;
 255        }
 256        offset = 512 + (512 *setup_sects);
 257
 258        info->kernel[0]  = kernel_buf + offset;
 259        info->filesz[0]  = kernel_size - offset;
 260        info->memsz[0]   = 0x700000;
 261        info->paddr[0]   = 0x100000;
 262        info->vaddr[0]   = 0x100000;
 263        info->phdrs = 1;
 264        info->entry   = info->paddr[0];
 265        info->switch_64   = 0; //not convert from elf64, even later bzImage become elf64, it still includes startup_32
 266        info->version = kernel_buf + 512 + le16_to_cpu(hdr->kver_addr);
 267}
 268
 269static void parse_kernel(struct kernel_info *info, char *kernel_buf, size_t kernel_size)
 270{
 271        memset(info, 0, sizeof(*info));
 272        if (parse_kernel_type) {
 273                parse_kernel_type(info, kernel_buf, kernel_size);
 274        }
 275        else {
 276                die("Unknown kernel format");
 277        }
 278}
 279
 280void linux_i386_usage(void)
 281{
 282        printf(
 283                "      --command-line=<string> Set the command line to <string>\n"
 284                "      --append=<string>       Set the command line to <string>\n"
 285                "      --initrd=<filename>     Set the initrd to <filename>\n"
 286                "      --ramdisk=<filename>    Set the initrd to <filename>\n"
 287                "      --ramdisk-base=<addr>   Set the initrd load address to <addr>\n"
 288                );
 289        return;
 290}
 291
 292
 293#define OPT_CMDLINE        OPT_MAX+0
 294#define OPT_RAMDISK        OPT_MAX+1
 295#define OPT_RAMDISK_BASE   OPT_MAX+2
 296
 297#define DEFAULT_RAMDISK_BASE (8*1024*1024)
 298
 299int linux_i386_mkelf(int argc, char **argv,
 300        struct memelfheader *ehdr, char *kernel_buf, off_t kernel_size)
 301{
 302        const char *ramdisk, *cmdline;
 303        unsigned long ramdisk_base;
 304        char *payload_buf, *ramdisk_buf;
 305        off_t payload_size, ramdisk_size;
 306        struct memelfphdr *phdr;
 307        struct memelfnote *note;
 308        struct kernel_info kinfo;
 309        struct image_parameters *params;
 310        int index;
 311        int i;
 312
 313        int opt;
 314        static const struct option options[] = {
 315                MKELF_OPTIONS
 316                { "command-line",    1, 0, OPT_CMDLINE },
 317                { "append",          1, 0, OPT_CMDLINE },
 318                { "initrd",          1, 0, OPT_RAMDISK },
 319                { "ramdisk",         1, 0, OPT_RAMDISK },
 320                { "ramdisk-base",    1, 0, OPT_RAMDISK_BASE },
 321                { 0 , 0, 0, 0 },
 322        };
 323        static const char short_options[] = MKELF_OPT_STR;
 324
 325        ramdisk_base = DEFAULT_RAMDISK_BASE;
 326        ramdisk = 0;
 327        cmdline="";
 328
 329        while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
 330                switch(opt) {
 331                case '?':
 332                        error("Unknown option %s\n", argv[optind]);
 333                        break;
 334                case OPT_RAMDISK_BASE:
 335                {
 336                        char *end;
 337                        unsigned long base;
 338                        base = strtoul(optarg, &end, 0);
 339                        if ((end == optarg) || (*end != '\0')) {
 340                                error("Invalid ramdisk base\n");
 341                        }
 342                        ramdisk_base = base;
 343                }
 344                case OPT_RAMDISK:
 345                        ramdisk = optarg;
 346                        break;
 347                case OPT_CMDLINE:
 348                        cmdline = optarg;
 349                        break;
 350                default:
 351                        break;
 352                }
 353        }
 354        ehdr->ei_class  = ELFCLASS32;
 355        ehdr->ei_data   = ELFDATA2LSB;
 356        ehdr->e_type    = ET_EXEC;
 357        ehdr->e_machine = EM_386;
 358
 359        /* locate the payload buffer */
 360        payload_buf = payload;
 361        payload_size = sizeof(payload);
 362
 363        /* slurp the input files */
 364        ramdisk_buf = slurp_file(ramdisk, &ramdisk_size);
 365
 366        /* parse the kernel */
 367        parse_kernel(&kinfo, kernel_buf, kernel_size);
 368
 369        /* Find the parameters */
 370        params = (void *)(payload_buf + (payload_size - sizeof(*params)));
 371
 372        /* A sanity check against bad versions of binutils */
 373        if (params->convert_magic != CONVERT_MAGIC) {
 374                die("Internal error convert_magic %08x != %08x\n",
 375                        params->convert_magic, CONVERT_MAGIC);
 376        }
 377
 378        /* Copy the command line */
 379        strncpy(params->cmdline, cmdline, sizeof(params->cmdline));
 380        params->cmdline[sizeof(params->cmdline)-1]= '\0';
 381
 382
 383        /* Add a program header for the note section */
 384        index = 4;
 385        index += (kinfo.phdrs - 1);
 386        index += ramdisk_size ? 1:0;
 387        phdr = add_program_headers(ehdr, index);
 388
 389        /* Fill in the program headers*/
 390        phdr[0].p_type = PT_NOTE;
 391
 392        /* Fill in the converter program headers */
 393        phdr[1].p_paddr  = CONVERTLOC;
 394        phdr[1].p_vaddr  = CONVERTLOC;
 395        phdr[1].p_filesz = payload_size;
 396        phdr[1].p_memsz  = payload_size + params->bss_size;
 397        phdr[1].p_data   = payload;
 398
 399        /* Reserve space for the REAL MODE DATA segment AND the GDT segment */
 400        phdr[2].p_paddr  = REAL_MODE_DATA_LOC;
 401        phdr[2].p_vaddr  = REAL_MODE_DATA_LOC;
 402        phdr[2].p_filesz = 0;
 403        if(!kinfo.switch_64)
 404                phdr[2].p_memsz = (GDTLOC - REAL_MODE_DATA_LOC) + params->gdt_size;
 405        else
 406                phdr[2].p_memsz = (PGTLOC - REAL_MODE_DATA_LOC) + params->pgt_size;
 407        phdr[2].p_data   = 0;
 408
 409        if( (phdr[1].p_paddr + phdr[1].p_memsz) > phdr[2].p_paddr) {
 410                die("Internal error: need to increase REAL_MODE_DATA_LOC !\n");
 411        }
 412
 413        index = 3;
 414        /* Put the second kernel frament if present */
 415        for(i=0;i<kinfo.phdrs;i++) {
 416                phdr[index].p_paddr  = kinfo.paddr[i];
 417                phdr[index].p_vaddr  = kinfo.vaddr[i];
 418                phdr[index].p_filesz = kinfo.filesz[i];
 419                phdr[index].p_memsz  = kinfo.memsz[i];
 420                phdr[index].p_data   = kinfo.kernel[i];
 421                index++;
 422        }
 423
 424        /* Put the ramdisk at ramdisk base.
 425         */
 426        params->initrd_start = params->initrd_size = 0;
 427        if (ramdisk_size) {
 428                if( (phdr[index-1].p_paddr + phdr[index-1].p_memsz) > ramdisk_base) {
 429                        die("need to increase increase ramdisk_base !\n");
 430                }
 431
 432                phdr[index].p_paddr  = ramdisk_base;
 433                phdr[index].p_vaddr  = ramdisk_base;
 434                phdr[index].p_filesz = ramdisk_size;
 435                phdr[index].p_memsz  = ramdisk_size;
 436                phdr[index].p_data   = ramdisk_buf;
 437                params->initrd_start = phdr[index].p_paddr;
 438                params->initrd_size  = phdr[index].p_filesz;
 439                index++;
 440        }
 441
 442        /* Set the start location */
 443        params->entry = kinfo.entry;
 444        params->switch_64 = kinfo.switch_64;
 445        ehdr->e_entry = phdr[1].p_paddr;
 446
 447        /* Setup the elf notes */
 448        note = add_notes(ehdr, 3);
 449        note[0].n_type = EIN_PROGRAM_NAME;
 450        note[0].n_name = "ELFBoot";
 451        note[0].n_desc = "Linux";
 452        note[0].n_descsz = strlen(note[0].n_desc)+1;
 453
 454        note[1].n_type = EIN_PROGRAM_VERSION;
 455        note[1].n_name = "ELFBoot";
 456        note[1].n_desc = kinfo.version;
 457        note[1].n_descsz = strlen(note[1].n_desc);
 458
 459        note[2].n_type = EIN_PROGRAM_CHECKSUM;
 460        note[2].n_name = "ELFBoot";
 461        note[2].n_desc = 0;
 462        note[2].n_descsz = 2;
 463
 464        return 0;
 465}
 466
 467
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.