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.
7 #define _POSIX_SOURCE 1
11 #include <sys/types.h>
13 #include <sys/ioctl.h>
21 #include <minix/config.h>
22 #include <minix/const.h>
23 #include <minix/partition.h>
24 #include <minix/u64.h>
28 #define BOOTBLOCK 0 /* Of course */
29 #define SECTOR_SIZE 512 /* Disk sector size. */
30 #define RATIO(b) ((b)/SECTOR_SIZE)
31 #define SIGNATURE 0xAA55 /* Boot block signature. */
32 #define BOOT_MAX 64 /* Absolute maximum size of secondary boot */
33 #define SIGPOS 510 /* Where to put signature word. */
34 #define PARTPOS 446 /* Offset to the partition table in a master
38 #define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a)))
39 #define control(c) between('\0', (c), '\37')
41 #define BOOT_BLOCK_SIZE 1024
43 void report(char *label
)
44 /* installboot: label: No such file or directory */
46 fprintf(stderr
, "installboot: %s: %s\n", label
, strerror(errno
));
49 void fatal(char *label
)
55 char *basename(char *name
)
56 /* Return the last component of name, stripping trailing slashes from name.
57 * Precondition: name != "/". If name is prefixed by a label, then the
58 * label is copied to the basename too.
61 static char base
[IM_NAME_MAX
];
64 if ((p
= strchr(name
, ':')) != nil
) {
65 while (name
<= p
&& bp
< base
+ IM_NAME_MAX
- 1)
69 if ((p
= strrchr(name
, '/')) == nil
) { p
= name
; break; }
73 while (*p
!= 0 && bp
< base
+ IM_NAME_MAX
- 1) *bp
++ = *p
++;
78 void bread(FILE *f
, char *name
, void *buf
, size_t len
)
79 /* Read len bytes. Don't dare return without them. */
81 if (len
> 0 && fread(buf
, len
, 1, f
) != 1) {
82 if (ferror(f
)) fatal(name
);
83 fprintf(stderr
, "installboot: Unexpected EOF on %s\n", name
);
88 void bwrite(FILE *f
, char *name
, void *buf
, size_t len
)
90 if (len
> 0 && fwrite(buf
, len
, 1, f
) != 1) fatal(name
);
93 long total_text
= 0, total_data
= 0, total_bss
= 0;
96 void read_header(int talk
, char *proc
, FILE *procf
, struct image_header
*ihdr
)
97 /* Read the a.out header of a program and check it. If procf happens to be
98 * nil then the header is already in *image_hdr and need only be checked.
102 static int banner
= 0;
103 struct exec
*phdr
= &ihdr
->process
;
106 /* Header already present. */
109 memset(ihdr
, 0, sizeof(*ihdr
));
111 /* Put the basename of proc in the header. */
112 strncpy(ihdr
->name
, basename(proc
), IM_NAME_MAX
);
114 /* Read the header. */
115 n
= fread(phdr
, sizeof(char), A_MINHDR
, procf
);
116 if (ferror(procf
)) fatal(proc
);
119 if (n
< A_MINHDR
|| BADMAG(*phdr
)) {
120 fprintf(stderr
, "installboot: %s is not an executable\n", proc
);
124 /* Get the rest of the exec header. */
126 bread(procf
, proc
, ((char *) phdr
) + A_MINHDR
,
127 phdr
->a_hdrlen
- A_MINHDR
);
130 if (talk
&& !banner
) {
131 printf(" text data bss size\n");
136 printf(" %8ld %8ld %8ld %9ld %s\n",
137 phdr
->a_text
, phdr
->a_data
, phdr
->a_bss
,
138 phdr
->a_text
+ phdr
->a_data
+ phdr
->a_bss
, proc
);
140 total_text
+= phdr
->a_text
;
141 total_data
+= phdr
->a_data
;
142 total_bss
+= phdr
->a_bss
;
144 if (phdr
->a_cpu
== A_I8086
) {
145 long data
= phdr
->a_data
+ phdr
->a_bss
;
147 if (!(phdr
->a_flags
& A_SEP
)) data
+= phdr
->a_text
;
149 if (phdr
->a_text
>= 65536) big
|= 1;
150 if (data
>= 65536) big
|= 2;
154 "%s will crash, %s%s%s segment%s larger then 64K\n",
156 big
& 1 ? "text" : "",
157 big
== 3 ? " and " : "",
158 big
& 2 ? "data" : "",
159 big
== 3 ? "s are" : " is");
163 void padimage(char *image
, FILE *imagef
, int n
)
164 /* Add n zeros to image to pad it to a sector boundary. */
167 if (putc(0, imagef
) == EOF
) fatal(image
);
172 #define align(n) (((n) + ((SECTOR_SIZE) - 1)) & ~((SECTOR_SIZE) - 1))
174 void copyexec(char *proc
, FILE *procf
, char *image
, FILE *imagef
, long n
)
175 /* Copy n bytes from proc to image padded to fill a sector. */
179 /* Compute number of padding bytes. */
183 if ((c
= getc(procf
)) == EOF
) {
184 if (ferror(procf
)) fatal(proc
);
185 fprintf(stderr
, "installboot: premature EOF on %s\n",
189 if (putc(c
, imagef
) == EOF
) fatal(image
);
192 padimage(image
, imagef
, pad
);
195 void make_image(char *image
, char **procv
)
196 /* Collect a set of files in an image, each "segment" is nicely padded out
197 * to SECTOR_SIZE, so it may be read from disk into memory without trickery.
200 FILE *imagef
, *procf
;
203 struct image_header ihdr
;
209 if ((imagef
= fopen(image
, "w")) == nil
) fatal(image
);
211 for (procn
= 0; (proc
= *procv
++) != nil
; procn
++) {
212 /* Remove the label from the file name. */
213 if ((file
= strchr(proc
, ':')) != nil
) file
++; else file
= proc
;
215 /* Real files please, may need to seek. */
216 if (stat(file
, &st
) < 0
217 || (errno
= EISDIR
, !S_ISREG(st
.st_mode
))
218 || (procf
= fopen(file
, "r")) == nil
221 /* Read a.out header. */
222 read_header(1, proc
, procf
, &ihdr
);
227 /* The symbol table is always stripped off. */
228 ihdr
.process
.a_syms
= 0;
229 ihdr
.process
.a_flags
&= ~A_NSYM
;
231 /* Write header padded to fill a sector */
232 bwrite(imagef
, image
, &ihdr
, sizeof(ihdr
));
234 padimage(image
, imagef
, SECTOR_SIZE
- sizeof(ihdr
));
236 /* A page aligned executable needs the header in text. */
237 if (phdr
.a_flags
& A_PAL
) {
239 phdr
.a_text
+= phdr
.a_hdrlen
;
242 /* Copy text and data of proc to image. */
243 if (phdr
.a_flags
& A_SEP
) {
244 /* Separate I&D: pad text & data separately. */
246 copyexec(proc
, procf
, image
, imagef
, phdr
.a_text
);
247 copyexec(proc
, procf
, image
, imagef
, phdr
.a_data
);
249 /* Common I&D: keep text and data together. */
251 copyexec(proc
, procf
, image
, imagef
,
252 phdr
.a_text
+ phdr
.a_data
);
255 /* Done with proc. */
256 (void) fclose(procf
);
258 /* Done with image. */
260 if (fclose(imagef
) == EOF
) fatal(image
);
262 printf(" ------ ------ ------ -------\n");
263 printf(" %8ld %8ld %8ld %9ld total\n",
264 total_text
, total_data
, total_bss
,
265 total_text
+ total_data
+ total_bss
);
268 void extractexec(FILE *imagef
, char *image
, FILE *procf
, char *proc
,
269 long count
, off_t
*alen
)
270 /* Copy a segment of an executable. It is padded to a sector in image. */
272 char buf
[SECTOR_SIZE
];
275 bread(imagef
, image
, buf
, sizeof(buf
));
278 bwrite(procf
, proc
, buf
,
279 count
< sizeof(buf
) ? (size_t) count
: sizeof(buf
));
284 void extract_image(char *image
)
285 /* Extract the executables from an image. */
287 FILE *imagef
, *procf
;
290 struct image_header ihdr
;
292 char buf
[SECTOR_SIZE
];
294 if (stat(image
, &st
) < 0) fatal(image
);
296 /* Size of the image. */
297 len
= S_ISREG(st
.st_mode
) ? st
.st_size
: -1;
299 if ((imagef
= fopen(image
, "r")) == nil
) fatal(image
);
302 /* Extract a program, first sector is an extended header. */
303 bread(imagef
, image
, buf
, sizeof(buf
));
306 memcpy(&ihdr
, buf
, sizeof(ihdr
));
310 read_header(1, ihdr
.name
, nil
, &ihdr
);
312 if ((procf
= fopen(ihdr
.name
, "w")) == nil
) fatal(ihdr
.name
);
314 if (phdr
.a_flags
& A_PAL
) {
315 /* A page aligned process contains a header in text. */
316 phdr
.a_text
+= phdr
.a_hdrlen
;
318 bwrite(procf
, ihdr
.name
, &ihdr
.process
, phdr
.a_hdrlen
);
321 /* Extract text and data segments. */
322 if (phdr
.a_flags
& A_SEP
) {
323 extractexec(imagef
, image
, procf
, ihdr
.name
,
325 extractexec(imagef
, image
, procf
, ihdr
.name
,
328 extractexec(imagef
, image
, procf
, ihdr
.name
,
329 phdr
.a_text
+ phdr
.a_data
, &len
);
332 if (fclose(procf
) == EOF
) fatal(ihdr
.name
);
336 int rawfd
; /* File descriptor to open device. */
337 char *rawdev
; /* Name of device. */
339 void readblock(off_t blk
, char *buf
, int block_size
)
340 /* For rawfs, so that it can read blocks. */
344 if (lseek(rawfd
, blk
* block_size
, SEEK_SET
) < 0
345 || (n
= read(rawfd
, buf
, block_size
)) < 0
348 if (n
< block_size
) {
349 fprintf(stderr
, "installboot: Unexpected EOF on %s\n", rawdev
);
354 void writeblock(off_t blk
, char *buf
, int block_size
)
355 /* Add a function to write blocks for local use. */
357 if (lseek(rawfd
, blk
* block_size
, SEEK_SET
) < 0
358 || write(rawfd
, buf
, block_size
) < 0
362 int raw_install(char *file
, off_t
*start
, off_t
*len
, int block_size
)
363 /* Copy bootcode or an image to the boot device at the given absolute disk
364 * block number. This "raw" installation is used to place bootcode and
365 * image on a disk without a filesystem to make a simple boot disk. Useful
366 * in automated scripts for J. Random User.
367 * Note: *len == 0 when an image is read. It is set right afterwards.
370 static char buf
[_MAX_BLOCK_SIZE
]; /* Nonvolatile block buffer. */
373 unsigned long devsize
;
374 static int banner
= 0;
375 struct partition entry
;
377 /* See if the device has a maximum size. */
379 if (ioctl(rawfd
, DIOCGETP
, &entry
) == 0) devsize
= cv64ul(entry
.size
);
381 if ((f
= fopen(file
, "r")) == nil
) fatal(file
);
383 /* Copy sectors from file onto the boot device. */
386 int off
= sec
% RATIO(BOOT_BLOCK_SIZE
);
388 if (fread(buf
+ off
* SECTOR_SIZE
, 1, SECTOR_SIZE
, f
) == 0)
391 if (sec
>= devsize
) {
393 "installboot: %s can't be attached to %s\n",
398 if (off
== RATIO(BOOT_BLOCK_SIZE
) - 1) writeblock(sec
/ RATIO(BOOT_BLOCK_SIZE
), buf
, BOOT_BLOCK_SIZE
);
399 } while (++sec
!= *start
+ *len
);
401 if (ferror(f
)) fatal(file
);
404 /* Write a partial block, this may be the last image. */
405 if (sec
% RATIO(BOOT_BLOCK_SIZE
) != 0) writeblock(sec
/ RATIO(BOOT_BLOCK_SIZE
), buf
, BOOT_BLOCK_SIZE
);
408 printf(" sector length\n");
412 printf("%8ld%8ld %s\n", *start
, *len
, file
);
417 enum howto
{ FS
, BOOT
};
419 void make_bootable(enum howto how
, char *device
, char *bootblock
,
420 char *bootcode
, char **imagev
)
421 /* Install bootblock on the bootsector of device with the disk addresses to
422 * bootcode patched into the data segment of bootblock. "How" tells if there
423 * should or shoudn't be a file system on the disk. The images in the imagev
424 * vector are added to the end of the device.
427 char buf
[_MAX_BLOCK_SIZE
+ 256], *adrp
, *parmp
;
431 } bootaddr
[BOOT_MAX
+ 1], *bap
= bootaddr
;
433 struct image_header dummy
;
436 off_t sector
, max_sector
;
438 off_t addr
, fssize
, pos
, len
;
439 char *labels
, *label
, *image
;
443 /* Open device and set variables for readblock. */
444 if ((rawfd
= open(rawdev
= device
, O_RDWR
)) < 0) fatal(device
);
446 /* Read and check the superblock. */
447 fssize
= r_super(&block_size
);
453 "installboot: %s is not a Minix file system\n",
461 printf("%s contains a file system!\n", device
);
462 printf("Scribbling in 10 seconds");
463 for (s
= 0; s
< 10; s
++) {
470 fssize
= 1; /* Just a boot block. */
474 /* See if the boot code can be found on the file system. */
475 if ((ino
= r_lookup(ROOT_INO
, bootcode
)) == 0) {
476 if (errno
!= ENOENT
) fatal(bootcode
);
479 /* Boot code must be attached at the end. */
484 /* For a raw installation, we need to copy the boot code onto
485 * the device, so we need to look at the file to be copied.
487 if (stat(bootcode
, &st
) < 0) fatal(bootcode
);
489 if ((bootf
= fopen(bootcode
, "r")) == nil
) fatal(bootcode
);
491 /* Boot code is present in the file system. */
494 /* Get the header from the first block. */
495 if ((addr
= r_vir2abs((off_t
) 0)) == 0) {
496 boothdr
.a_magic
[0]= !A_MAGIC0
;
498 readblock(addr
, buf
, block_size
);
499 /* Must skip 16 bytes of 'boot' as that contains code. */
500 memcpy(&boothdr
, buf
+ 16, sizeof(struct exec
));
503 dummy
.process
= boothdr
;
505 /* See if it is an executable (read_header does the check). */
506 read_header(0, bootcode
, bootf
, &dummy
);
507 boothdr
= dummy
.process
;
509 if (bootf
!= nil
) fclose(bootf
);
511 /* Get all the sector addresses of the secondary boot code. */
512 max_sector
= (boothdr
.a_hdrlen
+ boothdr
.a_text
513 + boothdr
.a_data
+ SECTOR_SIZE
- 1) / SECTOR_SIZE
;
515 if (max_sector
> BOOT_MAX
* RATIO(block_size
)) {
516 fprintf(stderr
, "installboot: %s is way too big\n", bootcode
);
520 /* Determine the addresses to the boot code to be patched into the
523 bap
->count
= 0; /* Trick to get the address recording going. */
525 for (sector
= 0; sector
< max_sector
; sector
++) {
527 addr
= fssize
+ (sector
/ RATIO(block_size
));
529 if ((addr
= r_vir2abs(sector
/ RATIO(block_size
))) == 0) {
530 fprintf(stderr
, "installboot: %s has holes!\n",
534 addr
= (addr
* RATIO(block_size
)) + (sector
% RATIO(block_size
));
536 /* First address of the addresses array? */
537 if (bap
->count
== 0) bap
->address
= addr
;
539 /* Paste sectors together in a multisector read. */
540 if (bap
->address
+ bap
->count
== addr
)
549 (++bap
)->count
= 0; /* No more. */
551 /* Get the boot block and patch the pieces in. */
552 readblock(BOOTBLOCK
, buf
, BOOT_BLOCK_SIZE
);
554 if ((bootf
= fopen(bootblock
, "r")) == nil
) fatal(bootblock
);
556 read_header(0, bootblock
, bootf
, &dummy
);
557 boothdr
= dummy
.process
;
559 if (boothdr
.a_text
+ boothdr
.a_data
+
560 4 * (bap
- bootaddr
) + 1 > PARTPOS
) {
562 "installboot: %s + addresses to %s don't fit in the boot sector\n",
563 bootblock
, bootcode
);
565 "You can try copying/reinstalling %s to defragment it\n",
570 /* All checks out right. Read bootblock into the boot block! */
571 bread(bootf
, bootblock
, buf
, boothdr
.a_text
+ boothdr
.a_data
);
572 (void) fclose(bootf
);
574 /* Patch the addresses in. */
575 adrp
= buf
+ (int) (boothdr
.a_text
+ boothdr
.a_data
);
576 for (bap
= bootaddr
; bap
->count
!= 0; bap
++) {
578 *adrp
++= (bap
->address
>> 0) & 0xFF;
579 *adrp
++= (bap
->address
>> 8) & 0xFF;
580 *adrp
++= (bap
->address
>> 16) & 0xFF;
582 /* Zero count stops bootblock's reading loop. */
585 if (bap
> bootaddr
+1) {
586 printf("%s and %d addresses to %s patched into %s\n",
587 bootblock
, (int)(bap
- bootaddr
), bootcode
, device
);
590 /* Boot block signature. */
591 buf
[SIGPOS
+0]= (SIGNATURE
>> 0) & 0xFF;
592 buf
[SIGPOS
+1]= (SIGNATURE
>> 8) & 0xFF;
594 /* Sector 2 of the boot block is used for boot parameters, initially
595 * filled with null commands (newlines). Initialize it only if
598 for (parmp
= buf
+ SECTOR_SIZE
; parmp
< buf
+ 2*SECTOR_SIZE
; parmp
++) {
599 if (*imagev
!= nil
|| (control(*parmp
) && *parmp
!= '\n')) {
600 /* Param sector must be initialized. */
601 memset(buf
+ SECTOR_SIZE
, '\n', SECTOR_SIZE
);
606 /* Offset to the end of the file system to add boot code and images. */
607 pos
= fssize
* RATIO(block_size
);
610 /* Place the boot code onto the boot device. */
612 if (!raw_install(bootcode
, &pos
, &len
, block_size
)) {
615 "\t(Isn't there a copy of %s on %s that can be used?)\n",
622 parmp
= buf
+ SECTOR_SIZE
;
626 /* A boot only disk needs to have floppies swapped. */
628 "trailer()echo \\nInsert the root diskette then hit RETURN\\n\\w\\c\n");
629 parmp
+= strlen(parmp
);
632 while ((labels
= *imagev
++) != nil
) {
633 /* Place each kernel image on the boot device. */
635 if ((image
= strchr(labels
, ':')) != nil
)
640 "installboot: Only one image can be the default\n");
648 if (!raw_install(image
, &pos
, &len
, block_size
)) exit(1);
651 /* Let this image be the default. */
652 sprintf(parmp
, "image=%ld:%ld\n", pos
-len
, len
);
653 parmp
+= strlen(parmp
);
656 while (labels
!= nil
) {
657 /* Image is prefixed by a comma separated list of
658 * labels. Define functions to select label and image.
661 if ((labels
= strchr(labels
, ',')) != nil
) *labels
++ = 0;
664 "%s(%c){label=%s;image=%ld:%ld;echo %s kernel selected;menu}\n",
666 between('A', label
[0], 'Z')
667 ? label
[0]-'A'+'a' : label
[0],
668 label
, pos
-len
, len
, label
);
669 parmp
+= strlen(parmp
);
672 if (parmp
> buf
+ block_size
) {
674 "installboot: Out of parameter space, too many images\n");
678 /* Install boot block. */
679 writeblock((off_t
) BOOTBLOCK
, buf
, 1024);
681 if (pos
> fssize
* RATIO(block_size
)) {
682 /* Tell the total size of the data on the device. */
683 printf("%16ld (%ld kb) total\n", pos
,
684 (pos
+ RATIO(block_size
) - 1) / RATIO(block_size
));
688 void install_master(char *device
, char *masterboot
, char **guide
)
689 /* Booting a hard disk is a two stage process: The master bootstrap in sector
690 * 0 loads the bootstrap from sector 0 of the active partition which in turn
691 * starts the operating system. This code installs such a master bootstrap
692 * on a hard disk. If guide[0] is non-null then the master bootstrap is
693 * guided into booting a certain device.
699 static char buf
[_MAX_BLOCK_SIZE
];
702 if ((rawfd
= open(rawdev
= device
, O_RDWR
)) < 0) fatal(device
);
704 /* Open the master boot code. */
705 if ((masf
= fopen(masterboot
, "r")) == nil
) fatal(masterboot
);
707 /* See if the user is cloning a device. */
708 if (fstat(fileno(masf
), &st
) >=0 && S_ISBLK(st
.st_mode
))
711 /* Read and check header otherwise. */
712 struct image_header ihdr
;
714 read_header(1, masterboot
, masf
, &ihdr
);
715 size
= ihdr
.process
.a_text
+ ihdr
.process
.a_data
;
717 if (size
> PARTPOS
) {
718 fprintf(stderr
, "installboot: %s is too big\n", masterboot
);
722 /* Read the master boot block, patch it, write. */
723 readblock(BOOTBLOCK
, buf
, BOOT_BLOCK_SIZE
);
725 memset(buf
, 0, PARTPOS
);
726 (void) bread(masf
, masterboot
, buf
, size
);
728 if (guide
[0] != nil
) {
729 /* Fixate partition to boot. */
730 char *keys
= guide
[0];
731 char *logical
= guide
[1];
735 struct partition geometry
;
737 /* A string of digits to be seen as keystrokes. */
740 if (!between('0', keys
[i
], '9')) {
742 "installboot: bad guide keys '%s'\n",
746 } while (keys
[++i
] != 0);
748 if (size
+ i
+ 1 > PARTPOS
) {
750 "installboot: not enough space after '%s' for '%s'\n",
754 memcpy(buf
+ size
, keys
, i
);
758 if (logical
!= nil
) {
759 if ((logfd
= open(logical
, O_RDONLY
)) < 0
760 || ioctl(logfd
, DIOCGETP
, &geometry
) < 0
764 offset
= div64u(geometry
.base
, SECTOR_SIZE
);
765 if (size
+ 5 > PARTPOS
) {
767 "installboot: not enough space "
768 "after '%s' for '%s' and an offset "
770 masterboot
, keys
, logical
);
774 memcpy(buf
+size
+1, &offset
, 4);
778 /* Install signature. */
779 buf
[SIGPOS
+0]= (SIGNATURE
>> 0) & 0xFF;
780 buf
[SIGPOS
+1]= (SIGNATURE
>> 8) & 0xFF;
782 writeblock(BOOTBLOCK
, buf
, BOOT_BLOCK_SIZE
);
788 "Usage: installboot -i(mage) image kernel mm fs ... init\n"
789 " installboot -(e)x(tract) image\n"
790 " installboot -d(evice) device bootblock boot [image ...]\n"
791 " installboot -b(oot) device bootblock boot image ...\n"
792 " installboot -m(aster) device masterboot [keys [logical]]\n");
796 int isoption(char *option
, char *test
)
797 /* Check if the option argument is equals "test". Also accept -i as short
798 * for -image, and the special case -x for -extract.
801 if (strcmp(option
, test
) == 0) return 1;
802 if (option
[0] != '-' && strlen(option
) != 2) return 0;
803 if (option
[1] == test
[1]) return 1;
804 if (option
[1] == 'x' && test
[1] == 'e') return 1;
808 int main(int argc
, char **argv
)
810 if (argc
< 2) usage();
812 if (argc
>= 4 && isoption(argv
[1], "-image")) {
813 make_image(argv
[2], argv
+ 3);
815 if (argc
== 3 && isoption(argv
[1], "-extract")) {
816 extract_image(argv
[2]);
818 if (argc
>= 5 && isoption(argv
[1], "-device")) {
819 make_bootable(FS
, argv
[2], argv
[3], argv
[4], argv
+ 5);
821 if (argc
>= 6 && isoption(argv
[1], "-boot")) {
822 make_bootable(BOOT
, argv
[2], argv
[3], argv
[4], argv
+ 5);
824 if ((4 <= argc
&& argc
<= 6) && isoption(argv
[1], "-master")) {
825 install_master(argv
[2], argv
[3], argv
+ 4);
833 * $PchId: installboot.c,v 1.10 2000/08/13 22:07:50 philip Exp $