00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include <stdio.h>
00029 #include <errno.h>
00030 #include <assert.h>
00031 #include <realmode.h>
00032 #include <multiboot.h>
00033 #include <gpxe/uaccess.h>
00034 #include <gpxe/image.h>
00035 #include <gpxe/segment.h>
00036 #include <gpxe/memmap.h>
00037 #include <gpxe/elf.h>
00038 #include <gpxe/init.h>
00039 #include <gpxe/features.h>
00040
00041 FEATURE ( FEATURE_IMAGE, "Multiboot", DHCP_EB_FEATURE_MULTIBOOT, 1 );
00042
00043 struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT );
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055 #define MAX_MODULES 8
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065 #define MB_MAX_CMDLINE 512
00066
00067
00068 #define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \
00069 MB_FLAG_VIDMODE | MB_FLAG_RAW )
00070
00071
00072 #define MB_COMPULSORY_FLAGS 0x0000ffff
00073
00074
00075 #define MB_OPTIONAL_FLAGS 0xffff0000
00076
00077
00078
00079
00080
00081
00082
00083 #define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS )
00084
00085
00086 struct multiboot_header_info {
00087
00088 struct multiboot_header mb;
00089
00090 size_t offset;
00091 };
00092
00093
00094 static char __bss16_array ( mb_cmdlines, [MB_MAX_CMDLINE] );
00095 #define mb_cmdlines __use_data16 ( mb_cmdlines )
00096
00097
00098 static unsigned int mb_cmdline_offset;
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108 static void multiboot_build_memmap ( struct image *image,
00109 struct multiboot_info *mbinfo,
00110 struct multiboot_memory_map *mbmemmap,
00111 unsigned int limit ) {
00112 struct memory_map memmap;
00113 unsigned int i;
00114
00115
00116 get_memmap ( &memmap );
00117
00118
00119 memset ( mbmemmap, 0, sizeof ( *mbmemmap ) );
00120 for ( i = 0 ; i < memmap.count ; i++ ) {
00121 if ( i >= limit ) {
00122 DBGC ( image, "MULTIBOOT %p limit of %d memmap "
00123 "entries reached\n", image, limit );
00124 break;
00125 }
00126 mbmemmap[i].size = ( sizeof ( mbmemmap[i] ) -
00127 sizeof ( mbmemmap[i].size ) );
00128 mbmemmap[i].base_addr = memmap.regions[i].start;
00129 mbmemmap[i].length = ( memmap.regions[i].end -
00130 memmap.regions[i].start );
00131 mbmemmap[i].type = MBMEM_RAM;
00132 mbinfo->mmap_length += sizeof ( mbmemmap[i] );
00133 if ( memmap.regions[i].start == 0 )
00134 mbinfo->mem_lower = ( memmap.regions[i].end / 1024 );
00135 if ( memmap.regions[i].start == 0x100000 )
00136 mbinfo->mem_upper = ( ( memmap.regions[i].end -
00137 0x100000 ) / 1024 );
00138 }
00139 }
00140
00141
00142
00143
00144
00145
00146
00147
00148 physaddr_t multiboot_add_cmdline ( const char *imgname, const char *cmdline ) {
00149 char *mb_cmdline;
00150
00151 if ( ! cmdline )
00152 cmdline = "";
00153
00154
00155 mb_cmdline = ( mb_cmdlines + mb_cmdline_offset );
00156 mb_cmdline_offset +=
00157 ( snprintf ( mb_cmdline,
00158 ( sizeof ( mb_cmdlines ) - mb_cmdline_offset ),
00159 "%s %s", imgname, cmdline ) + 1 );
00160
00161
00162 if ( mb_cmdline_offset > sizeof ( mb_cmdlines ) )
00163 mb_cmdline_offset = ( sizeof ( mb_cmdlines ) - 1 );
00164
00165 return virt_to_phys ( mb_cmdline );
00166 }
00167
00168
00169
00170
00171
00172
00173
00174
00175 static unsigned int
00176 multiboot_build_module_list ( struct image *image,
00177 struct multiboot_module *modules,
00178 unsigned int limit ) {
00179 struct image *module_image;
00180 struct multiboot_module *module;
00181 unsigned int count = 0;
00182 unsigned int insert;
00183 physaddr_t start;
00184 physaddr_t end;
00185 unsigned int i;
00186
00187
00188 for_each_image ( module_image ) {
00189
00190 if ( count >= limit ) {
00191 DBGC ( image, "MULTIBOOT %p limit of %d modules "
00192 "reached\n", image, limit );
00193 break;
00194 }
00195
00196
00197 if ( module_image == image )
00198 continue;
00199
00200
00201
00202
00203 start = user_to_phys ( module_image->data, 0 );
00204 end = user_to_phys ( module_image->data, module_image->len );
00205 for ( insert = 0 ; insert < count ; insert++ ) {
00206 if ( start < modules[insert].mod_start )
00207 break;
00208 }
00209 module = &modules[insert];
00210 memmove ( ( module + 1 ), module,
00211 ( ( count - insert ) * sizeof ( *module ) ) );
00212 module->mod_start = start;
00213 module->mod_end = end;
00214 module->string = multiboot_add_cmdline ( module_image->name,
00215 module_image->cmdline );
00216 module->reserved = 0;
00217
00218
00219 assert ( ( module->mod_start & 0xfff ) == 0 );
00220
00221 count++;
00222 }
00223
00224
00225 for ( i = 0 ; i < count ; i++ ) {
00226 DBGC ( image, "MULTIBOOT %p module %d is [%x,%x)\n",
00227 image, i, modules[i].mod_start,
00228 modules[i].mod_end );
00229 }
00230
00231 return count;
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241 static struct multiboot_info __bss16 ( mbinfo );
00242 #define mbinfo __use_data16 ( mbinfo )
00243
00244
00245 static char __data16_array ( mb_bootloader_name, [] ) = "gPXE " VERSION;
00246 #define mb_bootloader_name __use_data16 ( mb_bootloader_name )
00247
00248
00249 static struct multiboot_memory_map
00250 __bss16_array ( mbmemmap, [MAX_MEMORY_REGIONS] );
00251 #define mbmemmap __use_data16 ( mbmemmap )
00252
00253
00254 static struct multiboot_module __bss16_array ( mbmodules, [MAX_MODULES] );
00255 #define mbmodules __use_data16 ( mbmodules )
00256
00257
00258
00259
00260
00261
00262
00263 static int multiboot_exec ( struct image *image ) {
00264 physaddr_t entry = image->priv.phys;
00265
00266
00267 memset ( &mbinfo, 0, sizeof ( mbinfo ) );
00268 mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP |
00269 MBI_FLAG_CMDLINE | MBI_FLAG_MODS );
00270 mb_cmdline_offset = 0;
00271 mbinfo.cmdline = multiboot_add_cmdline ( image->name, image->cmdline );
00272 mbinfo.mods_count = multiboot_build_module_list ( image, mbmodules,
00273 ( sizeof(mbmodules) / sizeof(mbmodules[0]) ) );
00274 mbinfo.mods_addr = virt_to_phys ( mbmodules );
00275 mbinfo.mmap_addr = virt_to_phys ( mbmemmap );
00276 mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name );
00277
00278
00279
00280
00281 shutdown ( SHUTDOWN_BOOT );
00282
00283
00284
00285
00286 multiboot_build_memmap ( image, &mbinfo, mbmemmap,
00287 ( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) );
00288
00289
00290 DBGC ( image, "MULTIBOOT %p starting execution at %lx\n",
00291 image, entry );
00292 __asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t"
00293 "call *%%edi\n\t"
00294 "popl %%ebp\n\t" )
00295 : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ),
00296 "b" ( virt_to_phys ( &mbinfo ) ),
00297 "D" ( entry )
00298 : "ecx", "edx", "esi", "memory" );
00299
00300 DBGC ( image, "MULTIBOOT %p returned\n", image );
00301
00302
00303 while ( 1 ) {}
00304
00305 return -ECANCELED;
00306 }
00307
00308
00309
00310
00311
00312
00313
00314
00315 static int multiboot_find_header ( struct image *image,
00316 struct multiboot_header_info *hdr ) {
00317 uint32_t buf[64];
00318 size_t offset;
00319 unsigned int buf_idx;
00320 uint32_t checksum;
00321
00322
00323
00324
00325
00326 for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) {
00327
00328 if ( offset > image->len )
00329 break;
00330
00331 buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) );
00332 if ( buf_idx == 0 ) {
00333 copy_from_user ( buf, image->data, offset,
00334 sizeof ( buf ) );
00335 }
00336
00337 if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC )
00338 continue;
00339
00340 copy_from_user ( &hdr->mb, image->data, offset,
00341 sizeof ( hdr->mb ) );
00342 checksum = ( hdr->mb.magic + hdr->mb.flags +
00343 hdr->mb.checksum );
00344 if ( checksum != 0 )
00345 continue;
00346
00347 hdr->offset = offset;
00348 return 0;
00349 }
00350
00351
00352 return -ENOEXEC;
00353 }
00354
00355
00356
00357
00358
00359
00360
00361
00362 static int multiboot_load_raw ( struct image *image,
00363 struct multiboot_header_info *hdr ) {
00364 size_t offset;
00365 size_t filesz;
00366 size_t memsz;
00367 userptr_t buffer;
00368 int rc;
00369
00370
00371 if ( ! ( hdr->mb.flags & MB_FLAG_RAW ) ) {
00372 DBGC ( image, "MULTIBOOT %p is not flagged as a raw image\n",
00373 image );
00374 return -EINVAL;
00375 }
00376
00377
00378 offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
00379 filesz = ( hdr->mb.load_end_addr ?
00380 ( hdr->mb.load_end_addr - hdr->mb.load_addr ) :
00381 ( image->len - offset ) );
00382 memsz = ( hdr->mb.bss_end_addr ?
00383 ( hdr->mb.bss_end_addr - hdr->mb.load_addr ) : filesz );
00384 buffer = phys_to_user ( hdr->mb.load_addr );
00385 if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
00386 DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n",
00387 image, strerror ( rc ) );
00388 return rc;
00389 }
00390
00391
00392 memcpy_user ( buffer, 0, image->data, offset, filesz );
00393
00394
00395 image->priv.phys = hdr->mb.entry_addr;
00396
00397 return 0;
00398 }
00399
00400
00401
00402
00403
00404
00405
00406 static int multiboot_load_elf ( struct image *image ) {
00407 int rc;
00408
00409
00410 if ( ( rc = elf_load ( image ) ) != 0 ) {
00411 DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n",
00412 image, strerror ( rc ) );
00413 return rc;
00414 }
00415
00416 return 0;
00417 }
00418
00419
00420
00421
00422
00423
00424
00425 static int multiboot_load ( struct image *image ) {
00426 struct multiboot_header_info hdr;
00427 int rc;
00428
00429
00430 if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
00431 DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
00432 image );
00433 return rc;
00434 }
00435 DBGC ( image, "MULTIBOOT %p found header with flags %08x\n",
00436 image, hdr.mb.flags );
00437
00438
00439 if ( ! image->type )
00440 image->type = &multiboot_image_type;
00441
00442
00443 if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) {
00444 DBGC ( image, "MULTIBOOT %p flags %08x not supported\n",
00445 image, ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) );
00446 return -ENOTSUP;
00447 }
00448
00449
00450
00451
00452
00453
00454 if ( ( ( rc = multiboot_load_elf ( image ) ) != 0 ) &&
00455 ( ( rc = multiboot_load_raw ( image, &hdr ) ) != 0 ) )
00456 return rc;
00457
00458 return 0;
00459 }
00460
00461
00462 struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT ) = {
00463 .name = "Multiboot",
00464 .load = multiboot_load,
00465 .exec = multiboot_exec,
00466 };