免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12345下一页
最近访问板块 发新帖
查看: 40390 | 回复: 43

【x86 & x64 沉思录】(6.7 更新) [复制链接]

论坛徽章:
0
发表于 2008-12-14 23:36 |显示全部楼层
==== 直接点击条目打开 ====



目 录
--------------------------------------------------------------------------------------------
1、real 模式的思考
2、real 模式下访问 4G 地址空间
3、protected 模式
4、理解 paging
5、x86 vista 下的 paging 实例
6、x64 win7 下的 paging 实例
7、x86 win2008 下的 paging 实例
8、segmentation 情景分析
9、x86 winxp 下的 segmentation 实例
10、x86 下的 TSS 任务切换机制
11、利用 TSS 机制切换到任何权限级别
12、LDT descriptor
13、使用 gate 构建保护模式下的 protected 核心
14、gate 的用法
15、x64 体系下的 segmentation 情形
16、long mode 下的 call gate
17、long mode 下的 TSS 和 task gate
18、long mode 下的 interrupt/trap gate
19、x86 & x64 的保护措施
20、data segment 的访问
21、stack segment 的访问
22、使用哪个 segment registers 进行数据访问
23、canonical-address 地址形式
24、目标 code segment 的访问
25、call/jmp offset 段内调用
26、使用 call/jmp 直接调用/跳转目标 code segment
27、通过 call gate 访问目标 code segment
28、long mode 模式下 system/gate descriptor 的疑惑
29、选择 conforming 还是 non-conforming?
30、使用 TSS selector 进行任务切换
31、通过 task gate 进行 task 切换
32、使用 int n 调用系统例程
33、long mode 下的中断服务例程切换
34、使用 sysenter/sysexit 指令快速切换
35、使用 syscall/sysret 指令快速切换
36、使用 ret/iret 指令进行切换
--------------------------------------------------------------------------------------------


















.

[ 本帖最后由 mik 于 2009-6-7 03:24 编辑 ]

评分

参与人数 1可用积分 +15 收起 理由
prolj + 15 原创内容

查看全部评分

论坛徽章:
0
发表于 2008-12-15 00:49 |显示全部楼层
话题1: real 模式的思考



1.1、real 模式、protected 模式以及 Long 模式的物理资源

清楚认识这点:
  从物理资源方面看:real 模式是 protected 模式的子集,而 protected 模式是 long 模式的子集。所以:real 模式、protected 模式以及 long 模式的物理资源是一样的。


  processor 在 real 模式、protected 模式以及 long 模式物理结构是一致的。所不同只是 processor 的功能集在不同模式下不同。



1.2、real 模式
  大多数系统结构就是初始态,所以限制 real 模式只能访问 1M 地址空间的本质因素是 CS.limit = FFFFh,DS.limit = FFFFh,且 limit 属性在实模式下是不能改变的(但是还是有方法可以改变的)。



1.2.1、 CS.base 改变
  CS.base = 0000_0000_FFFF_0000h,RIP = 0000_0000_0000_FFF0h,但这从执第 1 条指令开始就开始改变了。第 1 条指令地址 FFFF_FFF0 的地方的指令:jmp far ptr F000:E05B。 执行后 processor 自动更新 CS.base = 000F_0000h,这个 base 的计算方法是使用 real 模式的段计算方法 F000 左移 4 位。
  这个 base 改变行式同样适合 DS、ES、SS、FS 及 GS,当加载新的段选择子时自动更新 base 。



1.2.2、 CS.D 及 CS.DPL 属性  
  另外 2 个限制 real 模式行为的属性是 CS.D 及 CS.DPL。
  CS.D = 0 本质上决定 real 模式的 Default Operands-Size 是 16 位,CS.DPL = 00 定义了 processor 的执行权限是 0 级(最高级)。



1.2.3、 IDTR.base  
  IDTR.base = 0,决定了 IVT(中断向量表)的位置在 0 处。原理上这个 IDTR.base 是可以改变,执行 LIDT 指令可以更新 IDTR.base 属性。


1.2.4、 其它系统数据结构
  除了 IDT 外,像 GDT 、LDT 及 TSS 这些系统数据结构是这不可用的。从而将相关 pretected 措施禁闭了。其实只是 IDTR 可用,IDT 数据结构在 real 模式下为 IVT 数据结构。




1.2.5 总结 real 模式的执行环境

CR0.PE = 0,禁止 protected 模式:
(1)CS.limit = FFFFh & DS.limit = FFFFh  :段限 64K。
(2)CS.base = CS.selector << 4  & DS.base = DS.selector << 4 :段基址 20 位,可寻址 1M 空间。
(3)CS.D = 0 & CS.DPL = 0:16 位代码,运行在 0 级。
(4)IDTR.base = 0:IVT 在 0 位置。



1.2.6 A20 作用
  增加了 Bit20 地址线,在 real 模式可访问 1M 以上的扩展空间,理论上可访问:0 ~ 1FFFFF

以下代码:

mov ax, FFFFh
mov ds, ax
mov al, byte ptr ds:[FFFFh]
------------------------------------------------
DS.base = FFFF << 4 = FFFF0h
address:  DS.base + FFFFh = FFFF0 + FFFFh = 10FFEFh


   
 bit20 
         1  0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1
         -

  这个 10FFEFh 在 1M 地址之上,在第 21 条地址线 A20 有效的时候可以访问,若 A20 = 0 无效时,只能访问:0FFEFh。这是一个地址回绕现象。

  开启 A20 时实际可访问空间为:0 ~ 10FFEFh,总共多了 FFF0 字节,即 65520 个字节扩展空间。



1.2.7 A20 Gate
  A20 gate 决定何时开启 A20,以前的 A20 gate 是连接到 keyboard controller(键盘控制器)端口 64h 上,通过对 64h 端口相应的 A20 gate 置位而控制开启 A20。
  现在的 processor 都有一个 A20M#(Address 20 Mask) 信号,仅在实模式下有效。有效时模拟 8086 行为。




.

[ 本帖最后由 mik 于 2008-12-25 21:57 编辑 ]

论坛徽章:
0
发表于 2008-12-24 00:35 |显示全部楼层
话题 2 : 实模式下访问 4G 地址空间


  在之前我的文档中几次提到在实模式下不受 1M 空间限制访问 4G 空间。

实现这个目的仅仅需要一点小“技俩”



2.1 原理

  real mode -> protected mode -> real mode

  在实模式转化为 protected 模式时,设置好该问 4G 空间的 descriptors,加载进相应的的 selector,而转回 real 模式时,却不改变相应的 selector。



2.2 实现

下面是相应的汇编码片断示例:

  cli
  lgdt gdt32
  mov eax, cr0
  or eax, 0x1
  mov cr0, eax                       /* 开启 protected mode */
  jmp code32                        /* jmp to code32 */
code32:
  mov bx, 0x18
  mov ds, bx
  mov ss, bx
  xor eax, 0x01                    
  mov cr0, eax                      /* 回到 real mode */
  jmp code16                  
code16:
  mov eax, 0x11223344
  mov dword ptr [eax], 0x12345678
    /* 关键代码:real 下写 1M 以上空间代码 */
looo:
  jmp looo                           /* 死循环 */



  开启 protected mode 时,将 32 位的 descriptor 加载到 segment registers 形成 protected 模式执行环境,返回 real 模式却没变回 real 模式的执行环境,仍旧是 protected 模式执行环境。


