Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1013758
  • 博文数量: 123
  • 博客积分: 5051
  • 博客等级: 大校
  • 技术积分: 1356
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-14 10:56
文章分类
文章存档

2012年(1)

2011年(21)

2010年(13)

2009年(55)

2008年(33)

分类: LINUX

2009-05-03 16:25:09

/*
 *  linux/arch/i386/kernel/head.S -- the 32-bit startup code.
 */

.text
/*
 *......
 */
/*
 * This is how much memory *in addition to the memory covered up to
 * and including _end* we need mapped initially.
 * We need:
 *  - one bit for each possible page, but only in low memory, which means
 *     2^32/4096/8 = 128K worst case (4G/4G split.)
 *  - enough space to map all low memory, which means
 *     (2^32/4096) / 1024 pages (worst case, non PAE)
 *     (2^32/4096) / 512 + 4 pages (worst case for PAE)
 *  - a few pages for allocator use before the kernel pagetable has
 *     been set up
 *
 * Modulo rounding, each megabyte assigned here requires a kilobyte of
 * memory, which is currently unreclaimed.
 *
 * This should be a multiple of a page.
 */
LOW_PAGES = 1<<(32-PAGE_SHIFT_asm)  //LOW_PAGES=1MB,PAGE_SHIFT_asm = 12

#if PTRS_PER_PMD > 1
PAGE_TABLE_SIZE = (LOW_PAGES / PTRS_PER_PMD) + PTRS_PER_PGD
#else
PAGE_TABLE_SIZE = (LOW_PAGES / PTRS_PER_PGD)
#endif
BOOTBITMAP_SIZE = LOW_PAGES / 8  //BOOTITMAP_SIZE=128KB
ALLOCATOR_SLOP = 4            

INIT_MAP_BEYOND_END = BOOTBITMAP_SIZE + (PAGE_TABLE_SIZE + ALLOCATOR_SLOP)*PAGE_SIZE_asm

/*
 * 32-bit kernel entrypoint; only used by the boot CPU.  On entry,
 * %esi points to the real-mode code as a 32-bit pointer.
 * CS and DS must be 4 GB flat segments, but we don't depend on
 * any particular GDT layout, because we load our own as soon as we
 * can.
 */
.section .text.head,"ax",@progbits
ENTRY(startup_32)       //linux
内核代码第一个字节的地址

/*
 * Set segments to known values.
 */
    cld
    lgdt boot_gdt_descr - __PAGE_OFFSET  //
加载GDTR寄存器,从文件head.S后面的全局数据段可以看出,在初始化引导阶段,GDT表的第一项和第二项设为0,第三和第四项分别设置为0x00cf9a000000ffff  0x00cf92000000ffff.
    movl $(__BOOT_DS),%eax               //__BOOT_DS=3*8
    movl %eax,%ds                        //
    movl %eax,%es
    movl %eax,%fs
    movl %eax,%gs

/*
 * Clear BSS first so that there are no surprises...
 * No need to cld as DF is already clear from cld above...
 */
    xorl %eax,%eax             //Clear eax
    movl $__bss_start - __PAGE_OFFSET,%edi  //__bss_start is the symbol of uninitialized segment  
    movl $__bss_stop - __PAGE_OFFSET,%ecx   //__bss_end is the symbol of the end of the uninitialized segment
    subl %edi,%ecx              //Register holds the value of the length of the bss segment.
    shrl $2,%ecx                //the following two codes set all of the bss segment to be 0
    rep ; stosl             //
设置未初始化段为
0
/*
 * Copy bootup parameters out of the way.
 * Note: %esi still has the pointer to the real-mode data.
 * With the kexec as boot loader, parameter segment might be loaded beyond
 * kernel image and might not even be addressable by early boot page tables.
 * (kexec on panic case). Hence copy out the parameters before initializing
 * page tables.
 */
        /*
     *......
与内存引导无关的代码

         */
1:

/*
 * Initialize page tables.  This creates a PDE(
页目录项) and a set of page
 * tables, which are located immediately beyond _end.  The variable
 * init_pg_tables_end is set up to point to the first "safe" location.
 * Mappings are created both at virtual address 0 (identity mapping)
 * and PAGE_OFFSET for up to _end+sizeof(page tables)+INIT_MAP_BEYOND_END.
 *
 * Warning: don't use %esi or the stack in this code.  However, %esp
 * can be used as a GPR if you really need it...
 */
page_pde_offset = (__PAGE_OFFSET >> 20);    // 

    movl $(pg0 - __PAGE_OFFSET), %edi   //
临时页表在pg0变量处开始存放

    movl $(swapper_pg_dir - __PAGE_OFFSET), %edx  
    movl $0x007, %eax           /* 0x007 = PRESENT+RW+USER */
10:
    leal 0x007(%edi),%ecx           /* Create PDE entry */  //
    movl %ecx,(%edx)            /* Store identity PDE entry */
  //
将第一个页表的第三项放入到swapper_pg_dir的第一项,设置页目录项所对应页表的最后三位,设置页表属性。
    movl %ecx,page_pde_offset(%edx)     /* Store kernel PDE entry */
    //
保存内核页表,这里是分页的第一个阶段,所以要实现在实
                        //
模式下和保护模式能很容易地进行寻址
    addl $4,%edx                //
这里肯定要将edx4了,因为前面已经映射了一项了,这里就仅仅是来设置页全局目录的第一项而已。这一点是要注意的。
    movl $1024, %ecx
11:
    stosl               //
此处设置的是第一个页表内的值,对于pg0映射的是第一个4MB页框
    addl $0x1000,%eax               //
设置页全局目录表,这里仅仅设置了第一个页全局目录项
    loop 11b
    /* End condition: we must map up to and including INIT_MAP_BEYOND_END */
    /* bytes beyond the end of our own page tables; the +0x007 is the attribute bits */
    leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp
    cmpl %ebp,%eax
    jb 10b
    movl %edi,(init_pg_tables_end - __PAGE_OFFSET)

    xorl %ebx,%ebx              /* This is the boot CPU (BSP) */
    jmp 3f             

    //这里是i386,所以就直接跳转到3处了,不需要执行SMP,这里针对的是单CPU
/*
 *......
 *
/*
 * Enable paging
 */
3:  movl $swapper_pg_dir-__PAGE_OFFSET,%eax
    movl %eax,%cr3      /* set the page table pointer..
*/ //设置页全局目录

    movl %cr0,%eax     
    orl $0x80000000,%eax               
    movl %eax,%cr0      /* ..and set paging (PG) bit */// Register cr0 takes the value of 0x80000000
,启动分页机制。
    ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip *///
正规化eip指针,进入到页保护模式执行
1:
    /* Set up the stack pointer */
    lss stack_start,%esp    //
设置堆栈段

/*
 * Initialize eflags.  Some BIOS's leave bits like NT set.  This would
 * confuse the debugger if this code is traced.
 * XXX - best to initialize before switching to protected mode.
 */
    pushl $0
    popfl
/*
 * start system 32-bit setup. We need to re-do some of the things done
 * in 16-bit mode for the "real" operations.
 */
    call setup_idt

checkCPUtype:
    ......

is386:  movl $2,%ecx        # set MP
设置协处理器位
2:  movl %cr0,%eax
    andl $0x80000011,%eax   # Save PG,PE,ET
    orl %ecx,%eax
    movl %eax,%cr0

    call check_x87
    lgdt early_gdt_descr        //
重新设置GDTR寄存器
    lidt idt_descr
    ljmp $(__KERNEL_CS),$1f    

/*__KERNEL_CS=12*8,进入到内核空间,上面的代码都是作为引导用的,通过这个跳转,正式进入到内核代码段*/
1:  movl $(__KERNEL_DS),%eax    # reload all the segment registers 

 

   //进入内核段,重载所有的段寄存器
    movl %eax,%ss           # after changing gdt.
    movl %eax,%fs           # gets reset once there's real percpu

    movl $(__USER_DS),%eax      # DS/ES contains default USER segment
    movl %eax,%ds
    movl %eax,%es

    xorl %eax,%eax          # Clear GS and LDT
    movl %eax,%gs
    lldt %ax

    cld         # gcc2 wants the direction flag cleared at all times
    pushl $0        # fake return address for unwinder
    jmp start_kernel        //
