libpayload: Add larfptr function
[coreboot.git] / payloads / libpayload / libc / lar.c
1 /*
2  * This file is part of the libpayload project.
3  *
4  * Copyright (C) 2008 Advanced Micro Devices, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <libpayload.h>
31 #include <arch/endian.h>
32
33 #define ROM_RESET_VECTOR 0xFFFFFFF0
34
35 static void * next_header(void * cur)
36 {
37         struct lar_header *header = (struct lar_header *) cur;
38         int offset = ((ntohl(header->offset) + ntohl(header->len)) + 15) &
39                 0xFFFFFFF0;
40
41         return (void *) (cur + offset);
42 }
43
44 static struct lar_header *lar_get_header(struct LAR *lar, int index)
45 {
46         int i;
47
48         if (index < lar->count)
49                 return (struct lar_header *) lar->headers[index];
50
51         if (lar->eof && index >= lar->eof)
52                 return NULL;
53
54         for(i = lar->count; i <= index; i++) {
55                 void *next = (i == 0) ?
56                         lar->start : next_header(lar->headers[i - 1]);
57
58                 if (strncmp((const char *) next, LAR_MAGIC, 8)) {
59                         lar->eof = lar->count;
60                         return NULL;
61                 }
62
63                 if (lar->count == lar->alloc) {
64                         void *tmp = realloc(lar->headers,
65                                             (lar->alloc + 16) * sizeof(void *));
66
67                         if (tmp == NULL)
68                                 return NULL;
69
70                         lar->headers = tmp;
71                         lar->alloc += 16;
72                 }
73
74                 lar->headers[lar->count++] = next;
75         }
76
77         return (struct lar_header *) lar->headers[index];
78 }
79
80
81 /**
82  * Open a LAR stream
83  *
84  * @param addr The address in memory where the LAR is located.
85  * Use NULL to specify the boot LAR
86  * @return a pointer to the LAR stream
87  */
88
89 struct LAR *openlar(void *addr)
90 {
91         struct LAR *lar;
92
93         /* If the address is null, then figure out the start of the
94            boot LAR */
95
96         if (addr == NULL) {
97                 u32 size = *((u32 *) (ROM_RESET_VECTOR + 4));
98                 addr = (void *) ((ROM_RESET_VECTOR  + 16) - size);
99         }
100
101         /* Check the magic to make sure this is a LAR */
102         if (strncmp((const char *) addr, LAR_MAGIC, strlen(LAR_MAGIC)))
103                 return NULL;
104
105         lar = calloc(sizeof(struct LAR), 1);
106
107         if (!lar)
108                 return NULL;
109
110         lar->start = addr;
111
112         /* Preallocate 16 slots in the cache - this saves wear and
113          * tear on the heap */
114
115         lar->headers = malloc(16 * sizeof(void *));
116         lar->alloc = 16;
117         lar->count = lar->eof = 0;
118         lar->cindex = 0;
119
120         return lar;
121 }
122
123 /**
124  * Close a LAR stream
125  *
126  * @param lar A pointer to the LAR stream
127  * @return Return 0 on success, -1 on error
128  */
129
130 int closelar(struct LAR *lar)
131 {
132         if (!lar)
133                 return 0;
134
135         if (lar->headers)
136                 free(lar->headers);
137
138         free(lar);
139
140         return 0;
141 }
142
143 /**
144  * Read an entry from the LAR
145  *
146  * @param lar A pointer to the LAR stream
147  * @return A pointer to a larent structure
148            representing the next file in the LAR
149  */
150
151 struct larent *readlar(struct LAR *lar)
152 {
153         static struct larent _larent;
154         struct lar_header *header;
155         int nlen;
156
157         if (!lar)
158                 return NULL;
159
160         header = lar_get_header(lar, lar->cindex);
161
162         if (header == NULL)
163                 return NULL;
164
165         nlen = ntohl(header->offset) - sizeof(*header);
166
167         if (nlen > LAR_MAX_PATHLEN - 1)
168                 nlen = LAR_MAX_PATHLEN - 1;
169
170         memcpy((void *) _larent.name, ((char *) header + sizeof(*header)),
171                 nlen);
172
173         _larent.name[nlen] = 0;
174
175         lar->cindex++;
176
177         return (struct larent *) &_larent;
178 }
179
180 void rewindlar(struct LAR *lar)
181 {
182         if (lar != NULL)
183                 lar->cindex = 0;
184 }
185
186 static struct lar_header *get_header_by_name(struct LAR *lar, const char *name)
187 {
188         struct lar_header *header;
189         int i;
190
191         for(i = 0; ; i++) {
192                 header = lar_get_header(lar, i);
193
194                 if (header == NULL)
195                         return NULL;
196
197                 if (!strcmp(name, ((char *) header + sizeof(*header))))
198                         return header;
199         }
200 }
201
202 int larstat(struct LAR *lar, const char *path, struct larstat *buf)
203 {
204         struct lar_header *header = get_header_by_name(lar, path);
205
206         if (header == NULL || buf == NULL)
207                 return -1;
208
209         buf->len = ntohl(header->len);
210         buf->reallen = ntohl(header->reallen);
211         buf->checksum = ntohl(header->checksum);
212         buf->compchecksum = ntohl(header->compchecksum);
213         buf->compression = ntohl(header->compression);
214         buf->entry = ntohll(header->entry);
215         buf->loadaddress = ntohll(header->loadaddress);
216         buf->offset = ((u32) header - (u32) lar->start) + ntohl(header->offset);
217
218         return 0;
219 }
220
221 void * larfptr(struct LAR *lar, const char *filename)
222 {
223         struct lar_header *header = get_header_by_name(lar, filename);
224
225         if (header == NULL)
226                 return NULL;
227
228         return (void *) ((u8 *) header + ntohl(header->offset));
229 }
230
231 struct LFILE * lfopen(struct LAR *lar, const char *filename)
232 {
233         struct LFILE *file;
234         struct lar_header *header = get_header_by_name(lar, filename);
235
236         if (header == NULL)
237                 return NULL;
238
239         /* FIXME: What other validations do we want to do on the file here? */
240
241         file = malloc(sizeof(struct LFILE));
242
243         if (file == NULL)
244                 return NULL;
245
246         file->lar = lar;
247         file->header = header;
248         file->size = ntohl(header->len);
249         file->start = ((u8 *) header + ntohl(header->offset));
250         file->offset = 0;
251
252         return file;
253 }
254
255 void *lfmap(struct LFILE *file, int offset)
256 {
257         if (file == NULL)
258                 return (void *) -1;
259
260         if (offset > file->size)
261                 return (void *) -1;
262
263         return (void *) (file->start + offset);
264 };
265
266 int lfread(void *ptr, size_t size, size_t nmemb, struct LFILE *stream)
267 {
268         size_t tsize, actual;
269         size_t remain = stream->size - stream->offset;
270
271         if (!stream || !remain)
272                 return 0;
273
274         tsize = (size * nmemb);
275         actual = (tsize > remain) ? remain : tsize;
276
277         memcpy(ptr, (void *) (stream->start + stream->offset), actual);
278         stream->offset += actual;
279
280         return actual;
281 }
282
283 int lfseek(struct LFILE *file, long offset, int whence)
284 {
285         int o = file->offset;
286
287         switch(whence) {
288         case SEEK_SET:
289                 o = offset;
290                 break;
291         case SEEK_CUR:
292                 o += offset;
293                 break;
294
295         case SEEK_END:
296                 return -1;
297         }
298
299         if (o < 0 || o > file->size)
300                 return -1;
301
302         file->offset = o;
303         return file->offset;
304 }
305
306 int lfclose(struct LFILE *file)
307 {
308         if (file)
309                 free(file);
310         return 0;
311 }