无用的知识:CPU执行的第一条指令

问题1:第一条指令在哪个硬件上?

答案:cpu要执行的第一条指令被固化在rom中。BIOS芯片一般都是EPROM,现在用来烧写BIOS的EPROM芯片有256KB的,1MB的,4MB的

注:RAM(Random Access Memory)随机访问存储器:ram不断电,可以保存信息,一旦断电,ram中的信息将会丢失;可编程只读存储器(PROM)、可擦除可编程只读存储器(EPROM),EPROM需要用紫外线长时间照射才能擦除,使用很不方便;电可擦除可编程只读存储器(EEPROM);Flash 存储器(FLASH EEPROM)又称闪存,快闪,它是EEPROM的一种。

问题2:CPU从哪里获得第一条指令的物理地址?

答案:段寄存器CS指向存放程序的内存段,IP是用来存放下条待执行的指令在该段的偏移量,把它们合在一起可在该内存段内取到下次要执行的指令。

3c1ef44fbc13112f11412077cd02f493.png

各种计算机的CPU可能不同,但是在CPU中至少至少要有六类寄存器:①指令寄存器(IR:Instruction Register);②程序计数器(PC:Program Counter);③地址寄存器(AR:Address Register);④数据寄存器,又叫缓冲寄存器(DR:Data Register);⑤通用寄存器(R0~R3);⑥状态字寄存器(PSW:Program Status Word)。

8086寄存器组就分为通用寄存器,专用寄存器和段寄存器三类总共15个,其中通用寄存器有AX、BX、CX、DX、SP、BP、DI及 SI,专用寄存器包括IP、SP和FLAGS三个16位寄存器,而段寄存器则有CS、DS、SS、ES。这些寄存器都是16位的。

段寄存器:8086CPU有20根地址线,而8086的寄存器只有16位,指令指针(IP)和变址寄存器(SI、DI)也是16位的(算术逻辑运算单元(ALU)宽度即数据总线却只有16位,也就是说直接参与运算的数值都是16位的)。用16位的地址寻址1MB空间是不可能的,为支持1MB寻址空间,8086在实模式下引入了分段的方法,也就是把1MB空间分为2^4,即16个段,每段不超过64KB(2^16,16位数据线就可以寻址)。在8086中设置4个16位的段寄存器,用于管理4种段:CS是代码段,DS是数据段,SS是堆栈段,ES是附加段。把内存分段后,每一个段就有一个段基址,段寄存器保存的就是这个段基址的高16位,这个16位的地址左移四位(后面加上4个0)就可构成20位的段基址。即寻址时,采用以下公式计算实际访问的物理内存地址:实际物理地址 = (段寄存器 << 4) + 偏移地址,即:段寄存器CS左移四位作为基址,再加上作为偏移的IP,最终形成指令的物理地址。这样使用两个16位的寄存器,便实现了16位内存地址到20位物理地址的转换,最大可寻址内存空间为1MB。下文会详细介绍“内存分段

程序状态寄存器:程序状态字(Program Status Word,PSW)用来表征当前运算的状态及程序的工作方式。
程序状态字寄存器用来保存由算术/逻辑指令运行或测试的结果所建立起来的各种条件码内容,如运算结果进/借位标志(C)、运算结果溢出标志(O)、运算结果为零标志(Z)、运算结果为负标志(N)、运算结果符号标志(S)等,这些标志位通常用1位触发器来保存。
除此之外,程序状态字寄存器还用来保存中断和系统工作状态等信息,以便CPU和系统及时了解机器运行状态和程序运行状态。
因此,程序状态字寄存器是一个保存各种状态条件标志的寄存器。

问题3:第一条指令的物理地址是多少?——第一条指令的物理地址计算方法

答案:8086和80286CPU的第一条指令的物理地址为FFFF0H,80386及以后的CPU,即使第一条指令的物理地址是FFFFFFF0H,因为A20默认为0,实际访问的物理地址依然是FFFF0H。

8086和80286的指令地址计算方法一致:实际物理地址 = (段寄存器 << 4) + 偏移地址,即:段寄存器CS左移四位作为基址,再加上作为偏移的IP,最终形成指令的物理地址。

