1 /******************************************************************************
2  * Copyright (c) 2004, 2011 IBM Corporation
3  * All rights reserved.
4  * This program and the accompanying materials
5  * are made available under the terms of the BSD License
6  * which accompanies this distribution, and is available at
7  * http://www.opensource.org/licenses/bsd-license.php
8  *
9  * Contributors:
10  *     IBM Corporation - initial implementation
11  *****************************************************************************/
12 
13 /*
14  * ELF loader
15  */
16 
17 #include <string.h>
18 #include <cache.h>
19 #include <libelf.h>
20 #include <byteorder.h>
21 
22 /**
23  * elf_check_file tests if the file at file_addr is
24  * a correct endian, ELF PPC executable
25  * @param file_addr  pointer to the start of the ELF file
26  * @return           the class (1 for 32 bit, 2 for 64 bit)
27  *                   -1 if it is not an ELF file
28  *                   -2 if it has the wrong endianness
29  *                   -3 if it is not an ELF executable
30  *                   -4 if it is not for PPC
31  */
32 static int
elf_check_file(unsigned long * file_addr)33 elf_check_file(unsigned long *file_addr)
34 {
35 	struct ehdr *ehdr = (struct ehdr *) file_addr;
36 	uint8_t native_endian;
37 
38 	/* check if it is an ELF image at all */
39 	if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46)
40 		return -1;
41 
42 #ifdef __BIG_ENDIAN__
43 	native_endian = ELFDATA2MSB;
44 #else
45 	native_endian = ELFDATA2LSB;
46 #endif
47 
48 	if (native_endian != ehdr->ei_data) {
49 		switch (ehdr->ei_class) {
50 		case 1:
51 			elf_byteswap_header32(file_addr);
52 			break;
53 		case 2:
54 			elf_byteswap_header64(file_addr);
55 			break;
56 		}
57 	}
58 
59 	/* check if it is an ELF executable ... and also
60 	 * allow DYN files, since this is specified by ePAPR */
61 	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
62 		return -3;
63 
64 	/* check if it is a PPC ELF executable */
65 	if (ehdr->e_machine != 0x14 && ehdr->e_machine != 0x15)
66 		return -4;
67 
68 	return ehdr->ei_class;
69 }
70 
71 /**
72  * load_elf_file tries to load the ELF file specified in file_addr
73  *
74  * it first checks if the file is a PPC ELF executable and then loads
75  * the segments depending if it is a 64bit or 32 bit ELF file
76  *
77  * @param file_addr  pointer to the start of the elf file
78  * @param entry      pointer where the ELF loader will store
79  *                   the entry point
80  * @param pre_load   handler that is called before copying a segment
81  * @param post_load  handler that is called after copying a segment
82  * @return           1 for a 32 bit file
83  *                   2 for a 64 bit BE file
84  *                   3 for a 64 bit LE ABIv1 file
85  *                   4 for a 64 bit LE ABIv2 file
86  *                   5 for a 32 bit LE ABIv1 file
87  *                   anything else means an error during load
88  */
89 int
elf_load_file(void * file_addr,unsigned long * entry,int (* pre_load)(void *,long),void (* post_load)(void *,long))90 elf_load_file(void *file_addr, unsigned long *entry,
91               int (*pre_load)(void*, long),
92               void (*post_load)(void*, long))
93 {
94 	int type = elf_check_file(file_addr);
95 	struct ehdr *ehdr = (struct ehdr *) file_addr;
96 
97 	switch (type) {
98 	case 1:
99 		*entry = elf_load_segments32(file_addr, 0, pre_load, post_load);
100 		if (ehdr->ei_data != ELFDATA2MSB) {
101 			type = 5; /* LE32 ABIv1 */
102 		}
103 		break;
104 	case 2:
105 		*entry = elf_load_segments64(file_addr, 0, pre_load, post_load);
106 		if (ehdr->ei_data != ELFDATA2MSB) {
107 			uint32_t flags = elf_get_eflags_64(file_addr);
108 			if ((flags & 0x3) == 2)
109 				type = 4; /* LE64 ABIv2 */
110 			else
111 				type = 3; /* LE64 ABIv1 */
112 		}
113 		break;
114 	}
115 	if (*entry == 0)
116 		type = 0;
117 
118 	return type;
119 }
120 
121 
122 /**
123  * load_elf_file_to_addr loads an ELF file to given address.
124  * This is useful for 64-bit vmlinux images that use the virtual entry
125  * point address in their headers, and thereby need a special treatment.
126  *
127  * @param file_addr  pointer to the start of the elf file
128  * @param entry      pointer where the ELF loader will store
129  *                   the entry point
130  * @param pre_load   handler that is called before copying a segment
131  * @param post_load  handler that is called after copying a segment
132  * @return           1 for a 32 bit file
133  *                   2 for a 64 bit file
134  *                   anything else means an error during load
135  */
136 int
elf_load_file_to_addr(void * file_addr,void * addr,unsigned long * entry,int (* pre_load)(void *,long),void (* post_load)(void *,long))137 elf_load_file_to_addr(void *file_addr, void *addr, unsigned long *entry,
138                       int (*pre_load)(void*, long),
139                       void (*post_load)(void*, long))
140 {
141 	int type;
142 	long offset;
143 	struct ehdr *ehdr = (struct ehdr *) file_addr;
144 
145 	type = elf_check_file(file_addr);
146 
147 	switch (type) {
148 	case 1:
149 		/* Parse 32-bit image */
150 		offset = (long)addr - elf_get_base_addr32(file_addr);
151 		*entry = elf_load_segments32(file_addr, offset, pre_load,
152 		                             post_load) + offset;
153 		// TODO: elf_relocate32(...)
154 		break;
155 	case 2:
156 		/* Parse 64-bit image */
157 		offset = (long)addr - elf_get_base_addr64(file_addr);
158 		*entry = elf_load_segments64(file_addr, offset, pre_load,
159 		                             post_load) + offset;
160 		elf_relocate64(file_addr, offset);
161 		if (ehdr->ei_data != ELFDATA2MSB) {
162 			uint32_t flags = elf_get_eflags_64(file_addr);
163 			if ((flags & 0x3) == 2)
164 				type = 4; /* LE64 ABIv2 */
165 			else
166 				type = 3; /* LE64 ABIv1 */
167 		}
168 		break;
169 	}
170 
171 	return type;
172 }
173 
174 
175 /**
176  * Get the base load address of the ELF image
177  * @return  The base address or -1 for error
178  */
179 long
elf_get_base_addr(void * file_addr)180 elf_get_base_addr(void *file_addr)
181 {
182 	int type;
183 
184 	type = elf_check_file(file_addr);
185 
186 	switch (type) {
187 	case 1:
188 		/* Return 32-bit image base address */
189 		return elf_get_base_addr32(file_addr);
190 		break;
191 	case 2:
192 		/* Return 64-bit image base address */
193 		return elf_get_base_addr64(file_addr);
194 		break;
195 	}
196 
197 	return -1;
198 }
199 
200 /**
201  * Get the file size of the ELF image that has been loaded into a
202  * buffer larger than the size of the file
203  * @return  The size of the ELF image or < 0 for error
204  */
elf_get_file_size(const void * buffer,const unsigned long buffer_size)205 long elf_get_file_size(const void *buffer, const unsigned long buffer_size)
206 {
207 	const struct ehdr *ehdr = (const struct ehdr *)buffer;
208 
209 	if (buffer_size < sizeof(struct ehdr))
210 		return -1;
211 
212 	/* check if it is an ELF image at all */
213 	if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46)
214 		return -1;
215 
216 	switch (ehdr->ei_class) {
217 	case 1:
218 		return elf_get_file_size32(buffer, buffer_size);
219 	case 2:
220 		return elf_get_file_size64(buffer, buffer_size);
221 	}
222 
223 	return -1;
224 }
225