2f81da07d5e8137a0bacc65a44f95296ba29d420
[coreboot.git] / util / cbfstool / cbfs-mkstage.c
1 /*
2  * cbfs-mkstage
3  *
4  * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
5  *               2009 coresystems GmbH
6  *                 written by Patrick Georgi <patrick.georgi@coresystems.de>
7  *
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.
11  *
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.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include "elf.h"
27 #include <fcntl.h>
28 #include <getopt.h>
29 #include <sys/stat.h>
30
31 #include "common.h"
32 #include "cbfs.h"
33
34 unsigned int idemp(unsigned int x)
35 {
36         return x;
37 }
38
39 unsigned int swap32(unsigned int x)
40 {
41         return ((x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) |
42                 (x << 24));
43 }
44
45 unsigned int (*elf32_to_native) (unsigned int) = idemp;
46
47 /* returns size of result, or -1 if error */
48 int parse_elf_to_stage(unsigned char *input, unsigned char **output,
49                        comp_algo algo, uint32_t * location)
50 {
51         Elf32_Phdr *phdr;
52         Elf32_Ehdr *ehdr = (Elf32_Ehdr *) input;
53         char *header, *buffer;
54         unsigned char *out;
55
56         int headers;
57         int i;
58         struct cbfs_stage *stage;
59         unsigned int data_start, data_end, mem_end;
60
61         int elf_bigendian = 0;
62         int host_bigendian = 0;
63
64         comp_func_ptr compress = compression_function(algo);
65         if (!compress)
66                 return -1;
67
68         if (!iself(input)) {
69                 fprintf(stderr, "E:  The incoming file is not an ELF\n");
70                 return -1;
71         }
72
73         if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {
74                 elf_bigendian = 1;
75         }
76         char test[4] = "1234";
77         uint32_t inttest = *(uint32_t *) test;
78         if (inttest == 0x31323334) {
79                 host_bigendian = 1;
80         }
81         if (elf_bigendian != host_bigendian) {
82                 elf32_to_native = swap32;
83         }
84
85         headers = ehdr->e_phnum;
86         header = (char *)ehdr;
87
88         phdr = (Elf32_Phdr *) & header[elf32_to_native(ehdr->e_phoff)];
89
90         /* Now, regular headers - we only care about PT_LOAD headers,
91          * because thats what we're actually going to load
92          */
93
94         data_start = 0xFFFFFFFF;
95         data_end = 0;
96         mem_end = 0;
97
98         for (i = 0; i < headers; i++) {
99                 unsigned int start, mend, rend;
100
101                 if (elf32_to_native(phdr[i].p_type) != PT_LOAD)
102                         continue;
103
104                 /* Empty segments are never interesting */
105                 if (elf32_to_native(phdr[i].p_memsz) == 0)
106                         continue;
107
108                 /* BSS */
109
110                 start = elf32_to_native(phdr[i].p_paddr);
111
112                 mend = start + elf32_to_native(phdr[i].p_memsz);
113                 rend = start + elf32_to_native(phdr[i].p_filesz);
114
115                 if (start < data_start)
116                         data_start = start;
117
118                 if (rend > data_end)
119                         data_end = rend;
120
121                 if (mend > mem_end)
122                         mem_end = mend;
123         }
124
125         if (data_start < *location) {
126                 data_start = *location;
127         }
128
129         if (data_end <= data_start) {
130                 fprintf(stderr, "E: data ends before it starts. Make sure the ELF file is correct and resides in ROM space.\n");
131                 exit(1);
132         }
133
134         /* allocate an intermediate buffer for the data */
135         buffer = calloc(data_end - data_start, 1);
136
137         if (buffer == NULL) {
138                 fprintf(stderr, "E: Unable to allocate memory: %m\n");
139                 return -1;
140         }
141
142         /* Copy the file data into the buffer */
143
144         for (i = 0; i < headers; i++) {
145                 unsigned int l_start, l_offset = 0;
146
147                 if (elf32_to_native(phdr[i].p_type) != PT_LOAD)
148                         continue;
149
150                 if (elf32_to_native(phdr[i].p_memsz) == 0)
151                         continue;
152
153                 l_start = elf32_to_native(phdr[i].p_paddr);
154                 if (l_start < *location) {
155                         l_offset = *location - l_start;
156                         l_start = *location;
157                 }
158
159                 memcpy(buffer + (l_start - data_start),
160                        &header[elf32_to_native(phdr[i].p_offset)+l_offset],
161                        elf32_to_native(phdr[i].p_filesz)-l_offset);
162         }
163
164         /* Now make the output buffer */
165         out = calloc(sizeof(struct cbfs_stage) + data_end - data_start, 1);
166
167         if (out == NULL) {
168                 fprintf(stderr, "E: Unable to allocate memory: %m\n");
169                 return -1;
170         }
171
172         stage = (struct cbfs_stage *)out;
173
174         stage->load = data_start;
175         stage->memlen = mem_end - data_start;
176         stage->compression = algo;
177         stage->entry = ehdr->e_entry;
178
179         compress(buffer, data_end - data_start,
180                  (char *)(out + sizeof(struct cbfs_stage)), (int *)&stage->len);
181
182         *output = out;
183
184         if (*location)
185                 *location -= sizeof(struct cbfs_stage);
186         return sizeof(struct cbfs_stage) + stage->len;
187 }