Author: rminnich Date: 2007-08-29 18:42:58 +0200 (Wed, 29 Aug 2007) New Revision: 484
Modified: LinuxBIOSv3/lib/lar.c Log: This patch is for two things that are too hard to seperate, as they affect one common file.
The first is the noelf option, which is a simple modification to look for payloads in the form payload/segmentX where X is a number. The segments are assumed to be contiguously numbered and the code steps when the first search fails (i.e. if 0 and 1 are there, and 2 is not, the code stops and will not try to find 3). The lar code will use the loadaddress to load the segment. Currently, the lar code will not run each loaded segment, but we need to consider this for uwe's lbmenu work. Consider this implementation somewhat preliminary until we integrate lbmenu. There is the complex issue of interdependent segments, as in the kernel, where no single segment can run until all are loaded.
The second is the fix for improperly wrapping to 0 when searching the LAR.
This has been tested with filo and BOCHS.
Signed-off-by: Ronald G. Minnich rminnich@gmail.com Acked-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Modified: LinuxBIOSv3/lib/lar.c =================================================================== --- LinuxBIOSv3/lib/lar.c 2007-08-29 15:26:54 UTC (rev 483) +++ LinuxBIOSv3/lib/lar.c 2007-08-29 16:42:58 UTC (rev 484) @@ -31,6 +31,13 @@ #define ntohl(x) (x) #endif
+int run_address(void *f) +{ + int (*v) (void); + v = f; + return v(); +} + int find_file(struct mem_file *archive, char *filename, struct mem_file *result) { char *walk, *fullname; @@ -40,31 +47,108 @@ printk(BIOS_SPEW, "LAR: Start %p len 0x%x\n", archive->start, archive->len);
+ if (archive->len < sizeof(struct lar_header)) + printk(BIOS_ERR, "Error: truncated archive (%d bytes); minimum possible size is %d bytes\n", + archive->len, sizeof(struct lar_header)); + + /* Getting this for loop right is harder than it looks. All quantities are + * unsigned. The LAR stretches from (e.g.) 0xfff0000 for 0x100000 + * bytes, i.e. to address ZERO. + * As a result, 'walk', can wrap to zero and keep going (this has been + * seen in practice). Recall that these are unsigned; walk can + * wrap to zero; so, question, when is walk less than any of these: + * archive->start + * Answer: once walk wraps to zero, it is < archive->start + * archive->start + archive->len + * archive->start + archive->len - 1 + * Answer: In the case that archive->start + archive->len == 0, ALWAYS! + * A lot of expressions have been tried and all have been wrong. + * So what would work? Simple: + * test for walk < archive->start + archive->len - 1 to cover the case + * that the archive does NOT occupy ALL of the top of memory and + * wrap to zero; + * and test for walk >= archive->start, + * to cover the case that you wrapped to zero. + * Unsigned pointer arithmetic that wraps to zero can be messy. + */ for (walk = archive->start; - (walk - 1) < (char *)(archive->start + archive->len - 1 ); walk += 16) { + (walk < (char *)(archive->start + archive->len - sizeof(struct lar_header))) && + (walk >= (char *)archive->start); walk += 16) { if (strcmp(walk, MAGIC) != 0) continue;
header = (struct lar_header *)walk; fullname = walk + sizeof(struct lar_header);
- printk(BIOS_SPEW, "LAR: current filename is %s\n", fullname); + printk(BIOS_SPEW, "LAR: search for %s\n", fullname); // FIXME: check checksum
if (strcmp(fullname, filename) == 0) { + printk(BIOS_SPEW, "LAR: CHECK %s @ %p\n", fullname, header); result->start = walk + ntohl(header->offset); result->len = ntohl(header->len); result->reallen = ntohl(header->reallen); result->compression = ntohl(header->compression); + result->entry = (void *)ntohl(header->entry); + result->loadaddress = (void *)ntohl(header->loadaddress); + printk(BIOS_SPEW, + "start %p len %d reallen %d compression %x entry %p loadaddress %p\n", + result->start, result->len, result->reallen, + result->compression, result->entry, result->loadaddress); return 0; } // skip file walk += (ntohl(header->len) + ntohl(header->offset) - 1) & 0xfffffff0; } + printk(BIOS_SPEW, "NO FILE FOUND\n"); return 1; }
+ +void *load_file(struct mem_file *archive, char *filename) +{ + int ret; + struct mem_file result; + void *where; + void *entry; + + ret = find_file(archive, filename, &result); + if (ret) { + printk(BIOS_INFO, "LAR: load_file: No such file '%s'\n", + filename); + return (void *)-1; + } + entry = result.entry; + where = result.loadaddress; + printk(BIOS_SPEW, "LAR: Compression algorithm #%i used\n", result.compression); + /* no compression */ + if (result.compression == 0) { + memcpy(where, result.start, result.len); + return entry; + } +#ifdef CONFIG_COMPRESSION_LZMA + /* lzma */ + unsigned long ulzma(unsigned char *src, unsigned char *dst); + if (result.compression == 1) { + ulzma(result.start, where); + return entry; + } +#endif +#ifdef CONFIG_COMPRESSION_NRV2B + /* nrv2b */ + unsigned long unrv2b(u8 *src, u8 *dst, unsigned long *ilen_p); + if (result.compression == 2) { + int tmp; + unrv2b(result.start, where, &tmp); + return entry; + } +#endif + printk(BIOS_INFO, "LAR: Compression algorithm #%i not supported!\n", result.compression); + return (void *)-1; +} + +/* FIXME -- most of copy_file should be replaced by load_file */ int copy_file(struct mem_file *archive, char *filename, void *where) { int ret; @@ -85,7 +169,7 @@ } #ifdef CONFIG_COMPRESSION_LZMA /* lzma */ - unsigned long ulzma(unsigned char * src, unsigned char * dst); + unsigned long ulzma(unsigned char *src, unsigned char *dst); if (result.compression == 1) { ulzma(result.start, where); return 0; @@ -93,7 +177,7 @@ #endif #ifdef CONFIG_COMPRESSION_NRV2B /* nrv2b */ - unsigned long unrv2b(u8 * src, u8 * dst, unsigned long *ilen_p); + unsigned long unrv2b(u8 *src, u8 *dst, unsigned long *ilen_p); if (result.compression == 2) { int tmp; unrv2b(result.start, where, &tmp); @@ -113,6 +197,7 @@ { int (*v) (void); struct mem_file result; + int ret;
if ((u32) where != 0xFFFFFFFF) { if (copy_file(archive, filename, where)) { @@ -130,9 +215,11 @@ } where = result.start; } - + printk(BIOS_SPEW, "where is %p\n", where); v = where; - return v(); + ret = v(); + printk(BIOS_SPEW, "run_file returns with %d\n", ret); + return ret; }
/**