blob: 3e62525d48fc23605126eab3d882a2cb437fe283 [file] [log] [blame]
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +00001/*
Peter Stuge45ae92ff2009-04-14 19:48:32 +00002 * cbfs-mkstage
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +00003 *
4 * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
Patrick Georgib7b56dd82009-09-14 13:29:27 +00005 * 2009 coresystems GmbH
6 * written by Patrick Georgi <patrick.georgi@coresystems.de>
David Hendricks90ca3b62012-11-16 14:48:22 -08007 * Copyright (C) 2012 Google, Inc.
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +00008 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
Patrick Georgib890a122015-03-26 15:17:45 +010020 * Foundation, Inc.
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +000021 */
22
Francis Rowe3fb8b0d2014-11-21 02:38:48 +000023#include <inttypes.h>
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +000024#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +000027
Aaron Durbin54ef3062014-03-05 12:12:09 -060028#include "elfparsing.h"
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +000029#include "common.h"
Patrick Georgib7b56dd82009-09-14 13:29:27 +000030#include "cbfs.h"
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +000031
Furquan Shaikh405304a2014-10-30 11:44:20 -070032/* Checks if program segment contains the ignored section */
33static int is_phdr_ignored(Elf64_Phdr *phdr, Elf64_Shdr *shdr)
34{
35 /* If no ignored section, return false. */
36 if (shdr == NULL)
37 return 0;
38
39 Elf64_Addr sh_start = shdr->sh_addr;
40 Elf64_Addr sh_end = shdr->sh_addr + shdr->sh_size;
41 Elf64_Addr ph_start = phdr->p_vaddr;
42 Elf64_Addr ph_end = phdr->p_vaddr + phdr->p_memsz;
43
44 /* Return true only if section occupies whole of segment. */
45 if ((sh_start == ph_start) && (sh_end == ph_end)) {
Francis Rowe3fb8b0d2014-11-21 02:38:48 +000046 DEBUG("Ignoring program segment at 0x%" PRIx64 "\n", ph_start);
Furquan Shaikh405304a2014-10-30 11:44:20 -070047 return 1;
48 }
49
50 /* If shdr intersects phdr at all, its a conflict */
51 if (((sh_start >= ph_start) && (sh_start <= ph_end)) ||
52 ((sh_end >= ph_start) && (sh_end <= ph_end))) {
53 ERROR("Conflicting sections in segment\n");
54 exit(1);
55 }
56
57 /* Program header doesn't need to be ignored. */
58 return 0;
59}
60
61/* Find section header based on ignored section name */
62static Elf64_Shdr *find_ignored_section_header(struct parsed_elf *pelf,
63 const char *ignore_section)
64{
65 int i;
66 const char *shstrtab;
67
68 /* No section needs to be ignored */
69 if (ignore_section == NULL)
70 return NULL;
71
72 DEBUG("Section to be ignored: %s\n", ignore_section);
73
74 /* Get pointer to string table */
75 shstrtab = buffer_get(pelf->strtabs[pelf->ehdr.e_shstrndx]);
76
77 for (i = 0; i < pelf->ehdr.e_shnum; i++) {
78 Elf64_Shdr *shdr;
79 const char *section_name;
80
81 shdr = &pelf->shdr[i];
82 section_name = &shstrtab[shdr->sh_name];
83
84 /* If section name matches ignored string, return shdr */
85 if (strcmp(section_name, ignore_section) == 0)
86 return shdr;
87 }
88
89 /* No section matches ignore string */
90 return NULL;
91}
92
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -080093/* returns size of result, or -1 if error.
94 * Note that, with the new code, this function
95 * works for all elf files, not just the restricted set.
96 */
97int parse_elf_to_stage(const struct buffer *input, struct buffer *output,
Sol Boucher6310ccc2015-05-07 21:12:28 -070098 enum comp_algo algo, uint32_t *location,
Furquan Shaikh405304a2014-10-30 11:44:20 -070099 const char *ignore_section)
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800100{
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700101 struct parsed_elf pelf;
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800102 Elf64_Phdr *phdr;
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700103 Elf64_Ehdr *ehdr;
Furquan Shaikh405304a2014-10-30 11:44:20 -0700104 Elf64_Shdr *shdr_ignored;
Furquan Shaikhf7a5b562015-05-29 12:46:18 -0700105 Elf64_Addr virt_to_phys;
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800106 char *buffer;
107 struct buffer outheader;
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700108 int ret = -1;
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800109
110 int headers;
111 int i, outlen;
112 uint32_t data_start, data_end, mem_end;
113
114 comp_func_ptr compress = compression_function(algo);
115 if (!compress)
116 return -1;
117
118 DEBUG("start: parse_elf_to_stage(location=0x%x)\n", *location);
119
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700120 int flags = ELF_PARSE_PHDR | ELF_PARSE_SHDR | ELF_PARSE_STRTAB;
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800121
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700122 if (parse_elf(input, &pelf, flags)) {
123 ERROR("Couldn't parse ELF\n");
124 return -1;
125 }
126
127 ehdr = &pelf.ehdr;
128 phdr = &pelf.phdr[0];
129
Furquan Shaikh405304a2014-10-30 11:44:20 -0700130 /* Find the section header corresponding to ignored-section */
131 shdr_ignored = find_ignored_section_header(&pelf, ignore_section);
132
133 if (ignore_section && (shdr_ignored == NULL))
134 WARN("Ignore section not found\n");
135
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700136 headers = ehdr->e_phnum;
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800137
Furquan Shaikh405304a2014-10-30 11:44:20 -0700138 /* Ignore the program header containing ignored section */
139 for (i = 0; i < headers; i++) {
140 if (is_phdr_ignored(&phdr[i], shdr_ignored))
141 phdr[i].p_type = PT_NULL;
142 }
143
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800144 data_start = ~0;
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000145 data_end = 0;
146 mem_end = 0;
Furquan Shaikhf7a5b562015-05-29 12:46:18 -0700147 virt_to_phys = 0;
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000148
149 for (i = 0; i < headers; i++) {
150 unsigned int start, mend, rend;
151
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800152 if (phdr[i].p_type != PT_LOAD)
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000153 continue;
154
155 /* Empty segments are never interesting */
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800156 if (phdr[i].p_memsz == 0)
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000157 continue;
158
159 /* BSS */
160
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800161 start = phdr[i].p_paddr;
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000162
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800163 mend = start + phdr[i].p_memsz;
164 rend = start + phdr[i].p_filesz;
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000165
166 if (start < data_start)
167 data_start = start;
168
169 if (rend > data_end)
170 data_end = rend;
171
172 if (mend > mem_end)
173 mem_end = mend;
Furquan Shaikhf7a5b562015-05-29 12:46:18 -0700174
175 if (virt_to_phys == 0)
176 virt_to_phys = phdr[i].p_paddr - phdr[i].p_vaddr;
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000177 }
178
Patrick Georgi9341acd2009-12-23 12:52:56 +0000179 if (data_start < *location) {
180 data_start = *location;
181 }
182
Patrick Georgia6c337d2010-02-03 17:56:37 +0000183 if (data_end <= data_start) {
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800184 ERROR("data ends (%08lx) before it starts (%08lx). Make sure "
185 "the ELF file is correct and resides in ROM space.\n",
186 (unsigned long)data_end, (unsigned long)data_start);
Patrick Georgia6c337d2010-02-03 17:56:37 +0000187 exit(1);
188 }
189
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000190 /* allocate an intermediate buffer for the data */
191 buffer = calloc(data_end - data_start, 1);
192
193 if (buffer == NULL) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800194 ERROR("Unable to allocate memory: %m\n");
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700195 goto err;
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000196 }
197
198 /* Copy the file data into the buffer */
199
200 for (i = 0; i < headers; i++) {
Patrick Georgi9341acd2009-12-23 12:52:56 +0000201 unsigned int l_start, l_offset = 0;
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000202
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800203 if (phdr[i].p_type != PT_LOAD)
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000204 continue;
205
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800206 if (phdr[i].p_memsz == 0)
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000207 continue;
208
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800209 l_start = phdr[i].p_paddr;
Patrick Georgi9341acd2009-12-23 12:52:56 +0000210 if (l_start < *location) {
211 l_offset = *location - l_start;
212 l_start = *location;
213 }
214
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800215 /* A legal ELF file can have a program header with
216 * non-zero length but zero-length file size and a
217 * non-zero offset which, added together, are > than
218 * input->size (i.e. the total file size). So we need
219 * to not even test in the case that p_filesz is zero.
220 */
221 if (! phdr[i].p_filesz)
222 continue;
223 if (input->size < (phdr[i].p_offset + phdr[i].p_filesz)){
224 ERROR("Underflow copying out the segment."
Paul Menzel470c37c2014-03-16 00:15:57 +0100225 "File has %zu bytes left, segment end is %zu\n",
226 input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz));
Daniele Forsi8e898472014-07-27 12:01:40 +0200227 free(buffer);
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700228 goto err;
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800229 }
Patrick Georgi9341acd2009-12-23 12:52:56 +0000230 memcpy(buffer + (l_start - data_start),
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800231 &input->data[phdr[i].p_offset + l_offset],
232 phdr[i].p_filesz - l_offset);
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000233 }
234
235 /* Now make the output buffer */
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800236 if (buffer_create(output, sizeof(struct cbfs_stage) + data_end - data_start,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +0800237 input->name) != 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800238 ERROR("Unable to allocate memory: %m\n");
Paul Menzel2c8f81b2013-04-11 10:45:11 +0200239 free(buffer);
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700240 goto err;
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000241 }
Hung-Te Linc13e4bf2013-01-29 15:22:11 +0800242 memset(output->data, 0, output->size);
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000243
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800244 /* Compress the data, at which point we'll know information
245 * to fill out the header. This seems backward but it works because
246 * - the output header is a known size (not always true in many xdr's)
247 * - we do need to know the compressed output size first
Gabe Black845aa142014-02-21 01:01:06 -0800248 * If compression fails or makes the data bigger, we'll warn about it
249 * and use the original data.
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800250 */
Gabe Blackdbd006b2014-02-20 23:38:49 -0800251 if (compress(buffer, data_end - data_start,
252 (output->data + sizeof(struct cbfs_stage)),
Sol Boucher0e539312015-03-05 15:38:03 -0800253 &outlen) < 0 || (unsigned)outlen > data_end - data_start) {
Gabe Black845aa142014-02-21 01:01:06 -0800254 WARN("Compression failed or would make the data bigger "
255 "- disabled.\n");
256 memcpy(output->data + sizeof(struct cbfs_stage),
257 buffer, data_end - data_start);
258 algo = CBFS_COMPRESS_NONE;
Gabe Blackdbd006b2014-02-20 23:38:49 -0800259 }
Stefan Reinauer63217582012-10-29 16:52:36 -0700260 free(buffer);
261
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800262 /* Set up for output marshaling. */
263 outheader.data = output->data;
264 outheader.size = 0;
265 /* N.B. The original plan was that SELF data was B.E.
266 * but: this is all L.E.
267 * Maybe we should just change the spec.
268 */
269 xdr_le.put32(&outheader, algo);
Furquan Shaikhf7a5b562015-05-29 12:46:18 -0700270 /* Coreboot expects entry point to be physical address. Thus, adjust the
271 * entry point accordingly.
272 */
273 xdr_le.put64(&outheader, ehdr->e_entry + virt_to_phys);
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800274 xdr_le.put64(&outheader, data_start);
275 xdr_le.put32(&outheader, outlen);
276 xdr_le.put32(&outheader, mem_end - data_start);
277
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000278 if (*location)
279 *location -= sizeof(struct cbfs_stage);
Ronald G. Minnichaa2f7392013-12-03 11:13:35 -0800280 output->size = sizeof(struct cbfs_stage) + outlen;
Furquan Shaikhcc6f84c2014-10-30 11:28:27 -0700281 ret = 0;
282
283err:
284 parsed_elf_destroy(&pelf);
285 return ret;
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +0000286}