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