This is a simple patch which allows payloads to be placed in memory in
[coreboot.git] / src / boot / elfboot.c
1 #include <console/console.h>
2 #include <part/fallback_boot.h>
3 #include <boot/elf.h>
4 #include <boot/elf_boot.h>
5 #include <boot/coreboot_tables.h>
6 #include <ip_checksum.h>
7 #include <stream/read_bytes.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 /* Maximum physical address we can use for the coreboot bounce buffer.
13  */
14 #ifndef MAX_ADDR
15 #define MAX_ADDR -1UL
16 #endif
17
18 extern unsigned char _ram_seg;
19 extern unsigned char _eram_seg;
20
21 struct segment {
22         struct segment *next;
23         struct segment *prev;
24         struct segment *phdr_next;
25         struct segment *phdr_prev;
26         unsigned long s_addr;
27         unsigned long s_memsz;
28         unsigned long s_offset;
29         unsigned long s_filesz;
30 };
31
32 struct verify_callback {
33         struct verify_callback *next;
34         int (*callback)(struct verify_callback *vcb, 
35                 Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head);
36         unsigned long desc_offset;
37         unsigned long desc_addr;
38 };
39
40 struct ip_checksum_vcb {
41         struct verify_callback data;
42         unsigned short ip_checksum;
43 };
44
45 int verify_ip_checksum(
46         struct verify_callback *vcb, 
47         Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head)
48 {
49         struct ip_checksum_vcb *cb;
50         struct segment *ptr;
51         unsigned long bytes;
52         unsigned long checksum;
53         unsigned char buff[2], *n_desc;
54         cb = (struct ip_checksum_vcb *)vcb;
55         /* zero the checksum so it's value won't
56          * get in the way of verifying the checksum.
57          */
58         n_desc = 0;
59         if (vcb->desc_addr) {
60                 n_desc = (unsigned char *)(vcb->desc_addr);
61                 memcpy(buff, n_desc, 2);
62                 memset(n_desc, 0, 2);
63         }
64         bytes = 0;
65         checksum = compute_ip_checksum(ehdr, sizeof(*ehdr));
66         bytes += sizeof(*ehdr);
67         checksum = add_ip_checksums(bytes, checksum, 
68                 compute_ip_checksum(phdr, ehdr->e_phnum*sizeof(*phdr)));
69         bytes += ehdr->e_phnum*sizeof(*phdr);
70         for(ptr = head->phdr_next; ptr != head; ptr = ptr->phdr_next) {
71                 checksum = add_ip_checksums(bytes, checksum,
72                         compute_ip_checksum((void *)ptr->s_addr, ptr->s_memsz));
73                 bytes += ptr->s_memsz;
74         }
75         if (n_desc != 0) {
76                 memcpy(n_desc, buff, 2);
77         }
78         if (checksum != cb->ip_checksum) {
79                 printk_err("Image checksum: %04x != computed checksum: %04x\n",
80                         cb->ip_checksum, checksum);
81         }
82         return checksum == cb->ip_checksum;
83 }
84
85 /* The problem:  
86  * Static executables all want to share the same addresses
87  * in memory because only a few addresses are reliably present on
88  * a machine, and implementing general relocation is hard.
89  *
90  * The solution:
91  * - Allocate a buffer twice the size of the coreboot image.
92  * - Anything that would overwrite coreboot copy into the lower half of
93  *   the buffer. 
94  * - After loading an ELF image copy coreboot to the upper half of the
95  *   buffer.
96  * - Then jump to the loaded image.
97  * 
98  * Benefits:
99  * - Nearly arbitrary standalone executables can be loaded.
100  * - Coreboot is preserved, so it can be returned to.
101  * - The implementation is still relatively simple,
102  *   and much simpler then the general case implemented in kexec.
103  * 
104  */
105
106 static unsigned long get_bounce_buffer(struct lb_memory *mem)
107 {
108         unsigned long lb_size;
109         unsigned long mem_entries;
110         unsigned long buffer;
111         int i;
112         lb_size = (unsigned long)(&_eram_seg - &_ram_seg);
113         /* Double coreboot size so I have somewhere to place a copy to return to */
114         lb_size = lb_size + lb_size;
115         mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
116         buffer = 0;
117         for(i = 0; i < mem_entries; i++) {
118                 unsigned long mstart, mend;
119                 unsigned long msize;
120                 unsigned long tbuffer;
121                 if (mem->map[i].type != LB_MEM_RAM)
122                         continue;
123                 if (unpack_lb64(mem->map[i].start) > MAX_ADDR)
124                         continue;
125                 if (unpack_lb64(mem->map[i].size) < lb_size)
126                         continue;
127                 mstart = unpack_lb64(mem->map[i].start);
128                 msize = MAX_ADDR - mstart +1;
129                 if (msize > unpack_lb64(mem->map[i].size))
130                         msize = unpack_lb64(mem->map[i].size);
131                 mend = mstart + msize;
132                 tbuffer = mend - lb_size;
133                 if (tbuffer < buffer) 
134                         continue;
135                 buffer = tbuffer;
136         }
137         return buffer;
138 }
139
140
141 static struct verify_callback *process_elf_notes(
142         unsigned char *header, 
143         unsigned long offset, unsigned long length)
144 {
145         struct verify_callback *cb_chain;
146         unsigned char *note, *end;
147         unsigned char *program, *version;
148
149         cb_chain = 0;
150         note = header + offset;
151         end = note + length;
152         program = version = 0;
153         while(note < end) {
154                 Elf_Nhdr *hdr;
155                 unsigned char *n_name, *n_desc, *next;
156                 hdr = (Elf_Nhdr *)note;
157                 n_name = note + sizeof(*hdr);
158                 n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
159                 next = n_desc + ((hdr->n_descsz + 3) & ~3);
160                 if (next > end) {
161                         break;
162                 }
163                 if ((hdr->n_namesz == sizeof(ELF_NOTE_BOOT)) && 
164                         (memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) {
165                         switch(hdr->n_type) {
166                         case EIN_PROGRAM_NAME:
167                                 if (n_desc[hdr->n_descsz -1] == 0) {
168                                         program = n_desc;
169                                 }
170                                 break;
171                         case EIN_PROGRAM_VERSION:
172                                 if (n_desc[hdr->n_descsz -1] == 0) {
173                                         version = n_desc;
174                                 }
175                                 break;
176                         case EIN_PROGRAM_CHECKSUM:
177                         {
178                                 struct ip_checksum_vcb *cb;
179                                 cb = malloc(sizeof(*cb));
180                                 cb->ip_checksum = *((uint16_t *)n_desc);
181                                 cb->data.callback = verify_ip_checksum;
182                                 cb->data.next = cb_chain;
183                                 cb->data.desc_offset = n_desc - header;
184                                 cb_chain = &cb->data;
185                                 break;
186                         }
187                         }
188                 }
189                 printk_spew("n_type: %08x n_name(%d): %-*.*s n_desc(%d): %-*.*s\n", 
190                         hdr->n_type,
191                         hdr->n_namesz, hdr->n_namesz, hdr->n_namesz, n_name,
192                         hdr->n_descsz,hdr->n_descsz, hdr->n_descsz, n_desc);
193                 note = next;
194         }
195         if (program && version) {
196                 printk_info("Loading %s version: %s\n",
197                         program, version);
198         }
199         return cb_chain;
200 }
201
202 static int valid_area(struct lb_memory *mem, unsigned long buffer,
203         unsigned long start, unsigned long len)
204 {
205         /* Check through all of the memory segments and ensure
206          * the segment that was passed in is completely contained
207          * in RAM.
208          */
209         int i;
210         unsigned long end = start + len;
211         unsigned long mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
212
213         /* See if I conflict with the bounce buffer */
214         if (end >= buffer) {
215                 return 0;
216         }
217
218         /* Walk through the table of valid memory ranges and see if I
219          * have a match.
220          */
221         for(i = 0; i < mem_entries; i++) {
222                 uint64_t mstart, mend;
223                 uint32_t mtype;
224                 mtype = mem->map[i].type;
225                 mstart = unpack_lb64(mem->map[i].start);
226                 mend = mstart + unpack_lb64(mem->map[i].size);
227                 if ((mtype == LB_MEM_RAM) && (start < mend) && (end > mstart)) {
228                         break;
229                 }
230                 if ((mtype == LB_MEM_TABLE) && (start < mend) && (end > mstart)) {
231                         printk_err("Payload is overwriting Coreboot tables.\n");
232                         break;
233                 }
234         }
235         if (i == mem_entries) {
236                 printk_err("No matching ram area found for range:\n");
237                 printk_err("  [0x%016lx, 0x%016lx)\n", start, end);
238                 printk_err("Ram areas\n");
239                 for(i = 0; i < mem_entries; i++) {
240                         uint64_t mstart, mend;
241                         uint32_t mtype;
242                         mtype = mem->map[i].type;
243                         mstart = unpack_lb64(mem->map[i].start);
244                         mend = mstart + unpack_lb64(mem->map[i].size);
245                         printk_err("  [0x%016lx, 0x%016lx) %s\n",
246                                 (unsigned long)mstart, 
247                                 (unsigned long)mend, 
248                                 (mtype == LB_MEM_RAM)?"RAM":"Reserved");
249                         
250                 }
251                 return 0;
252         }
253         return 1;
254 }
255
256 static void relocate_segment(unsigned long buffer, struct segment *seg)
257 {
258         /* Modify all segments that want to load onto coreboot
259          * to load onto the bounce buffer instead.
260          */
261         unsigned long lb_start = (unsigned long)&_ram_seg;
262         unsigned long lb_end = (unsigned long)&_eram_seg;
263         unsigned long start, middle, end;
264
265         printk_spew("lb: [0x%016lx, 0x%016lx)\n", 
266                 lb_start, lb_end);
267
268         start = seg->s_addr;
269         middle = start + seg->s_filesz;
270         end = start + seg->s_memsz;
271         /* I don't conflict with coreboot so get out of here */
272         if ((end <= lb_start) || (start >= lb_end))
273                 return;
274
275         printk_spew("segment: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
276                 start, middle, end);
277
278         /* Slice off a piece at the beginning
279          * that doesn't conflict with coreboot.
280          */
281         if (start < lb_start) {
282                 struct segment *new;
283                 unsigned long len = lb_start - start;
284                 new = malloc(sizeof(*new));
285                 *new = *seg;
286                 new->s_memsz = len;
287                 seg->s_memsz -= len;
288                 seg->s_addr += len;
289                 seg->s_offset += len;
290                 if (seg->s_filesz > len) {
291                         new->s_filesz = len;
292                         seg->s_filesz -= len;
293                 } else {
294                         seg->s_filesz = 0;
295                 }
296
297                 /* Order by stream offset */
298                 new->next = seg;
299                 new->prev = seg->prev;
300                 seg->prev->next = new;
301                 seg->prev = new;
302                 /* Order by original program header order */
303                 new->phdr_next = seg;
304                 new->phdr_prev = seg->phdr_prev;
305                 seg->phdr_prev->phdr_next = new;
306                 seg->phdr_prev = new;
307
308                 /* compute the new value of start */
309                 start = seg->s_addr;
310                 
311                 printk_spew("   early: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
312                         new->s_addr, 
313                         new->s_addr + new->s_filesz,
314                         new->s_addr + new->s_memsz);
315         }
316         
317         /* Slice off a piece at the end 
318          * that doesn't conflict with coreboot 
319          */
320         if (end > lb_end) {
321                 unsigned long len = lb_end - start;
322                 struct segment *new;
323                 new = malloc(sizeof(*new));
324                 *new = *seg;
325                 seg->s_memsz = len;
326                 new->s_memsz -= len;
327                 new->s_addr += len;
328                 new->s_offset += len;
329                 if (seg->s_filesz > len) {
330                         seg->s_filesz = len;
331                         new->s_filesz -= len;
332                 } else {
333                         new->s_filesz = 0;
334                 }
335                 /* Order by stream offset */
336                 new->next = seg->next;
337                 new->prev = seg;
338                 seg->next->prev = new;
339                 seg->next = new;
340                 /* Order by original program header order */
341                 new->phdr_next = seg->phdr_next;
342                 new->phdr_prev = seg;
343                 seg->phdr_next->phdr_prev = new;
344                 seg->phdr_next = new;
345
346                 /* compute the new value of end */
347                 end = start + len;
348                 
349                 printk_spew("   late: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
350                         new->s_addr, 
351                         new->s_addr + new->s_filesz,
352                         new->s_addr + new->s_memsz);
353                 
354         }
355         /* Now retarget this segment onto the bounce buffer */
356         seg->s_addr = buffer + (seg->s_addr - lb_start);
357
358         printk_spew(" bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
359                 seg->s_addr, 
360                 seg->s_addr + seg->s_filesz, 
361                 seg->s_addr + seg->s_memsz);
362 }
363
364
365 static int build_elf_segment_list(
366         struct segment *head, 
367         unsigned long bounce_buffer, struct lb_memory *mem,
368         Elf_phdr *phdr, int headers)
369 {
370         struct segment *ptr;
371         int i;
372         memset(head, 0, sizeof(*head));
373         head->phdr_next = head->phdr_prev = head;
374         head->next = head->prev = head;
375         for(i = 0; i < headers; i++) {
376                 struct segment *new;
377                 /* Ignore data that I don't need to handle */
378                 if (phdr[i].p_type != PT_LOAD) {
379                         printk_debug("Dropping non PT_LOAD segment\n");
380                         continue;
381                 }
382                 if (phdr[i].p_memsz == 0) {
383                         printk_debug("Dropping empty segment\n");
384                         continue;
385                 }
386                 new = malloc(sizeof(*new));
387                 new->s_addr = phdr[i].p_paddr;
388                 new->s_memsz = phdr[i].p_memsz;
389                 new->s_offset = phdr[i].p_offset;
390                 new->s_filesz = phdr[i].p_filesz;
391                 printk_debug("New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n",
392                         new->s_addr, new->s_memsz, new->s_offset, new->s_filesz);
393                 /* Clean up the values */
394                 if (new->s_filesz > new->s_memsz)  {
395                         new->s_filesz = new->s_memsz;
396                 }
397                 printk_debug("(cleaned up) New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n",
398                         new->s_addr, new->s_memsz, new->s_offset, new->s_filesz);
399                 for(ptr = head->next; ptr != head; ptr = ptr->next) {
400                         if (new->s_offset < ptr->s_offset)
401                                 break;
402                 }
403                 /* Order by stream offset */
404                 new->next = ptr;
405                 new->prev = ptr->prev;
406                 ptr->prev->next = new;
407                 ptr->prev = new;
408                 /* Order by original program header order */
409                 new->phdr_next = head;
410                 new->phdr_prev = head->phdr_prev;
411                 head->phdr_prev->phdr_next  = new;
412                 head->phdr_prev = new;
413
414                 /* Verify the memory addresses in the segment are valid */
415                 if (!valid_area(mem, bounce_buffer, new->s_addr, new->s_memsz)) 
416                         goto out;
417
418                 /* Modify the segment to load onto the bounce_buffer if necessary.
419                  */
420                 relocate_segment(bounce_buffer, new);
421         }
422         return 1;
423  out:
424         return 0;
425 }
426
427 static int load_elf_segments(
428         struct segment *head, unsigned char *header, unsigned long header_size)
429 {
430         unsigned long offset;
431         struct segment *ptr;
432         
433         offset = 0;
434         for(ptr = head->next; ptr != head; ptr = ptr->next) {
435                 unsigned long start_offset;
436                 unsigned long skip_bytes, read_bytes;
437                 unsigned char *dest, *middle, *end;
438                 byte_offset_t result;
439                 printk_debug("Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
440                         ptr->s_addr, ptr->s_memsz, ptr->s_filesz);
441                 
442                 /* Compute the boundaries of the segment */
443                 dest = (unsigned char *)(ptr->s_addr);
444                 end = dest + ptr->s_memsz;
445                 middle = dest + ptr->s_filesz;
446                 start_offset = ptr->s_offset;
447                 /* Ignore s_offset if I have a pure bss segment */
448                 if (ptr->s_filesz == 0) {
449                         start_offset = offset;
450                 }
451                 
452                 printk_spew("[ 0x%016lx, %016lx, 0x%016lx) <- %016lx\n",
453                         (unsigned long)dest,
454                         (unsigned long)middle,
455                         (unsigned long)end,
456                         (unsigned long)start_offset);
457                 
458                 /* Skip intial buffer unused bytes */
459                 if (offset < header_size) {
460                         if (start_offset < header_size) {
461                                 offset = start_offset;
462                         } else {
463                                 offset = header_size;
464                         }
465                 }
466                 
467                 /* Skip the unused bytes */
468                 skip_bytes = start_offset - offset;
469                 if (skip_bytes && 
470                         ((result = stream_skip(skip_bytes)) != skip_bytes)) {
471                         printk_err("ERROR: Skip of %ld bytes skipped %ld bytes\n",
472                                 skip_bytes, result);
473                         goto out;
474                 }
475                 offset = start_offset;
476                 
477                 /* Copy data from the initial buffer */
478                 if (offset < header_size) {
479                         size_t len;
480                         if ((ptr->s_filesz + start_offset) > header_size) {
481                                 len = header_size - start_offset;
482                         }
483                         else {
484                                 len = ptr->s_filesz;
485                         }
486                         memcpy(dest, &header[start_offset], len);
487                         dest += len;
488                 }
489                 
490                 /* Read the segment into memory */
491                 read_bytes = middle - dest;
492                 if (read_bytes && 
493                         ((result = stream_read(dest, read_bytes)) != read_bytes)) {
494                         printk_err("ERROR: Read of %ld bytes read %ld bytes...\n",
495                                 read_bytes, result);
496                         goto out;
497                 }
498                 offset += ptr->s_filesz;
499                 
500                 /* Zero the extra bytes between middle & end */
501                 if (middle < end) {
502                         printk_debug("Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
503                                 (unsigned long)middle, end - middle);
504                         
505                         /* Zero the extra bytes */
506                         memset(middle, 0, end - middle);
507                 }
508         }
509         return 1;
510  out:
511         return 0;
512 }
513
514 static int verify_loaded_image(
515         struct verify_callback *vcb,
516         Elf_ehdr *ehdr, Elf_phdr *phdr,
517         struct segment *head
518         )
519 {
520         struct segment *ptr;
521         int ok;
522         ok = 1;
523         for(; ok && vcb ; vcb = vcb->next) {
524                 /* Find where the note is loaded */
525                 /* The whole note must be loaded intact
526                  * so an address of 0 for the descriptor is impossible
527                  */
528                 vcb->desc_addr = 0; 
529                 for(ptr = head->next; ptr != head; ptr = ptr->next) {
530                         unsigned long desc_addr;
531                         desc_addr = ptr->s_addr + vcb->desc_offset - ptr->s_offset;
532                         if ((desc_addr >= ptr->s_addr) &&
533                                 (desc_addr < (ptr->s_addr + ptr->s_filesz))) {
534                                 vcb->desc_addr = desc_addr;
535                         }
536                 }
537                 ok = vcb->callback(vcb, ehdr, phdr, head);
538         }
539         return ok;
540 }
541
542 int elfload(struct lb_memory *mem,
543         unsigned char *header, unsigned long header_size)
544 {
545         Elf_ehdr *ehdr;
546         Elf_phdr *phdr;
547         void *entry;
548         struct segment head;
549         struct verify_callback *cb_chain;
550         unsigned long bounce_buffer;
551
552         /* Find a bounce buffer so I can load to coreboot's current location */
553         bounce_buffer = get_bounce_buffer(mem);
554         if (!bounce_buffer) {
555                 printk_err("Could not find a bounce buffer...\n");
556                 goto out;
557         }
558
559         ehdr = (Elf_ehdr *)header;
560         entry = (void *)(ehdr->e_entry);
561         phdr = (Elf_phdr *)(&header[ehdr->e_phoff]);
562
563         /* Digest elf note information... */
564         cb_chain = 0;
565         if ((phdr[0].p_type == PT_NOTE) && 
566                 ((phdr[0].p_offset + phdr[0].p_filesz) < header_size)) {
567                 cb_chain = process_elf_notes(header,
568                         phdr[0].p_offset, phdr[0].p_filesz);
569         }
570
571         /* Preprocess the elf segments */
572         if (!build_elf_segment_list(&head, 
573                 bounce_buffer, mem, phdr, ehdr->e_phnum))
574                 goto out;
575
576         /* Load the segments */
577         if (!load_elf_segments(&head, header, header_size))
578                 goto out;
579
580         printk_spew("Loaded segments\n");
581         /* Verify the loaded image */
582         if (!verify_loaded_image(cb_chain, ehdr, phdr, &head)) 
583                 goto out;
584
585         printk_spew("verified segments\n");
586         /* Shutdown the stream device */
587         stream_fini();
588         
589         printk_spew("closed down stream\n");
590         /* Reset to booting from this image as late as possible */
591         boot_successful();
592
593         printk_debug("Jumping to boot code at 0x%x\n", entry);
594         post_code(0xfe);
595
596         /* Jump to kernel */
597         jmp_to_elf_entry(entry, bounce_buffer);
598         return 1;
599
600  out:
601         return 0;
602 }
603
604 int elfboot(struct lb_memory *mem)
605 {
606         Elf_ehdr *ehdr;
607         static unsigned char header[ELF_HEAD_SIZE];
608         int header_offset;
609         int i, result;
610
611         result = 0;
612         printk_info("\n");
613         printk_info("Welcome to %s, the open sourced starter.\n", BOOTLOADER);
614         printk_info("January 2002, Eric Biederman.\n");
615         printk_info("Version %s\n", BOOTLOADER_VERSION);
616         printk_info("\n");
617         post_code(0xf8);
618
619         if (stream_init() < 0) {
620                 printk_err("Could not initialize driver...\n");
621                 goto out;
622         }
623
624         /* Read in the initial ELF_HEAD_SIZE bytes */
625         if (stream_read(header, ELF_HEAD_SIZE) != ELF_HEAD_SIZE) {
626                 printk_err("Read failed...\n");
627                 goto out;
628         }
629         /* Scan for an elf header */
630         header_offset = -1;
631         for(i = 0; i < ELF_HEAD_SIZE - (sizeof(Elf_ehdr) + sizeof(Elf_phdr)); i+=16) {
632                 ehdr = (Elf_ehdr *)(&header[i]);
633                 if (memcmp(ehdr->e_ident, ELFMAG, 4) != 0) {
634                         printk_debug("No header at %d\n", i);
635                         continue;
636                 }
637                 printk_debug("Found ELF candidate at offset %d\n", i);
638                 /* Sanity check the elf header */
639                 if ((ehdr->e_type == ET_EXEC) &&
640                         elf_check_arch(ehdr) &&
641                         (ehdr->e_ident[EI_VERSION] == EV_CURRENT) &&
642                         (ehdr->e_version == EV_CURRENT) &&
643                         (ehdr->e_ehsize == sizeof(Elf_ehdr)) &&
644                         (ehdr->e_phentsize = sizeof(Elf_phdr)) &&
645                         (ehdr->e_phoff < (ELF_HEAD_SIZE - i)) &&
646                         ((ehdr->e_phoff + (ehdr->e_phentsize * ehdr->e_phnum)) <= 
647                                 (ELF_HEAD_SIZE - i))) {
648                         header_offset = i;
649                         break;
650                 }
651                 ehdr = 0;
652         }
653         printk_debug("header_offset is %d\n", header_offset);
654         if (header_offset == -1) {
655                 goto out;
656         }
657
658         printk_debug("Try to load at offset 0x%x\n", header_offset);
659         result = elfload(mem, 
660                 header + header_offset , ELF_HEAD_SIZE - header_offset);
661  out:
662         if (!result) {
663                 /* Shutdown the stream device */
664                 stream_fini();
665
666                 printk_err("Can not load ELF Image.\n");
667
668                 post_code(0xff);
669         }
670         return 0;
671
672 }