1 /* @(#)boot.c	1.28 16/10/23 Copyright 1999-2016 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)boot.c	1.28 16/10/23 Copyright 1999-2016 J. Schilling";
6 #endif
7 /*
8  *	Support for generic boot (sector 0..16)
9  *	and to boot Sun sparc and Sun x86 systems.
10  *
11  *	Copyright (c) 1999-2016 J. Schilling
12  */
13 /*
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License version 2
16  * as published by the Free Software Foundation.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along with
24  * this program; see the file COPYING.  If not, write to the Free Software
25  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  */
27 
28 #include "mkisofs.h"
29 #include <schily/fcntl.h>
30 #include <schily/utypes.h>
31 #include <schily/intcvt.h>
32 #include <schily/schily.h>
33 #include "sunlabel.h"
34 
35 extern	int	use_sunx86boot;
36 
37 LOCAL struct sun_label cd_label;
38 LOCAL struct x86_label sx86_label;
39 LOCAL struct pc_part	fdisk_part;
40 LOCAL char	*boot_files[NDKMAP];	/* Change this for > 8 x86 parts */
41 
42 LOCAL	void	init_sparc_label	__PR((void));
43 LOCAL	void	init_sunx86_label	__PR((void));
44 EXPORT	int	sparc_boot_label	__PR((char *label));
45 EXPORT	int	sunx86_boot_label	__PR((char *label));
46 EXPORT	int	scan_sparc_boot		__PR((char *files));
47 EXPORT	int	scan_sunx86_boot	__PR((char *files));
48 EXPORT	int	make_sun_label		__PR((void));
49 EXPORT	int	make_sunx86_label	__PR((void));
50 LOCAL	void	dup_sun_label		__PR((int part));
51 LOCAL	int	sunboot_write		__PR((FILE *outfile));
52 LOCAL	int	sunlabel_size		__PR((UInt32_t starting_extent));
53 LOCAL	int	sunlabel_write		__PR((FILE *outfile));
54 LOCAL	int	genboot_size		__PR((UInt32_t starting_extent));
55 LOCAL	int	genboot_write		__PR((FILE *outfile));
56 
57 /*
58  * Set the virtual geometry in the disk label.
59  * If we like to make the geometry variable, we may change
60  * dkl_ncyl and dkl_pcyl later.
61  */
62 LOCAL void
init_sparc_label()63 init_sparc_label()
64 {
65 	i_to_4_byte(cd_label.dkl_vtoc.v_version, V_VERSION);
66 	i_to_2_byte(cd_label.dkl_vtoc.v_nparts, NDKMAP);
67 	i_to_4_byte(cd_label.dkl_vtoc.v_sanity, VTOC_SANE);
68 
69 	i_to_2_byte(cd_label.dkl_rpm, CD_RPM);
70 	i_to_2_byte(cd_label.dkl_pcyl, CD_PCYL);
71 	i_to_2_byte(cd_label.dkl_apc, CD_APC);
72 	i_to_2_byte(cd_label.dkl_intrlv, CD_INTRLV);
73 	i_to_2_byte(cd_label.dkl_ncyl, CD_NCYL);
74 	i_to_2_byte(cd_label.dkl_acyl, CD_ACYL);
75 	i_to_2_byte(cd_label.dkl_nhead, CD_NHEAD);
76 	i_to_2_byte(cd_label.dkl_nsect, CD_NSECT);
77 
78 	cd_label.dkl_magic[0] =	DKL_MAGIC_0;
79 	cd_label.dkl_magic[1] =	DKL_MAGIC_1;
80 }
81 
82 LOCAL void
init_sunx86_label()83 init_sunx86_label()
84 {
85 	li_to_4_byte(sx86_label.dkl_vtoc.v_sanity, VTOC_SANE);
86 	li_to_4_byte(sx86_label.dkl_vtoc.v_version, V_VERSION);
87 	li_to_2_byte(sx86_label.dkl_vtoc.v_sectorsz, 512);
88 	li_to_2_byte(sx86_label.dkl_vtoc.v_nparts, NX86MAP);
89 
90 	li_to_4_byte(sx86_label.dkl_pcyl, CD_PCYL);
91 	li_to_4_byte(sx86_label.dkl_ncyl, CD_NCYL);
92 	li_to_2_byte(sx86_label.dkl_acyl, CD_ACYL);
93 	li_to_2_byte(sx86_label.dkl_bcyl, 0);
94 
95 	li_to_4_byte(sx86_label.dkl_nhead, CD_NHEAD);
96 	li_to_4_byte(sx86_label.dkl_nsect, CD_NSECT);
97 	li_to_2_byte(sx86_label.dkl_intrlv, CD_INTRLV);
98 	li_to_2_byte(sx86_label.dkl_skew, 0);
99 	li_to_2_byte(sx86_label.dkl_apc, CD_APC);
100 	li_to_2_byte(sx86_label.dkl_rpm, CD_RPM);
101 
102 	li_to_2_byte(sx86_label.dkl_write_reinstruct, 0);
103 	li_to_2_byte(sx86_label.dkl_read_reinstruct, 0);
104 
105 	li_to_2_byte(sx86_label.dkl_magic, DKL_MAGIC);
106 }
107 
108 /*
109  * For command line parser: set ASCII label.
110  */
111 EXPORT int
sparc_boot_label(label)112 sparc_boot_label(label)
113 	char	*label;
114 {
115 	strncpy(cd_label.dkl_ascilabel, label, 127);
116 	cd_label.dkl_ascilabel[127] = '\0';
117 	return (1);
118 }
119 
120 EXPORT int
sunx86_boot_label(label)121 sunx86_boot_label(label)
122 	char	*label;
123 {
124 	strncpy(sx86_label.dkl_vtoc.v_asciilabel, label, 127);
125 	sx86_label.dkl_vtoc.v_asciilabel[127] = '\0';
126 	return (1);
127 }
128 
129 /*
130  * Parse the command line argument for boot images.
131  */
132 EXPORT int
scan_sparc_boot(files)133 scan_sparc_boot(files)
134 	char	*files;
135 {
136 	char		*p;
137 	int		i = 1;
138 	struct stat	statbuf;
139 	int		status;
140 extern	int		use_sparcboot;
141 extern	int		use_sunx86boot;
142 
143 	if (use_sunx86boot)
144 		comerrno(EX_BAD,
145 		_("-sparc-boot and -sunx86-boot are mutual exclusive.\n"));
146 	use_sparcboot++;
147 
148 	init_sparc_label();
149 
150 	do {
151 		if (i >= NDKMAP)
152 			comerrno(EX_BAD, _("Too many boot partitions.\n"));
153 		boot_files[i++] = files;
154 		if ((p = strchr(files, ',')) != NULL)
155 			*p++ = '\0';
156 		files = p;
157 	} while (p);
158 
159 	i_to_2_byte(cd_label.dkl_vtoc.v_part[0].p_tag,  V_USR);
160 	i_to_2_byte(cd_label.dkl_vtoc.v_part[0].p_flag, V_RONLY);
161 	for (i = 0; i < NDKMAP; i++) {
162 		p = boot_files[i];
163 		if (p == NULL || *p == '\0')
164 			continue;
165 		if (strcmp(p, "...") == '\0')
166 			break;
167 
168 		status = stat_filter(p, &statbuf);
169 		if (status < 0 || access(p, R_OK) < 0)
170 			comerr(_("Cannot access '%s'.\n"), p);
171 
172 		i_to_4_byte(cd_label.dkl_map[i].dkl_nblk,
173 			roundup(statbuf.st_size, CD_CYLSIZE)/512);
174 
175 		i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_tag,  V_ROOT);
176 		i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_flag, V_RONLY);
177 	}
178 	return (1);
179 }
180 
181 EXPORT int
scan_sunx86_boot(files)182 scan_sunx86_boot(files)
183 	char	*files;
184 {
185 	char		*p;
186 	int		i = 0;
187 	struct stat	statbuf;
188 	int		status;
189 extern	int		use_sparcboot;
190 extern	int		use_sunx86boot;
191 
192 	if (use_sparcboot)
193 		comerrno(EX_BAD,
194 		_("-sparc-boot and -sunx86-boot are mutual exclusive.\n"));
195 	use_sunx86boot++;
196 
197 
198 	init_sunx86_label();
199 
200 	do {
201 		if (i >= NDKMAP)
202 			comerrno(EX_BAD, _("Too many boot partitions.\n"));
203 		boot_files[i++] = files;
204 		if ((p = strchr(files, ',')) != NULL)
205 			*p++ = '\0';
206 		files = p;
207 	} while (p);
208 
209 	li_to_2_byte(sx86_label.dkl_vtoc.v_part[0].p_tag,  V_ROOT);  /* UFS */
210 	li_to_2_byte(sx86_label.dkl_vtoc.v_part[0].p_flag, V_RONLY);
211 	li_to_2_byte(sx86_label.dkl_vtoc.v_part[1].p_tag,  V_USR);   /* ISO */
212 	li_to_2_byte(sx86_label.dkl_vtoc.v_part[1].p_flag, V_RONLY);
213 	li_to_2_byte(sx86_label.dkl_vtoc.v_part[2].p_tag,  0);	    /* ALL */
214 	li_to_2_byte(sx86_label.dkl_vtoc.v_part[2].p_flag, 0);
215 	for (i = 0; i < NDKMAP; i++) {
216 		p = boot_files[i];
217 		if (p == NULL || *p == '\0')
218 			continue;
219 		if (i == 1 || i == 2) {
220 			comerrno(EX_BAD,
221 			_("Partition %d may not have a filename.\n"), i);
222 		}
223 
224 		status = stat_filter(p, &statbuf);
225 		if (status < 0 || access(p, R_OK) < 0)
226 			comerr(_("Cannot access '%s'.\n"), p);
227 
228 		li_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size,
229 			roundup(statbuf.st_size, CD_CYLSIZE)/512);
230 
231 		if (i > 2) {
232 			li_to_2_byte(sx86_label.dkl_vtoc.v_part[i].p_tag,  V_USR);
233 			li_to_2_byte(sx86_label.dkl_vtoc.v_part[i].p_flag, V_RONLY);
234 		}
235 	}
236 	return (1);
237 }
238 
239 /*
240  * Finish the Sun disk label and compute the size of the additional data.
241  */
242 EXPORT int
make_sun_label()243 make_sun_label()
244 {
245 	int	last;
246 	int	cyl = 0;
247 	int	nblk;
248 	int	bsize;
249 	int	i;
250 	char	*p;
251 
252 	/*
253 	 * Compute the size of the padding for the iso9660 image
254 	 * to allow the next partition to start on a cylinder boundary.
255 	 */
256 	last = roundup(last_extent, (CD_CYLSIZE/SECTOR_SIZE));
257 
258 	i_to_4_byte(cd_label.dkl_map[0].dkl_nblk, last*4);
259 	bsize = 0;
260 	for (i = 0; i < NDKMAP; i++) {
261 		p = boot_files[i];
262 		if (p != NULL && strcmp(p, "...") == '\0') {
263 			dup_sun_label(i);
264 			break;
265 		}
266 		if ((nblk = a_to_4_byte(cd_label.dkl_map[i].dkl_nblk)) == 0)
267 			continue;
268 
269 		i_to_4_byte(cd_label.dkl_map[i].dkl_cylno, cyl);
270 		cyl += nblk / (CD_CYLSIZE/512);
271 		if (i > 0)
272 			bsize += nblk;
273 	}
274 	bsize /= 4;
275 	return (last-last_extent+bsize);
276 }
277 
278 /*
279  * A typical Solaris boot/install CD from a Sun CD set looks
280  * this way:
281  *
282  * UFS	Part 0 tag 2 flag 10 start 3839 size 1314560
283  * ISO	Part 1 tag 4 flag 10 start    0 size    3839
284  * ALL	Part 2 tag 0 flag  0 start    0 size 1318400
285  */
286 EXPORT int
make_sunx86_label()287 make_sunx86_label()
288 {
289 	int	last;
290 	int	cyl = 0;
291 	int	nblk;
292 	int	bsize;
293 	int	i;
294 	int	partoff = 1;	/* The offset of the Solaris 0x82 partition */
295 
296 	/*
297 	 * Compute the size of the padding for the iso9660 image
298 	 * to allow the next partition to start on a cylinder boundary.
299 	 */
300 	last = roundup(last_extent, (CD_CYLSIZE/SECTOR_SIZE));
301 
302 	li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_size, last*4);
303 
304 	/*
305 	 * Note that the Solaris fdisk partition with fdisk signature 0x82
306 	 * is created at fixed offset 1 sector == 512 Bytes by this
307 	 * implementation.
308 	 * We need subtract this partition offset from all absolute
309 	 * partition offsets in order to get offsets relative to the
310 	 * Solaris primary partition.
311 	 */
312 	bsize = 0;
313 	for (i = 0; i < NDKMAP; i++) {
314 		if (i == 2)		/* Never include the whole disk in */
315 			continue;	/* size/offset computations	   */
316 
317 		if ((nblk = la_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size)) == 0)
318 			continue;
319 
320 		li_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_start,
321 						cyl*(CD_CYLSIZE/512)-partoff);
322 		cyl += nblk / (CD_CYLSIZE/512);
323 		if (i == 0 || i > 2)
324 			bsize += nblk;
325 	}
326 	li_to_4_byte(sx86_label.dkl_vtoc.v_part[0].p_start, last*4-partoff);
327 	li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_start, 0);
328 	li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_size, last*4-partoff);
329 	li_to_4_byte(sx86_label.dkl_vtoc.v_part[2].p_start, 0);
330 	li_to_4_byte(sx86_label.dkl_vtoc.v_part[2].p_size, last*4+bsize);
331 
332 	fdisk_part.part[0].pr_status = STATUS_ACTIVE;
333 	fdisk_part.part[0].pr_type   = TYPE_SOLARIS;
334 	li_to_4_byte(fdisk_part.part[0].pr_partoff, partoff);
335 	li_to_4_byte(fdisk_part.part[0].pr_nsect, last*4+bsize-partoff);
336 	fdisk_part.magic[0] = 0x55;
337 	fdisk_part.magic[1] = 0xAA;
338 
339 	bsize /= 4;
340 	return (last-last_extent+bsize);
341 }
342 
343 /*
344  * Duplicate a partition of the Sun disk label until all partitions are filled up.
345  */
346 LOCAL void
dup_sun_label(part)347 dup_sun_label(part)
348 	int	part;
349 {
350 	int	cyl;
351 	int	nblk;
352 	int	i;
353 
354 
355 	if (part < 1 || part >= NDKMAP)
356 		part = 1;
357 	cyl = a_to_4_byte(cd_label.dkl_map[part-1].dkl_cylno);
358 	nblk = a_to_4_byte(cd_label.dkl_map[part-1].dkl_nblk);
359 
360 	for (i = part; i < NDKMAP; i++) {
361 		i_to_4_byte(cd_label.dkl_map[i].dkl_cylno, cyl);
362 		i_to_4_byte(cd_label.dkl_map[i].dkl_nblk, nblk);
363 
364 		i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_tag,  V_ROOT);
365 		i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_flag, V_RONLY);
366 	}
367 }
368 
369 /*
370  * Write out Sun boot partitions.
371  */
372 LOCAL int
sunboot_write(outfile)373 sunboot_write(outfile)
374 	FILE	*outfile;
375 {
376 	char	buffer[SECTOR_SIZE];
377 	int	i;
378 	int	n;
379 	int	nblk;
380 	int	amt;
381 	int	f;
382 	char	*p;
383 
384 	memset(buffer, 0, sizeof (buffer));
385 
386 	/*
387 	 * Write padding to the iso9660 image to allow the
388 	 * boot partitions to start on a cylinder boundary.
389 	 */
390 	amt = roundup(last_extent_written, (CD_CYLSIZE/SECTOR_SIZE)) - last_extent_written;
391 	for (n = 0; n < amt; n++) {
392 		xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
393 		last_extent_written++;
394 	}
395 	if (use_sunx86boot)
396 		i = 0;
397 	else
398 		i = 1;
399 	for (; i < NDKMAP; i++) {
400 		if (use_sunx86boot && (i == 1 || i == 2))
401 			continue;
402 		p = boot_files[i];
403 		if (p == NULL || *p == '\0')
404 			continue;
405 		if (p != NULL && strcmp(p, "...") == '\0')
406 			break;
407 		if (use_sunx86boot) {
408 			if ((nblk = la_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size)) == 0)
409 				continue;
410 		} else {
411 			if ((nblk = a_to_4_byte(cd_label.dkl_map[i].dkl_nblk)) == 0)
412 				continue;
413 		}
414 		if ((f = open(boot_files[i], O_RDONLY| O_BINARY)) < 0)
415 			comerr(_("Cannot open '%s'.\n"), boot_files[i]);
416 
417 		amt = nblk / 4;
418 		for (n = 0; n < amt; n++) {
419 			memset(buffer, 0, sizeof (buffer));
420 			if (read(f, buffer, SECTOR_SIZE) < 0)
421 				comerr(_("Read error on '%s'.\n"), boot_files[i]);
422 			xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
423 			last_extent_written++;
424 		}
425 		close(f);
426 	}
427 	fprintf(stderr, _("Total extents including %s boot = %u\n"),
428 				use_sunx86boot ? "Solaris x86":"sparc",
429 				last_extent_written - session_start);
430 	return (0);
431 }
432 
433 /*
434  * Do size management for the Sun disk label that is located in the first
435  * sector of a disk.
436  */
437 LOCAL int
sunlabel_size(starting_extent)438 sunlabel_size(starting_extent)
439 	UInt32_t	starting_extent;
440 {
441 	if (last_extent != session_start)
442 		comerrno(EX_BAD, _("Cannot create sparc boot on offset != 0.\n"));
443 	last_extent++;
444 	return (0);
445 }
446 
447 /*
448  * Compute the checksum and write a Sun disk label to the first sector
449  * of the disk.
450  * If the -generic-boot option has been specified too, overlay the
451  * Sun disk label on the first 512 bytes of the generic boot code.
452  */
453 LOCAL int
sunlabel_write(outfile)454 sunlabel_write(outfile)
455 	FILE	*outfile;
456 {
457 		char	buffer[SECTOR_SIZE];
458 	register char	*p;
459 	register short	count = (512/2) - 1;
460 		int	f;
461 
462 	memset(buffer, 0, sizeof (buffer));
463 	if (genboot_image) {
464 		if ((f = open(genboot_image, O_RDONLY| O_BINARY)) < 0)
465 			comerr(_("Cannot open '%s'.\n"), genboot_image);
466 
467 		if (read(f, buffer, SECTOR_SIZE) < 0)
468 			comerr(_("Read error on '%s'.\n"), genboot_image);
469 		close(f);
470 	}
471 
472 	if (use_sunx86boot) {
473 		if (sx86_label.dkl_vtoc.v_asciilabel[0] == '\0')
474 			strcpy(sx86_label.dkl_vtoc.v_asciilabel, CD_X86LABEL);
475 
476 		p = (char *)&sx86_label;
477 		sx86_label.dkl_cksum[0] = 0;
478 		sx86_label.dkl_cksum[1] = 0;
479 		while (count-- > 0) {
480 			sx86_label.dkl_cksum[0] ^= *p++;
481 			sx86_label.dkl_cksum[1] ^= *p++;
482 		}
483 		memcpy(&buffer[0x1BE], fdisk_part.part, sizeof (fdisk_part.part));
484 		p = &buffer[510];
485 		*p++ = 0x55;
486 		*p   = 0xAA;
487 		memcpy(&buffer[1024], &sx86_label, 512);
488 	} else {
489 		/*
490 		 * If we don't already have a Sun disk label text
491 		 * set up the default.
492 		 */
493 		if (cd_label.dkl_ascilabel[0] == '\0')
494 			strcpy(cd_label.dkl_ascilabel, CD_DEFLABEL);
495 
496 		p = (char *)&cd_label;
497 		cd_label.dkl_cksum[0] = 0;
498 		cd_label.dkl_cksum[1] = 0;
499 		while (count--) {
500 			cd_label.dkl_cksum[0] ^= *p++;
501 			cd_label.dkl_cksum[1] ^= *p++;
502 		}
503 		memcpy(buffer, &cd_label, 512);
504 	}
505 
506 	xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
507 	last_extent_written++;
508 	return (0);
509 }
510 
511 /*
512  * Do size management for the generic boot code on sectors 0..16.
513  */
514 LOCAL int
genboot_size(starting_extent)515 genboot_size(starting_extent)
516 	UInt32_t	starting_extent;
517 {
518 	if (last_extent > (session_start + 1))
519 		comerrno(EX_BAD, _("Cannot create generic boot on offset != 0.\n"));
520 	last_extent = session_start + 16;
521 	return (0);
522 }
523 
524 /*
525  * Write the generic boot code to sectors 0..16.
526  * If there is a Sun disk label, start writing at sector 1.
527  */
528 LOCAL int
genboot_write(outfile)529 genboot_write(outfile)
530 	FILE	*outfile;
531 {
532 	char	buffer[SECTOR_SIZE];
533 	int	i;
534 	int	f;
535 
536 	if ((f = open(genboot_image, O_RDONLY| O_BINARY)) < 0)
537 		comerr(_("Cannot open '%s'.\n"), genboot_image);
538 
539 	for (i = 0; i < 16; i++) {
540 		memset(buffer, 0, sizeof (buffer));
541 		if (read(f, buffer, SECTOR_SIZE) < 0)
542 			comerr(_("Read error on '%s'.\n"), genboot_image);
543 
544 		if (i != 0 || last_extent_written == session_start) {
545 			xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
546 			last_extent_written++;
547 		}
548 	}
549 	close(f);
550 	return (0);
551 }
552 
553 struct output_fragment sunboot_desc	= {NULL, NULL,		NULL,	sunboot_write,  "Sun Boot" };
554 struct output_fragment sunlabel_desc	= {NULL, sunlabel_size,	NULL,	sunlabel_write, "Sun Disk Label" };
555 struct output_fragment genboot_desc	= {NULL, genboot_size,	NULL,	genboot_write,  "Generic Boot" };
556