实模式到保护模式的过渡

本文主要参考《深入分析linux内核》,配图都来自这本书,加入了一些自己的理解。

页目录项的位定义

页表项定义

本文只会对012位解释,0present表示是否在内存中

  1 /*
  2 * .org new-lc,fill,这条汇编程序指令将本节的位置计数器提前至new-lc(New Location Counter)处,中间的字节被填充fill值,默认为0
  3 * .fill repeat,size,value 如果size大于8也只会取8,以size为一组,重复repeat次,填充value值
  4 * 只能在本节中往前,计数器只能增加,所以第一条将位置计数器变为0xc000000+0x100000+0x1000,因为编译后这些地址都是虚拟地址,所以
  5 * 为内核地址需要加上__PAGE_OFFSET,而内核前1M有特殊用途,所以从1M之后开始初始化内存
  6 * swapper_pg_dir为全局页目录起始地址,第0,1项是映射的pg0和pg1,共8M,第3-767项为空,第768,769项也为pg0和pg1,第770-1023项为空
  7 * 所以0x1000手动完成了全局页目录的初始化,用户区和内核区的开始两项的映射相同
  8 */
  9 .org 0x1000
 10 ENTRY(swapper_pg_dir)
 11 .long 0x00102007
 12 .long 0x00103007
 13 .fill BOOT_USER_PGD_PTRS - 2, 4, 0
 14 /* default: 766 entries */
 15 .long 0x00102007
 16 .long 0x00103007
 17 /* default: 254 entries */
 18 .fill BOOT_KERNEL_PGD_PTRS - 2, 4, 0
 19 
 20 /*
 21 * The page tables are initialized to only 8MB here - the final page
 22 * tables are set up later depending on memory size.
 23 * 后文会介绍完成对这两个页表的初始化
 24 */
 25 .org 0x2000
 26 ENTRY(pg0)
 27 
 28 .org 0x3000
 29 ENTRY(pg1)
 30 
 31 /*
 32 * empty_zero_page must immediately follow the page tables ! (The
 33 * initialization loop counts until empty_zero_page)
 34 * 0页表是不允许访问的,全是0
 35 */
 36 
 37 .org 0x4000
 38 ENTRY(empty_zero_page)
 39 
 40 .org 0x5000
 41 
 42 /*
 43 * Real beginning of normal "text" segment
 44 * 这里开始存放内核的代码段
 45 */
 46 ENTRY(stext)
 47 ENTRY(_stext)
 48 
 49 /*
 50 * This starts the data section. Note that the above is all
 51 * in the text section because it has alignment requirements
 52 * that we cannot fulfill any other way.
 53 * 内核data段
 54 */
 55 .data
 56 
 57 ALIGN
 58 
 59 
 60 /*
 61 * Initialize page tables
 62 * 开始初始化pg0和pg1两组页表,pg0- __PAGE_OFFSET就是pg0的起始物理地址0x102000
 63 */
 64 movl $pg0 - __PAGE_OFFSET, %edi /* initialize page tables */
 65 movl $007, %eax        
 66 /* "007" doesn't mean with right to kill, but PRESENT+RW+USER
 67 */
 68 2:
 69 /* 将eax中的值存入edi指定的位置中,因而第一次回在pg0第0项处存入0x007,对应的页就是起始物理地址为0的4KB物理内存,edi自动增加4,一项为4个byte*/
 70 stosl
 71 /* eax+4K,代表下一页*/
 72 add $0x1000, %eax
 73 /* 如果edi还未到0x4000,继续循环,即初始化了pg0和pg1两张页表 */
 74 cmp $empty_zero_page - __PAGE_OFFSET, %edi
 75 jne 2b
 76 
 77 /*
 78 * Enable paging
 79 */
 80 3:
 81 movl $swapper_pg_dir - __PAGE_OFFSET, %eax
 82 movl %eax, %cr3        /* set the page table pointer.. */
 83 movl %cr0, %eax
 84 orl $0x80000000, %eax
 85 /* 此处启用cr0的最高位PG允许位,表示开启分页机制,但因为eip中存储的仍为内存的物理地址,所以cpu对eip中的地址通过分页机制解析出来将是用户区
 86 * 的,如果不映射到内存开始8M位置,那儿eip解析出来的物理地址就是无效的,后面的指令就无法执行了,而我们需要的是按当前顺序继续执行下去,完成从
 87 * 实模式到保护模式的平稳过渡,内核初始化完成之后会删除用户区的这两个映射
 88 *  ..and set paging (PG) bit
 89 */
 90 movl %eax, %cr0
 91 /* flush the prefetch-queue,这是intel所建议的操作,逻辑上无作用,但可以将流水线上其他指令丢弃,避免乱序执行的影响 */
 92 jmp 1f            
 93 1:
 94 /* 上面的jmp是短跳转,EIP中仍然是物理地址,所以需要手动去完成EIP刷新,下一个1:在编译生成的时候采用的是虚拟地址,所以跳转到这个虚拟地址就
 95 * 真正的完成了EIP的虚拟地址更新,也就完成了实模式到保护模式的过渡
 96 */
 97 movl $1f, %eax
 98 jmp *%eax        /* make sure eip is relocated */
 99 1 :
100 /* Set up the stack pointer */
101 lss stack_start, %esp

此时内存映射关系

 

 

之后linux会根据开机加电后的BIOS探测到的物理内存信息进行内存的初始化,生成一张内存构成图,下一节将会是这些内容。

posted @ 2015-10-30 14:36  幻暝玄冰  阅读(502)  评论(0编辑  收藏  举报