iommu: qcom-iommu-debug: Add dma operations

Add support for dma map/unmap operations that use dma_map_single_attrs().

Change-Id: Iaedd323dd9297a8b5af4de1bf3ccb6ff83295248
Signed-off-by: Georgi Djakov <gdjako@codeaurora.org>
This commit is contained in:
Georgi Djakov 2021-03-12 05:46:11 -08:00
parent 6aa0cf9996
commit 5424f8bc13
3 changed files with 227 additions and 0 deletions

View File

@ -243,6 +243,229 @@ const struct file_operations iommu_debug_unmap_fops = {
.write = iommu_debug_unmap_write,
};
/*
* Performs DMA mapping of a given virtual address and size to an iova address.
* User input format: (addr,len,dma attr) where dma attr is:
* 0: normal mapping
* 1: force coherent mapping
* 2: force non-cohernet mapping
* 3: use system cache
*/
static ssize_t iommu_debug_dma_map_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *offset)
{
ssize_t retval = -EINVAL;
int ret;
char *comma1, *comma2;
char buf[100] = {0};
unsigned long addr;
void *v_addr;
dma_addr_t iova;
size_t size;
unsigned int attr;
unsigned long dma_attrs;
struct iommu_debug_device *ddev = file->private_data;
struct device *dev = ddev->test_dev;
if (count >= sizeof(buf)) {
pr_err_ratelimited("Value too large\n");
return -EINVAL;
}
if (copy_from_user(buf, ubuf, count)) {
pr_err_ratelimited("Couldn't copy from user\n");
return -EFAULT;
}
comma1 = strnchr(buf, count, ',');
if (!comma1)
goto invalid_format;
comma2 = strnchr(comma1 + 1, count, ',');
if (!comma2)
goto invalid_format;
*comma1 = *comma2 = '\0';
if (kstrtoul(buf, 0, &addr))
goto invalid_format;
v_addr = (void *)addr;
if (kstrtosize_t(comma1 + 1, 0, &size))
goto invalid_format;
if (kstrtouint(comma2 + 1, 0, &attr))
goto invalid_format;
mutex_lock(&test_virt_addr_lock);
if (IS_ERR(test_virt_addr)) {
mutex_unlock(&test_virt_addr_lock);
goto allocation_failure;
}
if (!test_virt_addr) {
mutex_unlock(&test_virt_addr_lock);
goto missing_allocation;
}
mutex_unlock(&test_virt_addr_lock);
if (v_addr < test_virt_addr || v_addr + size > test_virt_addr + SZ_1M)
goto invalid_addr;
if (attr == 0)
dma_attrs = 0;
else if (attr == 1)
dma_attrs = DMA_ATTR_FORCE_COHERENT;
else if (attr == 2)
dma_attrs = DMA_ATTR_FORCE_NON_COHERENT;
else if (attr == 3)
dma_attrs = DMA_ATTR_SYS_CACHE_ONLY;
else
goto invalid_format;
mutex_lock(&ddev->state_lock);
if (!ddev->domain) {
pr_err_ratelimited("%s: No domain. Have you selected a usecase?\n", __func__);
mutex_unlock(&ddev->state_lock);
return -EINVAL;
}
iova = dma_map_single_attrs(dev, v_addr, size, DMA_TO_DEVICE, dma_attrs);
if (dma_mapping_error(dev, iova)) {
pr_err_ratelimited("Failed to perform dma_map_single\n");
ret = -EINVAL;
goto out;
}
retval = count;
pr_err_ratelimited("Mapped 0x%p to %pa (len=0x%zx)\n", v_addr, &iova, size);
ddev->iova = iova;
pr_err_ratelimited("Saved iova=%pa for future PTE commands\n", &iova);
out:
mutex_unlock(&ddev->state_lock);
return retval;
invalid_format:
pr_err_ratelimited("Invalid format. Expected: addr,len,dma attr where 'dma attr' is\n0: normal mapping\n1: force coherent\n2: force non-cohernet\n3: use system cache\n");
return retval;
invalid_addr:
pr_err_ratelimited("Invalid addr given (0x%p)! Address should be within 1MB size from start addr returned by doing 'cat test_virt_addr'.\n",
v_addr);
return retval;
allocation_failure:
pr_err_ratelimited("Allocation of test_virt_addr failed.\n");
return -ENOMEM;
missing_allocation:
pr_err_ratelimited("Please attempt to do 'cat test_virt_addr'.\n");
return retval;
}
static ssize_t iommu_debug_dma_map_read(struct file *file, char __user *ubuf,
size_t count, loff_t *offset)
{
struct iommu_debug_device *ddev = file->private_data;
char buf[100] = {};
dma_addr_t iova;
int len;
if (*offset)
return 0;
iova = ddev->iova;
len = scnprintf(buf, sizeof(buf), "%pa\n", &iova);
return simple_read_from_buffer(ubuf, count, offset, buf, len);
}
const struct file_operations iommu_debug_dma_map_fops = {
.open = simple_open,
.read = iommu_debug_dma_map_read,
.write = iommu_debug_dma_map_write,
};
static ssize_t iommu_debug_dma_unmap_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *offset)
{
ssize_t retval = 0;
char *comma1, *comma2;
char buf[100] = {};
size_t size;
unsigned int attr;
dma_addr_t iova;
unsigned long dma_attrs;
struct iommu_debug_device *ddev = file->private_data;
struct device *dev = ddev->test_dev;
if (count >= sizeof(buf)) {
pr_err_ratelimited("Value too large\n");
return -EINVAL;
}
if (copy_from_user(buf, ubuf, count)) {
pr_err_ratelimited("Couldn't copy from user\n");
retval = -EFAULT;
goto out;
}
comma1 = strnchr(buf, count, ',');
if (!comma1)
goto invalid_format;
comma2 = strnchr(comma1 + 1, count, ',');
if (!comma2)
goto invalid_format;
*comma1 = *comma2 = '\0';
if (kstrtoux(buf, 0, &iova))
goto invalid_format;
if (kstrtosize_t(comma1 + 1, 0, &size))
goto invalid_format;
if (kstrtouint(comma2 + 1, 0, &attr))
goto invalid_format;
if (attr == 0)
dma_attrs = 0;
else if (attr == 1)
dma_attrs = DMA_ATTR_FORCE_COHERENT;
else if (attr == 2)
dma_attrs = DMA_ATTR_FORCE_NON_COHERENT;
else if (attr == 3)
dma_attrs = DMA_ATTR_SYS_CACHE_ONLY;
else
goto invalid_format;
mutex_lock(&ddev->state_lock);
if (!ddev->domain) {
pr_err_ratelimited("%s: No domain. Have you selected a usecase?\n", __func__);
mutex_unlock(&ddev->state_lock);
return -EINVAL;
}
dma_unmap_single_attrs(dev, iova, size, DMA_TO_DEVICE, dma_attrs);
retval = count;
pr_err_ratelimited("Unmapped %pa (len=0x%zx)\n", &iova, size);
out:
mutex_unlock(&ddev->state_lock);
return retval;
invalid_format:
pr_err_ratelimited("Invalid format. Expected: iova,len, dma attr\n");
return -EINVAL;
}
const struct file_operations iommu_debug_dma_unmap_fops = {
.open = simple_open,
.write = iommu_debug_dma_unmap_write,
};
static int iommu_debug_build_phoney_sg_table(struct device *dev,
struct sg_table *table,
unsigned long total_size,

View File

@ -253,6 +253,8 @@ static int iommu_debug_debugfs_setup(struct iommu_debug_device *ddev)
debugfs_create_file("atos", 0600, dir, ddev, &iommu_debug_atos_fops);
debugfs_create_file("map", 0200, dir, ddev, &iommu_debug_map_fops);
debugfs_create_file("unmap", 0200, dir, ddev, &iommu_debug_unmap_fops);
debugfs_create_file("dma_map", 0200, dir, ddev, &iommu_debug_dma_map_fops);
debugfs_create_file("dma_unmap", 0200, dir, ddev, &iommu_debug_dma_unmap_fops);
debugfs_create_file("nr_iters", 0600, dir, ddev, &iommu_debug_nr_iters_fops);
debugfs_create_file("test_virt_addr", 0400, dir, ddev, &iommu_debug_test_virt_addr_fops);
debugfs_create_file("profiling", 0400, dir, ddev, &iommu_debug_profiling_fops);

View File

@ -67,6 +67,8 @@ extern const struct file_operations iommu_debug_functional_fast_dma_api_fops;
extern const struct file_operations iommu_debug_atos_fops;
extern const struct file_operations iommu_debug_map_fops;
extern const struct file_operations iommu_debug_unmap_fops;
extern const struct file_operations iommu_debug_dma_map_fops;
extern const struct file_operations iommu_debug_dma_unmap_fops;
extern const struct file_operations iommu_debug_test_virt_addr_fops;
extern const struct file_operations iommu_debug_profiling_fops;