Home | History | Annotate | Download | only in core
      1 #include <gpxe/io.h>
      2 #include <registers.h>
      3 #include <gpxe/memmap.h>
      4 
      5 /*
      6  * Originally by Eric Biederman
      7  *
      8  * Heavily modified by Michael Brown
      9  *
     10  */
     11 
     12 FILE_LICENCE ( GPL2_OR_LATER );
     13 
     14 /*
     15  * The linker passes in the symbol _max_align, which is the alignment
     16  * that we must preserve, in bytes.
     17  *
     18  */
     19 extern char _max_align[];
     20 #define max_align ( ( unsigned int ) _max_align )
     21 
     22 /* Linker symbols */
     23 extern char _textdata[];
     24 extern char _etextdata[];
     25 
     26 /* within 1MB of 4GB is too close.
     27  * MAX_ADDR is the maximum address we can easily do DMA to.
     28  *
     29  * Not sure where this constraint comes from, but kept it from Eric's
     30  * old code - mcb30
     31  */
     32 #define MAX_ADDR (0xfff00000UL)
     33 
     34 /**
     35  * Relocate Etherboot
     36  *
     37  * @v ix86		x86 register dump from prefix
     38  * @ret ix86		x86 registers to return to prefix
     39  *
     40  * This finds a suitable location for Etherboot near the top of 32-bit
     41  * address space, and returns the physical address of the new location
     42  * to the prefix in %edi.
     43  */
     44 __asmcall void relocate ( struct i386_all_regs *ix86 ) {
     45 	struct memory_map memmap;
     46 	unsigned long start, end, size, padded_size;
     47 	unsigned long new_start, new_end;
     48 	unsigned i;
     49 
     50 	/* Get memory map and current location */
     51 	get_memmap ( &memmap );
     52 	start = virt_to_phys ( _textdata );
     53 	end = virt_to_phys ( _etextdata );
     54 	size = ( end - start );
     55 	padded_size = ( size + max_align - 1 );
     56 
     57 	DBG ( "Relocate: currently at [%lx,%lx)\n"
     58 	      "...need %lx bytes for %d-byte alignment\n",
     59 	      start, end, padded_size, max_align );
     60 
     61 	/* Walk through the memory map and find the highest address
     62 	 * below 4GB that etherboot will fit into.  Ensure etherboot
     63 	 * lies entirely within a range with A20=0.  This means that
     64 	 * even if something screws up the state of the A20 line, the
     65 	 * etherboot code is still visible and we have a chance to
     66 	 * diagnose the problem.
     67 	 */
     68 	new_end = end;
     69 	for ( i = 0 ; i < memmap.count ; i++ ) {
     70 		struct memory_region *region = &memmap.regions[i];
     71 		unsigned long r_start, r_end;
     72 
     73 		DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
     74 
     75 		/* Truncate block to MAX_ADDR.  This will be less than
     76 		 * 4GB, which means that we can get away with using
     77 		 * just 32-bit arithmetic after this stage.
     78 		 */
     79 		if ( region->start > MAX_ADDR ) {
     80 			DBG ( "...starts after MAX_ADDR=%lx\n", MAX_ADDR );
     81 			continue;
     82 		}
     83 		r_start = region->start;
     84 		if ( region->end > MAX_ADDR ) {
     85 			DBG ( "...end truncated to MAX_ADDR=%lx\n", MAX_ADDR );
     86 			r_end = MAX_ADDR;
     87 		} else {
     88 			r_end = region->end;
     89 		}
     90 
     91 		/* Shrink the range down to use only even megabytes
     92 		 * (i.e. A20=0).
     93 		 */
     94 		if ( ( r_end - 1 ) & 0x100000 ) {
     95 			/* If last byte that might be used (r_end-1)
     96 			 * is in an odd megabyte, round down r_end to
     97 			 * the top of the next even megabyte.
     98 			 *
     99 			 * Make sure that we don't accidentally wrap
    100 			 * r_end below 0.
    101 			 */
    102 			if ( r_end >= 1 ) {
    103 				r_end = ( r_end - 1 ) & ~0xfffff;
    104 				DBG ( "...end truncated to %lx "
    105 				      "(avoid ending in odd megabyte)\n",
    106 				      r_end );
    107 			}
    108 		} else if ( ( r_end - size ) & 0x100000 ) {
    109 			/* If the last byte that might be used
    110 			 * (r_end-1) is in an even megabyte, but the
    111 			 * first byte that might be used (r_end-size)
    112 			 * is an odd megabyte, round down to the top
    113 			 * of the next even megabyte.
    114 			 *
    115 			 * Make sure that we don't accidentally wrap
    116 			 * r_end below 0.
    117 			 */
    118 			if ( r_end >= 0x100000 ) {
    119 				r_end = ( r_end - 0x100000 ) & ~0xfffff;
    120 				DBG ( "...end truncated to %lx "
    121 				      "(avoid starting in odd megabyte)\n",
    122 				      r_end );
    123 			}
    124 		}
    125 
    126 		DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end );
    127 
    128 		/* If we have rounded down r_end below r_ start, skip
    129 		 * this block.
    130 		 */
    131 		if ( r_end < r_start ) {
    132 			DBG ( "...truncated to negative size\n" );
    133 			continue;
    134 		}
    135 
    136 		/* Check that there is enough space to fit in Etherboot */
    137 		if ( ( r_end - r_start ) < size ) {
    138 			DBG ( "...too small (need %lx bytes)\n", size );
    139 			continue;
    140 		}
    141 
    142 		/* If the start address of the Etherboot we would
    143 		 * place in this block is higher than the end address
    144 		 * of the current highest block, use this block.
    145 		 *
    146 		 * Note that this avoids overlaps with the current
    147 		 * Etherboot, as well as choosing the highest of all
    148 		 * viable blocks.
    149 		 */
    150 		if ( ( r_end - size ) > new_end ) {
    151 			new_end = r_end;
    152 			DBG ( "...new best block found.\n" );
    153 		}
    154 	}
    155 
    156 	/* Calculate new location of Etherboot, and align it to the
    157 	 * required alignemnt.
    158 	 */
    159 	new_start = new_end - padded_size;
    160 	new_start += ( start - new_start ) & ( max_align - 1 );
    161 	new_end = new_start + size;
    162 
    163 	DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n",
    164 	      start, end, new_start, new_end );
    165 
    166 	/* Let prefix know what to copy */
    167 	ix86->regs.esi = start;
    168 	ix86->regs.edi = new_start;
    169 	ix86->regs.ecx = size;
    170 }
    171