1 | /* ELF Boot loader
|
---|
2 | * As we have seek, this implementation can be straightforward.
|
---|
3 | * 2003-07 by SONE Takeshi
|
---|
4 | */
|
---|
5 | #include <etherboot.h>
|
---|
6 | #include <elf.h>
|
---|
7 | #include <bits/elf_x.h>
|
---|
8 | #include <elf_boot.h>
|
---|
9 | #include <lib.h>
|
---|
10 | #include <sys_info.h>
|
---|
11 |
|
---|
12 | #include <fs.h>
|
---|
13 | #define DEBUG_THIS DEBUG_ELFBOOT
|
---|
14 | #include <debug.h>
|
---|
15 |
|
---|
16 | #if 1
|
---|
17 | //Use that in Etherboot
|
---|
18 | extern int elf_start(unsigned long __unused_i386, unsigned long entry, unsigned long param);
|
---|
19 | #define start_elf(x,y) elf_start(0, x, y)
|
---|
20 | #else
|
---|
21 | // original in filo
|
---|
22 | extern unsigned int start_elf(unsigned long entry_point, unsigned long param);
|
---|
23 | #endif
|
---|
24 |
|
---|
25 | extern char _virt_start[], _end[];
|
---|
26 |
|
---|
27 | static char *image_name, *image_version;
|
---|
28 |
|
---|
29 | static int check_mem_ranges(struct sys_info *info,
|
---|
30 | Elf_phdr *phdr, int phnum)
|
---|
31 | {
|
---|
32 | int i, j;
|
---|
33 | unsigned long start, end;
|
---|
34 | unsigned long prog_start, prog_end;
|
---|
35 | #if 0
|
---|
36 | struct memrange *mem;
|
---|
37 | #else
|
---|
38 | struct e820entry *mem;
|
---|
39 | #endif
|
---|
40 |
|
---|
41 | prog_start = virt_to_phys(&_virt_start);
|
---|
42 | prog_end = virt_to_phys(&_end);
|
---|
43 |
|
---|
44 | for (i = 0; i < phnum; i++) {
|
---|
45 | if (phdr[i].p_type != PT_LOAD)
|
---|
46 | continue;
|
---|
47 | start = phdr[i].p_paddr;
|
---|
48 | end = start + phdr[i].p_memsz;
|
---|
49 | if (start < prog_start && end > prog_start)
|
---|
50 | goto conflict;
|
---|
51 | if (start < prog_end && end > prog_end)
|
---|
52 | goto conflict;
|
---|
53 | #if 0
|
---|
54 | for (j = 0; j < info->n_memranges; j++) {
|
---|
55 | mem = &info->memrange[j];
|
---|
56 | if (mem->base <= start && mem->base + mem->size >= end)
|
---|
57 | break;
|
---|
58 | }
|
---|
59 | if (j >= info->n_memranges)
|
---|
60 | goto badseg;
|
---|
61 | #else
|
---|
62 | #define LB_MEM_RAM 1
|
---|
63 | for (j = 0; j < meminfo.map_count; j++) {
|
---|
64 | mem = &meminfo.map[j];
|
---|
65 | if (mem->type!=LB_MEM_RAM) continue;
|
---|
66 | if (mem->addr <= start && mem->addr + mem->size >= end)
|
---|
67 | break;
|
---|
68 | }
|
---|
69 | if (j >= meminfo.map_count)
|
---|
70 | goto badseg;
|
---|
71 | #endif
|
---|
72 | }
|
---|
73 | return 1;
|
---|
74 |
|
---|
75 | conflict:
|
---|
76 | printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end);
|
---|
77 |
|
---|
78 | badseg:
|
---|
79 | printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1);
|
---|
80 | return 0;
|
---|
81 | }
|
---|
82 |
|
---|
83 | static unsigned long process_image_notes(Elf_phdr *phdr, int phnum,
|
---|
84 | unsigned short *sum_ptr)
|
---|
85 | {
|
---|
86 | int i;
|
---|
87 | char *buf = NULL;
|
---|
88 | int retval = 0;
|
---|
89 | unsigned long addr, end;
|
---|
90 | Elf_Nhdr *nhdr;
|
---|
91 | const char *name;
|
---|
92 | void *desc;
|
---|
93 |
|
---|
94 | for (i = 0; i < phnum; i++) {
|
---|
95 | if (phdr[i].p_type != PT_NOTE)
|
---|
96 | continue;
|
---|
97 | buf = allot(phdr[i].p_filesz);
|
---|
98 | file_seek(phdr[i].p_offset);
|
---|
99 | if (file_read(buf, phdr[i].p_filesz) != phdr[i].p_filesz) {
|
---|
100 | printf("Can't read note segment\n");
|
---|
101 | goto out;
|
---|
102 | }
|
---|
103 | addr = (unsigned long) buf;
|
---|
104 | end = addr + phdr[i].p_filesz;
|
---|
105 | while (addr < end) {
|
---|
106 | nhdr = (Elf_Nhdr *) addr;
|
---|
107 | addr += sizeof(Elf_Nhdr);
|
---|
108 | name = (const char *) addr;
|
---|
109 | addr += (nhdr->n_namesz+3) & ~3;
|
---|
110 | desc = (void *) addr;
|
---|
111 | addr += (nhdr->n_descsz+3) & ~3;
|
---|
112 |
|
---|
113 | if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT)
|
---|
114 | && memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) {
|
---|
115 | if (nhdr->n_type == EIN_PROGRAM_NAME) {
|
---|
116 | image_name = calloc(1, nhdr->n_descsz + 1);
|
---|
117 | memcpy(image_name, desc, nhdr->n_descsz);
|
---|
118 | }
|
---|
119 | if (nhdr->n_type == EIN_PROGRAM_VERSION) {
|
---|
120 | image_version = calloc(1, nhdr->n_descsz + 1);
|
---|
121 | memcpy(image_version, desc, nhdr->n_descsz);
|
---|
122 | }
|
---|
123 | if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) {
|
---|
124 | *sum_ptr = *(unsigned short *) desc;
|
---|
125 | debug("Image checksum: %04x\n", *sum_ptr);
|
---|
126 | /* Where in the file */
|
---|
127 | retval = phdr[i].p_offset
|
---|
128 | + (unsigned long) desc - (unsigned long) buf;
|
---|
129 | }
|
---|
130 | }
|
---|
131 | }
|
---|
132 | }
|
---|
133 | out:
|
---|
134 | if (buf)
|
---|
135 | forget(buf);
|
---|
136 | return retval;
|
---|
137 | }
|
---|
138 |
|
---|
139 | static int load_segments(Elf_phdr *phdr, int phnum,
|
---|
140 | unsigned long checksum_offset)
|
---|
141 | {
|
---|
142 | unsigned long bytes;
|
---|
143 | unsigned int start_time, time;
|
---|
144 | int i;
|
---|
145 | int j;
|
---|
146 |
|
---|
147 | bytes = 0;
|
---|
148 | start_time = currticks();
|
---|
149 | #if 0
|
---|
150 | for (j = 0; j < phnum; j++) {
|
---|
151 | if (phdr[j].p_type != PT_LOAD)
|
---|
152 | continue;
|
---|
153 | debug("0 segment %d addr:%#x file:%#x mem:%#x, phdr%#x\n",
|
---|
154 | j, phdr[j].p_paddr, phdr[j].p_filesz, phdr[j].p_memsz, virt_to_phys(&phdr[j]));
|
---|
155 | }
|
---|
156 | #endif
|
---|
157 |
|
---|
158 | for (i = 0; i < phnum; i++) {
|
---|
159 | if (phdr[i].p_type != PT_LOAD)
|
---|
160 | continue;
|
---|
161 | debug("segment %d addr:%#x file:%#x mem:%#x phdr:%#x ",
|
---|
162 | i, phdr[i].p_paddr, phdr[i].p_filesz, phdr[i].p_memsz, virt_to_phys(&phdr[i]));
|
---|
163 | file_seek(phdr[i].p_offset);
|
---|
164 | debug("loading... ");
|
---|
165 | if (file_read(phys_to_virt(phdr[i].p_paddr), phdr[i].p_filesz)
|
---|
166 | != phdr[i].p_filesz) {
|
---|
167 | printf("Can't read program segment %d\n", i);
|
---|
168 | return 0;
|
---|
169 | }
|
---|
170 | bytes += phdr[i].p_filesz;
|
---|
171 | debug("clearing... ");
|
---|
172 | memset(phys_to_virt(phdr[i].p_paddr + phdr[i].p_filesz), 0,
|
---|
173 | phdr[i].p_memsz - phdr[i].p_filesz);
|
---|
174 | if (phdr[i].p_offset <= checksum_offset
|
---|
175 | && phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) {
|
---|
176 | debug("clearing checksum... ");
|
---|
177 | memset(phys_to_virt(phdr[i].p_paddr + checksum_offset
|
---|
178 | - phdr[i].p_offset), 0, 2);
|
---|
179 | }
|
---|
180 | debug("ok\n");
|
---|
181 |
|
---|
182 | }
|
---|
183 | time = (currticks() - start_time)*1000/18;
|
---|
184 | printf("Loaded %d bytes in %dms (%dKB/s)\n", bytes, time,
|
---|
185 | time? bytes/time : 0);
|
---|
186 | return 1;
|
---|
187 | }
|
---|
188 |
|
---|
189 | static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum,
|
---|
190 | unsigned short image_sum)
|
---|
191 | {
|
---|
192 | unsigned short sum, part_sum;
|
---|
193 | unsigned long offset;
|
---|
194 | int i;
|
---|
195 |
|
---|
196 | sum = 0;
|
---|
197 | offset = 0;
|
---|
198 |
|
---|
199 | part_sum = ipchksum(ehdr, sizeof *ehdr);
|
---|
200 | sum = add_ipchksums(offset, sum, part_sum);
|
---|
201 | offset += sizeof *ehdr;
|
---|
202 |
|
---|
203 | part_sum = ipchksum(phdr, phnum * sizeof(*phdr));
|
---|
204 | sum = add_ipchksums(offset, sum, part_sum);
|
---|
205 | offset += phnum * sizeof(*phdr);
|
---|
206 |
|
---|
207 | for (i = 0; i < phnum; i++) {
|
---|
208 | if (phdr[i].p_type != PT_LOAD)
|
---|
209 | continue;
|
---|
210 | part_sum = ipchksum(phys_to_virt(phdr[i].p_paddr), phdr[i].p_memsz);
|
---|
211 | sum = add_ipchksums(offset, sum, part_sum);
|
---|
212 | offset += phdr[i].p_memsz;
|
---|
213 | }
|
---|
214 |
|
---|
215 | if (sum != image_sum) {
|
---|
216 | printf("Verify FAILED (image:%04x vs computed:%04x)\n",
|
---|
217 | image_sum, sum);
|
---|
218 | return 0;
|
---|
219 | }
|
---|
220 | return 1;
|
---|
221 | }
|
---|
222 |
|
---|
223 | static inline unsigned const padded(unsigned s)
|
---|
224 | {
|
---|
225 | return (s + 3) & ~3;
|
---|
226 | }
|
---|
227 |
|
---|
228 | static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name,
|
---|
229 | unsigned type, const char *desc, unsigned descsz)
|
---|
230 | {
|
---|
231 | Elf_Nhdr nhdr;
|
---|
232 | unsigned ent_size, new_size, pad;
|
---|
233 | char *addr;
|
---|
234 |
|
---|
235 | if (!bhdr)
|
---|
236 | return NULL;
|
---|
237 |
|
---|
238 | nhdr.n_namesz = name? strlen(name)+1 : 0;
|
---|
239 | nhdr.n_descsz = descsz;
|
---|
240 | nhdr.n_type = type;
|
---|
241 | ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz);
|
---|
242 | if (bhdr->b_size + ent_size > 0xffff) {
|
---|
243 | printf("Boot notes too big\n");
|
---|
244 | forget(bhdr);
|
---|
245 | return NULL;
|
---|
246 | }
|
---|
247 | if (bhdr->b_size + ent_size > bhdr->b_checksum) {
|
---|
248 | do {
|
---|
249 | new_size = bhdr->b_checksum * 2;
|
---|
250 | } while (new_size < bhdr->b_size + ent_size);
|
---|
251 | if (new_size > 0xffff)
|
---|
252 | new_size = 0xffff;
|
---|
253 | debug("expanding boot note size to %u\n", new_size);
|
---|
254 | bhdr = realloc(bhdr, new_size);
|
---|
255 | bhdr->b_checksum = new_size;
|
---|
256 | }
|
---|
257 |
|
---|
258 | addr = (char *) bhdr;
|
---|
259 | addr += bhdr->b_size;
|
---|
260 | memcpy(addr, &nhdr, sizeof(nhdr));
|
---|
261 | addr += sizeof(nhdr);
|
---|
262 |
|
---|
263 | memcpy(addr, name, nhdr.n_namesz);
|
---|
264 | addr += nhdr.n_namesz;
|
---|
265 | pad = padded(nhdr.n_namesz) - nhdr.n_namesz;
|
---|
266 | memset(addr, 0, pad);
|
---|
267 | addr += pad;
|
---|
268 |
|
---|
269 | memcpy(addr, desc, nhdr.n_descsz);
|
---|
270 | addr += nhdr.n_descsz;
|
---|
271 | pad = padded(nhdr.n_descsz) - nhdr.n_descsz;
|
---|
272 | memset(addr, 0, pad);
|
---|
273 | addr += pad;
|
---|
274 |
|
---|
275 | bhdr->b_size += ent_size;
|
---|
276 | bhdr->b_records++;
|
---|
277 | return bhdr;
|
---|
278 | }
|
---|
279 |
|
---|
280 | static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name,
|
---|
281 | unsigned type, const char *desc)
|
---|
282 | {
|
---|
283 | return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1);
|
---|
284 | }
|
---|
285 |
|
---|
286 | static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline)
|
---|
287 | {
|
---|
288 | Elf_Bhdr *bhdr;
|
---|
289 |
|
---|
290 | bhdr = allot(256);
|
---|
291 | bhdr->b_signature = ELF_BHDR_MAGIC;
|
---|
292 | bhdr->b_size = sizeof *bhdr;
|
---|
293 | bhdr->b_checksum = 256; /* XXX cache the current buffer size here */
|
---|
294 | bhdr->b_records = 0;
|
---|
295 |
|
---|
296 | if (info->firmware)
|
---|
297 | bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware);
|
---|
298 | bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name);
|
---|
299 | bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version);
|
---|
300 | if (cmdline)
|
---|
301 | bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline);
|
---|
302 | if (!bhdr)
|
---|
303 | return bhdr;
|
---|
304 | bhdr->b_checksum = 0;
|
---|
305 | bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size);
|
---|
306 | return bhdr;
|
---|
307 | }
|
---|
308 |
|
---|
309 | int elf_load(struct sys_info *info, const char *filename, const char *cmdline)
|
---|
310 | {
|
---|
311 | Elf_ehdr ehdr;
|
---|
312 | Elf_phdr *phdr = NULL;
|
---|
313 | unsigned long phdr_size;
|
---|
314 | unsigned long checksum_offset;
|
---|
315 | unsigned short checksum;
|
---|
316 | Elf_Bhdr *boot_notes = NULL;
|
---|
317 | int retval = -1;
|
---|
318 | int image_retval;
|
---|
319 |
|
---|
320 | image_name = image_version = 0;
|
---|
321 |
|
---|
322 | if (!file_open(filename))
|
---|
323 | goto out;
|
---|
324 |
|
---|
325 | if (file_read(&ehdr, sizeof ehdr) != sizeof ehdr) {
|
---|
326 | debug("Can't read ELF header\n");
|
---|
327 | retval = LOADER_NOT_SUPPORT;
|
---|
328 | goto out;
|
---|
329 | }
|
---|
330 |
|
---|
331 | if (ehdr.e_ident[EI_MAG0] != ELFMAG0
|
---|
332 | || ehdr.e_ident[EI_MAG1] != ELFMAG1
|
---|
333 | || ehdr.e_ident[EI_MAG2] != ELFMAG2
|
---|
334 | || ehdr.e_ident[EI_MAG3] != ELFMAG3
|
---|
335 | || ehdr.e_ident[EI_CLASS] != ARCH_ELF_CLASS
|
---|
336 | || ehdr.e_ident[EI_DATA] != ARCH_ELF_DATA
|
---|
337 | || ehdr.e_ident[EI_VERSION] != EV_CURRENT
|
---|
338 | || ehdr.e_type != ET_EXEC
|
---|
339 | || !ARCH_ELF_MACHINE_OK(ehdr.e_machine)
|
---|
340 | || ehdr.e_version != EV_CURRENT
|
---|
341 | || ehdr.e_phentsize != sizeof(Elf_phdr)) {
|
---|
342 | debug("Not a bootable ELF image\n");
|
---|
343 | retval = LOADER_NOT_SUPPORT;
|
---|
344 | goto out;
|
---|
345 | }
|
---|
346 |
|
---|
347 | phdr_size = ehdr.e_phnum * sizeof *phdr;
|
---|
348 | phdr = allot(phdr_size);//hack LYH otherwise some one clear the last entry
|
---|
349 | file_seek(ehdr.e_phoff);
|
---|
350 | if (file_read(phdr, phdr_size) != phdr_size) {
|
---|
351 | printf("Can't read program header\n");
|
---|
352 | goto out;
|
---|
353 | }
|
---|
354 |
|
---|
355 | if (!check_mem_ranges(info, phdr, ehdr.e_phnum))
|
---|
356 | goto out;
|
---|
357 |
|
---|
358 | checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum);
|
---|
359 |
|
---|
360 | printf("Loading %s", image_name ? image_name : "image");
|
---|
361 | if (image_version)
|
---|
362 | printf(" version %s", image_version);
|
---|
363 | printf("...\n");
|
---|
364 |
|
---|
365 | if (!load_segments(phdr, ehdr.e_phnum, checksum_offset))
|
---|
366 | goto out;
|
---|
367 |
|
---|
368 | if (checksum_offset) {
|
---|
369 | if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum))
|
---|
370 | goto out;
|
---|
371 | }
|
---|
372 |
|
---|
373 | boot_notes = build_boot_notes(info, cmdline);
|
---|
374 |
|
---|
375 | debug("current time: %x\n", currticks());
|
---|
376 |
|
---|
377 | debug("entry point is %#x\n", ehdr.e_entry);
|
---|
378 | printf("Jumping to entry point...\n");
|
---|
379 |
|
---|
380 | image_retval = start_elf(ehdr.e_entry, virt_to_phys(boot_notes));
|
---|
381 | #if 0
|
---|
382 | console_init();
|
---|
383 | #endif
|
---|
384 |
|
---|
385 | printf("Image returned with return value %#x\n", image_retval);
|
---|
386 | retval = 0;
|
---|
387 |
|
---|
388 | out:
|
---|
389 | if (phdr)
|
---|
390 | forget(phdr);
|
---|
391 | if (boot_notes)
|
---|
392 | forget(boot_notes);
|
---|
393 | if (image_name)
|
---|
394 | forget(image_name);
|
---|
395 | if (image_version)
|
---|
396 | forget(image_version);
|
---|
397 | return retval;
|
---|
398 | }
|
---|