内核启动之启动内核——startup_32

来源:互联网 发布:人人会计软件 编辑:程序博客网 时间:2024/06/05 20:06

        这段代码在arch/x86/boot/kernel/header_32.S中,它是在内核被解压缩到0x100000处之后跳转到这个地址开始执行的,前面的操作算是为这一步做铺垫吧,现在到了真正的内核,为了能够让自己把内核代码真的弄懂,不再采用前面那种比较比较笼统的方式了,而是按照内核源码分析的那种方式将这个代码复制下来,然后逐行的分析,这样就不至于再给自己偷懒的机会了,嗯,以后就这样做,争取能够把不懂的内容搞懂。 

        在这个时候,我们已经进入了保护模式(也就是寻址方式发生了改变),但是没有启用分页,所以得到的线性地址就是物理地址,之前的bootloader的内容已经对我们没有任何意义了,唯一剩余的就是它留下来一个关于一些参数结构体boot_params,这个结构体的地址保存在寄存器esi中,现在重新开始内核的执行。

代码如下:

.text/* 省略头文件 *//* Physical address */#define pa(X) ((X) - __PAGE_OFFSET)      //逻辑地址转换成物理地址,因为这时候已经开启了保护模式,所以内核对应着高端的1G内存,并且和物理内存直接映射/* * References to members of the new_cpu_data structure. */#define X86 new_cpu_data+CPUINFO_x86#define X86_VENDOR new_cpu_data+CPUINFO_x86_vendor#define X86_MODEL new_cpu_data+CPUINFO_x86_model#define X86_MASK new_cpu_data+CPUINFO_x86_mask#define X86_HARD_MATH new_cpu_data+CPUINFO_hard_math#define X86_CPUID new_cpu_data+CPUINFO_cpuid_level#define X86_CAPABILITY new_cpu_data+CPUINFO_x86_capability#define X86_VENDOR_ID new_cpu_data+CPUINFO_x86_vendor_id/* * This is how much memory in addition to the memory covered up to * and including _end we need mapped initially. * We need: *     (KERNEL_IMAGE_SIZE/4096) / 1024 pages (worst case, non PAE) *     (KERNEL_IMAGE_SIZE/4096) / 512 + 4 pages (worst case for PAE) * * Modulo rounding, each megabyte assigned here requires a kilobyte of * memory, which is currently unreclaimed. * * This should be a multiple of a page. * * KERNEL_IMAGE_SIZE should be greater than pa(_end) * and small than max_low_pfn, otherwise will waste some page table entries */#if PTRS_PER_PMD > 1#define PAGE_TABLE_SIZE(pages) (((pages) / PTRS_PER_PMD) + PTRS_PER_PGD)#else#define PAGE_TABLE_SIZE(pages) ((pages) / PTRS_PER_PGD)    //pages页需要的页表项#endif/* Number of possible pages in the lowmem region */LOWMEM_PAGES = (((1<<32) - __PAGE_OFFSET) >> PAGE_SHIFT)         //内核能够映射的物理内存的大小,低端内存/* Enough space to fit pagetables for the low memory linear map */MAPPING_BEYOND_END = PAGE_TABLE_SIZE(LOWMEM_PAGES) << PAGE_SHIFT     //低端内存所占用的页表项的大小/* * Worst-case size of the kernel mapping we need to make: * a relocatable kernel can live anywhere in lowmem, so we need to be able * to map all of lowmem. */KERNEL_PAGES = LOWMEM_PAGES                            //内核能够映射的内存大小INIT_MAP_SIZE = PAGE_TABLE_SIZE(KERNEL_PAGES) * PAGE_SIZE           //同MAPPING_BEYOND_ENDRESERVE_BRK(pagetables, INIT_MAP_SIZE)/* * 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. */__HEADENTRY(startup_32)                    //0x100000,从这里开始执行内核的代码movl pa(stack_start),%ecx            //这里设置堆栈/* test KEEP_SEGMENTS flag to see if the bootloader is askingus to not reload segments */testb $(1<<6), BP_loadflags(%esi)          //判断是否需要重新设置各个段寄存器jnz 2f/* * Set segments to known values. */lgdt pa(boot_gdt_descr)                     //设置临时的全局页描述符表,后面还需要进一步初始化movl $(__BOOT_DS),%eax                      //初始化各个段寄存器movl %eax,%dsmovl %eax,%esmovl %eax,%fsmovl %eax,%gsmovl %eax,%ss2:leal -__PAGE_OFFSET(%ecx),%esp             //设置堆栈的栈顶指针,这个堆栈就是init_task的堆栈,这里相当于手工初始化了一个进程/* * Clear BSS first so that there are no surprises... */                                                 //初始化bss段,清0cldxorl %eax,%eaxmovl $pa(__bss_start),%edi                          //bss段的起始位置movl $pa(__bss_stop),%ecx                           subl %edi,%ecx                                      //计算bss段的大小shrl $2,%ecx                                        //以4字节为单位对bss段进行清0rep ; stosl/* * 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. */movl $pa(boot_params),%edi            //拷贝boot_params的内容movl $(PARAM_SIZE/4),%ecxcldrepmovslmovl pa(boot_params) + NEW_CL_POINTER,%esi         //这里保存的是boot_params.hdr.cmd_line_ptrandl %esi,%esijz 1f # No command linemovl $pa(boot_command_line),%edimovl $(COMMAND_LINE_SIZE/4),%ecxrepmovsl1:#ifdef CONFIG_OLPC/* save OFW's pgdir table for later use when calling into OFW */movl %cr3, %eaxmovl %eax, pa(olpc_ofw_pgd)#endif/* * Initialize page tables.  This creates a PDE and a set of page * tables, which are located immediately beyond __brk_base.  The variable * _brk_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. */#ifdef CONFIG_X86_PAE             //忽略使用PAE,假设没有开始PAE模式/** In PAE mode initial_page_table is statically defined to contain* enough entries to cover the VMSPLIT option (that is the top 1, 2 or 3* entries). The identity mapping is handled by pointing two PGD entries* to the first kernel PMD.** Note the upper half of each PMD or PTE are always zero at this stage.*/#define KPMDS (((-__PAGE_OFFSET) >> 30) & 3) /* Number of kernel PMDs */xorl %ebx,%ebx/* %ebx is kept at zero */movl $pa(__brk_base), %edimovl $pa(initial_pg_pmd), %edxmovl $PTE_IDENT_ATTR, %eax10:leal PDE_IDENT_ATTR(%edi),%ecx/* Create PMD entry */movl %ecx,(%edx)/* Store PMD entry *//* Upper half already zero */addl $8,%edxmovl $512,%ecx11:stoslxchgl %eax,%ebxstoslxchgl %eax,%ebxaddl $0x1000,%eaxloop 11b/** End condition: we must map up to the end + MAPPING_BEYOND_END.*/movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebpcmpl %ebp,%eaxjb 10b1:addl $__PAGE_OFFSET, %edimovl %edi, pa(_brk_end)shrl $12, %eaxmovl %eax, pa(max_pfn_mapped)/* Do early initialization of the fixmap area */movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eaxmovl %eax,pa(initial_pg_pmd+0x1000*KPMDS-8)#else /* Not PAE */page_pde_offset = (__PAGE_OFFSET >> 20);          //0x0C00,这里是内核所使用的第一个项页目录项在全局页目录表中的索引movl $pa(__brk_base), %edi            //找到_brk_base,从这个地址开始存放页表,由于对于4G的地址空间,只需要一个全局页目录表就足够了,于是在这里定义了这个全局页目录表,也就是initial_page_table,这里我们需要设置它每一项所对应的页表,__brk_base是从64K的地址开始的。movl $pa(initial_page_table), %edx         //全局页目录表的首地址movl $PTE_IDENT_ATTR, %eax                 //内核使用的页表项的属性10:leal PDE_IDENT_ATTR(%edi),%ecx/* Create PDE entry */         //内核使用的全局页目录项的属性,这里因为dei保存的是第一个页表的首地址(低12位对齐),所以使用加法相当于这个地址与属性域进行或操作,这也就是第一个页目录项的值(高20位的地址域正好对应到__brk_base,属性为这里设置的)movl %ecx,(%edx)/* Store identity PDE entry */               //将这里的全局页目录项的第一项(索引为0的那一项,是用户地址空间的页目录项)设置为刚才构造的值,但是这一项不会被使用,因为在init_task的地址空间中并不访问用户地址空间。这里的ecx的内容是0x00010067,也就是页目录项的第一项,所以当查找内地址空间0xC0000000的时候,它会找到全局页目录表的0x0C00项,得到的页目录项0x00010067,所以它映射的地址是0x00010000,也就是64K的地址,这里是一个页表,然后再通过查找这个页表取得地址。movl %ecx,page_pde_offset(%edx)/* Store kernel PDE entry */       //这里将全局页目录项的第0x0C00项也设置为刚才构造的值,这里可以看出,全局页目录项的0项和0x0C00项的内容是一样的。addl $4,%edxmovl $1024, %ecx11:stosl                    //这里循环1024次,为每一个页表项进行初始化。初始化的内容是由eax提供的,初始化的时候eax的内容是0x00000003,然后随着每次的循环,eax加上0x1000,也就是指向下一个页面。这里初始化了第一个页表。初始化的第一个页表项是0x00000003,第二个页表项是0x00001003,第三个页表项是0x00002003,依次增加,这样上面的例子0xC0000000(内核地址空间的地址0)取出中间的10位(也就是0),得到的是页表的索引,所以得到的页表项就是0x00000003,因此它映射的物理地址就是0x0的位置,所以这里通过这个全局页目录表之后内核地址空间爱你和物理地址仍然是一一对应的(也就相当于物理地址 = 内核地址 - 0xC0000000,只不过得到的方式不一样吧了)。addl $0x1000,%eaxloop 11b/** End condition: we must map up to the end + MAPPING_BEYOND_END.*/movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp    cmpl %ebp,%eax             //比较eax中的当前内容是否已经超出了内核能够映射的物理地址,如果没有那就需要继续映射jb 10b                     //直到内核的地址空间全部进行映射addl $__PAGE_OFFSET, %edi      movl %edi, pa(_brk_end)           //将映射结束之后的最后一个页表的结束地址的线性地址保存在__brk_end中shrl $12, %eaxmovl %eax, pa(max_pfn_mapped)       //保存已经映射的页面数到max_pxn_mapped中/* Do early initialization of the fixmap area */movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax             //这里单独进行一个全局页目录项的映射,这一项是全局页目录表的最后一项,它映射的页表是initial_pg_fixmap,是一个空的页表,但是它的地址紧跟着这个全局页目录表。movl %eax,pa(initial_page_table+0xffc)#endif#ifdef CONFIG_PARAVIRT                            //忽略。。。/* This is can only trip for a broken bootloader... */cmpw $0x207, pa(boot_params + BP_version)jb default_entry/* Paravirt-compatible boot parameters.  Look to see what architecturewe're booting under. */movl pa(boot_params + BP_hardware_subarch), %eaxcmpl $num_subarch_entries, %eaxjae bad_subarchmovl pa(subarch_entries)(,%eax,4), %eaxsubl $__PAGE_OFFSET, %eaxjmp *%eaxbad_subarch:WEAK(lguest_entry)WEAK(xen_entry)/* Unknown implementation; there's really  nothing we can do at this point. */ud2a__INITDATAsubarch_entries:.long default_entry/* normal x86/PC */.long lguest_entry/* lguest hypervisor */.long xen_entry/* Xen hypervisor */.long default_entry/* Moorestown MID */num_subarch_entries = (. - subarch_entries) / 4.previous#elsejmp default_entry#endif /* CONFIG_PARAVIRT *//* * Non-boot CPU entry point; entered from trampoline.S * We can't lgdt here, because lgdt itself uses a data segment, but * we know the trampoline has already loaded the boot_gdt for us. * * If cpu hotplug is not supported then this code can go in init section * which will be freed later */__CPUINIT#ifdef CONFIG_SMP                 //SMP处理,忽略。。。ENTRY(startup_32_smp)cldmovl $(__BOOT_DS),%eaxmovl %eax,%dsmovl %eax,%esmovl %eax,%fsmovl %eax,%gsmovl pa(stack_start),%ecxmovl %eax,%ssleal -__PAGE_OFFSET(%ecx),%esp#endif /* CONFIG_SMP */default_entry:/* * New page tables may be in 4Mbyte page mode and may * be using the global pages.  * * NOTE! If we are on a 486 we may have no cr4 at all! * So we do not try to touch it unless we really have * some bits in it to set.  This won't work if the BSP * implements cr4 but this AP does not -- very unlikely * but be warned!  The same applies to the pse feature * if not equally supported. --macro * * NOTE! We have to correct for the fact that we're * not yet offset PAGE_OFFSET.. */#define cr4_bits pa(mmu_cr4_features)movl cr4_bits,%edx                    //判断是否启用4M的页面,这里不使用,直接跳转到6处执行。andl %edx,%edxjz 6fmovl %cr4,%eax# Turn on paging options (PSE,PAE,..)orl %edx,%eaxmovl %eax,%cr4testb $X86_CR4_PAE, %al# check if PAE is enabledjz 6f/* Check if extended functions are implemented */movl $0x80000000, %eaxcpuid/* Value must be in the range 0x80000001 to 0x8000ffff */subl $0x80000001, %eaxcmpl $(0x8000ffff-0x80000001), %eaxja 6f/* Clear bogus XD_DISABLE bits */call verify_cpumov $0x80000001, %eaxcpuid/* Execute Disable bit supported? */btl $(X86_FEATURE_NX & 31), %edxjnc 6f/* Setup EFER (Extended Feature Enable Register) */movl $MSR_EFER, %ecxrdmsrbtsl $_EFER_NX, %eax/* Make changes effective */wrmsr6:/* * Enable paging */movl $pa(initial_page_table), %eaxmovl %eax,%cr3/* set the page table pointer.. */          //将全局页目录表的首地址装入到CR3寄存器。movl %cr0,%eaxorl  $X86_CR0_PG,%eaxmovl %eax,%cr0/* ..and set paging (PG) bit */             //开启分页机制!ljmp $__BOOT_CS,$1f/* Clear prefetch and normalize %eip */        //跳转,清除预取的指令1:/* Shift the stack pointer to a virtual address */addl $__PAGE_OFFSET, %esp                   //这里将esp设置为虚拟地址,主要是因为现在已经开启了分页机制,所以得到的线性地址必须经过分页单元才能得到物理地址,esp本来的内容是init_task进程的堆栈,它是一个虚拟地址,经过pa操作之后就得到了它的物理地址,也就是之前esp的内容,因为在分页机制开启之前,线性地址就是物理地址,现在得到线性地址之后需要进行分页单元的处理之后才能够得到物理地址,所以再次被映射到物理地址相同的地址。P.S.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             //初始化标志寄存器(EFLAGS)popfl#ifdef CONFIG_SMPcmpb $0, readyjnz checkCPUtype#endif /* CONFIG_SMP *//* * 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            //调用setup_idt进行中断描述符表的再次初始化checkCPUtype:            //检查并测试一些CPU参数并保存movl $-1,X86_CPUID#  -1 for no CPUID initially/* check if it is 486 or 386. *//* * XXX - this does a lot of unnecessary setup.  Alignment checks don't * apply at our cpl of 0 and the stack ought to be aligned already, and * we don't need to preserve eflags. */movb $3,X86 # at least 386pushfl # push EFLAGSpopl %eax # get EFLAGSmovl %eax,%ecx# save original EFLAGSxorl $0x240000,%eax# flip AC and ID bits in EFLAGSpushl %eax # copy to EFLAGSpopfl # set EFLAGSpushfl # get new EFLAGSpopl %eax # put it in eaxxorl %ecx,%eax# change in flagspushl %ecx # restore original EFLAGSpopfltestl $0x40000,%eax# check if AC bit changedje is386movb $4,X86 # at least 486testl $0x200000,%eax# check if ID bit changedje is486/* get vendor info */xorl %eax,%eax# call CPUID with 0 -> return vendor IDcpuidmovl %eax,X86_CPUID# save CPUID levelmovl %ebx,X86_VENDOR_ID# lo 4 charsmovl %edx,X86_VENDOR_ID+4# next 4 charsmovl %ecx,X86_VENDOR_ID+8# last 4 charsorl %eax,%eax # do we have processor info as well?je is486movl $1,%eax # Use the CPUID instruction to get CPU typecpuidmovb %al,%cl # save reg for future useandb $0x0f,%ah# mask processor familymovb %ah,X86andb $0xf0,%al# mask modelshrb $4,%almovb %al,X86_MODELandb $0x0f,%cl# mask mask revisionmovb %cl,X86_MASKmovl %edx,X86_CAPABILITYis486: movl $0x50022,%ecx# set AM, WP, NE and MPjmp 2fis386: movl $2,%ecx# set MP2: movl %cr0,%eaxandl $0x80000011,%eax# Save PG,PE,ETorl %ecx,%eaxmovl %eax,%cr0               //设置CR0寄存器的一些标志位call check_x87               //检查协处理器lgdt early_gdt_descr         //装载全局描述符表,这里面仅仅保存了四项lidt idt_descr               //装载中断描述符表,这里面简单设置了所有的256项ljmp $(__KERNEL_CS),$1f      //再次跳转1: movl $(__KERNEL_DS),%eax# reload all the segment registersmovl %eax,%ss # after changing gdt.         //设置段寄存器,SS置为内核代码段(可执行)寄存器的内容movl $(__USER_DS),%eax# DS/ES contains default USER segmentmovl %eax,%dsmovl %eax,%es         //ds和es设置为用户数据段的内容movl $(__KERNEL_PERCPU), %eaxmovl %eax,%fs # set this cpu's percpu#ifdef CONFIG_CC_STACKPROTECTOR         //栈保护,忽略。。。/** The linker can't handle this by relocation.  Manually set* base address in stack canary segment descriptor.*/cmpb $0,readyjne 1fmovl $gdt_page,%eaxmovl $stack_canary,%ecxmovw %cx, 8 * GDT_ENTRY_STACK_CANARY + 2(%eax)shrl $16, %ecxmovb %cl, 8 * GDT_ENTRY_STACK_CANARY + 4(%eax)movb %ch, 8 * GDT_ENTRY_STACK_CANARY + 7(%eax)1:#endifmovl $(__KERNEL_STACK_CANARY),%eaxmovl %eax,%gs        //设置gs寄存器xorl %eax,%eax# Clear LDT              //这里清除LDTR,因为我们不使用LDTlldt %axcld # gcc2 wants the direction flag cleared at all timespushl $0 # fake return address for unwinder       //如果返回,那么就会回到地址0处,出现错误。。。movb $1, readyjmp *(initial_code)            //这时候可以跳转了,并且一去不复返。。。/* * We depend on ET to be correct. This checks for 287/387. */check_x87:movb $0,X86_HARD_MATHcltsfninitfstsw %axcmpb $0,%alje 1fmovl %cr0,%eax/* no coprocessor: have to set bits */xorl $4,%eax /* set EM */movl %eax,%cr0retALIGN1: 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. */setup_idt:lea ignore_int,%edx          movl $(__KERNEL_CS << 16),%eaxmovw %dx,%ax /* selector = 0x0010 = cs */movw $0x8E00,%dx/* interrupt gate - dpl=0, present */ //设置了每一项的中断描述符,因为中断描述符是64为,所以需要使用两个寄存器保存其内容,其中eax保存低32为内容,edx保存高32位内容,这里我们假设ignore_int的地址是XXXXYYYY,所以eax的内容就是00020YYYY , edx的内容就是XXXX8E00 , 因为它的标识字段是0xE,所以这是一个中断门,DPL为0,当发生中断的时候,就会跳转到这里取出这个中断描述符,然后根据选择子字段(0x0020)和偏移量(ignore_int的地址)就找到了ignore_int的地址,从这里开始执行中断处理程序,当然,这只是一个临时的中断处理程序,之后还需要进一步完善。lea idt_table,%edi mov $256,%ecxrp_sidt:movl %eax,(%edi)movl %edx,4(%edi)addl $8,%edidec %ecx                 //设置全局的256项中断描述符,并且内容全部相同jne rp_sidt.macro set_early_handler handler,trapno              //这里定义了一个宏定义,它的作用是根据handler(中断处理程序)和trapno(中断号)两个参数在中断描述符表中设置对应的那一项lea \handler,%edxmovl $(__KERNEL_CS << 16),%eaxmovw %dx,%axmovw $0x8E00,%dx/* interrupt gate - dpl=0, present */lea idt_table,%edimovl %eax,8*\trapno(%edi)movl %edx,8*\trapno+4(%edi).endmset_early_handler handler=early_divide_err,trapno=0           //除法错误(除以0操作)set_early_handler handler=early_illegal_opcode,trapno=6       //无效操作码set_early_handler handler=early_protection_fault,trapno=13    //通用保护(越权访问)set_early_handler handler=early_page_fault,trapno=14          //缺页中断//虽然前面已经设置了所有的中断描述符表,但是那只是一个十分简陋的中断处理程序,由于以上四个中断(异常)相对比较重要,所以这里再一次强化了他们的处理程序,但是这仍不是最终的处理程序。ret                       //返回early_divide_err:xor %edx,%edxpushl $0 /* fake errcode */jmp early_faultearly_illegal_opcode:movl $6,%edxpushl $0 /* fake errcode */jmp early_faultearly_protection_fault:movl $13,%edxjmp early_faultearly_page_fault:movl $14,%edxjmp early_faultearly_fault:             //以上四个处理程序公用的函数cld#ifdef CONFIG_PRINTKpushamovl $(__KERNEL_DS),%eaxmovl %eax,%dsmovl %eax,%escmpl $2,early_recursion_flag              //最多发生两次这样的错误je hlt_loopincl early_recursion_flagmovl %cr2,%eaxpushl %eaxpushl %edx /* trapno */pushl $fault_msgcall printk#endifcall dump_stackhlt_loop:hltjmp hlt_loop/* This is the default interrupt "handler" :-) */ALIGNignore_int:              //默认的中断处理程序,打印一段字符。cld#ifdef CONFIG_PRINTKpushl %eaxpushl %ecxpushl %edxpushl %espushl %dsmovl $(__KERNEL_DS),%eaxmovl %eax,%dsmovl %eax,%escmpl $2,early_recursion_flag           //最多发生两次这样的中断。。。je hlt_loopincl early_recursion_flagpushl 16(%esp)pushl 24(%esp)pushl 32(%esp)pushl 40(%esp)pushl $int_msgcall printkcall dump_stackaddl $(5*4),%esppopl %dspopl %espopl %edxpopl %ecxpopl %eax#endifiret#include "verify_cpu.S"__REFDATA.align 4ENTRY(initial_code)                //最终内核要跳转到的地方:i386_start_kernel.long i386_start_kernel/* * BSS section */__PAGE_ALIGNED_BSS.align PAGE_SIZE#ifdef CONFIG_X86_PAEinitial_pg_pmd:                   .fill 1024*KPMDS,4,0#elseENTRY(initial_page_table)          //全局页目录表.fill 1024,4,0#endifinitial_pg_fixmap:                 //全局页目录表最后一项对应的页表.fill 1024,4,0ENTRY(empty_zero_page)             //空页.fill 4096,1,0ENTRY(swapper_pg_dir)              .fill 1024,4,0/* * This starts the data section. */#ifdef CONFIG_X86_PAE__PAGE_ALIGNED_DATA/* Page-aligned for the benefit of paravirt? */.align PAGE_SIZEENTRY(initial_page_table).long pa(initial_pg_pmd+PGD_IDENT_ATTR),0/* low identity map */# if KPMDS == 3.long pa(initial_pg_pmd+PGD_IDENT_ATTR),0.long pa(initial_pg_pmd+PGD_IDENT_ATTR+0x1000),0.long pa(initial_pg_pmd+PGD_IDENT_ATTR+0x2000),0# elif KPMDS == 2.long 0,0.long pa(initial_pg_pmd+PGD_IDENT_ATTR),0.long pa(initial_pg_pmd+PGD_IDENT_ATTR+0x1000),0# elif KPMDS == 1.long 0,0.long 0,0.long pa(initial_pg_pmd+PGD_IDENT_ATTR),0# else#  error "Kernel PMDs should be 1, 2 or 3"# endif.align PAGE_SIZE/* needs to be page-sized too */#endif.data.balign 4ENTRY(stack_start)                 //堆栈的起址地址,这里就是init_task的内核堆栈,这里可以看做它是被创建出来的进程,因为这时候已经对init_task进行了初始化。.long init_thread_union+THREAD_SIZEearly_recursion_flag:.long 0ready: .byte 0//打印的错误信息int_msg:.asciz "Unknown interrupt or fault at: %p %p %p\n"fault_msg:/* fault info: */.ascii "BUG: Int %d: CR2 %p\n"/* pusha regs: */.ascii "     EDI %p  ESI %p  EBP %p  ESP %p\n".ascii "     EBX %p  EDX %p  ECX %p  EAX %p\n"/* fault frame: */.ascii "     err %p  EIP %p   CS %p  flg %p\n".ascii "Stack: %p %p %p %p %p %p %p %p\n".ascii "       %p %p %p %p %p %p %p %p\n".asciz "       %p %p %p %p %p %p %p %p\n"#include "../../x86/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_descrALIGN# early boot GDT descriptor (must use 1:1 address mapping).word 0 # 32 bit align gdt_desc.addressboot_gdt_descr:           .word __BOOT_DS+7.long boot_gdt - __PAGE_OFFSET.word 0 # 32-bit align idt_desc.addressidt_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.addressENTRY(early_gdt_descr).word GDT_ENTRIES*8-1.long 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_BYTESENTRY(boot_gdt)                   //这里设置了四项全局页描述符,对于当前的环境只需要只用第三项和第四项.fill GDT_ENTRY_BOOT_CS,8,0.quad 0x00cf9a000000ffff/* kernel 4GB code at 0x00000000 */.quad 0x00cf92000000ffff/* kernel 4GB data at 0x00000000 */



       好了,这段代码算是将关键部分分析了一遍,现在总结起来也就执行了如下的操作:

1、初始化各个段寄存器和GDTR,并且设置堆栈、

2、清除bss段

3、拷贝系统参数

4、设置页表和全局页目录表(其中为用户设置1项,为内核设置所有项)并开启分页模式

5、再次初始化IDT,虽然这仍然不是最终的中断描述符表,但是较之于以前的有了改变

6、检查CPU并保存一些CPU参数、初始化协处理器

7、装载IGTR和GDTR,其中中断描述符表有256项,GDTR暂时只存在4项(在内核空间也只需要2项)

8、跳转到i386_start_kernel处执行。


        终于通过查资料把这段代码自己搞明白了,快5点了,兴奋的一点都不想睡,接下来的代码都是C语言编写的了,所以应该会轻松点了吧,加油!!!

妈的,我算是被CSDN的文本格式搞败了,辛辛苦苦搞了半天才发现发表出来格式一塌糊涂,真的不知道怎么使用,气死了。。。

       
原创粉丝点击