Signed-off-by: Stefan Reinauer <stepan@coresystems.de> Acked-by: Peter Stuge <peter@stuge.se> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@4474 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
		
			
				
	
	
		
			351 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			351 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This file is part of the libpayload project.
 | |
|  *
 | |
|  * Copyright (C) 2008 Advanced Micro Devices, Inc.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  * 3. The name of the author may not be used to endorse or promote products
 | |
|  *    derived from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | |
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | |
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | |
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | |
|  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | |
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | |
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|  * SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <libpayload.h>
 | |
| #include <arch/endian.h>
 | |
| 
 | |
| #define ROM_RESET_VECTOR 0xFFFFFFF0
 | |
| 
 | |
| static void * next_header(void * cur)
 | |
| {
 | |
| 	struct lar_header *header = (struct lar_header *) cur;
 | |
| 	int offset = ((ntohl(header->offset) + ntohl(header->len)) + 15) &
 | |
| 		0xFFFFFFF0;
 | |
| 
 | |
| 	return (void *) (cur + offset);
 | |
| }
 | |
| 
 | |
| static struct lar_header *lar_get_header(struct LAR *lar, int index)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	if (index < lar->count)
 | |
| 		return (struct lar_header *) lar->headers[index];
 | |
| 
 | |
| 	if (lar->eof && index >= lar->eof)
 | |
| 		return NULL;
 | |
| 
 | |
| 	for(i = lar->count; i <= index; i++) {
 | |
| 		void *next = (i == 0) ?
 | |
| 			lar->start : next_header(lar->headers[i - 1]);
 | |
| 
 | |
| 		if (strncmp((const char *) next, LAR_MAGIC, 8)) {
 | |
| 			lar->eof = lar->count;
 | |
| 			return NULL;
 | |
| 		}
 | |
| 
 | |
| 		if (lar->count == lar->alloc) {
 | |
| 			void *tmp = realloc(lar->headers,
 | |
| 					    (lar->alloc + 16) * sizeof(void *));
 | |
| 
 | |
| 			if (tmp == NULL)
 | |
| 				return NULL;
 | |
| 
 | |
| 			lar->headers = tmp;
 | |
| 			lar->alloc += 16;
 | |
| 		}
 | |
| 
 | |
| 		lar->headers[lar->count++] = next;
 | |
| 	}
 | |
| 
 | |
| 	return (struct lar_header *) lar->headers[index];
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Open a LAR stream
 | |
|  *
 | |
|  * @param addr The address in memory where the LAR is located.
 | |
|  * Use NULL to specify the boot LAR
 | |
|  * @return a pointer to the LAR stream
 | |
|  */
 | |
| 
 | |
| struct LAR *openlar(void *addr)
 | |
