iPXE
Functions | Variables
initrd.c File Reference

Initial ramdisk (initrd) reshuffling. More...

#include <errno.h>
#include <initrd.h>
#include <ipxe/image.h>
#include <ipxe/uaccess.h>
#include <ipxe/init.h>
#include <ipxe/memblock.h>

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static userptr_t initrd_squash_high (userptr_t top)
 Squash initrds as high as possible in memory.
static void initrd_swap (struct image *low, struct image *high, userptr_t free, size_t free_len)
 Swap position of two adjacent initrds.
static int initrd_swap_any (userptr_t free, size_t free_len)
 Swap position of any two adjacent initrds not currently in the correct order.
static void initrd_dump (void)
 Dump initrd locations (for debug)
void initrd_reshuffle (userptr_t bottom)
 Reshuffle initrds into desired order at top of memory.
int initrd_reshuffle_check (size_t len, userptr_t bottom)
 Check that there is enough space to reshuffle initrds.
static void initrd_startup (void)
 initrd startup function
struct startup_fn startup_initrd __startup_fn (STARTUP_LATE)
 initrd startup function

Variables

userptr_t initrd_top
 Maximum address available for initrd.
userptr_t initrd_bottom
 Minimum address available for initrd.

Detailed Description

Initial ramdisk (initrd) reshuffling.

Definition in file initrd.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static userptr_t initrd_squash_high ( userptr_t  top) [static]

Squash initrds as high as possible in memory.

Parameters:
topHighest possible address
Return values:
usedLowest address used by initrds

Definition at line 51 of file initrd.c.

References image::data, DBGC, for_each_image, images, INITRD_ALIGN, image::len, len, memcpy_user(), memmove_user(), image::name, NULL, top, user_to_phys(), and userptr_sub().

Referenced by initrd_reshuffle().

                                                      {
        userptr_t current = top;
        struct image *initrd;
        struct image *highest;
        size_t len;

        /* Squash up any initrds already within or below the region */
        while ( 1 ) {

                /* Find the highest image not yet in its final position */
                highest = NULL;
                for_each_image ( initrd ) {
                        if ( ( userptr_sub ( initrd->data, current ) < 0 ) &&
                             ( ( highest == NULL ) ||
                               ( userptr_sub ( initrd->data,
                                               highest->data ) > 0 ) ) ) {
                                highest = initrd;
                        }
                }
                if ( ! highest )
                        break;

                /* Move this image to its final position */
                len = ( ( highest->len + INITRD_ALIGN - 1 ) &
                        ~( INITRD_ALIGN - 1 ) );
                current = userptr_sub ( current, len );
                DBGC ( &images, "INITRD squashing %s [%#08lx,%#08lx)->"
                       "[%#08lx,%#08lx)\n", highest->name,
                       user_to_phys ( highest->data, 0 ),
                       user_to_phys ( highest->data, highest->len ),
                       user_to_phys ( current, 0 ),
                       user_to_phys ( current, highest->len ) );
                memmove_user ( current, 0, highest->data, 0, highest->len );
                highest->data = current;
        }

        /* Copy any remaining initrds (e.g. embedded images) to the region */
        for_each_image ( initrd ) {
                if ( userptr_sub ( initrd->data, top ) >= 0 ) {
                        len = ( ( initrd->len + INITRD_ALIGN - 1 ) &
                                ~( INITRD_ALIGN - 1 ) );
                        current = userptr_sub ( current, len );
                        DBGC ( &images, "INITRD copying %s [%#08lx,%#08lx)->"
                               "[%#08lx,%#08lx)\n", initrd->name,
                               user_to_phys ( initrd->data, 0 ),
                               user_to_phys ( initrd->data, initrd->len ),
                               user_to_phys ( current, 0 ),
                               user_to_phys ( current, initrd->len ) );
                        memcpy_user ( current, 0, initrd->data, 0,
                                      initrd->len );
                        initrd->data = current;
                }
        }

        return current;
}
static void initrd_swap ( struct image low,
struct image high,
userptr_t  free,
size_t  free_len 
) [static]

Swap position of two adjacent initrds.

Parameters:
lowLower initrd
highHigher initrd
freeFree space
free_lenLength of free space

Definition at line 116 of file initrd.c.

References assert, image::data, DBGC, images, INITRD_ALIGN, image::len, len, memcpy_user(), memmove_user(), image::name, user_to_phys(), and userptr_add().

Referenced by initrd_swap_any().

                                                            {
        size_t len = 0;
        size_t frag_len;
        size_t new_len;

        DBGC ( &images, "INITRD swapping %s [%#08lx,%#08lx)<->[%#08lx,%#08lx) "
               "%s\n", low->name, user_to_phys ( low->data, 0 ),
               user_to_phys ( low->data, low->len ),
               user_to_phys ( high->data, 0 ),
               user_to_phys ( high->data, high->len ), high->name );

        /* Round down length of free space */
        free_len &= ~( INITRD_ALIGN - 1 );
        assert ( free_len > 0 );

        /* Swap image data */
        while ( len < high->len ) {

                /* Calculate maximum fragment length */
                frag_len = ( high->len - len );
                if ( frag_len > free_len )
                        frag_len = free_len;
                new_len = ( ( len + frag_len + INITRD_ALIGN - 1 ) &
                            ~( INITRD_ALIGN - 1 ) );

                /* Swap fragments */
                memcpy_user ( free, 0, high->data, len, frag_len );
                memmove_user ( low->data, new_len, low->data, len, low->len );
                memcpy_user ( low->data, len, free, 0, frag_len );
                len = new_len;
        }

        /* Adjust data pointers */
        high->data = low->data;
        low->data = userptr_add ( low->data, len );
}
static int initrd_swap_any ( userptr_t  free,
size_t  free_len 
) [static]

