cpu地址空間和pci地址空間是兩個(gè)常用的比較容易混淆的概念,特別是其中不同系列的cpu的實(shí)現還各不相同:x86系列cpu地址空間和pci地址空間是重合的,即為同一空間;而非x86 cpu的cpu地址空間和pci地址空間為兩個(gè)獨立的空間。
也許是因為pci總線(xiàn)是intel發(fā)明的,所以x86內部總線(xiàn)和pci總線(xiàn)實(shí)現是一致的,但非x86系列的cpu實(shí)現pci總線(xiàn)需要用到總線(xiàn)轉換。


因為PCI地址空間和CPU地址空間是獨立的,所以PCI設備和CPU之間的互訪(fǎng)需要用到地址轉換。涉及到兩個(gè)概念,inbound和outbound。
Inbound窗口:即將CPU的一段地址空間映射到PCI地址空間上,供PCI設備訪(fǎng)問(wèn)CPU空間所用,即inbound窗口。因為對cpu來(lái)說(shuō)pci訪(fǎng)問(wèn)就是inbound訪(fǎng)問(wèn)。
Outbound窗口:即將PCI的一段地址空間映射到CPU地址空間上,供CPU訪(fǎng)問(wèn)PCI空間所用,即Outbound窗口。因為對cpu來(lái)說(shuō)訪(fǎng)問(wèn)pci空間就是outbound訪(fǎng)問(wèn)。

對x86 cpu來(lái)說(shuō),就沒(méi)有了cpu地址空間和pci地址空間之分,這兩個(gè)空間是重合的。所以pci外設和cpu之間的相互訪(fǎng)問(wèn)就不需要設置地址轉換窗口:
Inbound訪(fǎng)問(wèn):pci設備訪(fǎng)問(wèn)cpu空間,這個(gè)不需要設置inbound窗口,對外設來(lái)說(shuō)所有的地址空間都是可以訪(fǎng)問(wèn)的。
Outbound訪(fǎng)問(wèn):cpu訪(fǎng)問(wèn)pci外設,這個(gè)也還需要保留一段cpu地址給外設,讓外設的窗口能映射到這段空間來(lái),即MMIO空間。有點(diǎn)類(lèi)似于Outbound窗口,但是不需要地址轉換。

由于x86 cpu對pci標準的完美支持,所以x86 cpu還支持pci的io空間訪(fǎng)問(wèn)和pci的配置空間訪(fǎng)問(wèn):
io空間:pci標準規定的io地址空間最大可以有4G,但是x86只實(shí)現了64k的大小。io空間用來(lái)實(shí)現早期兼容的外設寄存器的訪(fǎng)問(wèn)(IO端口),和用來(lái)映射pci外設的io空間。
配置空間:用來(lái)訪(fǎng)問(wèn)pci總線(xiàn)設備的配置空間,而x86也把內部的寄存器組織成虛擬的pci設備,使用訪(fǎng)問(wèn)pci配置寄存器的方式來(lái)訪(fǎng)問(wèn)內部寄存器。
訪(fǎng)問(wèn)配置空間的方法有兩種:一是通過(guò)CF8/CFC io端口的間接訪(fǎng)問(wèn)來(lái)訪(fǎng)問(wèn)配置空間;二是通過(guò)mmcfg方式,把配置空間映射到memory空間來(lái)訪(fǎng)問(wèn)。對每個(gè)配置空間來(lái)說(shuō),CF8/CFC方式只能訪(fǎng)問(wèn)傳統的pci配置空間256字節,而mmcfg訪(fǎng)問(wèn)方式,能訪(fǎng)問(wèn)pcie的整個(gè)配置空間4k。
本節以系統的橋片為例,來(lái)說(shuō)明x86系列cpu的地址空間分配。x86 其他系列cpu的地址空間分配類(lèi)似。
x86 xeon系列cpu 在32位系統下面,通過(guò)PAE(Physical address Extension)機制可以訪(fǎng)問(wèn)到36位的地址,即最大64G的空間。

