Add -Werror to help us keep the code clean.
[coreboot.git] / util / cbfstool / fs.c
1 /*
2  * cbfstool
3  *
4  * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
18  */
19
20 #include <string.h>
21 #include <stdlib.h>
22 #include "cbfstool.h"
23
24 int namelen(const char *name)
25 {
26         return ALIGN(strlen(name) + 1, 16);
27 }
28
29 /**
30  * Given a name, return the header size for that name. 
31  * @param name The name
32  * @returns The header size given that name
33  */
34 int headersize(const char *name)
35 {
36         return sizeof(struct cbfs_file) + namelen(name);
37 }
38
39 /**
40  * Given a name, set it into the header in a standard way
41  * @param file the cbfs file
42  * @param name The name
43  */
44 void setname(struct cbfs_file *file, const char *name)
45 {
46         memset(CBFS_NAME(file), 0, namelen(name));
47         strcpy((char *)CBFS_NAME(file), name);
48 }
49
50 /**
51  * Given a name, size, and type, set them into the header in a standard way. 
52  * Special case of size of -1: set the size to all of ROM
53  * @param rom The rom
54  * @param c The cbfs file
55  * @param name The name
56  * @param size The size
57  * @param type The type
58  * @returns Always 0 for now
59  */
60 int rom_set_header(struct rom *rom, struct cbfs_file *c, const char *name, int size, int type)
61 {
62         unsigned int csize;
63         csize = headersize(name);
64
65         strcpy(c->magic, COMPONENT_MAGIC);
66
67         /* special case -- if size is -1, means "as much as you can"
68          * it's usually only used in init. 
69          */
70         if (size < 0)
71                 size = rom->fssize - csize;
72         c->len = htonl(size);
73         c->offset = htonl(csize);
74         c->type = htonl(type);
75
76         setname(c, name);
77         return 0;
78 }
79
80 int nextfile(struct rom *rom, struct cbfs_file *c, int offset)
81 {
82         return ALIGN(offset + ntohl(c->len),
83                                         ntohl(rom->header->align));
84 }
85
86 /**
87  * rom_alloc
88  * Given a rom, walk the headers and find the first header of type 
89  * CBFS_COMPONENT_NULL that is >= the desired size. 
90  * If the CBFS_COMPONENT_NULL is 'align' bytes > size, 
91  * create a new header of CBFS_COMPONENT_NULL following the file. 
92  * The 'len' structure member of the desired file is initialized, but 
93  * nothing else is. 
94  * @param rom The rom
95  * @param size the size of the file needed
96  * @returns pointer to a cbfs_file struct. 
97  */
98 struct cbfs_file * rom_alloc(struct rom *rom, unsigned long size)
99 {
100         /* walk the rom and find an empty file with a base > base, and a large enough size */
101         unsigned int offset = ntohl(rom->header->offset);
102         unsigned int ret = -1;
103         struct cbfs_file *c = NULL;
104         unsigned long nextoffset, truncoffset;
105         struct cbfs_file *newfile = NULL;
106
107         while (offset < rom->fssize) {
108
109                 c = (struct cbfs_file *)ROM_PTR(rom, offset);
110
111                 if (!strcmp(c->magic, COMPONENT_MAGIC)) {
112                         if (c->type != CBFS_COMPONENT_NULL) {
113                                 offset += ALIGN(ntohl(c->offset) + ntohl(c->len),
114                                         ntohl(rom->header->align));
115                                 continue;
116                 }
117                         /* Is this file big enough for our needs? */
118                         if (ntohl(c->len) >= size){
119                                 ret = offset;
120                                 break;
121                         }
122                         offset += ALIGN(ntohl(c->offset) + ntohl(c->len),
123                                         ntohl(rom->header->align));
124                 } else {
125                         fprintf(stderr, "Corrupt rom -- found no header at %d\n", offset);
126                         exit(1);
127                 }
128         }
129
130         if (ret < 0)
131                 return NULL;
132
133         /* figure out the real end of this file, and hence the size */
134         /* compute where the next file is */
135         nextoffset = ALIGN(ret + ntohl(c->len) + headersize((char *)CBFS_NAME(c)),
136                                 ntohl(rom->header->align));
137         /* compute where the end of this new file might be */
138         truncoffset = ALIGN(ret + size + headersize((char *)CBFS_NAME(c)),
139                                 ntohl(rom->header->align));
140         /* If there is more than align bytes difference, create a new empty file */
141         /* later, we can add code to merge all empty files. */
142         if (nextoffset - truncoffset > ntohl(rom->header->align)) {
143                 unsigned int csize;
144                 csize = headersize("");
145                 newfile = (struct cbfs_file *)ROM_PTR(rom, truncoffset);
146                 rom_set_header(rom, newfile, "", 
147                         nextoffset - truncoffset - csize, CBFS_COMPONENT_NULL);
148         } else truncoffset = nextoffset;
149
150         c->len = htonl(size);
151
152         return ((struct cbfs_file *)ROM_PTR(rom, ret));
153 }
154
155 struct cbfs_file *rom_find(struct rom *rom, int offset)
156 {
157         while (offset < rom->fssize) {
158                 struct cbfs_file *c =
159                     (struct cbfs_file *)ROM_PTR(rom, offset);
160
161                 if (!strcmp(c->magic, COMPONENT_MAGIC))
162                         return c;
163
164                 offset += ntohl(rom->header->align);
165         }
166
167         return NULL;
168 }
169
170 struct cbfs_file *rom_find_first(struct rom *rom)
171 {
172         return rom_find(rom, ntohl(rom->header->offset));
173 }
174
175 struct cbfs_file *rom_find_next(struct rom *rom, struct cbfs_file *prev)
176 {
177         unsigned int offset = ROM_OFFSET(rom, prev);
178
179         return rom_find(rom, offset +
180                         ALIGN(ntohl(prev->offset) + ntohl(prev->len),
181                               ntohl(rom->header->align)));
182 }
183
184 struct cbfs_file *rom_find_by_name(struct rom *rom, const char *name)
185 {
186         struct cbfs_file *c = rom_find_first(rom);
187
188         while (c) {
189                 if (!strcmp((char *)CBFS_NAME(c), name))
190                         return c;
191
192                 c = rom_find_next(rom, c);
193         }
194
195         return NULL;
196 }
197
198 int rom_used_space(struct rom *rom)
199 {
200         struct cbfs_file *c = rom_find_first(rom);
201         unsigned int ret = 0;
202
203         while (c) {
204                 int type;
205                 type = ntohl(c->type);
206                 if ((c->type == CBFS_COMPONENT_DELETED) ||
207                         (c->type == CBFS_COMPONENT_NULL))
208                         continue;
209                 ret += ROM_OFFSET(rom, c) + ntohl(c->offset) + ntohl(c->len);
210                 c = rom_find_next(rom, c);
211         }
212
213         return ret;
214 }
215
216 /** 
217  * delete an item. This is a flash-friendly version -- it just blows the 
218  * type to 0. Nothing else is changed. 
219  * N.B. We no longer shuffle contents of ROM. That will come later. 
220  * @param rom The rom
221  * @param name Name of file to remove. 
222  * @return -1 on error, 0 if a file was set to deleted. 
223  */
224 int rom_remove(struct rom *rom, const char *name)
225 {
226         struct cbfs_file *c = rom_find_by_name(rom, name);
227
228         if (c == NULL) {
229                 ERROR("Component %s does not exist\n", name);
230                 return -1;
231         }
232
233         c->type = CBFS_COMPONENT_DELETED; 
234
235         return 0;
236 }
237
238 int rom_extract(struct rom *rom, const char *name, void** buf, int *size )
239 {
240         struct cbfs_file *c = rom_find_by_name(rom, name);
241         unsigned int csize;
242
243         if (c == NULL) {
244                 ERROR("Component %s does not exist\n", name);
245                 return -1;
246         }
247
248         *size = ntohl(c->len);
249
250         csize = headersize(name);
251         *buf = ((unsigned char *)c) + csize;
252         return 0;
253 }
254
255 /**
256  * Add a new file named 'name', of type 'type', size 'size'. Initialize that file
257  * with the contents of 'buffer'. 
258  * @param rom The rom
259  * @param name file name
260  * @param buffer file data
261  * @param size Amount of data
262  * @param type File type
263  * @returns -1 on failure, 0 on success
264  */
265 int rom_add(struct rom *rom, const char *name, void *buffer, int size, int type)
266 {
267         struct cbfs_file *c = rom_alloc(rom, size);
268         int offset;
269         int csize;
270
271         if (rom_find_by_name(rom, name)) {
272                 ERROR("Component %s already exists in this rom\n", name);
273                 return -1;
274         }
275
276         if (c == NULL) {
277                 ERROR("There is no more room in this ROM\n");
278                 return -1;
279         }
280
281         csize = headersize(name);
282
283         offset = ROM_OFFSET(rom, c);
284
285         strcpy(c->magic, COMPONENT_MAGIC);
286
287         c->offset = htonl(csize);
288         c->type = htonl(type);
289
290         setname(c, name);
291
292         memcpy(((unsigned char *)c) + csize, buffer, size);
293         return 0;
294 }
295