Add constants for fast path resume copying
[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
117         if (!lar->headers)
118                 return NULL;
119
120         lar->alloc = 16;
121         lar->count = lar->eof = 0;
122         lar->cindex = 0;
123
124         return lar;
125 }
126
127 /**
128  * Close a LAR stream
129  *
130  * @param lar A pointer to the LAR stream
131  * @return Return 0 on success, -1 on error
132  */
133
134 int closelar(struct LAR *lar)
135 {
136         if (!lar)
137                 return 0;
138
139         if (lar->headers)
140                 free(lar->headers);
141
142         free(lar);
143
144         return 0;
145 }
146
147 /**
148  * Read an entry from the LAR
149  *
150  * @param lar A pointer to the LAR stream
151  * @return A pointer to a larent structure
152            representing the next file in the LAR
153  */
154
155 struct larent *readlar(struct LAR *lar)
156 {
157         static struct larent _larent;
158         struct lar_header *header;
159         int nlen;
160
161         if (!lar)
162                 return NULL;
163
164         header = lar_get_header(lar, lar->cindex);
165
166         if (header == NULL)
167                 return NULL;
168
169         nlen = ntohl(header->offset) - sizeof(*header);
170
171         if (nlen > LAR_MAX_PATHLEN - 1)
172                 nlen = LAR_MAX_PATHLEN - 1;
173
174         memcpy((void *) _larent.name, ((char *) header + sizeof(*header)),
175                 nlen);
176
177         _larent.name[nlen] = 0;
178
179         lar->cindex++;
180
181         return (struct larent *) &_larent;
182 }
183
184 void rewindlar(struct LAR *lar)
185 {
186         if (lar != NULL)
187                 lar->cindex = 0;
188 }
189
190 static struct lar_header *get_header_by_name(struct LAR *lar, const char *name)
191 {
192         struct lar_header *header;
193         int i;
194
195         for(i = 0; ; i++) {
196                 header = lar_get_header(lar, i);
197
198                 if (header == NULL)
199                         return NULL;
200
201                 if (!strcmp(name, ((char *) header + sizeof(*header))))
202                         return header;
203         }
204 }
205
206 int larstat(struct LAR *lar, const char *path, struct larstat *buf)
207 {
208         struct lar_header *header = get_header_by_name(lar, path);
209
210         if (header == NULL || buf == NULL)
211                 return -1;
212
213         buf->len = ntohl(header->len);
214         buf->reallen = ntohl(header->reallen);
215         buf->checksum = ntohl(header->checksum);
216         buf->compchecksum = ntohl(header->compchecksum);
217         buf->compression = ntohl(header->compression);
218         buf->entry = ntohll(header->entry);
219         buf->loadaddress = ntohll(header->loadaddress);
220         buf->offset = ((u32) header - (u32) lar->start) + ntohl(header->offset);
221
222         return 0;
223 }
224
225 void * larfptr(struct LAR *lar, const char *filename)
226 {
227         struct lar_header *header = get_header_by_name(lar, filename);
228
229         if (header == NULL)
230                 return NULL;
231
232         return (void *) ((u8 *) header + ntohl(header->offset));
233 }
234
235 /**
236  * Verify the checksum on a particular LAR entry
237  *
238  * @param lar A pointer to the LAR stream
239  * @param filename The lar entry to verify
240  * @return Return 1 if the entry is valid, 0 if it is not, or -1
241  * on error
242  */
243
244 int lfverify(struct LAR *lar, const char *filename)
245 {
246         struct lar_header *header = get_header_by_name(lar, filename);
247
248         u8 *ptr = (u8 *) header;
249         int len = ntohl(header->len) + ntohl(header->offset);
250         int offset;
251         u32 csum = 0;
252
253         if (header == NULL)
254                 return -1;
255
256         /* The checksum needs to be calulated on entire data section,
257          * including any padding for the 16 byte alignment (which should
258          * be zeros
259          */
260
261         len = (len + 15) & 0xFFFFFFF0;
262
263         for(offset = 0; offset < len; offset += 4) {
264                 csum += *((u32 *) (ptr + offset));
265         }
266
267         return (csum == 0xFFFFFFFF) ? 1 : 0;
268 }
269
270 struct LFILE * lfopen(struct LAR *lar, const char *filename)
271 {
272         struct LFILE *file;
273         struct lar_header *header = get_header_by_name(lar, filename);
274
275         if (header == NULL)
276                 return NULL;
277
278         /* FIXME: What other validations do we want to do on the file here? */
279
280         file = malloc(sizeof(struct LFILE));
281
282         if (file == NULL)
283                 return NULL;
284
285         file->lar = lar;
286         file->header = header;
287         file->size = ntohl(header->len);
288         file->start = ((u8 *) header + ntohl(header->offset));
289         file->offset = 0;
290
291         return file;
292 }
293
294 void *lfmap(struct LFILE *file, int offset)
295 {
296         if (file == NULL)
297                 return (void *) -1;
298
299         if (offset > file->size)
300                 return (void *) -1;
301
302         return (void *) (file->start + offset);
303 };
304
305 int lfread(void *ptr, size_t size, size_t nmemb, struct LFILE *stream)
306 {
307         size_t tsize, actual;
308         size_t remain = stream->size - stream->offset;
309
310         if (!stream || !remain)
311                 return 0;
312
313         tsize = (size * nmemb);
314         actual = (tsize > remain) ? remain : tsize;
315
316         memcpy(ptr, (void *) (stream->start + stream->offset), actual);
317         stream->offset += actual;
318
319         return actual;
320 }
321
322 int lfseek(struct LFILE *file, long offset, int whence)
323 {
324         int o = file->offset;
325
326         switch(whence) {
327         case SEEK_SET:
328                 o = offset;
329                 break;
330         case SEEK_CUR:
331                 o += offset;
332                 break;
333
334         case SEEK_END:
335                 return -1;
336         }
337
338         if (o < 0 || o > file->size)
339                 return -1;
340
341         file->offset = o;
342         return file->offset;
343 }
344
345 int lfclose(struct LFILE *file)
346 {
347         if (file)
348                 free(file);
349         return 0;
350 }