Swap position of any two adjacent initrds not currently in the correct order.

Parameters:
freeFree space
free_lenLength of free space
Return values:
swappedA pair of initrds was swapped

Definition at line 161 of file initrd.c.

References image::data, for_each_image, high, INITRD_ALIGN, initrd_swap(), image::len, low, and userptr_add().

Referenced by initrd_reshuffle().

                                                               {
        struct image *low;
        struct image *high;
        size_t padded_len;
        userptr_t adjacent;

        /* Find any pair of initrds that can be swapped */
        for_each_image ( low ) {

                /* Calculate location of adjacent image (if any) */
                padded_len = ( ( low->len + INITRD_ALIGN - 1 ) &
                               ~( INITRD_ALIGN - 1 ) );
                adjacent = userptr_add ( low->data, padded_len );

                /* Search for adjacent image */
                for_each_image ( high ) {

                        /* If we have found the adjacent image, swap and exit */
                        if ( high->data == adjacent ) {
                                initrd_swap ( low, high, free, free_len );
                                return 1;
                        }

                        /* Stop search if all remaining potential
                         * adjacent images are already in the correct
                         * order.
                         */
                        if ( high == low )
                                break;
                }
        }

        /* Nothing swapped */
        return 0;
}
static void initrd_dump ( void  ) [static]

Dump initrd locations (for debug)

Definition at line 201 of file initrd.c.

References image::data, DBG_LOG, DBGC, DBGC2_MD5A, for_each_image, images, image::len, image::name, user_to_phys(), and user_to_virt().

Referenced by initrd_reshuffle().

                                 {
        struct image *initrd;

        /* Do nothing unless debugging is enabled */
        if ( ! DBG_LOG )
                return;

        /* Dump initrd locations */
        for_each_image ( initrd ) {
                DBGC ( &images, "INITRD %s at [%#08lx,%#08lx)\n",
                       initrd->name, user_to_phys ( initrd->data, 0 ),
                       user_to_phys ( initrd->data, initrd->len ) );
                DBGC2_MD5A ( &images, user_to_phys ( initrd->data, 0 ),
                             user_to_virt ( initrd->data, 0 ), initrd->len );
        }
}
void initrd_reshuffle ( userptr_t  bottom)

Reshuffle initrds into desired order at top of memory.

Parameters:
bottomLowest address available for initrds

After this function returns, the initrds have been rearranged in memory and the external heap structures will have been corrupted. Reshuffling must therefore take place immediately prior to jumping to the loaded OS kernel; no further execution within iPXE is permitted.

Definition at line 229 of file initrd.c.

References bottom, DBGC, free, images, initrd_bottom, initrd_dump(), initrd_squash_high(), initrd_swap_any(), initrd_top, top, user_to_phys(), and userptr_sub().

Referenced by bzimage_load_initrds().

                                           {
        userptr_t top;
        userptr_t used;
        userptr_t free;
        size_t free_len;

        /* Calculate limits of available space for initrds */
        top = initrd_top;
        if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
                bottom = initrd_bottom;

        /* Debug */
        DBGC ( &images, "INITRD region [%#08lx,%#08lx)\n",
               user_to_phys ( bottom, 0 ), user_to_phys ( top, 0 ) );
        initrd_dump();

        /* Squash initrds as high as possible in memory */
        used = initrd_squash_high ( top );

        /* Calculate available free space */
        free = bottom;
        free_len = userptr_sub ( used, free );

        /* Bubble-sort initrds into desired order */
        while ( initrd_swap_any ( free, free_len ) ) {}

        /* Debug */
        initrd_dump();
}
int initrd_reshuffle_check ( size_t  len,
userptr_t  bottom 
)

Check that there is enough space to reshuffle initrds.

Parameters:
lenTotal length of initrds (including padding)
bottomLowest address available for initrds
Return values:
rcReturn status code

Definition at line 266 of file initrd.c.

References ENOBUFS, initrd_bottom, INITRD_MIN_FREE_LEN, initrd_top, top, and userptr_sub().

Referenced by bzimage_check_initrds().

                                                            {
        userptr_t top;
        size_t available;

        /* Calculate limits of available space for initrds */
        top = initrd_top;
        if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
                bottom = initrd_bottom;
        available = userptr_sub ( top, bottom );

        /* Allow for a sensible minimum amount of free space */
        len += INITRD_MIN_FREE_LEN;

        /* Check for available space */
        return ( ( len < available ) ? 0 : -ENOBUFS );
}
static void initrd_startup ( void  ) [static]

initrd startup function

Definition at line 287 of file initrd.c.

References initrd_bottom, initrd_top, largest_memblock(), len, and userptr_add().

                                    {
        size_t len;

        /* Record largest memory block available.  Do this after any
         * allocations made during driver startup (e.g. large host
         * memory blocks for Infiniband devices, which may still be in
         * use at the time of rearranging if a SAN device is hooked)
         * but before any allocations for downloaded images (which we
         * can safely reuse when rearranging).
         */
        len = largest_memblock ( &initrd_bottom );
        initrd_top = userptr_add ( initrd_bottom, len );
}
struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE  ) [read]

initrd startup function


Variable Documentation

Maximum address available for initrd.

Definition at line 40 of file initrd.c.

Referenced by initrd_reshuffle(), initrd_reshuffle_check(), and initrd_startup().

Minimum address available for initrd.

Definition at line 43 of file initrd.c.

Referenced by initrd_reshuffle(), initrd_reshuffle_check(), and initrd_startup().