NBI: try to calculate a valid free area; ignore non-PROGBITS segments
authorH. Peter Anvin <hpa@zytor.com>
Fri, 11 Jan 2008 01:56:56 +0000 (17:56 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Fri, 11 Jan 2008 01:56:56 +0000 (17:56 -0800)
NBI backend:
- try to pick a valid free area in low memory for the header.
- ignore segments other than PROGBITS segments.

nbi.c
segment.h

diff --git a/nbi.c b/nbi.c
index aab0805..57bf414 100644 (file)
--- a/nbi.c
+++ b/nbi.c
 #include "segment.h"
 #include "le.h"
 
+static inline int is_real_seg(struct segment *s)
+{
+       return (s->sh_type == SHT_PROGBITS) &&
+               (s->sh_flags & SHF_ALLOC);
+}
+
 int output_nbi(struct segment *segs, addr_t entry, FILE *out)
 {
        struct nbi_header nhdr;
        struct nbi_image_header ihdr;
        uint32_t offset;
        struct segment *s;
+       addr_t base;
 
        segs = sort_segments(segs);
 
@@ -37,25 +44,49 @@ int output_nbi(struct segment *segs, addr_t entry, FILE *out)
        wrle32(NBI_HFLAG_PROTMODE + (sizeof nhdr >> 2), &nhdr.flags);
        wrle32(entry, &nhdr.entry);
 
-       /* NBI wants a 512-byte area to load its header into memory. */
-       /* This kind of sucks for that purpose, but what to do... */
-       wrle16(0x7c00, &nhdr.header_off);
-       wrle16(0, &nhdr.header_seg);
+       /* NBI wants a 512-byte area to load its header into low memory.
+          The spec says it should be between 0x10000 and 0x94000.
+          Try our best to find such an area; if we totally fail, then
+          fall back to the classical boot sector address and hope the
+          loader can cope. */
+       base = 0x10000;
+       for (s = segs; s; s = s->next) {
+               if (is_real_seg(s)) {
+                       if (s->address >= base+512)
+                               break;  /* Found a safe area */
+                       
+                       base = (s->address + s->length + 511) & ~511;
+               }
+       }
+
+       if (s && base <= 0x93800) {
+               wrle16(0, &nhdr.header_off);
+               wrle16(base >> 4, &nhdr.header_seg);
+       } else {
+               /* Last resort: classical boot sector location */
+               wrle16(0x7c00, &nhdr.header_off);
+               wrle16(0, &nhdr.header_seg);
+       }
 
        fwrite(&nhdr, 1, sizeof nhdr, out);
        offset = sizeof nhdr;
 
        for (s = segs; s; s = s->next) {
-               ihdr.lengths = sizeof ihdr >> 2;
-               ihdr.tags = 0;
-               ihdr.resv = 0;
-               ihdr.flags = s->next ? 0 : NBI_IFLAG_LAST;
-               ihdr.load_addr = s->address;
-               ihdr.filesz = s->length;
-               ihdr.memsz  = s->length;
-
-               fwrite(&ihdr, 1, sizeof ihdr, out);
-               offset += sizeof ihdr;
+               if (is_real_seg(s)) {
+                       ihdr.lengths = sizeof ihdr >> 2;
+                       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;
+               }
        }
 
        while (offset < NBI_HEADER_SIZE) {
@@ -66,7 +97,8 @@ int output_nbi(struct segment *segs, addr_t entry, FILE *out)
        putc(0xaa, out);
 
        for (s = segs; s; s = s->next)
-               fwrite(s->data, 1, s->length, out);
+               if (is_real_seg(s))
+                       fwrite(s->data, 1, s->length, out);
 
        return 0;
 }
index ad0677b..f58e88d 100644 (file)
--- a/segment.h
+++ b/segment.h
@@ -21,6 +21,7 @@
 
 #include <stdio.h>
 #include <inttypes.h>
+#include "elf32.h"             /* For constants */
 
 typedef uint32_t addr_t;