x86 保护模式与内核 知识点汇总

本文是《x86汇编语言 从实模式到保护模式》一书的学习笔记。

在这里插入图片描述

正文

  • Bochs调试指令:点这里

  • 处理器引脚中,有一个是RESET,用于接受复位信号。每当处理器加电,或者RESET引脚的电平由低变高时,处理器都会执行一个硬件初始化,以及一个可选的内部自测试(Build-in Self-Test,BIST),然后将内部所有寄存器的内容初始到一个预置的状态

  • 处理器加电之后,它无法从内存中取得任何指令。

  • 与DRAM不同,只读存储器(ROM)不需要刷新,它的内容是预先写入的,占据0xF0000~0xfffff的内存空间。0xffff0处往往是一条跳转指令,防止因为溢出而变成0x00001。

  • 8086加电或者复位时,CS=0xffff,ip=ox0000,正好指向了ROM开始的地址。

  • 每个盘片都有两个磁头(Head),上面一个,下面一个,所以经常用磁头来指代盘面。磁头都有编号,第1个盘片,上面的磁头编号为0,下面的磁头编号为1;第2个盘片,上面的磁头编号为2,下面的磁头编号为3,以此类推。

  • 柱面从盘面最边缘的磁道开始,向着圆心从0开始编号

  • 为了加速数据在硬盘上的读写,最好的办法就是尽量不移动磁头。(寻道时间长)

  • 磁道划分为许多分段之后,每一部分都呈扇形,这就是扇区的由来。

  • 扇区和扇区之间使用空白间隔开,每个扇区以扇区头开始,然后是512个字节的数据区。扇区头包含了每个扇区自己的信息,主要有磁道号、磁头号和盘区号,用来供硬盘定位机构使用。

  • 值得注意的是,磁头和磁道是从0开始编号的,扇区则是从1开始编号的。

  • ROM-BIOS将读取硬盘主引导扇区的内容,将其加载到内存地址0x0000:0x7c00处,至于为什么是这个地址,没有什么特别的理由。

  • 如果更改了这个地址(0x0000:0x7c00)的内容,就会在操作系统被加载之前加载你自定义的程序,代价是操作系统的崩溃。

  • 要访问硬盘,运行中的程序必须至少向硬盘控制器提供4个参数,分别是磁头号、磁道号、扇区号,以及访问意图(是读还是写)。

  • conectix标识符用来告诉虚拟机这是一个合法的VHD文件。

  • LBA(逻辑块地址)模式:自动分块和换区来访问硬盘。扇区在编号时,以柱面为单位,减少了磁头的移动次数。

  • CHS模式:采用磁头、磁道、扇区来访问硬盘的模式

  • LBA=C*磁头总数*每道扇区数+H*每道扇区数+(S-1)LBA:逻辑号,C、H、S:磁道、磁头、扇区号

  • $用于表示当前指令的地址,$$用于表示程序开始地址

  • 一个有效的主引导扇区,最后两个字节应该是0x55和0xAA

  • 8086可以访问1MB的内存,其中0x00000~9FFFF属于常规内存,由内存条提供;0xF0000~0xFFFFF由主板上的一个芯片提供,即ROM-BIOS;中间320KB的空间由特定的外围设备提供。除非显卡出了问题,否则0xB8000~0xBFFFF这段空间总是可以访问的。

  • 当编译好的程序加载到物理内存后,它在段内的偏移地址和它在编译阶段的汇编地址是相同的。(这一点可以从lst文件中得知)

  • 机器码EA表示直接绝对转移指令;E9表示相对转移指令

  • 现代处理器在加电启动时,处理器的高位部分会被强制转换为高电平,直到遇到第一个段间转移指令,这个指令改变了CS和IP的值,以后处理器发出的物理地址仅仅取决于CS和IP了。

  • 32位x86处理器对寄存器做了扩展,使之达到32位,以处理32位的数据。这8个32位寄存器分别是EAX、EBX、ECX、EDX、ESI、EDI、EBP和ESP,它们可以在程序中直接做为32位寄存器使用。同时,指令指针寄存器IP也做了扩展,达到32位,即EIP。为了保持同8086的兼容性,这些寄存器的低16位依然保持以前的用法,这使得以前的程序可以在32位处理器上正常运行。

  • 在64位处理器上,这些寄存器再次被扩展,达到了64位,即RAX、RBX、RCX、RDX、RSI、RDI、RBP、RSP和RIP。同时,它们的低32位(包括低16位)依然保持从前的用法。除此之外,64位的x86处理器还新增了8个64位的寄存器R8、R9、R10、R11、R12、R13、R14和R15,它们只能整体作为64位的寄存器来用

  • 在32位和64位处理器中,以上6个段寄存器(CS、SS、DS、ES、FS、GS)都依然是16位的,但都额外增加了一个不可访问的部分,叫做段描述符高速缓存器。段描述符高速缓存器由处理器内部使用,不能在程序中访问,里面存放了段的起始地址、段的扩展范围,以及段和各种属性,比如它是代码段还是数据段,是否可以写入,是否被访问过,等等。

  • ‘\’代表续行符

  • Bochs调试指令总结
    s:单步执行
    n:自动完成循环过程,并在循环体外的下一条指令之前停住
    u:反汇编指令(两个参数,第一个参数是跟在’/'后面的指令,指定反汇编出多少条指令,第二个参数用于指定一个内存地址,从此处开始反汇编)
    b:设置断点
    c:连续执行程序
    info:显示寄存器的状态(info eflags)

  • Bochs跳转主引导扇区:
    b 0x7c00
    c

  • 在一些计算机系统中,端口号是映射到内存地址空间的,而在另一些计算机系统中,端口是独立编址的,不和内存发生关系。

  • “of”是溢出标志;“df”是方向标志;“if”和“tf”是和中断有关标志;“sf”是符号标志;“zf”是零标志;“af”是辅助进位标志;“pf”是奇偶标志;“cf”是进位标志。如果显示的标志名称是小写的,那么,说明该标志为“0”;否则,该标志的状态为“1”。

  • NASM编译器使用汇编指令“SECTION”或者“SEGMENT”来定义段。它的一般格式是SECTION 段名称或者SEGMENT 段名称

  • 在段定义中使用“align=”子句,用于指定SECTION的汇编地址对齐方式

  • ror指令:循环右移

  • 设置扇区数量;设置起始LBA扇区号,将28位的扇区号写入端口0x13、0x1f4、0x1f5和0x1f6(0x1f6端口的低4位用于存放逻辑扇区号的24~27位,第4位用于指示硬盘号,0表示主盘,1表示从盘。高3位是“111”,表示LBA模式。);向端口0xlf7写入0x20,请求硬盘读;等待读写操作完成。端口0xlf7既是命令端口,又是状态端口;连续取出数据。0x1f0是硬盘接口的数据端口,而且还是一个16位端口。一旦硬盘控制器空闲,且准备就绪,就可以连续从这个端口写入或者读取数据。

  • 外部硬件中断是通过两个信号线引入处理器内部的,这两根线名字叫NMI和INTR
    在这里插入图片描述

  • 几乎所有的NMI事件对于处理器来说都是致命的,所以在实模式中NMI被赋予了统一的中断号2

  • Intel处理器允许256个中断,中断号的范围是0~255,8259负责提供其中的15个,但中断号并不固定。之所以不固定,是因为当初设计的时候,允许软件根据自己的需要灵活设置中断号,以防止发生冲突。该中断控制器芯片有自己的端口号,可以像访问其他外部设备一样用in和out 指令来改变它的状态,包括各引脚的中断号。正是因为这样,它又叫可编程中断控制器(Programmable Interrupt Controller,PIC)。
    在这里插入图片描述

  • 每一片8259芯片只有8个中断输入引脚;第一块8259芯片的代理输出INT直接送到处理器的INTR引脚,这是主片(Master);第二块8259芯片的INT输出送到第一块的引脚2上,是从片(Slave),两块芯片之间形成级联(Cascade)关系。如此一来,两块8259芯片可以向处理器提供15个中断信号。当时,接在8259上的15个设备都是相当重要的,如PS/2键盘和鼠标、串行口、并行口、软磁盘驱动器、IDE硬盘等。现在,这些设备很多都已淘汰或者正在淘汰中,根据需要,这些中断引脚可以被其他设备使用。

  • 当IF为0时,所有从处理器INTR引脚来的中断信号都被忽略掉;当其为1时,处理器可以接受和响应中断。

  • 在8259芯片内部,有中断屏蔽寄存器(Interrupt Mask Register,IMR),这是个8位寄存器,对应着该芯片的8个中断输入引脚,对应的位是0还是1,决定了从该引脚来的中断信号是否能够通过8259送往处理器(0表示允许,1表示阻断)。当外部设备通过某个引脚送来一个中断请求信号时,如果它没有被IMR阻断,那么,它可以被送往处理器。

  • 8259芯片是可编程的,主片的端口号是0x20和0x21,从片的端口号是0xa0和0xal,可以通过这些端口访问8259芯片,设置它的工作方式,包括IMR的内容。

  • 8259的主片引脚0(IR0)接的是系统定时器/计数器芯片;从片的引脚0(IRO)接的是实时时钟芯片RTC。

  • 复习一下中断向量表(IVT)的位置:从内存0x00000开始,到0x003ff结束

  • 每个中断在中断向量表中占四个字节

  • NMI发生时,处理器不会从外部获得中断号,它自动获得中断号2

  • 中断随时可能发生,中断向量表的建立和初始化工作是由BIOS在计算机启动时负责完成的。BIOS为每个中断号填写入口地址,因为它不知道多数中断处理程序的位置,所以,一律将它们指向一个相同的入口地址,在那里,只有一条指令:iret。

  • 南桥内部集成了实时时钟电路(RTC)和两小块由互补金属氧化物组成的静态存储器(CMOS RAM)

  • CMOSRAM的访问,需要通过两个端口来进行。0x70或者0x74是索引端口,用来指定CMOSRAM内的单元;0x71或者0x75是数据端口,用来读写相应单元里的内容。

  • 端口0×70的最高位(bit 7)是控制NMl中断的开关。当它为0时,允许NMI中断到达处理器,为1时,则阻断所有的NMl信号;其他7个比特,即0~6位,则实际上用于指定CMOSRAM单元的索引号。
    在这里插入图片描述

  • 在这里插入图片描述

  • not按位取反

  • test指令和and指令相同,都是将两个操作数进行逻辑与

  • 软中断是由int指令引起的中断处理。这类中断也不需要中断识别总线周期,中断号在指令中给出。

  • int3是断点中断指令,机器指令码为CC。

  • int n 一类软中断

  • into 溢出中断

  • 32位寄存器的高16位是不可独立使用的,但是低16位保持了同16位处理器的兼容性

  • 因为64位寄存器有32根地址线,能够访问完整的4G内存,所以提供了一种平坦模型(Flat Mode)只分一个段,可以视为不分段。

  • 在32位模式下,处理器要求在加载程序时,先定义该程序所拥有的段,然后允许使用这些段。定义段时,除了基地址(起始地址)外,还附加了段界限、特权级别、类型等属性。当程序访问一个段时,处理器将用固件实施各种检查工作,以防止对内存的违规访问。

  • 传统的段寄存器保存的不再是16位段基地址,而是段的选择子,用于选择所要访问的段。出了段选择器之外,每个段寄存器还包括一个不可见的部分,叫做描述符高速缓存器。

  • 32位处理器提供虚拟8086模式(V86模式),V86模式是保护模式的一种

  • 未开启页功能时,段部件产生的线性地址就是物理地址;开启页功能后,段部件产生的地址是线性地址,需要经过页部件转换,才是物理地址。

  • 寄存器的速度是最快的,原因在于它使用了触发器,这是一种利用反馈原理制作的存储电路。触发器的工作速度是纳秒(ns)级别的,当然也可以用来作为内存的基本单元,即静态存储器(SRAM),缺点是成本太高,价格也不菲。所以,制作内存芯片的材料一般是电容和单个的晶体管,由于电容需要定时刷新,使得它的访问速度变得很慢,通常是几十个纳秒。因此,它也获得了一个恰当的名字:动态存储器(DRAM),我们所用的内存芯片,大部分都是DRAM。最后,硬盘是机电设备,是机械和电子的混合体,它的速度最慢,通常在毫秒级(ms)。

  • 在处理器内部,有一个小容量的高速缓存器,叫分支目标缓存器(Branch Target Buffer,BTB)。当处理器执行了一条分支语句后,它会在BTB中记录当前指令的地址、分支目标的地址,以及本次分支预测的结果。下一次,在那条转移指令实际执行前,处理器会查找BTB,看有没有最近的转移记录。如果能找到对应的条目,则推测执行和上一次相同的分支,把该分支的指令送入流水线。

  • 在这里插入图片描述

  • 在这里插入图片描述

  • 一个段有关的信息需要8个字节来描述,称为段描述符

  • 存放段描述符的表称为描述符表,最主要的描述符表是全局描述符表(GDT),通常定义在1MB以下的内存中(实模式只能访问1MB的内存)。

  • 为了跟踪全局描述符表,处理器提供了一个48位的寄存器称为全局描述符表寄存器(GDTR),前16位存放边界,后32位存放线性地址

  • 段描述符格式
    高32位

长度含义解释
0~7段基地址23~16
8~11TYPE描述符子类型
12S段描述符的类型,0系统段/1数据段
13~14DPL特权级别
15P段存在位
16~19段界限19~16
20AVL软件可以使用的位
21L64位代码段标志
22D/B默认的操作数大小
23G粒度,解释段界限的含义
24~31段基地址31~24

低32位

长度含义解释
0~15段界限15~0
16~31段基地址15~0
  • 界限值*粒度=段内偏移量(向上扩展的段)
  • 界限值*粒度+1=段内偏移量(向下扩展的段)
  • 在这里插入图片描述
  • 第一个描述符必须是空描述符,这个描述符的访问被禁止
  • cli禁止中断
  • 在32位cpu上,每当引用一个段时。处理器自动将段地址左移四位,传送到描述符高速缓存器,此后,一直使用描述符高速缓存器的内容作为段地址。(实模式下,段寄存器描述符高速缓存器的内容仅仅低20位有效)
  • 在保护模式下,传送到段选择器的内容不是逻辑段地址,而是段描述符在描述符表中的索引号
  • 在这里插入图片描述
  • TI是描述符表指示器,TI=0,表示描述符在GDT中,TI=1,描述符在LDT中。
  • RPL是请求特权级,表示给出当前选择子的那个程序的特权级别。
  • 处理器将索引号乘以8找到GDT中的描述符之后将描述符加载到描述符高速缓存中的过程在这里插入图片描述
  • 保护模式下,不允许使用mov指令改变段寄存器CS的内容
  • 在这里插入图片描述
  • 有前缀的指令比没有前缀的指令多花一个时钟周期
进入保护模式的过程:首先是创建GDT,并且安装进入保护模式时要使用的描述符,包括代码段、数据段、别名、栈段
然后初始化描述符表寄存器GDTR
mov word [cs: pgdt+0x7c00],39 ;描述符表的界限
lgdt [cs: pgdt+0x7c00]
然后取消A20屏蔽
关闭中断机制
设置PE位,正式进入保护模式
  • 在进行栈操作时,必须符合以下规则:实际使用的段界限+1≤(ESP的内容一操作数的长度)≤OxFFFFFFFF
  • 处理器访问数据段时,必须符合以下规则:0≤(EA+操作数大小一1)实际使用的段界限
  • 实际段界限=(描述符中的段界限+1)*4KB-1
  • 内核
段名作用
公用例程段提供各种用途的子程序
内核数据段供内核自己使用的可读写内存空间
内核代码段分配内存,读取和加载用户程序,控制用户程序执行
尾部用于计算内核长度
内存布局图
用户程序和数据区
文本模式显示缓冲区
系统核心程序和数据
全局描述符表
初始化代码段(主引导程序)
系统核心栈
  • 内核要具有访问全部内存空间的数据段在GDT中声明

  • 我们使用lgdt [cs:pgdt+0x7c00]指令来加载全局描述符表寄存器,要访问pgdt的内存空间,使用0x7c00+pgdt+0x02

  • 在加载内核中的段并且生成新的描述符表的时候,因为描述符并非显而易见,所以需要一个过程(假如是make_gdt_descriptor)来得到描述符,需要的参数是:段的基地址、段界限、段属性。生成后需要重新加载GDTR。

  • 伪指令助记符
    DB 定义的变量为字节型
    DW 定义的变量为字类型(双字节)
    DD 定义的变量为双字型(4字节)
    DQ 定义的变量为4字型(8字节)
    DT 定义的变量为10字节型

  • 定位类型
    PARA: 段的起点从节边界开始
    (16个字节为1节)
    BYTE: 段的起点从存储器任何地址开始
    WORD:段的起点从偶地址开始
    PAGE: 段的起点从页边界开始
    (256个字节为1页)

  • 为了有效地在任务之间实施隔离,处理器建议每个任务都应当具有自己的描述符表,称为局部描述符表LDT(Local Descriptor Table),并且把专属于自己的那些段放到LDT中。

  • 和GDTR一样,LDTR包含了32位线性基地址字段和16位段界限字段,以指示当前LDT的位置和大小。

  • 段选择子(16位)结构:
    INDEX:在GDT数组或LDT数组的索引号
    TI:Table Indicator,这个值为0表示查找GDT,1则查找LDT
    RPL:请求特权级。以什么样的权限去访问段。

  • 为了保存任务的状态,并在下次重新执行时恢复它们,每个任务都应当用一个额外的内存区域保存相关信息,这叫做任务状态段(Task State Segment:TSS)。处理器用TR寄存器(任务寄存器)来指向当前任务的TSS。

  • 特权级(Privilege Level),也叫特权级别,是存在于描述符及其选择子中的一个数值,当这些描述符或者选择子所指向的对象要进行某种操作,或者被别的对象访问时,该数值用于控制它们所能进行的操作,或者限制它们的可访问性。

  • 当任务在自己的局部空间内执行时,当前特权级CPL是3;当它通过调用系统服务,进入操作系统内核,在全局空间执行时,当前特权级CPL就变成了0。总之,很重要的一点是,不能僵化地看待任务和任务的特权级别。

  • 那些只有在当前特权级CPL为0时才能执行的指令,称为特权指令(Privileged Instructions)。典型的特权指令包括加载全局描述符表的指令lgdt(它在实模式下也可执行)、加载局部描述符表的指令lldt、加载任务寄存器的指令ltr、读写控制寄存器的mov指令、停机指令hlt等十几条。

  • 一般来说,控制转移只允许发生在两个特权级相同的代码段之间。如果当前特权级为2,那么,它可以转移到另一个DPL为2的代码段接着执行,但不允许转移到DPL为0、1和3的代码段执行。不过,为了让特权级低的应用程序可以调用特权级高的操作系统例程,处理器也提供了相应的解决办法。

  • 第一种方法是将高特权级的代码段定义为依从的。代码段描述符的TYPE字段有C位,如果C=0,这样的代码段只能供同特权级的程序使用;否则,如果C=1,则这样的代码段称为依从的代码段,可以从特权级比它低的程序调用并进入。

  • 但是,即使是将控制转移到依从的代码段,也是有条件的,要求当前特权级CPL必须低于,或者和目标代码段描述符的DPL相同。即,在数值上,CPL=目标代码段描述符的DPL

  • 依从的代码段不是在它的DPL特权级上运行,而是在调用程序的特权级上运行。就是说,当控制转移到依从的代码段上执行时,不改变当前特权级CPL,段寄存器CS的CPL字段不发生变化,被调用过程的特权级依从于调用者的特权级,这就是为什么它被称为“依从的”代码段。

  • 进入保护模式之后,处理器自动将当前特权级CPL设定为0,以0特权级的身份开始执行保护模式的初始指令。

特权级别缩写含义
CPL当前进程的权限级别
RPL位于段选择子中,指请求特权级
DPL存储在段描述符中,规定访问该段的权限级别
  • 调用门(Call-Gate)用于在不同特权级的程序之间进行控制转移。本质上,它只是一个描述符,一个不同于代码段和数据段的描述符,可以安装在GDT或者LDT中。该描述符的格式如图14-9所示,下面是低32位,上面是高32位。
    在这里插入图片描述
  • 为了切换栈,每个任务除了自己固有的栈之外,还必须额外定义几套栈,具体数量取决于任务的特权级别。0特权级任务不需要额外的栈,它自己固有的栈就足够使用,因为除了调用返回外,不可能将控制转移到低特权级的段;1特权级的任务需要额外定义一个描述符特权级DPL为0的栈,以便将控制转移到0特权级时使用;2特权级的任务则需要额外定义两个栈,描述符特权级DPL分别是0和1,在控制转移到0特权级和1特权级时使用;3特权级的任务最多额外定义3个栈,描述符特权级分别是0、1和2,在控制转移到0、1和2特权级时使用。
  • 加载程序并创建一个任务,需要用到很多数据,比如程序的大小、加载的位置,等等。当任务执行结束,还要依据这些信息来回收它所占用的内存空间。还有,多任务系统是多个任务同时运行的,特别是在一个单处理器(核)的系统中,为了在任务之间切换和轮转,必须能追踪到所有正在运行的任务,记录它们的状态,或者根据它们的当前状态来采取适当的操作。
  • 为了满足上述的需求,内核应当为每一个任务创建一个内存区域,来记录任务的信息和状态,称为任务控制块(TCB)。
  • 在这里插入图片描述
  • 当用户程序被读入内存,并处于运行或者等待运行的状态时,就视为一个任务。任务有自己的代码段和数据段(包括栈),这些段必须通过描述符来引用。
  • 任务状态段TSS在这里插入图片描述
  • EFLAGS寄存器的IOPL位决定了当前任务的IVO特权级别。如果当前特权级CPL高于,或者和任务的I/O特权级IOPL相同时,即,在数值上,CPL≤IOPL时,所有VO操作都是允许的,针对任何硬件端口的访问都可以通过
  • 在TSS内偏移为102的那个字单元,保存着IVO许可位串(IVO许可位映射区)的起始位置,从TSS的起始处(0)算起。因此,如果该字单元的内容大于或者等于TSS的段界限(在TSS描述符中),则表明没有I/O许可位串。在这种情况下,如果当前特权级CPL低于当前的I/O特权级IOPL,执行任何硬件I/O指令都会引发处理器异常中断。说明一下,和LDT一样,必须在GDT中创建TSS的描述符,TSS描述符中包括了TSS的基地址和界限,该界限值包括I/O许可位映射区在内。
  • 为了解决字节越界问题,要求TSS映射区最后一个字节的所有比特必须都是1,即0xff
  • 未完待续
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值