| 0-FFFFF | 0-640k常規內存(MS-DOS Area) | 這一段區域就是ram。 其中有功能劃分的區域是:起始位置的1 KB被用做BIOS中斷向量表,隨后的1 KB被用做BIOS數據區 | |
| A0000-BFFFF | 640 – 768 kB Video Buffer Area | 1、這一段區域是顯卡的顯示RAM區域,老式的VGA顯示模式直接往這段顯存寫(xiě)數據,就可以顯示?,F在估計只有bios階段使用這種顯示方式,系統起來(lái)后會(huì )開(kāi)啟更高級的顯卡顯示模式。 2、被顯存地址覆蓋的這一塊128K大小的內存,可以被利用起來(lái)當做SMM內存。SMM是CPU一種等級最高的管理模式,所以它的內存在常規下不可以被訪(fǎng)問(wèn)。 | 1、PCI在支持VGA顯示時(shí),有個(gè)VGAEN功能,比較特殊,值得關(guān)注。VGA顯卡設備不需要配置常規的pci bar寄存器地址,而只需要使能顯卡所掛在PCI-PCI橋設備的配置寄存器0x3E bit 3(VGA Enable),顯卡就會(huì )響應專(zhuān)為VGA保留的固定pci memory地址(A0000-BFFFF)和pci io地址(03c0-03df)。 2、什么是SMM模式? SMM是System Management Mode系統管理模式的縮寫(xiě)。從Intel 386SL開(kāi)始,此后的x86架構微處理器中都開(kāi)始支持這個(gè)模式。在這個(gè)模式中,所有正常執行的軟件,包括操作系統都已經(jīng)暫停運行。只有特別的單獨軟件,具備高特權模式的軟件才能運行。通常這些軟件都是一些固件程序或者是硬件輔助調試器。 x86 處理器的模式Mode模式 起始支持的處理器 Real mode Intel 8086 Protected mode Intel 80286 Virtual 8086 mode Intel 80386 Unreal mode Intel 80386 System Management Mode Intel 386SL Long mode AMD Opteron |
| C0000-CFFFF | 768 - 832 kB VGA Video BIOS ROM IDE Hard Disk BIOS ROM Optional Adapter ROM BIOS or RAM UMBs | 1、這一段區域存放顯卡的Option Rom還有其他設備的OptionRom(如硬盤(pán)、網(wǎng)卡..)。 | 這一段區域,是OptionRom和BIOS區域覆蓋了原RAM區域。由于RAM的訪(fǎng)問(wèn)速度遠遠快于這些固件的訪(fǎng)問(wèn)速度,所以通常的做法是把固件中的內容拷貝到相同地址的RAM中,然后再使能RAM而屏蔽原有的固件映射。 訪(fǎng)問(wèn)BIOS和OptionRom內容和地址都沒(méi)有改變,但是速度卻加快了。這種做法就叫ROM Shadowing |
| D0000-DFFFF | 832 - 896 kB Optional Adapter ROM BIOS or RAM UMBs | 這一段區域也是來(lái)存放設備的OptionRom。如果沒(méi)有OptionRom覆蓋,那就是常規內存 | |
| E0000-EFFFF | 896 - 960 kB System BIOS Plug and Play Extended Information | 擴展BIOS區域。 | |
| F0000-FFFFF | 960 kB–1 MB System BIOS ROM | 常規BIOS區域,映射到BIOS芯片。CPU的第一句指令0xFFFF0就跳到該區域 |
| 1M-TOLM | 低于4G的常規內存 | 這一段的內存就是低于4G的可用的內存,其中有兩段區域比較特殊。 1、15 MB - 16 MB Window (ISA Hole)傳統的ISA黑洞,現在基本不支持。 2、Extended SMRAM Space (TSEG)擴展SMM內存。前面已經(jīng)有VGA RAM覆蓋的128k內存可做SMM內存使用,系統還允許分配更多的SMM內存。 | “Coherency Protocol”Intel 橋片通過(guò)同步協(xié)議保證所有對內存訪(fǎng)問(wèn)的一致性。 |
| HECBASE-(HECBASE+256M) | MMCFG(Memory Mapped Configuration) 映射到memory空間的pci配置寄存器 | 256M的計算方法: 256bus x 32device x 8function x 4k bytes register = 256M bytes | mmcfg、mmio這兩段區域覆蓋的相同地址的常規內存比較大,怎么樣能使用到這段被覆蓋的內存? 芯片組和內存控制其有一種叫做Main Memory Reclaim AddressRange。通過(guò)這個(gè)技術(shù)可將重疊得部分的內存地址印射到4g以上的地址上去,這個(gè)功能是由硬件決定的。 |
| (HECBASE+256M) - (4G-32M) | MMIO(Memory Mapped I/O) 可分配給外設使用的pci memory空間 | 為什么要在4G以下設置Low MMIO區域,造成內存被分割成兩塊,為什么不能把MMIO都放在4G以上?主要還是為了照顧pci 32/64bit的兼容,32bit的pci地址需要放在4G以下的空間。 | |
| (4G-32M) - 4G | CPU-spec | 4G以下的32M區域是CPU的一些特殊區域,主要由以下幾部分組成: 1、16M fireware地址; 2、一些直接訪(fǎng)問(wèn)的cpu寄存器; 3、Interrupt、I/O APIC區域; | |
| 4G - ram_size | 高于4G的常規內存 | Coherency Protocol”Intel 橋片通過(guò)同步協(xié)議保證所有對內存訪(fǎng)問(wèn)的一致性。 | |
| ram_size - | high MMIO | 在高端內存之上一直到CPU支持的最大空間,都可以用來(lái)映射64bit的pci memory外設地址 |

