4 * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
5 * 2009 coresystems GmbH
6 * written by Patrick Georgi <patrick.georgi@coresystems.de>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
22 #include "elfparsing.h"
28 /* serialize the seg array into the buffer.
29 * The buffer is assumed to be large enough.
31 void xdr_segs(struct buffer
*output
,
32 struct cbfs_payload_segment
*segs
, int nseg
)
34 struct buffer outheader
;
37 outheader
.data
= output
->data
;
40 for(i
= 0; i
< nseg
; i
++){
41 xdr_be
.put32(&outheader
, segs
[i
].type
);
42 xdr_be
.put32(&outheader
, segs
[i
].compression
);
43 xdr_be
.put32(&outheader
, segs
[i
].offset
);
44 xdr_be
.put64(&outheader
, segs
[i
].load_addr
);
45 xdr_be
.put32(&outheader
, segs
[i
].len
);
46 xdr_be
.put32(&outheader
, segs
[i
].mem_len
);
50 void xdr_get_seg(struct cbfs_payload_segment
*out
,
51 struct cbfs_payload_segment
*in
)
53 struct buffer inheader
;
55 inheader
.data
= (void *)in
;
56 inheader
.size
= sizeof(*in
);
58 out
->type
= xdr_be
.get32(&inheader
);
59 out
->compression
= xdr_be
.get32(&inheader
);
60 out
->offset
= xdr_be
.get32(&inheader
);
61 out
->load_addr
= xdr_be
.get64(&inheader
);
62 out
->len
= xdr_be
.get32(&inheader
);
63 out
->mem_len
= xdr_be
.get32(&inheader
);
66 int parse_elf_to_payload(const struct buffer
*input
, struct buffer
*output
,
76 int isize
= 0, osize
= 0;
78 struct cbfs_payload_segment
*segs
= NULL
;
82 comp_func_ptr compress
= compression_function(algo
);
86 if (elf_headers(input
, &ehdr
, &phdr
, &shdr
) < 0)
89 DEBUG("start: parse_elf_to_payload\n");
90 headers
= ehdr
.e_phnum
;
93 strtab
= &header
[shdr
[ehdr
.e_shstrndx
].sh_offset
];
95 /* Count the number of headers - look for the .notes.pinfo
98 for (i
= 0; i
< ehdr
.e_shnum
; i
++) {
101 if (i
== ehdr
.e_shstrndx
)
104 if (shdr
[i
].sh_size
== 0)
107 name
= (char *)(strtab
+ shdr
[i
].sh_name
);
109 if (!strcmp(name
, ".note.pinfo")) {
111 isize
+= (unsigned int)shdr
[i
].sh_size
;
115 /* Now, regular headers - we only care about PT_LOAD headers,
116 * because thats what we're actually going to load
119 for (i
= 0; i
< headers
; i
++) {
120 if (phdr
[i
].p_type
!= PT_LOAD
)
123 /* Empty segments are never interesting */
124 if (phdr
[i
].p_memsz
== 0)
127 isize
+= phdr
[i
].p_filesz
;
131 /* allocate the segment header array */
132 segs
= calloc(segments
, sizeof(*segs
));
137 /* Allocate a block of memory to store the data in */
138 if (buffer_create(output
, (segments
* sizeof(*segs
)) + isize
,
143 memset(output
->data
, 0, output
->size
);
145 doffset
= (segments
* sizeof(*segs
));
147 /* set up for output marshaling. This is a bit
148 * tricky as we are marshaling the headers at the front,
149 * and the data starting after the headers. We need to convert
150 * the headers to the right format but the data
151 * passes through unchanged. Unlike most XDR code,
152 * we are doing these two concurrently. The doffset is
153 * used to compute the address for the raw data, and the
154 * outheader is used to marshal the headers. To make it simpler
155 * for The Reader, we set up the headers in a separate array,
156 * then marshal them all at once to the output.
160 for (i
= 0; i
< ehdr
.e_shnum
; i
++) {
162 if (i
== ehdr
.e_shstrndx
)
165 if (shdr
[i
].sh_size
== 0)
167 name
= (char *)(strtab
+ shdr
[i
].sh_name
);
168 if (!strcmp(name
, ".note.pinfo")) {
169 segs
[segments
].type
= PAYLOAD_SEGMENT_PARAMS
;
170 segs
[segments
].load_addr
= 0;
171 segs
[segments
].len
= (unsigned int)shdr
[i
].sh_size
;
172 segs
[segments
].offset
= doffset
;
174 memcpy((unsigned long *)(output
->data
+ doffset
),
175 &header
[shdr
[i
].sh_offset
], shdr
[i
].sh_size
);
177 doffset
+= segs
[segments
].len
;
178 osize
+= segs
[segments
].len
;
184 for (i
= 0; i
< headers
; i
++) {
185 if (phdr
[i
].p_type
!= PT_LOAD
)
187 if (phdr
[i
].p_memsz
== 0)
189 if (phdr
[i
].p_filesz
== 0) {
190 segs
[segments
].type
= PAYLOAD_SEGMENT_BSS
;
191 segs
[segments
].load_addr
= phdr
[i
].p_paddr
;
192 segs
[segments
].mem_len
= phdr
[i
].p_memsz
;
193 segs
[segments
].offset
= doffset
;
199 if (phdr
[i
].p_flags
& PF_X
)
200 segs
[segments
].type
= PAYLOAD_SEGMENT_CODE
;
202 segs
[segments
].type
= PAYLOAD_SEGMENT_DATA
;
203 segs
[segments
].load_addr
= phdr
[i
].p_paddr
;
204 segs
[segments
].mem_len
= phdr
[i
].p_memsz
;
205 segs
[segments
].offset
= doffset
;
207 /* If the compression failed or made the section is larger,
208 use the original stuff */
211 if (compress((char *)&header
[phdr
[i
].p_offset
],
212 phdr
[i
].p_filesz
, output
->data
+ doffset
, &len
) ||
213 (unsigned int)len
> phdr
[i
].p_filesz
) {
214 WARN("Compression failed or would make the data bigger "
216 segs
[segments
].compression
= 0;
217 segs
[segments
].len
= phdr
[i
].p_filesz
;
218 memcpy(output
->data
+ doffset
,
219 &header
[phdr
[i
].p_offset
], phdr
[i
].p_filesz
);
221 segs
[segments
].compression
= algo
;
222 segs
[segments
].len
= len
;
225 doffset
+= segs
[segments
].len
;
226 osize
+= segs
[segments
].len
;
231 segs
[segments
].type
= PAYLOAD_SEGMENT_ENTRY
;
232 segs
[segments
++].load_addr
= ehdr
.e_entry
;
234 output
->size
= (segments
* sizeof(*segs
)) + osize
;
235 xdr_segs(output
, segs
, segments
);
238 if (segs
) free(segs
);
239 if (shdr
) free(shdr
);
240 if (phdr
) free(phdr
);
244 int parse_flat_binary_to_payload(const struct buffer
*input
,
245 struct buffer
*output
,
246 uint32_t loadaddress
,
250 comp_func_ptr compress
;
251 struct cbfs_payload_segment segs
[2];
252 int doffset
, len
= 0;
254 compress
= compression_function(algo
);
258 DEBUG("start: parse_flat_binary_to_payload\n");
259 if (buffer_create(output
, (sizeof(segs
) + input
->size
),
262 memset(output
->data
, 0, output
->size
);
264 doffset
= (2 * sizeof(*segs
));
266 /* Prepare code segment */
267 segs
[0].type
= PAYLOAD_SEGMENT_CODE
;
268 segs
[0].load_addr
= loadaddress
;
269 segs
[0].mem_len
= input
->size
;
270 segs
[0].offset
= doffset
;
272 if (!compress(input
->data
, input
->size
, output
->data
+ doffset
, &len
) &&
273 (unsigned int)len
< input
->size
) {
274 segs
[0].compression
= algo
;
277 WARN("Compression failed or would make the data bigger "
279 segs
[0].compression
= 0;
280 segs
[0].len
= input
->size
;
281 memcpy(output
->data
+ doffset
, input
->data
, input
->size
);
284 /* prepare entry point segment */
285 segs
[1].type
= PAYLOAD_SEGMENT_ENTRY
;
286 segs
[1].load_addr
= entrypoint
;
287 output
->size
= doffset
+ segs
[0].len
;
288 xdr_segs(output
, segs
, 2);
292 int parse_fv_to_payload(const struct buffer
*input
, struct buffer
*output
,
295 comp_func_ptr compress
;
296 struct cbfs_payload_segment segs
[2];
297 int doffset
, len
= 0;
298 firmware_volume_header_t
*fv
;
299 ffs_file_header_t
*fh
;
300 common_section_header_t
*cs
;
305 uint32_t loadaddress
= 0;
306 uint32_t entrypoint
= 0;
308 compress
= compression_function(algo
);
312 DEBUG("start: parse_fv_to_payload\n");
314 fv
= (firmware_volume_header_t
*)input
->data
;
315 if (fv
->signature
!= FV_SIGNATURE
) {
316 INFO("Not a UEFI firmware volume.\n");
320 fh
= (ffs_file_header_t
*)(input
->data
+ fv
->header_length
);
321 while (fh
->file_type
== FILETYPE_PAD
) {
322 unsigned long offset
= (fh
->size
[2] << 16) | (fh
->size
[1] << 8) | fh
->size
[0];
323 ERROR("skipping %lu bytes of FV padding\n", offset
);
324 fh
= (ffs_file_header_t
*)(((uintptr_t)fh
) + offset
);
326 if (fh
->file_type
!= FILETYPE_SEC
) {
327 ERROR("Not a usable UEFI firmware volume.\n");
328 INFO("First file in first FV not a SEC core.\n");
332 cs
= (common_section_header_t
*)&fh
[1];
333 while (cs
->section_type
== SECTION_RAW
) {
334 unsigned long offset
= (cs
->size
[2] << 16) | (cs
->size
[1] << 8) | cs
->size
[0];
335 ERROR("skipping %lu bytes of section padding\n", offset
);
336 cs
= (common_section_header_t
*)(((uintptr_t)cs
) + offset
);
338 if (cs
->section_type
!= SECTION_PE32
) {
339 ERROR("Not a usable UEFI firmware volume.\n");
340 INFO("Section type not PE32.\n");
344 dh
= (dos_header_t
*)&cs
[1];
345 if (dh
->signature
!= DOS_MAGIC
) {
346 ERROR("Not a usable UEFI firmware volume.\n");
347 INFO("DOS header signature wrong.\n");
351 dh_offset
= (unsigned long)dh
- (unsigned long)input
->data
;
352 DEBUG("dos header offset = %x\n", dh_offset
);
354 ch
= (coff_header_t
*)(((uintptr_t)dh
)+dh
->e_lfanew
);
356 if (ch
->machine
== MACHINE_TYPE_X86
) {
357 pe_opt_header_32_t
*ph
;
358 ph
= (pe_opt_header_32_t
*)&ch
[1];
359 if (ph
->signature
!= PE_HDR_32_MAGIC
) {
360 WARN("PE header signature incorrect.\n");
363 DEBUG("image base %x\n", ph
->image_addr
);
364 DEBUG("entry point %x\n", ph
->entry_point
);
366 loadaddress
= ph
->image_addr
- dh_offset
;
367 entrypoint
= ph
->image_addr
+ ph
->entry_point
;
368 } else if (ch
->machine
== MACHINE_TYPE_X64
) {
369 pe_opt_header_64_t
*ph
;
370 ph
= (pe_opt_header_64_t
*)&ch
[1];
371 if (ph
->signature
!= PE_HDR_64_MAGIC
) {
372 WARN("PE header signature incorrect.\n");
375 DEBUG("image base %lx\n", (unsigned long)ph
->image_addr
);
376 DEBUG("entry point %x\n", ph
->entry_point
);
378 loadaddress
= ph
->image_addr
- dh_offset
;
379 entrypoint
= ph
->image_addr
+ ph
->entry_point
;
381 ERROR("Machine type not x86 or x64.\n");
385 if (buffer_create(output
, (sizeof(segs
) + input
->size
),
389 memset(output
->data
, 0, output
->size
);
391 doffset
= (sizeof(segs
));
393 /* Prepare code segment */
394 segs
[0].type
= PAYLOAD_SEGMENT_CODE
;
395 segs
[0].load_addr
= loadaddress
;
396 segs
[0].mem_len
= input
->size
;
397 segs
[0].offset
= doffset
;
399 if (!compress(input
->data
, input
->size
, output
->data
+ doffset
, &len
) &&
400 (unsigned int)len
< input
->size
) {
401 segs
[0].compression
= algo
;
404 WARN("Compression failed or would make the data bigger "
406 segs
[0].compression
= 0;
407 segs
[0].len
= input
->size
;
408 memcpy(output
->data
+ doffset
, input
->data
, input
->size
);
411 /* prepare entry point segment */
412 segs
[1].type
= PAYLOAD_SEGMENT_ENTRY
;
413 segs
[1].load_addr
= entrypoint
;
414 output
->size
= doffset
+ segs
[0].len
;
415 xdr_segs(output
, segs
, 2);