Colors of Ray+Hue'

MMIO in PCIe

Linux Kernel2016. 8. 24. 06:11

MMIO in PCIe: Device has CPU accessible memory

Abstract
디바이스 드라이버 모듈의 .init는 먼저 디바이스 드라이버를 등록하는데, name, ID (vendor, device), prove, remove 등을 등록한다. IDtable 에서 vendor ID 와 device ID는 규격에 의해 제품마다 정해져 있기 때문에, 드라이버 작성시 반드시 매칭을 해야 한다 (참고:http://pcidatabase.com/). 또한 lspci를 통해 ID를 확인 할 수 있다. 이후 정상적으로 디바이스가 OS에 의해 인식이 되면, 먼저 .probe가 호출된다. .probe의 콜백이 호출되지 않는 다면 디바이스는 인식되지 않은 것이다. .probe의 콜백의 주요한 역할은 실제 드라이버가 구동하기 전 해당 디바이스의 메모리를 사용할 수 있도록 준비 상태를 만드는 것이며, 이는 IOremap 에 의해 완성된다. 

 static struct pci_device_id test_ids[] = {

{ PCI_DEVICE(0x vendorID, 0x deviceID) },
{ 0 }
};

static struct pci_driver test_driver = {
.name = "test",
.id_table = test_ids,
.probe = test_probe,
.remove = test_remove
};


 static int __init test_init (void){

rc = pci_register_driver(&test_driver);

}

static void __exit test_exit (void){
pci_unregister_driver(&test_driver);
}

.probe에서는 가장 먼저 디바이스를 enable 한다. 정상적으로 디바이스가 구동 가능한 상태가 되면, 매핑을 하고자 하는 영역(bar)을 선택하여, 어떤 타입의 매핑(IORESOURCE_MEM)을 할것인지를 결정한다. 해당 디바이스의 시작 주소 (PFN)와 크기(PFN)을 선택한 후, ioremap을 통해 매핑된 영역의 시작 가상 주소 (VA)을 얻어내어, CPU를 통한 직접 읽기가 가능한 메모리 영역을 엑세스 할 수 있다. nopage method 와는 달리 이의 방법은 연속된 물리적 주소에만 가능하고, 당연히 page fault를 발생시키기 않는다. kernel page table은 해당 VMA에 모든 PFN을 홀드하고 있는 상태이기 때문이다.



 static int test_probe (struct pci_dev *pdev, const struct pci_device_id *id)


{


pci_enable_device_mem(pdev)


pci_select_bars(pdev,IORESOURCE_MEM)


start = pci_resource_start(pdev,0)


size = pci_resource_len(pdev,0)


io_va = ioremap/_wt/_nocache(start,size)


...






Accessing the I/O and Memory Spaces

Original Source: http://www.oreilly.com/openbook/linuxdrive3/book/ch12.pdf

A PCI device implements up to six I/O address regions. Each region consists of either memory or I/O locations. Most devices implement their I/O registers in memory regions, because it’s generally a saner approach (as explained in the section “I/O Ports and I/O Memory,” in Chapter 9). However, unlike normal memory, I/O registers should not be cached by the CPU because each access can have side effects. The PCI device that implements I/O registers as a memory region marks the difference by setting a “memory-is-prefetchable” bit in its configuration register.* If the memory region is marked as prefetchable, the CPU can cache its contents and do all sorts of optimization with it; nonprefetchable memory access, on the other hand, can’t be optimized because each access can have side effects, just as with I/O ports. Peripherals that map their control registers to a memory address range declare that range as nonprefetchable, whereas something like video memory on PCI boards is prefetchable.

In this section, we use the word region to refer to a generic I/O address space that is memory-mapped or port-mapped. An interface board reports the size and current location of its regions using configuration registers—the six 32-bit registers shown in Figure 12-2, whose symbolic names are PCI_BASE_ADDRESS_0 through PCI_BASE_ADDRESS_5. Since the I/O space defined by PCI is a 32-bit address space, it makes sense to use the same configuration interface for memory and I/O. If the device uses a 64-bit address bus, it can declare regions in the 64-bit memory space by using two consecutive PCI_BASE_ADDRESS registers for each region, low bits first. It is possible for one device to offer both 32-bit regions and 64-bit regions.

In the kernel, the I/O regions of PCI devices have been integrated into the generic resource management. For this reason, you don’t need to access the configuration variables in order to know where your device is mapped in memory or I/O space. The preferred interface for getting region information consists of the following functions:

커널에서 PCI 장치의 IO 영역은 범용 자원 관리자로 통합되었다. 따라서, 메모리나 입출력 공간의 어디에 장치가 매핑되었는지를 알기 위해 configuration variable을 엑세스 할 필요가 없다. 그냥 매핑이 일단 이루어지고, 연속된 해당 영역에 대해 시작과 크기 주소공간만 알면, 엑세스가 가능하다.

unsigned long pci_resource_start(struct pci_dev *dev, int bar);

The function returns the first address (memory address or I/O port number) associated with one of the six PCI I/O regions. The region is selected by the integer bar (the base address register), ranging from 0–5 (inclusive).

unsigned long pci_resource_end(struct pci_dev *dev, int bar);

The function returns the last address that is part of the I/O region number bar. Note that this is the last usable address, not the first address after the region.unsigned long pci_resource_flags(struct pci_dev *dev, int bar);

This function returns the flags associated with this resource. Resource flags are used to define some features of the individual resource. For PCI resources associated with PCI I/O regions, the information is extracted from the base address registers, but can come from elsewhere for resources not associated with PCI devices.

All resource flags are defined in <linux/ioport.h>; the most important are:

IORESOURCE_IO

IORESOURCE_MEM

If the associated I/O region exists, one and only one of these flags is set.

IORESOURCE_PREFETCH

IORESOURCE_READONLY

These flags tell whether a memory region is prefetchable and/or write protected. The latter flag is never set for PCI resources. By making use of the pci_resource_ functions, a device driver can completely ignore the underlying PCI registers, since the system already used them to structure resource information.

pci_resource 함수는 디바이스 드라이버가 PIC 레지스터를 무시할 수 있도록 해주는데, 시스템이 리소스 정보를 구조화 하는데 이미 그것들을 사용하고 있기 때문이다.

'Linux Kernel' 카테고리의 다른 글

glibc Malloc  (0) 2016.08.27
LD_PRELOAD example  (0) 2016.08.27
JEMALLOC: A Scalable Concurrent malloc(3) Implementation for FreeBSD  (0) 2016.08.18
malloc 소개  (0) 2016.08.18
posix_fallocate  (0) 2016.08.16