These changes are the raw update to qemu-2.6.
[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  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 /**
27  * @file
28  *
29  * ELF image format
30  *
31  * A "pure" ELF image is not a bootable image.  There are various
32  * bootable formats based upon ELF (e.g. Multiboot), which share
33  * common ELF-related functionality.
34  */
35
36 #include <errno.h>
37 #include <elf.h>
38 #include <ipxe/uaccess.h>
39 #include <ipxe/segment.h>
40 #include <ipxe/image.h>
41 #include <ipxe/elf.h>
42
43 /**
44  * Load ELF segment into memory
45  *
46  * @v image             ELF file
47  * @v phdr              ELF program header
48  * @v dest              Destination address
49  * @ret rc              Return status code
50  */
51 static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
52                               physaddr_t dest ) {
53         userptr_t buffer = phys_to_user ( dest );
54         int rc;
55
56         DBGC ( image, "ELF %p loading segment [%x,%x) to [%lx,%lx,%lx)\n",
57                image, phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
58                dest, ( dest + phdr->p_filesz ), ( dest + phdr->p_memsz ) );
59
60         /* Verify and prepare segment */
61         if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
62                                    phdr->p_memsz ) ) != 0 ) {
63                 DBGC ( image, "ELF %p could not prepare segment: %s\n",
64                        image, strerror ( rc ) );
65                 return rc;
66         }
67
68         /* Copy image to segment */
69         memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );
70
71         return 0;
72 }
73
74 /**
75  * Process ELF segment
76  *
77  * @v image             ELF file
78  * @v ehdr              ELF executable header
79  * @v phdr              ELF program header
80  * @v process           Segment processor
81  * @ret entry           Entry point, if found
82  * @ret max             Maximum used address
83  * @ret rc              Return status code
84  */
85 static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr,
86                          int ( * process ) ( struct image *image,
87                                              Elf_Phdr *phdr, physaddr_t dest ),
88                          physaddr_t *entry, physaddr_t *max ) {
89         physaddr_t dest;
90         physaddr_t end;
91         unsigned long e_offset;
92         int rc;
93
94         /* Do nothing for non-PT_LOAD segments */
95         if ( phdr->p_type != PT_LOAD )
96                 return 0;
97
98         /* Check segment lies within image */
99         if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
100                 DBGC ( image, "ELF %p segment outside image\n", image );
101                 return -ENOEXEC;
102         }
103
104         /* Find start address: use physical address for preference,
105          * fall back to virtual address if no physical address
106          * supplied.
107          */
108         dest = phdr->p_paddr;
109         if ( ! dest )
110                 dest = phdr->p_vaddr;
111         if ( ! dest ) {
112                 DBGC ( image, "ELF %p segment loads to physical address 0\n",
113                        image );
114                 return -ENOEXEC;
115         }
116         end = ( dest + phdr->p_memsz );
117
118         /* Update maximum used address, if applicable */
119         if ( end > *max )
120                 *max = end;
121
122         /* Process segment */
123         if ( ( rc = process ( image, phdr, dest ) ) != 0 )
124                 return rc;
125
126         /* Set execution address, if it lies within this segment */
127         if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) {
128                 *entry = ehdr->e_entry;
129                 DBGC ( image, "ELF %p found physical entry point at %lx\n",
130                        image, *entry );
131         } else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) )
132                     < phdr->p_filesz ) {
133                 if ( ! *entry ) {
134                         *entry = ( dest + e_offset );
135                         DBGC ( image, "ELF %p found virtual entry point at %lx"
136                                " (virt %lx)\n", image, *entry,
137                                ( ( unsigned long ) ehdr->e_entry ) );
138                 }
139         }
140
141         return 0;
142 }
143
144 /**
145  * Process ELF segments
146  *
147  * @v image             ELF file
148  * @v ehdr              ELF executable header
149  * @v process           Segment processor
150  * @ret entry           Entry point, if found
151  * @ret max             Maximum used address
152  * @ret rc              Return status code
153  */
154 int elf_segments ( struct image *image, Elf_Ehdr *ehdr,
155                    int ( * process ) ( struct image *image, Elf_Phdr *phdr,
156                                        physaddr_t dest ),
157                    physaddr_t *entry, physaddr_t *max ) {
158         Elf_Phdr phdr;
159         Elf_Off phoff;
160         unsigned int phnum;
161         int rc;
162
163         /* Initialise maximum used address */
164         *max = 0;
165
166         /* Invalidate entry point */
167         *entry = 0;
168
169         /* Read and process ELF program headers */
170         for ( phoff = ehdr->e_phoff , phnum = ehdr->e_phnum ; phnum ;
171               phoff += ehdr->e_phentsize, phnum-- ) {
172                 if ( phoff > image->len ) {
173                         DBGC ( image, "ELF %p program header %d outside "
174                                "image\n", image, phnum );
175                         return -ENOEXEC;
176                 }
177                 copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
178                 if ( ( rc = elf_segment ( image, ehdr, &phdr, process,
179                                           entry, max ) ) != 0 )
180                         return rc;
181         }
182
183         /* Check for a valid execution address */
184         if ( ! *entry ) {
185                 DBGC ( image, "ELF %p entry point %lx outside image\n",
186                        image, ( ( unsigned long ) ehdr->e_entry ) );
187                 return -ENOEXEC;
188         }
189
190         return 0;
191 }
192
193 /**
194  * Load ELF image into memory
195  *
196  * @v image             ELF file
197  * @ret entry           Entry point
198  * @ret max             Maximum used address
199  * @ret rc              Return status code
200  */
201 int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) {
202         static const uint8_t e_ident[] = {
203                 [EI_MAG0]       = ELFMAG0,
204                 [EI_MAG1]       = ELFMAG1,
205                 [EI_MAG2]       = ELFMAG2,
206                 [EI_MAG3]       = ELFMAG3,
207                 [EI_CLASS]      = ELFCLASS,
208         };
209         Elf_Ehdr ehdr;
210         int rc;
211
212         /* Read ELF header */
213         copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
214         if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident,
215                       sizeof ( e_ident ) ) != 0 ) {
216                 DBGC ( image, "ELF %p has invalid signature\n", image );
217                 return -ENOEXEC;
218         }
219
220         /* Load ELF segments into memory */
221         if ( ( rc = elf_segments ( image, &ehdr, elf_load_segment,
222                                    entry, max ) ) != 0 )
223                 return rc;
224
225         return 0;
226 }