while others dislike them being extra commits, let's clean them up once and for all for the existing code. If it's ugly, let it only be ugly once :-) Signed-off-by: Stefan Reinauer <stepan@coresystems.de> Acked-by: Stefan Reinauer <stepan@coresystems.de> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5507 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
		
			
				
	
	
		
			664 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			664 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include <stdarg.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <getopt.h>
 | 
						|
#ifdef HAVE_ZLIB_H
 | 
						|
#include <zlib.h>
 | 
						|
#endif
 | 
						|
#include "elf.h"
 | 
						|
#include "elf_boot.h"
 | 
						|
#include "mkelfImage.h"
 | 
						|
 | 
						|
static struct file_type file_type[] = {
 | 
						|
	{ "linux-i386", linux_i386_probe, linux_i386_mkelf, linux_i386_usage },
 | 
						|
	{ "bzImage-i386", bzImage_i386_probe, linux_i386_mkelf, linux_i386_usage },
 | 
						|
	{ "vmlinux-i386", vmlinux_i386_probe, linux_i386_mkelf, linux_i386_usage },
 | 
						|
	{ "linux-ia64", linux_ia64_probe, linux_ia64_mkelf, linux_ia64_usage },
 | 
						|
};
 | 
						|
static const int file_types = sizeof(file_type)/sizeof(file_type[0]);
 | 
						|
 | 
						|
void die(char *fmt, ...)
 | 
						|
{
 | 
						|
	va_list args;
 | 
						|
	va_start(args, fmt);
 | 
						|
	vfprintf(stderr, fmt, args);
 | 
						|
	va_end(args);
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**************************************************************************
 | 
						|
IPCHKSUM - Checksum IP Header
 | 
						|
**************************************************************************/
 | 
						|
uint16_t ipchksum(const void *data, unsigned long length)
 | 
						|
{
 | 
						|
	unsigned long sum;
 | 
						|
	unsigned long i;
 | 
						|
	const uint8_t *ptr;
 | 
						|
 | 
						|
	/* In the most straight forward way possible,
 | 
						|
	 * compute an ip style checksum.
 | 
						|
	 */
 | 
						|
	sum = 0;
 | 
						|
	ptr = data;
 | 
						|
	for(i = 0; i < length; i++) {
 | 
						|
		unsigned long value;
 | 
						|
		value = ptr[i];
 | 
						|
		if (i & 1) {
 | 
						|
			value <<= 8;
 | 
						|
		}
 | 
						|
		/* Add the new value */
 | 
						|
		sum += value;
 | 
						|
		/* Wrap around the carry */
 | 
						|
		if (sum > 0xFFFF) {
 | 
						|
			sum = (sum + (sum >> 16)) & 0xFFFF;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return (~cpu_to_le16(sum)) & 0xFFFF;
 | 
						|
}
 | 
						|
 | 
						|
uint16_t add_ipchksums(unsigned long offset, uint16_t sum, uint16_t new)
 | 
						|
{
 | 
						|
	unsigned long checksum;
 | 
						|
	sum = ~sum & 0xFFFF;
 | 
						|
	new = ~new & 0xFFFF;
 | 
						|
	if (offset & 1) {
 | 
						|
		/* byte swap the sum if it came from an odd offset
 | 
						|
		 * since the computation is endian independant this
 | 
						|
		 * works.
 | 
						|
		 */
 | 
						|
		new = bswap_16(new);
 | 
						|
	}
 | 
						|
	checksum = sum + new;
 | 
						|
	if (checksum > 0xFFFF) {
 | 
						|
		checksum -= 0xFFFF;
 | 
						|
	}
 | 
						|
	return (~checksum) & 0xFFFF;
 | 
						|
}
 | 
						|
 | 
						|
void *xmalloc(size_t size, const char *name)
 | 
						|
{
 | 
						|
	void *buf;
 | 
						|
	buf = malloc(size);
 | 
						|
	if (!buf) {
 | 
						|
		die("Cannot malloc %ld bytes to hold %s: %s\n",
 | 
						|
			size + 0UL, name, strerror(errno));
 | 
						|
	}
 | 
						|
	return buf;
 | 
						|
}
 | 
						|
 | 
						|
void *xrealloc(void *ptr, size_t size, const char *name)
 | 
						|
{
 | 
						|
	void *buf;
 | 
						|
	buf = realloc(ptr, size);
 | 
						|
	if (!buf) {
 | 
						|
		die("Cannot realloc %ld bytes to hold %s: %s\n",
 | 
						|
			size + 0UL, name, strerror(errno));
 | 
						|
	}
 | 
						|
	return buf;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
char *slurp_file(const char *filename, off_t *r_size)
 | 
						|
{
 | 
						|
	int fd;
 | 
						|
	char *buf;
 | 
						|
	off_t size, progress;
 | 
						|
	ssize_t result;
 | 
						|
	struct stat stats;
 | 
						|
 | 
						|
 | 
						|
	if (!filename) {
 | 
						|
		*r_size = 0;
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	fd = open(filename, O_RDONLY);
 | 
						|
	if (fd < 0) {
 | 
						|
		die("Cannot open `%s': %s\n",
 | 
						|
			filename, strerror(errno));
 | 
						|
	}
 | 
						|
	result = fstat(fd, &stats);
 | 
						|
	if (result < 0) {
 | 
						|
		die("Cannot stat: %s: %s\n",
 | 
						|
			filename, strerror(errno));
 | 
						|
	}
 | 
						|
	size = stats.st_size;
 | 
						|
	*r_size = size;
 | 
						|
	buf = xmalloc(size, filename);
 | 
						|
	progress = 0;
 | 
						|
	while(progress < size) {
 | 
						|
		result = read(fd, buf + progress, size - progress);
 | 
						|
		if (result < 0) {
 | 
						|
			if ((errno == EINTR) ||	(errno == EAGAIN))
 | 
						|
				continue;
 | 
						|
			die("read on %s of %ld bytes failed: %s\n",
 | 
						|
				filename, (size - progress)+ 0UL, strerror(errno));
 | 
						|
		}
 | 
						|
		progress += result;
 | 
						|
	}
 | 
						|
	result = close(fd);
 | 
						|
	if (result < 0) {
 | 
						|
		die("Close of %s failed: %s\n",
 | 
						|
			filename, strerror(errno));
 | 
						|
	}
 | 
						|
	return buf;
 | 
						|
}
 | 
						|
 | 
						|
#if HAVE_ZLIB_H
 | 
						|
char *slurp_decompress_file(const char *filename, off_t *r_size)
 | 
						|
{
 | 
						|
	gzFile fp;
 | 
						|
	int errnum;
 | 
						|
	const char *msg;
 | 
						|
	char *buf;
 | 
						|
	off_t size, allocated;
 | 
						|
	ssize_t result;
 | 
						|
 | 
						|
	if (!filename) {
 | 
						|
		*r_size = 0;
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	fp = gzopen(filename, "rb");
 | 
						|
	if (fp == 0) {
 | 
						|
		msg = gzerror(fp, &errnum);
 | 
						|
		if (errnum == Z_ERRNO) {
 | 
						|
			msg = strerror(errno);
 | 
						|
		}
 | 
						|
		die("Cannot open `%s': %s\n", filename, msg);
 | 
						|
	}
 | 
						|
	size = 0;
 | 
						|
	allocated = 65536;
 | 
						|
	buf = xmalloc(allocated, filename);
 | 
						|
	do {
 | 
						|
		if (size == allocated) {
 | 
						|
			allocated <<= 1;
 | 
						|
			buf = xrealloc(buf, allocated, filename);
 | 
						|
		}
 | 
						|
		result = gzread(fp, buf + size, allocated - size);
 | 
						|
		if (result < 0) {
 | 
						|
			if ((errno == EINTR) || (errno == EAGAIN))
 | 
						|
				continue;
 | 
						|
 | 
						|
			msg = gzerror(fp, &errnum);
 | 
						|
			if (errnum == Z_ERRNO) {
 | 
						|
				msg = strerror(errno);
 | 
						|
			}
 | 
						|
			die ("read on %s of %ld bytes failed: %s\n",
 | 
						|
				filename, (allocated - size) + 0UL, msg);
 | 
						|
		}
 | 
						|
		size += result;
 | 
						|
	} while(result > 0);
 | 
						|
	result = gzclose(fp);
 | 
						|
	if (result != Z_OK) {
 | 
						|
		msg = gzerror(fp, &errnum);
 | 
						|
		if (errnum == Z_ERRNO) {
 | 
						|
			msg = strerror(errno);
 | 
						|
		}
 | 
						|
		die ("Close of %s failed: %s\n", filename, msg);
 | 
						|
	}
 | 
						|
	*r_size =  size;
 | 
						|
	return buf;
 | 
						|
}
 | 
						|
#else
 | 
						|
char *slurp_decompress_file(const char *filename, off_t *r_size)
 | 
						|
{
 | 
						|
	return slurp_file(filename, r_size);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
struct memelfphdr *add_program_headers(struct memelfheader *ehdr, int count)
 | 
						|
{
 | 
						|
	struct memelfphdr *phdr;
 | 
						|
	int i;
 | 
						|
	ehdr->e_phnum = count;
 | 
						|
	ehdr->e_phdr = phdr = xmalloc(count *sizeof(*phdr), "Program headers");
 | 
						|
	/* Set the default values */
 | 
						|
	for(i = 0; i < count; i++) {
 | 
						|
		phdr[i].p_type   = PT_LOAD;
 | 
						|
		phdr[i].p_flags  = PF_R | PF_W | PF_X;
 | 
						|
		phdr[i].p_vaddr  = 0;
 | 
						|
		phdr[i].p_paddr  = 0;
 | 
						|
		phdr[i].p_filesz = 0;
 | 
						|
		phdr[i].p_memsz  = 0;
 | 
						|
		phdr[i].p_data   = 0;
 | 
						|
	}
 | 
						|
	return phdr;
 | 
						|
}
 | 
						|
 | 
						|
struct memelfnote *add_notes(struct memelfheader *ehdr, int count)
 | 
						|
{
 | 
						|
	struct memelfnote *notes;
 | 
						|
	ehdr->e_notenum = count;
 | 
						|
	ehdr->e_notes = notes = xmalloc(count *sizeof(*notes), "Notes");
 | 
						|
	memset(notes, 0, count *sizeof(*notes));
 | 
						|
	return notes;
 | 
						|
}
 | 
						|
 | 
						|
static int sizeof_notes(struct memelfnote *note, int notes)
 | 
						|
{
 | 
						|
	int size;
 | 
						|
	int i;
 | 
						|
 | 
						|
	size = 0;
 | 
						|
	for(i = 0; i < notes; i++) {
 | 
						|
		size += sizeof(Elf_Nhdr);
 | 
						|
		size += roundup(strlen(note[i].n_name)+1, 4);
 | 
						|
		size += roundup(note[i].n_descsz, 4);
 | 
						|
	}
 | 
						|
	return size;
 | 
						|
}
 | 
						|
 | 
						|
static uint16_t cpu_to_elf16(struct memelfheader *ehdr, uint16_t val)
 | 
						|
{
 | 
						|
	if (ehdr->ei_data == ELFDATA2LSB) {
 | 
						|
		return cpu_to_le16(val);
 | 
						|
	}
 | 
						|
	else if (ehdr->ei_data == ELFDATA2MSB) {
 | 
						|
		return cpu_to_be16(val);
 | 
						|
	}
 | 
						|
	die("Uknown elf layout in cpu_to_elf16");
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t cpu_to_elf32(struct memelfheader *ehdr, uint32_t val)
 | 
						|
{
 | 
						|
	if (ehdr->ei_data == ELFDATA2LSB) {
 | 
						|
		return cpu_to_le32(val);
 | 
						|
	}
 | 
						|
	else if (ehdr->ei_data == ELFDATA2MSB) {
 | 
						|
		return cpu_to_be32(val);
 | 
						|
	}
 | 
						|
	die("Uknown elf layout in cpu_to_elf32");
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static uint64_t cpu_to_elf64(struct memelfheader *ehdr, uint64_t val)
 | 
						|
{
 | 
						|
	if (ehdr->ei_data == ELFDATA2LSB) {
 | 
						|
		return cpu_to_le64(val);
 | 
						|
	}
 | 
						|
	else if (ehdr->ei_data == ELFDATA2MSB) {
 | 
						|
		return cpu_to_be64(val);
 | 
						|
	}
 | 
						|
	die("Uknown elf layout in cpu_to_elf64");
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void serialize_notes(char *buf, struct memelfheader *ehdr)
 | 
						|
{
 | 
						|
	struct Elf_Nhdr hdr;
 | 
						|
	struct memelfnote *note;
 | 
						|
	int notes;
 | 
						|
	size_t size, offset;
 | 
						|
	int i;
 | 
						|
 | 
						|
	/* Clear the buffer */
 | 
						|
	note = ehdr->e_notes;
 | 
						|
	notes = ehdr->e_notenum;
 | 
						|
	size = sizeof_notes(note, notes);
 | 
						|
	memset(buf, 0, size);
 | 
						|
 | 
						|
	/* Write the Elf Notes */
 | 
						|
	offset = 0;
 | 
						|
	for(i = 0; i < notes; i++) {
 | 
						|
		/* Compute the note header */
 | 
						|
		size_t n_namesz;
 | 
						|
		n_namesz = strlen(note[i].n_name) +1;
 | 
						|
		hdr.n_namesz = cpu_to_elf32(ehdr, n_namesz);
 | 
						|
		hdr.n_descsz = cpu_to_elf32(ehdr, note[i].n_descsz);
 | 
						|
		hdr.n_type   = cpu_to_elf32(ehdr, note[i].n_type);
 | 
						|
 | 
						|
		/* Copy the note into the buffer */
 | 
						|
		memcpy(buf + offset, &hdr,       sizeof(hdr));
 | 
						|
		offset += sizeof(hdr);
 | 
						|
		memcpy(buf + offset, note[i].n_name, n_namesz);
 | 
						|
		offset += roundup(n_namesz, 4);
 | 
						|
		memcpy(buf + offset, note[i].n_desc, note[i].n_descsz);
 | 
						|
		offset += roundup(note[i].n_descsz, 4);
 | 
						|
 | 
						|
	}
 | 
						|
}
 | 
						|
static void serialize_ehdr(char *buf, struct memelfheader *ehdr)
 | 
						|
{
 | 
						|
	if (ehdr->ei_class == ELFCLASS32) {
 | 
						|
		Elf32_Ehdr *hdr = (Elf32_Ehdr *)buf;
 | 
						|
		hdr->e_ident[EI_MAG0]    = ELFMAG0;
 | 
						|
		hdr->e_ident[EI_MAG1]    = ELFMAG1;
 | 
						|
		hdr->e_ident[EI_MAG2]    = ELFMAG2;
 | 
						|
		hdr->e_ident[EI_MAG3]    = ELFMAG3;
 | 
						|
		hdr->e_ident[EI_CLASS]   = ehdr->ei_class;
 | 
						|
		hdr->e_ident[EI_DATA]    = ehdr->ei_data;
 | 
						|
		hdr->e_ident[EI_VERSION] = EV_CURRENT;
 | 
						|
		hdr->e_type      = cpu_to_elf16(ehdr, ehdr->e_type);
 | 
						|
		hdr->e_machine   = cpu_to_elf16(ehdr, ehdr->e_machine);
 | 
						|
		hdr->e_version   = cpu_to_elf32(ehdr, EV_CURRENT);
 | 
						|
		hdr->e_entry     = cpu_to_elf32(ehdr, ehdr->e_entry);
 | 
						|
		hdr->e_phoff     = cpu_to_elf32(ehdr, sizeof(*hdr));
 | 
						|
		hdr->e_shoff     = cpu_to_elf32(ehdr, 0);
 | 
						|
		hdr->e_flags     = cpu_to_elf32(ehdr, ehdr->e_flags);
 | 
						|
		hdr->e_ehsize    = cpu_to_elf16(ehdr, sizeof(*hdr));
 | 
						|
		hdr->e_phentsize = cpu_to_elf16(ehdr, sizeof(Elf32_Phdr));
 | 
						|
		hdr->e_phnum     = cpu_to_elf16(ehdr, ehdr->e_phnum);
 | 
						|
		hdr->e_shentsize = cpu_to_elf16(ehdr, 0);
 | 
						|
		hdr->e_shnum     = cpu_to_elf16(ehdr, 0);
 | 
						|
		hdr->e_shstrndx  = cpu_to_elf16(ehdr, 0);
 | 
						|
	}
 | 
						|
	else if (ehdr->ei_class == ELFCLASS64) {
 | 
						|
		Elf64_Ehdr *hdr = (Elf64_Ehdr *)buf;
 | 
						|
		hdr->e_ident[EI_MAG0]    = ELFMAG0;
 | 
						|
		hdr->e_ident[EI_MAG1]    = ELFMAG1;
 | 
						|
		hdr->e_ident[EI_MAG2]    = ELFMAG2;
 | 
						|
		hdr->e_ident[EI_MAG3]    = ELFMAG3;
 | 
						|
		hdr->e_ident[EI_CLASS]   = ehdr->ei_class;
 | 
						|
		hdr->e_ident[EI_DATA]    = ehdr->ei_data;
 | 
						|
		hdr->e_ident[EI_VERSION] = EV_CURRENT;
 | 
						|
		hdr->e_type      = cpu_to_elf16(ehdr, ehdr->e_type);
 | 
						|
		hdr->e_machine   = cpu_to_elf16(ehdr, ehdr->e_machine);
 | 
						|
		hdr->e_version   = cpu_to_elf32(ehdr, EV_CURRENT);
 | 
						|
		hdr->e_entry     = cpu_to_elf64(ehdr, ehdr->e_entry);
 | 
						|
		hdr->e_phoff     = cpu_to_elf64(ehdr, sizeof(*hdr));
 | 
						|
		hdr->e_shoff     = cpu_to_elf64(ehdr, 0);
 | 
						|
		hdr->e_flags     = cpu_to_elf32(ehdr, ehdr->e_flags);
 | 
						|
		hdr->e_ehsize    = cpu_to_elf16(ehdr, sizeof(*hdr));
 | 
						|
		hdr->e_phentsize = cpu_to_elf16(ehdr, sizeof(Elf64_Phdr));
 | 
						|
		hdr->e_phnum     = cpu_to_elf16(ehdr, ehdr->e_phnum);
 | 
						|
		hdr->e_shentsize = cpu_to_elf16(ehdr, 0);
 | 
						|
		hdr->e_shnum     = cpu_to_elf16(ehdr, 0);
 | 
						|
		hdr->e_shstrndx  = cpu_to_elf16(ehdr, 0);
 | 
						|
	}
 | 
						|
	else die("Uknown elf class: %x\n", ehdr->ei_class);
 | 
						|
}
 | 
						|
static void serialize_phdrs(char *buf, struct memelfheader *ehdr, size_t note_size)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	size_t offset, note_offset;
 | 
						|
	if (ehdr->ei_class == ELFCLASS32) {
 | 
						|
		Elf32_Phdr *phdr = (Elf32_Phdr *)buf;
 | 
						|
		note_offset =
 | 
						|
			sizeof(Elf32_Ehdr) + (sizeof(Elf32_Phdr)*ehdr->e_phnum);
 | 
						|
		offset = note_offset + note_size;
 | 
						|
		for(i = 0; i < ehdr->e_phnum; i++) {
 | 
						|
			struct memelfphdr *hdr = ehdr->e_phdr + i;
 | 
						|
			phdr[i].p_type   = cpu_to_elf32(ehdr, hdr->p_type);
 | 
						|
			phdr[i].p_offset = cpu_to_elf32(ehdr, offset);
 | 
						|
			phdr[i].p_vaddr  = cpu_to_elf32(ehdr, hdr->p_vaddr);
 | 
						|
			phdr[i].p_paddr  = cpu_to_elf32(ehdr, hdr->p_paddr);
 | 
						|
			phdr[i].p_filesz = cpu_to_elf32(ehdr, hdr->p_filesz);
 | 
						|
			phdr[i].p_memsz  = cpu_to_elf32(ehdr, hdr->p_memsz);
 | 
						|
			phdr[i].p_flags  = cpu_to_elf32(ehdr, hdr->p_flags);
 | 
						|
			phdr[i].p_align  = cpu_to_elf32(ehdr, 0);
 | 
						|
			if (phdr[i].p_type == PT_NOTE) {
 | 
						|
				phdr[i].p_filesz = cpu_to_elf32(ehdr, note_size);
 | 
						|
				phdr[i].p_memsz  = cpu_to_elf32(ehdr, note_size);
 | 
						|
				phdr[i].p_offset = cpu_to_elf32(ehdr, note_offset);
 | 
						|
			} else {
 | 
						|
				offset += hdr->p_filesz;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (ehdr->ei_class == ELFCLASS64) {
 | 
						|
		Elf64_Phdr *phdr = (Elf64_Phdr *)buf;
 | 
						|
		note_offset =
 | 
						|
			sizeof(Elf64_Ehdr) + (sizeof(Elf64_Phdr)*ehdr->e_phnum);
 | 
						|
		offset = note_offset + note_size;
 | 
						|
		for(i = 0; i < ehdr->e_phnum; i++) {
 | 
						|
			struct memelfphdr *hdr = ehdr->e_phdr + i;
 | 
						|
			phdr[i].p_type   = cpu_to_elf32(ehdr, hdr->p_type);
 | 
						|
			phdr[i].p_flags  = cpu_to_elf32(ehdr, hdr->p_flags);
 | 
						|
			phdr[i].p_offset = cpu_to_elf64(ehdr, offset);
 | 
						|
			phdr[i].p_vaddr  = cpu_to_elf64(ehdr, hdr->p_vaddr);
 | 
						|
			phdr[i].p_paddr  = cpu_to_elf64(ehdr, hdr->p_paddr);
 | 
						|
			phdr[i].p_filesz = cpu_to_elf64(ehdr, hdr->p_filesz);
 | 
						|
			phdr[i].p_memsz  = cpu_to_elf64(ehdr, hdr->p_memsz);
 | 
						|
			phdr[i].p_align  = cpu_to_elf64(ehdr, 0);
 | 
						|
			if (phdr[i].p_type == PT_NOTE) {
 | 
						|
				phdr[i].p_filesz = cpu_to_elf64(ehdr, note_size);
 | 
						|
				phdr[i].p_memsz  = cpu_to_elf64(ehdr, note_size);
 | 
						|
				phdr[i].p_offset = cpu_to_elf64(ehdr, note_offset);
 | 
						|
			} else {
 | 
						|
				offset += hdr->p_filesz;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		die("Unknwon elf class: %x\n", ehdr->ei_class);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void write_buf(int fd, char *buf, size_t size)
 | 
						|
{
 | 
						|
	size_t progress = 0;
 | 
						|
	ssize_t result;
 | 
						|
	while(progress < size) {
 | 
						|
		result = write(fd, buf + progress, size - progress);
 | 
						|
		if (result < 0) {
 | 
						|
			if ((errno == EAGAIN) || (errno == EINTR)) {
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			die ("write of %ld bytes failed: %s\n",
 | 
						|
				size - progress, strerror(errno));
 | 
						|
		}
 | 
						|
		progress += result;
 | 
						|
	}
 | 
						|
}
 | 
						|
static void write_elf(struct memelfheader *ehdr, char *output)
 | 
						|
{
 | 
						|
	size_t ehdr_size;
 | 
						|
	size_t phdr_size;
 | 
						|
	size_t note_size;
 | 
						|
	size_t size;
 | 
						|
	uint16_t checksum;
 | 
						|
	size_t bytes;
 | 
						|
	char *buf;
 | 
						|
	int result, fd;
 | 
						|
	int i;
 | 
						|
	/* Prep for adding the checksum */
 | 
						|
	for(i = 0; i < ehdr->e_notenum; i++) {
 | 
						|
		if ((memcmp(ehdr->e_notes[i].n_name, "ELFBoot", 8) == 0) &&
 | 
						|
			(ehdr->e_notes[i].n_type == EIN_PROGRAM_CHECKSUM)) {
 | 
						|
			ehdr->e_notes[i].n_desc = &checksum;
 | 
						|
			ehdr->e_notes[i].n_descsz = 2;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Compute the sizes */
 | 
						|
	ehdr_size = 0;
 | 
						|
	phdr_size = 0;
 | 
						|
	note_size = 0;
 | 
						|
	if (ehdr->e_notenum) {
 | 
						|
		note_size = sizeof_notes(ehdr->e_notes, ehdr->e_notenum);
 | 
						|
	}
 | 
						|
	if (ehdr->ei_class == ELFCLASS32) {
 | 
						|
		ehdr_size = sizeof(Elf32_Ehdr);
 | 
						|
		phdr_size = sizeof(Elf32_Phdr) * ehdr->e_phnum;
 | 
						|
	}
 | 
						|
	else if (ehdr->ei_class == ELFCLASS64) {
 | 
						|
		ehdr_size = sizeof(Elf64_Ehdr);
 | 
						|
		phdr_size = sizeof(Elf64_Phdr) * ehdr->e_phnum;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		die("Unknown elf class: %x\n", ehdr->ei_class);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Allocate a buffer to temporarily hold the serialized forms */
 | 
						|
	size = ehdr_size + phdr_size + note_size;
 | 
						|
	buf = xmalloc(size, "Elf Headers");
 | 
						|
	memset(buf, 0, size);
 | 
						|
	serialize_ehdr(buf, ehdr);
 | 
						|
	serialize_phdrs(buf + ehdr_size, ehdr, note_size);
 | 
						|
 | 
						|
	/* Compute the checksum... */
 | 
						|
	checksum = ipchksum(buf, ehdr_size + phdr_size);
 | 
						|
	bytes = ehdr_size + phdr_size;
 | 
						|
	for(i = 0; i < ehdr->e_phnum; i++) {
 | 
						|
		checksum = add_ipchksums(bytes, checksum,
 | 
						|
			ipchksum(ehdr->e_phdr[i].p_data, ehdr->e_phdr[i].p_filesz));
 | 
						|
		bytes += ehdr->e_phdr[i].p_memsz;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Compute the final form of the notes */
 | 
						|
	serialize_notes(buf + ehdr_size + phdr_size, ehdr);
 | 
						|
 | 
						|
	/* Now write the elf image */
 | 
						|
	fd = open(output, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IRGRP | S_IROTH);
 | 
						|
	if (fd < 0) {
 | 
						|
		die("Cannot open ``%s'':%s\n",
 | 
						|
			output, strerror(errno));
 | 
						|
	}
 | 
						|
	write_buf(fd, buf, size);
 | 
						|
	for(i = 0; i < ehdr->e_phnum; i++) {
 | 
						|
		write_buf(fd, ehdr->e_phdr[i].p_data, ehdr->e_phdr[i].p_filesz);
 | 
						|
	}
 | 
						|
	result = close(fd);
 | 
						|
	if (result < 0) {
 | 
						|
		die("Close on %s failed: %s\n",
 | 
						|
			output, strerror(errno));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void version(void)
 | 
						|
{
 | 
						|
	printf("mkelfImage " VERSION " released " RELEASE_DATE "\n");
 | 
						|
}
 | 
						|
void usage(void)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	version();
 | 
						|
	printf(
 | 
						|
		"Usage: mkelfImage [OPTION]... <kernel> <elf_kernel>\n"
 | 
						|
		"Build an ELF bootable kernel image from a normal kernel image\n"
 | 
						|
		"\n"
 | 
						|
		" -h, --help                  Print this help.\n"
 | 
						|
		" -v, --version               Print the version of kexec.\n"
 | 
						|
		"     --kernel=<filename>     Set the kernel to <filename>\n"
 | 
						|
		"     --output=<filename>     Output to <filename>\n"
 | 
						|
		" -t, --type=TYPE             Specify the new kernel is of <type>.\n"
 | 
						|
		"\n"
 | 
						|
		"Supported kernel types: \n"
 | 
						|
		);
 | 
						|
	for(i = 0; i < file_types; i++) {
 | 
						|
		printf("%s\n", file_type[i].name);
 | 
						|
		file_type[i].usage();
 | 
						|
	}
 | 
						|
	printf("\n");
 | 
						|
}
 | 
						|
 | 
						|
void error(char *fmt, ...)
 | 
						|
{
 | 
						|
	va_list args;
 | 
						|
	va_start(args, fmt);
 | 
						|
	vfprintf(stderr, fmt, args);
 | 
						|
	va_end(args);
 | 
						|
	usage();
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
	int opt;
 | 
						|
	int fileind;
 | 
						|
	char *type, *kernel, *output;
 | 
						|
	off_t kernel_size;
 | 
						|
	char *kernel_buf;
 | 
						|
	int result;
 | 
						|
	int i;
 | 
						|
	struct memelfheader hdr;
 | 
						|
 | 
						|
	static const struct option options[] = {
 | 
						|
		MKELF_OPTIONS
 | 
						|
		{ 0, 0, 0, 0 },
 | 
						|
	};
 | 
						|
	static const char short_options[] = MKELF_OPT_STR;
 | 
						|
 | 
						|
	memset(&hdr, 0, sizeof(hdr));
 | 
						|
	kernel = 0;
 | 
						|
	output = 0;
 | 
						|
 | 
						|
	/* Get the default type from the program name */
 | 
						|
	type = strrchr(argv[0], '/');
 | 
						|
	if (!type) type = argv[0];
 | 
						|
	if (memcmp(type, "mkelf-", 6) == 0) {
 | 
						|
		type = type + 6;
 | 
						|
	} else {
 | 
						|
		type = 0;
 | 
						|
	}
 | 
						|
	opterr = 0; /* Don't complain about unrecognized options here */
 | 
						|
	while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
 | 
						|
		switch(opt) {
 | 
						|
		case OPT_HELP:
 | 
						|
			usage();
 | 
						|
			return 0;
 | 
						|
		case OPT_VERSION:
 | 
						|
			version();
 | 
						|
			return 0;
 | 
						|
		case OPT_KERNEL:
 | 
						|
			kernel = optarg;
 | 
						|
			break;
 | 
						|
		case OPT_OUTPUT:
 | 
						|
			output = optarg;
 | 
						|
			break;
 | 
						|
		case OPT_TYPE:
 | 
						|
			type = optarg;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	fileind = optind;
 | 
						|
 | 
						|
	/* Reset getopt for the next pass */
 | 
						|
	opterr = 1;
 | 
						|
	optind = 1;
 | 
						|
 | 
						|
	if (argc - fileind > 0) {
 | 
						|
		kernel = argv[fileind++];
 | 
						|
	}
 | 
						|
	if (argc - fileind > 0) {
 | 
						|
		output = argv[fileind++];
 | 
						|
	}
 | 
						|
	if (!kernel) {
 | 
						|
		error("No kernel specified!\n");
 | 
						|
	}
 | 
						|
	if (!output) {
 | 
						|
		error("No output file specified!\n");
 | 
						|
	}
 | 
						|
	if (argc - fileind > 0) {
 | 
						|
		error("%d extra options specified!\n", argc - fileind);
 | 
						|
	}
 | 
						|
 | 
						|
	/* slurp in the input kernel */
 | 
						|
	kernel_buf = slurp_decompress_file(kernel, &kernel_size);
 | 
						|
 | 
						|
	/* Find/verify the kernel type */
 | 
						|
	for(i = 0; i < file_types; i++) {
 | 
						|
		char *reason;
 | 
						|
		if (type && (strcmp(type, file_type[i].name) != 0)) {
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		reason = file_type[i].probe(kernel_buf, kernel_size);
 | 
						|
		if (reason == 0) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if (type) {
 | 
						|
			die("Not %s: %s\n", type, reason);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (i == file_types) {
 | 
						|
		die("Can not determine the file type of %s\n", kernel);
 | 
						|
	}
 | 
						|
	result = file_type[i].mkelf(argc, argv, &hdr, kernel_buf, kernel_size);
 | 
						|
	if (result < 0) {
 | 
						|
		die("Cannot create %s result: %d\n", output, result);
 | 
						|
	}
 | 
						|
	/* open the output file */
 | 
						|
	write_elf(&hdr, output);
 | 
						|
	return 0;
 | 
						|
}
 |