3 #include <gpxe/memmap.h>
6 * Originally by Eric Biederman
8 * Heavily modified by Michael Brown
12 FILE_LICENCE ( GPL2_OR_LATER
);
15 * The linker passes in the symbol _max_align, which is the alignment
16 * that we must preserve, in bytes.
19 extern char _max_align
[];
20 #define max_align ( ( unsigned int ) _max_align )
23 extern char _textdata
[];
24 extern char _etextdata
[];
26 /* within 1MB of 4GB is too close.
27 * MAX_ADDR is the maximum address we can easily do DMA to.
29 * Not sure where this constraint comes from, but kept it from Eric's
32 #define MAX_ADDR (0xfff00000UL)
37 * @v ix86 x86 register dump from prefix
38 * @ret ix86 x86 registers to return to prefix
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.
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
;
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 );
57 DBG ( "Relocate: currently at [%lx,%lx)\n"
58 "...need %lx bytes for %d-byte alignment\n",
59 start
, end
, padded_size
, max_align
);
61 /* Walk through the memory map and find the highest address
62 * below 4GB that gPXE will fit into.
65 for ( i
= 0 ; i
< memmap
.count
; i
++ ) {
66 struct memory_region
*region
= &memmap
.regions
[i
];
67 unsigned long r_start
, r_end
;
69 DBG ( "Considering [%llx,%llx)\n", region
->start
, region
->end
);
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.
75 if ( region
->start
> MAX_ADDR
) {
76 DBG ( "...starts after MAX_ADDR=%lx\n", MAX_ADDR
);
79 r_start
= region
->start
;
80 if ( region
->end
> MAX_ADDR
) {
81 DBG ( "...end truncated to MAX_ADDR=%lx\n", MAX_ADDR
);
86 DBG ( "...usable portion is [%lx,%lx)\n", r_start
, r_end
);
88 /* If we have rounded down r_end below r_ start, skip
91 if ( r_end
< r_start
) {
92 DBG ( "...truncated to negative size\n" );
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
);
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.
106 * Note that this avoids overlaps with the current
107 * gPXE, as well as choosing the highest of all viable
110 if ( ( r_end
- size
) > new_end
) {
112 DBG ( "...new best block found.\n" );
116 /* Calculate new location of gPXE, and align it to the
117 * required alignemnt.
119 new_start
= new_end
- padded_size
;
120 new_start
+= ( start
- new_start
) & ( max_align
- 1 );
121 new_end
= new_start
+ size
;
123 DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n",
124 start
, end
, new_start
, new_end
);
126 /* Let prefix know what to copy */
127 ix86
->regs
.esi
= start
;
128 ix86
->regs
.edi
= new_start
;
129 ix86
->regs
.ecx
= size
;