arch/i386/boot/compress/head.S is responsible for decompressed the kernel and move it to 0x100000. when it is executed, the virtual address is 0x1000 for low loaded vmlinux and 0x100000 for high loaded one, which are the same with their physical addresses, respectively.
/* check contents of 0x000000 and 0x100000, if not equal, means A20 is enabled. */
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000
je 1
/* decompress the kernel and jump to it (0x100000) for low loaded kernel */
subl $16,%esp # place for structure on the stack
pushl %esp # address of structure as first arg
call SYMBOL_NAME(decompress_kernel)
orl %eax,%eax
jnz 3f
xorl %ebx,%ebx
ljmp $(__KERNEL_CS), $0x10000
/* move move_routine_start to 0x1000 and jump to it */
movl $move_routine_start,%esi
movl $0x1000,%edi
movl $move_routine_end,%ecx
subl %esi,%ecx
cld
rep
movs
popl %esi # discard the address
popl %esi # low_buffer_start
popl %ecx # lcount
popl %edx # high_buffer_start
popl %eax # hcount
movl $0x100000,%edi
cli # make sure we don't get interrupted
ljmp $(__KERNEL_CS), $0x1000 # and jump to the move routine
/* move hcount from high_buffer_start to 0x100000 and jump to it for high loaded kernel */
move_routine_start:
rep
movsb
movl %edx,%esi
movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0
rep
movsb
xorl %ebx,%ebx
/*
* Well, the kernel relies on %esp pointing into low mem,
* with the decompressor loaded high this is no longer true,
* so we set esp here.
*/
mov $0x90000,%esp
ljmp $(__KERNEL_CS), $0x100000
move_routine_end: