This function verifies the checksum on a LAR file. Signed-off-by: Jordan Crouse <jordan.crouse@amd.com> Acked-by: Peter Stuge <peter@stuge.se> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3352 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
		
			
				
	
	
		
			347 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			347 lines
		
	
	
		
			7.5 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 *));
 | 
						|
	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;
 | 
						|
}
 |