• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
relocate(struct i386_all_regs * ix86)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