This patch also includes an EXPERT option for enabling no-ELF mode.

The system will default to old behaviour. See Kconfig in the root. 

I still wish to kill ELF mode very soon, however. 

LAR is a very capable format. With two simple extensions, we can use
LAR to replace all that we are using ELF for now. This change can
really make life better:
1. we can use streaming decompress instead of the current "uncompress
elf to memory and then copy segments" approach. So we can get rid of
THIS hardcode:
#define UNCOMPRESS_AREA (0x400000)
2. A simple lar l can show ALL segments, including payload segments
3. It's really easy to see where things will go in memory, and catch problems
4. We can figure out an ELF input file is bogus BEFORE we flash, not
AFTER we flash and try to boot it
5. did I mention streaming decompress?
6. We no longer have to worry about where we decompress the elf in
memory (this problem was causing trouble when the payload was a linux
kernel -- it was so big)
7. Since we have a load address, we can create this lar entry:
normal/cmdline
and specify that it be loaded at a place where linux will find it as
the cmdline.
8. The decision on whether to XIP can be made in the LAR entry, not in
hardcode. For example, if initram needs to be XIP, set the load
address to 0xffffffff. Done.

The change is simple. Add a load address and entry point to the lar
header. Extend the lar tool to parse the elf file and create multiple
lar segments. It looks like this:
 normal/payload0 (33192 bytes, lzma compressed to 18088 bytes @0x38
load @0x100000, entry 0x105258)
 normal/payload1 (72 bytes, lzma compressed to 47 bytes @0x4718 load
@0x1225a0, entry 0x105258)
 normal/option_table (932 bytes @0x4798 load @0, entry 0)
 normal/stage2 (33308 bytes, lzma compressed to 15474 bytes @0x4b78
load @0, entry 0)
 normal/initram (4208 bytes @0x8828 load @0, entry 0)
 linuxbios.bootblock (16384 bytes @0xfc000 load @0, entry 0)

note that the payload is now payload/segment0, payload/segment1, etc. I've extended
linuxbios to look for these. Note that you can now see all the things
that get loaded ;they're no longer hidden in an ELF header somewhere.
Elf failures are gone!

Note that I've left legacy elf support in, for now, but recommend we
get rid of it as soon as possible.

patch attached. This is a first pass. lar.c needs some refactoring but
I want to get the cmdline going. You can now have a linux payload and
it will uncompress with no problems.

This has been tested with filo and BOCHS.

This patch includes ONLY the lar changes, the other changes are next. 

Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>


git-svn-id: svn://coreboot.org/repository/LinuxBIOSv3@481 f3766cd6-281f-0410-b1cd-43a5c92072e9
This commit is contained in:
Ronald G. Minnich 2007-08-29 14:59:25 +00:00
parent eb1a12cf8d
commit c664a6953a
5 changed files with 294 additions and 71 deletions

View file

@ -17,7 +17,6 @@
## along with this program; if not, write to the Free Software ## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
## ##
LAROBJ := lar.o stream.o lib.o LAROBJ := lar.o stream.o lib.o
LARDIR := lardir LARDIR := lardir
@ -29,7 +28,6 @@ COMPRESSOR += $(obj)/util/nrv2b/nrv2b-compress.o
LARDIR += nrv2bdir LARDIR += nrv2bdir
LAROBJ_ABS := $(patsubst %,$(obj)/util/lar/%,$(LAROBJ)) LAROBJ_ABS := $(patsubst %,$(obj)/util/lar/%,$(LAROBJ))
lardir: lardir:
$(Q)printf " BUILD LAR\n" $(Q)printf " BUILD LAR\n"
$(Q)mkdir -p $(obj)/util/lar $(Q)mkdir -p $(obj)/util/lar

View file

@ -30,13 +30,14 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include "lib.h"
#include "lar.h" #include "lar.h"
#include "lib.h"
#define VERSION "v0.9.1" #define VERSION "v0.9.1"
#define COPYRIGHT "Copyright (C) 2006-2007 coresystems GmbH" #define COPYRIGHT "Copyright (C) 2006-2007 coresystems GmbH"
static int isverbose = 0; static int isverbose = 0;
static int iselfparse = 0;
static long larsize = 0; static long larsize = 0;
static char *bootblock = NULL; static char *bootblock = NULL;
enum compalgo algo = none; enum compalgo algo = none;
@ -44,7 +45,7 @@ enum compalgo algo = none;
static void usage(char *name) static void usage(char *name)
{ {
printf("\nLAR - the LinuxBIOS Archiver " VERSION "\n" COPYRIGHT "\n\n" printf("\nLAR - the LinuxBIOS Archiver " VERSION "\n" COPYRIGHT "\n\n"
"Usage: %s [-cxal] archive.lar [[[file1] file2] ...]\n\n", name); "Usage: %s [-ecxal] archive.lar [[[file1] file2] ...]\n\n", name);
printf("Examples:\n"); printf("Examples:\n");
printf(" lar -c -s 32k -b bootblock myrom.lar foo nocompress:bar\n"); printf(" lar -c -s 32k -b bootblock myrom.lar foo nocompress:bar\n");
printf(" lar -a myrom.lar foo blob:baz\n"); printf(" lar -a myrom.lar foo blob:baz\n");
@ -66,13 +67,18 @@ static void usage(char *name)
printf(" \ta 'm' suffix to multiple the size by 1024*1024.\n"); printf(" \ta 'm' suffix to multiple the size by 1024*1024.\n");
printf(" -b [bootblock]\tSpecify the bootblock blob\n"); printf(" -b [bootblock]\tSpecify the bootblock blob\n");
printf(" -C [lzma|nrv2b]\tSpecify the compression method to use\n\n"); printf(" -C [lzma|nrv2b]\tSpecify the compression method to use\n\n");
printf(" -e pre-parse the payload ELF into LAR segments. Recommended\n\n");
printf("General options\n"); printf("General options\n");
printf(" -v\tEnable verbose mode\n"); printf(" -v\tEnable verbose mode\n");
printf(" -V\tShow the version\n"); printf(" -V\tShow the version\n");
printf(" -h\tShow this help\n"); printf(" -h\tShow this help\n");
printf("\n"); printf("\n");
}
int elfparse(void)
{
return iselfparse;
} }
/* Add a human touch to the LAR size by allowing suffixes: /* Add a human touch to the LAR size by allowing suffixes:
@ -209,6 +215,7 @@ int main(int argc, char *argv[])
{"list", 0, 0, 'l'}, {"list", 0, 0, 'l'},
{"size", 1, 0, 's'}, {"size", 1, 0, 's'},
{"bootblock", 1, 0, 'b'}, {"bootblock", 1, 0, 'b'},
{"elfparse", 1, 0, 'e'},
{"verbose", 0, 0, 'v'}, {"verbose", 0, 0, 'v'},
{"version", 0, 0, 'V'}, {"version", 0, 0, 'V'},
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
@ -220,7 +227,7 @@ int main(int argc, char *argv[])
exit(1); exit(1);
} }
while ((opt = getopt_long(argc, argv, "acC:xls:b:vVh?", while ((opt = getopt_long(argc, argv, "acC:xels:b:vVh?",
long_options, &option_index)) != EOF) { long_options, &option_index)) != EOF) {
switch (opt) { switch (opt) {
case 'a': case 'a':
@ -240,6 +247,9 @@ int main(int argc, char *argv[])
case 'l': case 'l':
larmode = LIST; larmode = LIST;
break; break;
case 'e':
iselfparse = 1;
break;
case 'x': case 'x':
larmode = EXTRACT; larmode = EXTRACT;
break; break;

View file

@ -60,6 +60,7 @@
typedef uint32_t u32; typedef uint32_t u32;
typedef uint8_t u8; typedef uint8_t u8;
/* NOTE -- This and the linuxbios lar.h are NOT IN SYNC. Be careful. */
struct lar_header { struct lar_header {
char magic[8]; char magic[8];
u32 len; u32 len;
@ -73,6 +74,8 @@ struct lar_header {
* 2 = nrv2b * 2 = nrv2b
*/ */
u32 compression; u32 compression;
u32 entry; /* we might need to make this u64 */
u32 loadaddress; /* ditto */
}; };
/**\struct /**\struct

View file

@ -38,6 +38,7 @@ enum {
/* prototypes for lar.c functions */ /* prototypes for lar.c functions */
int verbose(void); int verbose(void);
int elfparse(void);
long get_larsize(void); long get_larsize(void);
char *get_bootblock(void); char *get_bootblock(void);
@ -50,13 +51,24 @@ int add_file_or_directory(const char *name);
struct file *get_files(void); struct file *get_files(void);
void free_files(void); void free_files(void);
/* Prototypes for ELF functions */
int iself(char *filebuf);
/* Prototypes for in-memory LAR operations */
int lar_process_name(char *name, char **pfilename, char **ppathname,
enum compalgo *thisalgo);
u32 lar_compress(char *ptr, ssize_t size, char *temp, enum compalgo *thisalgo);
int lar_add_entry(struct lar *lar, char *pathname, void *data,
u32 complen, u32 reallen, u32 loadaddress, u32 entry,
enum compalgo thisalgo);
/* Prototypes for the LAR I/O functions */ /* Prototypes for the LAR I/O functions */
char *mapfile(char *filename, u32 *size);
struct lar * lar_new_archive(const char *archive, unsigned int size); struct lar * lar_new_archive(const char *archive, unsigned int size);
struct lar * lar_open_archive(const char *archive); struct lar * lar_open_archive(const char *archive);
void lar_close_archive(struct lar *lar); void lar_close_archive(struct lar *lar);
void lar_list_files(struct lar *lar, struct file *files); void lar_list_files(struct lar *lar, struct file *files);
int lar_add_file(struct lar *lar, const char *name); int lar_add_file(struct lar *lar, char *name);
int lar_add_bootblock(struct lar *lar, const char *bootblock); int lar_add_bootblock(struct lar *lar, const char *bootblock);
int lar_extract_files(struct lar *lar, struct file *files); int lar_extract_files(struct lar *lar, struct file *files);

View file

@ -32,9 +32,10 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <libgen.h> #include <libgen.h>
#include <elf.h>
#include "lib.h"
#include "lar.h" #include "lar.h"
#include "lib.h"
/** /**
* \def err(fmt,args...) * \def err(fmt,args...)
@ -44,6 +45,124 @@
extern enum compalgo algo; extern enum compalgo algo;
/* ELF processing */
/**
* Given a ptr to data, determine if the data is an ELF image.
* @param filebuf pointer to the data
* @return 1 if ELF, 0 if not
*/
int iself(char *filebuf)
{
Elf32_Ehdr *ehdr;
/* validate elf header */
ehdr = (Elf32_Ehdr *)filebuf;
if (memcmp(ehdr->e_ident, ELFMAG, 4))
return 0;
return 1;
}
/**
* Output all the ELF segments for a given file
* @param lar The LAR Archoe
* @param name The LAR name
* @param filebuf The ELF file
* @param filelen Size of the ELF file
* @param algo The recommend compression algorithm
* Return 0 on success, -1 on failure
*/
int output_elf_segments(struct lar *lar, char *name, char *filebuf,
int filelen, enum compalgo algo)
{
int ret;
Elf32_Phdr *phdr;
Elf32_Ehdr *ehdr;
u32 entry;
int i;
int size;
unsigned char *header;
char ename[64];
int headers;
char *temp;
enum compalgo thisalgo;
u32 complen;
/* Allocate a temporary buffer to compress into - this is unavoidable,
because we need to make sure that the compressed data will fit in
the LAR, and we won't know the size of the compressed data until
we actually compress it */
temp = calloc(filelen, 1);
if (temp == NULL) {
err("Out of memory.\n");
return -1;
}
/* validate elf header */
ehdr = (Elf32_Ehdr *)filebuf;
headers = ehdr->e_phnum;
header = (unsigned char *)ehdr;
if (verbose())
fprintf(stderr, "Type %d machine %d version %d entry %p phoff %d shoff %d flags %#x hsize %d phentsize %d phnum %d s_hentsize %d s_shnum %d \n",
ehdr->e_type,
ehdr->e_machine,
ehdr->e_version,
(void *)ehdr->e_entry,
ehdr->e_phoff,
ehdr->e_shoff,
ehdr->e_flags,
ehdr->e_ehsize,
ehdr->e_phentsize,
ehdr->e_phnum,
ehdr->e_shentsize,
ehdr->e_shnum);
phdr = (Elf32_Phdr *)&(header[ehdr->e_phoff]);
if (verbose())
fprintf(stderr, "%s: header %p #headers %d\n", __FUNCTION__, ehdr, headers);
entry = ehdr->e_entry;
for(i = 0; i < headers; i++) {
/* Ignore data that I don't need to handle */
if (phdr[i].p_type != PT_LOAD) {
if (verbose())
fprintf(stderr, "Dropping non PT_LOAD segment\n");
continue;
}
if (phdr[i].p_memsz == 0) {
if (verbose())
fprintf(stderr, "Dropping empty segment\n");
continue;
}
thisalgo = algo;
if (verbose())
fprintf(stderr, "New segment addr 0x%ulx size 0x%ulx offset 0x%ulx filesize 0x%ulx\n",
(u32)phdr[i].p_paddr, (u32)phdr[i].p_memsz,
(u32)phdr[i].p_offset, (u32)phdr[i].p_filesz);
/* Clean up the values */
size = phdr[i].p_filesz;
if (phdr[i].p_filesz > phdr[i].p_memsz) {
size = phdr[i].p_memsz;
}
if (verbose()) {
fprintf(stderr, "(cleaned up) New segment addr %p size 0x%#x offset 0x%x\n",
(void *)phdr[i].p_paddr, size, phdr[i].p_offset);
fprintf(stderr, "Copy to %p from %p for %d bytes\n",
(unsigned char *)phdr[i].p_paddr,
&header[phdr[i].p_offset], size);
fprintf(stderr, "entry %ux loadaddr %ux\n",
entry, phdr[i].p_paddr);
}
/* ok, copy it out */
sprintf(ename, "%s/segment%d", name, i);
complen = lar_compress(&header[phdr[i].p_offset], size, temp, &thisalgo);
ret = lar_add_entry(lar, ename, temp, complen, size,
phdr[i].p_paddr, entry, thisalgo);
}
return 0;
out:
return -1;
}
/** /**
* Given a size, return the offset of the bootblock (including the * Given a size, return the offset of the bootblock (including the
* header) * header)
@ -259,7 +378,7 @@ struct lar * lar_new_archive(const char *archive, u32 size)
} }
return lar; return lar;
err: err:
lar_close_archive(lar); lar_close_archive(lar);
/* Don't leave a halfbaked LAR laying around */ /* Don't leave a halfbaked LAR laying around */
@ -315,7 +434,7 @@ struct lar * lar_open_archive(const char *archive)
return lar; return lar;
err: err:
lar_close_archive(lar); lar_close_archive(lar);
return NULL; return NULL;
} }
@ -535,102 +654,132 @@ int lar_extract_files(struct lar *lar, struct file *files)
} }
/** /**
* Add a new file to the LAR archive * Given a pathname in the form [option;]path, determine the file name,
* @param lar The LAR archive to write into * LAR path name, and compression algorithm.
* @param name The name of the file to add * @param name name in the [option:][./]path form
* @return 0 on success, or -1 on failure * @param pfilename reference pointer to file name -- this is modified
* @param ppathname reference pointer to LAR path name -- this is modified
* @param thisalgo pointer to algorithm, which can be modified by path name
* @return 0 success, or -1 on failure (i.e. a bad name)
*/ */
int lar_add_file(struct lar *lar, const char *name) int lar_process_name(char *name, char **pfilename, char **ppathname,
enum compalgo *thisalgo)
{ {
char *filename, *ptr, *temp; char *filename = name, *pathname = name;
char *pathname;
enum compalgo thisalgo;
struct lar_header *header;
u32 offset;
int ret, fd, hlen;
u32 complen;
int pathlen;
struct stat s;
u32 *walk, csum;
/* Find the beginning of the available space in the LAR */
offset = lar_empty_offset(lar);
thisalgo = algo;
filename = (char *) name;
if (!strncmp(name, "nocompress:",11)) { if (!strncmp(name, "nocompress:",11)) {
filename += 11; filename += 11;
thisalgo = none; *thisalgo = none;
} }
/* this is dangerous */
if (filename[0] == '.' && filename[1] == '/') if (filename[0] == '.' && filename[1] == '/')
filename += 2; filename += 2;
pathname = strchr(filename, ':'); pathname = strchr(filename, ':');
if (pathname != NULL) { if (pathname != NULL) {
*pathname = '\0'; *pathname = '\0';
pathname++; pathname++;
if (!strlen(pathname)) { if (!strlen(pathname)) {
err("Invalid pathname specified.\n"); err("Invalid pathname specified.\n");
return -1; return -1;
} }
} }
else else
pathname = filename; pathname = filename;
*pfilename = filename;
*ppathname = pathname;
return 0;
}
/**
* Given a pathname, open and mmap the file.
* @param filename
* @param size pointer to returned size
* @return ptr to mmap'ed area on success, or MAP_FAILED on failure
*/
char *mapfile(char *filename, u32 *size)
{
int fd;
struct stat s;
char *ptr;
/* Open the file */ /* Open the file */
fd = open(filename, O_RDONLY); fd = open(filename, O_RDONLY);
if (fd == -1) { if (fd == -1) {
err("Unable to open %s\n", filename); err("Unable to open %s\n", filename);
return -1; return MAP_FAILED;
} }
if (fstat(fd, &s)) { if (fstat(fd, &s)) {
err("Unable to stat the file %s\n", filename); err("Unable to stat the file %s\n", filename);
close(fd); close(fd);
return -1; return MAP_FAILED;
}
/* Allocate a temporary buffer to compress into - this is unavoidable,
because we need to make sure that the compressed data will fit in
the LAR, and we won't know the size of the compressed data until
we actually compress it */
temp = calloc(s.st_size, 1);
if (temp == NULL) {
err("Out of memory.\n");
return -1;
} }
ptr = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); ptr = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
if (ptr == MAP_FAILED) { if (ptr == MAP_FAILED) {
err("Unable to map %s\n", filename); err("Unable to map %s\n", filename);
close(fd); return MAP_FAILED;
free(temp);
return -1;
} }
*size = s.st_size;
return ptr;
}
/**
* Compress an area according to an algorithm. If the area grows,
* use no compression.
* @param ptr data to be compressed
* @param size size of the data
* @param temp destination of compressed data
* @param thisalgo pointer to algorithm -- this can be modified
* @return size of compressed data
*/
u32 lar_compress(char *ptr, ssize_t size, char *temp, enum compalgo *thisalgo)
{
u32 complen;
compress_functions[*thisalgo](ptr, size, temp, &complen);
/* Do the compression step */ if (complen >= size && (*thisalgo != none)) {
compress_functions[thisalgo](ptr, s.st_size, temp, &complen); *thisalgo = none;
compress_functions[*thisalgo](ptr, size, temp, &complen);
if (complen >= s.st_size && (thisalgo != none)) {
thisalgo = none;
compress_functions[thisalgo](ptr, s.st_size, temp, &complen);
} }
return complen;
}
munmap(ptr, s.st_size); /**
close(fd); * Add a new entry to the LAR archive
* @param lar The LAR archive to write into
* @param pathname The name of the segment
* @param data The data for the segment
* @param complen The compressed length of the segment
* @param reallen The real (uncompressed) length of the segment
* @param loadaddress The load address of the segment
* @param entry The entry point of the segment
* @param thisalgo The compression algorithm
* @return 0 on success, or -1 on failure
*/
int lar_add_entry(struct lar *lar, char *pathname, void *data,
u32 complen, u32 reallen, u32 loadaddress, u32 entry,
enum compalgo thisalgo)
{
struct lar_header *header;
int ret, hlen;
int pathlen;
u32 *walk, csum;
u32 offset;
pathlen = strlen(pathname) + 1 > MAX_PATHLEN ? MAX_PATHLEN : strlen(pathname) + 1; /* Find the beginning of the available space in the LAR */
offset = lar_empty_offset(lar);
pathlen = strlen(pathname) + 1 > MAX_PATHLEN ?
MAX_PATHLEN : strlen(pathname) + 1;
/* Figure out how big our header will be */ /* Figure out how big our header will be */
hlen = sizeof(struct lar_header) + pathlen; hlen = sizeof(struct lar_header) + pathlen;
@ -638,7 +787,6 @@ int lar_add_file(struct lar *lar, const char *name)
if (offset + hlen + complen >= get_bootblock_offset(lar->size)) { if (offset + hlen + complen >= get_bootblock_offset(lar->size)) {
err("Not enough room in the LAR to add the file.\n"); err("Not enough room in the LAR to add the file.\n");
free(temp);
return -1; return -1;
} }
@ -651,16 +799,17 @@ int lar_add_file(struct lar *lar, const char *name)
memcpy(header, MAGIC, 8); memcpy(header, MAGIC, 8);
header->compression = htonl(thisalgo); header->compression = htonl(thisalgo);
header->reallen = htonl(s.st_size); header->reallen = htonl(reallen);
header->len = htonl(complen); header->len = htonl(complen);
header->offset = htonl(hlen); header->offset = htonl(hlen);
header->loadaddress = htonl(loadaddress);
header->entry = htonl(entry);
/* Copy the path name */ /* Copy the path name */
strncpy((char *) (lar->map + offset + sizeof(struct lar_header)), strncpy((char *) (lar->map + offset + sizeof(struct lar_header)),
pathname, pathlen - 1); pathname, pathlen - 1);
/* Copy in the data */ /* Copy in the data */
memcpy(lar->map + (offset + hlen), temp, complen); memcpy(lar->map + (offset + hlen), data, complen);
/* Figure out the checksum */ /* Figure out the checksum */
@ -671,7 +820,58 @@ int lar_add_file(struct lar *lar, const char *name)
csum += ntohl(*walk); csum += ntohl(*walk);
} }
header->checksum = htonl(csum); header->checksum = htonl(csum);
free(temp);
return 0; return 0;
} }
/**
* Add a new file to the LAR archive
* @param lar The LAR archive to write into
* @param name The name of the file to add
* @return 0 on success, or -1 on failure
*/
int lar_add_file(struct lar *lar, char *name)
{
char *filename, *ptr, *temp;
char *pathname;
enum compalgo thisalgo;
struct lar_header *header;
int ret, hlen;
u32 complen;
int pathlen;
u32 size;
thisalgo = algo;
lar_process_name(name, &filename, &pathname, &thisalgo);
ptr = mapfile(filename, &size);
if (elfparse() && iself(ptr)) {
output_elf_segments(lar, pathname, ptr, size, thisalgo);
return 0;
}
/* This is legacy stuff. */
/* Allocate a temporary buffer to compress into - this is unavoidable,
because we need to make sure that the compressed data will fit in
the LAR, and we won't know the size of the compressed data until
we actually compress it */
temp = calloc(size, 1);
if (temp == NULL) {
err("Out of memory.\n");
munmap(ptr, size);
return -1;
}
complen = lar_compress(ptr, size, temp, &thisalgo);
munmap(ptr, size);
ret = lar_add_entry(lar, pathname, temp, complen, size, 0, 0, thisalgo);
free(temp);
return ret;
}