FreeNOS
BootImageCreate.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2009 Niek Linnenbank
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <Vector.h>
19 #include <ExecutableFormat.h>
20 #include <BootImage.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <sys/stat.h>
27 #include "BootImageCreate.h"
28 
29 BootImageCreate::BootImageCreate(int argc, char **argv)
30  : Application(argc, argv)
31 {
32  parser().setDescription("Create system boot image");
33  parser().registerFlag('p', "prefix", "Prefix each entry from the config file with the given path");
34  parser().registerPositional("CONFFILE", "Configuration file for the boot image");
35  parser().registerPositional("OUTFILE", "Output file name of the boot image");
36 }
37 
39 {
40 }
41 
43 {
44  printf("%s", string);
45  return Success;
46 }
47 
48 Size BootImageCreate::readBootSymbols(const char *conf_file,
49  const char *prefix,
50  Vector<BootEntry *> *entries)
51 {
52  char line[PATH_MAX];
53  const char *prog = *(parser().name());
54  const char *symbolname = 0;
56  Size totalBytes = 0, totalEntries = 0;
58  FILE *fp;
59  ExecutableFormat *format;
60  size_t prefix_len = prefix ? strlen(prefix) : 0;
61 
62  // Open configuration file
63  if ((fp = fopen(conf_file, "r")) == NULL)
64  {
65  fprintf(stderr, "%s: failed to open `%s': %s\r\n",
66  prog, conf_file, strerror(errno));
68  }
69 
70  // Read out lines
71  while (fgets(line, sizeof(line), fp) != NULL)
72  {
73  // Allocate new boot entry
74  entry = new BootEntry;
75 
76  // Remove trailing newline
77  if (strlen(line) < sizeof(line) - 1)
78  line[strlen(line)-1] = 0;
79 
80  // Get boot symbol type
81  if (strncmp(line, "BootProgram ", 12) == 0) {
82  entry->symbol.type = BootProgram;
83  symbolname = line + 12;
84  } else if (strncmp(line, "BootPrivProgram ", 16) == 0) {
85  entry->symbol.type = BootPrivProgram;
86  symbolname = line + 16;
87  } else if (strncmp(line, "BootData ", 9) == 0) {
88  entry->symbol.type = BootData;
89  symbolname = line + 9;
90  } else {
91  fprintf(stderr, "%s: symbol type unknown in line: %s\r\n",
92  prog, line);
94  }
95 
96  // Fill boot symbol name first
97  line[sizeof(line)-1] = 0;
98  strncpy(entry->symbol.name, symbolname, BOOTIMAGE_NAMELEN);
99 
100  // Append path prefix, if set
101  if (prefix_len && prefix_len < BOOTIMAGE_NAMELEN)
102  {
103  char tmp[PATH_MAX];
104  snprintf(tmp, sizeof(tmp), "%s/%s", prefix, symbolname);
105  strncpy(line, tmp, sizeof(line));
106  symbolname = line;
107  }
108 
109  // Find the file
110  struct stat st;
111  if (stat(symbolname, &st) == -1)
112  {
113  fprintf(stderr, "%s: failed to stat `%s': %s\r\n",
114  prog, symbolname, strerror(errno));
116  }
117  // Allocate buffer
118  entry->data = new u8[st.st_size];
119 
120  // Read the file
121  FILE *entry_fd = fopen(symbolname, "r");
122  if (!entry_fd)
123  {
124  fprintf(stderr, "%s: failed to open `%s': %s\r\n",
125  prog, symbolname, strerror(errno));
127  }
128  if (fread(entry->data, st.st_size, 1, entry_fd) != 1)
129  {
130  fprintf(stderr, "%s: failed to fread `%s': %s\r\n",
131  prog, symbolname, strerror(errno));
133  }
134  fclose(entry_fd);
135 
136  // Parse as BootProgram using libexec.
137  if (entry->symbol.type == BootProgram || entry->symbol.type == BootPrivProgram)
138  {
140  {
141  fprintf(stderr, "%s: failed to parse executable image format in `%s': %s\r\n",
142  prog, symbolname, strerror(errno));
144  }
145  // Extract memory regions
146  if (format->regions(entry->regions, &num) != ExecutableFormat::Success || num <= 0)
147  {
148  fprintf(stderr, "%s: failed to extract memory regions from `%s': %s\r\n",
149  prog, symbolname, strerror(errno));
151  }
152  entry->numRegions = num;
153 
154  format->entry((Address *)&entry->symbol.entry);
155  }
156  // BootData
157  else if (entry->symbol.type == BootData)
158  {
159  // Fill BootEntry
160  entry->symbol.type = BootData;
161  entry->numRegions = 1;
162  entry->regions[0].virt = 0;
163  entry->regions[0].access = Memory::User | Memory::Readable | Memory::Writable;
164  entry->regions[0].dataSize = st.st_size;
165  entry->regions[0].memorySize = st.st_size;
166  entry->regions[0].dataOffset = 0;
167  } else {
168  fprintf(stderr, "%s: unknown boot symbol type: %d\r\n",
169  prog, (uint) entry->symbol.type);
171  }
172  // Insert into Array
173  entries->insert(entry);
174  totalEntries++;
175 
176  // Debug out memory sections
177  for (Size i = 0; i < entry->numRegions; i++)
178  {
179  printf("%s[%u]: vaddr=%x size=%u\r\n",
180  symbolname, i, (uint) entry->regions[i].virt,
181  entry->regions[i].memorySize);
182  totalBytes += entry->regions[i].memorySize;
183  }
184  }
185  // Close config file
186  fclose(fp);
187 
188  // All done
189  printf("%d entries, %d bytes total\r\n", totalEntries, totalBytes);
190  return totalEntries;
191 }
192 
194 {
195  Vector<BootEntry *> input;
196  BootImage image;
197  BootSymbol *symbols;
198  BootSegment *segments;
199  FILE *fp;
200  Size numSegments = 0, dataOffset = 0, lastDataOffset = 0;
201  uint segCount = 0;
202  const char *prog = *(parser().name());
203  const char *conf_file = arguments().get("CONFFILE");
204  const char *out_file = arguments().get("OUTFILE");
205  const char *prefix = arguments().get("prefix");
206 
207  // Read boot symbols
208  if (readBootSymbols(conf_file, prefix, &input) == 0)
209  {
210  fprintf(stderr, "%s: failed to read boot symbols\r\n", prog);
211  return IOError;
212  }
213 
214  // Allocate tables
215  symbols = new BootSymbol[ input.count() ];
216  memset(&image, 0, sizeof(image));
217  memset(symbols, 0, sizeof(BootSymbol) * input.count());
218 
219  // Fill in the boot image header
220  image.magic[0] = BOOTIMAGE_MAGIC0;
221  image.magic[1] = BOOTIMAGE_MAGIC1;
223  image.symbolTableOffset = sizeof(BootImage);
224  image.symbolTableCount = input.count();
225  image.segmentsTableOffset = image.symbolTableOffset +
226  (image.symbolTableCount * sizeof(BootSymbol));
227 
228  // Fill in the boot symbols
229  for (Size i = 0; i < input.count(); i++)
230  {
231  strncpy(symbols[i].name, input[i]->symbol.name, BOOTIMAGE_NAMELEN);
232  symbols[i].type = input[i]->symbol.type;
233  symbols[i].entry = input[i]->symbol.entry;
234  symbols[i].segmentsOffset = numSegments;
235  symbols[i].segmentsCount = input[i]->numRegions;
236  symbols[i].segmentsTotalSize = 0;
237  numSegments += input[i]->numRegions;
238  }
239  // Update BootImage
240  image.segmentsTableCount = numSegments;
241 
242  // Now we allocate and clear the segments table
243  segments = new BootSegment[numSegments];
244  memset(segments, 0, sizeof(BootSegment) * numSegments);
245 
246  // Point segment data after the segments table
247  dataOffset = image.segmentsTableOffset +
248  image.segmentsTableCount * sizeof(BootSegment);
249  dataOffset += PageSize - (dataOffset % PageSize);
250 
251  // Fill the segments table by looping symbols
252  for (Size i = 0; i < input.count(); i++)
253  {
254  // Loop the symbol segments
255  for (Size j = 0; j < input[i]->numRegions; j++)
256  {
257  // Fill in the segment
258  segments[segCount].virtualAddress = input[i]->regions[j].virt;
259  segments[segCount].size = input[i]->regions[j].memorySize;
260  segments[segCount].offset = dataOffset;
261 
262  // Increment total segments size
263  symbols[i].segmentsTotalSize += segments[segCount].size;
264 
265  // Increment data pointer. Align on memory page boundary
266  dataOffset += segments[i].size;
267  lastDataOffset = dataOffset;
268  dataOffset += PageSize - (dataOffset % PageSize);
269  segCount++;
270  }
271  }
272  // Fill in the bootImageSize field
273  image.bootImageSize = lastDataOffset;
274 
275  // Open boot image for writing
276  if ((fp = fopen(out_file, "w")) == NULL)
277  {
278  fprintf(stderr, "%s: failed to open `%s': %s\r\n",
279  prog, out_file, strerror(errno));
280  return IOError;
281  }
282 
283  // Write the final boot image headers
284  if (fwrite(&image, sizeof(image), 1, fp) <= 0 ||
285  fwrite( symbols, sizeof(BootSymbol) * input.count(), 1, fp) <= 0 ||
286  fwrite( segments, sizeof(BootSegment) * numSegments, 1, fp) <= 0)
287  {
288  fprintf(stderr, "%s: failed to write BootImage headers to `%s': %s\r\n",
289  prog, out_file, strerror(errno));
290  return IOError;
291  }
292 
293  // Write the contents of the BootSegments
294  for (Size i = 0; i < input.count(); i++)
295  {
296  // Loop regions/segments per boot symbol entry
297  for (Size j = 0; j < input[i]->numRegions; j++)
298  {
299  // Adjust file pointer
300  if (fseek(fp, segments[symbols[i].segmentsOffset].offset,
301  SEEK_SET) == -1)
302  {
303  fprintf(stderr, "%s: failed to seek to BootSegment contents in `%s': %s\r\n",
304  prog, out_file, strerror(errno));
305  return IOError;
306  }
307 
308  // Write segment contents
309  if (fwrite(input[i]->data + input[i]->regions[j].dataOffset,
310  input[i]->regions[j].memorySize, 1, fp) <= 0)
311  {
312  fprintf(stderr, "%s: failed to write BootSegment contents to `%s': %s\r\n",
313  prog, out_file, strerror(errno));
314  return IOError;
315  }
316  }
317  }
318  // Close file
319  fclose(fp);
320 
321  // Done
322  return Success;
323 }
strncmp
int strncmp(const char *dest, const char *src, size_t count)
Compare two strings, by only a maximum number of bytes.
Definition: strncmp.cpp:20
ExecutableFormat.h
ArgumentContainer::get
const char * get(const char *name) const
Get argument by name.
Definition: ArgumentContainer.cpp:49
EXIT_FAILURE
#define EXIT_FAILURE
Unsuccessful termination.
Definition: stdlib.h:36
stat::st_size
off_t st_size
For regular files, the file size in bytes.
Definition: stat.h:226
stat
The <sys/stat.h> header shall define the stat structure.
Definition: stat.h:176
BootImageCreate.h
Vector.h
strlen
size_t strlen(const char *str)
Calculate the length of a string.
Definition: strlen.cpp:21
errno
C int errno
The lvalue errno is used by many functions to return error values.
BootProgram
@ BootProgram
Executable program.
Definition: BootImage.h:74
BootSegment
struct BootSegment BootSegment
Memory segment.
fopen
FILE * fopen(const char *filename, const char *mode)
Open a stream.
Definition: fopen.cpp:24
string.h
BootPrivProgram
@ BootPrivProgram
Privileged executable program.
Definition: BootImage.h:75
Memory::Writable
@ Writable
Definition: Memory.h:42
Memory::User
@ User
Definition: Memory.h:44
BootSymbol
struct BootSymbol BootSymbol
Program embedded in the BootImage.
BootSymbol::type
BootSymbolType type
Type of boot symbol.
Definition: BootImage.h:90
BootSymbol::segmentsOffset
u32 segmentsOffset
Offset in the segments table.
Definition: BootImage.h:96
Application
Generic application.
Definition: Application.h:38
Address
unsigned long Address
A memory address.
Definition: Types.h:131
Application::Success
@ Success
Definition: Application.h:55
ExecutableFormat::regions
virtual Result regions(Region *regions, Size *count) const =0
Memory regions a program needs at runtime.
PATH_MAX
#define PATH_MAX
Maximum file path length.
Definition: limits.h:37
BOOTIMAGE_MAGIC0
#define BOOTIMAGE_MAGIC0
First magic byte.
Definition: BootImage.h:30
Application::arguments
const ArgumentContainer & arguments() const
Get program arguments.
Definition: Application.cpp:112
ArgumentParser::setDescription
void setDescription(const String &desc)
Set program description.
Definition: ArgumentParser.cpp:95
BootImageCreate::output
virtual Result output(const char *string) const
Print text to output.
Definition: BootImageCreate.cpp:42
BootImageCreate::PageSize
static const Size PageSize
Size of memory pages as supported by this program.
Definition: BootImageCreate.h:60
BOOTIMAGE_NAMELEN
#define BOOTIMAGE_NAMELEN
Maximum length of BootSymbol names.
Definition: BootImage.h:39
BootImage::segmentsTableCount
u16 segmentsTableCount
Number of entries in the segments table.
Definition: BootImage.h:65
fwrite
size_t fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream)
The fwrite() function shall write, from the array pointed to by ptr, up to nitems elements whose size...
Definition: fwrite.cpp:24
Memory::Readable
@ Readable
Definition: Memory.h:41
ExecutableFormat::find
static Result find(const u8 *image, const Size size, ExecutableFormat **fmt)
Find a ExecutableFormat which can handle the given format.
Definition: ExecutableFormat.cpp:32
limits.h
BootImage::bootImageSize
u32 bootImageSize
Total size of the boot image in bytes.
Definition: BootImage.h:53
uint
unsigned int uint
Unsigned integer number.
Definition: Types.h:44
BootImageCreate::exec
virtual Result exec()
Execute the application.
Definition: BootImageCreate.cpp:193
Application::IOError
@ IOError
Definition: Application.h:57
BootEntry
Executable entry for use inside a boot image.
Definition: BootImageCreate.h:36
printf
int printf(const char *format,...)
Output a formatted string to standard output.
Definition: printf.cpp:22
BootImage.h
BootData
@ BootData
Binary data.
Definition: BootImage.h:77
BootImage::segmentsTableOffset
u32 segmentsTableOffset
Offset of the segments table.
Definition: BootImage.h:62
BootSymbol::segmentsTotalSize
u32 segmentsTotalSize
Total size of the BootSymbol segments.
Definition: BootImage.h:102
BootImage::symbolTableCount
u16 symbolTableCount
Number of entries in the symbols table.
Definition: BootImage.h:59
Vector::count
virtual Size count() const
Returns the number of items inside the Vector.
Definition: Vector.h:204
stdio.h
strerror
char * strerror(int errnum)
The strerror function maps the number in errnum to a message string.
Definition: strerror.cpp:20
NULL
#define NULL
NULL means zero.
Definition: Macros.h:39
Size
unsigned int Size
Any sane size indicator cannot go negative.
Definition: Types.h:128
Application::Result
Result
Result codes.
Definition: Application.h:53
ArgumentParser::registerFlag
Result registerFlag(char arg, const char *name, const char *description)
Register a flag Argument.
Definition: ArgumentParser.cpp:100
ArgumentParser::name
const String & name() const
Retrieve program name.
Definition: ArgumentParser.cpp:85
Vector::insert
virtual int insert(const T &item)
Adds the given item to the Vector, if possible.
Definition: Vector.h:93
stat
int stat(const char *path, struct stat *buf)
Get file status.
Definition: stat.cpp:25
BOOTENTRY_MAX_REGIONS
#define BOOTENTRY_MAX_REGIONS
Maximum memory regions.
Definition: BootImageCreate.h:31
ArgumentParser::registerPositional
Result registerPositional(const char *name, const char *description, Size count=1)
Register a positional argument.
Definition: ArgumentParser.cpp:119
stat.h
BootSymbol
Program embedded in the BootImage.
Definition: BootImage.h:84
BootImageCreate::BootImageCreate
BootImageCreate(int argc, char **argv)
Constructor.
Definition: BootImageCreate.cpp:29
ExecutableFormat::entry
virtual Result entry(Address *entry) const =0
Lookup the program entry point.
Application::parser
ArgumentParser & parser()
Get program arguments parser.
Definition: Application.cpp:102
SEEK_SET
#define SEEK_SET
Seek relative to start-of-file.
Definition: stdio.h:46
entry
u32 entry[]
Definition: IntelACPI.h:64
BootSegment::virtualAddress
u32 virtualAddress
Virtual memory address to load the segment.
Definition: BootImage.h:112
u8
unsigned char u8
Unsigned 8-bit number.
Definition: Types.h:59
BootSymbol::segmentsCount
u16 segmentsCount
Number of contiguous entries in the segment table.
Definition: BootImage.h:99
BootImage::layoutRevision
u8 layoutRevision
Version of the boot image layout.
Definition: BootImage.h:50
memset
void * memset(void *dest, int ch, size_t count)
Fill memory with a constant byte.
Definition: memset.cpp:20
BootImage::symbolTableOffset
u32 symbolTableOffset
Offset of the symbol table.
Definition: BootImage.h:56
BootSymbol::entry
u32 entry
Program entry point (only valid for BootProgram symbols).
Definition: BootImage.h:93
BootEntry
struct BootEntry BootEntry
Executable entry for use inside a boot image.
BOOTIMAGE_REVISION
#define BOOTIMAGE_REVISION
Version of the boot image layout.
Definition: BootImage.h:36
fread
size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream)
Binary input.
Definition: fread.cpp:24
BootImage
struct BootImage BootImage
BootImage contains executable programs to be loaded at system bootup.
BootImage
BootImage contains executable programs to be loaded at system bootup.
Definition: BootImage.h:44
BootSegment::size
u32 size
Total size of the segment.
Definition: BootImage.h:115
FILE
A structure containing information about a file.
Definition: stdio.h:60
stdlib.h
fclose
int fclose(FILE *stream)
Close a stream.
Definition: fclose.cpp:23
strncpy
int strncpy(char *dest, const char *src, size_t sz)
Copy a string, given a maximum number of bytes.
Definition: strncpy.cpp:20
BootSegment::offset
u32 offset
Offset in the boot image of the segment contents.
Definition: BootImage.h:118
BootImage::magic
u32 magic[2]
Magic numbers to detect a valid boot image.
Definition: BootImage.h:47
BootSegment
Memory segment.
Definition: BootImage.h:109
exit
C void exit(int status)
Terminate a process.
Definition: exit.cpp:21
Vector
Vectors are dynamically resizeable Arrays.
Definition: Vector.h:41
BootImageCreate::~BootImageCreate
virtual ~BootImageCreate()
Destructor.
Definition: BootImageCreate.cpp:38
ExecutableFormat
Abstraction class of various executable formats.
Definition: ExecutableFormat.h:48
BOOTIMAGE_MAGIC1
#define BOOTIMAGE_MAGIC1
Second magic byte.
Definition: BootImage.h:33
snprintf
int snprintf(char *buffer, unsigned int size, const char *fmt,...)
Write a formatted string into a buffer.
Definition: snprintf.cpp:22
BootImageCreate::readBootSymbols
Size readBootSymbols(const char *file, const char *prefix, Vector< BootEntry * > *entries)
Read boot symbols using a configuration file.
Definition: BootImageCreate.cpp:48
ExecutableFormat::Success
@ Success
Definition: ExecutableFormat.h:70
errno.h