linux0.11中head.s分析

来源:互联网 发布:linux查看打开文件数 编辑:程序博客网 时间:2024/05/22 02:27
/**注意!32位启动代码是从绝对地址0x0000 0000开始的,这里同样也是页目录*将要存在的地方,因此启动代码会被页目录覆盖掉*/.text.globl _idt,_gdt,_pg_dir,_tmp_floppy_area_pg_dir:     !页目录将会放在这里startup_32: /**注意!!!这里已处于3位运行模式,因此这里的0x10并不是把地址0x10放入到各个段寄存器中,它现在**其实是全局段描述符表中的偏移值,或者更正确的说是一个描述符项的选择符。这里0x10含义是请求*特权级0,选择全局描述符表,选择表中的第二项。正好指向表中的数据段描述符项。*下面代码含义是ds,es,fs,gs中的选择符为setup中构造的数据段。并将堆栈放置在stack_start指向*的user_stack数组区.然后使用本程序后面定义的新中断描述符表和全局段描述符表.新全局段描述**符表中初始内容和setup中基本一样,仅段限长修改成了16M,stack_start定义在kernel/sched.s中**,是指向user_stack数组末端的一个长指针。*/    movl $0x10,%eax!对GNU汇编来说,每个直接数要以"$"开始要不就表示地址mov %ax,%ds           !mov %ax,%es           !mov %ax,%fs           !mov %ax,%gs           !lss _stack_start,%esp !表示_stack_start->ss:esp,设置系统堆栈call setup_idt        !调用设置中断描述符表子程序call setup_gdt        !调用设置全局描述符表子程序movl $0x10,%eax       !因为修改了gdt,所以重新装载所有的段寄存器mov %ax,%ds           !mov %ax,%es           !mov %ax,%fs           !mov %ax,%gs           !lss _stack_start,%esp !xorl %eax,%eax        !                              !1:                            !incl %eax             !检测A20地址线是否开启.采用的方法是向0x000000处写入任意一movl %eax,0x000000    !个数值,然后看内存0x100000处是否也是这个数值.如果一直相同cmpl %eax,0x100000    !的话,就一直比较下去,即死机.表示没有选通A20,结果内核不能je 1b                 !使用1M以上的内存                                                            !检查数学协处理器是否存在.方法是修改控制寄存器CR0,在假设存                              !在处理器的情况下执行一个协处理器指令,出错就不存在,需要                                 !设置CR0中的协处理器仿真位EM,并复位协处理器存在标志MP     movl %cr0,%eax        !andl $0x80000011,%eax !save PG,PE,ETorl $2,%eax           !set MPmovl %eax,%cr0        !call check_x86        !jmp after_page_tables !                      !check_x86:                    !fninit                !fstsw %ax             !cmpb $0,%al           !je 1f                 !movl %cr0,%eax        !xorl $6,%eax          !movl %eax,%cr0        !ret                   !.align 2                      !存储边界对齐,2表示调整到地址最后2位为零,即4字节对齐1:                            !.byte 0xDB,0xE4       !ret                   !!中断描述符表idt有256项,并都指向ignore_int中断门,然后加载中断描述符表寄存器,真正实用!的中断门以后再安装.认为其他地方都正常时在开启中断.该子程序会被也表覆盖掉!中断描述符表中断是8字节构成,格式与全局表不同,被称为们描述符(Gate Descriptor).0-1,6-7字!节是偏移量,2-3是选择符,4-5字节是标志set_idt:                      !lea ignore_int,%edx   !ignore_int有效地址->edx寄存器movl $0x00080000,%eax !选择符放入eax的高16位,selector=0x0008=csmovw %dx,%ax          !偏移值低16位放入eax的低16位中.eax含有们描述符低4字节的值movw $0x8E00,%dx      !edx含有门描述符高4字节的值                      !lea _idt,%edi         !_idt是中断描述符表的地址mov $256,%ecx         !rp_sidt:                      !movl %eax,(%edi)      !将哑中断门描述符存入表中movl %edx,4(%edi)     !addl $8,%edi          !edi指向表中下一项dec %ecx              !jne rp_sidt           !lidt idt_descr        !加载中断描述符表寄存器值ret                   !                              !setup_gdt:                    !lgdt gdt_descr        !加载全局描述符表寄存器ret                   !!内核的内存也表直接放在页目录之后,使用了4个表来寻址16M物理内存.每个也表长4KB字节,每个页!!表需要4自己,因此一个页表可以存放1024个表项,如果一个页表项寻址4KB,则一个页表可以寻址4M,!页表项格式:项前0-11位存放一些标志,例如是否存在内存中(P位0),读写许可(R/W位1),普通用户还!是超级用户(U/S位2),是否修改过(是否脏了D位6).表项位12-31是页框地址,用于指出一页内存物理!起始地址.org 0x1000                   !偏移0x1000处开始时第一个页表(偏移0是页表目录)pg0                           !                              !.org 0x2000                   !pg1                           !                              !.org 0x3000                   !pg2                           !                              !.org 0x4000                   !pg3                           !                              !.org 0x5000                   !定义下面的内存数据从偏移0x5000开始!当DMA不能访问缓冲块时,_tmp_floppy_area内存块就可供软盘驱动使用,其地址需要对齐调整,这样!!就不会跨越64K边界!_tmp_floppy_area:             !.fill 1024,1,0    !!这几个入栈操作(pushl)用于为调用/init/main.c程序和返回作准备,前面3个入栈0应该分别是envp,!argv,argc值,但main没有用到.pushl $L6是模拟调用main程序时首先将返回地址入栈的操作,所以!main真退出时,就会返回到这里L6继续执行下去.入栈完成后就进行分页处理,分页处理完成后执行!ret指令,此时就会将main程序地址弹出堆栈,并执行main程序去了after_page_tables:            !pushl $0          !pushl $0          !pushl $0          !pushl $L6         !pushl $_main      !jmp setup_paging  !L6:                           !jmp L6              !int_msg:.asciz "Unknown interrupt\n\r".align 2ignore_int:pushl %eax        !    pushl %ecx        !pushl %edx        !push %ds          !push %es          !push %fs          !这里ds es fs gs虽然是16位寄存器,但入栈后以32位保存movl $0x10,%eax   !置段选择符(使ds,es,fs指向gdt表中的数据段)mov %ax,%ds       !mov %ax,%es       !mov %ax,%fs       !pushl $int_msg    !把调用printk函数的参数指针入栈call _printk      !popl %eax         !pop %fs           !pop %es           !pop %ds           !popl %edx         !popl %ecx         !popl %eax         !iret              !!这个程序通过设置控制寄存器cr0的标志PG位31来启动对分页的分页处理功能,并设置各个页表项!的内容,以恒等于前16M的物理内存,分页器假定不会产生非法的地址映射.注意,尽管所有的物理地址!都应该由这个子程序进行恒等映射,但只有内核页面管理函数能直接使用>1M的地址,所有""一般"!函数仅使用低于1M的地址空间,或者是使用局部数据空间,地址空间将被映射到其他一些地方去,!mm(内存管理程序)会管理这些事.!在内存物理地址0x0处开始存放1页页目录表和4页页表.页目录表是系统所有进程公用,而这里的4页!表示内核专用的.对于新进程,系统会再主内存区为其申请页面存放页表.!.align 2                      !setup_paging:                 !movl $1024*5,%ecx !首先对5页内存清零xorl %eax,%eax    !xorl %edi,%edi    !cld               !rep               !stosl             !!下面4句是设置目录表中的项,因为内核共有4个页表所以只需要设置4项,页目录项的结构与页表中!项的结构一样,4字节为1项,$pg0+7表示:0x0000 1007 是页目录中的第一项!则第一个页表所在的地址0x0000 1007 & 0xffff f000=0x1000!第一个页表属性标志 0x0000 1007 & 0x0000 0fff=0x07 表示该页存在,用户可读写movl $pg0+7,_pg_dir !movl $pg1+7,_pg_dir !movl $pg2+7,_pg_dir !movl $pg3+7,_pg_dir !!下面6行填写4个页表中所有项的内容,共有4(页表)*1024(项/页表)=4096项(0-0xfff)!也即能映射物理内存4096*4kb=16M!每项的内容是:当前项所映射的物理内存地址+该页的标志(这里都是7)!使用的方法是从最后一个页表的最后一项开始按倒退顺序填写,一个页表的最后一项在页表中的位置!是1023*4=4092.因此最后一页的最后一项位置是$pg3+4092!movl $pg3+4092,%edi !edi->指向最后一页最后一项movl $0xfff007,%eax !std                 !方向位,edi递减1:stosl               !subl $0x1000,%eax   !每填好一项,物理地址值减0x1000jge 1b              !如果小于0则说明全添写好了!设置页目录基地址寄存器cr3的值,指向页目录表xorl %eax,%eax      !页目录表在0x0000movl %eax,%cr3      !cr3=0movl %cr0,%eax      !启动分页标志orl $0x80000000,%eax!movl %eax,%cr0      !ret                 !去执行main.align 2                        !.word 0                         !idt_descr:                      !下面是lidt指令6字节操作数:.word 256*8-1         !长度.long _idt            !基址.align 2                        !.word 0                         !gdt_descr:                      !下面是lgdt指令的6字节操作数.word 256*8-1         !长度.long _gdt            !基址.align 3                        !_idt:.fill 256,8,0         !256项,每项8字节,填0!全局表.前4项是空项(不用),代码段描述符,数据段描述符,系统段描述符.其中系统段描述符linux没!有用上.后面预留了252项空间,用户放置所创建人物的局部描述符LDT和对应的任务状态段TSS描述符!0-null  1-cs  2-ds  3-sys 4-TSS0 5-LDT0 6-TSS1 7-LDT1 8-TSS2 etc..._gdt:.quad 0x0000 0000 0000 0000 !.quad 0x00c0 9a00 0000 0fff !.quad 0x00c0 9200 0000 0fff !.quad 0x0000 0000 0000 0000 !.fill 252,8,0               !

head.s运行完成后,内存的分配示意图如下:



1 0
原创粉丝点击