DMA transfer to a slave PCI device in Linux - c

I am a bit confused regarding DMA transfers with a PCIe device.
Say, for example, I have a slave PCIe device, and I want to transfer a block of data from the device to the RAM, using a DMA transaction. Note that the device is slave, and does not have a DMA "machine" on it.
I know I need to obtain a DMA-able buffer in RAM (either by allocating a coherent one, or by mapping a page) first.
But what's next? what's the API to start a DMA transfer of N bytes from address S to address D?
Can modern systems issue a DMA transfer to/from a slave pci device? if so, what is the Linux API for that?
As explained here:
[ISA]
In the original IBM PC, there was only one Intel 8237 DMA controller [...]
A PCI architecture has no central DMA controller, unlike ISA. Instead, any PCI component can request control of the bus ("become the bus master") and request to read from and write to system memory
The PCI bus does not have a "central" DMA controller - instead, each device can be a DMA "controller".

First of all, there are no slaves and slave holders inside modern PC. There is south bridge (in PCI) or Root Complex (root of PCI-express device tree) and there are some other PCI/PCIe actors, like bridges, soldered chips, plugged cards, hardware debuggers etc. I'll assume that you are asking about plugged card or some other peripheral device, like soldered Sound Card or Ethernet chip.
According to this detailed description of "Transaction Layer Packet" (TLP, "PCIe’s uppermost layer"), there is "Bus Mastership (DMA)":
On PCIe, it’s significantly less exotic. ... anyone on the bus can send read and write TLPs on the bus, exactly like the Root Complex. This allows the peripheral to access the CPU’s memory directly (DMA) or exchange TLPs with peer peripherals (to the extent that the switching entities support that).
Also, there is some benefits of DMA capability from plugged devices: DMA attack. And PCIe is listed as capable of initiating DMA transfer:
Systems may be vulnerable to a DMA attack by an external device if they have a FireWire, ExpressCard, Thunderbolt, or other expansion port that, like PCI and PCI-Express in general, hooks up attached devices directly to the physical address space.
I think, there is no universal API for programming DMA transfers that are initiated from the peripheral device itself. This depends on the what the device is, when the DMA should be started and what will be sent.

Related

How does serial console communication work in a microcontroller?

My doubt is regarding the physical layer of the communication, I know from practice that for using for example teraterm to communicate with a MCU is simply enabling and configuring an UART peripheral, and then connect via USB the microcontroller and voila.
But it doesnt make sense to me yet that if USB connects to DN and DP, and an UART uses RX and TX, how does the host effectively communicates to the microcontroller?
There are two possibilities.
You connect to a USB/UART bridge such as devices made by FTDI or Prolific,
The microcontroller has a USB device controller and USB stack implementing the CDC/ACM device class (virtual COM port).
In the first instance, the bridge chip presents the CDC/ACM device to the host and exchanges data with a UART connected to the microcontroller UART. I/O control such as setting the baud rate have no impact on the USB connection, rather they are used to configure the UART link.
In the bridge arrangement, the bridge chip may be on the micro board, or it may be in a USB/serial adapter cable. Moreover internally the bridge chip is a microcontroller with a CDC/ACM device stack.
Unlike say RS-232, USB is not a peer-to-peer full duplex connection, and is not merely an electrical connection; USB requires quite complex device and host controllers and is more analogous to a device bus such as PCI than it is to UART serial connections. A CDC/ACM class device conforms to a specific protocol to allow a "virtual" UART to appear at the host. The UART you see at the PC is emulated, and is not physically the UART in the bridge.
The physical actual USB connection is a master-slave connection, with data and I/O control commands (such as baud rate and flow control) sent in USB packets to be unpacked, interpreted and transferred to the application layer via the CDC/ACM USB stack. In this arrangement the device, acting as a slave cannot initiate an exchange; rather the host continuously polls the device to which the device may return a packet containing its "tx" data. The polling and data rate of USB are fast enough to allow the simplex master-slave exchange to emulate a full-duplex UART connection, at higher throughput than can normally be achieved by a typical real UART, and certainly faster than a physical RS-xxx connection.
You can get an idea of how all this works by observing the raw USB data exchange using a tool such as WireShark. You will see that a lot more than just your application "serial" data is being exchanged.
You need to use a uart-usb interface IC that can convert uart to usb (and vice versa).

STM32 - How to choose between DMA or Interrupt for peripheral R/W in HAL library

I am using STM32F3 microcontrollers and the HAL library. For many peripherals (e.g. ADC, SPI, I2C), the HAL library provides 3 ways to read/write data: polling mode, interrupt mode, and DMA mode. I know I don't want the polling mode because it's blocking. However, I am unsure how to choose between interrupt and DMA mode. Is there a general rule of thumb? I feel like DMA mode should always be better because it can write values into memory without CPU intervention?
The advantage of DMA is that it does not require CPU intervention. DMA transfers can run while the CPU is busy doing other things, or while it is idle.
Some disadvantages of DMA are that:
Most microcontrollers have a limited number of DMA channels, so it may not be possible to use DMA for all peripherals.
The overhead of setting up and executing a DMA transfer may negate its benefits when many small transfers are required, e.g. when receiving individual characters over a USART.
Unusual interactions with devices (like bidirectional data transfers with some SPI devices) are often not supported with DMA.
DMA transfers place heavier (and less predictable) loads on the microcontroller's bus matrix, making them a frequent source of errata.
Generally speaking, I'd advise against using DMA for I2C. The protocol typically only runs at 100 - 200 kHz, so using interrupts will not place an especially heavy load on the microcontroller.