以下是示例中的 descriptors table:
0x00000000
0x00000000          /* gdt0: null descriptor */
0x0000ffff
0x00cf9e00           /* gdt1: 32 bit code descriptor */
0x0000ffff
0x00009e00         /* gdt2: 16 bit code descriptor */
0x0000ffff
0x00cf9300          /* gdt3:  32 bit data descriptor */
0x0000ffff
0x00009300        /*  gdt4: 16 bit data descriptor */


示例中只用了 gdt1 及 gdt3



2.3  实验

00007c00:    fa                                      ; cli
00007c01:    0f 01 16 f0 7c                   ; lgdt [0x7cf0]
00007c06:    66 0f 20 c0                       ; mov eax, cr0
00007c0a:    80 c8 01                           ; or al, 0x01
00007c0d:    66 0f 22 c0                       ; mov cr0, eax
00007c11:    ea 16 7c 00 08                 ; jmp far 0008:7c16
00007c16:    c6 c3 18                           ; mov bl, 0x18
00007c19:    8e db                               ; mov ds, bx
00007c1b:    8e d3                               ; mov ss, bx
00007c1d:    80 f0 01                           ; xor al, 0x01  
00007c20:    66 0f 22 c0                      ; mov cr0,  eax
00007c24:    eb 00                              ; jmp $+00
00007c26:    b8 44 33 22 11               ; mov eax, 0x11223344
00007c2b:    c7 00 78 56 34 12           ; mov dword ptr [eax], 0x12345678
00007c31:    eb fe                               ; jmp .


  上面这段机器码是我手工翻译的,懒得使用 nasm,而对 nasm 反感,所以就亲自动手了
将它复制到 floppy 映像的 boot 块。

bios 会将 floppy bootsect 加载了 0x7c00 处执行......



然后,使用 bochs 用 floppy 启动。

附上完整的 a.img (可启动的软盘映像)



2.4 结果  
  在 bochs 启动 a.img 是死循环这是正常的,因为程序中结果是死循环。
  用 vmware 加载 floppy 却出现故障启动不了。
  没在真实环境试过。

[ 本帖最后由 mik 于 2008-12-24 00:44 编辑 ]

a.img.rar

1.22 KB, 下载次数: 103

论坛徽章:
0
发表于 2008-12-25 23:44 |显示全部楼层
话题 3、 protected 模式


  x86 的 protected 模式是构建在段机制上,实质上 protected 模式应称为 segmentation protected 模式。整个 protected 模式的核心是 Privilege(权限),众多机制都是围绕 privilege 这个核心元素来构建。


3.1、 保护的意图
  在久远的 real 模式下的 dos 平台,还是那种“硬件为软件服务,软件服从硬件”,现今的 processor 与 软件的之间的的关系已经由传统的关系变为互相依靠而又互相影响。
  软件服务硬件的时候,又反过来影响硬件的设计。硬件为软件提供平台的时候,又反过来受软件设计的影响不断完善硬件。在 dos 单任务的系统无所谓保护不保护。在多任务系统出现后,processor 同时运行多个任务时那么就需要为每个任务的正确运行提供一种保障措施。保证每个任务的数据是安全的。 processor 就要提供这么一个保护的借施。
  随着软件日益复杂及庞大。processor 不断完善自身提供更强大的功能。一个典型的例子是:NX (不可执行)页出现从硬件层面上解决了系统软件防止数据区不可执行的功能。以及 processor 的 virtualzation 虚拟化技术的出现,从硬件层提供了支持。


3.2、 保护着什么 ?
  protected 的核心目的保护些什么?这是值得思考的问题,其实就是保护数据。让属于自己权限范围的数据不被他人非法地使用或修改。这些数据是任务的执行代码或任务的堆栈数据等。
  系统软件划分几个层次的数据,将这些数据隔离起来。系统软件核心数据不受用户软件的干扰和破坏,从而增强系统的壮健性,也是主要目的。每个用户软件的数据也可被隔离互不干扰。



3.3、 怎样保护 ?
  前面提过 x86 保护核心是 privilege,x86 processor 定义了 4 个 privilege level(权限级别)从 level0 ~ level3,代表最高权限级别的 0 级到最低权限的 3 级。0 级可访问所有硬件资源,3 级访问有限的硬件资源。

3.3.1、 如何对数据进行隔离 ?
  x86 提供的机制是:对每一个被隔离的数据区进行属性定义,即通过每个数据区进行定义他们的属性,包括它们的位置、长度大小、被访问的权限等属性。定义每个数据区的不同的属性从而将它们互相隔离。
  这种属性的定义是通过 descriptor(描述符)数据结构进行定义的。代码区域对应的是 code segment descriptors,而数据区域则对应 data segment descriptors。
  实际上,descriptor 有两种性质:一种是直接对数据区进行属性定义,如上述的代码段和数据段。这种 descriptor 直接定义了数据区的位置、段长度、段相关的访问权限以及数据段的其他属性。另一种则是描述一种间接访问措施,这种间接访问措旋是通过另一个被称为 gate(门)的数据结构来实现的。


3.3.2、 数据区访问权限的审查
  为每个数据区域定义 descriptor 时,根据需要定义这些数据区域的访问权限。系统软件核心部件总是拥有最高级别的权限,用户软件则总是拥最低级别的权限。
  保证用户级别代码不能访问系统级别代码或数据的通过审查它们的权限级别,经过审查通过后才能访问。一个简单的原则是低权限不能访问属于最高权限级别的数据。level 3 只能访问属于 level 3 级别的数据,level 2 可以访问属于 level 2 级别和属于 level 3 级别的数据。
  

3.3.3、 数据区的访问途径
  所有数据访问都是基址+偏移,这个基址在 descriptor 的定义。对于直接访问方式偏移值是程序逻辑中指出。对于间接访问方式基址和偏移值都是间接数据层中指出。




.

论坛徽章:
0
发表于 2009-01-29 23:52 |显示全部楼层
话题4、理解 paging



物理地址:
linear address  ---->  paging  ----> physical address


SO, 这里主要理解 paging 的来龙去脉。


4.1、地址组成

physical address = page's base address + offset

这个页基址经过几级数据结构(page-translation tables)寻址得,offset 在 linear address 中提供。



4.2、page-translation tables 的基址

CR3 寄存器中存放最高一层 page-translation tables 基址,在 x86 下是 32 位,x64 下被扩展为 64 位



4.2.1、 x86 下的 CR3 结构

(1) 非 PAE 模式下
CR3[31,12]: 对应着 page tables 的基址
CR3[4]:是 PCD(page cache disable)标志位。
CR3[3]:是 PWT(page writethrough)标志位。

其余保留,这意味着高最级的 page tables 是 4Kbyte 边界对齐,bit11 ~ bit0 为 0。

(2) PAE 模式下
CR3[31,5]: 对应着 page tables 的基址
CR3[4]:是 PCD(page cache disable)标志位。
CR3[3]:是 PWT(page writethrough)标志位。

其余保留,这意味着高最级的 page tables 是 32byte 边界对齐,bit4 ~ bit0 为 0。



4.2.2、x64 下的 CR3 结构

