1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12 
13 /*
14  * Program boot-mips.c - Handle big-endian boot extensions to iso9660.
15  *
16  * Written by Steve McIntyre <steve@einval.com> June 2004
17  *
18  * Heavily inspired by / borrowed from genisovh:
19  *
20  * Copyright: (C) 2002 by Florian Lohoff <flo@rfc822.org>
21  *            (C) 2004 by Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
22  *
23  * This program is free software; you can redistribute it and/or modify it under
24  * the terms of the GNU General Public License, Version 2, as published by the
25  * Free Software Foundation.
26  *
27  * Format for volume header information
28  *
29  * The volume header is a block located at the beginning of all disk
30  * media (sector 0).  It contains information pertaining to physical
31  * device parameters and logical partition information.
32  *
33  * The volume header is manipulated by disk formatters/verifiers,
34  * partition builders (e.g. fx, dvhtool, and mkfs), and disk drivers.
35  *
36  * Previous versions of IRIX wrote a copy of the volume header is
37  * located at sector 0 of each track of cylinder 0.  These copies were
38  * never used, and reduced the capacity of the volume header to hold large
39  * files, so this practice was discontinued.
40  * The volume header is constrained to be less than or equal to 512
41  * bytes long.  A particular copy is assumed valid if no drive errors
42  * are detected, the magic number is correct, and the 32 bit 2's complement
43  * of the volume header is correct.  The checksum is calculated by initially
44  * zeroing vh_csum, summing the entire structure and then storing the
45  * 2's complement of the sum.  Thus a checksum to verify the volume header
46  * should be 0.
47  *
48  * The error summary table, bad sector replacement table, and boot blocks are
49  * located by searching the volume directory within the volume header.
50  *
51  * Tables are sized simply by the integral number of table records that
52  * will fit in the space indicated by the directory entry.
53  *
54  * The amount of space allocated to the volume header, replacement blocks,
55  * and other tables is user defined when the device is formatted.
56  */
57 
58 #include <inttypes.h>
59 #ifndef MIN
60 #define MIN(a,b) ( (a<b) ? a : b )
61 #endif
62 
63 /*
64  * device parameters are in the volume header to determine mapping
65  * from logical block numbers to physical device addresses
66  *
67  * Linux doesn't care ...
68  */
69 struct device_parameters {
70 	uint8_t dp_skew;	/* spiral addressing skew */
71 	uint8_t dp_gap1;	/* words of 0 before header */
72 	uint8_t dp_gap2;	/* words of 0 between hdr and data */
73 	uint8_t dp_spares_cyl;	/* This is for drives (such as SCSI
74 		that support zone oriented sparing, where the zone is larger
75 		than one track.  It gets subracteded from the cylinder size
76 		( dp_trks0 * dp_sec) when doing partition size calculations */
77 	uint16_t dp_cyls;	/* number of usable cylinders (i.e.,
78 		doesn't include cylinders reserved by the drive for badblocks,
79 		etc.). For drives with variable geometry, this number may be
80 		decreased so that:
81 		dp_cyls * ((dp_heads * dp_trks0) - dp_spares_cyl) <= actualcapacity
82 		This happens on SCSI drives such as the Wren IV and Toshiba 156
83 		Also see dp_cylshi below */
84 	uint16_t dp_shd0;	/* starting head vol 0 */
85 	uint16_t dp_trks0;	/* number of tracks / cylinder vol 0*/
86 	uint8_t dp_ctq_depth;	/* Depth of CTQ queue */
87 	uint8_t dp_cylshi;	/* high byte of 24 bits of cylinder count */
88 	uint16_t dp_unused;	/* not used */
89 	uint16_t dp_secs;	/* number of sectors/track */
90 	uint16_t dp_secbytes;	/* length of sector in bytes */
91 	uint16_t dp_interleave;	/* sector interleave */
92 	int32_t dp_flags;		/* controller characteristics */
93 	int32_t dp_datarate;		/* bytes/sec for kernel stats */
94 	int32_t dp_nretries;		/* max num retries on data error */
95 	int32_t dp_mspw;		/* ms per word to xfer, for iostat */
96 	uint16_t dp_xgap1;	/* Gap 1 for xylogics controllers */
97 	uint16_t dp_xsync;    /* sync delay for xylogics controllers */
98 	uint16_t dp_xrdly;    /* read delay for xylogics controllers */
99 	uint16_t dp_xgap2;    /* gap 2 for xylogics controllers */
100 	uint16_t dp_xrgate;   /* read gate for xylogics controllers */
101 	uint16_t dp_xwcont;   /* write continuation for xylogics */
102 };
103 
104 /*
105  * Device characterization flags
106  * (dp_flags)
107  */
108 #define	DP_SECTSLIP	0x00000001	/* sector slip to spare sector */
109 #define	DP_SECTFWD	0x00000002	/* forward to replacement sector */
110 #define	DP_TRKFWD	0x00000004	/* forward to replacement track */
111 #define	DP_MULTIVOL	0x00000008	/* multiple volumes per spindle */
112 #define	DP_IGNOREERRORS	0x00000010	/* transfer data regardless of errors */
113 #define DP_RESEEK	0x00000020	/* recalibrate as last resort */
114 #define	DP_CTQ_EN	0x00000040	/* enable command tag queueing */
115 
116 /*
117  * Boot blocks, bad sector tables, and the error summary table, are located
118  * via the volume_directory.
119  */
120 #define VDNAMESIZE	8
121 
122 struct volume_directory {
123 	int8_t  vd_name[VDNAMESIZE];	/* name */
124 	int32_t vd_lbn;			/* logical block number */
125 	int32_t vd_nbytes;		/* file length in bytes */
126 };
127 
128 /*
129  * partition table describes logical device partitions
130  * (device drivers examine this to determine mapping from logical units
131  * to cylinder groups, device formatters/verifiers examine this to determine
132  * location of replacement tracks/sectors, etc)
133  *
134  * NOTE: pt_firstlbn SHOULD BE CYLINDER ALIGNED
135  */
136 struct partition_table {		/* one per logical partition */
137 	int32_t pt_nblks;		/* # of logical blks in partition */
138 	int32_t pt_firstlbn;		/* first lbn of partition */
139 	int32_t pt_type;		/* use of partition */
140 };
141 
142 #define	PTYPE_VOLHDR	0		/* partition is volume header */
143 #define	PTYPE_TRKREPL	1		/* partition is used for repl trks */
144 #define	PTYPE_SECREPL	2		/* partition is used for repl secs */
145 #define	PTYPE_RAW	3		/* partition is used for data */
146 #define	PTYPE_BSD42	4		/* partition is 4.2BSD file system */
147 #define	PTYPE_BSD	4		/* partition is 4.2BSD file system */
148 #define	PTYPE_SYSV	5		/* partition is SysV file system */
149 #define	PTYPE_VOLUME	6		/* partition is entire volume */
150 #define	PTYPE_EFS	7		/* partition is sgi EFS */
151 #define	PTYPE_LVOL	8		/* partition is part of a logical vol */
152 #define	PTYPE_RLVOL	9		/* part of a "raw" logical vol */
153 #define	PTYPE_XFS	10		/* partition is sgi XFS */
154 #define	PTYPE_XFSLOG	11		/* partition is sgi XFS log */
155 #define	PTYPE_XLV	12		/* partition is part of an XLV vol */
156 #define	PTYPE_XVM	13		/* partition is sgi XVM */
157 #define	PTYPE_LSWAP	0x82		/* partition is Linux swap */
158 #define	PTYPE_LINUX	0x83		/* partition is Linux native */
159 #define NPTYPES		16
160 
161 #define	VHMAGIC		0xbe5a941	/* randomly chosen value */
162 #define	NPARTAB		16		/* 16 unix partitions */
163 #define	NVDIR		15		/* max of 15 directory entries */
164 #define BFNAMESIZE	16		/* max 16 chars in boot file name */
165 
166 /* Partition types for ARCS */
167 #define NOT_USED        0       /* Not used 				*/
168 #define FAT_SHORT       1       /* FAT filesystem, 12-bit FAT entries 	*/
169 #define FAT_LONG        4       /* FAT filesystem, 16-bit FAT entries 	*/
170 #define EXTENDED        5       /* extended partition 			*/
171 #define HUGE            6       /* huge partition- MS/DOS 4.0 and later */
172 
173 /* Active flags for ARCS */
174 #define BOOTABLE        0x00;
175 #define NOT_BOOTABLE    0x80;
176 
177 struct volume_header {
178 	int32_t vh_magic; /* identifies volume header */
179 	int16_t vh_rootpt; /* root partition number */
180 	int16_t vh_swappt; /* swap partition number */
181 	int8_t vh_bootfile[BFNAMESIZE]; /* name of file to boot */
182 	struct device_parameters vh_dp; /* device parameters */
183 	struct volume_directory vh_vd[NVDIR]; /* other vol hdr contents */
184 	struct partition_table vh_pt[NPARTAB]; /* device partition layout */
185 	int32_t vh_csum; /* volume header checksum */
186 	int32_t vh_fill; /* fill out to 512 bytes */
187     char pad[1536];  /* pad out to 2048 */
188 };
189 
190 #include <mconfig.h>
191 #include "genisoimage.h"
192 #include <fctldefs.h>
193 #include <utypes.h>
194 #include <intcvt.h>
195 #include "match.h"
196 #include "diskmbr.h"
197 #include "bootinfo.h"
198 #include <schily.h>
199 #include "endianconv.h"
200 
201 int     add_boot_mips_filename(char *filename);
202 
203 static  int     boot_mips_write(FILE *outfile);
204 
205 #define MAX_NAMES 15
206 static char *boot_mips_filename[MAX_NAMES] =
207 {
208     NULL, NULL, NULL,
209     NULL, NULL, NULL,
210     NULL, NULL, NULL,
211     NULL, NULL, NULL,
212     NULL, NULL, NULL
213 };
214 
215 static int boot_mips_num_files = 0;
216 
217 #define SECTORS_PER_TRACK	32
218 #define BYTES_PER_SECTOR	512
219 
add_boot_mips_filename(char * filename)220 int add_boot_mips_filename(char *filename)
221 {
222     if (boot_mips_num_files < MAX_NAMES)
223     {
224         boot_mips_filename[boot_mips_num_files] = filename;
225         boot_mips_num_files++;
226     }
227 
228     else
229     {
230 #ifdef	USE_LIBSCHILY
231         comerrno(EX_BAD, "Too many MIPS boot files!\n");
232 #else
233         fprintf(stderr, "Too many MIPS boot files!\n");
234         exit(1);
235 #endif
236     }
237     return 0;
238 }
239 
vh_calc_checksum(struct volume_header * vh)240 static void vh_calc_checksum(struct volume_header *vh)
241 {
242 	uint32_t newsum = 0;
243 	unsigned char *buffer = (unsigned char *)vh;
244 	unsigned int i;
245 
246 	vh->vh_csum = 0;
247 
248 	for(i = 0; i < sizeof(struct volume_header); i += 4)
249         newsum -= read_be32(&buffer[i]);
250 
251     write_be32(newsum, (unsigned char *)&vh->vh_csum);
252 }
253 
file_base_name(char * path)254 static char *file_base_name(char *path)
255 {
256     char *endptr = path;
257     char *ptr = path;
258 
259     while (*ptr != '\0')
260     {
261         if ('/' == *ptr)
262             endptr = ++ptr;
263         else
264             ++ptr;
265     }
266     return endptr;
267 }
268 
boot_mips_write(FILE * outfile)269 static int boot_mips_write(FILE *outfile)
270 {
271 	struct directory_entry	*boot_file;	/* Boot file we need to search for */
272     unsigned long length = 0;
273     unsigned long extent = 0;
274 	int i;
275 	struct volume_header vh;
276     unsigned long long iso_size = 0;
277     char *filename = NULL;
278 
279 	memset(&vh, 0, sizeof(vh));
280 
281     iso_size = last_extent * 2048;
282 
283     write_be32(VHMAGIC, (unsigned char *)&vh.vh_magic);
284 
285 	/* Values from an IRIX cd */
286     write_be16(BYTES_PER_SECTOR, (unsigned char *)&vh.vh_dp.dp_secbytes);
287     write_be16(SECTORS_PER_TRACK, (unsigned char *)&vh.vh_dp.dp_secs);
288     write_be32(DP_RESEEK|DP_IGNOREERRORS|DP_TRKFWD, (unsigned char *)&vh.vh_dp.dp_flags);
289     write_be16(1, (unsigned char *)&vh.vh_dp.dp_trks0);
290 
291     write_be16((iso_size + BYTES_PER_SECTOR - 1) / (SECTORS_PER_TRACK * BYTES_PER_SECTOR),
292                (unsigned char *)&vh.vh_dp.dp_cyls);
293 
294 	for(i = 0; i < boot_mips_num_files; i++)
295     {
296         boot_file = search_tree_file(root, boot_mips_filename[i]);
297 
298         if (!boot_file) {
299 #ifdef	USE_LIBSCHILY
300             comerrno(EX_BAD, "Uh oh, I cant find the MIPS boot file '%s'!\n",
301                      boot_mips_filename[i]);
302 #else
303             fprintf(stderr, "Uh oh, I cant find the MIPS boot file '%s'!\n",
304                     boot_mips_filename[i]);
305             exit(1);
306 #endif
307         }
308 
309         extent = get_733(boot_file->isorec.extent) * 4;
310         length = ((get_733(boot_file->isorec.size) + 2047) / 2048) * 2048;
311         filename = file_base_name(boot_mips_filename[i]);
312 
313         strncpy((char *)vh.vh_vd[i].vd_name, filename, MIN(VDNAMESIZE, strlen(filename)));
314         write_be32(extent, (unsigned char *)&vh.vh_vd[i].vd_lbn);
315         write_be32(length, (unsigned char *)&vh.vh_vd[i].vd_nbytes);
316 
317         fprintf(stderr, "Found mips boot image %s, using extent %lu (0x%lX), #blocks %lu (0x%lX)\n",
318                 filename, extent, extent, length, length);
319 	}
320 
321 	/* Create volume partition on whole cd iso */
322     write_be32((iso_size + (BYTES_PER_SECTOR - 1))/ BYTES_PER_SECTOR, (unsigned char *)&vh.vh_pt[10].pt_nblks);
323     write_be32(0, (unsigned char *)&vh.vh_pt[10].pt_firstlbn);
324     write_be32(PTYPE_VOLUME, (unsigned char *)&vh.vh_pt[10].pt_type);
325 
326 	/* Create volume header partition, also on WHOLE cd iso */
327     write_be32((iso_size + (BYTES_PER_SECTOR - 1))/ BYTES_PER_SECTOR, (unsigned char *)&vh.vh_pt[8].pt_nblks);
328     write_be32(0, (unsigned char *)&vh.vh_pt[8].pt_firstlbn);
329     write_be32(PTYPE_VOLHDR, (unsigned char *)&vh.vh_pt[8].pt_type);
330 
331 	/* Create checksum */
332 	vh_calc_checksum(&vh);
333 
334     jtwrite(&vh, sizeof(vh), 1, 0, FALSE);
335     xfwrite(&vh, sizeof(vh), 1, outfile, 0, FALSE);
336     last_extent_written++;
337 
338 	return 0;
339 }
340 
341 struct output_fragment mipsboot_desc = {NULL, oneblock_size, NULL, boot_mips_write, "MIPS boot block"};
342