Why do we do this? Don’t ask me.. Incomprehensible are the ways of bootloaders.
                             — comments in arch/i386/boot/compressed/misc.c

There are two ‘head.S’ in linux source package. One is in $(Linux-2.6.15.3_dir/arch/i386/boot/compressed and the other one is in $(Linux-2.6.15.3_dir/arch/i386/kernel. The first one will be analyzed in this artical. Before we go ahead, let’s show a news of linux, that is ‘Army leans toward Linux for FCS(Future Combat System)’.

The first ‘head.S’ is also called ‘compressed head’, which used to decompress the kernel image. Different from those code before, we are now in 32-bit protected mode with paging disabled. The ‘compressed head’ starts from ‘startup_32′.

.text /* ! here just ‘.text’, without ‘.code16′ assembly directive */
.globl startup_32
 
startup_32:
 /*
  * ! clear direction flag
  * ! and clear interrupt flag
  */
 cld
 cli

 /*
  * ! all other segment registers are
  * ! reloaded after protected mode enabled
  * ! __BOOT_DS = 0×18
  */
 movl $(__BOOT_DS),%eax
 movl %eax, %ds
 movl %eax, %es
 movl %eax, %fs
 movl %eax, %gs

 /*
  * ! lss – load full pointer from memory
  * !       to register
  * ! and here ‘ss:esp = stack_start’
  */
 lss stack_start,%esp

 /*
  * ! EAX = 0;
  * ! do {
  * !     DS:[0] = ++EAX;
  * ! } while (DS:[0x100000] == EAX);
  */
 xorl %eax, %eax
1: incl %eax  # check that A20 really IS enabled
 movl %eax, 0×000000 # loop forever if it isn’t
 cmpl %eax, 0×100000
 je 1b

After reload the segment registers, the ‘compressed head’ clears the ‘eflags’ register and fills the kernel bss(the area of uninitialized data of the kernel identified by the _edata and _end symbols) with zeros. Then the decompressed process begins.

 /*
  * ! %esi has been loaded in ‘setup.S’ with ‘INITSET << 4′
  * ! ‘subl $16,%esp’ used to store the first arg, that is
  * ! struct moveparams {
  * !     uch *low_buffer_start;
  * !     int lcount;
  * !     uch *high_buffer_start;
  * !     int hcount;
  * ! } mv;
  * ! the second arg is the %esi which indicates the position
  * ! of the real-mode data
  */
 subl $16,%esp # place for structure on the stack
 movl %esp,%eax
 pushl %esi # real mode pointer as second arg
 pushl %eax # address of structure as first arg

 /*
  * ! if (!decompress_kernel(&mv, esi)) {         // return value in AX
  * !    restore esi from stack;
  * !    ebx = 0;
  * !    goto __BOOT_CS: $__PHYSICAL_START;
  * !    // see linux/arch/i386/kernel/head.S:startup_32
  * ! }
  * ! ‘decompress_kernel’ is coded in
  * ! $(linux-2.6.15.3_dir)/arch/i386/boot/compressed/misc.c
  *
/
 call decompress_kernel
 orl  %eax,%eax
 jnz  3f
 popl %esi # discard address
 popl %esi # real mode pointer
 xorl %ebx,%ebx
 ljmp $(__BOOT_CS), $__PHYSICAL_START

3:
 /*
  * ! move move_rountine_start..move_routine_end to 0×1000
  * ! both the two functions are defined in the tail of
  * ! this file
  */
 movl $move_routine_start,%esi
 movl $0×1000,%edi
 movl $move_routine_end,%ecx
 subl %esi,%ecx
 addl $3,%ecx
 shrl $2,%ecx
 cld
 rep
 movsl

 /*
  * ! Do preparation for ‘move_routine_start’:
  * ! set the parameters
  * ! ebx = real mode pointer
  * ! esi = mv.low_buffer_start
  * ! ecx = mv.lcount
  * ! edx = mv.high_buffer_start
  * ! eax = mv.hcount
  * ! edi = $__PHYSICAL_START
  */
 popl %esi # discard the address
 popl %ebx # real mode pointer
 popl %esi # low_buffer_start
 popl %ecx # lcount
 popl %edx # high_buffer_start
 popl %eax # hcount
 movl $__PHYSICAL_START,%edi
 cli  # make sure we don’t get interrupted

 /*
  * ! jump to physical address: __BOOT_CS:0×1000
  * ! where the move_routine_start function stays
  */
 ljmp $(__BOOT_CS), $0×1000 # and jump to the move routine

 /*
  * ! the control has been transfered to ‘move_routine_start’
  */
move_routine_start:
 movl %ecx,%ebp
 shrl $2,%ecx
 rep
 movsl
 movl %ebp,%ecx
 andl $3,%ecx
 rep
 movsb
 movl %edx,%esi
 movl %eax,%ecx # NOTE: rep movsb won’t move if %ecx == 0
 addl $3,%ecx
 shrl $2,%ecx
 rep
 movsl
 movl %ebx,%esi # Restore setup pointer
 xorl %ebx,%ebx
 ljmp $(__BOOT_CS), $__PHYSICAL_START
move_routine_end:

In ‘move_routine_start’, we perform the operations as follows:
(1) move mv.low_buffer_start to $__PHYSICAL_START, (mv.lcount >> 2) words;
(2) move/append (mv.lcount & 3) bytes;
(3) move/append mv.high_buffer_start, ((mv.hcount + 3) >> 2) words.

After move the decompressed kernel image to its right place, the control will be transfered to physical address:’$(__BOOT_CS):$__PHYSICAL_START’, where the second ‘head.S’ stays.

© 2006, bigwhite. 版权所有.

Related posts:

  1. 汇编之路-栈操作与栈帧
  2. Transfer to '32-bit'
  3. Begin 'setup.S'
  4. Outline 'memory layout'
  5. Retired 'bootsect.S'