1 /* SPDX-License-Identifier: GPL-2.0-only */
6 #include <commonlib/endian.h>
8 #include "elfparsing.h"
15 /* serialize the seg array into the buffer.
16 * The buffer is assumed to be large enough.
18 void xdr_segs(struct buffer
*output
,
19 struct cbfs_payload_segment
*segs
, int nseg
)
21 struct buffer outheader
;
24 outheader
.data
= output
->data
;
27 for(i
= 0; i
< nseg
; i
++){
28 xdr_be
.put32(&outheader
, segs
[i
].type
);
29 xdr_be
.put32(&outheader
, segs
[i
].compression
);
30 xdr_be
.put32(&outheader
, segs
[i
].offset
);
31 xdr_be
.put64(&outheader
, segs
[i
].load_addr
);
32 xdr_be
.put32(&outheader
, segs
[i
].len
);
33 xdr_be
.put32(&outheader
, segs
[i
].mem_len
);
37 void xdr_get_seg(struct cbfs_payload_segment
*out
,
38 struct cbfs_payload_segment
*in
)
40 struct buffer inheader
;
42 inheader
.data
= (void *)in
;
43 inheader
.size
= sizeof(*in
);
45 out
->type
= xdr_be
.get32(&inheader
);
46 out
->compression
= xdr_be
.get32(&inheader
);
47 out
->offset
= xdr_be
.get32(&inheader
);
48 out
->load_addr
= xdr_be
.get64(&inheader
);
49 out
->len
= xdr_be
.get32(&inheader
);
50 out
->mem_len
= xdr_be
.get32(&inheader
);
53 int parse_elf_to_payload(const struct buffer
*input
, struct buffer
*output
,
54 enum cbfs_compression algo
)
63 int isize
= 0, osize
= 0;
65 struct cbfs_payload_segment
*segs
= NULL
;
69 comp_func_ptr compress
= compression_function(algo
);
73 if (elf_headers(input
, &ehdr
, &phdr
, &shdr
) < 0)
76 DEBUG("start: parse_elf_to_payload\n");
77 headers
= ehdr
.e_phnum
;
80 strtab
= &header
[shdr
[ehdr
.e_shstrndx
].sh_offset
];
82 /* Count the number of headers - look for the .notes.pinfo
85 for (i
= 0; i
< ehdr
.e_shnum
; i
++) {
88 if (i
== ehdr
.e_shstrndx
)
91 if (shdr
[i
].sh_size
== 0)
94 name
= (char *)(strtab
+ shdr
[i
].sh_name
);
96 if (!strcmp(name
, ".note.pinfo")) {
98 isize
+= (unsigned int)shdr
[i
].sh_size
;
102 /* Now, regular headers - we only care about PT_LOAD headers,
103 * because that's what we're actually going to load
106 for (i
= 0; i
< headers
; i
++) {
107 if (phdr
[i
].p_type
!= PT_LOAD
)
110 /* Empty segments are never interesting */
111 if (phdr
[i
].p_memsz
== 0)
114 isize
+= phdr
[i
].p_filesz
;
118 /* Allocate and initialize the segment header array */
119 segs
= calloc(segments
, sizeof(*segs
));
124 /* Allocate a block of memory to store the data in */
125 if (buffer_create(output
, (segments
* sizeof(*segs
)) + isize
,
130 memset(output
->data
, 0, output
->size
);
132 doffset
= (segments
* sizeof(*segs
));
134 /* set up for output marshaling. This is a bit
135 * tricky as we are marshaling the headers at the front,
136 * and the data starting after the headers. We need to convert
137 * the headers to the right format but the data
138 * passes through unchanged. Unlike most XDR code,
139 * we are doing these two concurrently. The doffset is
140 * used to compute the address for the raw data, and the
141 * outheader is used to marshal the headers. To make it simpler
142 * for The Reader, we set up the headers in a separate array,
143 * then marshal them all at once to the output.
147 for (i
= 0; i
< ehdr
.e_shnum
; i
++) {
149 if (i
== ehdr
.e_shstrndx
)
152 if (shdr
[i
].sh_size
== 0)
154 name
= (char *)(strtab
+ shdr
[i
].sh_name
);
155 if (!strcmp(name
, ".note.pinfo")) {
156 segs
[segments
].type
= PAYLOAD_SEGMENT_PARAMS
;
157 segs
[segments
].load_addr
= 0;
158 segs
[segments
].len
= (unsigned int)shdr
[i
].sh_size
;
159 segs
[segments
].offset
= doffset
;
161 memcpy((unsigned long *)(output
->data
+ doffset
),
162 &header
[shdr
[i
].sh_offset
], shdr
[i
].sh_size
);
164 doffset
+= segs
[segments
].len
;
165 osize
+= segs
[segments
].len
;
171 for (i
= 0; i
< headers
; i
++) {
172 if (phdr
[i
].p_type
!= PT_LOAD
)
174 if (phdr
[i
].p_memsz
== 0)
176 if (phdr
[i
].p_filesz
== 0) {
177 segs
[segments
].type
= PAYLOAD_SEGMENT_BSS
;
178 segs
[segments
].load_addr
= phdr
[i
].p_paddr
;
179 segs
[segments
].mem_len
= phdr
[i
].p_memsz
;
180 segs
[segments
].offset
= doffset
;
186 if (phdr
[i
].p_flags
& PF_X
)
187 segs
[segments
].type
= PAYLOAD_SEGMENT_CODE
;
189 segs
[segments
].type
= PAYLOAD_SEGMENT_DATA
;
190 segs
[segments
].load_addr
= phdr
[i
].p_paddr
;
191 segs
[segments
].mem_len
= phdr
[i
].p_memsz
;
192 segs
[segments
].offset
= doffset
;
194 /* If the compression failed or made the section is larger,
195 use the original stuff */
198 if (compress((char *)&header
[phdr
[i
].p_offset
],
199 phdr
[i
].p_filesz
, output
->data
+ doffset
, &len
) ||
200 (unsigned int)len
> phdr
[i
].p_filesz
) {
201 WARN("Compression failed or would make the data bigger "
203 segs
[segments
].compression
= 0;
204 segs
[segments
].len
= phdr
[i
].p_filesz
;
205 memcpy(output
->data
+ doffset
,
206 &header
[phdr
[i
].p_offset
], phdr
[i
].p_filesz
);
208 segs
[segments
].compression
= algo
;
209 segs
[segments
].len
= len
;
212 doffset
+= segs
[segments
].len
;
213 osize
+= segs
[segments
].len
;
218 segs
[segments
].type
= PAYLOAD_SEGMENT_ENTRY
;
219 segs
[segments
++].load_addr
= ehdr
.e_entry
;
221 output
->size
= (segments
* sizeof(*segs
)) + osize
;
222 xdr_segs(output
, segs
, segments
);
225 if (segs
) free(segs
);
226 if (shdr
) free(shdr
);
227 if (phdr
) free(phdr
);
231 int parse_flat_binary_to_payload(const struct buffer
*input
,
232 struct buffer
*output
,
233 uint32_t loadaddress
,
235 enum cbfs_compression algo
)
237 comp_func_ptr compress
;
238 struct cbfs_payload_segment segs
[2] = { {0} };
239 int doffset
, len
= 0;
241 compress
= compression_function(algo
);
245 DEBUG("start: parse_flat_binary_to_payload\n");
246 if (buffer_create(output
, (sizeof(segs
) + input
->size
),
249 memset(output
->data
, 0, output
->size
);
251 doffset
= (2 * sizeof(*segs
));
253 /* Prepare code segment */
254 segs
[0].type
= PAYLOAD_SEGMENT_CODE
;
255 segs
[0].load_addr
= loadaddress
;
256 segs
[0].mem_len
= input
->size
;
257 segs
[0].offset
= doffset
;
259 if (!compress(input
->data
, input
->size
, output
->data
+ doffset
, &len
) &&
260 (unsigned int)len
< input
->size
) {
261 segs
[0].compression
= algo
;
264 WARN("Compression failed or would make the data bigger "
266 segs
[0].compression
= 0;
267 segs
[0].len
= input
->size
;
268 memcpy(output
->data
+ doffset
, input
->data
, input
->size
);
271 /* prepare entry point segment */
272 segs
[1].type
= PAYLOAD_SEGMENT_ENTRY
;
273 segs
[1].load_addr
= entrypoint
;
274 output
->size
= doffset
+ segs
[0].len
;
275 xdr_segs(output
, segs
, 2);
279 int parse_fv_to_payload(const struct buffer
*input
, struct buffer
*output
,
280 enum cbfs_compression algo
)
282 comp_func_ptr compress
;
283 struct cbfs_payload_segment segs
[2] = { {0} };
284 int doffset
, len
= 0;
285 firmware_volume_header_t
*fv
;
286 firmware_volume_ext_header_t
*fvh_ext
;
287 ffs_file_header_t
*fh
;
288 common_section_header_t
*cs
;
293 uint32_t loadaddress
= 0;
294 uint32_t entrypoint
= 0;
296 compress
= compression_function(algo
);
300 DEBUG("start: parse_fv_to_payload\n");
302 fv
= (firmware_volume_header_t
*)input
->data
;
303 if (fv
->signature
!= FV_SIGNATURE
) {
304 INFO("Not a UEFI firmware volume.\n");
308 fh
= (ffs_file_header_t
*)(input
->data
+ fv
->header_length
);
309 if (fv
->ext_header_offs
!= 0) {
310 fvh_ext
= (firmware_volume_ext_header_t
*)((uintptr_t)fv
+ fv
->ext_header_offs
);
311 fh
= (ffs_file_header_t
*)((uintptr_t)fvh_ext
+ fvh_ext
->ext_header_size
);
312 fh
= (ffs_file_header_t
*)(((uintptr_t)fh
+ 7) & ~7);
315 while (fh
->file_type
== FILETYPE_PAD
) {
316 unsigned long offset
= (fh
->size
[2] << 16) | (fh
->size
[1] << 8) | fh
->size
[0];
317 DEBUG("skipping %lu bytes of FV padding\n", offset
);
318 fh
= (ffs_file_header_t
*)(((uintptr_t)fh
) + offset
);
320 if (fh
->file_type
!= FILETYPE_SEC
) {
321 ERROR("Not a usable UEFI firmware volume.\n");
322 INFO("First file in first FV not a SEC core.\n");
326 cs
= (common_section_header_t
*)&fh
[1];
327 while (cs
->section_type
== SECTION_RAW
) {
328 unsigned long offset
= (cs
->size
[2] << 16) | (cs
->size
[1] << 8) | cs
->size
[0];
329 DEBUG("skipping %lu bytes of section padding\n", offset
);
330 cs
= (common_section_header_t
*)(((uintptr_t)cs
) + offset
);
332 if (cs
->section_type
!= SECTION_PE32
) {
333 ERROR("Not a usable UEFI firmware volume.\n");
334 INFO("Section type not PE32.\n");
338 dh
= (dos_header_t
*)&cs
[1];
339 if (dh
->signature
!= DOS_MAGIC
) {
340 ERROR("Not a usable UEFI firmware volume.\n");
341 INFO("DOS header signature wrong.\n");
345 dh_offset
= (unsigned long)dh
- (unsigned long)input
->data
;
346 DEBUG("dos header offset = %x\n", dh_offset
);
348 ch
= (coff_header_t
*)(((uintptr_t)dh
)+dh
->e_lfanew
);
350 if (ch
->machine
== MACHINE_TYPE_X86
) {
351 pe_opt_header_32_t
*ph
;
352 ph
= (pe_opt_header_32_t
*)&ch
[1];
353 if (ph
->signature
!= PE_HDR_32_MAGIC
) {
354 WARN("PE header signature incorrect.\n");
357 DEBUG("image base %x\n", ph
->image_addr
);
358 DEBUG("entry point %x\n", ph
->entry_point
);
360 loadaddress
= ph
->image_addr
- dh_offset
;
361 entrypoint
= ph
->image_addr
+ ph
->entry_point
;
362 } else if (ch
->machine
== MACHINE_TYPE_X64
|| ch
->machine
== MACHINE_TYPE_ARM64
) {
363 pe_opt_header_64_t
*ph
;
364 ph
= (pe_opt_header_64_t
*)&ch
[1];
365 if (ph
->signature
!= PE_HDR_64_MAGIC
) {
366 WARN("PE header signature incorrect.\n");
369 DEBUG("image base %lx\n", (unsigned long)ph
->image_addr
);
370 DEBUG("entry point %x\n", ph
->entry_point
);
372 loadaddress
= ph
->image_addr
- dh_offset
;
373 entrypoint
= ph
->image_addr
+ ph
->entry_point
;
375 ERROR("Machine type not x86, x64, or arm64.\n");
379 if (buffer_create(output
, (sizeof(segs
) + input
->size
),
383 memset(output
->data
, 0, output
->size
);
385 doffset
= (sizeof(segs
));
387 /* Prepare code segment */
388 segs
[0].type
= PAYLOAD_SEGMENT_CODE
;
389 segs
[0].load_addr
= loadaddress
;
390 segs
[0].mem_len
= input
->size
;
391 segs
[0].offset
= doffset
;
393 if (!compress(input
->data
, input
->size
, output
->data
+ doffset
, &len
) &&
394 (unsigned int)len
< input
->size
) {
395 segs
[0].compression
= algo
;
398 WARN("Compression failed or would make the data bigger "
400 segs
[0].compression
= 0;
401 segs
[0].len
= input
->size
;
402 memcpy(output
->data
+ doffset
, input
->data
, input
->size
);
405 /* prepare entry point segment */
406 segs
[1].type
= PAYLOAD_SEGMENT_ENTRY
;
407 segs
[1].load_addr
= entrypoint
;
408 output
->size
= doffset
+ segs
[0].len
;
409 xdr_segs(output
, segs
, 2);
414 int parse_fit_to_payload(const struct buffer
*input
, struct buffer
*output
,
415 enum cbfs_compression algo
)
417 struct fdt_header
*fdt_h
;
419 DEBUG("start: parse_fit_to_payload\n");
421 fdt_h
= buffer_get(input
);
422 if (read_be32(&fdt_h
->magic
) != FDT_HEADER_MAGIC
) {
423 INFO("Not a FIT payload.\n");
429 * Compress the kernel binary you're sourcing in your its-script
430 * manually with LZ4 or LZMA and add 'compression = "lz4"' or "lzma" to
431 * the kernel@1 node in the its-script before assembling the image with
434 if (algo
!= CBFS_COMPRESS_NONE
) {
435 ERROR("FIT images don't support whole-image compression,"
436 " compress the kernel component instead!\n")
440 if (buffer_create(output
, buffer_size(input
), input
->name
) != 0)
443 memcpy(buffer_get(output
), buffer_get(input
), buffer_size(input
));