8086:CPU reset后CS寄存器的值为0xFFFF,IP寄存器的值为0,所以将CS左移4位+IP,换算出的物理地址为0xFFFF0,这个地址就是1MB往下16字节的位置。

80286:CPU reset之后CS的只为0xF000,IP的只为0xFFF0,算法同样是CS左移4位+IP,计算出的物理地址也是0xFFFF0

80386及之后的CPU第一条指令的计算方法:实际物理地址=CS寄存器基地址(BASE)+EIP658ff0795a2da3262f7867de829ff140.png

CPU的演变

8086处理器是一个16位的处理器,它的数据总线是16位,而地址总线是20位的,最多可以寻址1MB的地址空间。

之后的80286处理器也是16位,但是地址总线有24位,

从Intel 80286开始,具有24根地址线,最高可以寻址16MB,并且有和8088一样的实地址模式,因此无法在超出1MB的地方产生地址环绕(下文会详细介绍wrap-around技术)。为了实现完全的兼容性,IBM公司公司开发了一个开关来开启或禁止0x100000地址位,这个信号开关就是A20,如果它为0,则20位及以上地址都会被清除,从而实现了兼容性。——具体看问题3.1

wrap-around:对于这部分超出1M地址空间的部分,对于超出0x100000(1MB)的寻址地址将默认地环绕到0x0FFEF,这就是所谓的地址回绕或者地址环绕。也就是说,系统计算实际物理地址时是依照对1M求模运算的方式进行的。在有些技术文献里你会看到这样的技术被称之为wrap-around。

80386及其后继的机型,数据、地址总线都是32位,因此寻址空间为4G。80386中对内存的管理有两种,一个是段式内存管理,一个是页式内存管理。

从80286开始CPU演变出两种工作模式:实模式和保护模式,实模式和保护模式之间可以相互转换

内存分段

段式内存管理带来了优势:

  • 调试错误更容易定位
  • 程序的地址不再需要采用内存物理地址进行编码
  • 支持更大的内存地址

但是在该环境下,应用程序可以直接对系统的任意内存地址(包括操作系统所在的区域)进行操作

实地址模式(实模式)

80x86中除8086/8088只能在实模式下工作之外,其他微处理器均可在实模式和保护模式下工作。

CPU硬件初始化过程中,硬件可能请求执行自检。如果执行自检,自检完成后,EAX的值为自检错误码,0表示没有任何错误;——该过程不分析了

CPU硬件初始化完成后,CPU被设置为实地址模式,地址无分页。所有寄存器被初始化为特定的值,如下表, Cache、TLB(Translation Lookup Table)、BLB(Branch Target Buffer)这三个部件的内容被清空(Invalidate);

RegisterPentium 4 and Intel Xeon ProcessorP6 Family Processor Including DisplayFamily = 06H)Pentium Processor
EFLAGS100000002H00000002H00000002H
EIP0000FFF0H0000FFF0H0000FFF0H
CR060000010H60000010H60000010H
CR2, CR3, CR400000000H00000000H00000000H
CSSelector = F000H
Base = FFFF0000H
Limit = FFFFH
AR = Present, R/W, Accessed
Selector = F000H
Base = FFF0000H
Limit = FFFFH
AR = Present, R/W, Accessed
Selector = F000H
Base = FFFF0000H
Limit = FFFFH
AR = Present, R/W, Accessed
SS, DS, ES, FS, GSSelector = 0000H
Base = 00000000H
Limit = FFFFH
AR = Present, R/W, Accessed
Selector = 0000H
Base = 00000000H
Limit = FFFFH
AR = Present, R/W, Accessed
Selector = 0000H
Base = 00000000H
Limit = FFFFH
AR = Present, R/W, Accessed
EDX00000FxxH000n06xxH000005xxH
EAX000
EBX, ECX, ESI, EDI, EBP,ESP00000000H00000000H00000000H

"<center>CPU初始化后寄存器的初始值</center>"

参考Intel IA32 architectures software developer manual(源文件已放到附件中),该章节介绍了第一条指令的地址计算方法,如下:

5a95b95e96e9bcc8cf9dacfe0ea11122.png

"<center>Intel手册中介绍CPU第一条指令</center>"

The first instruction that is fetched and executed following a hardware reset is located at physical address FFFFFFF0H. This address is 16 bytes below the processor’s uppermost physical address. The EPROM containing the software-initialization code must be located at this address.

