123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- #ifndef NORELOCATE
-
- #include "etherboot.h"
-
- /* by Eric Biederman */
-
- /* On some platforms etherboot is compiled as a shared library, and we use
- * the ELF pic support to make it relocateable. This works very nicely
- * for code, but since no one has implemented PIC data yet pointer
- * values in variables are a a problem. Global variables are a
- * pain but the return addresses on the stack are the worst. On these
- * platforms relocate_to will restart etherboot, to ensure the stack
- * is reinitialize and hopefully get the global variables
- * appropriately reinitialized as well.
- *
- */
-
- void relocate(void)
- {
- unsigned long addr, eaddr, size;
- unsigned i;
- /* Walk through the memory map and find the highest address
- * below 4GB that etherboot will fit into. Ensure etherboot
- * lies entirely within a range with A20=0. This means that
- * even if something screws up the state of the A20 line, the
- * etherboot code is still visible and we have a chance to
- * diagnose the problem.
- */
- /* First find the size of etherboot */
- addr = virt_to_phys(_text);
- eaddr = virt_to_phys(_end);
- size = (eaddr - addr + 0xf) & ~0xf;
-
- /* If the current etherboot is beyond MAX_ADDR pretend it is
- * at the lowest possible address.
- */
- if (eaddr > MAX_ADDR) {
- eaddr = 0;
- }
-
- for(i = 0; i < meminfo.map_count; i++) {
- unsigned long r_start, r_end;
- if (meminfo.map[i].type != E820_RAM) {
- continue;
- }
- if (meminfo.map[i].addr > MAX_ADDR) {
- continue;
- }
- if (meminfo.map[i].size > MAX_ADDR) {
- continue;
- }
- r_start = meminfo.map[i].addr;
- r_end = r_start + meminfo.map[i].size;
- /* Make the addresses 16 byte (128 bit) aligned */
- r_start = (r_start + 15) & ~15;
- r_end = r_end & ~15;
- if (r_end < r_start) {
- r_end = MAX_ADDR;
- }
- if (r_end < size) {
- /* Avoid overflow weirdness when r_end - size < 0 */
- continue;
- }
- /* Shrink the range down to use only even megabytes
- * (i.e. A20=0).
- */
- if ( r_end & 0x100000 ) {
- /* If r_end is in an odd megabyte, round down
- * r_end to the top of the next even megabyte.
- */
- r_end = r_end & ~0xfffff;
- } else if ( ( r_end - size ) & 0x100000 ) {
- /* If r_end is in an even megabyte, but the
- * start of Etherboot would be in an odd
- * megabyte, round down to the top of the next
- * even megabyte.
- */
- r_end = ( r_end - 0x100000 ) & ~0xfffff;
- }
- /* If we have rounded down r_end below r_ start, skip
- * this block.
- */
- if ( r_end < r_start ) {
- continue;
- }
- if (eaddr < r_end - size) {
- addr = r_end - size;
- eaddr = r_end;
- }
- }
- if (addr != virt_to_phys(_text)) {
- unsigned long old_addr = virt_to_phys(_text);
- printf("Relocating _text from: [%lx,%lx) to [%lx,%lx)\n",
- old_addr, virt_to_phys(_end),
- addr, eaddr);
- arch_relocate_to(addr);
- cleanup();
- relocate_to(addr);
- arch_relocated_from(old_addr);
- }
- }
-
- #endif /* NORELOCATE */
|