| {
 | |
| 	struct LAR *lar;
 | |
| 
 | |
| 	/* If the address is null, then figure out the start of the
 | |
| 	   boot LAR */
 | |
| 
 | |
| 	if (addr == NULL) {
 | |
| 		u32 size = *((u32 *) (ROM_RESET_VECTOR + 4));
 | |
| 		addr = (void *) ((ROM_RESET_VECTOR  + 16) - size);
 | |
| 	}
 | |
| 
 | |
| 	/* Check the magic to make sure this is a LAR */
 | |
| 	if (strncmp((const char *) addr, LAR_MAGIC, strlen(LAR_MAGIC)))
 | |
| 		return NULL;
 | |
| 
 | |
| 	lar = calloc(sizeof(struct LAR), 1);
 | |
| 
 | |
| 	if (!lar)
 | |
| 		return NULL;
 | |
| 
 | |
| 	lar->start = addr;
 | |
| 
 | |
| 	/* Preallocate 16 slots in the cache - this saves wear and
 | |
| 	 * tear on the heap */
 | |
| 
 | |
| 	lar->headers = malloc(16 * sizeof(void *));
 | |
| 
 | |
| 	if (!lar->headers)
 | |
| 		return NULL;
 | |
| 
 | |
| 	lar->alloc = 16;
 | |
| 	lar->count = lar->eof = 0;
 | |
| 	lar->cindex = 0;
 | |
| 
 | |
| 	return lar;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Close a LAR stream
 | |
|  *
 | |
|  * @param lar A pointer to the LAR stream
 | |
|  * @return Return 0 on success, -1 on error
 | |
|  */
 | |
| 
 | |
| int closelar(struct LAR *lar)
 | |
| {
 | |
| 	if (!lar)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (lar->headers)
 | |
| 		free(lar->headers);
 | |
| 
 | |
| 	free(lar);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Read an entry from the LAR
 | |
|  *
 | |
|  * @param lar A pointer to the LAR stream
 | |
|  * @return A pointer to a larent structure
 | |
|            representing the next file in the LAR
 | |
|  */
 | |
| 
 | |
| struct larent *readlar(struct LAR *lar)
 | |
| {
 | |
| 	static struct larent _larent;
 | |
| 	struct lar_header *header;
 | |
| 	int nlen;
 | |
| 
 | |
| 	if (!lar)
 | |
| 		return NULL;
 | |
| 
 | |
| 	header = lar_get_header(lar, lar->cindex);
 | |
| 
 | |
| 	if (header == NULL)
 | |
| 		return NULL;
 | |
| 
 | |
| 	nlen = ntohl(header->offset) - sizeof(*header);
 | |
| 
 | |
| 	if (nlen > LAR_MAX_PATHLEN - 1)
 | |
| 		nlen = LAR_MAX_PATHLEN - 1;
 | |
| 
 | |
| 	memcpy((void *) _larent.name, ((char *) header + sizeof(*header)),
 | |
| 		nlen);
 | |
| 
 | |
| 	_larent.name[nlen] = 0;
 | |
| 
 | |
| 	lar->cindex++;
 | |
| 
 | |
| 	return (struct larent *) &_larent;
 | |
| }
 | |
| 
 | |
| void rewindlar(struct LAR *lar)
 | |
| {
 | |
| 	if (lar != NULL)
 | |
| 		lar->cindex = 0;
 | |
| }
 | |
| 
 | |
| static struct lar_header *get_header_by_name(struct LAR *lar, const char *name)
 | |
| {
 | |
| 	struct lar_header *header;
 | |
| 	int i;
 | |
| 
 | |
| 	for(i = 0; ; i++) {
 | |
| 		header = lar_get_header(lar, i);
 | |
| 
 | |
| 		if (header == NULL)
 | |
| 			return NULL;
 | |
| 
 | |
| 		if (!strcmp(name, ((char *) header + sizeof(*header))))
 | |
| 			return header;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int larstat(struct LAR *lar, const char *path, struct larstat *buf)
 | |
| {
 | |
| 	struct lar_header *header = get_header_by_name(lar, path);
 | |
| 
 | |
| 	if (header == NULL || buf == NULL)
 | |
| 		return -1;
 | |
| 
 | |
| 	buf->len = ntohl(header->len);
 | |
| 	buf->reallen = ntohl(header->reallen);
 | |
| 	buf->checksum = ntohl(header->checksum);
 | |
| 	buf->compchecksum = ntohl(header->compchecksum);
 | |
| 	buf->compression = ntohl(header->compression);
 | |
| 	buf->entry = ntohll(header->entry);
 | |
| 	buf->loadaddress = ntohll(header->loadaddress);
 | |
| 	buf->offset = ((u32) header - (u32) lar->start) + ntohl(header->offset);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void * larfptr(struct LAR *lar, const char *filename)
 | |
| {
 | |
| 	struct lar_header *header = get_header_by_name(lar, filename);
 | |
| 
 | |
| 	if (header == NULL)
 | |
| 		return NULL;
 | |
| 
 | |
| 	return (void *) ((u8 *) header + ntohl(header->offset));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Verify the checksum on a particular LAR entry
 | |
|  *
 | |
|  * @param lar A pointer to the LAR stream
 | |
|  * @param filename The lar entry to verify
 | |
|  * @return Return 1 if the entry is valid, 0 if it is not, or -1
 | |
|  * on error
 | |
|  */
 | |
| 
 | |
| int lfverify(struct LAR *lar, const char *filename)
 | |
| {
 | |
| 	struct lar_header *header = get_header_by_name(lar, filename);
 | |
| 
 | |
| 	u8 *ptr = (u8 *) header;
 | |
| 	int len = ntohl(header->len) + ntohl(header->offset);
 | |
| 	int offset;
 | |
| 	u32 csum = 0;
 | |
| 
 | |
| 	if (header == NULL)
 | |
| 		return -1;
 | |
| 
 | |
| 	/* The checksum needs to be calulated on entire data section,
 | |
| 	 * including any padding for the 16 byte alignment (which should
 | |
| 	 * be zeros
 | |
| 	 */
 | |
| 
 | |
| 	len = (len + 15) & 0xFFFFFFF0;
 | |
| 
 | |
| 	for(offset = 0; offset < len; offset += 4) {
 | |
| 		csum += *((u32 *) (ptr + offset));
 | |
| 	}
 | |
| 
 | |
| 	return (csum == 0xFFFFFFFF) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| struct LFILE * lfopen(struct LAR *lar, const char *filename)
 | |
| {
 | |
| 	struct LFILE *file;
 | |
| 	struct lar_header *header = get_header_by_name(lar, filename);
 | |
| 
 | |
| 	if (header == NULL)
 | |
| 		return NULL;
 | |
| 
 | |
| 	/* FIXME: What other validations do we want to do on the file here? */
 | |
| 
 | |
| 	file = malloc(sizeof(struct LFILE));
 | |
| 
 | |
| 	if (file == NULL)
 | |
| 		return NULL;
 | |
| 
 | |
| 	file->lar = lar;
 | |
| 	file->header = header;
 | |
| 	file->size = ntohl(header->len);
 | |
| 	file->start = ((u8 *) header + ntohl(header->offset));
 | |
| 	file->offset = 0;
 | |
| 
 | |
| 	return file;
 | |
| }
 | |
| 
 | |
| void *lfmap(struct LFILE *file, int offset)
 | |
| {
 | |
| 	if (file == NULL)
 | |
| 		return (void *) -1;
 | |
| 
 | |
| 	if (offset > file->size)
 | |
| 		return (void *) -1;
 | |
| 
 | |
| 	return (void *) (file->start + offset);
 | |
| };
 | |
| 
 | |
| int lfread(void *ptr, size_t size, size_t nmemb, struct LFILE *stream)
 | |
| {
 | |
| 	size_t tsize, actual;
 | |
| 	size_t remain = stream->size - stream->offset;
 | |
| 
 | |
| 	if (!stream || !remain)
 | |
| 		return 0;
 | |
| 
 | |
| 	tsize = (size * nmemb);
 | |
| 	actual = (tsize > remain) ? remain : tsize;
 | |
| 
 | |
| 	memcpy(ptr, (void *) (stream->start + stream->offset), actual);
 | |
| 	stream->offset += actual;
 | |
| 
 | |
| 	return actual;
 | |
| }
 | |
| 
 | |
| int lfseek(struct LFILE *file, long offset, int whence)
 | |
| {
 | |
| 	int o = file->offset;
 | |
| 
 | |
| 	switch(whence) {
 | |
| 	case SEEK_SET:
 | |
| 		o = offset;
 | |
| 		break;
 | |
| 	case SEEK_CUR:
 | |
| 		o += offset;
 | |
| 		break;
 | |
| 
 | |
| 	case SEEK_END:
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (o < 0 || o > file->size)
 | |
| 		return -1;
 | |
| 
 | |
| 	file->offset = o;
 | |
| 	return file->offset;
 | |
| }
 | |
| 
 | |
| int lfclose(struct LFILE *file)
 | |
| {
 | |
| 	if (file)
 | |
| 		free(file);
 | |
| 	return 0;
 | |
| }
 |