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 | */
|
---|