x86只實(shí)現64k大小的io空間,其中低4k是兼容的io空間用做專(zhuān)門(mén)用途,4k以上的io地址空間可以分配給外部設備使用。
在linux下通過(guò)以下命令查看cpu的地址空間分配:
除了一些是需要直接在memory空間訪(fǎng)問(wèn)的寄存器。x86把大部分內部的寄存器組織成虛擬的pci設備,使用訪(fǎng)問(wèn)pci配置寄存器的方式來(lái)訪(fǎng)問(wèn)內部寄存器。
訪(fǎng)問(wèn)配置空間的方法有兩種:
linux內核態(tài)實(shí)現pci配置空間訪(fǎng)問(wèn)的函數有以下:
static inline int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val)
{
return pci_bus_read_config_byte (dev->bus, dev->devfn, where, val);
}
static inline int pci_read_config_word(struct pci_dev *dev, int where, u16 *val)
{
return pci_bus_read_config_word (dev->bus, dev->devfn, where, val);
}
static inline int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val)
{
return pci_bus_read_config_dword (dev->bus, dev->devfn, where, val);
}
static inline int pci_write_config_byte(struct pci_dev *dev, int where, u8 val)
{
return pci_bus_write_config_byte (dev->bus, dev->devfn, where, val);
}
static inline int pci_write_config_word(struct pci_dev *dev, int where, u16 val)
{
return pci_bus_write_config_word (dev->bus, dev->devfn, where, val);
}
static inline int pci_write_config_dword(struct pci_dev *dev, int where, u32 val)
{
return pci_bus_write_config_dword (dev->bus, dev->devfn, where, val);
}
這些函數的內部實(shí)現,會(huì )判斷cpu是否進(jìn)行了mmcfg映射。如果已經(jīng)進(jìn)行了mmcfg映射,則使用mmcfg模式訪(fǎng)問(wèn);如果沒(méi)有實(shí)現mmcfg,則使用CF8/CFC方式訪(fǎng)問(wèn)。
具體的實(shí)現過(guò)程如下:
#define PCI_OP_READ(size,type,len) int pci_bus_read_config_##size (struct pci_bus *bus, unsigned int devfn, int pos, type *value) { int res; unsigned long flags; u32 data = 0; if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; spin_lock_irqsave(&pci_lock, flags); res = bus->ops->read(bus, devfn, pos, len, &data); *value = (type)data; spin_unlock_irqrestore(&pci_lock, flags); return res; }
#define PCI_OP_WRITE(size,type,len) int pci_bus_write_config_##size (struct pci_bus *bus, unsigned int devfn, int pos, type value) { int res; unsigned long flags; if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; spin_lock_irqsave(&pci_lock, flags); res = bus->ops->write(bus, devfn, pos, len, value); spin_unlock_irqrestore(&pci_lock, flags); return res; }
PCI_OP_READ(byte, u8, 1)
PCI_OP_READ(word, u16, 2)
PCI_OP_READ(dword, u32, 4)
PCI_OP_WRITE(byte, u8, 1)
PCI_OP_WRITE(word, u16, 2)
PCI_OP_WRITE(dword, u32, 4)
static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
{
return raw_pci_ops->read(0, bus->number, devfn, where, size, value);
}
static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value)
{
return raw_pci_ops->write(0, bus->number, devfn, where, size, value);
}
struct pci_ops pci_root_ops = {
.read = pci_read,
.write = pci_write,
};
static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value)
{
unsigned long flags;
u32 base;
if (!value || (bus > 255) || (devfn > 255) || (reg > 4095))
return -EINVAL;
base = get_base_addr(seg, bus, devfn);
if (!base)
return pci_conf1_read(seg,bus,devfn,reg,len,value);
spin_lock_irqsave(&pci_config_lock, flags);
pci_exp_set_dev_base(base, bus, devfn);
switch (len) {
case 1:
*value = readb(mmcfg_virt_addr + reg);
break;
case 2:
*value = readw(mmcfg_virt_addr + reg);
break;
case 4:
*value = readl(mmcfg_virt_addr + reg);
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 value)
{
unsigned long flags;
u32 base;
if ((bus > 255) || (devfn > 255) || (reg > 4095))
return -EINVAL;
base = get_base_addr(seg, bus, devfn);
if (!base)
return pci_conf1_write(seg,bus,devfn,reg,len,value);
spin_lock_irqsave(&pci_config_lock, flags);
pci_exp_set_dev_base(base, bus, devfn);
switch (len) {
case 1:
writeb(value, mmcfg_virt_addr + reg);
break;
case 2:
writew(value, mmcfg_virt_addr + reg);
break;
case 4:
writel(value, mmcfg_virt_addr + reg);
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
int pci_conf1_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value)
{
unsigned long flags;
if (!value || (bus > 255) || (devfn > 255) || (reg > 255))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
switch (len) {
case 1:
*value = inb(0xCFC + (reg & 3));
break;
case 2:
*value = inw(0xCFC + (reg & 2));
break;
case 4:
*value = inl(0xCFC);
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
int pci_conf1_write(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 value)
{
unsigned long flags;
if ((bus > 255) || (devfn > 255) || (reg > 255))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
switch (len) {
case 1:
outb((u8)value, 0xCFC + (reg & 3));
break;
case 2:
outw((u16)value, 0xCFC + (reg & 2));
break;
case 4:
outl((u32)value, 0xCFC);
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
聯(lián)系客服