Use alignment parameter for initrd; coalescing support for NBI
authorH. Peter Anvin <hpa@zytor.com>
Tue, 15 Jan 2008 19:30:54 +0000 (14:30 -0500)
committerH. Peter Anvin <hpa@zytor.com>
Tue, 15 Jan 2008 19:30:54 +0000 (14:30 -0500)
Use the alignment parameter instead of padding the initrd segments;
add support for coalescing adjacent segments in the NBI backend.

Makefile
cwrite.c [new file with mode: 0644]
elf.c
linux.c
nbi.c
wraplinux.h

index 248c3f6..9267c82 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -26,7 +26,8 @@ reloc/reloc.elf: $(RELOC_OBJS) reloc/reloc.ld
 
 reloc.o: reloc.S reloc/reloc.bin
 
-wraplinux: main.o linux.o reloc.o elf.o nbi.o segment.o mapfile.o xmalloc.o
+wraplinux: main.o linux.o reloc.o elf.o nbi.o segment.o mapfile.o \
+          cwrite.o xmalloc.o
        $(CC) $(LDFLAGS) -o $@ $^
 
 #
diff --git a/cwrite.c b/cwrite.c
new file mode 100644 (file)
index 0000000..c0f9aa9
--- /dev/null
+++ b/cwrite.c
@@ -0,0 +1,46 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2008 rPath, Inc. - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * cwrite.c
+ *
+ * Conditional fwrite and writezero
+ */
+
+#include "wraplinux.h"
+
+size_t c_fwrite(const void *ptr, size_t bytes, FILE *stream)
+{
+       if (!stream)
+               return bytes;
+
+       return fwrite(ptr, 1, bytes, stream);
+}
+
+size_t c_writezero(size_t n, FILE *stream)
+{
+       static const char zerobuf[BUFSIZ]; /* All zero */
+       size_t r, o = 0;
+
+       if (!stream)
+               return n;
+
+       while (n) {
+               size_t w = (n < BUFSIZ) ? n : BUFSIZ;
+               r = fwrite(zerobuf, 1, w, stream);
+               o += r;
+               n -= r;
+               if (r < w)
+                       break;
+       }
+       return o;
+}
diff --git a/elf.c b/elf.c
index 672ef71..b2204b3 100644 (file)
--- a/elf.c
+++ b/elf.c
 #include "segment.h"
 #include "le.h"
 
