天天看點

1.4 x86 CPU位址空間配置設定和寄存器通路1、基本概念2、x86 CPU位址空間配置設定3、x86内部寄存器的通路

1、基本概念

cpu位址空間和pci位址空間是兩個常用的比較容易混淆的概念,特别是其中不同系列的cpu的實作還各不相同:x86系列cpu位址空間和pci位址空間是重合的,即為同一空間;而非x86 cpu的cpu位址空間和pci位址空間為兩個獨立的空間。

也許是因為pci總線是intel發明的,是以x86内部總線和pci總線實作是一緻的,但非x86系列的cpu實作pci總線需要用到總線轉換。

1.1、非x86系列的CPU空間和PCI空間互訪(以PowerPC為例):

1.4 x86 CPU位址空間配置設定和寄存器通路1、基本概念2、x86 CPU位址空間配置設定3、x86内部寄存器的通路
1.4 x86 CPU位址空間配置設定和寄存器通路1、基本概念2、x86 CPU位址空間配置設定3、x86内部寄存器的通路

因為PCI位址空間和CPU位址空間是獨立的,是以PCI裝置和CPU之間的互訪需要用到位址轉換。涉及到兩個概念,inbound和outbound。

Inbound視窗:即将CPU的一段位址空間映射到PCI位址空間上,供PCI裝置通路CPU空間所用,即inbound視窗。因為對cpu來說pci通路就是inbound通路。

Outbound視窗:即将PCI的一段位址空間映射到CPU位址空間上,供CPU通路PCI空間所用,即Outbound視窗。因為對cpu來說通路pci空間就是outbound通路。

1.2、x86系列的CPU空間和PCI空間互訪:

1.4 x86 CPU位址空間配置設定和寄存器通路1、基本概念2、x86 CPU位址空間配置設定3、x86内部寄存器的通路

對x86 cpu來說,就沒有了cpu位址空間和pci位址空間之分,這兩個空間是重合的。是以pci外設和cpu之間的互相通路就不需要設定位址轉換視窗:

Inbound通路:pci裝置通路cpu空間,這個不需要設定inbound視窗,對外設來說所有的位址空間都是可以通路的。

Outbound通路:cpu通路pci外設,這個也還需要保留一段cpu位址給外設,讓外設的視窗能映射到這段空間來,即MMIO空間。有點類似于Outbound視窗,但是不需要位址轉換。

1.3、x86系列的IO空間和CFG空間:

1.4 x86 CPU位址空間配置設定和寄存器通路1、基本概念2、x86 CPU位址空間配置設定3、x86内部寄存器的通路

由于x86 cpu對pci标準的完美支援,是以x86 cpu還支援pci的io空間通路和pci的配置空間通路:

io空間:pci标準規定的io位址空間最大可以有4G,但是x86隻實作了64k的大小。io空間用來實作早期相容的外設寄存器的通路(IO端口),和用來映射pci外設的io空間。

配置空間:用來通路pci總線裝置的配置空間,而x86也把内部的寄存器組織成虛拟的pci裝置,使用通路pci配置寄存器的方式來通路内部寄存器。

通路配置空間的方法有兩種:一是通過CF8/CFC io端口的間接通路來通路配置空間;二是通過mmcfg方式,把配置空間映射到memory空間來通路。對每個配置空間來說,CF8/CFC方式隻能通路傳統的pci配置空間256位元組,而mmcfg通路方式,能通路pcie的整個配置空間4k。

2、x86 CPU位址空間配置設定

本節以系統的橋片為例,來說明x86系列cpu的位址空間配置設定。x86 其他系列cpu的位址空間配置設定類似。

x86 xeon系列cpu 在32位系統下面,通過PAE(Physical address Extension)機制可以通路到36位的位址,即最大64G的空間。

1.4 x86 CPU位址空間配置設定和寄存器通路1、基本概念2、x86 CPU位址空間配置設定3、x86内部寄存器的通路

2.1、0-1M 相容空間:

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顯示模式直接往這段顯存寫資料,就可以顯示。現在估計隻有bios階段使用這種顯示方式,系統起來後會開啟更進階的顯示卡顯示模式。 2、被顯存位址覆寫的這一塊128K大小的記憶體,可以被利用起來當做SMM記憶體。SMM是CPU一種等級最高的管理模式,是以它的記憶體在正常下不可以被通路。 1、PCI在支援VGA顯示時,有個VGAEN功能,比較特殊,值得關注。VGA顯示卡裝置不需要配置正常的pci bar寄存器位址,而隻需要使能顯示卡所挂在PCI-PCI橋裝置的配置寄存器0x3E bit 3(VGA Enable),顯示卡就會響應專為VGA保留的固定pci memory位址(A0000-BFFFF)和pci io位址(03c0-03df)。 2、什麼是SMM模式? SMM是System Management Mode系統管理模式的縮寫。從Intel 386SL開始,此後的x86架構微處理器中都開始支援這個模式。在這個模式中,所有正常執行的軟體,包括作業系統都已經暫停運作。隻有特别的單獨軟體,具備高特權模式的軟體才能運作。通常這些軟體都是一些固件程式或者是硬體輔助調試器。 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(如硬碟、網卡..)。 這一段區域,是OptionRom和BIOS區域覆寫了原RAM區域。由于RAM的通路速度遠遠快于這些固件的通路速度,是以通常的做法是把固件中的内容拷貝到相同位址的RAM中,然後再使能RAM而屏蔽原有的固件映射。 通路BIOS和OptionRom内容和位址都沒有改變,但是速度卻加快了。這種做法就叫ROM Shadowing
D0000-DFFFF 832 - 896 kB Optional Adapter ROM BIOS or RAM UMBs 這一段區域也是來存放裝置的OptionRom。如果沒有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就跳到該區域

