processors. Therefore, every HAL provides mappings between the logical address ranges that DMA devices can access and physical address ranges of each computer.
Each adapter object is associated with one or more map registers, depending on the amount of data to be transferred and the amount of available memory. During DMA transfers, the HAL uses each map register to alias a device-accessible logical page to a page of physical memory in the CPU. In effect, map registers provide scatter/gather support for drivers that use DMA whether or not their devices have scatter/gather capabilities.
The following figure illustrates such a physical-to-logical address mapping for the driver of an ISA DMA device that does not have scatter/gather capabilities.
Address Mapping for a Sample ISA DMA Device
The previous figure shows the following types of mappings:
Each map register maps a range of physical addresses (pointed to by solid lines) to low-order logical addresses (dotted lines) for an ISA DMA device.
Here, three map registers are used to alias three paged ranges of data in system physical memory to three page-sized ranges of low-order logical addresses for an ISA DMA device.
The ISA device uses the mapped logical addresses to access system memory during DMA operations.
For a comparable PCI DMA device, three map registers would also be used for three page-sized ranges of data. However, the mapped logical address ranges would not necessarily be identical to the corresponding physical address ranges, so a PCI device would also use logical addresses to access system memory.
Each entry in the MDL maps a location in virtual address space to a physical address. Note the correspondence between a map register and a virtual-to-physical entry in the MDL:
Each map register and each virtual entry in an MDL maps at most a full physical page of data for a DMA transfer operation.
Each map register and each virtual entry in an MDL might map less than a full page of data. For example, the initial virtual entry in an MDL can map to an offset from the physical page boundary, as shown earlier in the Physical, Logical, and Virtual Address Mappings figure.
Each map register and each virtual entry in an MDL maps, at a minimum, one byte.
In an IRP requesting a read or write operation, each virtual entry in the opaque-to-drivers MDL at Irp->MdlAddress represents a page boundary in the system physical memory for a user buffer. Similarly, each additional map register needed for a single DMA transfer represents a page boundary in the device-accessible logical address range aliased to system physical memory.
On every Windows platform, each adapter object has an associated set of one or more map registers located at a platform-specific (and opaque-to-drivers) base address. From a driver's point of view, the map register base shown
in the figure illustrating address mapping for a sample ISA DMA device is a handle for a set of map registers that could be hardware registers in a chip, in a system DMA controller, or in a bus-master adapter, or could even be HAL-created virtual registers in system memory.
The number of map registers available with an adapter object can vary for different devices and Windows platforms. For example, the HAL can make more map registers available to drivers that use system DMA on some platforms than on other platforms because the DMA controllers on different Windows platforms have different capabilities.
总线地址和物理地址例子分析
对于ppc处理器而言,从CPU角度看到的memory和PCI设备角度看到的地址可能不一样,所以virt_to_bus和bus_to_virt定义为:
391 /* the local DRAM has a different
395 * address from the PCI point of view, thus buffer addresses also 396 * have to be modified [mapped] appropriately. 397 */
398 extern inline unsigned long virt_to_bus(volatile void * address) 399 {
400 #ifndef CONFIG_APUS 401 if (address == (void *)0) 402 return 0;
403 return (unsigned long)address - KERNELBASE + PCI_DRAM_OFFSET; 404 #else
405 return iopa ((unsigned long) address); 406 #endif 407 } 408
409 extern inline void * bus_to_virt(unsigned long address) 410 {
411 #ifndef CONFIG_APUS 412 if (address == 0) 413 return NULL;
414 return (void *)(address - PCI_DRAM_OFFSET + KERNELBASE); 415 #else
416 return (void*) mm_ptov (address); 417 #endif 418 } ARM:
对于S3C2410平台而言,物理地址与总线地址一致: 142 /*
143 * Virtual <-> DMA view memory address translations
144 * Again, these are *only* valid on the kernel direct mapped RAM 145 * memory. Use of these is *deprecated* (and that doesn't mean
146 * use the __ prefixed forms instead.) See dma-mapping.h. 147 */
148 static inline __deprecated unsigned long virt_to_bus(void *x) 149 {
150 return __virt_to_bus((unsigned long)x); 151 } 152
153 static inline __deprecated void *bus_to_virt(unsigned long x) 154 {
155 return (void *)__bus_to_virt(x); 156 } 36 /*
37 * These are exactly the same on the S3C2410 as the 38 * physical memory view. 39 */ 40
41 #define __virt_to_bus(x) __virt_to_phys(x) 42 #define __bus_to_virt(x) __phys_to_virt(x)
在arch-ixp23xx平台下,总线地址和物理地址关系如下: 33 #define __virt_to_bus(v) \\ 34 ({ unsigned int ret; \\ 35 ret = ((__virt_to_phys(v) - 0x00000000) + \\
36 (*((volatile int *)IXP23XX_PCI_SDRAM_BAR) & 0xfffffff0)); \\ 37 ret; }) 38
39 #define __bus_to_virt(b) \\ 40 ({ unsigned int data; \\
41 data = *((volatile int *)IXP23XX_PCI_SDRAM_BAR); \\ 42 __phys_to_virt((((b - (data & 0xfffffff0)) + 0x00000000))); }) 对于arch-iop3xx平台: 20 /*
21 * Virtual view <-> PCI DMA view memory address translations 22 * virt_to_bus: Used to translate the virtual address to an 23 * address suitable to be passed to set_dma_addr 24 * bus_to_virt: Used to convert an address for DMA operations 25 * to an address that the kernel can use. 26 */
27 #if defined(CONFIG_ARCH_IOP321) 28
29 #define __virt_to_bus(x) (((__virt_to_phys(x)) & ~(*IOP321_IATVR2)) | ((*IOP321_IABAR2) & 0xfffffff0))
30 #define __bus_to_virt(x) (__phys_to_virt(((x) & ~(*IOP321_IALR2)) | ( *IOP321_IATVR2)))
31
32 #elif defined(CONFIG_ARCH_IOP331) 33
34 #define __virt_to_bus(x) (((__virt_to_phys(x)) & ~(*IOP331_IATVR2)) | ((*IOP331_IABAR2) & 0xfffffff0))
35 #define __bus_to_virt(x) (__phys_to_virt(((x) & ~(*IOP331_IALR2)) | ( *IOP331_IATVR2))) 36 37 #endif
所以感觉总线地址和物理地址的关系直接与平台的设计相关。感觉一般的ARM手持设备中由于没有PCI接口,所以总线地址和物理地址应该一样?
1、申请和释放DMA缓冲区
内存中用于与外设交互数据的一块区域被称作DMA缓冲区,在设备不支持scatter/gather(SG,分散/聚集)操作的情况下,DMA 缓冲区必须是物理上连续的。
对于ISA设备而言,其DMA操作只能在16MB以下的内存中进行,因此,在使用kmalloc()和__get_free_pages()及其类似函数 申请DMA缓冲区时应使用GFP_DMA标志,这样能保证获得的内存是具备DMA能力的(DMA-capable)。内核中定义了 __get_free_pages()针对DMA的“快捷方式”__get_dma_pages(),它在申请标志中添加了GFP_DMA: #define __get_dma_pages(gfp_mask, order) \\
__get_free_pages((gfp_mask) | GFP_DMA,(order))
如果不想使用log2size即order为参数申请DMA内存,则可以使用另一个函数dma_mem_alloc(),其源代码如代码清单11.17。
代码清单11.17 dma_mem_alloc()函数 1 static unsigned long dma_mem_alloc(int size) 2 {
3 int order = get_order(size);//大小->指数 4 return __get_dma_pages(GFP_KERNEL, order); 5 }
基于DMA的硬件使用总线地址而非物理地址,虽然在PC上,对于ISA和PCI而言,总线地址即为物理地址,但并非每个平台都是如此。因为有时候接口总线被通过桥接电路连接,桥接电路会将I/O地址映射为不同的物理地址。还有一些系统提供了页面映射机制,它能将任意的页面映射为连续的外设总线地址。内核提供了如下函数用于进行简单的虚拟地址/总线地址转换: unsigned long virt_to_bus(volatile void *address); void *bus_to_virt(unsigned long address);
在必须使用IOMMU或反弹缓冲区的情况下,上述函数一般不会正常工作。而且,这2个函数并不建议被使用。如图11.13所示,IOMMU的工作原理与 CPU内的MMU非常类似,不过它针对的是外设总线地址和内存地址之间的转化。由于IOMMU可以使得外设看到“虚拟地址”,因此在使用IOMMU的情况 下,在修改映射寄存器后,可以使得SG中分段的缓冲区地址对外设变得连续。

