Browse Source

[bzimage] Allow initrds to be rearranged in place

At present, loading a bzImage via iPXE requires enough RAM to hold two
copies of each initrd file.  Remove this constraint by rearranging the
initrds in place.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 11 years ago
parent
commit
d6b0b76a05
1 changed files with 140 additions and 73 deletions
  1. 140
    73
      src/arch/i386/image/bzimage.c

+ 140
- 73
src/arch/i386/image/bzimage.c View File

@@ -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 );

Loading…
Cancel
Save