iPXE
Defines | Functions | Variables
relocate.c File Reference
#include <ipxe/io.h>
#include <registers.h>

Go to the source code of this file.

Defines

#define MAX_ADDR   (0xfff00000UL)
#define ALIGN   4096

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
__asmcall void relocate (struct i386_all_regs *ix86)
 Relocate iPXE.

Variables

char _textdata []
char _etextdata []

Define Documentation

#define MAX_ADDR   (0xfff00000UL)

Definition at line 23 of file relocate.c.

Referenced by relocate().

#define ALIGN   4096

Definition at line 29 of file relocate.c.

Referenced by relocate().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
__asmcall void relocate ( struct i386_all_regs ix86)

Relocate iPXE.

Parameters:
ebpMaximum address to use for relocation
Return values:
esiCurrent physical address
ediNew physical address
ecxLength to copy

This finds a suitable location for iPXE near the top of 32-bit address space, and returns the physical address of the new location to the prefix in edi.

Definition at line 43 of file relocate.c.

References _etextdata, _textdata, ALIGN, memory_map::count, DBG, i386_regs::ebp, i386_regs::ecx, i386_regs::edi, end, memory_region::end, i386_regs::esi, get_memmap(), max, MAX_ADDR, memory_map::regions, i386_all_regs::regs, size, start, memory_region::start, and virt_to_phys().

                                                       {
        struct memory_map memmap;
        uint32_t start, end, size, padded_size, max;
        uint32_t new_start, new_end;
        unsigned i;

        /* Get memory map and current location */
        get_memmap ( &memmap );
        start = virt_to_phys ( _textdata );
        end = virt_to_phys ( _etextdata );
        size = ( end - start );
        padded_size = ( size + ALIGN - 1 );

        DBG ( "Relocate: currently at [%x,%x)\n"
              "...need %x bytes for %d-byte alignment\n",
              start, end, padded_size, ALIGN );

        /* Determine maximum usable address */
        max = MAX_ADDR;
        if ( ix86->regs.ebp < max ) {
                max = ix86->regs.ebp;
                DBG ( "Limiting relocation to [0,%x)\n", max );
        }

        /* Walk through the memory map and find the highest address
         * below 4GB that iPXE will fit into.
         */
        new_end = end;
        for ( i = 0 ; i < memmap.count ; i++ ) {
                struct memory_region *region = &memmap.regions[i];
                uint32_t r_start, r_end;

                DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
                
                /* Truncate block to maximum address.  This will be
                 * less than 4GB, which means that we can get away
                 * with using just 32-bit arithmetic after this stage.
                 */
                if ( region->start > max ) {
                        DBG ( "...starts after max=%x\n", max );
                        continue;
                }
                r_start = region->start;
                if ( region->end > max ) {
                        DBG ( "...end truncated to max=%x\n", max );
                        r_end = max;
                } else {
                        r_end = region->end;
                }
                DBG ( "...usable portion is [%x,%x)\n", r_start, r_end );

                /* If we have rounded down r_end below r_ start, skip
                 * this block.
                 */
                if ( r_end < r_start ) {
                        DBG ( "...truncated to negative size\n" );
                        continue;
                }

                /* Check that there is enough space to fit in iPXE */
                if ( ( r_end - r_start ) < size ) {
                        DBG ( "...too small (need %x bytes)\n", size );
                        continue;
                }

                /* If the start address of the iPXE we would
                 * place in this block is higher than the end address
                 * of the current highest block, use this block.
                 *
                 * Note that this avoids overlaps with the current
                 * iPXE, as well as choosing the highest of all viable
                 * blocks.
                 */
                if ( ( r_end - size ) > new_end ) {
                        new_end = r_end;
                        DBG ( "...new best block found.\n" );
                }
        }

        /* Calculate new location of iPXE, and align it to the
         * required alignemnt.
         */
        new_start = new_end - padded_size;
        new_start += ( ( start - new_start ) & ( ALIGN - 1 ) );
        new_end = new_start + size;

        DBG ( "Relocating from [%x,%x) to [%x,%x)\n",
              start, end, new_start, new_end );
        
        /* Let prefix know what to copy */
        ix86->regs.esi = start;
        ix86->regs.edi = new_start;
        ix86->regs.ecx = size;
}

Variable Documentation

char _textdata[]
char _etextdata[]

Referenced by hide_textdata(), and relocate().