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