PCI(Peripheral Component Interconnect) 드라이버
버스는 전기적 인터페이스와 프로그래밍 인터페이스로 구성된다.
주로 현대 데스크탑 및 대형 컴퓨터에서는 PCI 버스 가 많이 사용된다.
PCI 장치가 시스템의 하드웨어를 찾고 접근하는 방법
특정 드라이버가 하드웨어를 탐지하고 접근할 수 있도록 지원
- 32비트 데이터 버스를 기본으로 사용하며, 64비트 확장도 포함되어 있음
- PCI 버스는 ISA보다 높은 클럭 속도로 더 나은 성능을 달성
- 플랫폼 독립성을 고려한 설계로, 다양한 프로세서 아키텍처(IA-32, Alpha, PowerPC, SPARC64, IA-64 등)에서 사용됨
- 특히 중요한 점은 PCI가 인터페이스 보드의 자동 감지 및 구성(auto-detection)을 지원한다는 점
- 이는 점퍼가 필요 없고 부팅 시 자동으로 설정되며, 드라이버는 탐색 없이 구성 정보를 얻어 초기화를 진행할 수 있음
PCI 주소 지정
- 각 PCI 주변 장치는 버스 번호, 장치 번호, 함수 번호로 식별됨
- 하나의 시스템은 최대 256개의 버스를 가질 수 있지만, 이 한계를 넘어서는 경우 Linux에서는 PCI 도메인 기능을 지원
- 각 PCI 도메인은 최대 256개의 버스를, 각 버스는 최대 32개의 장치를, 각 장치는 최대 8개의 기능을 포함할 수 있음
PCI 시스템 레이아웃
다음은 일반적인 PCI 시스템의 구조이다. RAM, CPU가 있고, 여러 브리지(Host Bridge, ISA Bridge, CardBus Bridge, PCI Bridge)가 연결되어 있다.
- Host Bridge는 CPU와 PCI 버스를 연결
- PCI Bridge는 여러 개의 PCI 버스를 서로 연결
- ISA Bridge는 ISA 장치와 연결
- CardBus Bridge는 노트북에서 사용되는 확장 슬롯(CardBus)와 연결
PCI 주소 포맷
- PCI 장치는 보통 버스 번호, 장치 번호, 기능 번호를 기준으로 식별됨
- 이 주소 값들은 일반적으로 16진수로 표시됨
- 예를 들어, /proc/bus/pci/devices 파일은 단일 16비트 필드로 주소를 표시하여 구문 분석과 정렬을 쉽게함
- 반면, /proc/bus/busnumber 파일은 주소를 세 가지 필드(버스, 장치, 기능)로 나눔
- lspci 명령을 사용하면 PCI 장치의 주소와 장치 종류를 확인할 수 있음
- lspci 명령에서 나오는 정보는 /proc 파일 시스템에서 가져옴
- 예를 들어, VGA 비디오 컨트롤러 주소 0x00a0는 0000:00:14.0로 해석
- 여기서 도메인(16비트), 버스(8비트), 장치(5비트), 기능(3비트)로 구성
$ lspci
00:00.0 Host bridge: Intel Corporation Device 191f (rev 07)
00:02.0 VGA compatible controller: Intel Corporation Device 1912 (rev 06)
00:14.0 USB controller: Intel Corporation Device a12f (rev 31)
00:14.2 Signal processing controller: Intel Corporation Device a131 (rev 31)
00:16.0 Communication controller: Intel Corporation Device a13a (rev 31)
00:17.0 SATA controller: Intel Corporation Device a102 (rev 31)
00:1c.0 PCI bridge: Intel Corporation Device a114 (rev f1)
00:1c.5 PCI bridge: Intel Corporation Device a115 (rev f1)
00:1d.0 PCI bridge: Intel Corporation Device a118 (rev f1)
00:1f.0 ISA bridge: Intel Corporation Device a143 (rev 31)
00:1f.2 Memory controller: Intel Corporation Device a121 (rev 31)
00:1f.3 Audio device: Intel Corporation Device a170 (rev 31)
00:1f.4 SMBus: Intel Corporation Device a123 (rev 31)
01:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (rev 0c)
02:00.0 PCI bridge: Integrated Technology Express, Inc. Device 8892 (rev 41)
03:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8169 PCI Gigabit Ethernet Controller (rev 10)
PCI 주소 공간
- PCI 장치는 세 가지 주소 공간을 사용
- 메모리 위치
- I/O 포트
- 구성 레지스터(Configuration Registers)
- 메모리와 I/O 주소는 동일한 PCI 버스에 있는 모든 장치가 동시에 접근할 수 있음
- 구성 공간은 지리적 주소 지정 방식을 사용하여 한 번에 하나의 장치만 접근하도록 함
메모리 및 I/O 공간 처리
- 드라이버 관점에서 메모리 및 I/O 영역은 일반적으로 readb, readw, readl 등으로 접근
- 구성 트랜잭션은 특정 장치의 구성 레지스터에 접근하는 방식으로 수행
- PCI 사양은 인터럽트 라인을 공유할 수 있도록 하여, 제한된 IRQ 라인을 가진 시스템에서도 여러 PCI 장치를 연결할 수 있게함
- 예를 들어, x86 시스템은 제한된 IRQ 라인을 가지지만, 각 인터럽트에 네 개의 핀을 연결할 수 있어 여러 PCI 인터페이스 보드를 처리할 수 있음
I/O 공간과 메모리 공간
- PCI 버스의 I/O 공간은 32비트 주소 버스를 사용하여 4GB의 I/O 포트를 지원
- 메모리 공간은 32비트 또는 64비트 주소를 사용할 수 있으며, 최신 플랫폼에서는 64비트를 지원
- 각 장치의 메모리와 I/O 주소가 재배치될 수 있으며, 이 과정은 충돌을 방지하기 위해 시스템 부팅 시 펌웨어가 처리
- 구성 공간에서 주소 정보를 읽은 후, Linux 드라이버는 장치에 안전하게 접근할 수 있음
구성 공간(Configuration Space)
**PCI 구성 공간 레지스터 맵:**
|--MSB LSB--|
| +03H | +02H | +01H | +00H |
00H | 디바이스 ID | 벤더 ID |
04H | 스테이터스 | 커맨드 |
08H | 클래스 코드 | revision ID |
0CH | 자기진단(BIST) | 헤더타입 | 레이턴시타이머 | 캐시라인크기 |
10H | 기준주소 레지스터 0 |
14H | 기준주소 레지스터 1 |
18H | 기준주소 레지스터 2 |
1CH | 기준주소 레지스터 3 |
20H | 기준주소 레지스터 4 |
24H | 기준주소 레지스터 5 |
28H | 예약 |
2CH | 예약 |
30H | 기준주소 레지스터 5 |
34H | 예약 |
38H | 예약 |
3CH | 최대 레이턴시 | 최소 그란트 | 인터럽트 핀 | 인터럽트 라인 |
- 각 장치 함수는 256바이트의 구성 공간을 가지며, PCI Express 장치는 4KB의 구성 공간을 가짐
- 구성 레지스터의 레이아웃은 표준화되어 있음
- 구성 공간의 첫 4바이트는 장치의 고유한 ID를 포함하고 있어, 드라이버가 이를 통해 장치를 식별할 수 있음
- PCI 인터페이스의 주요 혁신 중 하나는 구성 주소 공간의 존재
- 이를 통해 드라이버는 장치 탐색 과정에서 위험한 작업을 피하고 안전하게 장치에 접근할 수 있음
부팅 (Boot Time) 후 초기화
- PCI 장치 설정은 시스템 부팅 시 이루어짐
- 부팅 시점에서 장치가 설정되며, 드라이버는 장치의 구성 공간을 통해 장치를 식별하고 접근할 수 있음
PCI 장치 초기화
1. PCI 장치는 전원이 공급될 때 메모리와 I/O 포트가 비활성화된 상태로 시작한다.
- 이유는 PCI 버스에 연결된 장치들은 기본적으로 버스와 연결이 끊어진 상태이다.
- 이는 장치가 초기에는 동작하지 않는 상태임을 의미하며, 장치의 모든 기능이 비활성화되어 있다.
- 즉, 장치는 설정 트랜잭션에만 응답하며, 전원이 켜진 후에는 인터럽트 보고와 같은 장치 고유의 기능이 모두 비활성화된다.
- 하지만 구성 공간(Configuration Space), 즉 장치의 환경 설정 영역만은 시스템 프로세서(CPU)가 접근할 수 있다.
- 대부분의 마더보드는 BIOS, NVRAM 또는 PROM과 같은 PCI 지원 펌웨어를 갖추고 있으며, 이를 통해 설정 공간을 읽고 쓸 수 있게한다.
- 구성 공간은 운영체제나 CPU가 장치와 통신할 수 있도록 설정 값을 저장하는 곳
- 이 영역을 통해 장치에 관한 기본 정보(장치 ID, 벤더 ID 등)를 확인할 수 있다.
2. 운영체제가 장치 정보를 수집
- 운영체제는 부팅 과정에서 PCI 버스에 연결된 모든 장치의 구성 공간을 차례로 검사한다.
- 이때 각 장치가 필요로 하는 I/O 주소 공간과 인터럽트 번호와 같은 자원을 확인하고, 장치의 종류와 기능에 관한 기타 필요한 정보를 수집한다.
- 이를 통해 운영체제는 장치가 어떤 기능을 수행할 수 있는지 파악하게 된다.
3. 운영체제가 내부 정보 정리 및 자원 할당
- 운영체제는 수집한 정보를 바탕으로 각 PCI 장치를 관리하기 위한 내부적인 데이터 구조를 구성한다.
- 그리고 각 장치가 사용할 I/O 주소와 인터럽트 번호를 할당한다.
- 이렇게 할당된 자원은 시스템 내에서 다른 장치와 겹치지 않도록 관리된다.
4. 환경 설정 영역에 자원 정보 기록
- 운영체제는 할당한 I/O 주소와 인터럽트 번호를 각 장치의 구성 공간(Configuration Space)에 기록한다.
- 이 과정을 통해 장치가 운영체제에서 설정한 자원 정보를 알 수 있게된다.
5. 장치 활성화
- 프로세서가 I/O 주소와 인터럽트 번호를 장치의 구성 공간에 쓰는 순간, 장치는 설정된 I/O 주소와 인터럽트 번호를 사용하여 작동을 시작할 수 있다.
- 이때부터 프로세서는 해당 I/O 주소를 통해 장치에 접근할 수 있으며, 장치는 운영체제에서 할당된 인터럽트 번호를 사용해 인터럽트를 발생시킬 수 있다.
- 시스템 부팅 시 펌웨어 또는 Linux 커널이 PCI 주변 장치와의 설정 트랜잭션을 수행하여 각 주소 영역에 안전한 위치를 할당한다.
- 드라이버가 장치에 접근할 때는 이미 메모리와 I/O 영역이 프로세서 주소 공간에 매핑된 상태이다.
PCI 장치 정보 조회
- 사용자는 /proc/bus/pci/devices 및 /proc/bus/pci/* 파일을 통해 PCI 장치 목록과 구성 레지스터 정보를 확인할 수 있다.
$ ls /proc/bus/pci
00 01 02 03 devices
$ ls /proc/bus/pci/01
00.0
- /proc/bus/pci
- 커널이 제공하는 가상 파일 시스템으로, 시스템의 PCI 버스와 장치 정보를 가지고 있음
- 00, 01, 02, 03과 같은 디렉토리는 각각의 PCI 버스를 나타내며, 숫자는 버스 번호
- 예를 들어, /proc/bus/pci/01/00.0 파일은 버스 번호 01에 있는 장치 00.0에 대한 정보를 포함
- devices 파일은 시스템에 있는 모든 PCI 장치에 대한 요약 정보를 제공하는 파일
- sysfs 트리(/sys/bus/pci/devices/)에서 개별 PCI 장치 디렉터리를 찾아볼 수 있다.
$ ls /sys/bus/pci/devices/
0000:00:00.0 0000:00:02.0 0000:00:14.0 0000:00:14.2 0000:00:16.0 0000:00:17.0 0000:00:1c.0 0000:00:1c.5 0000:00:1d.0 0000:00:1f.0 0000:00:1f.2 0000:00:1f.3 0000:00:1f.4 0000:01:00.0 0000:02:00.0 0000:03:00.0
- /sys/bus/pci/devices
- sysfs를 통해 PCI 장치의 정보를 제공
- 0000:00:00.0, 0000:00:02.0 등은 PCI 장치를 식별하는 고유한 경로
- 0000은 PCI 도메인
- 00은 버스 번호
- 00.0은 장치 및 함수 번호
- 이 경로에서 각각의 장치 디렉토리는 해당 장치의 상태, 리소스 매핑, 구성 레지스터 등 다양한 정보를 담고 있는 파일들로 구성되어 있음
$ tree /sys/bus/pci/devices/0000:00:00.0
/sys/bus/pci/devices/0000:00:00.0
├── broken_parity_status
├── class
├── config
├── consistent_dma_mask_bits
├── d3cold_allowed
├── device
├── dma_mask_bits
├── enabled
├── irq
├── local_cpulist
├── local_cpus
├── modalias
├── msi_bus
├── numa_node
├── power
│ ├── async
│ ├── autosuspend_delay_ms
│ ├── control
│ ├── runtime_active_kids
│ ├── runtime_active_time
│ ├── runtime_enabled
│ ├── runtime_status
│ ├── runtime_suspended_time
│ └── runtime_usage
├── remove
├── rescan
├── resource
├── subsystem -> ../../../bus/pci
├── subsystem_device
├── subsystem_vendor
├── uevent
└── vendor
- config: PCI 구성 정보를 담고 있는 바이너리 파일로, /proc/bus/pci/*에서 제공하는 내용과 동일
- vendor, device, subsystem_device, subsystem_vendor, class: 장치의 특정 값을 나타내는 파일들로, 각각의 PCI 장치에 대해 고유한 정보를 제공
- irq: 현재 PCI 장치에 할당된 IRQ
- resource: 이 장치가 할당받은 메모리 리소스
PCI 장치의 구성 레지스터(Configuration Registers)
- 모든 PCI 장치는 최소 256바이트의 구성 공간을 가지며, 이 중 처음 64바이트는 표준화되어 있다. 나머지 부분은 장치에 따라 달라짐
- 필수 레지스터(Required Register)는 모든 PCI 장치에서 반드시 의미 있는 값을 가져야 하며, 선택적 레지스터(Optional Register)는 장치의 기능에 따라 달라짐
구성 레지스터 필드 설명
- Vendor ID와 Device ID: 장치 제조사와 장치 고유 ID를 식별
- Command Register와 Status Register: 장치 제어와 상태 정보
- Base Address Registers: 장치의 메모리 및 I/O 주소를 지정
- Interrupt Line와 Interrupt Pin: 장치에 할당된 인터럽트 정보를 포함
- Subsystem Vendor ID와 Subsystem Device ID: 서브시스템(예: 특정 제조사의 하드웨어)에 대한 추가 정보를 제공
레지스터의 엔디안 형식
- PCI 레지스터는 항상 리틀 엔디안 형식을 사용 (여러 플랫폼에서 사용할 경우 바이트 순서를 주의해야함)
- Linux 개발자는 바이트 순서 변환 문제를 해결하기 위해 asm/byteorder.h에 정의된 함수를 사용할 수 있음
레지스터 설명
- vendorID: 16비트 레지스터로, 하드웨어 제조사를 식별한다. 예를 들어, Intel의 모든 장치는 0x8086이라는 고유 번호를 가진다. 모든 제조사는 PCI Special Interest Group에 의해 관리되는 전역 레지스트리를 통해 고유 번호를 부여받는다.
- deviceID: 제조사가 지정한 16비트 레지스터로, 특정 장치를 식별한다. 이 값은 vendorID와 함께 사용되어 장치의 고유 ID가된다.
- class: 16비트 값으로, 장치의 기본 그룹 또는 클래스(예: 네트워크, 통신 등)를 식별한다. 드라이버는 이 값을 사용하여 유사한 기능의 장치들을 식별할 수 있다.
하위 시스템 식별자
- subsystem vendorID와 subsystem deviceID: 특정 장치의 세부 구분을 위해 사용한다.
- 예를 들어, 칩셋이 여러 역할을 할 수 있는 경우 이 필드를 통해 특정 장치로 식별할 수 있다.
pci_device_id 배열을 정의하여, 드라이버가 지원하는 장치 유형을 설정한다.
여기에는 PCI_DEVICE_CLASS 매크로를 사용하여 특정 클래스의 USB 2.0 EHCI 컨트롤러를 처리하는 드라이버 설정이 포함되어 있다.
/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids [] = { {
/* handle any USB 2.0 EHCI controller */
PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0),
}, {
PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_HOST),
},
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, pci_ids);
struct pci_device_id
struct pci_device_id {
__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* Data private to the driver */
};
- vendor 및 device:
- 장치의 제조사와 장치 ID를 지정
- 드라이버가 모든 제조사 또는 장치를 처리할 수 있는 경우, PCI_ANY_ID 값을 사용할 수 있음
- subvendor 및 subdevice:
- 서브시스템 제조사 및 장치 ID를 지정
- 모든 서브시스템 ID를 처리할 수 있는 경우 PCI_ANY_ID를 사용할 수 있음
- class 및 class_mask:
- 드라이버가 지원하는 PCI 클래스 유형을 지정
- 예를 들어, VGA 컨트롤러와 같은 특정 장치 클래스가 PCI 사양에 정의되어 있음
- 모든 서브시스템 ID를 처리할 수 있는 경우 PCI_ANY_ID를 사용할 수 있음
- driver_data
- 장치를 매칭하는 데 사용되지 않지만, 드라이버가 다양한 장치를 구분하고자 할 때 사용할 수 있는 정보 저장 용도로 사용
PCI_DEVICE(vendor, device) 매크로
- 특정 vendor 및 device ID에 맞는 struct pci_device_id를 생성
- 이 매크로는 subvendor와 subdevice 필드를 PCI_ANY_ID로 설정
PCI_DEVICE_CLASS(device_class, device_class_mask) 매크로
- 특정 PCI 클래스에 맞는 struct pci_device_id를 생성
pci_device_id
- static struct pci_device_id i810_ids[] 배열은 Intel 장치에 대한 여러 pci_device_id 구조체를 정의
- 마지막 값으로 { 0, }를 설정하여 배열이 끝났음을 표시
static struct pci_device_id i810_ids[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810E_IG) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG) },
{ 0, },
};
References
'System Programming > Linux Device Driver' 카테고리의 다른 글
Netlink Sockets #1 내부 아키텍쳐, 메세지 포맷 (0) | 2024.10.27 |
---|---|
Linux Device Driver 기초 #5 문자 드라이버 (0) | 2024.10.27 |
Linux Device Driver 기초 #4 system daemon과 라이브러리 개발 (0) | 2024.10.20 |
Linux Device Driver 기초 #3 Linux Device Driver 추가하기 (0) | 2024.10.13 |
Linux Inside #1 Booting: Bootloader에서 Kernel까지 (0) | 2024.10.01 |