2.2、1M以上的memory位址空間:

           

1M-TOLM 低于4G的正常記憶體 這一段的記憶體就是低于4G的可用的記憶體,其中有兩段區域比較特殊。 1、15 MB - 16 MB Window (ISA Hole)傳統的ISA黑洞,現在基本不支援。 2、Extended SMRAM Space (TSEG)擴充SMM記憶體。前面已經有VGA RAM覆寫的128k記憶體可做SMM記憶體使用,系統還允許配置設定更多的SMM記憶體。 “Coherency Protocol”Intel 橋片通過同步協定保證所有對記憶體通路的一緻性。
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。通過這個技術可将重疊得部分的記憶體位址印射到4g以上的位址上去,這個功能是由硬體決定的。
(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、一些直接通路的cpu寄存器; 3、Interrupt、I/O APIC區域;
4G - ram_size 高于4G的正常記憶體 Coherency Protocol”Intel 橋片通過同步協定保證所有對記憶體通路的一緻性。
ram_size - high MMIO 在高端記憶體之上一直到CPU支援的最大空間,都可以用來映射64bit的pci memory外設位址

2.3、x86 io位址空間:

1.4 x86 CPU位址空間配置設定和寄存器通路1、基本概念2、x86 CPU位址空間配置設定3、x86内部寄存器的通路

x86隻實作64k大小的io空間,其中低4k是相容的io空間用做專門用途,4k以上的io位址空間可以配置設定給外部裝置使用。

2.4、指令操作:

在linux下通過以下指令檢視cpu的位址空間配置設定:

  • cat /proc/iomem 檢視cpu的pci memory空間配置設定
  • cat /proc/ioports 檢視cpu的pci io空間配置設定

3、x86内部寄存器的通路

除了一些是需要直接在memory空間通路的寄存器。x86把大部分内部的寄存器組織成虛拟的pci裝置,使用通路pci配置寄存器的方式來通路内部寄存器。

通路配置空間的方法有兩種:

  • 一是通過CF8/CFC io端口的間接通路來通路配置空間;
  • 二是通過mmcfg方式,把配置空間映射到memory空間來通路。對每個配置空間來說,CF8/CFC方式隻能通路傳統的pci配置空間256位元組,而mmcfg通路方式,能通路pcie的整個配置空間4k。

linux核心态實作pci配置空間通路的函數有以下:

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);
}
           

這些函數的内部實作,會判斷cpu是否進行了mmcfg映射。如果已經進行了mmcfg映射,則使用mmcfg模式通路;如果沒有實作mmcfg,則使用CF8/CFC方式通路。

具體的實作過程如下:

  • 1、這些函數的定義在driver/pci/access.c,可以看到其中的關鍵是bus->ops指針:
#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 = ;                           \
    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, )
PCI_OP_READ(word, u16, )
PCI_OP_READ(dword, u32, )
PCI_OP_WRITE(byte, u8, )
PCI_OP_WRITE(word, u16, )
PCI_OP_WRITE(dword, u32, )
           
  • 2、bus->ops的實作由arch/i386/pci/common.c中的pci_root_ops結構提供,pci_root_ops結構實際調用的是raw_pci_ops結構:
static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
{
    return raw_pci_ops->read(, 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(, bus->number, devfn, where, size, value);
}

struct pci_ops pci_root_ops = {
    .read = pci_read,
    .write = pci_write,
};
           
  • 3、raw_pci_ops的mmcfg模式實作在arch/i386/pci/mmconfig.c中定義:
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 > ) || (devfn > ) || (reg > ))
        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 :
        *value = readb(mmcfg_virt_addr + reg);
        break;
    case :
        *value = readw(mmcfg_virt_addr + reg);
        break;
    case :
        *value = readl(mmcfg_virt_addr + reg);
        break;
    }

    spin_unlock_irqrestore(&pci_config_lock, flags);

    return ;
}

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 > ) || (devfn > ) || (reg > )) 
        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 :
        writeb(value, mmcfg_virt_addr + reg);
        break;
    case :
        writew(value, mmcfg_virt_addr + reg);
        break;
    case :
        writel(value, mmcfg_virt_addr + reg);
        break;
    }

    spin_unlock_irqrestore(&pci_config_lock, flags);

    return ;
}
           
  • 4、raw_pci_ops的CF8/CFC方式通路模式實作在arch/i386/pci/mmconfig.c中定義:
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 > ) || (devfn > ) || (reg > ))
        return -EINVAL;

    spin_lock_irqsave(&pci_config_lock, flags);

    outl(PCI_CONF1_ADDRESS(bus, devfn, reg), );

    switch (len) {
    case :
        *value = inb( + (reg & ));
        break;
    case :
        *value = inw( + (reg & ));
        break;
    case :
        *value = inl();
        break;
    }

    spin_unlock_irqrestore(&pci_config_lock, flags);

    return ;
}

int pci_conf1_write(unsigned int seg, unsigned int bus,
               unsigned int devfn, int reg, int len, u32 value)
{
    unsigned long flags;

    if ((bus > ) || (devfn > ) || (reg > )) 
        return -EINVAL;

    spin_lock_irqsave(&pci_config_lock, flags);

    outl(PCI_CONF1_ADDRESS(bus, devfn, reg), );

    switch (len) {
    case :
        outb((u8)value,  + (reg & ));
        break;
    case :
        outw((u16)value,  + (reg & ));
        break;
    case :
        outl((u32)value, );
        break;
    }

    spin_unlock_irqrestore(&pci_config_lock, flags);

    return ;
}
           

繼續閱讀