Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / image / elf.c
1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 /**
23  * @file
24  *
25  * ELF image format
26  *
27  * A "pure" ELF image is not a bootable image.  There are various
28  * bootable formats based upon ELF (e.g. Multiboot), which share
29  * common ELF-related functionality.
30  */
31
32 #include <errno.h>
33 #include <elf.h>
34 #include <ipxe/uaccess.h>
35 #include <ipxe/segment.h>
36 #include <ipxe/image.h>
37 #include <ipxe/elf.h>
38
39 typedef Elf32_Ehdr      Elf_Ehdr;
40 typedef Elf32_Phdr      Elf_Phdr;
41 typedef Elf32_Off       Elf_Off;
42 #define ELFCLASS        ELFCLASS32
43
44 /**
45  * Load ELF segment into memory
46  *
47  * @v image             ELF file
48  * @v phdr              ELF program header
49  * @v ehdr              ELF executable header
50  * @ret entry           Entry point, if found
51  * @ret max             Maximum used address
52  * @ret rc              Return status code
53  */
54 static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
55                               Elf_Ehdr *ehdr, physaddr_t *entry,
56                               physaddr_t *max ) {
57         physaddr_t dest;
58         physaddr_t end;
59         userptr_t buffer;
60         unsigned long e_offset;
61         int rc;
62
63         /* Do nothing for non-PT_LOAD segments */
64         if ( phdr->p_type != PT_LOAD )
65                 return 0;
66
67         /* Check segment lies within image */
68         if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
69                 DBGC ( image, "ELF %p segment outside image\n", image );
70                 return -ENOEXEC;
71         }
72
73         /* Find start address: use physical address for preference,
74          * fall back to virtual address if no physical address
75          * supplied.
76          */
77         dest = phdr->p_paddr;
78         if ( ! dest )
79                 dest = phdr->p_vaddr;
80         if ( ! dest ) {
81                 DBGC ( image, "ELF %p segment loads to physical address 0\n",
82                        image );
83                 return -ENOEXEC;
84         }
85         buffer = phys_to_user ( dest );
86         end = ( dest + phdr->p_memsz );
87
88         DBGC ( image, "ELF %p loading segment [%x,%x) to [%x,%x,%x)\n", image,
89                phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
90                phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ),
91                ( phdr->p_paddr + phdr->p_memsz ) );
92
93         /* Verify and prepare segment */
94         if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
95                                    phdr->p_memsz ) ) != 0 ) {
96                 DBGC ( image, "ELF %p could not prepare segment: %s\n",
97                        image, strerror ( rc ) );
98                 return rc;
99         }
100
101         /* Update maximum used address, if applicable */
102         if ( end > *max )
103                 *max = end;
104
105         /* Copy image to segment */
106         memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );
107
108         /* Set execution address, if it lies within this segment */
109         if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) {
110                 *entry = ehdr->e_entry;
111                 DBGC ( image, "ELF %p found physical entry point at %lx\n",
112                        image, *entry );
113         } else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) )
114                     < phdr->p_filesz ) {
115                 if ( ! *entry ) {
116                         *entry = ( dest + e_offset );
117                         DBGC ( image, "ELF %p found virtual entry point at %lx"
118                                " (virt %lx)\n", image, *entry,
119                                ( ( unsigned long ) ehdr->e_entry ) );
120                 }
121         }
122
123         return 0;
124 }
125
126 /**
127  * Load ELF image into memory
128  *
129  * @v image             ELF file
130  * @ret entry           Entry point
131  * @ret max             Maximum used address
132  * @ret rc              Return status code
133  */
134 int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) {
135         static const uint8_t e_ident[] = {
136                 [EI_MAG0]       = ELFMAG0,
137                 [EI_MAG1]       = ELFMAG1,
138                 [EI_MAG2]       = ELFMAG2,
139                 [EI_MAG3]       = ELFMAG3,
140                 [EI_CLASS]      = ELFCLASS,
141         };
142         Elf_Ehdr ehdr;
143         Elf_Phdr phdr;
144         Elf_Off phoff;
145         unsigned int phnum;
146         int rc;
147
148         /* Read ELF header */
149         copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
150         if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident,
151                       sizeof ( e_ident ) ) != 0 ) {
152                 DBGC ( image, "ELF %p has invalid signature\n", image );
153                 return -ENOEXEC;
154         }
155
156         /* Initialise maximum used address */
157         *max = 0;
158
159         /* Invalidate entry point */
160         *entry = 0;
161
162         /* Read ELF program headers */
163         for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ;
164               phoff += ehdr.e_phentsize, phnum-- ) {
165                 if ( phoff > image->len ) {
166                         DBGC ( image, "ELF %p program header %d outside "
167                                "image\n", image, phnum );
168                         return -ENOEXEC;
169                 }
170                 copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
171                 if ( ( rc = elf_load_segment ( image, &phdr, &ehdr,
172                                                entry, max ) ) != 0 ) {
173                         return rc;
174                 }
175         }
176
177         /* Check for a valid execution address */
178         if ( ! *entry ) {
179                 DBGC ( image, "ELF %p entry point %lx outside image\n",
180                        image, ( ( unsigned long ) ehdr.e_entry ) );
181                 return -ENOEXEC;
182         }
183
184         return 0;
185 }