man: --nbi, not --NBI
[wraplinux.git] / nbi.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2008 rPath, Inc. - All Rights Reserved
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
8  *   Boston MA 02110-1301, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ----------------------------------------------------------------------- */
12
13 /*
14  * nbi.c
15  *
16  * Take a linked list of segments and output it as an NBI image
17  */
18
19 #include "wraplinux.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <inttypes.h>
25 #include "nbi.h"
26 #include "segment.h"
27 #include "le.h"
28
29 static inline int is_real_seg(struct segment *s)
30 {
31         return (s->sh_type == SHT_PROGBITS) &&
32                 (s->sh_flags & SHF_ALLOC);
33 }
34
35 int output_nbi(struct segment *segs, addr_t entry, FILE *out)
36 {
37         struct nbi_header nhdr;
38         struct nbi_image_header ihdr;
39         uint32_t offset;
40         struct segment *s;
41         addr_t base;
42         addr_t address, length;
43
44         segs = sort_segments(segs);
45
46         wrle32(NBI_MAGIC, &nhdr.magic);
47         wrle32(NBI_HFLAG_PROTMODE + (sizeof nhdr >> 2), &nhdr.flags);
48         wrle32(entry, &nhdr.entry);
49
50         /* NBI wants a 512-byte area to load its header into low memory.
51            The spec says it should be between 0x10000 and 0x94000.
52            Try our best to find such an area; if we totally fail, then
53            fall back to the classical boot sector address and hope the
54            loader can cope. */
55         base = 0x10000;
56         for (s = segs; s; s = s->next) {
57                 if (is_real_seg(s)) {
58                         if (s->address >= base+512)
59                                 break;  /* Found a safe area */
60
61                         base = (s->address + s->length + 511) & ~511;
62                 }
63         }
64
65         if (s && base <= 0x93800) {
66                 wrle16(0, &nhdr.header_off);
67                 wrle16(base >> 4, &nhdr.header_seg);
68         } else {
69                 /* Last resort: classical boot sector location */
70                 wrle16(0x7c00, &nhdr.header_off);
71                 wrle16(0, &nhdr.header_seg);
72         }
73
74         offset = c_fwrite(&nhdr, sizeof nhdr, out);
75
76         for (s = segs; s; s = s->next) {
77                 if (is_real_seg(s)) {
78                         ihdr.lengths = sizeof ihdr >> 2;
79                         ihdr.tags = 0;
80                         ihdr.resv = 0;
81                         /* The semantics of NBI memsz > filesz is
82                            unclear.  It might be desirable to actually
83                            allow generation of NOBITS segments. */
84
85                         address = s->address;
86                         length  = s->length;
87
88                         while (s->next && is_real_seg(s->next) &&
89                                s->next->align <= s->align &&
90                                s->next->address ==
91                                align_up(address+length, s->next->align)) {
92                                 /* Merge sections */
93                                 s = s->next;
94                                 length += padsize(address+length, s->align);
95                                 length += s->length;
96                         }
97
98                         ihdr.load_addr = address;
99                         ihdr.filesz    = length;
100                         ihdr.memsz     = length;
101                         ihdr.flags     = s->next ? 0 : NBI_IFLAG_LAST;
102
103                         offset += c_fwrite(&ihdr, sizeof ihdr, out);
104                 }
105         }
106
107         if (offset <= NBI_HEADER_SIZE)
108                 offset += c_writezero(NBI_HEADER_SIZE-offset, out);
109
110         if (offset == NBI_HEADER_SIZE)
111                 offset += c_fwrite("\x55", 1, out);
112         if (offset == NBI_HEADER_SIZE+1)
113                 offset += c_fwrite("\xaa", 1, out);
114
115         for (s = segs; s; s = s->next) {
116                 if (is_real_seg(s)) {
117                         address = s->address;
118                         address += c_fwrite(s->data, s->length, out);
119
120                         while (s->next && is_real_seg(s->next) &&
121                                s->next->align <= s->align &&
122                                s->next->address ==
123                                align_up(address, s->next->align)) {
124                                 /* Merge sections */
125                                 s = s->next;
126                                 address +=
127                                         c_writezero(padsize(address, s->align),
128                                                     out);
129                                 address += c_fwrite(s->data, s->length, out);
130                         }
131                 }
132         }
133
134         return 0;
135 }