|  | @@ -1,7 +1,7 @@
 | 
		
	
		
			
			| 1 |  | -#ifndef	NORELOCATE
 | 
		
	
		
			
			| 2 |  | -
 | 
		
	
		
			
			| 3 |  | -#include "etherboot.h"
 | 
		
	
		
			
			|  | 1 | +#include "virtaddr.h"
 | 
		
	
		
			
			| 4 | 2 |  #include "memsizes.h"
 | 
		
	
		
			
			|  | 3 | +#include "osdep.h"
 | 
		
	
		
			
			|  | 4 | +#include "etherboot.h"
 | 
		
	
		
			
			| 5 | 5 |  
 | 
		
	
		
			
			| 6 | 6 |  /* by Eric Biederman */
 | 
		
	
		
			
			| 7 | 7 |  
 | 
		
	
	
		
			
			|  | @@ -16,10 +16,37 @@
 | 
		
	
		
			
			| 16 | 16 |   * 
 | 
		
	
		
			
			| 17 | 17 |   */
 | 
		
	
		
			
			| 18 | 18 |  
 | 
		
	
		
			
			| 19 |  | -void relocate(void)
 | 
		
	
		
			
			| 20 |  | -{
 | 
		
	
		
			
			|  | 19 | +/*
 | 
		
	
		
			
			|  | 20 | + * relocate() must be called without any hardware resources pointing
 | 
		
	
		
			
			|  | 21 | + * at the current copy of Etherboot.  The easiest way to achieve this
 | 
		
	
		
			
			|  | 22 | + * is to call relocate() from within arch_initialise(), before the NIC
 | 
		
	
		
			
			|  | 23 | + * gets touched in any way.
 | 
		
	
		
			
			|  | 24 | + *
 | 
		
	
		
			
			|  | 25 | + */
 | 
		
	
		
			
			|  | 26 | +
 | 
		
	
		
			
			|  | 27 | +/*
 | 
		
	
		
			
			|  | 28 | + * The linker passes in the symbol _max_align, which is the alignment
 | 
		
	
		
			
			|  | 29 | + * that we must preserve, in bytes.
 | 
		
	
		
			
			|  | 30 | + *
 | 
		
	
		
			
			|  | 31 | + */
 | 
		
	
		
			
			|  | 32 | +extern char _max_align[];
 | 
		
	
		
			
			|  | 33 | +#define max_align ( ( unsigned int ) _max_align )
 | 
		
	
		
			
			|  | 34 | +
 | 
		
	
		
			
			|  | 35 | +/* Linker symbols */
 | 
		
	
		
			
			|  | 36 | +extern char _text[];
 | 
		
	
		
			
			|  | 37 | +extern char _end[];
 | 
		
	
		
			
			|  | 38 | +
 | 
		
	
		
			
			|  | 39 | +#undef DBG
 | 
		
	
		
			
			|  | 40 | +#ifdef DEBUG_RELOCATE
 | 
		
	
		
			
			|  | 41 | +#define DBG(...) printf ( __VA_ARGS__ )
 | 
		
	
		
			
			|  | 42 | +#else
 | 
		
	
		
			
			|  | 43 | +#define DBG(...)
 | 
		
	
		
			
			|  | 44 | +#endif
 | 
		
	
		
			
			|  | 45 | +
 | 
		
	
		
			
			|  | 46 | +void relocate ( void ) {
 | 
		
	
		
			
			| 21 | 47 |  	unsigned long addr, eaddr, size;
 | 
		
	
		
			
			| 22 | 48 |  	unsigned i;
 | 
		
	
		
			
			|  | 49 | +
 | 
		
	
		
			
			| 23 | 50 |  	/* Walk through the memory map and find the highest address
 | 
		
	
		
			
			| 24 | 51 |  	 * below 4GB that etherboot will fit into.  Ensure etherboot
 | 
		
	
		
			
			| 25 | 52 |  	 * lies entirely within a range with A20=0.  This means that
 | 
		
	
	
		
			
			|  | @@ -27,78 +54,133 @@ void relocate(void)
 | 
		
	
		
			
			| 27 | 54 |  	 * etherboot code is still visible and we have a chance to
 | 
		
	
		
			
			| 28 | 55 |  	 * diagnose the problem.
 | 
		
	
		
			
			| 29 | 56 |  	 */
 | 
		
	
		
			
			| 30 |  | -	/* First find the size of etherboot */
 | 
		
	
		
			
			| 31 |  | -	addr = virt_to_phys(_text);
 | 
		
	
		
			
			| 32 |  | -	eaddr = virt_to_phys(_end);
 | 
		
	
		
			
			| 33 |  | -	size = (eaddr - addr + 0xf) & ~0xf;
 | 
		
	
		
			
			| 34 | 57 |  
 | 
		
	
		
			
			| 35 |  | -	/* If the current etherboot is beyond MAX_ADDR pretend it is
 | 
		
	
		
			
			| 36 |  | -	 * at the lowest possible address.
 | 
		
	
		
			
			|  | 58 | +	/* First find the size of etherboot, including enough space to
 | 
		
	
		
			
			|  | 59 | +	 * pad it to the required alignment
 | 
		
	
		
			
			|  | 60 | +	 */
 | 
		
	
		
			
			|  | 61 | +	size = _end - _text + max_align - 1;
 | 
		
	
		
			
			|  | 62 | +
 | 
		
	
		
			
			|  | 63 | +	/* Current end address of Etherboot.  If the current etherboot
 | 
		
	
		
			
			|  | 64 | +	 * is beyond MAX_ADDR pretend it is at the lowest possible
 | 
		
	
		
			
			|  | 65 | +	 * address.
 | 
		
	
		
			
			| 37 | 66 |  	 */
 | 
		
	
		
			
			| 38 |  | -	if (eaddr > MAX_ADDR) {
 | 
		
	
		
			
			|  | 67 | +	eaddr = virt_to_phys(_end);
 | 
		
	
		
			
			|  | 68 | +	if ( eaddr > MAX_ADDR ) {
 | 
		
	
		
			
			| 39 | 69 |  		eaddr = 0;
 | 
		
	
		
			
			| 40 | 70 |  	}
 | 
		
	
		
			
			| 41 | 71 |  
 | 
		
	
		
			
			| 42 |  | -	for(i = 0; i < meminfo.map_count; i++) {
 | 
		
	
		
			
			|  | 72 | +	DBG ( "Relocate: currently at [%x,%x)\n"
 | 
		
	
		
			
			|  | 73 | +	      "...need %x bytes for %d-byte alignment\n",
 | 
		
	
		
			
			|  | 74 | +	      virt_to_phys ( _text ), eaddr, size, max_align );
 | 
		
	
		
			
			|  | 75 | +
 | 
		
	
		
			
			|  | 76 | +	for ( i = 0; i < meminfo.map_count; i++ ) {
 | 
		
	
		
			
			| 43 | 77 |  		unsigned long r_start, r_end;
 | 
		
	
		
			
			|  | 78 | +
 | 
		
	
		
			
			|  | 79 | +		DBG ( "Considering [%x%x,%x%x)\n",
 | 
		
	
		
			
			|  | 80 | +		      ( unsigned long ) ( meminfo.map[i].addr >> 32 ),
 | 
		
	
		
			
			|  | 81 | +		      ( unsigned long ) meminfo.map[i].addr,
 | 
		
	
		
			
			|  | 82 | +		      ( unsigned long )
 | 
		
	
		
			
			|  | 83 | +		       ( ( meminfo.map[i].addr + meminfo.map[i].size ) >> 32 ),
 | 
		
	
		
			
			|  | 84 | +		      ( unsigned long )
 | 
		
	
		
			
			|  | 85 | +		       ( meminfo.map[i].addr + meminfo.map[i].size ) );
 | 
		
	
		
			
			|  | 86 | +		
 | 
		
	
		
			
			|  | 87 | +		/* Check block is usable memory */
 | 
		
	
		
			
			| 44 | 88 |  		if (meminfo.map[i].type != E820_RAM) {
 | 
		
	
		
			
			|  | 89 | +			DBG ( "...not RAM\n" );
 | 
		
	
		
			
			| 45 | 90 |  			continue;
 | 
		
	
		
			
			| 46 | 91 |  		}
 | 
		
	
		
			
			| 47 |  | -		if (meminfo.map[i].addr > MAX_ADDR) {
 | 
		
	
		
			
			| 48 |  | -			continue;
 | 
		
	
		
			
			| 49 |  | -		}
 | 
		
	
		
			
			| 50 |  | -		if (meminfo.map[i].size > MAX_ADDR) {
 | 
		
	
		
			
			|  | 92 | +
 | 
		
	
		
			
			|  | 93 | +		/* Truncate block to MAX_ADDR.  This will be less than
 | 
		
	
		
			
			|  | 94 | +		 * 4GB, which means that we can get away with using
 | 
		
	
		
			
			|  | 95 | +		 * just 32-bit arithmetic after this stage.
 | 
		
	
		
			
			|  | 96 | +		 */
 | 
		
	
		
			
			|  | 97 | +		if ( meminfo.map[i].addr > MAX_ADDR ) {
 | 
		
	
		
			
			|  | 98 | +			DBG ( "...starts after MAX_ADDR=%x\n", MAX_ADDR );
 | 
		
	
		
			
			| 51 | 99 |  			continue;
 | 
		
	
		
			
			| 52 | 100 |  		}
 | 
		
	
		
			
			| 53 | 101 |  		r_start = meminfo.map[i].addr;
 | 
		
	
		
			
			| 54 |  | -		r_end = r_start + meminfo.map[i].size;
 | 
		
	
		
			
			| 55 |  | -		/* Make the addresses 16 byte (128 bit) aligned */
 | 
		
	
		
			
			| 56 |  | -		r_start = (r_start + 15) & ~15;
 | 
		
	
		
			
			| 57 |  | -		r_end = r_end & ~15;
 | 
		
	
		
			
			| 58 |  | -		if (r_end < r_start) {
 | 
		
	
		
			
			|  | 102 | +		if ( meminfo.map[i].addr + meminfo.map[i].size > MAX_ADDR ) {
 | 
		
	
		
			
			| 59 | 103 |  			r_end = MAX_ADDR;
 | 
		
	
		
			
			|  | 104 | +			DBG ( "...end truncated to MAX_ADDR=%x\n", MAX_ADDR );
 | 
		
	
		
			
			|  | 105 | +		} else {
 | 
		
	
		
			
			|  | 106 | +			r_end = meminfo.map[i].addr + meminfo.map[i].size;
 | 
		
	
		
			
			| 60 | 107 |  		}
 | 
		
	
		
			
			| 61 |  | -		if (r_end < size) {
 | 
		
	
		
			
			| 62 |  | -			/* Avoid overflow weirdness when r_end - size < 0 */
 | 
		
	
		
			
			| 63 |  | -			continue;
 | 
		
	
		
			
			| 64 |  | -		}
 | 
		
	
		
			
			|  | 108 | +
 | 
		
	
		
			
			| 65 | 109 |  		/* Shrink the range down to use only even megabytes
 | 
		
	
		
			
			| 66 | 110 |  		 * (i.e. A20=0).
 | 
		
	
		
			
			| 67 | 111 |  		 */
 | 
		
	
		
			
			| 68 |  | -		if ( r_end & 0x100000 ) {
 | 
		
	
		
			
			| 69 |  | -			/* If r_end is in an odd megabyte, round down
 | 
		
	
		
			
			| 70 |  | -			 * r_end to the top of the next even megabyte.
 | 
		
	
		
			
			|  | 112 | +		if ( ( r_end - 1 ) & 0x100000 ) {
 | 
		
	
		
			
			|  | 113 | +			/* If last byte that might be used (r_end-1)
 | 
		
	
		
			
			|  | 114 | +			 * is in an odd megabyte, round down r_end to
 | 
		
	
		
			
			|  | 115 | +			 * the top of the next even megabyte.
 | 
		
	
		
			
			| 71 | 116 |  			 */
 | 
		
	
		
			
			| 72 |  | -			r_end = r_end & ~0xfffff;
 | 
		
	
		
			
			|  | 117 | +			r_end = ( r_end - 1 ) & ~0xfffff;
 | 
		
	
		
			
			|  | 118 | +			DBG ( "...end truncated to %x "
 | 
		
	
		
			
			|  | 119 | +			      "(avoid ending in odd megabyte)\n",
 | 
		
	
		
			
			|  | 120 | +			      r_end );
 | 
		
	
		
			
			| 73 | 121 |  		} else if ( ( r_end - size ) & 0x100000 ) {
 | 
		
	
		
			
			| 74 |  | -			/* If r_end is in an even megabyte, but the
 | 
		
	
		
			
			| 75 |  | -			 * start of Etherboot would be in an odd
 | 
		
	
		
			
			| 76 |  | -			 * megabyte, round down to the top of the next
 | 
		
	
		
			
			| 77 |  | -			 * even megabyte.
 | 
		
	
		
			
			| 78 |  | -			*/
 | 
		
	
		
			
			| 79 |  | -			r_end = ( r_end - 0x100000 ) & ~0xfffff;
 | 
		
	
		
			
			|  | 122 | +			/* If the last byte that might be used
 | 
		
	
		
			
			|  | 123 | +			 * (r_end-1) is in an even megabyte, but the
 | 
		
	
		
			
			|  | 124 | +			 * first byte that might be used (r_end-size)
 | 
		
	
		
			
			|  | 125 | +			 * is an odd megabyte, round down to the top
 | 
		
	
		
			
			|  | 126 | +			 * of the next even megabyte.
 | 
		
	
		
			
			|  | 127 | +			 * 
 | 
		
	
		
			
			|  | 128 | +			 * Make sure that we don't accidentally wrap
 | 
		
	
		
			
			|  | 129 | +			 * r_end below 0.
 | 
		
	
		
			
			|  | 130 | +			 */
 | 
		
	
		
			
			|  | 131 | +			if ( r_end > 0x100000 ) {
 | 
		
	
		
			
			|  | 132 | +				r_end = ( r_end - 0x100000 ) & ~0xfffff;
 | 
		
	
		
			
			|  | 133 | +				DBG ( "...end truncated to %x "
 | 
		
	
		
			
			|  | 134 | +				      "(avoid starting in odd megabyte)\n",
 | 
		
	
		
			
			|  | 135 | +				      r_end );
 | 
		
	
		
			
			|  | 136 | +			}
 | 
		
	
		
			
			| 80 | 137 |  		}
 | 
		
	
		
			
			|  | 138 | +
 | 
		
	
		
			
			|  | 139 | +		DBG ( "...usable portion is [%x,%x)\n", r_start, r_end );
 | 
		
	
		
			
			|  | 140 | +
 | 
		
	
		
			
			| 81 | 141 |  		/* If we have rounded down r_end below r_ start, skip
 | 
		
	
		
			
			| 82 | 142 |  		 * this block.
 | 
		
	
		
			
			| 83 | 143 |  		 */
 | 
		
	
		
			
			| 84 | 144 |  		if ( r_end < r_start ) {
 | 
		
	
		
			
			|  | 145 | +			DBG ( "...truncated to negative size\n" );
 | 
		
	
		
			
			|  | 146 | +			continue;
 | 
		
	
		
			
			|  | 147 | +		}
 | 
		
	
		
			
			|  | 148 | +
 | 
		
	
		
			
			|  | 149 | +		/* Check that there is enough space to fit in Etherboot */
 | 
		
	
		
			
			|  | 150 | +		if ( r_end - r_start < size ) {
 | 
		
	
		
			
			|  | 151 | +			DBG ( "...too small (need %x bytes)\n", size );
 | 
		
	
		
			
			| 85 | 152 |  			continue;
 | 
		
	
		
			
			| 86 | 153 |  		}
 | 
		
	
		
			
			| 87 |  | -		if (eaddr < r_end - size) {
 | 
		
	
		
			
			| 88 |  | -			addr = r_end - size;
 | 
		
	
		
			
			|  | 154 | +
 | 
		
	
		
			
			|  | 155 | +		/* If the start address of the Etherboot we would
 | 
		
	
		
			
			|  | 156 | +		 * place in this block is higher than the end address
 | 
		
	
		
			
			|  | 157 | +		 * of the current highest block, use this block.
 | 
		
	
		
			
			|  | 158 | +		 *
 | 
		
	
		
			
			|  | 159 | +		 * Note that this avoids overlaps with the current
 | 
		
	
		
			
			|  | 160 | +		 * Etherboot, as well as choosing the highest of all
 | 
		
	
		
			
			|  | 161 | +		 * viable blocks.
 | 
		
	
		
			
			|  | 162 | +		 */
 | 
		
	
		
			
			|  | 163 | +		if ( r_end - size > eaddr ) {
 | 
		
	
		
			
			| 89 | 164 |  			eaddr = r_end;
 | 
		
	
		
			
			|  | 165 | +			DBG ( "...new best block found.\n" );
 | 
		
	
		
			
			| 90 | 166 |  		}
 | 
		
	
		
			
			| 91 | 167 |  	}
 | 
		
	
		
			
			| 92 |  | -	if (addr != virt_to_phys(_text)) {
 | 
		
	
		
			
			| 93 |  | -		unsigned long old_addr = virt_to_phys(_text);
 | 
		
	
		
			
			| 94 |  | -		printf("Relocating _text from: [%lx,%lx) to [%lx,%lx)\n",
 | 
		
	
		
			
			| 95 |  | -			old_addr, virt_to_phys(_end),
 | 
		
	
		
			
			| 96 |  | -			addr, eaddr);
 | 
		
	
		
			
			| 97 |  | -		/* arch_relocate_to ( addr ) */
 | 
		
	
		
			
			| 98 |  | -		cleanup();
 | 
		
	
		
			
			| 99 |  | -		relocate_to(addr);
 | 
		
	
		
			
			| 100 |  | -		/* arch_relocated_from ( addr ) */
 | 
		
	
		
			
			|  | 168 | +
 | 
		
	
		
			
			|  | 169 | +	DBG ( "New location will be in [%x,%x)\n", eaddr - size, eaddr );
 | 
		
	
		
			
			|  | 170 | +
 | 
		
	
		
			
			|  | 171 | +	/* Calculate new location of Etherboot, and align it to the
 | 
		
	
		
			
			|  | 172 | +	 * required alignemnt.
 | 
		
	
		
			
			|  | 173 | +	 */
 | 
		
	
		
			
			|  | 174 | +	addr = eaddr - size;
 | 
		
	
		
			
			|  | 175 | +	addr += ( virt_to_phys ( _text ) - addr ) & ( max_align - 1 );
 | 
		
	
		
			
			|  | 176 | +	DBG ( "After alignment, new location is [%x,%x)\n",
 | 
		
	
		
			
			|  | 177 | +	      addr, addr + _end - _text );
 | 
		
	
		
			
			|  | 178 | +
 | 
		
	
		
			
			|  | 179 | +	if ( addr != virt_to_phys ( _text ) ) {
 | 
		
	
		
			
			|  | 180 | +		DBG ( "Relocating _text from: [%lx,%lx) to [%lx,%lx)\n",
 | 
		
	
		
			
			|  | 181 | +		      virt_to_phys ( _text ), virt_to_phys ( _end ),
 | 
		
	
		
			
			|  | 182 | +		      addr, addr + _end - _text );
 | 
		
	
		
			
			|  | 183 | +
 | 
		
	
		
			
			|  | 184 | +		relocate_to ( addr );
 | 
		
	
		
			
			| 101 | 185 |  	}
 | 
		
	
		
			
			| 102 | 186 |  }
 | 
		
	
		
			
			| 103 |  | -
 | 
		
	
		
			
			| 104 |  | -#endif /* NORELOCATE */
 |