|
@@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
33
|
33
|
#include <assert.h>
|
34
|
34
|
#include <realmode.h>
|
35
|
35
|
#include <bzimage.h>
|
|
36
|
+#include <initrd.h>
|
36
|
37
|
#include <ipxe/uaccess.h>
|
37
|
38
|
#include <ipxe/image.h>
|
38
|
39
|
#include <ipxe/segment.h>
|
|
@@ -304,11 +305,10 @@ static int bzimage_parse_cmdline ( struct image *image,
|
304
|
305
|
* @v image bzImage image
|
305
|
306
|
* @v bzimg bzImage context
|
306
|
307
|
* @v cmdline Kernel command line
|
307
|
|
- * @ret rc Return status code
|
308
|
308
|
*/
|
309
|
|
-static int bzimage_set_cmdline ( struct image *image,
|
310
|
|
- struct bzimage_context *bzimg,
|
311
|
|
- const char *cmdline ) {
|
|
309
|
+static void bzimage_set_cmdline ( struct image *image,
|
|
310
|
+ struct bzimage_context *bzimg,
|
|
311
|
+ const char *cmdline ) {
|
312
|
312
|
size_t cmdline_len;
|
313
|
313
|
|
314
|
314
|
/* Copy command line down to real-mode portion */
|
|
@@ -318,8 +318,6 @@ static int bzimage_set_cmdline ( struct image *image,
|
318
|
318
|
copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline,
|
319
|
319
|
cmdline, cmdline_len );
|
320
|
320
|
DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
|
321
|
|
-
|
322
|
|
- return 0;
|
323
|
321
|
}
|
324
|
322
|
|
325
|
323
|
/**
|
|
@@ -354,7 +352,7 @@ static void bzimage_parse_cpio_cmdline ( struct image *image,
|
354
|
352
|
* @v image bzImage image
|
355
|
353
|
* @v initrd initrd image
|
356
|
354
|
* @v address Address at which to load, or UNULL
|
357
|
|
- * @ret len Length of loaded image, rounded up to 4 bytes
|
|
355
|
+ * @ret len Length of loaded image, rounded up to INITRD_ALIGN
|
358
|
356
|
*/
|
359
|
357
|
static size_t bzimage_load_initrd ( struct image *image,
|
360
|
358
|
struct image *initrd,
|
|
@@ -364,6 +362,7 @@ static size_t bzimage_load_initrd ( struct image *image,
|
364
|
362
|
struct cpio_header cpio;
|
365
|
363
|
size_t offset = 0;
|
366
|
364
|
size_t name_len;
|
|
365
|
+ size_t pad_len;
|
367
|
366
|
|
368
|
367
|
/* Do not include kernel image itself as an initrd */
|
369
|
368
|
if ( initrd == image )
|
|
@@ -371,8 +370,6 @@ static size_t bzimage_load_initrd ( struct image *image,
|
371
|
370
|
|
372
|
371
|
/* Create cpio header before non-prebuilt images */
|
373
|
372
|
if ( filename && filename[0] ) {
|
374
|
|
- DBGC ( image, "bzImage %p inserting initrd %p as %s\n",
|
375
|
|
- image, initrd, filename );
|
376
|
373
|
cmdline = strchr ( filename, ' ' );
|
377
|
374
|
name_len = ( ( cmdline ? ( ( size_t ) ( cmdline - filename ) )
|
378
|
375
|
: strlen ( filename ) ) + 1 /* NUL */ );
|
|
@@ -402,80 +399,146 @@ static size_t bzimage_load_initrd ( struct image *image,
|
402
|
399
|
|
403
|
400
|
/* Copy in initrd image body */
|
404
|
401
|
if ( address )
|
405
|
|
- memcpy_user ( address, offset, initrd->data, 0, initrd->len );
|
406
|
|
- offset += initrd->len;
|
|
402
|
+ memmove_user ( address, offset, initrd->data, 0, initrd->len );
|
407
|
403
|
if ( address ) {
|
408
|
|
- DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n",
|
409
|
|
- image, initrd, user_to_phys ( address, 0 ),
|
410
|
|
- user_to_phys ( address, offset ) );
|
|
404
|
+ DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)"
|
|
405
|
+ "%s%s\n", image, initrd, user_to_phys ( address, 0 ),
|
|
406
|
+ user_to_phys ( address, offset ),
|
|
407
|
+ user_to_phys ( address, ( offset + initrd->len ) ),
|
|
408
|
+ ( filename ? " " : "" ), ( filename ? filename : "" ) );
|
|
409
|
+ DBGC2_MD5A ( image, user_to_phys ( address, offset ),
|
|
410
|
+ user_to_virt ( address, offset ), initrd->len );
|
411
|
411
|
}
|
|
412
|
+ offset += initrd->len;
|
|
413
|
+
|
|
414
|
+ /* Round up to multiple of INITRD_ALIGN and zero-pad */
|
|
415
|
+ pad_len = ( ( -offset ) & ( INITRD_ALIGN - 1 ) );
|
|
416
|
+ if ( address )
|
|
417
|
+ memset_user ( address, offset, 0, pad_len );
|
|
418
|
+ offset += pad_len;
|
412
|
419
|
|
413
|
|
- /* Round up to 4-byte boundary */
|
414
|
|
- offset = ( ( offset + 0x03 ) & ~0x03 );
|
415
|
420
|
return offset;
|
416
|
421
|
}
|
417
|
422
|
|
418
|
423
|
/**
|
419
|
|
- * Load initrds, if any
|
|
424
|
+ * Check that initrds can be loaded
|
420
|
425
|
*
|
421
|
426
|
* @v image bzImage image
|
422
|
427
|
* @v bzimg bzImage context
|
423
|
428
|
* @ret rc Return status code
|
424
|
429
|
*/
|
425
|
|
-static int bzimage_load_initrds ( struct image *image,
|
426
|
|
- struct bzimage_context *bzimg ) {
|
|
430
|
+static int bzimage_check_initrds ( struct image *image,
|
|
431
|
+ struct bzimage_context *bzimg ) {
|
427
|
432
|
struct image *initrd;
|
428
|
|
- size_t total_len = 0;
|
429
|
|
- physaddr_t address;
|
|
433
|
+ userptr_t bottom;
|
|
434
|
+ size_t len = 0;
|
430
|
435
|
int rc;
|
431
|
436
|
|
432
|
|
- /* Add up length of all initrd images */
|
433
|
|
- for_each_image ( initrd )
|
434
|
|
- total_len += bzimage_load_initrd ( image, initrd, UNULL );
|
|
437
|
+ /* Calculate total loaded length of initrds */
|
|
438
|
+ for_each_image ( initrd ) {
|
435
|
439
|
|
436
|
|
- /* Give up if no initrd images found */
|
437
|
|
- if ( ! total_len )
|
438
|
|
- return 0;
|
|
440
|
+ /* Skip kernel */
|
|
441
|
+ if ( initrd == image )
|
|
442
|
+ continue;
|
|
443
|
+
|
|
444
|
+ /* Calculate length */
|
|
445
|
+ len += bzimage_load_initrd ( image, initrd, UNULL );
|
439
|
446
|
|
440
|
|
- /* Find a suitable start address. Try 1MB boundaries,
|
441
|
|
- * starting from the downloaded kernel image itself and
|
442
|
|
- * working downwards until we hit an available region.
|
|
447
|
+ DBGC ( image, "bzImage %p initrd %p from [%#08lx,%#08lx)%s%s\n",
|
|
448
|
+ image, initrd, user_to_phys ( initrd->data, 0 ),
|
|
449
|
+ user_to_phys ( initrd->data, initrd->len ),
|
|
450
|
+ ( initrd->cmdline ? " " : "" ),
|
|
451
|
+ ( initrd->cmdline ? initrd->cmdline : "" ) );
|
|
452
|
+ DBGC2_MD5A ( image, user_to_phys ( initrd->data, 0 ),
|
|
453
|
+ user_to_virt ( initrd->data, 0 ), initrd->len );
|
|
454
|
+ }
|
|
455
|
+
|
|
456
|
+ /* Calculate lowest usable address */
|
|
457
|
+ bottom = userptr_add ( bzimg->pm_kernel, bzimg->pm_sz );
|
|
458
|
+
|
|
459
|
+ /* Check that total length fits within space available for
|
|
460
|
+ * reshuffling. This is a conservative check, since CPIO
|
|
461
|
+ * headers are not present during reshuffling, but this
|
|
462
|
+ * doesn't hurt and keeps the code simple.
|
443
|
463
|
*/
|
444
|
|
- for ( address = ( user_to_phys ( image->data, 0 ) & ~0xfffff ) ; ;
|
445
|
|
- address -= 0x100000 ) {
|
446
|
|
- /* Check that we're not going to overwrite the
|
447
|
|
- * kernel itself. This check isn't totally
|
448
|
|
- * accurate, but errs on the side of caution.
|
449
|
|
- */
|
450
|
|
- if ( address <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) {
|
451
|
|
- DBGC ( image, "bzImage %p could not find a location "
|
452
|
|
- "for initrd\n", image );
|
453
|
|
- return -ENOBUFS;
|
454
|
|
- }
|
455
|
|
- /* Check that we are within the kernel's range */
|
456
|
|
- if ( ( address + total_len - 1 ) > bzimg->mem_limit )
|
457
|
|
- continue;
|
458
|
|
- /* Prepare and verify segment */
|
459
|
|
- if ( ( rc = prep_segment ( phys_to_user ( address ), 0,
|
460
|
|
- total_len ) ) != 0 )
|
461
|
|
- continue;
|
462
|
|
- /* Use this address */
|
463
|
|
- break;
|
|
464
|
+ if ( ( rc = initrd_reshuffle_check ( len, bottom ) ) != 0 ) {
|
|
465
|
+ DBGC ( image, "bzImage %p failed reshuffle check: %s\n",
|
|
466
|
+ image, strerror ( rc ) );
|
|
467
|
+ return rc;
|
464
|
468
|
}
|
465
|
469
|
|
466
|
|
- /* Record initrd location */
|
467
|
|
- bzimg->ramdisk_image = address;
|
468
|
|
- bzimg->ramdisk_size = total_len;
|
|
470
|
+ /* Check that total length fits within kernel's memory limit */
|
|
471
|
+ if ( user_to_phys ( bottom, len ) > bzimg->mem_limit ) {
|
|
472
|
+ DBGC ( image, "bzImage %p not enough space for initrds\n",
|
|
473
|
+ image );
|
|
474
|
+ return -ENOBUFS;
|
|
475
|
+ }
|
|
476
|
+
|
|
477
|
+ return 0;
|
|
478
|
+}
|
|
479
|
+
|
|
480
|
+/**
|
|
481
|
+ * Load initrds, if any
|
|
482
|
+ *
|
|
483
|
+ * @v image bzImage image
|
|
484
|
+ * @v bzimg bzImage context
|
|
485
|
+ */
|
|
486
|
+static void bzimage_load_initrds ( struct image *image,
|
|
487
|
+ struct bzimage_context *bzimg ) {
|
|
488
|
+ struct image *initrd;
|
|
489
|
+ struct image *highest = NULL;
|
|
490
|
+ struct image *other;
|
|
491
|
+ userptr_t top;
|
|
492
|
+ userptr_t dest;
|
|
493
|
+ size_t len;
|
|
494
|
+
|
|
495
|
+ /* Reshuffle initrds into desired order */
|
|
496
|
+ initrd_reshuffle ( userptr_add ( bzimg->pm_kernel, bzimg->pm_sz ) );
|
469
|
497
|
|
470
|
|
- /* Construct initrd */
|
471
|
|
- DBGC ( image, "bzImage %p constructing initrd at [%lx,%lx)\n",
|
472
|
|
- image, address, ( address + total_len ) );
|
|
498
|
+ /* Find highest initrd */
|
473
|
499
|
for_each_image ( initrd ) {
|
474
|
|
- address += bzimage_load_initrd ( image, initrd,
|
475
|
|
- phys_to_user ( address ) );
|
|
500
|
+ if ( ( highest == NULL ) ||
|
|
501
|
+ ( userptr_sub ( initrd->data, highest->data ) > 0 ) ) {
|
|
502
|
+ highest = initrd;
|
|
503
|
+ }
|
476
|
504
|
}
|
477
|
505
|
|
478
|
|
- return 0;
|
|
506
|
+ /* Do nothing if there are no initrds */
|
|
507
|
+ if ( ! highest )
|
|
508
|
+ return;
|
|
509
|
+
|
|
510
|
+ /* Find highest usable address */
|
|
511
|
+ top = userptr_add ( highest->data,
|
|
512
|
+ ( ( highest->len + INITRD_ALIGN - 1 ) &
|
|
513
|
+ ~( INITRD_ALIGN - 1 ) ) );
|
|
514
|
+ if ( user_to_phys ( top, 0 ) > bzimg->mem_limit )
|
|
515
|
+ top = phys_to_user ( bzimg->mem_limit );
|
|
516
|
+ DBGC ( image, "bzImage %p loading initrds from %#08lx downwards\n",
|
|
517
|
+ image, user_to_phys ( top, 0 ) );
|
|
518
|
+
|
|
519
|
+ /* Load initrds in order */
|
|
520
|
+ for_each_image ( initrd ) {
|
|
521
|
+
|
|
522
|
+ /* Calculate cumulative length of following
|
|
523
|
+ * initrds (including padding).
|
|
524
|
+ */
|
|
525
|
+ len = 0;
|
|
526
|
+ for_each_image ( other ) {
|
|
527
|
+ if ( other == initrd )
|
|
528
|
+ len = 0;
|
|
529
|
+ len += bzimage_load_initrd ( image, other, UNULL );
|
|
530
|
+ }
|
|
531
|
+
|
|
532
|
+ /* Load initrd at this address */
|
|
533
|
+ dest = userptr_add ( top, -len );
|
|
534
|
+ bzimage_load_initrd ( image, initrd, dest );
|
|
535
|
+
|
|
536
|
+ /* Record initrd location */
|
|
537
|
+ if ( ! bzimg->ramdisk_image ) {
|
|
538
|
+ bzimg->ramdisk_image = user_to_phys ( dest, 0 );
|
|
539
|
+ bzimg->ramdisk_size = len;
|
|
540
|
+ }
|
|
541
|
+ }
|
479
|
542
|
}
|
480
|
543
|
|
481
|
544
|
/**
|
|
@@ -508,33 +571,37 @@ static int bzimage_exec ( struct image *image ) {
|
508
|
571
|
return rc;
|
509
|
572
|
}
|
510
|
573
|
|
|
574
|
+ /* Parse command line for bootloader parameters */
|
|
575
|
+ if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0)
|
|
576
|
+ return rc;
|
|
577
|
+
|
|
578
|
+ /* Check that initrds can be loaded */
|
|
579
|
+ if ( ( rc = bzimage_check_initrds ( image, &bzimg ) ) != 0 )
|
|
580
|
+ return rc;
|
|
581
|
+
|
|
582
|
+ /* Remove kernel from image list (without invalidating image pointer) */
|
|
583
|
+ unregister_image ( image_get ( image ) );
|
|
584
|
+
|
511
|
585
|
/* Load segments */
|
512
|
586
|
memcpy_user ( bzimg.rm_kernel, 0, image->data,
|
513
|
587
|
0, bzimg.rm_filesz );
|
514
|
588
|
memcpy_user ( bzimg.pm_kernel, 0, image->data,
|
515
|
589
|
bzimg.rm_filesz, bzimg.pm_sz );
|
516
|
590
|
|
517
|
|
- /* Update and write out header */
|
518
|
|
- bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
|
519
|
|
-
|
520
|
|
- /* Parse command line for bootloader parameters */
|
521
|
|
- if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0)
|
522
|
|
- return rc;
|
523
|
|
-
|
524
|
591
|
/* Store command line */
|
525
|
|
- if ( ( rc = bzimage_set_cmdline ( image, &bzimg, cmdline ) ) != 0 )
|
526
|
|
- return rc;
|
|
592
|
+ bzimage_set_cmdline ( image, &bzimg, cmdline );
|
|
593
|
+
|
|
594
|
+ /* Prepare for exiting. Must do this before loading initrds,
|
|
595
|
+ * since loading the initrds will corrupt the external heap.
|
|
596
|
+ */
|
|
597
|
+ shutdown_boot();
|
527
|
598
|
|
528
|
599
|
/* Load any initrds */
|
529
|
|
- if ( ( rc = bzimage_load_initrds ( image, &bzimg ) ) != 0 )
|
530
|
|
- return rc;
|
|
600
|
+ bzimage_load_initrds ( image, &bzimg );
|
531
|
601
|
|
532
|
602
|
/* Update kernel header */
|
533
|
603
|
bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
|
534
|
604
|
|
535
|
|
- /* Prepare for exiting */
|
536
|
|
- shutdown_boot();
|
537
|
|
-
|
538
|
605
|
DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
|
539
|
606
|
"(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ),
|
540
|
607
|
bzimg.rm_kernel_seg, bzimg.rm_heap );
|