Clarification regarding PCI device initialization

Wikipedia says:
To address a PCI device, it must be enabled by being mapped into the system's I/O port address space or memory-mapped address space. The system's firmware, device drivers or the operating system program the Base Address Registers (commonly called BARs) to inform the device of its address mapping by writing configuration commands to the PCI controller.
Does this mean that a PCI device gets initialized when an address is written to the BAR? I'm trying to initialize the Bochs VGA card on Qemu Aarch64 thru bare metal and that's why I'm asking. Thanks!
Writing to the BAR simply tells the device what address range it should respond to. (It doesn't even enable the device to respond to the address; for that you need to set MSE [memory space enable].) There are many steps typically needed to initialize a device. Some of the steps are common for different PCI devices and others are completely device specific.

Linux Networking Driver With DMA Bus Mastering

I am currently working on writing my first Linux Networking driver and it seems to be going fairly smoothly right now. My network device is going to create an Ethernet interface but forward the Ethernet frames over PCIe to a PCIe endpoint. My question has to do with reception of forwarded Ethernet frames from the PCIe endpoint destined for my interface.
What I would normally do would be to allocate a large DMA buffer, tell the endpoint with Bus Mastering capabilities where the buffer was, and allow it to DMA to that buffer. It would then send an interrupt to signal reception and I could copy the data into an sk_buf.
My question is this:
In LDD3, it says that I should be able to DMA directly to the sk_buf because all sk_buf are in DMA memory. When and where do I allocate this buffer and tell the Bus Master where it is located? Do I do it on initialization first, then once the Bus Master has written it's first sk_buf and interrupts me signalling reception, do I allocate a new buffer and write the new location? Can this only be done with poll enabled (I think its called NAPI) reception?
Thanks in advance for your help.

Coherently understand the software-hardware interaction with regard to DMA and buses

I've gathered some level of knowledge on several components (including software and hardware) which are involved in general DMA transactions in ARM based boards, but I don't understand how is it all perfectly integrated, I didn't find a full coherent description about this.
I'll write down the high level of the knowledge I already have and I hope that someone could fix me where I'm wrong and complete the missing parts so the whole picture would be clear. My description starts with the userspace software and drills down to the hardware components. The misunderstood parts are in italic-bold format.
The user-mode application requests to read/write from some device, i.e. makes I/O operation.
The operating system receives the request and hand it to the appropriate driver (every OS has its own mechanism to do this, I don't need a further drill down here but if you want to share insights here you are welcome)
The driver which is on charge to handle the I/O request, has to know the address to which the device is mapped to (since I'm interested in ARM based boards, afaik there is only memory-mapped I/O and no port I/O). In most of the cases (if we consider smartphone-like boards) there is a linux kernel that parses the devices addresses from the device-tree which is given from the bootloader at the boot time (the modern approach), or the linux is precompiled for the specific model family and board with the device addresses within it (hardcoded in its source code) (in older and obsolete? approach). In some cases (happens a lot in smartphones) part of the drivers are precompiled and are just packaged into the kernel, i.e. their source is closed, thus, the addresses correspond to the devices are unknown. Is it correct?
Given that the driver knows the address of the relevant registers of the device it want to communicate with, it allocate a buffer (usually in the kernel space) to which the device would write its data (with the help of the DMA). The driver needs to inform the device about the location of that buffer, but the addresses that the devices work with (to manipulate memory) are different from the addresses that the drivers (cpu) work with, hence, the driver needs to inform the device about the 'bus address' of the buffer it has just allocated. How does the driver inform the device about that address? How popular is to use an IOMMU? when using IOMMU is there one hardware component that manages addressing or one per device?
Then the driver commands the device to do its job (by manipulating its registers) and the device transfers output data directly to the allocated buffer in the memory. Here I'm confused a bit with the relation of device-driver:bus:bus-controller:actual-device. Take for example some imaginary device which knows to communicate in the I2C protocol; the SoC specify an I2C bus interface - what is this actually? does the I2C bus has some kind of bus controller? Does the cpu communicate with the I2C bus interface or directly with the device? (i.e. the I2C bus interface is seamless). I guess that someone with some experience with device drivers could answer this easily..
The device populates a DMA channel. Since the device is not connected directly to the memory but rather is connected through some bus to the DMA controller (which masters the bus), it interacts with the DMA to transfer the required data to the allocated buffer in the memory. When the board vendor uses ARM IP cores and bus specifications then this step involves transactions over a bus from the AMBA spec (i.e. AHB/multi-AHB/AXI), and some protocol between the device and a DMAC on top of it. I would like to know more about this step, what actually happens? There are many specifications for DMA controller by ARM, which one is the popular? which is obsolete?
When the device is done, it sends an interrupt, which travel to the OS through the interrupt controller, and the OS's interrupt handler direct it to the appropriate driver which now knows that the DMA transfer is completed.
You've slightly conflated two things here - there are some devices (e.g. UARTs, MMC controllers, audio controllers, typically lower-bandwidth devices) which rely on an external DMA controller ("DMA engine" in Linux terminology), but many devices are simply bus masters in their own right and perform their own DMA directly (e.g. GPUs, USB host controllers, and of course the DMA controllers themselves). The former involves a bunch of extra complexity with the CPU programming the DMA controller, so I'm going to ignore it and just consider straightforward bus-master DMA.
In a typical ARM SoC, the CPU clusters and other master peripherals, and the memory controller and other slave peripherals, are all connected together with various AMBA interconnects, forming a single "bus" (generally all mapped to the "platform bus" in Linux), over which masters address slaves according to the address maps of the interconnect. You can safely assume that the device drivers know (whether by device tree or hardcoded) where devices appear in the CPU's physical address map, because otherwise they'd be useless.
On simpler systems, there is a single address map, so the physical addresses used by the CPU to address RAM and peripherals can be freely shared with other masters as DMA addresses. Other systems are more complex - one of the more well-known is the Raspberry Pi's BCM2835, in which the CPU and GPU have different address maps; e.g. the interconnect is hard-wired such that where the GPU sees peripherals at "bus address" 0x7e000000, the CPU sees them at "physical address" 0x20000000. Furthermore, in LPAE systems with 40-bit physical addresses, the interconnect might need to provide different views to different masters - e.g. in the TI Keystone 2 SoCs, all the DRAM is above the 32-bit boundary from the CPUs' point of view, so the 32-bit DMA masters would be useless if the interconnect didn't show them a different addresses map. For Linux, check out the dma-ranges device tree property for how such CPU→bus translations are described. The CPU must take these translations into account when telling a master to access a particular RAM or peripheral address; Linux drivers should be using the DMA mapping API which provides appropriately-translated DMA addresses.
IOMMUs provide more flexibility than fixed interconnect offsets - typically, addresses can be remapped dynamically, and for system integrity masters can be prevented from accessing any addresses other than those mapped for DMA at any given time. Furthermore, in an LPAE or AArch64 system with more than 4GB of RAM, an IOMMU becomes necessary if a 32-bit peripheral needs to be able to access buffers anywhere in RAM. You'll see IOMMUs on a lot of the current 64-bit systems for the purpose of integrating legacy 32-bit devices, but they are also increasingly popular for the purpose of device virtualisation.
IOMMU topology depends on the system and the IOMMUs in use - the system I'm currently working with has 7 separate ARM MMU-401/400 devices in front of individual bus-master peripherals; the ARM MMU-500 on the other hand can be implemented as a single system-wide device with a separate TLB for each master; other vendors have their own designs. Either way, from a Linux perspective, most device drivers should be using the aforementioned DMA mapping API to allocate and prepare physical buffers for DMA, which will also automatically set up the appropriate IOMMU mappings if the device is attached to one. That way, individual device drivers need not care about the presence of an IOMMU or not. Other drivers (typically GPU drivers) however, depend on an IOMMU and want complete control, so manage the mappings directly via the IOMMU API. Essentially, the IOMMU's page tables are set up to map certain ranges of physical addresses* to ranges of I/O virtual addresses, those IOVAs are given to the device as DMA (i.e. bus) addresses, and the IOMMU translates the IOVAs back to physical addresses as the device accesses them. Once the DMA operation is finished, the driver typically removes the IOMMU mapping, both to free up IOVA space and so that the device no longer has access to RAM.
Note that in some cases the DMA transfer is cyclic and never "finishes". With something like a display controller, the CPU might just map a buffer for DMA, pass that address to the controller and trigger it to start, and it will then continuously perform DMA reads to scan out whatever the CPU writes to that buffer until it is told to stop.
Other peripheral buses beyond the SoC interconnect, like I2C/SPI/USB/etc. work as you suspect - there is a bus controller (which is itself a device on the AMBA bus, so any of the above might apply to it) with its own device driver. In a crude generalisation, the CPU doesn't communicate directly with devices on the external bus - where a driver for an AMBA device says "write X to register Y", that just happens by the CPU performing a store to a memory-mapped address; where an I2C device driver says "write X to register Y", the OS usually has some bus abstraction layer which the bus controller driver implements, whereby the CPU programs the controller with a command saying "write X to register Y on device Z", the bus controller hardware will go off and do that, then notify the OS of the peripheral device's response via an interrupt or some other means.
* technically, the IOMMU itself, being more or less "just another device", could have a different address map in the interconnect as previously described, but I would doubt the sanity of anyone actually building a system like that.

Resources