(1) Intel 的实现
CR3[39,12]: 对应着 page tables 的基址
CR3[4]:是 PCD(page cache disable)标志位。
CR3[3]:是 PWT(page writethrough)标志位。

其余保留,这意味着高最级的 page tables 是 4Kbyte 边界对齐,bit11 ~ bit0 为 0。
Intel 实现支持最高 40 位物理地址。

(2) AMD 实现
CR3[52,12]: 对应着 page tables 的基址
CR3[4]:是 PCD(page cache disable)标志位。
CR3[3]:是 PWT(page writethrough)标志位。

其余保留,这意味着高最级的 page tables 是 4Kbyte 边界对齐,bit11 ~ bit0 为 0。
AMD 实现最高支持 52 位物理地址。




4.3、 page-translation tables 数据结构

AMD 实现 4 种尺寸的页:4K page、2M page、4M page 以及 1G page
Intel 不支持 1G page,实现 4K page、2M page 以及 4M page

每种大小的页面决定了不同的 page-tables 数据结构



4.3.1、 先说说 4K page

4K 大小的页面,意味着:页地址是 4Kbyte 边界对齐,也即是 bit11 ~ bit0 为 0。

所以:linear address 中的低 12  位是 page 内 offset 值,整个地址空间中可以有 1M (1024*1024) 个 pages。


(1) 在 x86 下 32 位的 linear address 进行 paging 时,其 page-translation tables 经过 2 级寻址,分别寻址 page tabls 以及 pages。

2 个数据结构:PDT(page-directory tables)、PT(page tables)

所以,linear address 分成 3 个部分:

Bit31 ~ Bit22:共10 位,用来寻址 PT (page tables),即:在 PDT 中索引 PDE 值。
Bit21 ~ Bit12:共10 位,用来寻址真正页面 pages,即:在 PT 中索引 PTE 值。
Bit11 ~ Bit0 :共12 位,代表 4Kbyte 页面空间的 offset 值,即:在 page 中具体定位。


(2) 在 x64 下 64 位的 linear address 进行 paging 时,其 page-translation tables 经过 3  级寻址,分别寻址 page-directory pointer tables、page-directory tables、page tables 以及 pages。

4 个数据结构为:PML4T(page map level4 tables)、PDPT(page directory pointer tables)、PDT(page directory tables)以及 PT(page tables)。
实际上,64 位的 linear address 只实现了 48 位 linear address,高 16 位 linear address 是符号扩展位。

所以,48 位的 linear address 分成 5 个部分:

bit47 ~ bit39:共 9 位,用来寻址 PDPT(page directory pointer tables),即在 PML4T 中索引 PML4E 值。
bit38 ~ bit30:共 9 位,用来寻址 PDT(page directory tables),即在 PDPT 中索引 PDPE 值。
bit29 ~ bit21:共 9 位,用来寻址 PT (page tables),即在 PDT 中索引 PDE 值。
bit20 ~ bit12:共 9 位,用来寻址 page,即在 PT 中索引 PTE 值。
bit11 ~ bit0:共 12  位,代表在 4Kbyte 页面空间中 offset 值,即:在 page 中具体定位。




4.3.2、 大页面:4M page

  在 CR4.PSE 置为 1 时,将开启 4M page,但在具体由相应的 page-translation tables 的属性来决定哪个是 4M pages,哪个是 4K pages。
  4M page 意味着,线性地址的低 22 位是 page offset 值,32 位地址空间中有 1K (1024)个 pages。

那么:32 位 linear address 将为分成两个部分:

bit31 ~ bit22:用来直接寻址 PT(page tables),在 PDT 中索引 PDE
bit21 ~ bit0:代表在 4M 页面空间中的 offset 值,即在 page 中具体定位。










术语:
LA        =     linear addess
PA        =     physcial address
PML4T  =     pages map level4 tables
PML4E  =     PML4T's entry
PDPT    =     pages directory pointer tables
PDPE   =     PDPT's entry
PDT     =     pages directory tables
PDE     =     PDT's entry
PT        =     pages tables
PTE      =     PT's entry

PAE      =     physcial address extension
PSE      =     page size extension



一、x86 下的 paging



1、   CR4.PAE = 0   &&   CR4.PSE = 0

其 paging 过程如下:

PDT(CR3)---> PDE(PT)---> PTE(pages)---> physical address



如以下 linear address 0x81b3a6de:

1000000110      1100111010      011011011110           =           0x81b3a6de (linear address)
-----------
(PDE index)        (PTE index)          (offset)







2、   CR4.PAE = 0   && CR4.PSE = 1

其 paging 过程如下:

PDT(CR3)---> PDE(PT)---------------> PTE(pages)---> PA(page + offset)
                          |     PDE.PS = 0 (4K pages)
                          |
                         +--------------------------------------> PA(page + offset)
                         PDE.PS = 1(4M pages)





线性地址会被分割 4 个组成部分

1000000110      1100111010      011011011110         =     0x81b3a6de (linear address)
------------
PDE index          (PTE index)         (offset)
    |
    |
    |
    +-----------> PDE.PS = 1 时,跳过 PT,直接获取 page
                       PDE.PS = 0 时,接下来的10位,用来在 PT 中索引 PTE。


这里情况稍有些复杂 PDE.PS 是大小页的开关(4M page / 4K pages)

(1) 4M page:有 22 位是 offset,高 10 是 PDE index (即:4M 的 offset 空间)

(2) 4K page:有 12 位是 offset(即:4K 的 offset 空间)






3、  CR4.PAE = 1 (CR4.PSE 忽略)

PDPT(CR3)---> PDPE(PDT)---> PDE(PT)-------> PTE(pages)------> PA(page + offset)
                                         |        PDE.PS = 0(4K pages)
                                          |
                                         +---------------------------------------> PA(page + offset)
                      PDE.PS = 1(2M pages)




10     000001101        100111010        011011011110         =     0x81b3a6de (linear address)
--      (PDE index)        (PTE index)     (offset)
|            |
|            |
|            +---------> PDE.PS = 1 时:跳过 PT,直接获取 page
|                             PDE.PS = 0 时:接下来 9 位,用来在 PT 中索引 PTE
PDPE index


在开启 PAE 时,情况更复杂一些 PDE.PS 这个开关用来选择(2M page / 4K page)

(1) 2M page: 有 21 位的 offset(即:2M 空间的 offset )
(2) 4K page: 有 12 位的 offset (即:4K 空间的 offset)







二、  x64  下的 paging(PAE = 1)


PML4T(CR3)
  |
  +---> PML4E(PDPT)---> PDPE(PDT)---> PDE(PT)-------> PTE(pages)------> PA(page + offset)
                                          |                        |    PDE.PS = 0 (4k pages)
                                          |                        |
                                          |                        +------------------------------------> PA
                                          |                              PDE.PS = 1 (2M pages)
                                          |
                                          +------------------------------------------------------> PA
                                                                      PDPE.PS = 1 (1G pages)







x64 下的 paging 是更复杂的。在 AMD 实现 1G page 时达到最复杂状态


线性地址 0xfffff80002bc7c36 如下:

