CPU的访问范围
这里我们针对Intel CPU进行讨论。
在计算机中,CPU做的工作简单来讲就是从某一个位置取回数据然后进行相应的运算并且将运算的结果在送出去,这种计算操作的不断执行就实现了某种功能。知道了这些,更多的问题就是:CPU能够访问哪些位置?CPU是如何实现访问这些位置的?
CPU能够访问的资源包括了两种:物理内存 (physical memory
)和寄存器(register
)。
物理内存 (physical memory
)这里需要注意的第一个定义 不仅仅指日常最经常说的主存(main memory
),还包括了其他各种各样的ROM,比如BIOS的ROM PCIE设备上的ROM等。
寄存器register:也分为两种:内部寄存器和外设寄存器。一般来说内部寄存器指的就是CPU内部的寄存器,如EX DX寄存器等。而外设寄存器就比较特殊了,外设寄存器一般指的是主板上除了CPU外其他位置上分部的能够让CPU直接操作以方便CPU对外部设备进行访问的寄存器,我们常说的端口Port 本质上就是指这些外设寄存器。
[区分] 接口interface
与端口 port
个人的理解:IO
接口 Interface
:解决外设和主机之间的连接问题,IO interface从物理上最直观的例子就是主板上的对外接口,这些接口中的接口芯片就起到了沟通CPU和外设的功能呢。比如最最常见的USB 接口 PCIe接口。并非所有的接口都是我们能够看到的直观形态,各种隐藏的接口芯片也不在少数,很多都是我们不能直观看到的。
在IO interface
的两端连接的分别是CPU和外设的[设备控制器](这个控制器对外设起到了一个管理的作用 可以根据CPU传递过来的数据选择对应的设备并且命令设备进行相应的操作)
IO接口为了能够实现上述的功能,其内部就需要许多的寄存器。这些寄存器根据功能大致分为三类:
- 数据寄存器
- 控制寄存器
- 状态寄存器
CPU通过总线读写控制寄存器告知设备自己想要进行的操作。设备通过状态寄存器告知CPU自己是否准备好进行操作;CPU通过读写状态寄存器来决定是否将准备传送的数据放到数据寄存器中。也就是说,本质上CPU对外部设备的控制就是对外设(控制器)对应的寄存器的控制。
一次数据的传输过程中,可以发现控制寄存器只在第一步告知操作的时候用到,其他的时候并没有用到,所以往往将控制寄存器和状态寄存区进行合并节约空间,这样就只剩下数据寄存器和控制寄存器两个了
我们平时所说的外设的端口(Port),指的就是IO接口中的能够被CPU直接访问的寄存器。我们所说的统一编址和独立编址就是给这些Port进行编址。
一个常见的接口的逻辑示意图:
地址空间
前面已经详尽的讨论了CPU能够访问的位置有哪些,接下来的问题就是CPU如何访问这些位置。假设我们是CPU,面对众多的存储单元、寄存器,如何才能精准的找到我们想要的位置获取数据呢?一个不难想到的思路就是对这些存储单元、寄存器进行“编号”。在计算机中,也确实是这样做的,只不过这种给地址单元、寄存器赋予特定“号码”的动作在计算机中被称为编址操作,而这些号码也被称为地址。前面说过physical memory
的概念,那么对这些physical memory
依次进行“编号”,得到的“号码”就是物理地址physical address
这里需要注意的第二个定义 ,物理地址最大能够达到的范围,就被称为物理地址空间physical address space
这里需要注意的第三个定义 。
说到这里,我们就不得不说一下physical address space
大小到底是怎么回事。
首先建立基础的正确认知,32位 、64位CPU中,32 64指的是CPU寄存器的位宽而并非地址线位数,而且CPU地址线位数与CPU寄存器的位宽没有任何关系!!! CPU寄存器的位宽限制的是CPU处理数据的能力,CPU地址线才是关键的能够决定physical address space
的。
再明确了上述认识的基础上继续来看,限制physical address space
大小的因素都有什么?首先地址总线的位数极大程度的影响物理地址空间,毕竟物理地址需要在地址总线上传递,2根地址线不可能找到5个地址。其次,CPU的位数也是限制物理地址空间的原因之一,毕竟需要CPU去处理数据,如果CPU的寄存器只有2位,是没有办法找到地址5的。
在早期的CPU中,CPU的位宽为32,而地址线能够达到36根(是的没错,并不是大多数人以为的32位地址线),此时限制physical address space
的主要因素就是CPU的位宽了。
经过发展到了后期CPU的位宽达到了64,但是目前地址总线最大能够达到的只有52根,此时限制physical address space
的主要因素就是地址总线的位数了。
以上并不是我瞎说的,具体的信息可以在Intel 开发者手册 找到,具体内容在手册的Vol1 3.2 以及 3.2.1
需要的可以从Intel官网自行下载。网址:Intel® 64 and IA-32 Architectures Software Developer Manuals
现在已经说清楚了限制physical address space
大小的原因和当前理论上能够实现的physical address space
范围。但是这并不代表所有的64位CPU都能够实现2^52的physical address space
,具体某一款CPU的实际的地址线位数是需要通过汇编指令 CPUID 读取EAX寄存器的第八位得到,然后在根据读出来的位数计算出physical address space
大小。
CPU支持的地址线的情况则由(可在手册中通过关键字 MAXPHYADDR查找 ) CPUID(汇编指令)读取,结果在EAX低八位中保存。
可以将参考Intel手册Vol4 2.15 P4781
已经将物理地址空间的概念说清楚了 ,接下来需要考虑的就是如何将主板上所有CPU能够访问的地址空间进行编址了
不同架构的CPU对于这一问题有着不同的处理方式,主要就是两种统一编址和独立编址。顾名思义,统一编址就是对physical memory
和 Register
统一处理,相对应的,独立编址就是将physical memory
和 Register
分别进行编址。
独立编址 Port-mapped I/O
Port-mapped I/O often uses a special class of CPU instructions designed specifically for performing I/O, such as the
in
andout
instructions found on microprocessors based on the x86 and x86-64 architectures. Different forms of these two instructions can copy one, two or four bytes (outb
,outw
andoutl
, respectively) between the EAX register or one of that register’s subdivisions on the CPU and a specified I/O port which is assigned to an I/O device. I/O devices have a separate address space from general memory, either accomplished by an extra “I/O” pin on the CPU’s physical interface, or an entire bus dedicated to I/O. Because the address space for I/O is isolated from that for main memory, this is sometimes referred to as isolated I/O. [^1]
以上是引用Wikipedia上关于 PMIO的简单解释。具体内容自己看吧,英文不难。总的来说PMIO的情况下,CPU使用特殊的指令去对外设寄存器直接进行操作,具体到以X86为架构的处理器上就是 IN/OUT指令。这种情况下IO的地址空间和物理内存地址空间是分开的、彼此隔离孤立的。
Intel手册中有着更加具体的说明
IO address space和 physical-memory address space是彼此隔离并且独立的。IO address space可以为64K的 8bit IO port进行编码,范围0-0xFFFFH,其中0x0F8-0X0FFH属于保留的地址不能随意分配。
IO space的范围其实是由最初的8086CPU的架构延续下来的,8086中IO address space 的范围由AD线数目决定,即16根。
关于独立编址的IO Port,目前需要记住的就是对于PCI设备访问的时候,需要用到的 I/O Port的address 为CF8 CONFIG_ADDRESS 和 CFC CONFIG_DATA !!!
端口意义可以从这两个网站中找到:
https://bochs.sourceforge.io/techspec/PORTS.LST
www.os2site.com/sw/info/memory/ports.txt
统一编址 Memory-Mapped I/O
独立编址是很容易理解的一种编址方式,两个空间相互不干扰,想要访问的位置分别使用对应的指令对地址进行操作就行了。虽然这样对于外设的访问方便,但是也是有问题的: IO独立编址需要单独的控制引脚用来区分IO address space
和 physical-memory address space
这样会增加芯片面积,不利于芯片设计。因此将端口和物理内存进行统一编址访问就成为另一个编址方向。
其实8086时代就已经出现了MMIO,只不过那个时候的MMIO远没有现在的MMIO复杂,只是简单的牺牲DRAM达到统一编址的目的
现在MMIO之所以越来越复杂,主要的原因就是DRAM的大小急剧扩张,如果还按照之前的思路会白白浪费DRAM,为了避免这一情况更好的利用空间,现在的MMIO变得更加复杂,还引入了内存重映射,当前以我的笔记本CPU为例对这个部分进行简单的了解
在Datasheet中,能够找到当前系统的system address map
看着有点复杂,其实慢慢不求甚解的看下去也还好
首先说图中最上面标注的System View 和 DRAM Controller View 应该怎么样理解。
DRAM Controller View就是内存控制器能够控制的内存地址的范围,那顾名思义,内存控制器的这个控制范围主要肯定就是内存的大小范围,在内存还没有增长到今天这个范围的以前,内存控制器能够识别的大小可能只有2G,而今天它能够识别的范围远远超过之前。
System View是指从CPU的角度来看,当前系统的地址范围到底是多大。需要注意的是,我们之前就说过,MMIO是将IO设备也同时映射到内存地址空间进行统一的编址,这样的话CPU能发现的地址范围肯定是要大于内存控制器所能发现的地址范围的,所以在上图中也能够看到System View是远大于DRAM Controller View的。
图中最为明显的就是4G的红线,这个分界具有极强的历史渊源,早期的CPU的地址空间上限一般都是4G,所以早期MMIO的布局情况是以4G为上限的。这也是为什么现在虽然能够支持的地址范围已经远远超过4G,但是还是以4G为一个重要的分界点——不好改了,需要兼容那些之前的设备和OS。既然如此,首先来从看4G以下地址空间的情况。
(图是我自己的 觉得这样更加便于理解)
0-1M:被分配成DOS Legacy Address Range ,其中还被细分为三个部分 可以从Datasheet查看
1M-TOLUD :被分配成Low Main Memory Address Range,这一部分主要是Main Memory,i也就是对应DRAM,但是在这段memory中间还有一些小小的地址被占用作他用,比如ISA Hole TSEG等,这些被占用的地址是极少部分,绝大部分都是被分配用在了memory上,所以这些地址大部分是可以直接被DRAM Controller访问找到对应的DRAM的
TOLUD-4G:被分配成为PCI Memory Address Range (MMIO Low),其中主要分了三个部分 Flash APCI等,DMI Interface 和 PCIe Configuration Space (后面经常会提到!!!MMCFG就是可以用来定位这个地方)。这部分地址就是被MMIO所占用的,所以这部分地址就不能让DRAM Controller在DRAM上找到对应的位置了,因为这地址表示的就不是DRAM
可以看到,在DRAM Controller的角度上,在4G以下,最重要的分割点就是TOLUD,TOLUD以上都被用作了MMIO,那么这部分地址对应的memory就不能被访问,这样就造成了memory资源的浪费。其实在TOLUD以下,也是有部分被占用的地址,但是这部分都是很少的,不像MMIO部分,大块的内存被浪费掉。TOLUD决定了4G以下能够使用的MMIO地址空间的大小,目前很多机器上,这个值都是能通过BIOS进行设置的。
在早期,计算机能够支持的内存很小,这时TOLUD的大小就是实际上DRAM的大小,这样4G-DRAM的部分就都能够用来进行MMIO,就算有大量的IO设备需要remapping,也不会浪费大量的memory,所以这种在4G下进行MMIO映射的规范就被保留了下来,但是随着内存的不断扩大,4G以上内存早就屡见不鲜,memory占用问题也就急需解决了。看4G以上部分,就能了解到这个解决的思路是什么样的
4G以上的情况就比较容易了,主要分成了三个部分
4G -TOUUD :Upper Main Memory Address Range, 这部分就比较容易理解,就是DRAM中超过4G的部分,所对应的地址
TOUUD-TOUUD+X : Reclaim Address 对4G以下MMIO占用部分内存的重新映射,利用这部分地址就能够重新利用之前浪费掉的地址空间。
TOUUD+X- MAX range : MMIO High 这部分都可以用来作为PCIE MMIO 的地址了 并且利用这部分地址也不会再占用内存地址造成内存空间的浪费了
4G以上 DRAM View需要注意的有两个地方:
-
重映射部分的地址对应的内存需要在 TOLUD-4G 的位置找
关于重映射:
-
部分DRAM 被 ME占用,并没有参与address的分配
以上就是MMIO的核心了,其实其中很多的词能够扩展说很多 但是大致的思想就是这样,我觉得我的图已经很清晰了 起码比Datasheet上容易理解。之所以研究了一下这个部分是因为看PCIe 配置空间的时候发现这个地方理解问题很多,所以顺藤摸瓜,找到了根源研究了一下。
看的不是很仔细,如果有错误希望不吝赐教
[1] [Memory-mapped I/O and port-mapped I/O - Wikipedia]Wikipedia