1 /* installboot 3.0 - Make a device bootable Author: Kees J. Bot
4 * Either make a device bootable or make an image from kernel, mm, fs, etc.
6 #define _POSIX_SOURCE 1
10 #include <sys/types.h>
12 #include <sys/ioctl.h>
20 #include <minix/config.h>
21 #include <minix/const.h>
22 #include <minix/partition.h>
23 #include <minix/u64.h>
27 #define BOOTBLOCK 0 /* Of course */
28 #define SECTOR_SIZE 512 /* Disk sector size. */
29 #define RATIO(b) ((b)/SECTOR_SIZE)
30 #define SIGNATURE 0xAA55 /* Boot block signature. */
31 #define BOOT_MAX 64 /* Absolute maximum size of secondary boot */
32 #define SIGPOS 510 /* Where to put signature word. */
33 #define PARTPOS 446 /* Offset to the partition table in a master
37 #define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a)))
38 #define control(c) between('\0', (c), '\37')
40 #define BOOT_BLOCK_SIZE 1024
42 static void report(const char *label
)
43 /* installboot: label: No such file or directory */
45 fprintf(stderr
, "installboot: %s: %s\n", label
, strerror(errno
));
48 static void fatal(const char *label
)
54 char *basename(char *name
)
55 /* Return the last component of name, stripping trailing slashes from name.
56 * Precondition: name != "/". If name is prefixed by a label, then the
57 * label is copied to the basename too.
60 static char base
[IM_NAME_MAX
];
63 if ((p
= strchr(name
, ':')) != NULL
) {
64 while (name
<= p
&& bp
< base
+ IM_NAME_MAX
- 1)
68 if ((p
= strrchr(name
, '/')) == NULL
) { p
= name
; break; }
72 while (*p
!= 0 && bp
< base
+ IM_NAME_MAX
- 1) *bp
++ = *p
++;
77 static void bread(FILE *f
, char *name
, void *buf
, size_t len
)
78 /* Read len bytes. Don't dare return without them. */
80 if (len
> 0 && fread(buf
, len
, 1, f
) != 1) {
81 if (ferror(f
)) fatal(name
);
82 fprintf(stderr
, "installboot: Unexpected EOF on %s\n", name
);
87 static void bwrite(FILE *f
, const char *name
, const void *buf
, size_t len
)
89 if (len
> 0 && fwrite(buf
, len
, 1, f
) != 1) fatal(name
);
92 long total_text
= 0, total_data
= 0, total_bss
= 0;
95 void read_header(int talk
, char *proc
, FILE *procf
, struct image_header
*ihdr
)
96 /* Read the a.out header of a program and check it. If procf happens to be
97 * NULL then the header is already in *image_hdr and need only be checked.
101 static int banner
= 0;
102 struct exec
*phdr
= &ihdr
->process
;
105 /* Header already present. */
108 memset(ihdr
, 0, sizeof(*ihdr
));
110 /* Put the basename of proc in the header. */
111 strncpy(ihdr
->name
, basename(proc
), IM_NAME_MAX
);
113 /* Read the header. */
114 n
= fread(phdr
, sizeof(char), A_MINHDR
, procf
);
115 if (ferror(procf
)) fatal(proc
);
118 if (n
< A_MINHDR
|| BADMAG(*phdr
)) {
119 fprintf(stderr
, "installboot: %s is not an executable\n", proc
);
123 /* Get the rest of the exec header. */
125 bread(procf
, proc
, ((char *) phdr
) + A_MINHDR
,
126 phdr
->a_hdrlen
- A_MINHDR
);
129 if (talk
&& !banner
) {
130 printf(" text data bss size\n");
135 printf(" %8ld %8ld %8ld %9ld %s\n",
136 phdr
->a_text
, phdr
->a_data
, phdr
->a_bss
,
137 phdr
->a_text
+ phdr
->a_data
+ phdr
->a_bss
, proc
);
139 total_text
+= phdr
->a_text
;
140 total_data
+= phdr
->a_data
;
141 total_bss
+= phdr
->a_bss
;
143 if (phdr
->a_cpu
== A_I8086
) {
144 long data
= phdr
->a_data
+ phdr
->a_bss
;
146 if (!(phdr
->a_flags
& A_SEP
)) data
+= phdr
->a_text
;
148 if (phdr
->a_text
>= 65536) big
|= 1;
149 if (data
>= 65536) big
|= 2;
153 "%s will crash, %s%s%s segment%s larger then 64K\n",
155 big
& 1 ? "text" : "",
156 big
== 3 ? " and " : "",
157 big
& 2 ? "data" : "",
158 big
== 3 ? "s are" : " is");
162 void padimage(const char *image
, FILE *imagef
, int n
)
163 /* Add n zeros to image to pad it to a sector boundary. */
166 if (putc(0, imagef
) == EOF
) fatal(image
);
171 #define align(n) (((n) + ((SECTOR_SIZE) - 1)) & ~((SECTOR_SIZE) - 1))
173 void copyexec(char *proc
, FILE *procf
, char *image
, FILE *imagef
, long n
)
174 /* Copy n bytes from proc to image padded to fill a sector. */
178 /* Compute number of padding bytes. */
182 if ((c
= getc(procf
)) == EOF
) {
183 if (ferror(procf
)) fatal(proc
);
184 fprintf(stderr
, "installboot: premature EOF on %s\n",
188 if (putc(c
, imagef
) == EOF
) fatal(image
);
191 padimage(image
, imagef
, pad
);
194 void make_image(char *image
, char **procv
)
195 /* Collect a set of files in an image, each "segment" is nicely padded out
196 * to SECTOR_SIZE, so it may be read from disk into memory without trickery.
199 FILE *imagef
, *procf
;
202 struct image_header ihdr
;
208 if ((imagef
= fopen(image
, "w")) == NULL
) fatal(image
);
210 for (procn
= 0; (proc
= *procv
++) != NULL
; procn
++) {
211 /* Remove the label from the file name. */
212 if ((file
= strchr(proc
, ':')) != NULL
) file
++; else file
= proc
;
214 /* Real files please, may need to seek. */
215 if (stat(file
, &st
) < 0
216 || (errno
= EISDIR
, !S_ISREG(st
.st_mode
))
217 || (procf
= fopen(file
, "r")) == NULL
220 /* Read a.out header. */
221 read_header(1, proc
, procf
, &ihdr
);
226 /* The symbol table is always stripped off. */
227 ihdr
.process
.a_syms
= 0;
228 ihdr
.process
.a_flags
&= ~A_NSYM
;
230 /* Write header padded to fill a sector */
231 bwrite(imagef
, image
, &ihdr
, sizeof(ihdr
));
233 padimage(image
, imagef
, SECTOR_SIZE
- sizeof(ihdr
));
235 /* A page aligned executable needs the header in text. */
236 if (phdr
.a_flags
& A_PAL
) {
238 phdr
.a_text
+= phdr
.a_hdrlen
;
241 /* Copy text and data of proc to image. */
242 if (phdr
.a_flags
& A_SEP
) {
243 /* Separate I&D: pad text & data separately. */
245 copyexec(proc
, procf
, image
, imagef
, phdr
.a_text
);
246 copyexec(proc
, procf
, image
, imagef
, phdr
.a_data
);
248 /* Common I&D: keep text and data together. */
250 copyexec(proc
, procf
, image
, imagef
,
251 phdr
.a_text
+ phdr
.a_data
);
254 /* Done with proc. */
255 (void) fclose(procf
);
257 /* Done with image. */
259 if (fclose(imagef
) == EOF
) fatal(image
);
261 printf(" ------ ------ ------ -------\n");
262 printf(" %8ld %8ld %8ld %9ld total\n",
263 total_text
, total_data
, total_bss
,
264 total_text
+ total_data
+ total_bss
);
267 void extractexec(FILE *imagef
, char *image
, FILE *procf
, char *proc
,
268 long count
, off_t
*alen
)
269 /* Copy a segment of an executable. It is padded to a sector in image. */
271 char buf
[SECTOR_SIZE
];
274 bread(imagef
, image
, buf
, sizeof(buf
));
277 bwrite(procf
, proc
, buf
,
278 count
< sizeof(buf
) ? (size_t) count
: sizeof(buf
));
283 void extract_image(char *image
)
284 /* Extract the executables from an image. */
286 FILE *imagef
, *procf
;
289 struct image_header ihdr
;
291 char buf
[SECTOR_SIZE
];
293 if (stat(image
, &st
) < 0) fatal(image
);
295 /* Size of the image. */
296 len
= S_ISREG(st
.st_mode
) ? st
.st_size
: -1;
298 if ((imagef
= fopen(image
, "r")) == NULL
) fatal(image
);
301 /* Extract a program, first sector is an extended header. */
302 bread(imagef
, image
, buf
, sizeof(buf
));
305 memcpy(&ihdr
, buf
, sizeof(ihdr
));
309 read_header(1, ihdr
.name
, NULL
, &ihdr
);
311 if ((procf
= fopen(ihdr
.name
, "w")) == NULL
) fatal(ihdr
.name
);
313 if (phdr
.a_flags
& A_PAL
) {
314 /* A page aligned process contains a header in text. */
315 phdr
.a_text
+= phdr
.a_hdrlen
;
317 bwrite(procf
, ihdr
.name
, &ihdr
.process
, phdr
.a_hdrlen
);
320 /* Extract text and data segments. */
321 if (phdr
.a_flags
& A_SEP
) {
322 extractexec(imagef
, image
, procf
, ihdr
.name
,
324 extractexec(imagef
, image
, procf
, ihdr
.name
,
327 extractexec(imagef
, image
, procf
, ihdr
.name
,
328 phdr
.a_text
+ phdr
.a_data
, &len
);
331 if (fclose(procf
) == EOF
) fatal(ihdr
.name
);
335 static int rawfd
; /* File descriptor to open device. */
336 static const char *rawdev
; /* Name of device. */
338 void readblock(off_t blk
, char *buf
, int block_size
)
339 /* For rawfs, so that it can read blocks. */
343 if (lseek(rawfd
, blk
* block_size
, SEEK_SET
) < 0
344 || (n
= read(rawfd
, buf
, block_size
)) < 0
347 if (n
< block_size
) {
348 fprintf(stderr
, "installboot: Unexpected EOF on %s\n", rawdev
);
353 void writeblock(off_t blk
, const char *buf
, int block_size
)
354 /* Add a function to write blocks for local use. */
356 if (lseek(rawfd
, blk
* block_size
, SEEK_SET
) < 0
357 || write(rawfd
, buf
, block_size
) < 0
361 int raw_install(char *file
, off_t
*start
, off_t
*len
, int block_size
)
362 /* Copy bootcode or an image to the boot device at the given absolute disk
363 * block number. This "raw" installation is used to place bootcode and
364 * image on a disk without a filesystem to make a simple boot disk. Useful
365 * in automated scripts for J. Random User.
366 * Note: *len == 0 when an image is read. It is set right afterwards.
369 static char buf
[_MAX_BLOCK_SIZE
]; /* Nonvolatile block buffer. */
372 unsigned long devsize
;
373 static int banner
= 0;
374 struct partition entry
;
376 /* See if the device has a maximum size. */
378 if (ioctl(rawfd
, DIOCGETP
, &entry
) == 0) devsize
= cv64ul(entry
.size
);
380 if ((f
= fopen(file
, "r")) == NULL
) fatal(file
);
382 /* Copy sectors from file onto the boot device. */
385 int off
= sec
% RATIO(BOOT_BLOCK_SIZE
);
387 if (fread(buf
+ off
* SECTOR_SIZE
, 1, SECTOR_SIZE
, f
) == 0)
390 if (sec
>= devsize
) {
392 "installboot: %s can't be attached to %s\n",
397 if (off
== RATIO(BOOT_BLOCK_SIZE
) - 1) writeblock(sec
/ RATIO(BOOT_BLOCK_SIZE
), buf
, BOOT_BLOCK_SIZE
);
398 } while (++sec
!= *start
+ *len
);
400 if (ferror(f
)) fatal(file
);
403 /* Write a partial block, this may be the last image. */
404 if (sec
% RATIO(BOOT_BLOCK_SIZE
) != 0) writeblock(sec
/ RATIO(BOOT_BLOCK_SIZE
), buf
, BOOT_BLOCK_SIZE
);
407 printf(" sector length\n");
411 printf("%8ld%8ld %s\n", *start
, *len
, file
);
416 enum howto
{ FS
, BOOT
};
418 void make_bootable(enum howto how
, char *device
, char *bootblock
,
419 char *bootcode
, char **imagev
)
420 /* Install bootblock on the bootsector of device with the disk addresses to
421 * bootcode patched into the data segment of bootblock. "How" tells if there
422 * should or shoudn't be a file system on the disk. The images in the imagev
423 * vector are added to the end of the device.
426 char buf
[_MAX_BLOCK_SIZE
+ 256], *adrp
, *parmp
;
430 } bootaddr
[BOOT_MAX
+ 1], *bap
= bootaddr
;
432 struct image_header dummy
;
435 off_t sector
, max_sector
;
437 off_t addr
, fssize
, pos
, len
;
438 char *labels
, *label
, *image
;
442 /* Open device and set variables for readblock. */
443 if ((rawfd
= open(rawdev
= device
, O_RDWR
)) < 0) fatal(device
);
445 /* Read and check the superblock. */
446 fssize
= r_super(&block_size
);
452 "installboot: %s is not a Minix file system\n",
460 printf("%s contains a file system!\n", device
);
461 printf("Scribbling in 10 seconds");
462 for (s
= 0; s
< 10; s
++) {
469 fssize
= 1; /* Just a boot block. */
473 /* See if the boot code can be found on the file system. */
474 if ((ino
= r_lookup(ROOT_INO
, bootcode
)) == 0) {
475 if (errno
!= ENOENT
) fatal(bootcode
);
478 /* Boot code must be attached at the end. */
483 /* For a raw installation, we need to copy the boot code onto
484 * the device, so we need to look at the file to be copied.
486 if (stat(bootcode
, &st
) < 0) fatal(bootcode
);
488 if ((bootf
= fopen(bootcode
, "r")) == NULL
) fatal(bootcode
);
490 /* Boot code is present in the file system. */
493 /* Get the header from the first block. */
494 if ((addr
= r_vir2abs((off_t
) 0)) == 0) {
495 boothdr
.a_magic
[0]= !A_MAGIC0
;
497 readblock(addr
, buf
, block_size
);
498 /* Must skip 16 bytes of 'boot' as that contains code. */
499 memcpy(&boothdr
, buf
+ 16, sizeof(struct exec
));
502 dummy
.process
= boothdr
;
504 /* See if it is an executable (read_header does the check). */
505 read_header(0, bootcode
, bootf
, &dummy
);
506 boothdr
= dummy
.process
;
508 if (bootf
!= NULL
) fclose(bootf
);
510 /* Get all the sector addresses of the secondary boot code. */
511 max_sector
= (boothdr
.a_hdrlen
+ boothdr
.a_text
512 + boothdr
.a_data
+ SECTOR_SIZE
- 1) / SECTOR_SIZE
;
514 if (max_sector
> BOOT_MAX
* RATIO(block_size
)) {
515 fprintf(stderr
, "installboot: %s is way too big\n", bootcode
);
519 /* Determine the addresses to the boot code to be patched into the
522 bap
->count
= 0; /* Trick to get the address recording going. */
524 for (sector
= 0; sector
< max_sector
; sector
++) {
526 addr
= fssize
+ (sector
/ RATIO(block_size
));
528 if ((addr
= r_vir2abs(sector
/ RATIO(block_size
))) == 0) {
529 fprintf(stderr
, "installboot: %s has holes!\n",
533 addr
= (addr
* RATIO(block_size
)) + (sector
% RATIO(block_size
));
535 /* First address of the addresses array? */
536 if (bap
->count
== 0) bap
->address
= addr
;
538 /* Paste sectors together in a multisector read. */
539 if (bap
->address
+ bap
->count
== addr
)
548 (++bap
)->count
= 0; /* No more. */
550 /* Get the boot block and patch the pieces in. */
551 readblock(BOOTBLOCK
, buf
, BOOT_BLOCK_SIZE
);
553 if ((bootf
= fopen(bootblock
, "r")) == NULL
) fatal(bootblock
);
555 read_header(0, bootblock
, bootf
, &dummy
);
556 boothdr
= dummy
.process
;
558 if (boothdr
.a_text
+ boothdr
.a_data
+
559 4 * (bap
- bootaddr
) + 1 > PARTPOS
) {
561 "installboot: %s + addresses to %s don't fit in the boot sector\n",
562 bootblock
, bootcode
);
564 "You can try copying/reinstalling %s to defragment it\n",
569 /* All checks out right. Read bootblock into the boot block! */
570 bread(bootf
, bootblock
, buf
, boothdr
.a_text
+ boothdr
.a_data
);
571 (void) fclose(bootf
);
573 /* Patch the addresses in. */
574 adrp
= buf
+ (int) (boothdr
.a_text
+ boothdr
.a_data
);
575 for (bap
= bootaddr
; bap
->count
!= 0; bap
++) {
577 *adrp
++= (bap
->address
>> 0) & 0xFF;
578 *adrp
++= (bap
->address
>> 8) & 0xFF;
579 *adrp
++= (bap
->address
>> 16) & 0xFF;
581 /* Zero count stops bootblock's reading loop. */
584 if (bap
> bootaddr
+1) {
585 printf("%s and %d addresses to %s patched into %s\n",
586 bootblock
, (int)(bap
- bootaddr
), bootcode
, device
);
589 /* Boot block signature. */
590 buf
[SIGPOS
+0]= (SIGNATURE
>> 0) & 0xFF;
591 buf
[SIGPOS
+1]= (SIGNATURE
>> 8) & 0xFF;
593 /* Sector 2 of the boot block is used for boot parameters, initially
594 * filled with null commands (newlines). Initialize it only if
597 for (parmp
= buf
+ SECTOR_SIZE
; parmp
< buf
+ 2*SECTOR_SIZE
; parmp
++) {
598 if (*imagev
!= NULL
|| (control(*parmp
) && *parmp
!= '\n')) {
599 /* Param sector must be initialized. */
600 memset(buf
+ SECTOR_SIZE
, '\n', SECTOR_SIZE
);
605 /* Offset to the end of the file system to add boot code and images. */
606 pos
= fssize
* RATIO(block_size
);
609 /* Place the boot code onto the boot device. */
611 if (!raw_install(bootcode
, &pos
, &len
, block_size
)) {
614 "\t(Isn't there a copy of %s on %s that can be used?)\n",
621 parmp
= buf
+ SECTOR_SIZE
;
625 /* A boot only disk needs to have floppies swapped. */
627 "trailer()echo \\nInsert the root diskette then hit RETURN\\n\\w\\c\n");
628 parmp
+= strlen(parmp
);
631 while ((labels
= *imagev
++) != NULL
) {
632 /* Place each kernel image on the boot device. */
634 if ((image
= strchr(labels
, ':')) != NULL
)
639 "installboot: Only one image can be the default\n");
647 if (!raw_install(image
, &pos
, &len
, block_size
)) exit(1);
649 if (labels
== NULL
) {
650 /* Let this image be the default. */
651 sprintf(parmp
, "image=%ld:%ld\n", pos
-len
, len
);
652 parmp
+= strlen(parmp
);
655 while (labels
!= NULL
) {
656 /* Image is prefixed by a comma separated list of
657 * labels. Define functions to select label and image.
660 if ((labels
= strchr(labels
, ',')) != NULL
) *labels
++ = 0;
663 "%s(%c){label=%s;image=%ld:%ld;echo %s kernel selected;menu}\n",
665 between('A', label
[0], 'Z')
666 ? label
[0]-'A'+'a' : label
[0],
667 label
, pos
-len
, len
, label
);
668 parmp
+= strlen(parmp
);
671 if (parmp
> buf
+ block_size
) {
673 "installboot: Out of parameter space, too many images\n");
677 /* Install boot block. */
678 writeblock((off_t
) BOOTBLOCK
, buf
, 1024);
680 if (pos
> fssize
* RATIO(block_size
)) {
681 /* Tell the total size of the data on the device. */
682 printf("%16ld (%ld kb) total\n", pos
,
683 (pos
+ RATIO(block_size
) - 1) / RATIO(block_size
));
687 static void install_master(const char *device
, char *masterboot
, char **guide
)
688 /* Booting a hard disk is a two stage process: The master bootstrap in sector
689 * 0 loads the bootstrap from sector 0 of the active partition which in turn
690 * starts the operating system. This code installs such a master bootstrap
691 * on a hard disk. If guide[0] is non-null then the master bootstrap is
692 * guided into booting a certain device.
698 static char buf
[_MAX_BLOCK_SIZE
];
701 if ((rawfd
= open(rawdev
= device
, O_RDWR
)) < 0) fatal(device
);
703 /* Open the master boot code. */
704 if ((masf
= fopen(masterboot
, "r")) == NULL
) fatal(masterboot
);
706 /* See if the user is cloning a device. */
707 if (fstat(fileno(masf
), &st
) >=0 && S_ISBLK(st
.st_mode
))
710 /* Read and check header otherwise. */
711 struct image_header ihdr
;
713 read_header(1, masterboot
, masf
, &ihdr
);
714 size
= ihdr
.process
.a_text
+ ihdr
.process
.a_data
;
716 if (size
> PARTPOS
) {
717 fprintf(stderr
, "installboot: %s is too big\n", masterboot
);
721 /* Read the master boot block, patch it, write. */
722 readblock(BOOTBLOCK
, buf
, BOOT_BLOCK_SIZE
);
724 memset(buf
, 0, PARTPOS
);
725 (void) bread(masf
, masterboot
, buf
, size
);
727 if (guide
[0] != NULL
) {
728 /* Fixate partition to boot. */
729 const char *keys
= guide
[0];
730 const char *logical
= guide
[1];
734 struct partition geometry
;
736 /* A string of digits to be seen as keystrokes. */
739 if (!between('0', keys
[i
], '9')) {
741 "installboot: bad guide keys '%s'\n",
745 } while (keys
[++i
] != 0);
747 if (size
+ i
+ 1 > PARTPOS
) {
749 "installboot: not enough space after '%s' for '%s'\n",
753 memcpy(buf
+ size
, keys
, i
);
757 if (logical
!= NULL
) {
758 if ((logfd
= open(logical
, O_RDONLY
)) < 0
759 || ioctl(logfd
, DIOCGETP
, &geometry
) < 0
763 offset
= div64u(geometry
.base
, SECTOR_SIZE
);
764 if (size
+ 5 > PARTPOS
) {
766 "installboot: not enough space "
767 "after '%s' for '%s' and an offset "
769 masterboot
, keys
, logical
);
773 memcpy(buf
+size
+1, &offset
, 4);
777 /* Install signature. */
778 buf
[SIGPOS
+0]= (SIGNATURE
>> 0) & 0xFF;
779 buf
[SIGPOS
+1]= (SIGNATURE
>> 8) & 0xFF;
781 writeblock(BOOTBLOCK
, buf
, BOOT_BLOCK_SIZE
);
784 static void usage(void)
787 "Usage: installboot -i(mage) image kernel mm fs ... init\n"
788 " installboot -(e)x(tract) image\n"
789 " installboot -d(evice) device bootblock boot [image ...]\n"
790 " installboot -b(oot) device bootblock boot image ...\n"
791 " installboot -m(aster) device masterboot [keys [logical]]\n");
795 static int isoption(const char *option
, const char *test
)
796 /* Check if the option argument is equals "test". Also accept -i as short
797 * for -image, and the special case -x for -extract.
800 if (strcmp(option
, test
) == 0) return 1;
801 if (option
[0] != '-' && strlen(option
) != 2) return 0;
802 if (option
[1] == test
[1]) return 1;
803 if (option
[1] == 'x' && test
[1] == 'e') return 1;
807 int main(int argc
, char **argv
)
809 if (argc
< 2) usage();
811 if (argc
>= 4 && isoption(argv
[1], "-image")) {
812 make_image(argv
[2], argv
+ 3);
814 if (argc
== 3 && isoption(argv
[1], "-extract")) {
815 extract_image(argv
[2]);
817 if (argc
>= 5 && isoption(argv
[1], "-device")) {
818 make_bootable(FS
, argv
[2], argv
[3], argv
[4], argv
+ 5);
820 if (argc
>= 6 && isoption(argv
[1], "-boot")) {
821 make_bootable(BOOT
, argv
[2], argv
[3], argv
[4], argv
+ 5);
823 if ((4 <= argc
&& argc
<= 6) && isoption(argv
[1], "-master")) {
824 install_master(argv
[2], argv
[3], argv
+ 4);
832 * $PchId: installboot.c,v 1.10 2000/08/13 22:07:50 philip Exp $