• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 FILE_LICENCE ( GPL2_OR_LATER );
20 
21 /**
22  * @file
23  *
24  * El Torito bootable ISO image format
25  *
26  */
27 
28 #include <stdint.h>
29 #include <errno.h>
30 #include <assert.h>
31 #include <realmode.h>
32 #include <bootsector.h>
33 #include <int13.h>
34 #include <gpxe/uaccess.h>
35 #include <gpxe/image.h>
36 #include <gpxe/segment.h>
37 #include <gpxe/ramdisk.h>
38 #include <gpxe/init.h>
39 
40 #define ISO9660_BLKSIZE 2048
41 #define ELTORITO_VOL_DESC_OFFSET ( 17 * ISO9660_BLKSIZE )
42 
43 /** An El Torito Boot Record Volume Descriptor */
44 struct eltorito_vol_desc {
45 	/** Boot record indicator; must be 0 */
46 	uint8_t record_indicator;
47 	/** ISO-9660 identifier; must be "CD001" */
48 	uint8_t iso9660_id[5];
49 	/** Version, must be 1 */
50 	uint8_t version;
51 	/** Boot system indicator; must be "EL TORITO SPECIFICATION" */
52 	uint8_t system_indicator[32];
53 	/** Unused */
54 	uint8_t unused[32];
55 	/** Boot catalog sector */
56 	uint32_t sector;
57 } __attribute__ (( packed ));
58 
59 /** An El Torito Boot Catalog Validation Entry */
60 struct eltorito_validation_entry {
61 	/** Header ID; must be 1 */
62 	uint8_t header_id;
63 	/** Platform ID
64 	 *
65 	 * 0 = 80x86
66 	 * 1 = PowerPC
67 	 * 2 = Mac
68 	 */
69 	uint8_t platform_id;
70 	/** Reserved */
71 	uint16_t reserved;
72 	/** ID string */
73 	uint8_t id_string[24];
74 	/** Checksum word */
75 	uint16_t checksum;
76 	/** Signature; must be 0xaa55 */
77 	uint16_t signature;
78 } __attribute__ (( packed ));
79 
80 /** A bootable entry in the El Torito Boot Catalog */
81 struct eltorito_boot_entry {
82 	/** Boot indicator
83 	 *
84 	 * Must be @c ELTORITO_BOOTABLE for a bootable ISO image
85 	 */
86 	uint8_t indicator;
87 	/** Media type
88 	 *
89 	 */
90 	uint8_t media_type;
91 	/** Load segment */
92 	uint16_t load_segment;
93 	/** System type */
94 	uint8_t filesystem;
95 	/** Unused */
96 	uint8_t reserved_a;
97 	/** Sector count */
98 	uint16_t length;
99 	/** Starting sector */
100 	uint32_t start;
101 	/** Unused */
102 	uint8_t reserved_b[20];
103 } __attribute__ (( packed ));
104 
105 /** Boot indicator for a bootable ISO image */
106 #define ELTORITO_BOOTABLE 0x88
107 
108 /** El Torito media types */
109 enum eltorito_media_type {
110 	/** No emulation */
111 	ELTORITO_NO_EMULATION = 0,
112 };
113 
114 struct image_type eltorito_image_type __image_type ( PROBE_NORMAL );
115 
116 /**
117  * Calculate 16-bit word checksum
118  *
119  * @v data		Data to checksum
120  * @v len		Length (in bytes, must be even)
121  * @ret sum		Checksum
122  */
word_checksum(void * data,size_t len)123 static unsigned int word_checksum ( void *data, size_t len ) {
124 	uint16_t *words;
125 	uint16_t sum = 0;
126 
127 	for ( words = data ; len ; words++, len -= 2 ) {
128 		sum += *words;
129 	}
130 	return sum;
131 }
132 
133 /**
134  * Execute El Torito image
135  *
136  * @v image		El Torito image
137  * @ret rc		Return status code
138  */
eltorito_exec(struct image * image)139 static int eltorito_exec ( struct image *image ) {
140 	struct ramdisk ramdisk;
141 	struct int13_drive int13_drive;
142 	unsigned int load_segment = image->priv.ul;
143 	unsigned int load_offset = ( load_segment ? 0 : 0x7c00 );
144 	int rc;
145 
146 	memset ( &ramdisk, 0, sizeof ( ramdisk ) );
147 	init_ramdisk ( &ramdisk, image->data, image->len, ISO9660_BLKSIZE );
148 
149 	memset ( &int13_drive, 0, sizeof ( int13_drive ) );
150 	int13_drive.blockdev = &ramdisk.blockdev;
151 	register_int13_drive ( &int13_drive );
152 
153 	if ( ( rc = call_bootsector ( load_segment, load_offset,
154 				      int13_drive.drive ) ) != 0 ) {
155 		DBGC ( image, "ElTorito %p boot failed: %s\n",
156 		       image, strerror ( rc ) );
157 		goto err;
158 	}
159 
160 	rc = -ECANCELED; /* -EIMPOSSIBLE */
161  err:
162 	unregister_int13_drive ( &int13_drive );
163 	return rc;
164 }
165 
166 /**
167  * Read and verify El Torito Boot Record Volume Descriptor
168  *
169  * @v image		El Torito file
170  * @ret catalog_offset	Offset of Boot Catalog
171  * @ret rc		Return status code
172  */
eltorito_read_voldesc(struct image * image,unsigned long * catalog_offset)173 static int eltorito_read_voldesc ( struct image *image,
174 				   unsigned long *catalog_offset ) {
175 	static const struct eltorito_vol_desc vol_desc_signature = {
176 		.record_indicator = 0,
177 		.iso9660_id = "CD001",
178 		.version = 1,
179 		.system_indicator = "EL TORITO SPECIFICATION",
180 	};
181 	struct eltorito_vol_desc vol_desc;
182 
183 	/* Sanity check */
184 	if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) {
185 		DBGC ( image, "ElTorito %p too short\n", image );
186 		return -ENOEXEC;
187 	}
188 
189 	/* Read and verify Boot Record Volume Descriptor */
190 	copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET,
191 			 sizeof ( vol_desc ) );
192 	if ( memcmp ( &vol_desc, &vol_desc_signature,
193 		      offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) {
194 		DBGC ( image, "ElTorito %p invalid Boot Record Volume "
195 		       "Descriptor\n", image );
196 		return -ENOEXEC;
197 	}
198 	*catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE );
199 
200 	DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n",
201 	       image, *catalog_offset );
202 
203 	return 0;
204 }
205 
206 /**
207  * Read and verify El Torito Boot Catalog
208  *
209  * @v image		El Torito file
210  * @v catalog_offset	Offset of Boot Catalog
211  * @ret boot_entry	El Torito boot entry
212  * @ret rc		Return status code
213  */
eltorito_read_catalog(struct image * image,unsigned long catalog_offset,struct eltorito_boot_entry * boot_entry)214 static int eltorito_read_catalog ( struct image *image,
215 				   unsigned long catalog_offset,
216 				   struct eltorito_boot_entry *boot_entry ) {
217 	struct eltorito_validation_entry validation_entry;
218 
219 	/* Sanity check */
220 	if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) {
221 		DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n",
222 		       image, catalog_offset );
223 		return -ENOEXEC;
224 	}
225 
226 	/* Read and verify the Validation Entry of the Boot Catalog */
227 	copy_from_user ( &validation_entry, image->data, catalog_offset,
228 			 sizeof ( validation_entry ) );
229 	if ( word_checksum ( &validation_entry,
230 			     sizeof ( validation_entry ) ) != 0 ) {
231 		DBGC ( image, "ElTorito %p bad Validation Entry checksum\n",
232 		       image );
233 		return -ENOEXEC;
234 	}
235 
236 	/* Read and verify the Initial/Default entry */
237 	copy_from_user ( boot_entry, image->data,
238 			 ( catalog_offset + sizeof ( validation_entry ) ),
239 			 sizeof ( *boot_entry ) );
240 	if ( boot_entry->indicator != ELTORITO_BOOTABLE ) {
241 		DBGC ( image, "ElTorito %p not bootable\n", image );
242 		return -ENOEXEC;
243 	}
244 	if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) {
245 		DBGC ( image, "ElTorito %p cannot support media type %d\n",
246 		       image, boot_entry->media_type );
247 		return -ENOTSUP;
248 	}
249 
250 	DBGC ( image, "ElTorito %p media type %d segment %04x\n",
251 	       image, boot_entry->media_type, boot_entry->load_segment );
252 
253 	return 0;
254 }
255 
256 /**
257  * Load El Torito virtual disk image into memory
258  *
259  * @v image		El Torito file
260  * @v boot_entry	El Torito boot entry
261  * @ret rc		Return status code
262  */
eltorito_load_disk(struct image * image,struct eltorito_boot_entry * boot_entry)263 static int eltorito_load_disk ( struct image *image,
264 				struct eltorito_boot_entry *boot_entry ) {
265 	unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE );
266 	unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE );
267 	unsigned int load_segment;
268 	userptr_t buffer;
269 	int rc;
270 
271 	/* Sanity check */
272 	if ( image->len < ( start + length ) ) {
273 		DBGC ( image, "ElTorito %p virtual disk lies outside image\n",
274 		       image );
275 		return -ENOEXEC;
276 	}
277 	DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n",
278 	       image, start, length );
279 
280 	/* Calculate load address */
281 	load_segment = boot_entry->load_segment;
282 	buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) );
283 
284 	/* Verify and prepare segment */
285 	if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) {
286 		DBGC ( image, "ElTorito %p could not prepare segment: %s\n",
287 		       image, strerror ( rc ) );
288 		return rc;
289 	}
290 
291 	/* Copy image to segment */
292 	memcpy_user ( buffer, 0, image->data, start, length );
293 
294 	return 0;
295 }
296 
297 /**
298  * Load El Torito image into memory
299  *
300  * @v image		El Torito file
301  * @ret rc		Return status code
302  */
eltorito_load(struct image * image)303 static int eltorito_load ( struct image *image ) {
304 	struct eltorito_boot_entry boot_entry;
305 	unsigned long bootcat_offset;
306 	int rc;
307 
308 	/* Read Boot Record Volume Descriptor, if present */
309 	if ( ( rc = eltorito_read_voldesc ( image, &bootcat_offset ) ) != 0 )
310 		return rc;
311 
312 	/* This is an El Torito image, valid or otherwise */
313 	if ( ! image->type )
314 		image->type = &eltorito_image_type;
315 
316 	/* Read Boot Catalog */
317 	if ( ( rc = eltorito_read_catalog ( image, bootcat_offset,
318 					    &boot_entry ) ) != 0 )
319 		return rc;
320 
321 	/* Load Virtual Disk image */
322 	if ( ( rc = eltorito_load_disk ( image, &boot_entry ) ) != 0 )
323 		return rc;
324 
325 	/* Record load segment in image private data field */
326 	image->priv.ul = boot_entry.load_segment;
327 
328 	return 0;
329 }
330 
331 /** El Torito image type */
332 struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = {
333 	.name = "El Torito",
334 	.load = eltorito_load,
335 	.exec = eltorito_exec,
336 };
337