跳转进入内核启动阶段

/*
 * We depend on ET to be correct. This checks for 287/387.
 */
check_x87:
    movb $0,X86_HARD_MATH
    clts
    fninit
    fstsw %ax
    cmpb $0,%al
    je 1f
    movl %cr0,%eax      /* no coprocessor: have to set bits */
    xorl $4,%eax        /* set EM */
    movl %eax,%cr0
    ret
    ALIGN
1:  movb $1,X86_HARD_MATH
    .byte 0xDB,0xE4     /* fsetpm for 287, ignored by 387 */
    ret

/*
 *  setup_idt
 *
 *  sets up a idt with 256 entries pointing to
 *  ignore_int, interrupt gates. It doesn't actually load
 *  idt - that can be done only after paging has been enabled
 *  and the kernel moved to PAGE_OFFSET. Interrupts
 *  are enabled elsewhere, when we can be relatively
 *  sure everything is ok.
 *
 *  Warning: %esi is live across this function.
 */
    /*
     *......
     */
.section .text
/*
 * Real beginning of normal "text" segment
 */
ENTRY(stext)
ENTRY(_stext)

/*
 * BSS section
 */
.section ".bss.page_aligned","wa"
    .align PAGE_SIZE_asm
ENTRY(swapper_pg_dir)
    .fill 1024,4,0          //
页全局目录表初始化为0
ENTRY(swapper_pg_pmd)
    .fill 1024,4,0          //
页中间表项初始化为
0
ENTRY(empty_zero_page)
    .fill 4096,1,0      //Initializing all of the page 0 at the Original Booting time!

/*
 * This starts the data section.
 */
.data
ENTRY(stack_start)
    .long init_thread_union+THREAD_SIZE
    .long __BOOT_DS

ready:  .byte 0

early_recursion_flag:
    .long 0

int_msg:
    .asciz "Unknown interrupt or fault at EIP %p %p %p\n"

fault_msg:
    .ascii "Int %d: CR2 %p  err %p  EIP %p  CS %p  flags %p\n"
    .asciz "Stack: %p %p %p %p %p %p %p %p\n"

#include "../xen/xen-head.S"

/*
 * The IDT and GDT 'descriptors' are a strange 48-bit object
 * only used by the lidt and lgdt instructions. They are not
 * like usual segment descriptors - they consist of a 16-bit
 * segment size, and 32-bit linear address value:
 */

.globl boot_gdt_descr
.globl idt_descr

    ALIGN
# early boot GDT descriptor (must use 1:1 address mapping)
    .word 0             # 32 bit align gdt_desc.address
boot_gdt_descr:
    .word __BOOT_DS+7
    .long boot_gdt - __PAGE_OFFSET

    .word 0             # 32-bit align idt_desc.address
idt_descr:
    .word IDT_ENTRIES*8-1       # idt contains 256 entries
    .long idt_table

# boot GDT descriptor (later on used by CPU#0):
    .word 0             # 32 bit align gdt_desc.address
ENTRY(early_gdt_descr)
    .word GDT_ENTRIES*8-1       //GDT_ENTRIES=32
    .long per_cpu__gdt_page     /* Overwritten for secondary CPUs */

/*
 * The boot_gdt must mirror the equivalent in setup.S and is
 * used only for booting.
 */
    .align L1_CACHE_BYTES
ENTRY(boot_gdt)
    .fill GDT_ENTRY_BOOT_CS,8,0      //GDT_ENTRY_BOOT_CS=2
    .quad 0x00cf9a000000ffff    /* kernel 4GB code at 0x00000000 */

    //第一、二,第七位到第十二共八个数字,每个数字是四位,来表示线性地址。
    .quad 0x00cf92000000ffff    /* kernel 4GB data at 0x00000000 */

阅读(1744) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~