11111111 11111111      111110000      000000000     000010101     111000111      1100 00110110
-------------------      (PML4 index)    (PDPE index)   (PDE index)     (PTE index)           ( offset )
(未实现)
                                                             |                      |
                                                             |                      |
                                                             |                     +---------> PDE.PS 进一步决定 4K / 2M page
                                                             |
                                                             |
                                                             +--------> PDPE.PS = 1 开启 1G 页,直接获取 page
                                                                             PDPE.PS = 0 由 PDE.PS 进一步决定 4K / 2M page


x64 只实现了 48 位的虚拟地址,因此高 16 位是符号扩展。但是:这样子在以后也很容易进行扩展。
实现全 64 位的虚拟地址是很容易的。

AMD 的处理器上可以有 3 种大小的 page 共存:4K page / 2M page / 1G page
Intel 的处理器上未实现 1G page,只能有 4K page / 2M page

每一项 index 是 9 位

1G page: 有 30 位 offset 值,也就是 page 内有 1G 的 offset 空间


理解了 4K / 2M / 4M / 1G page 的含义,paging 也就不难了

:wink:

[ 本帖最后由 mik 于 2009-1-31 18:10 编辑 ]

论坛徽章:
0
发表于 2009-01-30 01:07 |显示全部楼层
(接上)


看一个实际的例子:

kd> u
nt!InitBootProcessor+0x3df:
81b3a6de fec8            dec     al
81b3a6e0 f6d8            neg     al
81b3a6e2 bfe0df8f81      mov     edi,offset nt!ExpBootEnvironmentInformation (818fdfe0)
81b3a6e7 1bc0            sbb     eax,eax
81b3a6e9 40              inc     eax
81b3a6ea 40              inc     eax
81b3a6eb a3f0df8f81      mov     dword ptr [nt!ExpBootEnvironmentInformation+0x10 (818fdff0)],eax
81b3a6f0 8b7358          mov     esi,dword ptr [ebx+58h]


上面是 windbg 调试 vista 内核中摘录的一段代码。

现在看一看 81b3a6de 这个 linear address 对应的物理地址是什么?

kd> r cr3
cr3=00122000

kd> r cr4
cr4=00000020

CR4.PAE = 1、CR4.PSE = 0
PAE 开启,是 2M page 还是 4K page,由 PDE 来决定


由 CR3 寄存器得出:PDPT 的基址是 0x00122000


kd> .formats 0x81b3a6de
Evaluate expression:
  Hex:     81b3a6de
  Decimal: -2118932770
  Octal:   20154723336
  Binary:  10000001 10110011 10100110 11011110

kd> !dq 0x122000+0x2*8
#  122010 00000000`00125001 00000000`00126001
#  122020 00000000`00000000 00000000`00000000
#  122030 00000000`00000000 00000000`00000000


PDT 的基地址是 0x125000,未使用 2M page,它是 4K pages

kd> !dq 0x125000+0xd*8
#  125068 00000000`00141063 00000000`00142063
#  125078 00000000`00143063 00000000`00144063
#  125088 00000000`00145063 00000000`03400963
#  125098 00000000`03401963 00000000`03402963
#  1250a8 00000000`03403963 00000000`03404963


PT 的基址是 0x00141000

kd> !dq 0x141000+0x13a*8
#  1419d0 00000000`01b3a163 00000000`01b3b163
#  1419e0 00000000`01b3c163 00000000`01b3d163
#  1419f0 00000000`01b3e163 00000000`01b3f163
#  141a00 00000000`01b40163 00000000`01b41163


page 的基址是 0x01b3a000,所以,最终 physical address 是:0x01b3a6de

kd> !db 0x01b3a6de
# 1b3a6de fe c8 f6 d8 bf e0 df 8f-81 1b c0 40 40 a3 f0 df ...........@@...
# 1b3a6ee 8f 81 8b 73 58 83 c6 6c-a5 a5 a5 33 c0 40 a5 e8 ...sX..l...3.@..
# 1b3a6fe dd 7f cc ff 8b 43 58 83-38 7c 72 06 53 e8 b5 41 .....CX.8|r.S..A
# 1b3a70e ff ff 53 e8 ca 3e ff ff-a1 a8 15 93 81 a3 b4 02 ..S..>..........

kd> db 0x81b3a6de
81b3a6de  fe c8 f6 d8 bf e0 df 8f-81 1b c0 40 40 a3 f0 df  ...........@@...
81b3a6ee  8f 81 8b 73 58 83 c6 6c-a5 a5 a5 33 c0 40 a5 e8  ...sX..l...3.@..
81b3a6fe  dd 7f cc ff 8b 43 58 83-38 7c 72 06 53 e8 b5 41  .....CX.8|r.S..A
81b3a70e  ff ff 53 e8 ca 3e ff ff-a1 a8 15 93 81 a3 b4 02  ..S..>..........


使用 0x01b3a6de 这个物理地址查看内容,和使用 0x81b3a6de 这个线性地址查看内容是完全一样的。


使用 windbg 的这种方式有助于理解 paging 过程

:wink:

论坛徽章:
0
发表于 2009-01-31 15:02 |显示全部楼层
(接上)

再看一看 win7 x64 体系中的 paging 实例:

从 windbg 中摘录一段 win7 的 kernel 代码:
kd> u
nt!KeSetProfileIrql+0x1306:
fffff800`02bc7c36 33d2            xor     edx,edx
fffff800`02bc7c38 2401            and     al,1
fffff800`02bc7c3a 3c01            cmp     al,1
fffff800`02bc7c3c 0f94c2          sete    dl
fffff800`02bc7c3f ffc2            inc     edx
fffff800`02bc7c41 8915297ecaff    mov     dword ptr [nt!ExWindowStationObjectType+0x270 (fffff800`0286fa70)],edx
fffff800`02bc7c47 488b81b8000000  mov     rax,qword ptr [rcx+0B8h]
fffff800`02bc7c4e f30f6f80a0000000 movdqu  xmm0,xmmword ptr [rax+0A0h]


观察一下 0xfffff800_02bc7c36 这个 linear address 的 physical address 是多少。


kd> r cr4
cr4=00000000000006b8
kd> r cr3
cr3=0000000000187000


CR4.PAE 及 CR4.PSE 都开启了,在 x64 下必须开启 PAE 。

1、PML4T 的基址是 0x187000

kd> .formats 0xfffff80002bc7c36
Evaluate expression:
  Hex:     fffff800`02bc7c36
  Decimal: -8796047115210
  Octal:   1777777600000257076066
  Binary:  11111111 11111111 11111000 00000000 00000010 10111100 01111100 00110110


kd> !dq 0x187000+0x1f0*8
#  187f80 00000000`00199063 00000000`74604863
#  187f90 00000000`00000000 00000000`00000000
#  187fa0 00000000`00000000 00000000`03c00863
#  187fb0 00000000`00000000 00000000`00000000
#  187fc0 00000000`00000000 00000000`00000000


2、获取 PML4E = 0x187000+0x1f0*8 = [0x187f80] = 0x199063
  而 PDPT = 0x199000


kd> !dq 0x199000
#  199000 00000000`00198063 00000000`00000000
#  199010 00000000`00000000 00000000`00000000
#  199020 00000000`00000000 00000000`00000000
#  199030 00000000`00000000 00000000`00000000
#  199040 00000000`00000000 00000000`00000000


3、得出 PDPE = [0x199000] = 0x198063     PDPE.PS = 0,1G 页未开启。
  而 PDT = 0x198000

kd> !dq 0x198000 + 0x15*8
#  1980a8 00000000`001db063 00000000`001dc063
#  1980b8 00000000`00000000 00000000`001dd063
#  1980c8 00000000`001de063 00000000`001df063
#  1980d8 00000000`001e0063 00000000`001e1063
#  1980e8 00000000`001e2063 00000000`00000000


4、得出 PDE = [0x1980a8] = 0x1db063 PDE.PS = 0,即:2M 页未开启。它是个 4K 页 paging
    而 PT = 0x1db000

kd> !dq 0x1db000 + 0x1c7*8
#  1dbe38 00000000`02bc7163 00000000`02bc8163
#  1dbe48 00000000`02bc9163 00000000`02bca163
#  1dbe58 00000000`02bcb163 00000000`02bcc163
#  1dbe68 00000000`02bcd163 00000000`02bce163
#  1dbe78 00000000`02bcf163 00000000`02bd0163


5、得出 PTE = [0x1dbe38] = 0x2bc7163
     得出最终的 page‘s base address = 0x2bc7000



因此,最终 0xfffff800_02bc7c36(linear address)= 0x00000000_02bc7c36(physical address)

kd> !db 0x0000000002bc7c36
# 2bc7c36 33 d2 24 01 3c 01 0f 94-c2 ff c2 89 15 29 7e ca 3.$.<........)~.
# 2bc7c46 ff 48 8b 81 b8 00 00 00-f3 0f 6f 80 a0 00 00 00 .H........o.....
# 2bc7c56 f3 0f 7f 05 02 7e ca ff-c3 90 90 90 90 90 90 90 .....~..........
# 2bc7c66 90 90 90 90 90 90 90 90-90 90 48 83 ec 38 83 39 ..........H..8.9
# 2bc7c76 06 75 25 83 79 04 01 75-1f 81 79 08 f0 00 00 00 .u%.y..u..y.....
# 2bc7c86 75 16 48 8b 81 b8 00 00-00 8b 10 81 fa 40 01 00 u.H..........@..
# 2bc7c96 00 75 07 48 83 c4 38 c3-33 d2 44 8b 49 08 44 8b .u.H..8.3.D.I.D.
# 2bc7ca6 41 04 8b c2 8b 11 b9 00-01 00 00 48 89 44 24 20 A..........H.D$
kd> db 0xfffff80002bc7c36
fffff800`02bc7c36  33 d2 24 01 3c 01 0f 94-c2 ff c2 89 15 29 7e ca  3.$.<........)~.
fffff800`02bc7c46  ff 48 8b 81 b8 00 00 00-f3 0f 6f 80 a0 00 00 00  .H........o.....
fffff800`02bc7c56  f3 0f 7f 05 02 7e ca ff-c3 90 90 90 90 90 90 90  .....~..........
fffff800`02bc7c66  90 90 90 90 90 90 90 90-90 90 48 83 ec 38 83 39  ..........H..8.9
fffff800`02bc7c76  06 75 25 83 79 04 01 75-1f 81 79 08 f0 00 00 00  .u%.y..u..y.....
fffff800`02bc7c86  75 16 48 8b 81 b8 00 00-00 8b 10 81 fa 40 01 00  u.H..........@..
fffff800`02bc7c96  00 75 07 48 83 c4 38 c3-33 d2 44 8b 49 08 44 8b  .u.H..8.3.D.I.D.
fffff800`02bc7ca6  41 04 8b c2 8b 11 b9 00-01 00 00 48 89 44 24 20  A..........H.D$


用线性地址 0xfffff800_02bc7c36 查看的内容
用物理地址 0x00000000_02bc7c36 查看内容
是完全一样的。



x64 体系的 paging:

PML4T(CR3)---> PML4E(PDPT)---> PDPE(PDT)---> PDE(PT)---> PTE(page)---> physical address

论坛徽章:
0
发表于 2009-01-31 23:14 |显示全部楼层
最后看一个 windows server 2008 版本上的 paging

kd> u
nt!KeUpdateSystemTime+0xed:
816d502d fa              cli
816d502e 648b0d1c000000  mov     ecx,dword ptr fs:[1Ch]
816d5035 fe8931010000    dec     byte ptr [ecx+131h]
816d503b 7526            jne     nt!KeUpdateSystemTime+0x123 (816d5063)


kd> r cr4
cr4=000006b9
kd> r cr3
cr3=3f083080


开启了 PAE 后, PDPT 基地址是 0x3f083080


kd> !dq 0x3f083080+0x2*8
#3f083090 00000000`375ad801 00000000`375be801
#3f0830a0 00000000`3878b801 00000000`3878c801
#3f0830b0 00000000`387b5801 00000000`387c6801


PDPE = 0x375ad801, PDT 的基地址是 0x375ad000


kd> !dq 0x375ad000 + 0x0b*8
#375ad058 00000000`016009e3 00000000`018009e3
#375ad068 00000000`00142063 00000000`00143063
#375ad078 00000000`00144063 00000000`00145063


PDE = 0x16009e3,这里开启了 2M page, 所以直接得出 page 的地址是:0x1600000

因此,物理地址是:0x016d502d

kd> !db 0x1600000 + 0xd502d
# 16d502d fa 64 8b 0d 1c 00 00 00-fe 89 31 01 00 00 75 26 .d........1...u&
# 16d503d 0f 31 2b 81 d8 1b 00 00-1b 91 dc 1b 00 00 01 81 .1+.............
# 16d504d e0 1b 00 00 11 91 e4 1b-00 00 01 81 d8 1b 00 00 ................
# 16d505d 11 91 dc 1b 00 00 ff 15-98 e0 61 81 e9 fa 03 fa ..........a.....
# 16d506d ff 8b ff 58 5b 6a 00 53-50 eb 00 8b 6c 24 04 64 ...X[j.SP...l$.d
# 16d507d ff 05 c4 06 00 00 f7 05-c4 cc 71 81 02 00 00 00 ..........q.....
# 16d508d 74 10 8b 4c 24 08 8b 55-68 e8 90 bf 02 00 8b 6c t..L$..Uh......l
# 16d509d 24 04 81 7d 68 dc 85 67-81 72 10 81 7d 68 eb 85 $..}h..g.r..}h..
kd> db 0x816d502d
816d502d  fa 64 8b 0d 1c 00 00 00-fe 89 31 01 00 00 75 26  .d........1...u&
816d503d  0f 31 2b 81 d8 1b 00 00-1b 91 dc 1b 00 00 01 81  .1+.............
816d504d  e0 1b 00 00 11 91 e4 1b-00 00 01 81 d8 1b 00 00  ................
816d505d  11 91 dc 1b 00 00 ff 15-98 e0 61 81 e9 fa 03 fa  ..........a.....
816d506d  ff 8b ff 58 5b 6a 00 53-50 eb 00 8b 6c 24 04 64  ...X[j.SP...l$.d
816d507d  ff 05 c4 06 00 00 f7 05-c4 cc 71 81 02 00 00 00  ..........q.....
816d508d  74 10 8b 4c 24 08 8b 55-68 e8 90 bf 02 00 8b 6c  t..L$..Uh......l
816d509d  24 04 81 7d 68 dc 85 67-81 72 10 81 7d 68 eb 85  $..}h..g.r..}h..



可见,在 windows 服务器版本上才使用大页 2M page

论坛徽章:
0
发表于 2009-02-02 23:32 |显示全部楼层
话题 5、 segmentation 情景分析



  用户程序 A 君,是某酒店公寓的新住户,该公寓有若干层,每层有若干寓所,还包括公用的日用百货仓库间,会所等公共设施,是一座自助式的电子化的公寓大厦。
  这一天是用户程序 A 君,新入住公寓的日子,管理员 OS 交给了 A 君自己寓所的电子门匙,A 君要凭这条电子门匙在公寓的管理系统里找到自己的寓所。




5.1、寻找 room 的 key:segment registers 和 selectors

----------------------------------------------------------------------------------------
  OS 载入用户程序 A,A 从 OS 里接过控制权,A 找到自己的执行代码然后跳转到自己的代码执行,OS 通过 call 跳转到用户程序 A 执行。
----------------------------------------------------------------------------------------


(1)用户程序 A 君从管理员 OS 接过属于自己的 key,A 君用这把电子门匙在公寓管理系统的检索模块进行验证,管理系统正在验证匹配中... ...
  电子门匙内部是一块芯片,芯片存储着这条 key 对应的寓所的相关信息。管理系统从这条电子门匙中读取信息... ...





5.1.1、开启 room 的 key:  segment registers(CS、DS、SS、ES、FS & GS)

  segment registers 事实上就是这把电子门匙,不过 segment registers 仅仅是个载体,即:segment registers 是存储相关的 descriptors 信息的载体,光有载体而失去里面的 descriptors 信息,这把门匙等于是没用。

  segment registers 是个 16 位宽的 registers,segment registers 里装载的是 selector 以及相应的 descriptors 信息。x86 体系使用分段式的内存管理机制,将线性的内存区域分成若干个区域,这个区域的起始地址,区域的大小及相关的属性信息将用一个 descriptors(描述符)的结构存储起来。



5.1.2、 key 里的索引信息: selectors

  前面已经提过,光有 segment registers 这个载体是不够的,这个载体首先要存储索引信息,这就叫做 selector。通过 selectors 索引子到出相关的描述段结构的信息 descriptors (描述符)



看一看 selector 的结构:

16 位宽的 selector 分成三个域:

0000000000000  X   XX
---------------  -   ---
       |              |     |
       |              |     +------------>  RPL (Requestor Privilege Level)
       |              |
       |              +------------------> TI ( Table Index)
       |
       +-----------------------------> SI (Selector Index)


RPL (Requestor Privilege Level):实际上 RPL 的作用是构建一个集线器部件。
TI ( Table Index):指引在 GDT 表还是在 LDT 中寻找 descriptors,0 ---  GDT    1 --- LDT
SI (Selector Index):这是真正的索引子,这个域形成一个数值,在相应的 descriptor table 中接这个数值寻找。


情景提示:
  x86 的权限级别分为 0 ~ 3 级,共 4  个权限级别。对于 segment 级保护措施来说,0 级为最高权限级别,依次是1、2 级,最低是 3 级。
  对于 page 级的保护措施来说,只有 2 个级别:0 ~ 2 属于 supervisor 级别,3 级则属于 user 级。
 


用户程序通过使用不同的 RPL 来访问不同权限的代码段:
   
                              +--------->  selector A(0x11) -----+
                              |                                                   |
                              |                                                   |
user A ------------>  +---------> selector B (0x10) -----|  ----+
                              |                                                    |        |
                              |                                                    |        |
                              +----------> selector C (0x13) -----+        |
                                                                                             |
                                                                                             |
                                                                                             |
                                                         +------------------------+
                                                         |   
                                                         |
                                                         |
                                                         V
           --------------------------------------------------
                  code 3
           --------------------------------------------------
                  code 2
           --------------------------------------------------
                  code 1
           --------------------------------------------------
                  code 0



selector A 是 0x11,表示: RPL = 1,TI = 0,SI = 2
selector B 是 0x10,表示: RPL = 0,TI = 0,SI = 2
selector C 是 0x13,表示: RPL = 3,TI = 0,SI = 2
code 3 ~ code 0 分别是运行在权限级别为 3 ~ 0 的代码


这 3  个 selector 都是在 GDT 表上索引第 2 个 descriptor,所不同的是各自使用了不同的请求权限。

若当前 CPL = 2: 
(1) 用户代码 A 可以使用 selector A 和 selector B 通过 call/jmp 来访问 code 2 ,效果是完全一样的。
(2) 用户代码 A 可使用 selector A、selector B 以及 selector C 通过 3 级的 gate 来访问 code 0 与 code 1 代码。
(3) 用户代码不能使用这 3 个 selector 来达到访问 code 3 代码。

使用同样的 descriptor index 但使用不同 RPL 的 selector 达到访问不同权限级别的代码。



特别的 selector: NULL selector

  当 TI = 0,意味着将 GDT 寻找到 descriptor,当 RPL = 0 以及 SI = 0 时,即 selector 等于 0 时,这是一个特别的 selector,被称为 NULL selector。
  

NULL Selector:
  在 GDT 表的第一个 entry,即索引为 0 的第 1 个 descriptor,这个 descriptor 是个无效的不可用的 descriptor,设计这样一个 selector 的目的,是防止 0 值被加载到 CS 和 SS。
  当一个 0 值被加载到 CS 和 SS 时,将会产生 #GP 异常。


  实际上,当 SI = 0、TI = 0 时,但 RPL 不等于 0,即 0x00 ~ 0x03 的 descriptors 也是 NULL selectors ,这样的 Null Selector 被加载到 CS 和 SS 同样会产生 #GP 异常。






5.1.3、 descriptor 的载体: segment register

  前面已经提到:segment registers 相当于 key,而这把 key 内信息才是最重要的,才是最终的目的。



segment register 的结构:

  segment register 在物理上分为 2 部分:用户可见的 selector 部分和用户不可见的隐藏部分,用户可见的 selector 为 16 位宽,这个 selector 就是被加载进 segment register 的 selector。
  隐藏部分仅对 processor 可见,这部分存储着被加载的 descriptor 信息,包括:base、limit 以及 attribute 部分。


  base 是 64 位宽,但在 x86 下 base 高 32 位不可用,意即为 32 位宽。limit 固定为 32 位宽,在 x64 下 limit 是无效的, limit 仅在是可用的,所以 limit 固定为 32 位。
  接下来,attribute 推测为 16 位宽,Intel 和 AMD 都没明显说明 attribute 是多少。


descriptor 的载体:

  当 selector 加载进 segment registers,processor 根据 Selector.TI 在 GDT 或者 LDT 搜索对应的 Descriptor,将 Descriptor 的相应部分加载到 segment register 的隐藏部分。



以 CS 为例:
  当 selector 0x13 被 load 到 CS,Selector.TI = 0、Selector.SI = 2,那么 processor 在 GDT 中寻找第 2 个 descriptor,当通过了权限检查后,这个 descriptor 的 base、limit 及 attribute 被相应加载到 CS.base、CS.limit 及 CS.attribute 域。CS.selector 保持不变。

情景提示:
  在实模式下,相关的保护模式的数据结构不可用,descriptors、GDT 等数据结构是不存在的。当 selector 0x13 被 load 到 CS 时,processor 作如下处理:
  CS.base = selector << 4,selector 左移 4 位加载到 base 域,即 base 变成了 0x00000130,Selector.RPL 及 Selector.TI 是不可用的。


这就是实模式分段管理模式的典型内存寻址:段寄存器左移 4 位 + offset。


有一点值得注意的是:
  descriptor 里描述的 limit 只有 20 位宽,在加载到 segment register 里时,limit 被扩展到 32 位宽后再加载到 segment register 里。
  具体的扩展措施是:descriptor 的 G 标志位代表粒度,G = 1 表示 limit 域的粒度为 4K (4096 bytes),G = 0 表示 limit 域的粒度为 1 byte。因此 G = 1 时:32 位 limit = 4096 * limit + 0xFFF,G = 0 时:32 位 limit = 1 * limit。





5.2、 descriptors 是怎样描述 segment 信息的?

  descriptors 描述符,顾名思义就是描述 segment 相关信息的数据结构,定义了 segment 的基址,长度、类型及相关属性。不同类型的 descriptors 有不同的意义。
  总体上有 2 种类型的 descriptors:user 描述符和 system 描述符。由 descriptors 结构的 S 标志位指出。user 描述符就是指基本的 segment descriptors,system 描述符包括有 LDT descripts、TSS descriptors 和各种 Gate descriptors。


5.2.1、理解 descriptors 结构是很容易的,基本上 descriptors 结构描述的无非就是以下几个信息:

(1) segment 的基址,即 base
(2) segment 的长度,即 limit
(3) segment 的类型,即 S(System 标志位)。
(4) segment 的具体类型,在确定 descriptors 是 System 还是 user 后,还要定义具体的类型,如:code segment / date segment、LDT / TSS 还是 Gate(什么类型的 gate)。
(5) segment 的属性,接下来就是 descriptors 相关的属性,如:G(粒度标志位)、D/B(缺省 operand)、L(long mode)等
(6) 最后就是访问该 segment 的权限,即 DPL(Descriptor Privlilege-Level)



下面是 x86 下的 segment descriptor 结构:

例 5.2.1.1

struct x86_descriptor_struct {

/**** descriptor 的前 4 字节 ****/
        int limit_lo : 16;                 /* low 2 bytes of segment limit */
        int base_lo : 16;           /* low 2 bytes of segment base */

/**** descriptor 的后 4 字节 ****/
        int base_3rd : 8;          /* 3rd byte of segment base */
        int type : 4 ;                 /* descriptor type */
        int S : 1;                       /* System flag bit */
        int DPL : 3;                   /* Descriptor Privilege Level */
        int P : 1;                       /* persent bit */
        int AVL : 1;                   /* avallable bit */
        int limit_hi : 4;              /* hight one byte of segment limit */
        int L : 1;                     /* x86: reserved bit,  x64 : L bit : long bit */
        int D : 1;                       /* Default bit */
        int G : 1;                       /* Granularlty bit */
        int base_hi : 8              /* hight one byte of segment base */
};







5.2.2、descriptor 结构的 size

这个也很好理解:
(1) base 是 32 位
(2) limit 是 20 位(前面已经说过 descriptors 结构里的 limit 域是 20 位)
(3) S 标志位占 1 位 加上相关的属性及类型

  相信已经道结果了:x86 下的 descriptors 结构是 8 个字节,即 64 位,因为 base 已经是 4 个字节了,加上 limit  及其它域就是  8 个字节。这样 descriptors 结构是 8 bytes 边界对齐的。


那么,在 x64 的 long mode 下情况稍有些复杂:

(1)由于 long mode 下采用了单一内存管理模式,有效地屏蔽了 segmentation 分段机制的内存管理模式,base、limit 及相关的属性都是无效的,因此,在 long mode 下,user 描述符,即 segment descriptors 结构依旧是 8 bytes 长。

(2)对于 System 描述符,由于 base 需要指出具体值,因此,long mode 下,System 描述符是 16 bytes 长的。典型的如:Call Gate descriptors 结构,需要指出具体 64 位的 offset 值 及 16 位的 selector 值,因此它是 16 bytes 边界对齐的。




5.2.3、 segment 的 base
  x86 下的 descriptor 描述 segments base 是 4 bytes 32 位地址。前面提到过 x86 下 descriptors 结构是 8 bytes 64 位结构。
  由例 5.2.1 可知,4 bytes 的 segment base 分布在 descriptor 的 8 bytes 空间里的第 3 字节 ~ 第 5 字节以及第 8 个字节,两部分组成 4 个字节。

  在 x64 long 模式下的 segment descriptor 它的 base 是无效的,因此,它不描述的 segment base。




5.2.4、 segment 的 limit
  同样,segment limit 仅在 x86 & x64 的 32 位环境中才有效,在 long mode 是无效的。前面已经提到:limit 在 descriptor 结构是 20 bit,它如何描述 segment 的 32 位长度呢?

(1)G =1: limit = 4096 * limit + 0xFFF
(2)G = 0: limit = 1 * limit

  最终,形成的 32 位的 limit 会被加载到 segment register 的 limit 域里。



5.2.4.1、processor 是怎样检查 segment 的 limit

  对于 CS 来说,它描述的 segment 是 Expand-Up 类型的。那么,对于 Expand-Up 类型的 segment 来说,检查 limit 是否超限相对简单。

情景提示:
  当 G = 0 时:segment 的范围是:base ~ base + limit
  当 G = 1 时:segment 的范围是:base ~ base + limit * 4096 + 0xFFF


这里有 2 个比较特殊的情况:
(1)、当 G = 0 时,若 limit = 0,那么 segment 的宽度是 1 byte,segment 范围是:base ~ base + 1
(2)、当 G = 1 时,若 limit = 0,那么 segment 的宽度是 0xFFF, segment 范围是:base ~ base + 0xFFF

  对于描述 Expand-Up 类型的 segment descriptor,它描述的 segment 的宽度是在粒度内,因此:当 G = 0 时,segment 的宽度 1 byte,而 G =1,segment 宽度是 4095,也就是 0xFFF。



5.2.4.2、 下面解释一下为什么 G = 1 时要加上 0xFFF 这个值

  以 descriptor 20 位的 limit 饱和时:limit = FFFFF 为例,若 G = 1,那么此时,它是描述饱和的 32 位空间,即 4G 空间。

看一看:
  limit = 4096 * limit = 1000h * limit,也就是 limit << 12  结果是:FFFFF000,此时的还不足描述 4G 空间,因此必须加上 0xFFF 这个值,类似补码的特征。
  此时:FFFFF000 + FFF = FFFFFFFF 这就能表示完整的 4G 空间。






5.3、 descriptors 存放的地方:descriptor table

  没错!若干个 descriptors 存放在一个连续的空间,形成数组形式的结构,这个数组就被称为 descriptor table。以前面例 5.2.1.1 定义的 descriptor 结构来说,那么,descritptor table 结构就是这样的:

descriptor table 结构:

struct x86_descriptor_struct  dtable[n];      /* descriptor table 结构*/


上面定义了 descriptor table 结构。



5.3.1、 descriptor table 的类型

  有三种类型的的 descriptor table:GDT(Global Descriptor Table)、LDT(Local Descriptor Table)以及 IDT(Interrupt Descriptor Table)。这三种 descriptor table 能容纳的 descriptors 类型是不同的。
  GDT 容纳所有类型的 descriptors,包括:segment descriptor(code segment & data segment)、system descriptors(LDT descriptor、TSS descritpor、所有的 gate descriptor)。
  LDT 容纳 segment descriptor 以及 gate descriptor。
  IDT 仅容纳 interrupt gate descriptor、trap gate descriptor 和 task gate descriptor,但不包含 call gate descriptor。




5.3.2、 descriptor table 的容量

  descriptor table 的 base 存储在相应的 descriptor table register 中:

(1) GDT base 存放在 GDTR 中
(2) LDT base 存放在 LDTR 中
(3) IDT base 存放在 IDTR 中

  同时,descriptor table register 还存储着 descriptor table 的 limit 值。descriptor table 的 limit 值是 16 位,即值为:FFFFh。

情景提示:
  (1)selector 中有 13 位是表示 index 值,1111111111111b = 1FFFh = 8191,也就是说:index 的范围是 0 ~ 8191,共可以寻址 8192 个 descriptors。

  (2)另一个角度来看:descriptor table register 中的 limit 是 16 位,值为 FFFFh,这是一个字节值。在 x86 下 descriptor 的 size 是 8 bytes,那么它容纳的 descriptors 数是: FFFFh / 8 = 1FFFh = 8191,也就是 FFFFh >> 3 = 1FFFh 刚刚好是:8191,这和 selector 中的最大可容纳的 index 值是一样的。

  (3)在 x64 的 long 模式下,system descriptor 和 gate descriptor 是 16 bytes。那么,它能容纳的 descriptors 数是:FFFFh / 16 = FFFF >> 4 = FFFh = 4095,selector 仅能索引到 4095,若是 selector 的 index 为 4096 时将产生越界,引发 #GP 异常。



  以 GDT 为例,加载 base 到 GDTR 的指令是 LGDT,指令形式是 LGDT dword ptr [dtr]。这个 dtr 地址处存放着 descriptor table 的 base 和 limit 值。低 16 位是 limit,高 32 位是 base 值。








[ 本帖最后由 mik 于 2009-2-9 23:25 编辑 ]

论坛徽章:
0
发表于 2009-02-10 01:05 |显示全部楼层
(2)... ... 管理系统从 key 中读取出信息,用户程序 A 君使用的 key 是 0x1b 号,管理系统需要在数据库里检索房号为 0x1b 的 room 的具体位置。 管理系统正在检索中... ...

-------------------------------------------------------------------------------

  GDTR 与 GDT 构建成公寓的数据库系统,descriptors 是数据库里的数据信息。管理系统就是从 GDTR 与 GDT 里检索 seleoctor 为 0x1b 的 descriptor 数据信息。


  在用户程序中使用了指令 call 0x1b:0x00401000 进行接收控制权,执行自己的任务... ...



5.4、 查找 descriptor

  selector 0x1b 的结构是:

0000000000011  0  11
---------------  -  --
           |          |   |
           |          |   |
           |          |   +-------> DPL = 3
           |          |
           |          +----------> TI = 0
           |
           +------------------> SI = 3

  使用这个 selector 的结果是:以 DPL = 3 的权限,在 GDT 中寻找编号为 3 的 descriptor,

gdtr.base = 0x800f3000   gdtr.limit = 0x3ff

这是一段截取 xp 启动时瞬间情况。




5.4.1、 查找 descriptor 的规则。

下面是 C 代码表示:


1、获取 descriptor table base

/*
*  假如 selector 的 TI 为 0 时,在 gdt 中索引。
*  假始 selector 的 TI 为 1 时,则在 LDT 中索引。
*/

if (selector.TI  == 0)
        base = gdtr.base;
else
        base = ldtr.base;



2、索引 descriptor 地址

/*
*  根据 selector 的 SI (索引值)在 base 索引。
*  索引因子是 sizeof(descriptor),x86/x64 的 segment descriptor 是 8 个字节的
*  所以,等于乘上 8
*/

descriptor = base + selector.SI * sizeof(descriptor);      /* base + SI * 8  */


从更高级的 C 语言层面来描述是:

struct x86_descriptor_struct      descriptor_table[gdtr.limit];     /* 定义 descriptor table 结构 */

descriptor = descriptor_table[selector.SI];                               /* 获取 descriptor 结构 */





5.4.2、 加载 descriptor

  当获得相应的 descriptor 后,processor 会将 descirptor 信息加载到相应的 segment registers 中。


以下是默认的 descriptor 加载到相应的 segment registers 列表。

code segment descriptor     ----->  cs
data segment descriptor     ----->  ds
data segment descriptor     ----->  ss
data segment descriptor     ----->  ss
data segment descriptor     ----->  fs
data segment descriptor     ----->  gs

  前面已经提过:segment registers 分部两部分,一个是可见的 selector 部分,另一个是仅对 processor 可见的隐藏部分。因此 descriptor 被找到之后,通过仅限及相关的检查之后,会被 processor 加载到 segment registers 的隐藏部分。
 

正如所料:
1、descriptor 的 selector 是 segment register 的 selector 域。
2、descriptor 的 base 被加载到 segment register 的 base 域。
3、descriptor 的 limit 被扩屋到 32 位后,加载到 segment register 的 limit 域。
4、最后 descriptor 的相关 attribute 加载到 segment register 的 attribute 域。

  segment register 的 base、limit 以及 attribute 域构成 segment register 的隐藏部分。这部分信息在下一次加载之前是保持恒值的。使用同样的 selector 来获取数据,无须做多次的 segmentation 转换过程。



下面是 descriptor 的寻扯过程示意图:

                                      base+SI*8
selector ---------->  GDT  -------------> descritptor  -------> segment register
            |   TI = 0            ^  
            |                        |
            |                        |
            +--------> LDT  --+
               TI = 1







5.5、  Linear Address 的产生

  selector : offset  这种形式的地址被 Logic Address(逻辑地址)。Logic Address(逻辑地址)和 Linear Adress(线性地址)都应该被称为 Virtual Address(虚拟地址)。Logic Address 经过 segmentation(分段机制)转换为 Linear Address。
  逻辑地址中的 offset 值实际上也被称为 Effective Address(有效地址)。

  Linear Address 的形式是:LA = base + offset    或表达为: LA = base + EA。 在 32 位的地址空间中,linear address 的表达范围是:0x00000000 ~ 0xFFFFFFFF 共 4G 的地址空间。在 64 位地址空间范围是:0x00000000_00000000 ~ 0xFFFFFFFF_FFFFFFFF。

  如前所述:linear address 的 base 地址是由 selector 寻址 descriptor 获取。

因此:
  Logic Address 形式中的 selector 索引到相应的 descriptor,从而获得 base 地址值,再加上 Logic Address 形式中的 offset 值,就产生了 Linear Address 。



下面是 Linear Address 产生的示例图:

selector : offset
-------    ------
    |           |
    |           +------------------------------+--------------> linear address
    |                                                       |
    |                                                       |
    +--------> descriptor ------> base ------+



  当第 1 次加载 descriptor 后,在 selector 不变的情况下,linear address 的产生已经成为 cs.base + offset 或 ds.base + offset ...







:wink:

[ 本帖最后由 mik 于 2009-2-24 00:32 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP