cfd79869aebfd97096a9c76beec5765ca3599ced
[coreboot.git] / util / mkelfImage / linux-ia64 / mkelf-linux-ia64.c
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 "mkelfImage.h"
12
13 static unsigned char payload[] = {
14 #include "convert.bin.c"
15 };
16
17 char *linux_ia64_probe(char *kernel_buf, off_t kernel_size)
18 {
19         Elf64_Ehdr *ehdr;
20         Elf64_Phdr *phdr;
21         int i;
22         int phdrs;
23         ehdr = (Elf64_Ehdr *)kernel_buf;
24         if (
25                 (ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
26                 (ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
27                 (ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
28                 (ehdr->e_ident[EI_MAG3] != ELFMAG3)) {
29                 return "No ELF signature found on kernel\n";
30         }
31         if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
32                 return "Not a 64bit ELF kernel\n";
33         }
34         if (ehdr->e_ident[EI_DATA]  != ELFDATA2LSB) {
35                 return "Not a little endian ELF kernel\n";
36         }
37         if (le16_to_cpu(ehdr->e_type) != ET_EXEC) {
38                 return "Not an executable kernel\n";
39         }
40         if (le16_to_cpu(ehdr->e_machine) != EM_IA_64) {
41                 return "Not an ia64 kernel\n";
42         }
43         if (    (ehdr->e_ident[EI_VERSION] != EV_CURRENT) ||
44                 (le32_to_cpu(ehdr->e_version) != EV_CURRENT)) {
45                 return "Kernel not using ELF version 1.\n";
46         }
47         if (le16_to_cpu(ehdr->e_phentsize) != sizeof(*phdr)) {
48                 return "Kernel uses bad program header size.\n";
49         }
50         phdr = (Elf64_Phdr *)(kernel_buf + le64_to_cpu(ehdr->e_phoff));
51         phdrs = 0;
52         for(i = 0; i < le16_to_cpu(ehdr->e_phnum); i++) {
53                 if (le32_to_cpu(phdr[i].p_type) != PT_LOAD)
54                         continue;
55                 phdrs++;
56         }
57         if (phdrs == 0) {
58                 return "No PT_LOAD segments!\n";
59         }
60         return 0;
61 }
62
63 struct kernel_info
64 {
65         int phdrs;
66         char *kernel_buf;
67         Elf64_Ehdr *ehdr;
68         Elf64_Phdr *phdr;
69         uint64_t entry;
70         char *version;
71 };
72
73 static void parse_kernel(struct kernel_info *info,
74         char *kernel_buf, size_t kernel_size)
75 {
76         Elf64_Ehdr *ehdr;
77         Elf64_Phdr *phdr;
78         int i;
79         int phdrs;
80         ehdr = (Elf64_Ehdr *)kernel_buf;
81         phdr = (Elf64_Phdr *)(kernel_buf + le64_to_cpu(ehdr->e_phoff));
82         phdrs = 0;
83         for(i = 0; i < le16_to_cpu(ehdr->e_phnum); i++) {
84                 if (le32_to_cpu(phdr[i].p_type) != PT_LOAD)
85                         continue;
86                 phdrs++;
87         }
88         if (phdrs == 0) {
89                 die("No PT_LOAD segments!\n");
90         }
91         info->kernel_buf = kernel_buf;
92         info->ehdr    = ehdr;
93         info->phdrs   = phdrs;
94         info->phdr    = phdr;
95         info->entry   = le64_to_cpu(ehdr->e_entry);
96         info->version = "unknown";
97 }
98
99 static int populate_kernel_phdrs(struct kernel_info *info, struct memelfphdr *phdr)
100 {
101         uint64_t paddr;
102         int i;
103         paddr = 0;
104         for(i = 0; i < info->phdrs; i++) {
105                 Elf64_Phdr *hdr;
106                 int j;
107                 hdr = 0;
108                 for(j = 0; j < le16_to_cpu(info->ehdr->e_phnum); j++) {
109                         if (le16_to_cpu(info->phdr[j].p_type != PT_LOAD)) {
110                                 continue;
111                         }
112                         if (paddr > le64_to_cpu(info->phdr[j].p_paddr)) {
113                                 continue;
114                         }
115                         if (hdr && 
116                                 le64_to_cpu(hdr->p_paddr) < 
117                                 le64_to_cpu(info->phdr[j].p_paddr)) {
118                                 continue;
119                         }
120                         hdr = info->phdr + j;
121                 }
122                 if (!hdr) {
123                         die("Expected %d phdrs found %d!", info->phdrs, i);
124                 }
125                 phdr[i].p_paddr  = le64_to_cpu(hdr->p_paddr);
126                 phdr[i].p_vaddr  = le64_to_cpu(hdr->p_vaddr);
127                 phdr[i].p_filesz = le64_to_cpu(hdr->p_filesz);
128                 phdr[i].p_memsz  = le64_to_cpu(hdr->p_memsz);
129                 phdr[i].p_data   = info->kernel_buf + le64_to_cpu(hdr->p_offset);
130                 paddr = phdr[i].p_paddr + phdr[i].p_memsz;
131         }
132         return i;
133 }
134         
135
136 void linux_ia64_usage(void)
137 {
138         printf(
139                 "      --command-line=<string> Set the command line to <string>\n"
140                 "      --append=<string>       Set the command line to <string>\n"
141                 "      --initrd=<filename>     Set the initrd to <filename>\n"
142                 "      --ramdisk=<filename>    Set the initrd to <filename>\n"
143                 );
144         return;
145 }
146
147 #define OPT_CMDLINE        OPT_MAX+0
148 #define OPT_RAMDISK        OPT_MAX+1
149
150 int linux_ia64_mkelf(int argc, char **argv,
151         struct memelfheader *ehdr, char *kernel_buf, off_t kernel_size)
152 {
153         const char *ramdisk, *cmdline;
154         char *payload_buf, *ramdisk_buf;
155         off_t payload_size, ramdisk_size;
156         struct memelfphdr *phdr;
157         struct memelfnote *note;
158         struct kernel_info kinfo;
159         struct image_parameters *params;
160         int index;
161
162         int opt;
163         static const struct option options[] = {
164                 MKELF_OPTIONS
165                 { "command-line",    1, 0, OPT_CMDLINE },
166                 { "append",          1, 0, OPT_CMDLINE },
167                 { "initrd",          1, 0, OPT_RAMDISK },
168                 { "ramdisk",         1, 0, OPT_RAMDISK },
169                 { 0 , 0, 0, 0 },
170         };
171         static const char short_options[] = "HV";
172
173         ramdisk = 0;
174         cmdline="";
175
176         while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
177                 switch(opt) {
178                 case '?':
179                         error("Unknown option %s\n", argv[optind]);
180                         break;
181                 case OPT_RAMDISK:
182                         ramdisk = optarg;
183                         break;
184                 case OPT_CMDLINE:
185                         cmdline = optarg;
186                         break;
187                 default:
188                         break;
189                 }
190         }
191         ehdr->ei_class  = ELFCLASS64;
192         ehdr->ei_data   = ELFDATA2LSB;
193         ehdr->e_type    = ET_EXEC;
194         ehdr->e_machine = EM_IA_64;
195         
196         /* locate the payload buffer */
197         payload_buf = payload;
198         payload_size = sizeof(payload);
199
200         /* slurp the input files */
201         ramdisk_buf = slurp_file(ramdisk, &ramdisk_size);
202
203         /* parse the kernel */
204         parse_kernel(&kinfo, kernel_buf, kernel_size);
205
206         /* Find the parameters */
207         params = (void *)(payload_buf + (payload_size - sizeof(*params)));
208
209         /* A sanity check against bad versions of binutils */
210         if (params->convert_magic != CONVERT_MAGIC) {
211                 die("Internal error convert_magic %16llx != %16llx\n",
212                         (unsigned long long)(params->convert_magic), CONVERT_MAGIC);
213         }
214
215         /* Copy the command line */
216         strncpy(params->cmdline, cmdline, sizeof(params->cmdline));
217         params->cmdline[sizeof(params->cmdline)-1]= '\0';
218
219         /* Add a program header for the note section */
220         phdr = add_program_headers(ehdr, 2 + kinfo.phdrs + (ramdisk_size?1:0));
221
222         /* Fill in the program headers*/
223         phdr[0].p_type = PT_NOTE;
224         
225         /* Fill in the kernel program headers */
226         index = 1 + populate_kernel_phdrs(&kinfo, phdr + 1); 
227         
228         /* Fill in the converter program header */
229         phdr[index].p_paddr  = roundup(phdr[index -1].p_paddr + phdr[index -1].p_memsz, 16);
230         phdr[index].p_vaddr  = phdr[index].p_paddr;
231         phdr[index].p_filesz = payload_size;
232         phdr[index].p_memsz  = payload_size;
233         phdr[index].p_data   = payload_buf;
234         index++;
235
236         /* Set the start location */
237         params->entry = kinfo.entry;
238         ehdr->e_entry = phdr[index -1].p_paddr;
239
240
241         /* Fill in the ramdisk program header */
242         params->initrd_start = params->initrd_size = 0;
243         if (ramdisk_size) {
244                 phdr[index].p_paddr  = roundup(phdr[index -1].p_paddr + phdr[index -1].p_memsz, 16);
245                 phdr[index].p_vaddr  = phdr[index].p_paddr;
246                 phdr[index].p_filesz = ramdisk_size;
247                 phdr[index].p_memsz  = ramdisk_size;
248                 phdr[index].p_data   = ramdisk_buf;
249                 params->initrd_start = phdr[index].p_paddr;
250                 params->initrd_size  = phdr[index].p_filesz;
251                 index++;
252         }
253
254         /* Compute the elf notes */
255         note = add_notes(ehdr, 3);
256         note[0].n_type = EIN_PROGRAM_NAME;
257         note[0].n_name = "ELFBoot";
258         note[0].n_desc = "Linux";
259         note[0].n_descsz = strlen(note[0].n_desc)+1;
260
261         note[1].n_type = EIN_PROGRAM_VERSION;
262         note[1].n_name = "ELFBoot";
263         note[1].n_desc = kinfo.version;
264         note[1].n_descsz = strlen(note[1].n_desc)+1;
265
266         note[2].n_type = EIN_PROGRAM_CHECKSUM;
267         note[2].n_name = "ELFBoot";
268         note[2].n_desc = 0;
269         note[2].n_descsz = 2;
270
271         return 0;
272 }