1. 개념을 알고 가자!
PCI configuration space
는 PCI, PCI-X, PCI Express의 버스에 삽입된 카드의 configuration을 자동으로 설정하는 기본 방법입니다. 장치를 사용하려면 장치가 어떤 것이고(identification), 어떤 장치와 어떻게 통신해야되는지(protocol)를 알아야 하는데, PCI 버스에서는 장치를 인식하고, 장치의 기본적인 정보를 얻기 위하여 PCI configuration space를 사용합니다. 쉽게 말하면 Device ID, Vendor ID, Class code 와 같은 다양한 정보들이 담겨있는 구조체라고 볼 수 있습니다.
예를 들어, 컴퓨터에 담긴 내 ssd를 검색한다고 가정해보겠습니다. 이 ssd의 configuration space를 읽어오려면 어떻게 해야 할까요?? 뭔가 효율적인 방법이 있을 것 같지만, 모든 장치를 검색해야 한다고 합니다. PCI 장치들은 모두 BDF(Bus, Device, Fuction) 이라는 번호로 구성된 식별 번호를 가지고 있으며, BDF는 PCI Slot에 따라 부여됩니다. 이 BDF를 모두 스캔하면 컴퓨터에 달린 모든 디바이스 정보를 알 수 있습니다.
밑에 자세히 나오겠지만 BDF는 16bit = 2Byte 로 구성되어 있고 이는 총 65,536개입니다. 이 BDF들을 모두 스캔하면 컴퓨터에 달린 장치들의 정보를 알 수 있습니다. BDF를 모두 스캔하는 것을 enumeration
이라고 합니다. 리눅스에서는 이러한 정보를 /drivers 폴더 안에 있는 디바이스 드라이버에 있는 코드와 함께 확인하면 이해가 더 잘 될 수 있습니다.
조금 더 자세하고 디테일한 설명은 아래에서 확인할 수 있습니다.
2. Overview
PCI 장치들은 configuration space
라고 불리는 레지스터들의 집합을 가지고 있습니다. PCIe 에서는 extended configuration space
라고 합니다. configuration space 레지스터들은 메모리 공간에 할당되어있습니다. device driver와 diagnostic software는 configuration space에 접근할 수 있어야됩니다. OS는 장치의 configuration space에 접근할 수 있는 API를 사용합니다.
OS가 memory mapped configuration space에 접근할 수 있는 방법이나 API가 없는 경우에는, driver나 diagonostic software가 configuration space에 os의 기본 접근 방법과 호환되는 방식으로 접근해야 됩니다. 모든 시스템에서, device driver는 OS에 의하여 제공되는 장치의 configuration space에 접근 가능한 API 사용이 권장됩니다.
3. Technical information
PCI local bus가 다른 I/O 아키텍처에 비해 크게 개선된 것 중 하나는 configuration mechanism입니다. normal memory-mapped 와 I/O port space 이외에도 버스 위의 각각의 device function은 configuration space를 가지고 있습니다. configuration space는 256바이트 길이이며, 주소를 지정할 수 있는 8비트 PCI bus, 5비트 장치, 3비트 function 번호를 가지고 있습니다. 이는 BDF(Bus/Device/Function) 라고도 합니다.
256개의 Bus 위에, 각각 32개의 Device와, 각각 8개의 function을 지원합니다. 단일 PCI 확장 카드는 장치로서 응답할 수 있으며, 최소한 기능 번호 0을 구현해야합니다. 첫 configuration space 64바이트는 표준화되어있습니다. 나머지는 벤더 정의에 의하여 결정됩니다. 아래와 같이 생각하면 됩니다.
- Bus : 끊어지지 않은 선들의 한 집합. 각 Bus 마다 하나의 번호가 부여됨
- Device : 한 Bus에 연결된 여러 장치가 있는 경우에 부여되는 번호
- Function : 각 디바이스가 구현할 수 있는 기능의 개수
더 많은 configuration space의 부분이 존재하는 요소들과 충돌하지 않게 표준화하기 위해서, PCI configuration space의 첫 192바이트에 capabilities 항목들이 있습니다. 각각의 capability들은 1바이트를 이용하여 그 capability가 무엇인지 나타내고, 1바이트는 다음 capability를 나타냅니다. 추가적인 바이트의 개수는 capability ID에 따라 달렸습니다. 만약 capability들이 사용 중이라면, Status register에 있는 bit가 set되고 링크드리스트 형태로 된 capability의 가장 첫번째 요소의 포인터는 표준화된 레지스터(Standardized Registers)에 있는 Cap.pointer 레지스터에 있습니다.
PCI-X는 확장된 configuration space로 4096바이트까지 지원됩니다. 확장된 configuration space의 표준으로 정해진 부분은 오직 처음 4바이트가 0x100 이라는 것입니다. 이는 확장된 configuration space라는 것을 의미합니다. 확장된 configuration space는 보통의 capability과 매우 유사합니다. 다만 확장된 configuration space는 8바이트가 아니라 12비트를 사용하여 4바이트 버전 번호와 16비트의 capability ID를 갖는 모든 바이트를 참조할 수 있는 것은 예외적으로 다른 점입니다.
4. Standardized registers(표준화된 레지스터들)
PCI Type 0 Configuration Space Header의 Standard register
- Device ID, Vendor ID 레지스터 : device를 식별하고, 흔히 PCI ID 라고 불립니다. 16비트 Vendor ID는 PCI-SIG에 의하여 할당되어있습니다. 16비트 Device ID는 Vendor 사에 의하여 결정됩니다.
- Status 레지스터 : 지원되는 기능과 특정 종류의 오류가 발생했는지 여부를 보고하는데 사용됩니다.
- Command 레지스터 : 개별적으로 활성화 or 비활성화 할 수 있는 기능을 가진 bitmask를 가지고 있습니다. Interrupt Disable, Memory Write and Invalidate, Bus Master Enable과 같은 옵션들을 설정할 수 있습니다.
- Class Code 레지스터 : PCI 장치의 분류를 나타냅니다. Storage인지, USB controller인지, Network Card인지와 같은 정보가 저장됩니다. 이 정보만 봐도 장치의 종류를 알 수 있습니다.
- Header Type 레지스터 : 헤더의 나머지 48바이트(64-16)의 다른 레이아웃을 결정합니다. 위의 그림에서 보면 10h(16)부터 아래의 레지스터 구조를 결정한다고 보면 됩니다.
- Type 1 : Root Complex, Switches, Bridges
- Type 0 : endpoint
- Cache Line Size 레지스터 : device가 memory-write-and-invalidate transaction을 사용할 수 있다고 말하기 전에, 프로그램되어야 합니다. 이 레지스터는 보통 CPU’s 캐시 라인 사이즈와 매칭되야 합니다. 하지만, 올바른 설정은 시스템에 의하여 결정됩니다. 이 레지스터는 PCIe에는 적용되지 않습니다.
- Base Address Register : PCI configuration space는 장치를 찾을 때만 이용하는 공간으로 매우 용량이 작습니다. 일반적으로 Device driver는 이 공간보다 용량이 더 크기 때문에 메모리에 적재합니다. 따라서, 사용할 공간을 따로 잡아서 디바이스와 OS가 어디를 사용할지 공유를 해야 하는데, Base Address Register에 이러한 내용을 입력합니다.
- Subsystem ID(SSID) & Subsystem Vendor ID(SVID) : 이 레지스터는 구체적인 모델을 구분합니다. Vendor ID가 chipset 제조사의 것이고, Subsystem Vendor ID는 card 제조사의 것입니다. Subsystem ID는 Subsystem 제조사에 의하여 device ID와 동일한 번호 공간에서 할당됩니다. (Subsystem 제조사가 가지고 있는 device ID에서 할당된다는 의미로 파악) 예를 들어, 무선 네트워크 카드의 경우 chipset 제조사는 Broadcom이 될 수 있고, 카드 제조업체는 Netgear가 될 수 있습니다. 일반적으로 Vendor ID - Device ID 결합은 host가 장치를 다루기 위하여 로드한 driver로 지정합니다.
5. Bus enumeration
PCI 장치의 주소를 지정하려면 시스템의 I/O port address space나 memory-mapped address space에 mapping되어서 활성화되어야 합니다. 시스템 펌웨어, 디바이스 드라이버, 또는 OS는 Base Address Registers (=BARs) 를 program 합니다. 이 때, PCI controller에게 configuration command를 쓰는데, 장치에게 address mapping을 알려주기 위해서 이루어집니다. 모든 PCI 장치들이 시스템이 reset 되는 동안에는 비활성화된 상태로 있기 때문에, 장치들 자신에게 할당된 주소를 가질 수 없습니다. BIOS나 OS에서는 IDSEL(Initialization Device Select) 신호를 보내어 PCI Controller를 통해 geographically(지리적으로, 예 : 마더 보드의 첫 번째 PCI 슬롯, 두 번째 PCI 슬롯, 세 번째 PCI 슬롯…..)하게 주소를 부여합니다.
BIOS나 OS에서 어떤 PCI 슬롯에 장치가 설치되어 있는지, 장치가 구현하는 function은 무엇인지에 대하여 알기 위해서는 Bus enumberation이 필요합니다. Bus enumeration은 장치의 function #0에서 버스 번호와 장치 번호의 조합에 대한 Vendor ID & Device ID (VID/DID)를 읽으려고 시도합니다. 새 Bridge가 감지되면 새로 버스 번호가 정의되고, enumeration이 이루어집니다. 새로운 장치가 감지되지 않으면 다른 요소를 탐색합니다.
장치의 function #0에서 응답이 없는 경우에는 VID / DID 값이 FFFFFFFF로 리턴되고, bus/device number/function(BDF) 이 없습니다. 위의 값이 아닌 경우에는 장치 인식에 성공했다고 보고, BIOS나 OS에서 장치의 BAR configuration register(BAR)에 memory-mapped and I/O Port addresses를 프로그램합니다. 이 주소는 시스템의 전원이 꺼지지 않는 이상 유효합니다. 전원을 끄면 이런 설정들이 모두 사라지고, 다음 전원이 들어올 때 재설정을 해야합니다. 이러한 과정은 완전히 자동화되었고, 플러그 앤 플레이(Plug and Play)가 구현되었습니다.
위와 같은 방식으로 DFS 알고리즘을 이용하여 모든 연결된 장치들을 찾는다고 생각하면 이해가 빠릅니다. non-bridge PCI device function는 최대 6개의 BAR를 구현할 수 있으며, 각 BAR은 I/O 포트의 다른 주소 및 메모리 매핑 된 주소 공간에 응답할 수 있습니다. PCI 장치에는 드라이버 코드나 configuration 정보를 가지고 있는 option ROM이 있을 수도 있습니다.
출처
- https://en.wikipedia.org/wiki/PCI_configuration_space
- http://egloos.zum.com/nimhaplz/v/5314763
- http://wikigu.blogspot.com/2016/11/pcie-enumeration.html