1#include <sys/types.h>
2#include <sys/stat.h>
3
4#include <assert.h>
5#include <errno.h>
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9
10#include <libelf.h>
11#include <multiboot2.h>
12
13#include "efi.h"
14
15void usage(char *name) {
16    fprintf(stderr, "usage: %s <multiboot image> <load address> "
17                    "<offset of mb header>\n", name);
18    exit(EXIT_FAILURE);
19}
20
21void fail(char *name) {
22    perror(name);
23    exit(EXIT_FAILURE);
24}
25
26void elf_fail(char *name) {
27    fprintf(stderr, "%s: %s\n", name, elf_errmsg(elf_errno()));
28    exit(EXIT_FAILURE);
29}
30
31/* Read the complete contents of a file. */
32void *
33load_file(const char *path, size_t *length) {
34    FILE *file= fopen(path, "r");
35    if(!file) fail("fopen");
36
37    struct stat stat;
38    if(fstat(fileno(file), &stat) < 0) fail("stat");
39
40    char *buf= malloc(stat.st_size);
41    if(!buf) fail("malloc");
42
43    if(fread(buf, 1, stat.st_size, file) != stat.st_size) fail("fread");
44
45    if(fclose(file) != 0) fail("fclose");
46
47    *length= stat.st_size;
48    return buf;
49}
50
51int
52main(int argc, char *argv[]) {
53    if(argc != 4) usage(argv[0]);
54
55    const char *infile= argv[1];
56
57    errno= 0;
58    uint64_t load_addr= strtoul(argv[2], NULL, 0);
59    if(errno) fail("strtoul");
60
61    errno= 0;
62    uint64_t offset= strtoul(argv[3], NULL, 0);
63    if(errno) fail("strtoul");
64
65    if(elf_version(EV_CURRENT) == EV_NONE) elf_fail("elf_version");
66
67    printf("Loading multiboot image \"%s\" at 0x%lx\n", infile, load_addr);
68
69    /* Load it. */
70    size_t mb_len;
71    void *mb_data= load_file(infile, &mb_len);
72
73    /* Start walking the header. */
74    void *cursor= mb_data + offset;
75
76    /* Check the fixed 8-byte header. */
77    uint32_t total_size= *((uint32_t *)cursor);
78    cursor+= sizeof(uint32_t);
79    assert(*((uint32_t *)cursor) == 0);
80    cursor+= sizeof(uint32_t);
81
82    /* Read the tags. */
83    while(cursor < mb_data + offset + total_size) {
84        uint64_t paddr= (cursor - mb_data) + load_addr;
85        struct multiboot_tag *tag=
86            (struct multiboot_tag *)cursor;
87
88        printf("Tag of %uB @ 0x%lx\n", (unsigned int)tag->size, paddr);
89        switch(tag->type) {
90            case MULTIBOOT_TAG_TYPE_CMDLINE:
91            {
92                struct multiboot_tag_string *str_tag=
93                    (struct multiboot_tag_string *)cursor;
94                printf("CPU driver command line: \"%s\"\n",
95                       str_tag->string);
96                break;
97            }
98            case MULTIBOOT_TAG_TYPE_ELF_SECTIONS:
99            {
100                struct multiboot_tag_elf_sections *sect_tag=
101                    (struct multiboot_tag_elf_sections *)cursor;
102                printf("CPU driver ELF section headers\n");
103                printf("%u headers of size %u\n",
104                        sect_tag->num, sect_tag->entsize);
105                printf("String table in section %u\n",
106                        sect_tag->shndx);
107
108                Elf64_Shdr *shdrs= (Elf64_Shdr *)sect_tag->sections;
109                for(size_t i= 0; i < sect_tag->num; i++) {
110                    printf("section %lu, type %u, address 0x%lx\n",
111                           i, shdrs[i].sh_type, shdrs[i].sh_addr);
112                }
113                break;
114            }
115            case MULTIBOOT_TAG_TYPE_MODULE_64:
116            {
117                struct multiboot_tag_module_64 *mod_tag=
118                    (struct multiboot_tag_module_64 *)cursor;
119                printf("64-bit ELF module @ 0x%lx-0x%lx\n",
120                       (uint64_t)mod_tag->mod_start,
121                       (uint64_t)mod_tag->mod_end);
122                printf("Command line: \"%s\"\n", mod_tag->cmdline);
123
124                void *elf_data= mb_data + (mod_tag->mod_start - load_addr);
125                size_t elf_size= mod_tag->mod_end - mod_tag->mod_start + 1;
126
127                Elf *elf= elf_memory(elf_data, elf_size);
128                if(!elf) elf_fail("elf_memory");
129
130                size_t e_i_size;
131                char *e_ident= elf_getident(elf, &e_i_size);
132                if(!e_ident) elf_fail("elf_getident");
133
134                printf("e_ident bytes:");
135                for(int i= 0; i < e_i_size; i++)
136                    printf(" %02x", e_ident[i]);
137                printf("\n");
138
139                Elf64_Ehdr *ehdr= elf64_getehdr(elf);
140                if(!ehdr) elf_fail("elf64_getehdr");
141
142                printf("Entry point: %lx\n", ehdr->e_entry);
143
144                if(elf_end(elf)) elf_fail("elf_end");
145                break;
146            }
147            case MULTIBOOT_TAG_TYPE_EFI_MMAP:
148            {
149                struct multiboot_tag_efi_mmap *mmap_tag=
150                    (struct multiboot_tag_efi_mmap *)cursor;
151                size_t mmap_len= mmap_tag->size / mmap_tag->descr_size;
152                print_mmap((efi_memory_descriptor *)&mmap_tag->efi_mmap,
153                           mmap_len);
154                break;
155            }
156        }
157        printf("\n");
158
159        cursor+= tag->size;
160    }
161}
162