[contrib] Allow Network Protocol header to display in rom-o-matic
[gpxe.git] / src / arch / i386 / core / relocate.c
blob3f6c6176ec54ec6613de6c8a532e7950c6d26df3
1 #include <gpxe/io.h>
2 #include <registers.h>
3 #include <gpxe/memmap.h>
5 /*
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 )
22 /* Linker symbols */
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
30 * old code - mcb30
32 #define MAX_ADDR (0xfff00000UL)
34 /**
35 * Relocate gPXE
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;
48 unsigned i;
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.
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;
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 );
77 continue;
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;
86 DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end );
88 /* If we have rounded down r_end below r_ start, skip
89 * this block.
91 if ( r_end < r_start ) {
92 DBG ( "...truncated to negative size\n" );
93 continue;
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;
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
108 * blocks.
110 if ( ( r_end - size ) > new_end ) {
111 new_end = r_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;