DMA Address Masks: Diagnose a Buffer That Works Only Below a Memory Boundary
When DMA works only for some allocations, compare the address the device receives with its documented addressing capability. A CPU pointer, a CPU physical address, and a DMA address are different quantities. Fix the mask, mapping, or descriptor encoding contract before moving buffers into a conveniently low region and calling the problem solved.
Applicability: this article uses the Linux v6.12 DMA API. The address examples and descriptor widths are hypothetical, not specifications for a named controller. Obtain the actual device’s DMA address fields, descriptor format, transfer limits, and platform integration requirements before selecting a mask.

1. Draw three address columns
The v6.12 DMA mapping guide distinguishes CPU virtual, CPU physical, and device-visible bus addresses. An IOMMU or host bridge can translate between physical memory and the address used by the device. Therefore, physical RAM above a boundary does not by itself prove that a device must issue an address above that boundary.
Create a transfer record containing buffer identity, allocation method, CPU address information available through approved diagnostics, returned DMA address, byte length, and descriptor fields actually programmed. Keep address types and widths in the labels. A hexadecimal value without its address domain is ambiguous evidence.
Do not replace a mapping call with a virtual-to-physical conversion because it worked on an earlier board. The platform may have changed its translation, bounce buffering, or device address window. The DMA API is the integration boundary that supplies the device-visible address.
2. Set masks from hardware capability
Use dma_set_mask_and_coherent when streaming mappings and coherent allocations have the same addressing capability. Where those capabilities genuinely differ, dma_set_mask and dma_set_coherent_mask configure them separately. Check the applicable return values before enabling DMA. These interfaces are defined in the v6.12 DMA API reference.
For a hypothetical engine with 64-bit payload addresses but a 32-bit ring-base register, the payload and coherent-ring requirements differ. A successful 64-bit streaming-mask setup does not establish that the ring may be placed anywhere in that wider range. Conversely, imposing the ring’s narrow constraint on every payload can unnecessarily restrict the design.
Build a capability worksheet from the real hardware specification: ring base, data pointers, next-descriptor pointers, and any secondary engine. Record whether an upper-address register exists, when it is sampled, and how it is paired with the lower word. The most restrictive field in a particular transaction determines that transaction’s requirement.
Do not advertise a wider mask simply to make allocation succeed. Kernel acceptance of a mask describes mapping support for the capability declared by the driver; it does not interrogate an arbitrary peripheral and prove that the peripheral has those address bits.
3. Preserve the complete returned address
Keep mapped addresses in dma_addr_t until deliberately encoding the documented hardware fields. The mapping guide identifies this type as the container for valid DMA addresses. Audit intermediate variables, structure members, debug formatting, and helper arguments for narrowing conversions.
Consider a hypothetical returned DMA address of 0x1_00001000. Truncating it to a 32-bit field produces 0x00001000, a different address. That is not a valid way to place the original buffer below the boundary. If the device only accepts 32-bit addresses, its mapping contract should request that capability; the driver must not manufacture a smaller address afterward.
Separately, a hypothetical CPU physical address of 0x1_00001000 could legitimately be mapped through an IOMMU to DMA address 0x00201000. Programming the returned low DMA address would then be correct. The equality or inequality of the numerical addresses is not the deciding factor; the established mapping is.
Check the whole transfer interval, not only its starting address. For a nonzero length, calculate the final byte with overflow-aware arithmetic and compare it with the applicable window. Also apply the device’s independent length, alignment, and segment-boundary rules. An address mask is not a substitute for those constraints.
4. Distinguish mapping failure from execution failure
For dma_map_single or dma_map_page, use dma_mapping_error before submitting the returned value. For coherent allocation, check the returned CPU pointer for allocation failure. The DMA API contracts specify these different failure checks; testing an address against zero is not a portable replacement.
If mapping fails, record that as a resource or mapping failure and keep the device from consuming an invalid descriptor. If mapping succeeds but the transfer fails, preserve the returned address and programmed descriptor separately. Their first mismatch can identify truncation, incorrect field splitting, endianness, or a stale descriptor.
Use the correct device object consistently for setup, mapping, error checking, and release. A mapping for one DMA domain is not automatically valid for another peripheral. Preserve the original size and direction for the corresponding cleanup path.
5. Propose a boundary-focused test matrix
- Known low device address: establish a baseline with the full address and descriptor captured.
- Different physical placement: observe whether the DMA address actually changes across the suspected boundary.
- Wide supported DMA address: where the platform and hardware permit it, verify upper-address encoding rather than merely allocating high physical RAM.
- Near-window transfer: review the final byte and any independent segment restriction.
- Mapping failure: use an approved test mechanism to verify that no descriptor is submitted and partial setup is unwound.
Run these tests on an isolated development target with bounded buffers and known ownership. Do not deliberately program an unsupported address to see what memory it corrupts. A software descriptor-encoding fixture can test upper-word preservation without giving a device access to arbitrary memory.
Repeat across relevant platform configurations, such as supported IOMMU modes, only through the project’s controlled build and boot procedure. Record configuration differences explicitly. A test that changes both translation and cache policy cannot isolate addressability by itself.
6. Report the failed boundary precisely
Classify the outcome as unsupported mask setup, mapping allocation failure, incorrectly encoded DMA address, transfer-window violation, or a later ownership/visibility problem. If low-memory placement hides the issue, keep it as diagnostic evidence until the actual boundary is explained.
No DMA mapping or hardware transfer was performed for this article. The deliverable is an address-domain audit and a proposed boundary matrix. Acceptance requires documented hardware capability, successful mapping, lossless descriptor encoding, and correct transfer completion; a low CPU allocation alone proves none of those together.