diff --git a/src/arch/i386/core/relocate.c b/src/arch/i386/core/relocate.c index a51d515e..c9ac7ee1 100644 --- a/src/arch/i386/core/relocate.c +++ b/src/arch/i386/core/relocate.c @@ -1,6 +1,6 @@ -#include +#include #include -#include +#include /* * Originally by Eric Biederman @@ -35,14 +35,27 @@ extern char _end[]; * @v ix86 x86 register dump from prefix * @ret ix86 x86 registers to return to prefix * - * This copies Etherboot to a suitable location near the top of 32-bit + * This finds a suitable location for Etherboot near the top of 32-bit * address space, and returns the physical address of the new location * to the prefix in %edi. */ void relocate ( struct i386_all_regs *ix86 ) { - unsigned long addr, eaddr, size; + struct memory_map memmap; + unsigned long start, end, size, padded_size; + unsigned long new_start, new_end; unsigned i; + /* Get memory map and current location */ + get_memmap ( &memmap ); + start = virt_to_phys ( _text ); + end = virt_to_phys ( _end ); + size = ( end - start ); + padded_size = ( size + max_align - 1 ); + + DBG ( "Relocate: currently at [%lx,%lx)\n" + "...need %lx bytes for %d-byte alignment\n", + start, end, padded_size, max_align ); + /* 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 @@ -50,58 +63,29 @@ void relocate ( struct i386_all_regs *ix86 ) { * etherboot code is still visible and we have a chance to * diagnose the problem. */ - - /* First find the size of etherboot, including enough space to - * pad it to the required alignment - */ - size = _end - _text + max_align - 1; - - /* Current end address of Etherboot. If the current etherboot - * is beyond MAX_ADDR pretend it is at the lowest possible - * address. - */ - eaddr = virt_to_phys(_end); - if ( eaddr > MAX_ADDR ) { - eaddr = 0; - } - - DBG ( "Relocate: currently at [%x,%x)\n" - "...need %x bytes for %d-byte alignment\n", - virt_to_phys ( _text ), eaddr, size, max_align ); - - for ( i = 0; i < meminfo.map_count; i++ ) { + new_end = end; + for ( i = 0 ; i < memmap.count ; i++ ) { + struct memory_region *region = &memmap.regions[i]; unsigned long r_start, r_end; - DBG ( "Considering [%x%x,%x%x)\n", - ( unsigned long ) ( meminfo.map[i].addr >> 32 ), - ( unsigned long ) meminfo.map[i].addr, - ( unsigned long ) - ( ( meminfo.map[i].addr + meminfo.map[i].size ) >> 32 ), - ( unsigned long ) - ( meminfo.map[i].addr + meminfo.map[i].size ) ); + DBG ( "Considering [%llx,%llx)\n", region->start, region->end); - /* Check block is usable memory */ - if (meminfo.map[i].type != E820_RAM) { - DBG ( "...not RAM\n" ); - continue; - } - /* Truncate block to MAX_ADDR. This will be less than * 4GB, which means that we can get away with using * just 32-bit arithmetic after this stage. */ - if ( meminfo.map[i].addr > MAX_ADDR ) { - DBG ( "...starts after MAX_ADDR=%x\n", MAX_ADDR ); + if ( region->start > MAX_ADDR ) { + DBG ( "...starts after MAX_ADDR=%lx\n", MAX_ADDR ); continue; } - r_start = meminfo.map[i].addr; - if ( meminfo.map[i].addr + meminfo.map[i].size > MAX_ADDR ) { + r_start = region->start; + if ( region->end > MAX_ADDR ) { + DBG ( "...end truncated to MAX_ADDR=%lx\n", MAX_ADDR ); r_end = MAX_ADDR; - DBG ( "...end truncated to MAX_ADDR=%x\n", MAX_ADDR ); } else { - r_end = meminfo.map[i].addr + meminfo.map[i].size; + r_end = region->end; } - + /* Shrink the range down to use only even megabytes * (i.e. A20=0). */ @@ -111,7 +95,7 @@ void relocate ( struct i386_all_regs *ix86 ) { * the top of the next even megabyte. */ r_end = ( r_end - 1 ) & ~0xfffff; - DBG ( "...end truncated to %x " + DBG ( "...end truncated to %lx " "(avoid ending in odd megabyte)\n", r_end ); } else if ( ( r_end - size ) & 0x100000 ) { @@ -126,13 +110,13 @@ void relocate ( struct i386_all_regs *ix86 ) { */ if ( r_end > 0x100000 ) { r_end = ( r_end - 0x100000 ) & ~0xfffff; - DBG ( "...end truncated to %x " + DBG ( "...end truncated to %lx " "(avoid starting in odd megabyte)\n", r_end ); } } - DBG ( "...usable portion is [%x,%x)\n", r_start, r_end ); + DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end ); /* If we have rounded down r_end below r_ start, skip * this block. @@ -143,8 +127,8 @@ void relocate ( struct i386_all_regs *ix86 ) { } /* Check that there is enough space to fit in Etherboot */ - if ( r_end - r_start < size ) { - DBG ( "...too small (need %x bytes)\n", size ); + if ( ( r_end - r_start ) < size ) { + DBG ( "...too small (need %lx bytes)\n", size ); continue; } @@ -156,30 +140,24 @@ void relocate ( struct i386_all_regs *ix86 ) { * Etherboot, as well as choosing the highest of all * viable blocks. */ - if ( r_end - size > eaddr ) { - eaddr = r_end; + if ( ( r_end - size ) > new_end ) { + new_end = r_end; DBG ( "...new best block found.\n" ); } } - DBG ( "New location will be in [%x,%x)\n", eaddr - size, eaddr ); - /* Calculate new location of Etherboot, and align it to the * required alignemnt. */ - addr = eaddr - size; - addr += ( virt_to_phys ( _text ) - addr ) & ( max_align - 1 ); - DBG ( "After alignment, new location is [%x,%x)\n", - addr, addr + _end - _text ); + new_start = new_end - padded_size; + new_start += ( start - new_start ) & ( max_align - 1 ); + new_end = new_start + size; - if ( addr != virt_to_phys ( _text ) ) { - DBG ( "Relocating _text from: [%lx,%lx) to [%lx,%lx)\n", - virt_to_phys ( _text ), virt_to_phys ( _end ), - addr, addr + _end - _text ); - - memcpy ( phys_to_virt ( addr ), _text, _end - _text ); - } + DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n", + start, end, new_start, new_end ); - /* Let prefix know where the new copy is */ - ix86->regs.edi = addr; + /* Let prefix know what to copy */ + ix86->regs.esi = start; + ix86->regs.edi = new_start; + ix86->regs.ecx = size; }