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