[9] | 1 | /* installboot 3.0 - Make a device bootable Author: Kees J. Bot
|
---|
| 2 | * 21 Dec 1991
|
---|
| 3 | *
|
---|
| 4 | * Either make a device bootable or make an image from kernel, mm, fs, etc.
|
---|
| 5 | */
|
---|
| 6 | #define nil 0
|
---|
| 7 | #define _POSIX_SOURCE 1
|
---|
| 8 | #define _MINIX 1
|
---|
| 9 | #include <stdio.h>
|
---|
| 10 | #include <stddef.h>
|
---|
| 11 | #include <sys/types.h>
|
---|
| 12 | #include <sys/stat.h>
|
---|
| 13 | #include <sys/ioctl.h>
|
---|
| 14 | #include <stdlib.h>
|
---|
| 15 | #include <unistd.h>
|
---|
| 16 | #include <fcntl.h>
|
---|
| 17 | #include <string.h>
|
---|
| 18 | #include <errno.h>
|
---|
| 19 | #include <dirent.h>
|
---|
| 20 | #include <a.out.h>
|
---|
| 21 | #include <minix/config.h>
|
---|
| 22 | #include <minix/const.h>
|
---|
| 23 | #include <minix/partition.h>
|
---|
| 24 | #include <minix/u64.h>
|
---|
| 25 | #include "rawfs.h"
|
---|
| 26 | #include "image.h"
|
---|
| 27 |
|
---|
| 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
|
---|
| 35 | * boot block.
|
---|
| 36 | */
|
---|
| 37 |
|
---|
| 38 | #define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a)))
|
---|
| 39 | #define control(c) between('\0', (c), '\37')
|
---|
| 40 |
|
---|
| 41 | #define BOOT_BLOCK_SIZE 1024
|
---|
| 42 |
|
---|
| 43 | void report(char *label)
|
---|
| 44 | /* installboot: label: No such file or directory */
|
---|
| 45 | {
|
---|
| 46 | fprintf(stderr, "installboot: %s: %s\n", label, strerror(errno));
|
---|
| 47 | }
|
---|
| 48 |
|
---|
| 49 | void fatal(char *label)
|
---|
| 50 | {
|
---|
| 51 | report(label);
|
---|
| 52 | exit(1);
|
---|
| 53 | }
|
---|
| 54 |
|
---|
| 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.
|
---|
| 59 | */
|
---|
| 60 | {
|
---|
| 61 | static char base[IM_NAME_MAX];
|
---|
| 62 | char *p, *bp= base;
|
---|
| 63 |
|
---|
| 64 | if ((p= strchr(name, ':')) != nil) {
|
---|
| 65 | while (name <= p && bp < base + IM_NAME_MAX - 1)
|
---|
| 66 | *bp++ = *name++;
|
---|
| 67 | }
|
---|
| 68 | for (;;) {
|
---|
| 69 | if ((p= strrchr(name, '/')) == nil) { p= name; break; }
|
---|
| 70 | if (*++p != 0) break;
|
---|
| 71 | *--p= 0;
|
---|
| 72 | }
|
---|
| 73 | while (*p != 0 && bp < base + IM_NAME_MAX - 1) *bp++ = *p++;
|
---|
| 74 | *bp= 0;
|
---|
| 75 | return base;
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | void bread(FILE *f, char *name, void *buf, size_t len)
|
---|
| 79 | /* Read len bytes. Don't dare return without them. */
|
---|
| 80 | {
|
---|
| 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);
|
---|
| 84 | exit(1);
|
---|
| 85 | }
|
---|
| 86 | }
|
---|
| 87 |
|
---|
| 88 | void bwrite(FILE *f, char *name, void *buf, size_t len)
|
---|
| 89 | {
|
---|
| 90 | if (len > 0 && fwrite(buf, len, 1, f) != 1) fatal(name);
|
---|
| 91 | }
|
---|
| 92 |
|
---|
| 93 | long total_text= 0, total_data= 0, total_bss= 0;
|
---|
| 94 | int making_image= 0;
|
---|
| 95 |
|
---|
| 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.
|
---|
| 99 | */
|
---|
| 100 | {
|
---|
| 101 | int n, big= 0;
|
---|
| 102 | static int banner= 0;
|
---|
| 103 | struct exec *phdr= &ihdr->process;
|
---|
| 104 |
|
---|
| 105 | if (procf == nil) {
|
---|
| 106 | /* Header already present. */
|
---|
| 107 | n= phdr->a_hdrlen;
|
---|
| 108 | } else {
|
---|
| 109 | memset(ihdr, 0, sizeof(*ihdr));
|
---|
| 110 |
|
---|
| 111 | /* Put the basename of proc in the header. */
|
---|
| 112 | strncpy(ihdr->name, basename(proc), IM_NAME_MAX);
|
---|
| 113 |
|
---|
| 114 | /* Read the header. */
|
---|
| 115 | n= fread(phdr, sizeof(char), A_MINHDR, procf);
|
---|
| 116 | if (ferror(procf)) fatal(proc);
|
---|
| 117 | }
|
---|
| 118 |
|
---|
| 119 | if (n < A_MINHDR || BADMAG(*phdr)) {
|
---|
| 120 | fprintf(stderr, "installboot: %s is not an executable\n", proc);
|
---|
| 121 | exit(1);
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | /* Get the rest of the exec header. */
|
---|
| 125 | if (procf != nil) {
|
---|
| 126 | bread(procf, proc, ((char *) phdr) + A_MINHDR,
|
---|
| 127 | phdr->a_hdrlen - A_MINHDR);
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 | if (talk && !banner) {
|
---|
| 131 | printf(" text data bss size\n");
|
---|
| 132 | banner= 1;
|
---|
| 133 | }
|
---|
| 134 |
|
---|
| 135 | if (talk) {
|
---|
| 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);
|
---|
| 139 | }
|
---|
| 140 | total_text+= phdr->a_text;
|
---|
| 141 | total_data+= phdr->a_data;
|
---|
| 142 | total_bss+= phdr->a_bss;
|
---|
| 143 |
|
---|
| 144 | if (phdr->a_cpu == A_I8086) {
|
---|
| 145 | long data= phdr->a_data + phdr->a_bss;
|
---|
| 146 |
|
---|
| 147 | if (!(phdr->a_flags & A_SEP)) data+= phdr->a_text;
|
---|
| 148 |
|
---|
| 149 | if (phdr->a_text >= 65536) big|= 1;
|
---|
| 150 | if (data >= 65536) big|= 2;
|
---|
| 151 | }
|
---|
| 152 | if (big) {
|
---|
| 153 | fprintf(stderr,
|
---|
| 154 | "%s will crash, %s%s%s segment%s larger then 64K\n",
|
---|
| 155 | proc,
|
---|
| 156 | big & 1 ? "text" : "",
|
---|
| 157 | big == 3 ? " and " : "",
|
---|
| 158 | big & 2 ? "data" : "",
|
---|
| 159 | big == 3 ? "s are" : " is");
|
---|
| 160 | }
|
---|
| 161 | }
|
---|
| 162 |
|
---|
| 163 | void padimage(char *image, FILE *imagef, int n)
|
---|
| 164 | /* Add n zeros to image to pad it to a sector boundary. */
|
---|
| 165 | {
|
---|
| 166 | while (n > 0) {
|
---|
| 167 | if (putc(0, imagef) == EOF) fatal(image);
|
---|
| 168 | n--;
|
---|
| 169 | }
|
---|
| 170 | }
|
---|
| 171 |
|
---|
| 172 | #define align(n) (((n) + ((SECTOR_SIZE) - 1)) & ~((SECTOR_SIZE) - 1))
|
---|
| 173 |
|
---|
| 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. */
|
---|
| 176 | {
|
---|
| 177 | int pad, c;
|
---|
| 178 |
|
---|
| 179 | /* Compute number of padding bytes. */
|
---|
| 180 | pad= align(n) - n;
|
---|
| 181 |
|
---|
| 182 | while (n > 0) {
|
---|
| 183 | if ((c= getc(procf)) == EOF) {
|
---|
| 184 | if (ferror(procf)) fatal(proc);
|
---|
| 185 | fprintf(stderr, "installboot: premature EOF on %s\n",
|
---|
| 186 | proc);
|
---|
| 187 | exit(1);
|
---|
| 188 | }
|
---|
| 189 | if (putc(c, imagef) == EOF) fatal(image);
|
---|
| 190 | n--;
|
---|
| 191 | }
|
---|
| 192 | padimage(image, imagef, pad);
|
---|
| 193 | }
|
---|
| 194 |
|
---|
| 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.
|
---|
| 198 | */
|
---|
| 199 | {
|
---|
| 200 | FILE *imagef, *procf;
|
---|
| 201 | char *proc, *file;
|
---|
| 202 | int procn;
|
---|
| 203 | struct image_header ihdr;
|
---|
| 204 | struct exec phdr;
|
---|
| 205 | struct stat st;
|
---|
| 206 |
|
---|
| 207 | making_image= 1;
|
---|
| 208 |
|
---|
| 209 | if ((imagef= fopen(image, "w")) == nil) fatal(image);
|
---|
| 210 |
|
---|
| 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;
|
---|
| 214 |
|
---|
| 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
|
---|
| 219 | ) fatal(proc);
|
---|
| 220 |
|
---|
| 221 | /* Read a.out header. */
|
---|
| 222 | read_header(1, proc, procf, &ihdr);
|
---|
| 223 |
|
---|
| 224 | /* Scratch. */
|
---|
| 225 | phdr= ihdr.process;
|
---|
| 226 |
|
---|
| 227 | /* The symbol table is always stripped off. */
|
---|
| 228 | ihdr.process.a_syms= 0;
|
---|
| 229 | ihdr.process.a_flags &= ~A_NSYM;
|
---|
| 230 |
|
---|
| 231 | /* Write header padded to fill a sector */
|
---|
| 232 | bwrite(imagef, image, &ihdr, sizeof(ihdr));
|
---|
| 233 |
|
---|
| 234 | padimage(image, imagef, SECTOR_SIZE - sizeof(ihdr));
|
---|
| 235 |
|
---|
| 236 | /* A page aligned executable needs the header in text. */
|
---|
| 237 | if (phdr.a_flags & A_PAL) {
|
---|
| 238 | rewind(procf);
|
---|
| 239 | phdr.a_text+= phdr.a_hdrlen;
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | /* Copy text and data of proc to image. */
|
---|
| 243 | if (phdr.a_flags & A_SEP) {
|
---|
| 244 | /* Separate I&D: pad text & data separately. */
|
---|
| 245 |
|
---|
| 246 | copyexec(proc, procf, image, imagef, phdr.a_text);
|
---|
| 247 | copyexec(proc, procf, image, imagef, phdr.a_data);
|
---|
| 248 | } else {
|
---|
| 249 | /* Common I&D: keep text and data together. */
|
---|
| 250 |
|
---|
| 251 | copyexec(proc, procf, image, imagef,
|
---|
| 252 | phdr.a_text + phdr.a_data);
|
---|
| 253 | }
|
---|
| 254 |
|
---|
| 255 | /* Done with proc. */
|
---|
| 256 | (void) fclose(procf);
|
---|
| 257 | }
|
---|
| 258 | /* Done with image. */
|
---|
| 259 |
|
---|
| 260 | if (fclose(imagef) == EOF) fatal(image);
|
---|
| 261 |
|
---|
| 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);
|
---|
| 266 | }
|
---|
| 267 |
|
---|
| 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. */
|
---|
| 271 | {
|
---|
| 272 | char buf[SECTOR_SIZE];
|
---|
| 273 |
|
---|
| 274 | while (count > 0) {
|
---|
| 275 | bread(imagef, image, buf, sizeof(buf));
|
---|
| 276 | *alen-= sizeof(buf);
|
---|
| 277 |
|
---|
| 278 | bwrite(procf, proc, buf,
|
---|
| 279 | count < sizeof(buf) ? (size_t) count : sizeof(buf));
|
---|
| 280 | count-= sizeof(buf);
|
---|
| 281 | }
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 | void extract_image(char *image)
|
---|
| 285 | /* Extract the executables from an image. */
|
---|
| 286 | {
|
---|
| 287 | FILE *imagef, *procf;
|
---|
| 288 | off_t len;
|
---|
| 289 | struct stat st;
|
---|
| 290 | struct image_header ihdr;
|
---|
| 291 | struct exec phdr;
|
---|
| 292 | char buf[SECTOR_SIZE];
|
---|
| 293 |
|
---|
| 294 | if (stat(image, &st) < 0) fatal(image);
|
---|
| 295 |
|
---|
| 296 | /* Size of the image. */
|
---|
| 297 | len= S_ISREG(st.st_mode) ? st.st_size : -1;
|
---|
| 298 |
|
---|
| 299 | if ((imagef= fopen(image, "r")) == nil) fatal(image);
|
---|
| 300 |
|
---|
| 301 | while (len != 0) {
|
---|
| 302 | /* Extract a program, first sector is an extended header. */
|
---|
| 303 | bread(imagef, image, buf, sizeof(buf));
|
---|
| 304 | len-= sizeof(buf);
|
---|
| 305 |
|
---|
| 306 | memcpy(&ihdr, buf, sizeof(ihdr));
|
---|
| 307 | phdr= ihdr.process;
|
---|
| 308 |
|
---|
| 309 | /* Check header. */
|
---|
| 310 | read_header(1, ihdr.name, nil, &ihdr);
|
---|
| 311 |
|
---|
| 312 | if ((procf= fopen(ihdr.name, "w")) == nil) fatal(ihdr.name);
|
---|
| 313 |
|
---|
| 314 | if (phdr.a_flags & A_PAL) {
|
---|
| 315 | /* A page aligned process contains a header in text. */
|
---|
| 316 | phdr.a_text+= phdr.a_hdrlen;
|
---|
| 317 | } else {
|
---|
| 318 | bwrite(procf, ihdr.name, &ihdr.process, phdr.a_hdrlen);
|
---|
| 319 | }
|
---|
| 320 |
|
---|
| 321 | /* Extract text and data segments. */
|
---|
| 322 | if (phdr.a_flags & A_SEP) {
|
---|
| 323 | extractexec(imagef, image, procf, ihdr.name,
|
---|
| 324 | phdr.a_text, &len);
|
---|
| 325 | extractexec(imagef, image, procf, ihdr.name,
|
---|
| 326 | phdr.a_data, &len);
|
---|
| 327 | } else {
|
---|
| 328 | extractexec(imagef, image, procf, ihdr.name,
|
---|
| 329 | phdr.a_text + phdr.a_data, &len);
|
---|
| 330 | }
|
---|
| 331 |
|
---|
| 332 | if (fclose(procf) == EOF) fatal(ihdr.name);
|
---|
| 333 | }
|
---|
| 334 | }
|
---|
| 335 |
|
---|
| 336 | int rawfd; /* File descriptor to open device. */
|
---|
| 337 | char *rawdev; /* Name of device. */
|
---|
| 338 |
|
---|
| 339 | void readblock(off_t blk, char *buf, int block_size)
|
---|
| 340 | /* For rawfs, so that it can read blocks. */
|
---|
| 341 | {
|
---|
| 342 | int n;
|
---|
| 343 |
|
---|
| 344 | if (lseek(rawfd, blk * block_size, SEEK_SET) < 0
|
---|
| 345 | || (n= read(rawfd, buf, block_size)) < 0
|
---|
| 346 | ) fatal(rawdev);
|
---|
| 347 |
|
---|
| 348 | if (n < block_size) {
|
---|
| 349 | fprintf(stderr, "installboot: Unexpected EOF on %s\n", rawdev);
|
---|
| 350 | exit(1);
|
---|
| 351 | }
|
---|
| 352 | }
|
---|
| 353 |
|
---|
| 354 | void writeblock(off_t blk, char *buf, int block_size)
|
---|
| 355 | /* Add a function to write blocks for local use. */
|
---|
| 356 | {
|
---|
| 357 | if (lseek(rawfd, blk * block_size, SEEK_SET) < 0
|
---|
| 358 | || write(rawfd, buf, block_size) < 0
|
---|
| 359 | ) fatal(rawdev);
|
---|
| 360 | }
|
---|
| 361 |
|
---|
| 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.
|
---|
| 368 | */
|
---|
| 369 | {
|
---|
| 370 | static char buf[_MAX_BLOCK_SIZE]; /* Nonvolatile block buffer. */
|
---|
| 371 | FILE *f;
|
---|
| 372 | off_t sec;
|
---|
| 373 | unsigned long devsize;
|
---|
| 374 | static int banner= 0;
|
---|
| 375 | struct partition entry;
|
---|
| 376 |
|
---|
| 377 | /* See if the device has a maximum size. */
|
---|
| 378 | devsize= -1;
|
---|
| 379 | if (ioctl(rawfd, DIOCGETP, &entry) == 0) devsize= cv64ul(entry.size);
|
---|
| 380 |
|
---|
| 381 | if ((f= fopen(file, "r")) == nil) fatal(file);
|
---|
| 382 |
|
---|
| 383 | /* Copy sectors from file onto the boot device. */
|
---|
| 384 | sec= *start;
|
---|
| 385 | do {
|
---|
| 386 | int off= sec % RATIO(BOOT_BLOCK_SIZE);
|
---|
| 387 |
|
---|
| 388 | if (fread(buf + off * SECTOR_SIZE, 1, SECTOR_SIZE, f) == 0)
|
---|
| 389 | break;
|
---|
| 390 |
|
---|
| 391 | if (sec >= devsize) {
|
---|
| 392 | fprintf(stderr,
|
---|
| 393 | "installboot: %s can't be attached to %s\n",
|
---|
| 394 | file, rawdev);
|
---|
| 395 | return 0;
|
---|
| 396 | }
|
---|
| 397 |
|
---|
| 398 | if (off == RATIO(BOOT_BLOCK_SIZE) - 1) writeblock(sec / RATIO(BOOT_BLOCK_SIZE), buf, BOOT_BLOCK_SIZE);
|
---|
| 399 | } while (++sec != *start + *len);
|
---|
| 400 |
|
---|
| 401 | if (ferror(f)) fatal(file);
|
---|
| 402 | (void) fclose(f);
|
---|
| 403 |
|
---|
| 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);
|
---|
| 406 |
|
---|
| 407 | if (!banner) {
|
---|
| 408 | printf(" sector length\n");
|
---|
| 409 | banner= 1;
|
---|
| 410 | }
|
---|
| 411 | *len= sec - *start;
|
---|
| 412 | printf("%8ld%8ld %s\n", *start, *len, file);
|
---|
| 413 | *start= sec;
|
---|
| 414 | return 1;
|
---|
| 415 | }
|
---|
| 416 |
|
---|
| 417 | enum howto { FS, BOOT };
|
---|
| 418 |
|
---|
| 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.
|
---|
| 425 | */
|
---|
| 426 | {
|
---|
| 427 | char buf[_MAX_BLOCK_SIZE + 256], *adrp, *parmp;
|
---|
| 428 | struct fileaddr {
|
---|
| 429 | off_t address;
|
---|
| 430 | int count;
|
---|
| 431 | } bootaddr[BOOT_MAX + 1], *bap= bootaddr;
|
---|
| 432 | struct exec boothdr;
|
---|
| 433 | struct image_header dummy;
|
---|
| 434 | struct stat st;
|
---|
| 435 | ino_t ino;
|
---|
| 436 | off_t sector, max_sector;
|
---|
| 437 | FILE *bootf;
|
---|
| 438 | off_t addr, fssize, pos, len;
|
---|
| 439 | char *labels, *label, *image;
|
---|
| 440 | int nolabel;
|
---|
| 441 | int block_size = 0;
|
---|
| 442 |
|
---|
| 443 | /* Open device and set variables for readblock. */
|
---|
| 444 | if ((rawfd= open(rawdev= device, O_RDWR)) < 0) fatal(device);
|
---|
| 445 |
|
---|
| 446 | /* Read and check the superblock. */
|
---|
| 447 | fssize= r_super(&block_size);
|
---|
| 448 |
|
---|
| 449 | switch (how) {
|
---|
| 450 | case FS:
|
---|
| 451 | if (fssize == 0) {
|
---|
| 452 | fprintf(stderr,
|
---|
| 453 | "installboot: %s is not a Minix file system\n",
|
---|
| 454 | device);
|
---|
| 455 | exit(1);
|
---|
| 456 | }
|
---|
| 457 | break;
|
---|
| 458 | case BOOT:
|
---|
| 459 | if (fssize != 0) {
|
---|
| 460 | int s;
|
---|
| 461 | printf("%s contains a file system!\n", device);
|
---|
| 462 | printf("Scribbling in 10 seconds");
|
---|
| 463 | for (s= 0; s < 10; s++) {
|
---|
| 464 | fputc('.', stdout);
|
---|
| 465 | fflush(stdout);
|
---|
| 466 | sleep(1);
|
---|
| 467 | }
|
---|
| 468 | fputc('\n', stdout);
|
---|
| 469 | }
|
---|
| 470 | fssize= 1; /* Just a boot block. */
|
---|
| 471 | }
|
---|
| 472 |
|
---|
| 473 | if (how == FS) {
|
---|
| 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);
|
---|
| 477 | }
|
---|
| 478 | } else {
|
---|
| 479 | /* Boot code must be attached at the end. */
|
---|
| 480 | ino= 0;
|
---|
| 481 | }
|
---|
| 482 |
|
---|
| 483 | if (ino == 0) {
|
---|
| 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.
|
---|
| 486 | */
|
---|
| 487 | if (stat(bootcode, &st) < 0) fatal(bootcode);
|
---|
| 488 |
|
---|
| 489 | if ((bootf= fopen(bootcode, "r")) == nil) fatal(bootcode);
|
---|
| 490 | } else {
|
---|
| 491 | /* Boot code is present in the file system. */
|
---|
| 492 | r_stat(ino, &st);
|
---|
| 493 |
|
---|
| 494 | /* Get the header from the first block. */
|
---|
| 495 | if ((addr= r_vir2abs((off_t) 0)) == 0) {
|
---|
| 496 | boothdr.a_magic[0]= !A_MAGIC0;
|
---|
| 497 | } else {
|
---|
| 498 | readblock(addr, buf, block_size);
|
---|
| 499 | memcpy(&boothdr, buf, sizeof(struct exec));
|
---|
| 500 | }
|
---|
| 501 | bootf= nil;
|
---|
| 502 | dummy.process= boothdr;
|
---|
| 503 | }
|
---|
| 504 | /* See if it is an executable (read_header does the check). */
|
---|
| 505 | read_header(0, bootcode, bootf, &dummy);
|
---|
| 506 | boothdr= dummy.process;
|
---|
| 507 |
|
---|
| 508 | if (bootf != nil) fclose(bootf);
|
---|
| 509 |
|
---|
| 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;
|
---|
| 513 |
|
---|
| 514 | if (max_sector > BOOT_MAX * RATIO(block_size)) {
|
---|
| 515 | fprintf(stderr, "installboot: %s is way too big\n", bootcode);
|
---|
| 516 | exit(0);
|
---|
| 517 | }
|
---|
| 518 |
|
---|
| 519 | /* Determine the addresses to the boot code to be patched into the
|
---|
| 520 | * boot block.
|
---|
| 521 | */
|
---|
| 522 | bap->count= 0; /* Trick to get the address recording going. */
|
---|
| 523 |
|
---|
| 524 | for (sector= 0; sector < max_sector; sector++) {
|
---|
| 525 | if (ino == 0) {
|
---|
| 526 | addr= fssize + (sector / RATIO(block_size));
|
---|
| 527 | } else
|
---|
| 528 | if ((addr= r_vir2abs(sector / RATIO(block_size))) == 0) {
|
---|
| 529 | fprintf(stderr, "installboot: %s has holes!\n",
|
---|
| 530 | bootcode);
|
---|
| 531 | exit(1);
|
---|
| 532 | }
|
---|
| 533 | addr= (addr * RATIO(block_size)) + (sector % RATIO(block_size));
|
---|
| 534 |
|
---|
| 535 | /* First address of the addresses array? */
|
---|
| 536 | if (bap->count == 0) bap->address= addr;
|
---|
| 537 |
|
---|
| 538 | /* Paste sectors together in a multisector read. */
|
---|
| 539 | if (bap->address + bap->count == addr)
|
---|
| 540 | bap->count++;
|
---|
| 541 | else {
|
---|
| 542 | /* New address. */
|
---|
| 543 | bap++;
|
---|
| 544 | bap->address= addr;
|
---|
| 545 | bap->count= 1;
|
---|
| 546 | }
|
---|
| 547 | }
|
---|
| 548 | (++bap)->count= 0; /* No more. */
|
---|
| 549 |
|
---|
| 550 | /* Get the boot block and patch the pieces in. */
|
---|
| 551 | readblock(BOOTBLOCK, buf, BOOT_BLOCK_SIZE);
|
---|
| 552 |
|
---|
| 553 | if ((bootf= fopen(bootblock, "r")) == nil) fatal(bootblock);
|
---|
| 554 |
|
---|
| 555 | read_header(0, bootblock, bootf, &dummy);
|
---|
| 556 | boothdr= dummy.process;
|
---|
| 557 |
|
---|
| 558 | if (boothdr.a_text + boothdr.a_data +
|
---|
| 559 | 4 * (bap - bootaddr) + 1 > PARTPOS) {
|
---|
| 560 | fprintf(stderr,
|
---|
| 561 | "installboot: %s + addresses to %s don't fit in the boot sector\n",
|
---|
| 562 | bootblock, bootcode);
|
---|
| 563 | fprintf(stderr,
|
---|
| 564 | "You can try copying/reinstalling %s to defragment it\n",
|
---|
| 565 | bootcode);
|
---|
| 566 | exit(1);
|
---|
| 567 | }
|
---|
| 568 |
|
---|
| 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);
|
---|
| 572 |
|
---|
| 573 | /* Patch the addresses in. */
|
---|
| 574 | adrp= buf + (int) (boothdr.a_text + boothdr.a_data);
|
---|
| 575 | for (bap= bootaddr; bap->count != 0; bap++) {
|
---|
| 576 | *adrp++= bap->count;
|
---|
| 577 | *adrp++= (bap->address >> 0) & 0xFF;
|
---|
| 578 | *adrp++= (bap->address >> 8) & 0xFF;
|
---|
| 579 | *adrp++= (bap->address >> 16) & 0xFF;
|
---|
| 580 | }
|
---|
| 581 | /* Zero count stops bootblock's reading loop. */
|
---|
| 582 | *adrp++= 0;
|
---|
| 583 |
|
---|
| 584 | if (bap > bootaddr+1) {
|
---|
| 585 | printf("%s and %d addresses to %s patched into %s\n",
|
---|
| 586 | bootblock, (int)(bap - bootaddr), bootcode, device);
|
---|
| 587 | }
|
---|
| 588 |
|
---|
| 589 | /* Boot block signature. */
|
---|
| 590 | buf[SIGPOS+0]= (SIGNATURE >> 0) & 0xFF;
|
---|
| 591 | buf[SIGPOS+1]= (SIGNATURE >> 8) & 0xFF;
|
---|
| 592 |
|
---|
| 593 | /* Sector 2 of the boot block is used for boot parameters, initially
|
---|
| 594 | * filled with null commands (newlines). Initialize it only if
|
---|
| 595 | * necessary.
|
---|
| 596 | */
|
---|
| 597 | for (parmp= buf + SECTOR_SIZE; parmp < buf + 2*SECTOR_SIZE; parmp++) {
|
---|
| 598 | if (*imagev != nil || (control(*parmp) && *parmp != '\n')) {
|
---|
| 599 | /* Param sector must be initialized. */
|
---|
| 600 | memset(buf + SECTOR_SIZE, '\n', SECTOR_SIZE);
|
---|
| 601 | break;
|
---|
| 602 | }
|
---|
| 603 | }
|
---|
| 604 |
|
---|
| 605 | /* Offset to the end of the file system to add boot code and images. */
|
---|
| 606 | pos= fssize * RATIO(block_size);
|
---|
| 607 |
|
---|
| 608 | if (ino == 0) {
|
---|
| 609 | /* Place the boot code onto the boot device. */
|
---|
| 610 | len= max_sector;
|
---|
| 611 | if (!raw_install(bootcode, &pos, &len, block_size)) {
|
---|
| 612 | if (how == FS) {
|
---|
| 613 | fprintf(stderr,
|
---|
| 614 | "\t(Isn't there a copy of %s on %s that can be used?)\n",
|
---|
| 615 | bootcode, device);
|
---|
| 616 | }
|
---|
| 617 | exit(1);
|
---|
| 618 | }
|
---|
| 619 | }
|
---|
| 620 |
|
---|
| 621 | parmp= buf + SECTOR_SIZE;
|
---|
| 622 | nolabel= 0;
|
---|
| 623 |
|
---|
| 624 | if (how == BOOT) {
|
---|
| 625 | /* A boot only disk needs to have floppies swapped. */
|
---|
| 626 | strcpy(parmp,
|
---|
| 627 | "trailer()echo \\nInsert the root diskette then hit RETURN\\n\\w\\c\n");
|
---|
| 628 | parmp+= strlen(parmp);
|
---|
| 629 | }
|
---|
| 630 |
|
---|
| 631 | while ((labels= *imagev++) != nil) {
|
---|
| 632 | /* Place each kernel image on the boot device. */
|
---|
| 633 |
|
---|
| 634 | if ((image= strchr(labels, ':')) != nil)
|
---|
| 635 | *image++= 0;
|
---|
| 636 | else {
|
---|
| 637 | if (nolabel) {
|
---|
| 638 | fprintf(stderr,
|
---|
| 639 | "installboot: Only one image can be the default\n");
|
---|
| 640 | exit(1);
|
---|
| 641 | }
|
---|
| 642 | nolabel= 1;
|
---|
| 643 | image= labels;
|
---|
| 644 | labels= nil;
|
---|
| 645 | }
|
---|
| 646 | len= 0;
|
---|
| 647 | if (!raw_install(image, &pos, &len, block_size)) exit(1);
|
---|
| 648 |
|
---|
| 649 | if (labels == nil) {
|
---|
| 650 | /* Let this image be the default. */
|
---|
| 651 | sprintf(parmp, "image=%ld:%ld\n", pos-len, len);
|
---|
| 652 | parmp+= strlen(parmp);
|
---|
| 653 | }
|
---|
| 654 |
|
---|
| 655 | while (labels != nil) {
|
---|
| 656 | /* Image is prefixed by a comma separated list of
|
---|
| 657 | * labels. Define functions to select label and image.
|
---|
| 658 | */
|
---|
| 659 | label= labels;
|
---|
| 660 | if ((labels= strchr(labels, ',')) != nil) *labels++ = 0;
|
---|
| 661 |
|
---|
| 662 | sprintf(parmp,
|
---|
| 663 | "%s(%c){label=%s;image=%ld:%ld;echo %s kernel selected;menu}\n",
|
---|
| 664 | label,
|
---|
| 665 | between('A', label[0], 'Z')
|
---|
| 666 | ? label[0]-'A'+'a' : label[0],
|
---|
| 667 | label, pos-len, len, label);
|
---|
| 668 | parmp+= strlen(parmp);
|
---|
| 669 | }
|
---|
| 670 |
|
---|
| 671 | if (parmp > buf + block_size) {
|
---|
| 672 | fprintf(stderr,
|
---|
| 673 | "installboot: Out of parameter space, too many images\n");
|
---|
| 674 | exit(1);
|
---|
| 675 | }
|
---|
| 676 | }
|
---|
| 677 | /* Install boot block. */
|
---|
| 678 | writeblock((off_t) BOOTBLOCK, buf, 1024);
|
---|
| 679 |
|
---|
| 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));
|
---|
| 684 | }
|
---|
| 685 | }
|
---|
| 686 |
|
---|
| 687 | void install_master(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.
|
---|
| 693 | */
|
---|
| 694 | {
|
---|
| 695 | FILE *masf;
|
---|
| 696 | unsigned long size;
|
---|
| 697 | struct stat st;
|
---|
| 698 | static char buf[_MAX_BLOCK_SIZE];
|
---|
| 699 |
|
---|
| 700 | /* Open device. */
|
---|
| 701 | if ((rawfd= open(rawdev= device, O_RDWR)) < 0) fatal(device);
|
---|
| 702 |
|
---|
| 703 | /* Open the master boot code. */
|
---|
| 704 | if ((masf= fopen(masterboot, "r")) == nil) fatal(masterboot);
|
---|
| 705 |
|
---|
| 706 | /* See if the user is cloning a device. */
|
---|
| 707 | if (fstat(fileno(masf), &st) >=0 && S_ISBLK(st.st_mode))
|
---|
| 708 | size= PARTPOS;
|
---|
| 709 | else {
|
---|
| 710 | /* Read and check header otherwise. */
|
---|
| 711 | struct image_header ihdr;
|
---|
| 712 |
|
---|
| 713 | read_header(1, masterboot, masf, &ihdr);
|
---|
| 714 | size= ihdr.process.a_text + ihdr.process.a_data;
|
---|
| 715 | }
|
---|
| 716 | if (size > PARTPOS) {
|
---|
| 717 | fprintf(stderr, "installboot: %s is too big\n", masterboot);
|
---|
| 718 | exit(1);
|
---|
| 719 | }
|
---|
| 720 |
|
---|
| 721 | /* Read the master boot block, patch it, write. */
|
---|
| 722 | readblock(BOOTBLOCK, buf, BOOT_BLOCK_SIZE);
|
---|
| 723 |
|
---|
| 724 | memset(buf, 0, PARTPOS);
|
---|
| 725 | (void) bread(masf, masterboot, buf, size);
|
---|
| 726 |
|
---|
| 727 | if (guide[0] != nil) {
|
---|
| 728 | /* Fixate partition to boot. */
|
---|
| 729 | char *keys= guide[0];
|
---|
| 730 | char *logical= guide[1];
|
---|
| 731 | size_t i;
|
---|
| 732 | int logfd;
|
---|
| 733 | u32_t offset;
|
---|
| 734 | struct partition geometry;
|
---|
| 735 |
|
---|
| 736 | /* A string of digits to be seen as keystrokes. */
|
---|
| 737 | i= 0;
|
---|
| 738 | do {
|
---|
| 739 | if (!between('0', keys[i], '9')) {
|
---|
| 740 | fprintf(stderr,
|
---|
| 741 | "installboot: bad guide keys '%s'\n",
|
---|
| 742 | keys);
|
---|
| 743 | exit(1);
|
---|
| 744 | }
|
---|
| 745 | } while (keys[++i] != 0);
|
---|
| 746 |
|
---|
| 747 | if (size + i + 1 > PARTPOS) {
|
---|
| 748 | fprintf(stderr,
|
---|
| 749 | "installboot: not enough space after '%s' for '%s'\n",
|
---|
| 750 | masterboot, keys);
|
---|
| 751 | exit(1);
|
---|
| 752 | }
|
---|
| 753 | memcpy(buf + size, keys, i);
|
---|
| 754 | size += i;
|
---|
| 755 | buf[size]= '\r';
|
---|
| 756 |
|
---|
| 757 | if (logical != nil) {
|
---|
| 758 | if ((logfd= open(logical, O_RDONLY)) < 0
|
---|
| 759 | || ioctl(logfd, DIOCGETP, &geometry) < 0
|
---|
| 760 | ) {
|
---|
| 761 | fatal(logical);
|
---|
| 762 | }
|
---|
| 763 | offset= div64u(geometry.base, SECTOR_SIZE);
|
---|
| 764 | if (size + 5 > PARTPOS) {
|
---|
| 765 | fprintf(stderr,
|
---|
| 766 | "installboot: not enough space "
|
---|
| 767 | "after '%s' for '%s' and an offset "
|
---|
| 768 | "to '%s'\n",
|
---|
| 769 | masterboot, keys, logical);
|
---|
| 770 | exit(1);
|
---|
| 771 | }
|
---|
| 772 | buf[size]= '#';
|
---|
| 773 | memcpy(buf+size+1, &offset, 4);
|
---|
| 774 | }
|
---|
| 775 | }
|
---|
| 776 |
|
---|
| 777 | /* Install signature. */
|
---|
| 778 | buf[SIGPOS+0]= (SIGNATURE >> 0) & 0xFF;
|
---|
| 779 | buf[SIGPOS+1]= (SIGNATURE >> 8) & 0xFF;
|
---|
| 780 |
|
---|
| 781 | writeblock(BOOTBLOCK, buf, BOOT_BLOCK_SIZE);
|
---|
| 782 | }
|
---|
| 783 |
|
---|
| 784 | void usage(void)
|
---|
| 785 | {
|
---|
| 786 | fprintf(stderr,
|
---|
| 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");
|
---|
| 792 | exit(1);
|
---|
| 793 | }
|
---|
| 794 |
|
---|
| 795 | int isoption(char *option, 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.
|
---|
| 798 | */
|
---|
| 799 | {
|
---|
| 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;
|
---|
| 804 | return 0;
|
---|
| 805 | }
|
---|
| 806 |
|
---|
| 807 | int main(int argc, char **argv)
|
---|
| 808 | {
|
---|
| 809 | if (argc < 2) usage();
|
---|
| 810 |
|
---|
| 811 | if (argc >= 4 && isoption(argv[1], "-image")) {
|
---|
| 812 | make_image(argv[2], argv + 3);
|
---|
| 813 | } else
|
---|
| 814 | if (argc == 3 && isoption(argv[1], "-extract")) {
|
---|
| 815 | extract_image(argv[2]);
|
---|
| 816 | } else
|
---|
| 817 | if (argc >= 5 && isoption(argv[1], "-device")) {
|
---|
| 818 | make_bootable(FS, argv[2], argv[3], argv[4], argv + 5);
|
---|
| 819 | } else
|
---|
| 820 | if (argc >= 6 && isoption(argv[1], "-boot")) {
|
---|
| 821 | make_bootable(BOOT, argv[2], argv[3], argv[4], argv + 5);
|
---|
| 822 | } else
|
---|
| 823 | if ((4 <= argc && argc <= 6) && isoption(argv[1], "-master")) {
|
---|
| 824 | install_master(argv[2], argv[3], argv + 4);
|
---|
| 825 | } else {
|
---|
| 826 | usage();
|
---|
| 827 | }
|
---|
| 828 | exit(0);
|
---|
| 829 | }
|
---|
| 830 |
|
---|
| 831 | /*
|
---|
| 832 | * $PchId: installboot.c,v 1.10 2000/08/13 22:07:50 philip Exp $
|
---|
| 833 | */
|
---|