[relocate] Remove the even megabyte constraint
[gpxe.git] / src / arch / i386 / core / relocate.c
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 gPXE
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 gPXE 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 gPXE will fit into.
63          */
64         new_end = end;
65         for ( i = 0 ; i < memmap.count ; i++ ) {
66                 struct memory_region *region = &memmap.regions[i];
67                 unsigned long r_start, r_end;
68
69                 DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
70                 
71                 /* Truncate block to MAX_ADDR.  This will be less than
72                  * 4GB, which means that we can get away with using
73                  * just 32-bit arithmetic after this stage.
74                  */
75                 if ( region->start > MAX_ADDR ) {
76                         DBG ( "...starts after MAX_ADDR=%lx\n", MAX_ADDR );
77                         continue;
78                 }
79                 r_start = region->start;
80                 if ( region->end > MAX_ADDR ) {
81                         DBG ( "...end truncated to MAX_ADDR=%lx\n", MAX_ADDR );
82                         r_end = MAX_ADDR;
83                 } else {
84                         r_end = region->end;
85                 }
86                 DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end );
87
88                 /* If we have rounded down r_end below r_ start, skip
89                  * this block.
90                  */
91                 if ( r_end < r_start ) {
92                         DBG ( "...truncated to negative size\n" );
93                         continue;
94                 }
95
96                 /* Check that there is enough space to fit in gPXE */
97                 if ( ( r_end - r_start ) < size ) {
98                         DBG ( "...too small (need %lx bytes)\n", size );
99                         continue;
100                 }
101
102                 /* If the start address of the gPXE we would
103                  * place in this block is higher than the end address
104                  * of the current highest block, use this block.
105                  *
106                  * Note that this avoids overlaps with the current
107                  * gPXE, as well as choosing the highest of all viable
108                  * blocks.
109                  */
110                 if ( ( r_end - size ) > new_end ) {
111                         new_end = r_end;
112                         DBG ( "...new best block found.\n" );
113                 }
114         }
115
116         /* Calculate new location of gPXE, and align it to the
117          * required alignemnt.
118          */
119         new_start = new_end - padded_size;
120         new_start += ( start - new_start ) & ( max_align - 1 );
121         new_end = new_start + size;
122
123         DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n",
124               start, end, new_start, new_end );
125         
126         /* Let prefix know what to copy */
127         ix86->regs.esi = start;
128         ix86->regs.edi = new_start;
129         ix86->regs.ecx = size;
130 }