-static size_t c_fwrite(const void *ptr, size_t bytes, FILE *stream)
-{
-       if (stream)
-               return fwrite(ptr, 1, bytes, stream);
-       else
-               return bytes;
-}
-
-static size_t c_writezero(size_t n, FILE *stream)
-{
-       static const char zerobuf[BUFSIZ]; /* All zero */
-       size_t r, o = 0;
-
-       while (n) {
-               size_t w = (n < BUFSIZ) ? n : BUFSIZ;
-               r = c_fwrite(zerobuf, w, stream);
-               o += r;
-               n -= r;
-               if (r < w)
-                       break;
-       }
-       return o;
-}
-
-static inline addr_t align_up(addr_t addr, int align)
-{
-       addr_t align_mask = ((addr_t)1 << align)-1;
-       return (addr + align_mask) & ~align_mask;
-}
-
 static inline uint32_t sh_to_p_type(uint32_t sh_type)
 {
        switch (sh_type) {
diff --git a/linux.c b/linux.c
index 6a796c8..4f97d88 100644 (file)
--- a/linux.c
+++ b/linux.c
@@ -216,6 +216,9 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
 
        initrd_len = 0;
        for (ip = initrd_list, i = 0; ip; ip = ip->next, i++) {
+               /* Each sub-initrd is aligned to a 4-byte boundary */
+               initrd_len = align_up(initrd_len, 2);
+
                ird[i].fd = open(ip->str, O_RDONLY);
                if (ird[i].fd < 0) {
                        fprintf(stderr, "%s: %s: %s", program, ip->str,
@@ -232,12 +235,6 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
                        goto err;
                }
 
-               /* We need to pad the space between initrds to a
-                  4-byte boundary.  This is safe because an mmap is
-                  always padded with zero to a page boundary... */
-               if (i < ninitrd - 1)
-                       ird[i].seg.length = (ird[i].seg.length + 3) & ~3;
-
                initrd_len += ird[i].seg.length;
        }
 
@@ -304,11 +301,13 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
        if (skrn.address < 0x100000)
                initrd_addr = 0x100000;
        else
-               initrd_addr = (skrn.address + skrn.length + 3) & ~3;
+               initrd_addr = skrn.address + skrn.length;
 
        for (i = 0; i < ninitrd; i++) {
                char *name;
 
+               initrd_addr = align_up(initrd_addr, 2);
+
                ird[i].seg.next = (i < ninitrd - 1) ? &ird[i + 1].seg : 0;
                ird[i].seg.address = initrd_addr;
                ird[i].seg.align = 2;   /* 2**2 = 4 bytes */
diff --git a/nbi.c b/nbi.c
index a6e38cc..c26a010 100644 (file)
--- a/nbi.c
+++ b/nbi.c
@@ -39,6 +39,7 @@ int output_nbi(struct segment *segs, addr_t entry, FILE *out)
        uint32_t offset;
        struct segment *s;
        addr_t base;
+       addr_t address, length;
 
        segs = sort_segments(segs);
 
@@ -70,8 +71,7 @@ int output_nbi(struct segment *segs, addr_t entry, FILE *out)
                wrle16(0, &nhdr.header_seg);
        }
 
-       fwrite(&nhdr, 1, sizeof nhdr, out);
-       offset = sizeof nhdr;
+       offset = c_fwrite(&nhdr, sizeof nhdr, out);
 
        for (s = segs; s; s = s->next) {
                if (is_real_seg(s)) {
@@ -79,28 +79,57 @@ int output_nbi(struct segment *segs, addr_t entry, FILE *out)
                        ihdr.tags = 0;
                        ihdr.resv = 0;
                        ihdr.flags = s->next ? 0 : NBI_IFLAG_LAST;
-                       ihdr.load_addr = s->address;
                        /* The semantics of NBI memsz > filesz is
                           unclear.  It might be desirable to actually
                           allow generation of NOBITS segments. */
-                       ihdr.filesz = s->length;
-                       ihdr.memsz  = s->length;
 
-                       fwrite(&ihdr, 1, sizeof ihdr, out);
-                       offset += sizeof ihdr;
+                       address = s->address;
+                       length  = s->length;
+
+                       while (s->next && is_real_seg(s->next) &&
+                              s->next->align <= s->align &&
+                              s->next->address ==
+                              align_up(address+length, s->next->align)) {
+                               /* Merge sections */
+                               s = s->next;
+                               length += padsize(address+length, s->align);
+                               length += s->length;
+                       }
+
+                       ihdr.load_addr = address;
+                       ihdr.filesz    = length;
+                       ihdr.memsz     = length;
+
+                       offset += c_fwrite(&ihdr, sizeof ihdr, out);
                }
        }
 
-       while (offset < NBI_HEADER_SIZE) {
-               putc(0, out);
-               offset++;
-       }
-       putc(0x55, out);
-       putc(0xaa, out);
+       if (offset <= NBI_HEADER_SIZE)
+               offset += c_writezero(NBI_HEADER_SIZE-offset, out);
+
+       if (offset == NBI_HEADER_SIZE)
+               offset += c_fwrite("\x55", 1, out);
+       if (offset == NBI_HEADER_SIZE+1)
+               offset += c_fwrite("\xaa", 1, out);
 
-       for (s = segs; s; s = s->next)
-               if (is_real_seg(s))
-                       fwrite(s->data, 1, s->length, out);
+       for (s = segs; s; s = s->next) {
+               if (is_real_seg(s)) {
+                       address = s->address;
+                       address += c_fwrite(s->data, s->length, out);
+
+                       while (s->next && is_real_seg(s->next) &&
+                              s->next->align <= s->align &&
+                              s->next->address ==
+                              align_up(address, s->next->align)) {
+                               /* Merge sections */
+                               s = s->next;
+                               address +=
+                                       c_writezero(padsize(address, s->align),
+                                                   out);
+                               address += c_fwrite(s->data, s->length, out);
+                       }
+               }
+       }
 
        return 0;
 }
index 8456f61..919b69c 100644 (file)
@@ -36,6 +36,25 @@ struct string_list {
        const char *str;
 };
 
+/* Convenience inline functions */
+static inline addr_t align_up(addr_t addr, int align)
+{
+       addr_t align_mask = ((addr_t)1 << align)-1;
+       return (addr + align_mask) & ~align_mask;
+}
+
+static inline addr_t align_down(addr_t addr, int align)
+{
+       addr_t align_mask = ((addr_t)1 << align)-1;
+       return addr & ~align_mask;
+}
+
+static inline addr_t padsize(addr_t addr, int align)
+{
+       addr_t align_mask = ((addr_t)1 << align)-1;
+       return -addr & align_mask;
+}
+
 /* linux.c */
 int wrap_kernel(const char *kernel, const char *cmdline,
                const struct string_list *initrd_list, FILE *out);
@@ -49,4 +68,8 @@ void *xmalloc(size_t);
 void *xcalloc(size_t, size_t);
 int xasprintf(char **strp, const char *fmt, ...);
 
+/* cwrite.c */
+size_t c_fwrite(const void *ptr, size_t bytes, FILE *stream);
+size_t c_writezero(size_t n, FILE *stream);
+
 #endif