热门关键字:  ubuntu  分区  Fedora  linux系统进程  函数

当前位置 :| 主页>Linux教程>内核研究>

/sys/i386/i386/locore.s分析笔记

来源: 作者: 时间:2008-08-27 Tag: 点击:

若非0,则将此内容赋给esi。示例中,bi_kernend字段的值是0xd74000。
PDRMASK的低22比特全1,734和735两行就是把esi中的值,也就是传入的bi_kernend
字段的值向上调整为4M的辈数。示例中即为0x01000000。随后将调整之后的内核结束位置
写入全局变量KERNend和physfree,而下一个空闲页面就是从physfree开始的。
739 /* Allocate Kernel Page Tables */
740 ALLOCPAGES(NKPT)
NKPT目前定义为30,此处即通过ALLOCPAGES分配30个页面。
158 #define ALLOCPAGES(foo) \
159 movl R(physfree), %esi ; \
160 movl $((foo)*PAGE_SIZE), %eax ; \
161 addl %esi, %eax ; \
162 movl %eax, R(physfree) ; \
163 movl %esi, %edi ; \
164 movl $((foo)*PAGE_SIZE),%ecx ; \
165 xorl %eax,%eax ; \
166 cld ; \
167 rep ; \
168 stosb
首先将当前的空闲物理页面地址赋给esi,即0x01000000。PAGE_SIZE是4096,
入参foo是30,由此算出总字节数是122880,将其存入eax,在加上esi中的起始地址,
得到分配区域之后的地址,即0x0101e000,将其存为physfree变量的新值。
随后将edi指向新分配的30个页面空间的起始位置0x01000000。将新分配空间的字节数
0x1e000写入ecx作为计数,165行到168行将这段新分配的空间清0。
741 movl %esi,R(KPTphys)
完成30个页面空间的分配之后,将这部分空间的起始位置,即0x01000000写入全局变量
KPTphys,表示内核页表的物理地址。
749 ALLOCPAGES(NPGPTD)
NPGPTD是页表目录所占用的页面数目,对于非PAE的情况,页表目录共需
4*(2^10)=4096字节,即1个页面。此处再通过ALLOCPAGES分配一个页面的空间,
分配完毕之后,physfree指向新的空闲页面起始地址0x0101f000,esi指向这个页面
的起始位置0x0101e000。
750 movl %esi,R(IdlePTD)
将新分配的这个页面的起始地址0x0101e000写入全局变量IdlePTD,
表示内核页表目录的物理地址。
752 /* Allocate KSTACK */
753 ALLOCPAGES(KSTACK_PAGES)
754 movl %esi,R(p0kpa)
755 addl $KERNBASE, %esi
756 movl %esi, R(proc0kstack)
757
758 ALLOCPAGES(1) /* vm86/bios stack */
759 movl %esi,R(vm86phystk)
760
761 ALLOCPAGES(3) /* pgtable + ext + IOPAGES */
762 movl %esi,R(vm86pa)
763 addl $KERNBASE, %esi
764 movl %esi, R(vm86paddr)
KSTACK_PAGES在/sys/i386/include/param.h中定义为2,此处即再分配2个页面的空间,
physfree指向0x01021000。
分配完成之后,将这段空间的起始地址0x0101f000赋给全局变量p0kpa,
表示proc0的栈的物理地址,另外将这个
起始地址对应的虚拟地址0xc101f000写入全局变量proc0kstack,表示proc0的kstack
空间的(虚拟)地址。
随后再分配一个页面的空间,physfree指向0x01022000。分配完成之后,将这段空间的
起始地址0x01021000赋给全局变量vm86phystk,表示vm86/bios栈的物理地址。
随后再分配三个页面的空间,physfree指向0x01025000。分配完成之后,将这段空间的
起始地址0x01022000赋给全局变量vm86pa,表示vm86区域的物理地址,同时将这个起始
地址对应的虚拟地址0xc1022000赋给全局变量vm86paddr,表示vm86区域的(虚拟)地址。
766 #ifdef SMP
767 /* Allocate cpu0's private data page */
768 ALLOCPAGES(1)
769 movl %esi,R(cpu0pp)
770 addl $KERNBASE, %esi
771 movl %esi, R(cpu0prvpage) /* relocated to KVM space */
772
773 /* Allocate SMP page table page */
774 ALLOCPAGES(1)
775 movl %esi,R(SMPptpa)
776 addl $KERNBASE, %esi
777 movl %esi, R(SMPpt) /* relocated to KVM space */
778 #endif /* SMP */
对于smp的情况,再分配一个页面的空间给cpu0的私有数据页,physfree指向0x01026000。
分配完成之后,将这段空间的起始地址0x01025000赋给全局变量cpu0pp,同时将这个
起始地址对应的虚拟地址0xc1025000赋给全局变量cpu0prvpage。随后再分配一个页面的
空间给SMP页表页,physfree指向0x01027000。分配完成之后,将这段空间的起始地址
0x0x01026000赋给全局变量SMPptpa,同时将这个起始地址对应的虚拟地址0xc1026000
赋给全局变量SMPpt。
780 /*
781 * Enable PSE and PGE.
782 */
783 #ifndef DISABLE_PSE
784 testl $CPUID_PSE, R(cpu_feature)
785 jz 1f
786 movl $PG_PS, R(pseflag)
787 movl %cr4, %eax
788 orl $CR4_PSE, %eax
789 movl %eax, %cr4
790 1:
791 #endif
CPUID_PSE在/sys/i386/include/specialreg.h中定义为0x00000008,这是PSE标志在
cpuid返回值中的位置。全局变量cpu_feature由之前调用的identify_cpu函数设置,
内容获取自cpuid指令。此处判断其PSE对应比特是否为1。PG_PS在/sys/i386/include/pmap.h
中定义为0x080,这是页表目录项和页表项中表示页面尺寸的比特位置。pseflag是在
/sys/i386/i386/pmap.c中定义的全局变量,用于表示是否开启了PSE功能。CR4_PSE在
/sys/i386/include/specialreg.h中定义为0x00000010,这是PSE在cr4寄存器中的比特位置。
上面这几行的意思就是如果cpuid指令的结果显示cpu支持PSE,则将全局变量pseflag设置为
0x80,并将cr4寄存器中PSE对应比特设置为1。
792 #ifndef DISABLE_PG_G
793 testl $CPUID_PGE, R(cpu_feature)
794 jz 2f
795 movl $PG_G, R(pgeflag)
796 movl %cr4, %eax
797 orl $CR4_PGE, %eax
798 movl %eax, %cr4
799 2:
800 #endif
CPUID_PGE在/sys/i386/include/specialreg.h中定义为0x00002000,这是PGE标志在
cpuid返回值中的位置。PG_G在/sys/i386/include/pmap.h中定义为0x100,此处将其赋给
全局变量pgeflag,表示开启了全局页面支持。CR4_PGE在/sys/i386/include/specialreg.h
中定义为0x00000080,这是PGE在cr4寄存器中的比特位置。上面这几行的意思就是如果
cpuid指令的结果显示cpu支持PGE,则将全局变量pgeflag设置为0x100,并将cr4寄存器中
PGE对应比特设置为1。
814 xorl %eax, %eax
815 movl R(KERNend),%ecx
816 shrl $PAGE_SHIFT,%ecx
817 fillkptphys($PG_RW)
清空eax。将KRENend的值0x1000000(16M)写入ecx,并右移12比特,得到页面数目
0x1000(4096)。我们先来看看fillkpt宏的代码:
170 /*
171 * fillkpt
172 * eax = page frame address
173 * ebx = index into page table
174 * ecx = how many pages to map
175 * base = base address of page dir/table
176 * prot = protection bits
177 */
178 #define fillkpt(base, prot) \
179 shll $PTESHIFT,%ebx ; \
180 addl base,%ebx ; \
181 orl $PG_V,%eax ; \
182 orl prot,%eax ; \
183 1: movl %eax,(%ebx) ; \
184 addl $PAGE_SIZE,%eax ; /* increment physical address */ \
185 addl $PTESIZE,%ebx ; /* next pte */ \
186 loop 1b
187
入参ebx是页表内的索引,PTESHIFT在/sys/i386/include/pmap.h中定义为2(非PAE),
179行将ebx左移2位得到的就是页表内的偏移地址。加上base中存放的页表基址得到的
就是页表项的地址。PG_V在/sys/i386/include/pmap.h中定义为0x001,这是页表目录项
或页表项中表示对应的页面当前是否在物理内存中的标志比特。入参eax存放的页面的
基地址,因此低12比特均为0。181和182行将eax的第0比特和第1比特置1,表示对应页面
存在于内存中,属性为读写。183行将在eax中构建好的页表项写入ebx指向的页表项地址。
184行在eax中构造用于描述下一个页面的页表项,185行将ebx指向下一条页表项的地址,
186行循环写入新构建的页表项。
因此,fillkpt的功能就是,给定某个页表页面的基地址(base)、页面保护模式(prot)、
需要填充的第一页表项在页表页面内的索引(ebx)、页面的基地址(eax)、需要填充的
页表项的数目(ecx),填充从ebx开始的ecx个页表项,让其指向从eax开始的ecx个连续
页面,每个页表项的第0比特置1,表示页面存在于物理内存中,第1比特置1,表示属性为读写。
下面来看fillkptphys的代码:
188 /*
189 * fillkptphys(prot)
190 * eax = physical address
191 * ecx = how many pages to map
192 * prot = protection bits
193 */
194 #define fillkptphys(prot) \
195 movl %eax, %ebx ; \
196 shrl $PAGE_SHIFT, %ebx ; \
197 fillkpt(R(KPTphys), prot)
eax为页面物理地址,将其右移12比特之后得到页面在页表内的索引。fillkpt的第一
入参是页表页面的基地址,这里指定的是KPTphys,这是紧邻在KERNend之后的30个
页面空间的起始地址(0x1000000)。 回到814到817行,这几行的意思就是填充
KPTphys(0x1000000)之后的4096个4字节条目,每个条目指向一个页面,这4096个条目
指向的就是从0地址到0x1000000之间的16M空间,这是之前存放内核的地方。
这些页面显然已经存在于物理内存中,因此对应条目的第0比特为1,第1比特为1表示
这些条目的属性为读写。完成设置之后的这4096个页表项的内容如下:
0x1000000 : 0x00000003 0x00001003 0x00002003 0x00003003
0x1000010 : 0x00004003 0x00005003 0x00006003 0x00007003
......
0x1003fe0 : 0x00ff8003 0x00ff9003 0x00ffa003 0x00ffb003
0x1003ff0 : 0x00ffc003 0x00ffd003 0x00ffe003 0x00fff003
826 movl R(IdlePTD), %eax
827 movl $NPGPTD, %ecx
828 fillkptphys($PG_RW)
这几行是建立页表目录所在页面的映射,NPGPTD定义为1,表示页表目录所占页面为1,
IdlePTD是页表目录页面的基地址0x101e000,紧邻在KPTphys指向的30个页面空间的
后面。此处即在前面描述内核16M空间的4096个页表项的后面再用一个页表项来描述
页表目录页面,属性为读写。
0x1000000 : 0x00000003 0x00001003 0x00002003 0x00003003
0x1000010 : 0x00004003 0x00005003 0x00006003 0x00007003
......
0x1003fe0 : 0x00ff8003 0x00ff9003 0x00ffa003 0x00ffb003
0x1003ff0 : 0x00ffc003 0x00ffd003 0x00ffe003 0x00fff003
0x1004000 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004010 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004020 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004030 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004040 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004050 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004060 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004070 : 0x00000000 0x00000000 0x0101e003
中间空的30个条目就对应于KPTphys指向的30个页面。
830 /* Map proc0's KSTACK in the physical way ... */
831 movl R(p0kpa), %eax
832 movl $(KSTACK_PAGES), %ecx
833 fillkptphys($PG_RW)
p0kpa(0x101f000)是紧邻在IdelPTD之后的2个页面的空间,KSTACK_PAGES定义为2,
此处即再分配两个页表项来描述这两个页面,属性为读写。
0x1000000 : 0x00000003 0x00001003 0x00002003 0x00003003
0x1000010 : 0x00004003 0x00005003 0x00006003 0x00007003
......
0x1003fe0 : 0x00ff8003 0x00ff9003 0x00ffa003 0x00ffb003
0x1003ff0 : 0x00ffc003 0x00ffd003 0x00ffe003 0x00fff003
0x1004000 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004010 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004020 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004030 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004040 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004050 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004060 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004070 : 0x00000000 0x00000000 0x0101e003 0x0101f003
0x1004080 : 0x01020003
835 /* Map ISA hole */
836 movl $ISA_HOLE_START, %eax
837 movl $ISA_HOLE_LENGTH>>PAGE_SHIFT, %ecx
838 fillkptphys($PG_RW)
ISA_HOLE_START在/sys/i386/include/pmap.h中定义为0xa0000,ISA_HOLE_LENGTH
定义为0x100000-0xa0000,即393216字节,96个页面。此处即用96个页表条目来映射从
0xa0000开始的96个页面的空间。但这个空间是属于0-KERNend的,之前已经映射过了。
840 /* Map space for the vm86 region */
841 movl R(vm86phystk), %eax
842 movl $4, %ecx
843 fillkptphys($PG_RW)
再分配4个条目来映射vm86phystk(0x1021000)之后的4个页面的空间,属性为读写。
0x1000000 : 0x00000003 0x00001003 0x00002003 0x00003003
0x1000010 : 0x00004003 0x00005003 0x00006003 0x00007003
......
0x1003fe0 : 0x00ff8003 0x00ff9003 0x00ffa003 0x00ffb003
0x1003ff0 : 0x00ffc003 0x00ffd003 0x00ffe003 0x00fff003
0x1004000 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004010 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004020 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004030 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004040 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004050 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004060 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1004070 : 0x00000000 0x00000000 0x0101e003 0x0101f003
0x1004080 : 0x01020003 0x01021003 0x01022003 0x01023003
0x1004090 : 0x01024003
845 /* Map page 0 into the vm86 page table */
846 movl $0, %eax
847 movl $0, %ebx
848 movl $1, %ecx
849 fillkpt(R(vm86pa), $PG_RW|PG_U)
这几行将第0页映射到vm86pa指向的区域中,vm86pa指向的是0x1022000区域:
0x1022000 : 0x00000007 0x00000000 0x00000000 0x00000000
只映射了1页,属性为PG_V|PG_RS|PG_U,即0x7,表示当前存在于物理内存中、
可读写、特权级别为用户级。
851 /* ...likewise for the ISA hole */
852 movl $ISA_HOLE_START, %eax
853 movl $ISA_HOLE_START>>PAGE_SHIFT, %ebx
854 movl $ISA_HOLE_LENGTH>>PAGE_SHIFT, %ecx
855 fillkpt(R(vm86pa), $PG_RW|PG_U)
在vm86pa区域中建立ISA hole的映射,共计96个页表条目:
0x1022000 : 0x00000007 0x00000000 0x00000000 0x00000000
0x1022010 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1022270 : 0x00000000 0x00000000 0x00000000 0x00000000
......
0x1022280 : 0x000a0007 0x000a1007 0x000a2007 0x000a3007
0x1022290 : 0x000a4007 0x000a5007 0x000a6007 0x000a7007
0x10222a0 : 0x000a8007 0x000a9007 0x000aa007 0x000ab007
0x10222b0 : 0x000ac007 0x000ad007 0x000ae007 0x000af007
0x10222c0 : 0x000b0007 0x000b1007 0x000b2007 0x000b3007
0x10222d0 : 0x000b4007 0x000b5007 0x000b6007 0x000b7007
0x10222e0 : 0x000b8007 0x000b9007 0x000ba007 0x000bb007
0x10222f0 : 0x000bc007 0x000bd007 0x000be007 0x000bf007
0x1022300 : 0x000c0007 0x000c1007 0x000c2007 0x000c3007
0x1022310 : 0x000c4007 0x000c5007 0x000c6007 0x000c7007
0x1022320 : 0x000c8007 0x000c9007 0x000ca007 0x000cb007
0x1022330 : 0x000cc007 0x000cd007 0x000ce007 0x000cf007
0x1022340 : 0x000d0007 0x000d1007 0x000d2007 0x000d3007
0x1022350 : 0x000d4007 0x000d5007 0x000d6007 0x000d7007
0x1022360 : 0x000d8007 0x000d9007 0x000da007 0x000db007
0x1022370 : 0x000dc007 0x000dd007 0x000de007 0x000df007
0x1022380 : 0x000e0007 0x000e1007 0x000e2007 0x000e3007
0x1022390 : 0x000e4007 0x000e5007 0x000e6007 0x000e7007
0x10223a0 : 0x000e8007 0x000e9007 0x000ea007 0x000eb007
0x10223b0 : 0x000ec007 0x000ed007 0x000ee007 0x000ef007
0x10223c0 : 0x000f0007 0x000f1007 0x000f2007 0x000f3007
0x10223d0 : 0x000f4007 0x000f5007 0x000f6007 0x000f7007
0x10223e0 : 0x000f8007 0x000f9007 0x000fa007 0x000fb007
0x10223f0 : 0x000fc007 0x000fd007 0x000fe007 0x000ff007
......
0x1025fe0 : 0x00000000 0x00000000 0x00000000 0x00000000
0x1025ff0 : 0x00000000 0x00000000 0x00000000 0x00000000
858 /* Map cpu0's private page into global kmem (4K @ cpu0prvpage) */
859 movl R(cpu0pp), %eax
860 movl $1, %ecx
861 fillkptphys($PG_RW)


相关文章:
精通initramfs构建step by step
Linux利用kexec迅速切换内核
进程上下文VS中断上下文
内核通知链 学习笔记
linux spi子系统驱动分析
menuconfig 配置选项
《Linux操作系统内核实习》之练习一
udev详解
什么叫微内核,宏内核?
Linux 信号signal处理机制
开发简单的 Linux2.6 内核模块
删除内核的perl脚本
Linux2.6内核usb gadget驱动移植
GCC hacks in the Linux kernel
iomem
kernel学习的想法
让自己的驱动支持udev
linux内核编译步骤
内核的等待队列
Linux内核wait_queue深入分析
升级和删除内核
SD卡驱动分析2
Linux Kernel VDSO本地权限提升漏洞
内核中的TCP的追踪分析-15-TCP(IPV4)的客户端与
linux 2.6内核可加载模块的编译
内核模块HelloWorld
在环回接口上发送一个数据报
ARP初始化
1分钟编译FreeBSD内核
linux设备模型之uart驱动架构分析