The address FFFFFFF0H is beyond the 1-MByte addressable range of the processor while in real-address mode. The processor is initialized to this starting address as follows. The CS register has two parts: the visible segment selector part and the hidden base address part. In real-address mode, the base address is normally formed by shifting the 16-bit segment selector value 4 bits to the left to produce a 20-bit base address. However, during a hardware reset, the segment selector in the CS register is loaded with F000H and the base address is loaded with FFFF0000H. The starting address is thus formed by adding the base address to the value in the EIP register (that is, FFFF0000 + FFF0H = FFFFFFF0H).

The first time the CS register is loaded with a new value after a hardware reset, the processor will follow the normal rule for address translation in real-address mode (that is, [CS base address = CS segment selector * 16]). To ensure that the base address in the CS register remains unchanged until the EPROM based software-initialization code is completed, the code must not contain a far jump or far call or allow an interrupt to occur (which would cause the CS selector value to be changed).

上面这段话的大概意思如下:

在硬件重置之后获取并执行的第一条指令位于物理地址FFFFFFF0H。该地址比处理器的最高物理地址低16个字节。包含软件初始化代码的EPROM必须位于该地址。

在实际地址模式下,地址FFFFFFF0H 超出了处理器的1-MByte可寻址范围。CS寄存器有两部分:可见段选择器部分和隐藏基址部分。在实际地址模式中,基本地址通常是通过将16位段选择器值向左移位4位以产生20位的基本地址而形成的。然而,在硬件复位期间,CS寄存器中的段选择器加载F000H,基址加载FFFF0000H。因此,通过将基址与EIP寄存器中的值相加来形成起始地址(即FFFF0000+FFF0H=FFFFFFF0H)。

硬件复位后,CS寄存器第一次加载新值时,处理器将在实际地址模式下遵循地址转换的正常规则(即[CS基本地址=CS段选择器*16])。为了确保CS寄存器中的基址在基于EPROM的软件初始化代码完成之前保持不变,该代码不得包含远跳或远调用,也不得允许发生中断(这将导致CS选择器值发生变化)。

也就是说Intel明确了第一条指令的物理地址是FFFFFFF0H,通过CS寄存器的基地址和EIP寄存器中的值相加计算而来。

注:虽然说实地址模式下CS base address = CS segment selector * 16,但是Intel规定这种计算方式是在CS中的初始值被改变以后才使用,所以CS中的初始值未改变之前是用“物理地址=Base+EIP”的计算方式,所以第一条指令的物理地址是0xFFFFFFF0

解释:CPU复位时,进入实模式,物理地址=Base+EIP,Base为复位后的初始值;在实模式下,一旦CS寄存器的值被改变,则CS base address = CS segment selector * 16;也就是说不管任何时间,物理地址=Base+EIP都成立,仅仅是Base地址的计算改变了。

保护模式

太复杂了,暂不介绍,后面会介绍保护模式下加载bios代码初始化硬件。

问题3.1:实模式下采用16位地址总线寻址,通过内存分段(引入4个分段寄存器)最高可寻址1Mbyte,但第一条指令的物理地址为FFFFFFF0H,超出1Mbyte,如何访问?

实模式向下兼容

参考Intel IA32 architectures software developer manual,21.1.1 Address Translation in Real-Address Mode,该章节介绍了实地址模式下,地址映射方法。

70c10743d9ab4a25977b12f639e6881a.png
0581c2bb32d97131f5c5c1a064d5a6f3.png

上面这段话的大概意思如下:

在实际地址模式中,处理器不将段选择器解释为描述符表的索引;相反,它像8086处理器那样直接使用它们来形成线性地址。它将段选择器向左移动4位,形成一个20位的基址(见图21-1)。段中的偏移量被添加到基地址,以创建直接映射到物理地址空间的线性地址。

