《内核引导启动程序.ppt》由会员分享,可在线阅读,更多相关《内核引导启动程序.ppt(90页珍藏版)》请在金锄头文库上搜索。
1、Linux操作系统内核分析湘潭大学信息工程学院讲课内容bootsect.s程序分析setup.s程序分析head.s程序分析要求大家知道每个程序的作用!操作系统引导应用程序操作系统引导程序BIOS装载装载装载?磁盘结构磁道:不同半径的同心圆称为磁道 扇区:512B磁头:每个磁盘有两个面,每个面都有一个磁头 使用磁头号、柱面号、扇区号可唯一确定一个扇区引导扇区引导扇区是磁盘的第一个扇区(0磁头0磁道1扇区)。引导扇区中的程序是负责装载操作系统的程序,被成为自举程序或引导程序(bootstrap)。限制:自举程序的大小为512B,且最后两个字节必须为0xaa55。开机过程开机BIOS完成加电自检将
2、引导盘的引导扇区读入到物理内存0x7c00处检查0x7c00+510开始的两个字节是否是0xaa55跳到0x7c00执行P36实模式内存寻址段内偏移0154段地址015+019段地址015物理地址逻辑地址基地址64K1M段地址:段内偏移bootsect程序bootsect程序就是一个引导程序,负责把Linux操作系统内核从存储设备装入内存。用汇编语言编写,遵循Intel汇编语法装载过程0x0000000x007c000x0100000x0900000x0902000x100000setupbootsectsystem移动自己movw指令: 从ds:si移动一个字到es:di,然后根据标志寄存器
3、中direct标志位,把si和di分别加2(d=0)或减2(d=1)。rep:重复执行后面的命令,重复次数放在cx寄存器中,每执行一次后面的命令则把cx中的值减1,直到cx为0。 移动自己ds=0x7c0; si=0; /0x7c0:0es=0x9000;di=0; /0x9000:0cx=256; While(cx0)从ds:si移动一个字到es:di; if( d=0 ) si+=2; di+=2;else si-=2; di-=2; cx-;移动自己0x7c00x9000gogoJumpi go,initseg装载中断0x13中断,2号功能类似于函数:int load(驱动器号,磁头号,
4、磁道号,扇区号,要读扇区数量,读入内存地址)P39第67装载setup驱动器号:软盘磁头号:0磁头磁道号:0磁道开始扇区号:2(?)要读扇区数量:4读入内存地址:0:0x90200装载System限制l13中断一次只能读同一磁道上的扇区l实模式下内存段大小不能超过64K解决方法l一次读一个扇区l读完后l检测磁道是否读完,是则调整到下一个磁道;l检测内存段是否满了,是则调整到下一个内存段。装载System未读入已读入Min(未读入扇区字节大小,内存段空余字节大小)装载System磁盘读取规则:当0磁头的某个磁道读完,下一次就读1磁头的相同磁道;当1磁头的某个磁道读完,下一次就读0磁头的下一个磁道
5、。例如:0磁头0磁道,1磁头0磁道,0磁头1磁道,1磁头的1磁道。装载system1、if( 已读大小=64K ) then 段地址+=64K;已读大小=0;2、可读入的扇区数量=(64k-已读大小)/512B;3、if(可读入的扇区数量+当前扇区号=磁道扇区总量) then 可读入的扇区数量=磁道扇区总量-当前扇区号;4、读入5、if(磁道上无剩余扇区) then if(磁头号为0) then 磁头号=1;扇区号=1; else 磁头号=0;磁道号+;扇区号=1;跳入setup139行jmpi 0,SETUPSEG在37行定义为0x9020虚拟地址:0x9020:0物理地址:0x90200s
6、etup的开始的开始地址地址讲课内容bootsect.s程序分析setup.s程序分析head.s程序分析程序功能1-106行:获取并保存系统参数113-126行:移动system130-193行:进入保护模式获得并保存系统参数通过BIOS中断调用获得系统参数把获得的系统参数保存在0x90000开始的内存块中还记得0x90000地址上的内容吗?程序举例38行读光标位置:0x10中断0x03功能:输入参数:ah:0x03bh:页号输出参数:dh:行号dl:列号mov ax,0x9000mov ds,axmov ah,0x03xor bh,bhint 0x10mov 0,dxds:程序举例获得内存
7、大小:0x15中断0x88功能:输入参数:ah:0x88输出参数:ax:扩展内存大小,单位KBmov ah,#0x88int0x15mov 2,ax系统参数保存布局程序功能1-106行:获取并保存系统参数113-126行:移动system130-193行:进入保护模式移动system把System从0x10000(64K)移到0x00000为什么一开始不把system装载到物理内存0x00000处?在bootsect和setup前一段程序中用到了实模式下的中断调用,而这些中断的处理函数地址(中断向量表)调用被安排在物理内存0x00000开始的内存中移动system00中断处理函数的地址1中断处
8、理函数的地址4中断向量表移动system0x0000:00x1000:00x2000:00x3000:00x4000:00x5000:00x6000:00x7000:00x8000:00x9000:0es:dids:si程序功能1-106行:获取并保存系统参数113-126行:移动system130-193行:进入保护模式保护模式寻址寻址就是逻辑地址到物理地址的转换。在保护模式下有两种内存管理机制:分段和分页,其中分页是可选的。三种不同地址l逻辑地址逻辑地址:包含在机器语言指令中的地址,由一个段:段内偏移组成。l线性地址线性地址:逻辑地址到物理地址变换间的中间层。l物理地址物理地址:内存单元的
9、地址。P16第2.5节保护模式寻址段选择子段内偏移:031015线性地址031分段物理地址031分页段选择子段内偏移:031015线性地址031分段物理地址031分段段描述符保护模式下把内存分为多个段,每个段用一个描述符(数据结构)用来描述它的属性,包括段的开始地址、段长度、类型、访问权限等。段描述符由8个字节组成。段描述符的结构BASE 字段:指向段的基地址粒度标志G:段大小的单位。G=0时以字节为单位,否则以4096B为单位。LIMIT字段:段的大小,段的长度=段大小*1(G=0)或 段大小*4k(G=1)系统标志S:系统段(S=0),用于存储内核数据;否则是普通代码段或数据段TYPE字段
10、:描述段的类型DPL字段:描述符特权级别,表示为访问这个段而要求的CPU最小特权级。Present字段:段不在内存中(S=0),否则段在内存中。Linux总是把此标志设为1,因为Linux从来不把段交换到磁盘上去。D或B字段:段偏移量的地址是32位(D=1),否则是16位。AVL字段:Linux不使用保留字段,总是为0 BASE(0-15)LIMIT(0-15)BASE(16-23)TYPESDPL1BASE(24-31)LIMIT(16-19)AVL0B/DG描述符表保存有多个段描述符的内存块(数组)叫做描述符表,分为全局描述符表(Global Descriptor Table,GDT)和局
11、部描述符表(Local Descriptor Table,LDT)GDT中定义的内存段可以被所有进程使用,通常只定义一个; LDT中定义的内存段只被进程自己使用,通常每个进程一个LDT。GDT在内存中的地址存放在寄存器gdtr中,而当前进程使用的LDT的地址存放在寄存器ldtr中段选择子实模式中,段寄存器保存的是段的基地址保护模式中,段寄存器内容的含义发生了改变,它不再指向一个物理段的基地址了,而是一个段描述符的索引,通常把它称为段选择子13位索引,指定了放在GDT或LDT中的相应段描述符TI指定了描述符是放在GDT(TI=0)中还是在LDT中(TI=1)两位RPL(Requestor Pri
12、vilege Level)指定了进程的特权级别,RPL=DPL才能访问成功IndexTIRPL023115分段的实现给定一个逻辑地址检查段选择子中的TI标志,决定段描述符是保存在哪个描述符表中,并到相应的寄存器中取得描述符表的基地址把索引字段的值乘以8(段描述符的大小),加上描述符表的基地址,从而得到段描述符用段描述符中段的基地址加上逻辑地址中的偏移量,得到了线性地址GDTR/LDTR*+IndexTIRPL :Offset段描述符+线性地址8特权级别IA32提供了03四个特权级别,数字越小,级别越高高特权级可以访问低特权级和同一特权级,而低特权级不能随便访问高特权级,否则产生常规保护错误(G
13、P)Linux只使用了其中的两个级别,即0级别,对应内核态;3级别,对应用户态0123特权级别下降特权级别IndexTIRPL023115BASE(0-15)LIMIT(0-15)BASE(16-23)TYPESDPL1BASE(24-31)LIMIT(16-19)AVL0B/DG只有段选择子的RPL=段描述符中的DPL才允许访问该描述符所代表的内存段一般只考虑CS选择子的RPL要做的事段机制由CPU自动实现,我们要做的事包括:l定义好描述符表l把描述符表的基地址装入gdtr或ldtrl使用正确的段选择子gdt的定义205行 gdt:.word0,0,0,0 .word0x07FF/ 8M.w
14、ord0x0000/基地址为0.word0x9A00/可读可执行的代码段,dpl=0.word0x00C0/粒度=4K.word0x07FF / 8M .word0x0000 /基地址为0.word0x9200 /可读写的数据段,dpl=0.word0x00C0 /粒度=4Kgdt的定义BASE(0-15)LIMIT(0-15)BASE(16-23)TYPESDPL1BASE(24-31)LIMIT(16-19)AVL0B/DG0000000000000000000001111111111100000000101010010000000000001100低字节高字节0x00000x07FF0x
15、00C00x9A00装载gdtrGDTR寄存器包括两部分:32位的物理地址,以及16位GDT大小(以字节为单位)设置好GDT之后,我们需要通过LGDT指令将设定的gdt的入口地址和gdt表的大小装入GDTR寄存器。可以使用LGDT指令来设置GDT。lgdt addr(保存gdt基地址和大小的地址)装载gdtr134行:lgdt gdt_48222行:gdt_48:.word 0x800 /gdt长度2kb,256项.word 512+gdt,0x9 /gdt的基地址gdtgdt地址的高16位gdt地址的低16位0x90000+0x200+gdt0x90200setup进入保护模式控制寄存器CR
16、0PE:允许保护模式,PE=1切换到保护模式PE进入保护模式191行 mov ax,#0x0001lmsw ax /把ax的值装载到cr0中执行完setup后的内存映像跳到head中执行jmpi 0,81000基地址为0,长度为8M,gpl为0的代码段基地址为0,长度为8M,gpl为0的数据段indextirpl段选择子0x10指的是GDT中的数据段段选择子0x08指的是GDT中的代码段讲课内容bootsect.s程序分析setup.s程序分析head.s程序分析Intel和AT&T语法Intel语法操作符 目的操作数 源操作数AT&T语法操作符 源操作数 目的操作数寄存器前有%程序功能18-
17、23行:设置数据段段选择子和堆栈24行:设置中断描述符表25行:设置全局描述符表135行:设置页表,启动分页并调用内核启动函数main数据段段选择子数据段段选择子包括ds、es、fs、gsmovl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gs数据段段选择子10000基地址为0,长度为8M,gpl为0的代码段基地址为0,长度为8M,gpl为0的数据段indextirpl代码段段选择子代码段段选择子包括cssetup中的最后一条语句已经将cs设置为8jmpi 0,8设置GDT的效果虚拟地址:0x8:0x5 或 0x10:0x100不论
18、段选择子是什么,对应段的基地址都是0,所以线性地址=段的基地址+段内偏移 = 0 + 段内偏移 = 段内偏移未开启分页,所以段内偏移=线性地址=物理地址程序功能18-23行:设置数据段段选择子和堆栈24行:设置中断描述符表25行:设置全局描述符表135行:设置页表,启动分页并调用内核启动函数main实模式下的中断处理当中断发生时,用中断号检索中断表,得到中断处理程序的地址,然后进入中断处理程序进行处理0号中断处理程序地址1号中断处理程序地址2号中断处理程序地址3号中断处理程序地址0x016位的段地址16位的段内偏移保护模式下的中断处理用中断描述符来记录中断处理程序的属性,包括处理程序的地址,D
19、PL等。每个描述符8个字节。段选择子段内偏移(0-15)000D1100DPLP段内偏移(16-31)保留中断描述符表中断描述符表是中断描述符的集合,共256项。中断描述符表的基地址保存在寄存器idtr中保护模式下中断处理idtr0号中断描述符1号中断描述符2号中断描述符3号中断描述符段选择子段内偏移(0-15)00011100DPLP段内偏移(16-31)保留当中断发生时,用中断号和中断描述符表的基地址检索到中断描述符(描述符表的基地址+8*中断号),得到中断处理程序的地址,然后进入中断处理程序进行处理要做的事中断处理由CPU自动实现,我们要做的事包括:l定义好中断描述符表l把中断描述符表的
20、基地址装入idtr中定义中断描述符表在232行定义了一个256*8个字节的内存块作为中断描述符表,并且用0来初始化_idt:.fill 256,8,0 /2k大小分配256个8字节,并用0来初始化。设置中断描述符在78-91行设置这256个中断描述符ignor_int定义在150行,作用是打印一条消息“Unknown interrupt”段选择子(0x0008)段内偏移(ignor_int地址的底16位)00011100001段内偏移(ignor_int地址的高16位)保留0x8E00设置中断描述符ignore_int高16位ignore_int低16位edx0x080x0eaxignore_
21、int低16位ignore_int低16位中断描述符中断描述符ediedi装载中断描述符表基地址第92行:lidt idt_descr第222行:idt_descr: .word 256*8-1 /描述符表长度 .long _idt /描述符表地址程序功能18-23行:设置数据段段选择子和堆栈24行:设置中断描述符表25行:设置全局描述符表135行:设置页表,启动分页并调用内核启动函数main定义gdt234行_gdt:.quad 0x0000000000000000 /未用.quad 0x00c09a0000000fff /16M.quad 0x00c0920000000fff /16M.q
22、uad 0x0000000000000000 /未用 .fill 252,8,0/预留共256项,256*8=2k大小装载段描述符表基地址106行lgdt gdt_descr227行gdt_descr:.word 256*8-1 /GDT长度 .long _gdt /GDT基地址设置GDT26行30行movl 0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gs1664看不见部分程序功能18-23行:设置数据段段选择子和堆栈24行:设置中断描述符表25行:设置全局描述符表135行:设置页表,启动分页并调用内核启动函数main保护模式寻址启动
23、分页段选择子段内偏移:031015线性地址031物理地址031分段机制分页机制保护模式寻址启动分页线性地址空间物理内存保护模式寻址启动分页线性地址空间物理内存页目录页目录页表页表下一级页面的基地址页的属性0111231CR3P329保护模式寻址启动分页线性地址空间4G0M4M8M16M页目录页目录1024项index = 线性地址/4M保护模式寻址启动分页线性地址空间0M4M页目录页目录页表页表1024项4K8K12K16Kindex = (线性地址 mod 4M)/4K分页机制实现原理把线性地址分成3部分ldirectory(10位)ltable(10位)loffset(12位)页目录的地址
24、存放在寄存器CR3中线性地址的directory决定页目录中的目录项从页目录的目录项的基地址中找到页表的基地址线性地址的table决定页表中的目录项从页表的目录项的基地址中找到物理页的基地址线性地址的offset决定物理地址在物理页中的编移位置offset011table1221directory2231CR3页目录+页目录项页表页表项+物理页+*4*4要做的事分页机制由CPU自动实现,我们要做的事包括:l定义好页目录表和页表l设置页目录表和页表l把页目录表的基地址装入CR3中l启动分页定义页目录和页表第16行: _pg_dir:第114行:.org 0x1000 pg0:第117行:.org
25、 0x2000 pg1:第120行:.org 0x3000 pg2:第123行:.org 0x4000 pg3:页表3页表2页表1页表0页目录0x00000x10000x20000x30000x4000包含有部分head代码启动分页清0 (198202行)lstosl 把eax的值保存到ds:edi中,然后修改edi。(d=0时加4;否则减4) 设置页目录 (203206行)设置页表 (207212行)页表/页框的基地址页的属性0111231pg07设置页目录表和页表第4095个4k第0个4k(pg_dir)第1个4k(pg0)第2个4k(pg1)第3个4k(pg2)页目录页表0页表1页表2页
26、表3第4094个4k第4093个4k第4092个4k物物理理地地址址16M第4个4k(pg3)pg_dirpg0pg1pg2pg30xFFF000装载页目录地址第213-214行xorl %eax,%eaxmovl %eax,%cr3页目录地址在页目录地址在0x0处处启动分页控制寄存器CR0PG: PG=1允许分页PEPG启动分页第215-217行movl %cr0, %eax /把cr0的值保存到eaxorl $0x80000000,%eax /eax最高位置1movl %eax,%cr0 /把eax的值写入cr0设置页表的效果第0个4k第1个4k第2个4k第3个4k第4个4kpg_dirp
27、g0pg1pg2pg3线性地址物理地址00000000000000000000000000000010线性地址线性地址=物理地址物理地址0x20x00x400设置页表的效果第1024个4k第1025个4k第1026个4kpg_dirpg0pg1pg2pg3线性地址物理地址00000000010000000001000000000010线性地址线性地址=物理地址物理地址0x4010020x4000000x401000Linux内核寻址在分段时有x=seg(x) 即 逻辑地址偏移=线性地址在分页时有x=pg(x) 即 线性地址=物理地址总体看有x=pg(seg(x) 即 逻辑地址偏移=线性地址=物
28、理地址Linux内核寻址自己编写的代码和数据内核代码与数据16M进程物理内存16M?逻辑地址偏移=线性地址=物理地址函数调用时的堆栈void func(int a,int b) .return;baretAddrespespesp栈espLinux模拟main函数调用第136行:pushl $0pushl $0pushl $0pushl $L6pushl $_mainjmp setup_pagingsetup_paging: ret栈esp000L6的地址main的地址esp调用main的参数调用main后的返回地址void main(int argc,char *argv,char *env)程序执行后的内存布局