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 12 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
 #include <assert.h>
33
 #include <assert.h>
34
 #include <realmode.h>
34
 #include <realmode.h>
35
 #include <bzimage.h>
35
 #include <bzimage.h>
36
+#include <initrd.h>
36
 #include <ipxe/uaccess.h>
37
 #include <ipxe/uaccess.h>
37
 #include <ipxe/image.h>
38
 #include <ipxe/image.h>
38
 #include <ipxe/segment.h>
39
 #include <ipxe/segment.h>
304
  * @v image		bzImage image
305
  * @v image		bzImage image
305
  * @v bzimg		bzImage context
306
  * @v bzimg		bzImage context
306
  * @v cmdline		Kernel command line
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
 	size_t cmdline_len;
312
 	size_t cmdline_len;
313
 
313
 
314
 	/* Copy command line down to real-mode portion */
314
 	/* Copy command line down to real-mode portion */
318
 	copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline,
318
 	copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline,
319
 		       cmdline, cmdline_len );
319
 		       cmdline, cmdline_len );
320
 	DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
320
 	DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
321
-
322
-	return 0;
323
 }
321
 }
324
 
322
 
325
 /**
323
 /**
354
  * @v image		bzImage image
352
  * @v image		bzImage image
355
  * @v initrd		initrd image
353
  * @v initrd		initrd image
356
  * @v address		Address at which to load, or UNULL
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
 static size_t bzimage_load_initrd ( struct image *image,
357
 static size_t bzimage_load_initrd ( struct image *image,
360
 				    struct image *initrd,
358
 				    struct image *initrd,
364
 	struct cpio_header cpio;
362
 	struct cpio_header cpio;
365
         size_t offset = 0;
363
         size_t offset = 0;
366
 	size_t name_len;
364
 	size_t name_len;
365
+	size_t pad_len;
367
 
366
 
368
 	/* Do not include kernel image itself as an initrd */
367
 	/* Do not include kernel image itself as an initrd */
369
 	if ( initrd == image )
368
 	if ( initrd == image )
371
 
370
 
372
 	/* Create cpio header before non-prebuilt images */
371
 	/* Create cpio header before non-prebuilt images */
373
 	if ( filename && filename[0] ) {
372
 	if ( filename && filename[0] ) {
374
-		DBGC ( image, "bzImage %p inserting initrd %p as %s\n",
375
-		       image, initrd, filename );
376
 		cmdline = strchr ( filename, ' ' );
373
 		cmdline = strchr ( filename, ' ' );
377
 		name_len = ( ( cmdline ? ( ( size_t ) ( cmdline - filename ) )
374
 		name_len = ( ( cmdline ? ( ( size_t ) ( cmdline - filename ) )
378
 			       : strlen ( filename ) ) + 1 /* NUL */ );
375
 			       : strlen ( filename ) ) + 1 /* NUL */ );
402
 
399
 
403
 	/* Copy in initrd image body */
400
 	/* Copy in initrd image body */
404
 	if ( address )
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
 	if ( address ) {
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
 	return offset;
420
 	return offset;
416
 }
421
 }
417
 
422
 
418
 /**
423
 /**
419
- * Load initrds, if any
424
+ * Check that initrds can be loaded
420
  *
425
  *
421
  * @v image		bzImage image
426
  * @v image		bzImage image
422
  * @v bzimg		bzImage context
427
  * @v bzimg		bzImage context
423
  * @ret rc		Return status code
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
 	struct image *initrd;
432
 	struct image *initrd;
428
-	size_t total_len = 0;
429
-	physaddr_t address;
433
+	userptr_t bottom;
434
+	size_t len = 0;
430
 	int rc;
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
 	for_each_image ( initrd ) {
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
 		return rc;
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
 	/* Load segments */
585
 	/* Load segments */
512
 	memcpy_user ( bzimg.rm_kernel, 0, image->data,
586
 	memcpy_user ( bzimg.rm_kernel, 0, image->data,
513
 		      0, bzimg.rm_filesz );
587
 		      0, bzimg.rm_filesz );
514
 	memcpy_user ( bzimg.pm_kernel, 0, image->data,
588
 	memcpy_user ( bzimg.pm_kernel, 0, image->data,
515
 		      bzimg.rm_filesz, bzimg.pm_sz );
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
 	/* Store command line */
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
 	/* Load any initrds */
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
 	/* Update kernel header */
602
 	/* Update kernel header */
533
 	bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
603
 	bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
534
 
604
 
535
-	/* Prepare for exiting */
536
-	shutdown_boot();
537
-
538
 	DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
605
 	DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
539
 	       "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ),
606
 	       "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ),
540
 	       bzimg.rm_kernel_seg, bzimg.rm_heap );
607
 	       bzimg.rm_kernel_seg, bzimg.rm_heap );

Loading…
Cancel
Save