当使用8086风格的地址转换时,可以指定大于1M字节的地址。例如,对于FFFFH的段选择器值和FFFFH偏移,线性(和物理)地址将是10FFEFH(1兆字节加64KB)。8086处理器可以形成最多20位长的地址,它截断高阶位,从而将此地址“包装”为FFEFH。然而,在实际地址模式下操作时,处理器不会截断这样的地址,而是将其用作物理地址。(但是,请注意,对于以Intel486处理器开始的IA-32处理器,A20M#信号可以在实际地址模式下用于掩码线A20,从而模仿8086处理器的20位环绕行为。)应注意确保在基于多处理器的系统中正确处理基于A20M#的地址环绕。

概括一下的意思就是:为了解决这个高端地址访问的不兼容性,所以80286(地址总线有24位)及之后的处理器运行在实模式下时,A20被置零来保证实际地址的一致性。即使使用的是32位CPU,也会假装我们用的是16位CPU。即实地址模式下,地址总线为 20 位,可以寻址 1MB 的内存空间,地址范围大概是 0x00000 ~ 0xFFFFF。

注:从CPU初始化,到段寄存器被重写(通过跨段跳转指令)前,80386及之后的处理器,CS段基地址为初始化值FFFF0000H

CPU会执行一条长跳转,从高地址跳到1M以下地址空间,这会引起cs寄存器的初始值被改变,然后CPU寻址方式重新回归到cs左移4位 + ip的模式。

问题3.2:第一条指令为什么在实模式可寻址范围的顶部向下16个字节?

在8086中,中断向量位于内存位置0和更高的位置。中断向量必须位于已知地址,并且为了灵活起见,必须位于RAM中,但是CPU设计人员无法知道系统中将有多少RAM,因此,从0开始并进行适当的处理。当然,从80286开始,中断向量可以移动到除0以外的其他起始位置,但是现代的64位x86 CPU仍以8086模式启动。因此,由于中断向量从0开始并向上工作,因此ROM必须从顶部开始并向下工作。一条指令16字节,所以指令地址在寻址范围的顶部向下16个字节。

问题4:有了第一条指令的物理地址,如何找到第一条指令所在位置(物理器件)?CPU如何读取该指令的内容?

这个问题涉及到太多的总线和芯片,不做介绍了。

b670da01d8388ac07f5948c93c27ad0c.png
b111393c0b4494474a9592e768dc5369.png

PMC控制器在系统中是以PCI设备的形式存在的,在该PCI设备中存在着一组PAM(Programmable Attribute Map)寄存器:PAM0-PAM6,该寄存器组用于控制内存的属性,即可读或者可写。如果一个区域的可读标志位为0,则对该地址的读将会被转到PCI空间中,类似的,如果一个区域的可写标志位为0,则对该区域地址的写将会被转到PCI空间中。

DMI总线:是PCH桥与CPU通讯的差分信号总线,共有4组信号(每组包含一对发送、一对接收)16根线,维修过程中,可以通过CPU假负载打值,每个对地值都要一致(350~420),如果测得无穷、或者阻值异常,表示CPU与桥之间断线,DMI总线异常(断线、短路)会引起CPU不工作、不跑码。

SPI总线:Serial Peripheral Interface,是串行外围设备接口,是一种高速,全双工,同步的通信总线。常规只占用四根线,节约了芯片管脚,PCB的布局省空间。现在越来越多的芯片集成了这种通信协议,常见的有EEPROM、FLASH、AD转换器等。

CPU发出寻址信息寻找自检程序,寻址信息通过前端总线发向北桥芯片,北桥接到寻址信息后,再发给南桥芯片,南桥收到寻址信息后,通过PCI总线到ISA总线,再由ISA总线控制器和译码器向BIOS芯片传输16位地址信号。

在CPU上电的时候,CPU发送此地址到北桥上解码,如果该地址没有被北桥上的设备占用,那么就会通过DMI通道发送到南桥上的设备进行解码,此地址会被南桥的SPI控制器接收并通过SPI访问BIOS 芯片。

简单点说就是某个芯片里(南桥或者北桥芯片)里面存储了一张物理地址映射表

从物理上来讲,在地址解码时,F0000 ~ FFFFFh 与 FFFF0000 ~ FFFFFFFFh 会被解码到同一个区域,靠硬件来保证。现在的芯片组提供的厂商有很多,如:Intel,AMD,nvidia,VIA,SIS 等,它们的解码实现方法可能会不同,但都要保证这个所谓的“别名”机制。

Intel 实现是:MCH 将 F_0000~F_FFFF(64k)这段区域定义为 PAM(Programed attribute memory),分 disable,read-only ,wirte-only,read/write 四种属性,初始属性是 disable,也即是无用,因此这段区域将被送去 ICH 解码,0xFFFF_0000~0xFFFF_FFFF(64k)的也被 ICH 解码,ICH 转交 LPC bridge 处理,它们被解码为同一区域。

问题5:第一条指令的内容是什么?

CPU执行的第一条指令是一个跳转指令,有的BIOS IBV这条指令是JMP F000:E05Bh,有的则是JMP F000:FFAAh。不同产商做法不同,都是一条跳转指令。

CPU从EPROM芯片中(第一条指令的物理地址)取指令,存储到指令寄存器中。执行完后,CS将被更新成0xF000(CS寄存器的初始值也为F000H,被修改后,CS寄存器基地址计算方法为:CS base address = CS segment selector * 16),表面上看CS其实没变化,但CS的基址域将被更新为0x000F0000h(0xF000左移4位),此时形成的物理地址为Base(0x000F0000h)+EIP(0xE05Bh)=0x000FE50B,而此地址是在1Mbyte内了。

问题5.1:实地址模式如何切换到保护模式?

实模式到保护模式的转换:初始化控制寄存器、GDTR、LDTR、IDTR与TR等管理寄存器以及页表,然后通过加载CR0的PE位(第0位)为1 转换成保护模式。

从实地址模式切换到保护模式,通常需要建立描述符表(descriptor table),设置控制寄存器CR0的PE位。

  • 打开A20

地址线编号从0开始,实模式下只用了其中20根即A0~A19,这是因为A20Gate的存在,它可以控制A20地址线的有效性,而实模式下是将A20关闭了的。因此只要打开A20便可突破1M的局限性,访问更大的内存空间。

A20Gate由键盘控制器(i8042芯片)控制,虽然由键盘控制器控制,但实际上与键盘并没有什么关系,这是IBM利用键盘控制器上剩余的输出线来对A20管理控制,物尽其用嘛。

  • 加载GDT

先在操作系统引导程序中创建GDT,再使用lgdt m16&32(m为48位内存数据)指令将m加载到GDTR寄存器,使得CPU知道全局描述符表在哪。fac85087077262a0d441f08417fd4c50.png

32位的地址,16位的界限,216 / 8 = 8192,同样说明最多由8192个描述符。

  • 将CR0寄存器PE位置1

CR0寄存器是控制寄存器,大致结构如下:7bf9d21f1bd76266dc3dde92eed6c96f.png

如图所示PE位为第0位,置1便进入了保护模式的大门。

三个步骤都有相应的指令,可使用汇编代码直接操作。

A20Gate由i8042控制,则需要对i8042的寄存器进行某些操作或者直接操控0x92h端口来打开;

GDT就像一个大数组,每一个表项为一个结构体,在c等高级语言中很好实现,只是在loader程序中是用汇编语言实现的,本质上并没有区别。创建好GDT之后构建lgdt指令需要的48位内存数据,之后lgdt指令将其加载到GDTR寄存器;

将CR0寄存器PE位置1也是借助相应指令实现的;

下面为一种实现,仅供参考,对应的汇编指令如下:

04d5d71aff3548e41e96c10554a0b89e.png

问题5.2:跳转指令为什么会改变CS寄存器?

ljmp用选择器加载cs,该选择器通常在GDT中选择DPL = 0、32位代码段的描述符。 在执行ljmp之前,无论是否设置了PE,您仍处于16位代码段中。 PE的设置会影响加载段寄存器的行为。 是cs选择器加载了一个真正改变模式的描述符。

题外话1:为什么8086和80386+的第一条指令没有使用相同的物理地址?

题外话2:新架构的x86cpu已经和以前的80386有了很大的区别,第一条指令的地址依然还是0xFFFFFFF0吗?

题外话3:EC(Embedded Controller)做了什么?

mythos

我还没有学会写个人说明!

相关推荐

暂无评论

QQ QQ

客服 工作时间:周一至周六 9:30-22:00 QQ:670088886(点击咨询) 直奔主题,别问在不在,谢谢!

热线 热线

13888888888

微信 微信
微信
公众号 公众号
公众号
百度 百度

test