check for IOMMU support on linux
Since 2014 enabled iommu are registered in /sys (sysfs) special file system as class iommu
(documented at ABI/testing/sysfs-class-iommu):
https://patchwork.kernel.org/patch/4345491/ "[2/3] iommu/intel: Make use of IOMMU sysfs support" - June 12, 2014
Register our DRHD IOMMUs, cross link devices, and provide a base set
of attributes for the IOMMU. ...
On a typical desktop system, this provides the following (pruned):$ find /sys | grep dmar
/sys/devices/virtual/iommu/dmar0
...
/sys/class/iommu/dmar0
/sys/class/iommu/dmar1
The code is iommu_device_create
(http://elixir.free-electrons.com/linux/v4.5/ident/iommu_device_create, around 4.5) or iommu_device_sysfs_add
(http://elixir.free-electrons.com/linux/v4.11/ident/iommu_device_sysfs_add) in more recent kernels.
/*
* Create an IOMMU device and return a pointer to it. IOMMU specific
* attributes can be provided as an attribute group, allowing a unique
* namespace per IOMMU type.
*/
struct device *iommu_device_create(struct device *parent, void *drvdata,
const struct attribute_group **groups,
const char *fmt, ...)
Registration is done only for enabled IOMMU. DMAR:
if (intel_iommu_enabled) {
iommu->iommu_dev = iommu_device_create(NULL, iommu,
intel_iommu_groups,
"%s", iommu->name);
AMD IOMMU:
static int iommu_init_pci(struct amd_iommu *iommu)
{ ...
if (!iommu->dev)
return -ENODEV;
...
iommu->iommu_dev = iommu_device_create(&iommu->dev->dev, iommu,
amd_iommu_groups, "ivhd%d",
iommu->index);
Intel:
int __init intel_iommu_init(void)
{ ...
pr_info("Intel(R) Virtualization Technology for Directed I/O\n");
...
for_each_active_iommu(iommu, drhd)
iommu->iommu_dev = iommu_device_create(NULL, iommu,
intel_iommu_groups,
"%s", iommu->name);
With 4.11 linux kernel version iommu_device_sysfs_add
is referenced in many IOMMU drivers, so checking /sys/class/iommu is better (more universal) way to programmatically detect enabled IOMMU than parsing dmesg
output or searching in /var/log/kern.log
or /var/log/messages
for driver-specific enable messages:
Referenced in 10 files:
- drivers/iommu/amd_iommu_init.c, line 1640
- drivers/iommu/arm-smmu-v3.c, line 2709
- drivers/iommu/arm-smmu.c, line 2163
- drivers/iommu/dmar.c, line 1083
- drivers/iommu/exynos-iommu.c, line 623
- drivers/iommu/intel-iommu.c, line 4878
- drivers/iommu/iommu-sysfs.c, line 57
- drivers/iommu/msm_iommu.c, line 797
- drivers/iommu/mtk_iommu.c, line 581
Create an IOMMU entry in Linux
I ended up with a pretty hacky solution, not the optimal one, but it worked for my usecase. Adjusted the function iommu_dma_map_page
in dma-iommu.c
to look like the following and export it.
(vanilla 5.18 except for this modification)
dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
unsigned long attrs)
{
bool coherent = dev_is_dma_coherent(dev);
int prot = dma_info_to_prot(dir, coherent, attrs);
struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iova_domain *iovad = &cookie->iovad;
dma_addr_t iova, dma_mask = dma_get_mask(dev);
phys_addr_t phys;
if (page->flags == 0xF0F0F0F0F0F0F) {
phys = page->dma_addr;
} else {
phys = page_to_phys(page) + offset;
}
/*
* If both the physical buffer start address and size are
* page aligned, we don't need to use a bounce page.
*/
if (dev_use_swiotlb(dev) && iova_offset(iovad, phys | size)) {
void *padding_start;
size_t padding_size, aligned_size;
aligned_size = iova_align(iovad, size);
phys = swiotlb_tbl_map_single(dev, phys, size, aligned_size,
iova_mask(iovad), dir, attrs);
if (phys == DMA_MAPPING_ERROR)
return DMA_MAPPING_ERROR;
/* Cleanup the padding area. */
padding_start = phys_to_virt(phys);
padding_size = aligned_size;
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) {
padding_start += size;
padding_size -= size;
}
memset(padding_start, 0, padding_size);
}
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
arch_sync_dma_for_device(phys, size, dir);
iova = __iommu_dma_map(dev, phys, size, prot, dma_mask);
if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(dev, phys))
swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
return iova;
}
EXPORT_SYMBOL(iommu_dma_map_page);
Then use the following kernel module to program the entry. This could be also extended and programmed in a more usable manner, but for prototyping, it should be enough.
#include <linux/init.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
extern dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
unsigned long attrs);
int magic_value = 0xF0F0F0F0F0F0F;
struct page page_ = {
.flags = 0xF0F0F0F0F0F0F,
.dma_addr = 0x0000002f000f0000,
};
static int my_init(void)
{
dma_addr_t dma_addr;
struct pci_dev *dummy = pci_get_device(0x10EE, 0x0666, NULL);
if (dummy != NULL)
{
printk(KERN_INFO "module loaded.\n");
dma_addr = iommu_dma_map_page(&(dummy->dev), &page_, 0, 4096, DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
printk(KERN_INFO "DMA_addr: %llx", dma_addr);
}
else
{
printk("Error getting device");
}
return 0;
}
static void my_exit(void)
{
printk(KERN_INFO "iommu_alloc unloaded.\n");
return;
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("benedict.schlueter@inf.ethz.ch");
MODULE_DESCRIPTION("Alloc IOMMU entry");
Related Topics
Why Would I Use "Service Sshd Reload" in Preference to "Service Sshd Restart"
How to List All Binary File Extensions Within a Directory Tree
Difference Between Unistd.H and Sys/Types.H in Linux
Using Netcat to Pipe Unix Socket to Tcp Socket
Detect If Stdout Is Redirected to a Pipe (Not to a File, Character Device, Terminal, or Socket)
Cross-Platform Build Under Windows Targeting Linux Using Cmake
How Does Boost Asio's Hostname Resolution Work on Linux? How to Use Nss
How to Get The Bash Date Script to Return a Day of The Week Relative to a Non-Current Time
Live Socket Monitoring with Netlink Inet_Diag
How to Diagnose a Python Process Chewing CPU in Linux
Setup Cron from Morning to Evening Only
Deploying War in Jboss 7.0.1 Through Commandline
Sorting with Multiple Keys with Linux Sort Command
How Are Interrupts Handled on Smp
Undefined Reference to Symbol 'Pthread_Key_Delete@@Glibc_2.2.5