mkbootimage.c revision 1.3 1 /* $NetBSD: mkbootimage.c,v 1.3 2007/12/19 19:45:33 garbled Exp $ */
2
3 /*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tim Rightnour and NONAKA Kimihiro
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #if HAVE_NBTOOL_CONFIG_H
40 #include "nbtool_config.h"
41 #include "../../sys/sys/bootblock.h"
42 #else
43 #include <sys/bootblock.h>
44 #endif
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <fcntl.h>
50 #include <unistd.h>
51 #include <errno.h>
52 #include <zlib.h>
53 #include <err.h>
54 #include <sys/stat.h>
55 #include <sys/types.h>
56 #include <sys/uio.h>
57
58 #ifdef __NetBSD__
59 #include <sys/sysctl.h>
60 #include <sys/utsname.h>
61 #endif
62
63 /* BFD ELF headers */
64 #include <elf/common.h>
65 #include <elf/external.h>
66
67 #include "byteorder.h"
68 #include "magic.h"
69
70 /* Globals */
71
72 int saloneflag = 0;
73 Elf32_External_Ehdr hdr, khdr;
74 struct stat elf_stat;
75 unsigned char mbr[512];
76 char *sup_plats[] = {
77 "prep",
78 NULL,
79 };
80
81 /*
82 * Macros to get values from multi-byte ELF header fields. These assume
83 * a big-endian image.
84 */
85 #define ELFGET16(x) (((x)[0] << 8) | (x)[1])
86
87 #define ELFGET32(x) (((x)[0] << 24) | ((x)[1] << 16) | \
88 ((x)[2] << 8) | (x)[3])
89
90 static void usage(void);
91 static int open_file(const char *, char *, Elf32_External_Ehdr *,
92 struct stat *);
93 static void check_mbr(int, int, char *);
94 static int prep_build_image(char *, char *, char *, char *, int);
95 int main(int, char **);
96
97 static void
98 usage(void)
99 {
100 #ifdef __NetBSD__
101 fprintf(stderr, "usage: %s [-ls] [-m machine_arch] [-b bootfile] "
102 "[-k kernel] [-r rawdev] bootimage\n", getprogname());
103 #else
104 fprintf(stderr, "usage: %s [-ls] -m machine_arch [-b bootfile] "
105 "[-k kernel] [-r rawdev] bootimage\n", getprogname());
106 #endif
107 exit(1);
108 }
109
110 /* verify the file is ELF and ppc, and open it up */
111 static int
112 open_file(const char *ftype, char *file, Elf32_External_Ehdr *hdr,
113 struct stat *f_stat)
114 {
115 int fd;
116
117 if ((fd = open(file, 0)) < 0)
118 errx(2, "Can't open %s '%s': %s", ftype, file, strerror(errno));
119 fstat(fd, f_stat);
120
121 if (read(fd, hdr, sizeof(Elf32_External_Ehdr)) !=
122 sizeof(Elf32_External_Ehdr))
123 errx(3, "Can't read input '%s': %s", file, strerror(errno));
124
125 if (hdr->e_ident[EI_MAG0] != ELFMAG0 ||
126 hdr->e_ident[EI_MAG1] != ELFMAG1 ||
127 hdr->e_ident[EI_MAG2] != ELFMAG2 ||
128 hdr->e_ident[EI_MAG3] != ELFMAG3 ||
129 hdr->e_ident[EI_CLASS] != ELFCLASS32)
130 errx(3, "input '%s' is not ELF32 format", file);
131
132 if (hdr->e_ident[EI_DATA] != ELFDATA2MSB)
133 errx(3, "input '%s' is not big-endian", file);
134
135 if (ELFGET16(hdr->e_machine) != EM_PPC)
136 errx(3, "input '%s' is not PowerPC exec binary", file);
137
138 return(fd);
139 }
140
141 static void
142 prep_check_mbr(int prep_fd, int lfloppyflag, char *rawdev)
143 {
144 int raw_fd;
145 unsigned long entry, length;
146 struct mbr_partition *mbrp;
147 struct stat raw_stat;
148
149 /* If we are building a standalone image, do not write an MBR, just
150 * set entry point and boot image size skipping over elf header
151 */
152 if (saloneflag) {
153 entry = sa_htole32(0x400);
154 length = sa_htole32(elf_stat.st_size - sizeof(hdr) + 0x400);
155 lseek(prep_fd, sizeof(mbr), SEEK_SET);
156 write(prep_fd, &entry, sizeof(entry));
157 write(prep_fd, &length, sizeof(length));
158 return;
159 }
160
161 /*
162 * if we have a raw device, we need to check to see if it already
163 * has a partition table, and if so, read it in and check for
164 * suitability.
165 */
166 if (rawdev != NULL) {
167 raw_fd = open(rawdev, O_RDONLY, 0);
168 if (raw_fd == -1)
169 errx(3, "couldn't open raw device %s: %s", rawdev,
170 strerror(errno));
171
172 fstat(raw_fd, &raw_stat);
173 if (!S_ISCHR(raw_stat.st_mode))
174 errx(3, "%s is not a raw device", rawdev);
175
176 if (read(raw_fd, mbr, 512) != 512)
177 errx(3, "MBR Read Failed: %s", strerror(errno));
178
179 mbrp = (struct mbr_partition *)&mbr[MBR_PART_OFFSET];
180 if (mbrp->mbrp_type != MBR_PTYPE_PREP)
181 errx(3, "First partition is not of type 0x%x.",
182 MBR_PTYPE_PREP);
183 if (mbrp->mbrp_start != 0)
184 errx(3, "Use of the raw device is intended for"
185 " upgrading of legacy installations. Your"
186 " install does not have a PReP boot partition"
187 " starting at sector 0. Use the -s option"
188 " to build an image instead.");
189
190 /* if we got this far, we are fine, write back the partition
191 * and write the entry points and get outta here */
192 /* Set entry point and boot image size skipping over elf header */
193 lseek(prep_fd, 0, SEEK_SET);
194 entry = sa_htole32(0x400);
195 length = sa_htole32(elf_stat.st_size - sizeof(hdr) + 0x400);
196 write(prep_fd, mbr, sizeof(mbr));
197 write(prep_fd, &entry, sizeof(entry));
198 write(prep_fd, &length, sizeof(length));
199 close(raw_fd);
200 return;
201 }
202
203 /* if we get to here, we want to build a standard floppy or netboot
204 * image to file, so just build it */
205
206 memset(mbr, 0, sizeof(mbr));
207 mbrp = (struct mbr_partition *)&mbr[MBR_PART_OFFSET];
208
209 /* Set entry point and boot image size skipping over elf header */
210 entry = sa_htole32(0x400);
211 length = sa_htole32(elf_stat.st_size - sizeof(hdr) + 0x400);
212
213 /*
214 * Set magic number for msdos partition
215 */
216 *(unsigned short *)&mbr[MBR_MAGIC_OFFSET] = sa_htole16(MBR_MAGIC);
217
218 /*
219 * Build a "PReP" partition table entry in the boot record
220 * - "PReP" may only look at the system_indicator
221 */
222 mbrp->mbrp_flag = MBR_PFLAG_ACTIVE;
223 mbrp->mbrp_type = MBR_PTYPE_PREP;
224
225 /*
226 * The first block of the diskette is used by this "boot record" which
227 * actually contains the partition table. (The first block of the
228 * partition contains the boot image, but I digress...) We'll set up
229 * one partition on the diskette and it shall contain the rest of the
230 * diskette.
231 */
232 mbrp->mbrp_shd = 0; /* zero-based */
233 mbrp->mbrp_ssect = 2; /* one-based */
234 mbrp->mbrp_scyl = 0; /* zero-based */
235 mbrp->mbrp_ehd = 1; /* assumes two heads */
236 if (lfloppyflag)
237 mbrp->mbrp_esect = 36; /* 2.88MB floppy */
238 else
239 mbrp->mbrp_esect = 18; /* assumes 18 sectors/track */
240 mbrp->mbrp_ecyl = 79; /* assumes 80 cylinders/diskette */
241
242 /*
243 * The "PReP" software ignores the above fields and just looks at
244 * the next two.
245 * - size of the diskette is (assumed to be)
246 * (2 tracks/cylinder)(18 sectors/tracks)(80 cylinders/diskette)
247 * - unlike the above sector numbers,
248 * the beginning sector is zero-based!
249 */
250
251 /* This has to be 0 on the PowerStack? */
252 mbrp->mbrp_start = sa_htole32(0);
253 mbrp->mbrp_size = sa_htole32(2 * 18 * 80 - 1);
254
255 write(prep_fd, mbr, sizeof(mbr));
256 write(prep_fd, &entry, sizeof(entry));
257 write(prep_fd, &length, sizeof(length));
258 }
259
260 static int
261 prep_build_image(char *kernel, char *boot, char *rawdev, char *outname,
262 int lflag)
263 {
264 unsigned char *elf_img = NULL, *kern_img = NULL;
265 int i, ch, tmp, kgzlen, err;
266 int elf_fd, prep_fd, kern_fd, elf_img_len = 0;
267 off_t lenpos, kstart, kend;
268 unsigned long length;
269 long flength;
270 gzFile gzf;
271 struct stat kern_stat;
272 Elf32_External_Phdr phdr;
273
274 elf_fd = open_file("bootloader", boot, &hdr, &elf_stat);
275 kern_fd = open_file("kernel", kernel, &khdr, &kern_stat);
276 kern_len = kern_stat.st_size + PREP_MAGICSIZE + KERNLENSIZE;
277
278 for (i = 0; i < ELFGET16(hdr.e_phnum); i++) {
279 lseek(elf_fd, ELFGET32(hdr.e_phoff) + sizeof(phdr) * i,
280 SEEK_SET);
281 if (read(elf_fd, &phdr, sizeof(phdr)) != sizeof(phdr))
282 errx(3, "Can't read input '%s' phdr : %s", boot,
283 strerror(errno));
284
285 if ((ELFGET32(phdr.p_type) != PT_LOAD) ||
286 !(ELFGET32(phdr.p_flags) & PF_X))
287 continue;
288
289 fstat(elf_fd, &elf_stat);
290 elf_img_len = elf_stat.st_size - ELFGET32(phdr.p_offset);
291 lseek(elf_fd, ELFGET32(phdr.p_offset), SEEK_SET);
292
293 break;
294 }
295 if ((prep_fd = open(outname, O_RDWR|O_TRUNC, 0)) < 0) {
296 /* we couldn't open it, it must be new */
297 prep_fd = creat(outname, 0644);
298 if (prep_fd < 0)
299 errx(2, "Can't open output '%s': %s", outname,
300 strerror(errno));
301 }
302
303 prep_check_mbr(prep_fd, lflag, rawdev);
304
305 /* Set file pos. to 2nd sector where image will be written */
306 lseek(prep_fd, 0x400, SEEK_SET);
307
308 /* Copy boot image */
309 elf_img = (unsigned char *)malloc(elf_img_len);
310 if (!elf_img)
311 errx(3, "Can't malloc: %s", strerror(errno));
312 if (read(elf_fd, elf_img, elf_img_len) != elf_img_len)
313 errx(3, "Can't read file '%s' : %s", boot, strerror(errno));
314
315 write(prep_fd, elf_img, elf_img_len);
316 free(elf_img);
317
318 /* Copy kernel */
319 kern_img = (unsigned char *)malloc(kern_stat.st_size);
320
321 if (kern_img == NULL)
322 errx(3, "Can't malloc: %s", strerror(errno));
323
324 /* we need to jump back after having read the headers */
325 lseek(kern_fd, 0, SEEK_SET);
326 if (read(kern_fd, (void *)kern_img, kern_stat.st_size) !=
327 kern_stat.st_size)
328 errx(3, "Can't read kernel '%s' : %s", kernel, strerror(errno));
329
330 gzf = gzdopen(dup(prep_fd), "a");
331 if (gzf == NULL)
332 errx(3, "Can't init compression: %s", strerror(errno));
333 if (gzsetparams(gzf, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY) != Z_OK)
334 errx(3, "%s", gzerror(gzf, &err));
335
336 /* write a magic number and size before the kernel */
337 write(prep_fd, (void *)prep_magic, PREP_MAGICSIZE);
338 lenpos = lseek(prep_fd, 0, SEEK_CUR);
339 tmp = sa_htobe32(0);
340 write(prep_fd, (void *)&tmp, KERNLENSIZE);
341
342 /* write in the compressed kernel */
343 kstart = lseek(prep_fd, 0, SEEK_CUR);
344 kgzlen = gzwrite(gzf, kern_img, kern_stat.st_size);
345 gzclose(gzf);
346 kend = lseek(prep_fd, 0, SEEK_CUR);
347
348 /* jump back to the length position now that we know the length */
349 lseek(prep_fd, lenpos, SEEK_SET);
350 kgzlen = kend - kstart;
351 tmp = sa_htobe32(kgzlen);
352 write(prep_fd, (void *)&tmp, KERNLENSIZE);
353
354 length = sa_htole32(0x400 + elf_img_len + 8 + kgzlen);
355 lseek(prep_fd, sizeof(mbr) + 4, SEEK_SET);
356 write(prep_fd, &length, sizeof(length));
357
358 flength = 0x400 + elf_img_len + 8 + kgzlen;
359 if (lflag)
360 flength -= (5760 * 512);
361 else
362 flength -= (2880 * 512);
363 if (flength > 0 && !saloneflag)
364 fprintf(stderr, "%s: Image %s is %d bytes larger than single"
365 " floppy. Can only be used for netboot.\n", getprogname(),
366 outname, flength);
367
368 free(kern_img);
369 close(kern_fd);
370 close(prep_fd);
371 close(elf_fd);
372
373 return(0);
374 }
375
376 int
377 main(int argc, char **argv)
378 {
379 int ch, lfloppyflag=0;
380 char *kernel = NULL, *boot = NULL, *rawdev = NULL, *outname = NULL;
381 char *march = NULL;
382 #ifdef __NetBSD__
383 char machine_arch[SYS_NMLN];
384 int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
385 #endif
386
387 setprogname(argv[0]);
388 kern_len = 0;
389
390 while ((ch = getopt(argc, argv, "b:k:lm:r:s")) != -1)
391 switch (ch) {
392 case 'b':
393 boot = optarg;
394 break;
395 case 'k':
396 kernel = optarg;
397 break;
398 case 'l':
399 lfloppyflag = 1;
400 break;
401 case 'm':
402 march = optarg;
403 break;
404 case 'r':
405 rawdev = optarg;
406 break;
407 case 's':
408 saloneflag = 1;
409 break;
410 case '?':
411 default:
412 usage();
413 /* NOTREACHED */
414 }
415 argc -= optind;
416 argv += optind;
417
418 if (argc < 1)
419 usage();
420
421 if (kernel == NULL)
422 kernel = "/netbsd";
423
424 if (boot == NULL)
425 boot = "/usr/mdec/boot";
426
427 if (march == NULL) {
428 int i;
429 #ifdef __NetBSD__
430 size_t len = sizeof(machine_arch);
431
432 if (sysctl(mib, sizeof (mib) / sizeof (mib[0]), machine_arch,
433 &len, NULL, 0) != -1) {
434 for (i=0; sup_plats[i] != NULL; i++) {
435 if (strcmp(sup_plats[i], machine_arch) == 0) {
436 march = strdup(sup_plats[i]);
437 break;
438 }
439 }
440 }
441 if (march == NULL) {
442 #endif
443 fprintf(stderr, "You are not running this program on"
444 " the target machine. You must supply the\n"
445 "machine architecture with the -m flag\n");
446 fprintf(stderr, "Supported architectures: ");
447 for (i=0; sup_plats[i] != NULL; i++)
448 fprintf(stderr, " %s", sup_plats[i]);
449 fprintf(stderr, "\n\n");
450 usage();
451 #ifdef __NetBSD__
452 }
453 #endif
454 }
455
456 outname = argv[0];
457
458 if (strcmp(march, "prep") == 0)
459 return(prep_build_image(kernel, boot, rawdev, outname,
460 lfloppyflag));
461
462 return(0);
463 }
464