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
19FILE_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 */
44struct 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 */
60struct 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 */
81struct 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 */
109enum eltorito_media_type {
110	/** No emulation */
111	ELTORITO_NO_EMULATION = 0,
112};
113
114struct 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 */
123static 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 */
139static 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 */
173static 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 */
214static 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 */
263static 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 */
303static 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 */
332struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = {
333	.name = "El Torito",
334	.load = eltorito_load,
335	.exec = eltorito_exec,
336};
337