Commit 1eb55075 authored by zhangshuowen's avatar zhangshuowen Committed by zhangshuowen96
Browse files

drivers:misc:sdma-dae: fix icsl code check problems

kunpeng inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IAP49S


CVE: NA

----------------------------------------------------------------------

1. add mmap size validations, avoid mapping too big memory
2. add chn check before register read/write
3. use rwlock instead of spinlock while checking authority
4. Add validation of bios resources: io register size,
irq_cnt and number of sdma channels to avoid Out-of-Bounds
5. add vm_flags while mmapping avoid copy while forking
6. add device lock for operations of accessing global info
7. change umem spinlock use method to avoid UAF
8. add ida validation while using exclusive channel
9. add task num validation while sending task
10. fix print infos to speed up problem fixing

Signed-off-by: default avatarzhangshuowen <zhangshuowen@hisilicon.com>
Fixes: 7d91f398 ("drivers: misc: sdma-dae: support sqe task send and execute")
parent 154916b7
Loading
Loading
Loading
Loading
+8 −5
Original line number Diff line number Diff line
@@ -14,8 +14,11 @@
#define HISI_SDMA_MMAP_CQE			1
#define HISI_SDMA_MMAP_IO			2
#define HISI_SDMA_MMAP_SHMEM			3
#define HISI_SDMA_FSM_TIMEOUT			100
#define HISI_SDMA_FSM_INTERVAL			20
#define HISI_SDMA_FSM_TIMEOUT			10

#define HISI_SDMA_MAX_BASE_ADDR_SIZE		0x100000
#define HISI_SDMA_MAX_COMMEN_BASE_ADDR_SIZE	0x10000
#define HISI_SDMA_CHANNEL_IOMEM_SIZE		0x1000
#define HISI_SDMA_SQ_ENTRY_SIZE			64UL
#define HISI_SDMA_CQ_ENTRY_SIZE			16UL
@@ -135,7 +138,7 @@ struct hisi_sdma_share_chn {
};

struct hisi_sdma_reg_info {
	int chn;
	u32 chn;
	int type;
	u32 reg_value;
};
@@ -168,7 +171,7 @@ struct hisi_sdma_sqe_task {
};

struct hisi_sdma_task_info {
	int chn;
	u32 chn;
	u32 req_cnt;
	u32 task_cnt;
	uintptr_t task_addr;
@@ -181,8 +184,8 @@ struct hisi_sdma_ioctl_func_list {
};

#define IOCTL_SDMA_GET_PROCESS_ID	_IOR('s', 1, u32)
#define IOCTL_SDMA_GET_CHN		_IOR('s', 2, int)
#define IOCTL_SDMA_PUT_CHN		_IOW('s', 3, int)
#define IOCTL_SDMA_GET_CHN		_IOR('s', 2, u32)
#define IOCTL_SDMA_PUT_CHN		_IOW('s', 3, u32)
#define IOCTL_SDMA_GET_STREAMID		_IOR('s', 4, u32)
#define IOCTL_SDMA_PIN_UMEM		_IOWR('s', 5, struct hisi_sdma_umem_info)
#define IOCTL_SDMA_UNPIN_UMEM		_IOW('s', 6, u64)
+42 −20
Original line number Diff line number Diff line
@@ -17,12 +17,11 @@ struct hisi_sdma_own_pid_hte {
	u32 pasid;
	struct hlist_node node;
	DECLARE_HASHTABLE(sdma_submitter_pid_ht, HISI_SDMA_AUTH_HASH_BUCKETS_BITS);
	spinlock_t submitter_pid_lock;
};

struct hisi_sdma_auth_ht {
	DECLARE_HASHTABLE(sdma_owner_pid_ht, HISI_SDMA_AUTH_HASH_BUCKETS_BITS);
	spinlock_t owner_pid_lock;
	rwlock_t owner_pid_lock;
};

static struct hisi_sdma_auth_ht *g_authority;
@@ -34,7 +33,7 @@ int sdma_authority_hash_init(void)
		return -ENOMEM;

	hash_init(g_authority->sdma_owner_pid_ht);
	spin_lock_init(&g_authority->owner_pid_lock);
	rwlock_init(&g_authority->owner_pid_lock);

	return 0;
}
@@ -45,13 +44,10 @@ static void entry_free_pid_ht(struct hisi_sdma_own_pid_hte *entry)
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&entry->submitter_pid_lock);
	hash_for_each_safe(entry->sdma_submitter_pid_ht, bkt, tmp, sub_entry, pnode) {
		hash_del(&sub_entry->pnode);
		kfree(sub_entry);
		sub_entry = NULL;
	}
	spin_unlock(&entry->submitter_pid_lock);
	hash_del(&entry->node);
	kfree(entry);
}
@@ -62,11 +58,11 @@ void sdma_authority_ht_free(void)
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&g_authority->owner_pid_lock);
	write_lock(&g_authority->owner_pid_lock);
	hash_for_each_safe(g_authority->sdma_owner_pid_ht, bkt, tmp, entry, node)
		entry_free_pid_ht(entry);

	spin_unlock(&g_authority->owner_pid_lock);
	write_unlock(&g_authority->owner_pid_lock);

	kfree(g_authority);
	g_authority = NULL;
@@ -91,22 +87,19 @@ void sdma_free_authority_ht_with_pid(u32 pid)
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&g_authority->owner_pid_lock);
	write_lock(&g_authority->owner_pid_lock);
	hash_for_each_safe(g_authority->sdma_owner_pid_ht, bkt, tmp, entry, node) {
		if (entry->pid == pid)
			entry_free_pid_ht(entry);
		else {
			spin_lock(&entry->submitter_pid_lock);
			sub_entry = sdma_search_submitter_pid(entry, pid);
			if (sub_entry) {
				hash_del(&sub_entry->pnode);
				kfree(sub_entry);
				sub_entry = NULL;
			}
			spin_unlock(&entry->submitter_pid_lock);
		}
	}
	spin_unlock(&g_authority->owner_pid_lock);
	write_unlock(&g_authority->owner_pid_lock);
}

static struct hisi_sdma_own_pid_hte *sdma_search_owner_pid_ht(u32 pid)
@@ -120,24 +113,52 @@ static struct hisi_sdma_own_pid_hte *sdma_search_owner_pid_ht(u32 pid)
	return NULL;
}

static void sdma_clear_residual_auth_ht(struct hisi_sdma_own_pid_hte *entry, u32 *list, u32 pos,
					bool *stored_info)
{
	struct hisi_sdma_sub_pid_hte *sub_entry;
	u32 i = pos;

	while (i > 0) {
		i--;
		if (stored_info[i]) {
			sub_entry = sdma_search_submitter_pid(entry, list[i]);
			if (sub_entry) {
				hash_del(&sub_entry->pnode);
				kfree(sub_entry);
			}
		}
	}
}

static int sdma_add_authority_ht(struct hisi_sdma_own_pid_hte *entry, u32 count, u32 *list)
{
	struct hisi_sdma_sub_pid_hte *sub_entry;
	bool *stored;
	u32 i;

	stored = kcalloc(count, sizeof(bool), GFP_KERNEL);
	if (!stored)
		return -ENOMEM;

	for (i = 0; i < count; i++) {
		sub_entry = sdma_search_submitter_pid(entry, list[i]);
		if (sub_entry)
			continue;

		sub_entry = kzalloc(sizeof(struct hisi_sdma_sub_pid_hte), GFP_KERNEL);
		if (!sub_entry)
		if (!sub_entry) {
			sdma_clear_residual_auth_ht(entry, list, i, stored);
			kfree(stored);
			return -ENOMEM;
		}

		sub_entry->pid = list[i];
		hash_add(entry->sdma_submitter_pid_ht, &sub_entry->pnode, sub_entry->pid);
		stored[i] = true;
	}

	kfree(stored);
	return 0;
}

@@ -152,7 +173,6 @@ static int sdma_create_authority_ht(u32 pid, u32 pasid, u32 num, u32 *list)

	entry->pid = pid;
	entry->pasid = pasid;
	spin_lock_init(&entry->submitter_pid_lock);
	hash_init(entry->sdma_submitter_pid_ht);
	ret = sdma_add_authority_ht(entry, num, list);
	hash_add(g_authority->sdma_owner_pid_ht, &entry->node, entry->pid);
@@ -164,22 +184,20 @@ int sdma_auth_add(u32 pasid, u32 num, u32 *pid_list)
{
	struct hisi_sdma_own_pid_hte *owner;
	u32 pid = (u32)current->tgid;
	int ret = 0;
	int ret;

	write_lock(&g_authority->owner_pid_lock);
	owner = sdma_search_owner_pid_ht(pid);
	if (owner) {
		spin_lock(&owner->submitter_pid_lock);
		ret = sdma_add_authority_ht(owner, num, pid_list);
		spin_unlock(&owner->submitter_pid_lock);
		if (ret < 0)
			pr_err("add_pid_ht failed\n");
	} else {
		spin_lock(&g_authority->owner_pid_lock);
		ret = sdma_create_authority_ht(pid, pasid, num, pid_list);
		spin_unlock(&g_authority->owner_pid_lock);
		if (ret < 0)
			pr_err("create_pid_ht failed\n");
	}
	write_unlock(&g_authority->owner_pid_lock);

	return ret;
}
@@ -193,16 +211,20 @@ int sdma_check_authority(u32 pasid, u32 owner_pid, u32 submitter_pid, u32 *owner
		*owner_pasid = pasid;
		return 0;
	}
	read_lock(&g_authority->owner_pid_lock);
	entry = sdma_search_owner_pid_ht(owner_pid);
	if (!entry) {
		pr_err("the owner_pid_ht[%u] not exist\n", owner_pid);
		read_unlock(&g_authority->owner_pid_lock);
		return -ENODATA;
	}
	sub_entry = sdma_search_submitter_pid(entry, submitter_pid);
	if (!sub_entry) {
		pr_err("the submitter[%u] not authorithed\n", submitter_pid);
		read_unlock(&g_authority->owner_pid_lock);
		return -ENODATA;
	}
	read_unlock(&g_authority->owner_pid_lock);

	*owner_pasid = entry->pasid;
	return 0;
+173 −86
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/sort.h>
#include <linux/mm.h>
#include <linux/version.h>

#include "sdma_hal.h"
#include "sdma_umem.h"
@@ -14,7 +16,7 @@ static struct hisi_sdma_global_info g_info;

struct hisi_sdma_channel_list {
	struct list_head chn_list;
	int chn_idx;
	u32 chn_idx;
};

struct file_open_data {
@@ -103,13 +105,13 @@ static int ioctl_sdma_unpin_umem(struct file *file, unsigned long arg)
	int ret;
	int ida;

	if (copy_from_user(&cookie, (u64 __user *)(uintptr_t)arg, sizeof(u64)))
		return -EFAULT;

	ida = (int)(cookie >> COOKIE_IDA_SHIFT);
	if (ida != data->ida)
		return -EPERM;

	if (copy_from_user(&cookie, (u64 __user *)(uintptr_t)arg, sizeof(u64)))
		return -EFAULT;

	ret = sdma_umem_release(cookie);
	if (ret)
		dev_err(&data->psdma_dev->pdev->dev, "umem release fail!\n");
@@ -124,8 +126,10 @@ static int ioctl_sdma_pin_umem(struct file *file, unsigned long arg)
	int ret;

	if (copy_from_user(&umemInfo, (struct hisi_sdma_umem_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_umem_info)))
			   sizeof(struct hisi_sdma_umem_info))) {
		dev_dbg(&data->psdma_dev->pdev->dev, "umem_info copy from user failed!\n");
		return -EFAULT;
	}

	ret = sdma_umem_get((u64)umemInfo.vma, umemInfo.size, data->ida, &umemInfo.cookie);
	if (ret < 0)
@@ -134,6 +138,7 @@ static int ioctl_sdma_pin_umem(struct file *file, unsigned long arg)
	if (copy_to_user((struct hisi_sdma_umem_info __user *)(uintptr_t)arg, &umemInfo,
			 sizeof(struct hisi_sdma_umem_info))) {
		sdma_umem_release(umemInfo.cookie);
		dev_dbg(&data->psdma_dev->pdev->dev, "umem_info copy to user failed!\n");
		return -EFAULT;
	}

@@ -187,14 +192,14 @@ static int ioctl_sdma_get_chn(struct file *file, unsigned long arg)
	}

	idx += share_chns;
	list_node->chn_idx = (int)idx;
	list_node->chn_idx = idx;
	list_add(&list_node->chn_list, &data->non_share_chn_list);
	pchannel = pdev->channels + idx;
	pchannel->cnt_used++;
	pchannel->ida = (u32)data->ida;
	spin_unlock(&pdev->channel_lock);

	dev_dbg(&pdev->pdev->dev, "sdma get chn %u\n", idx);
	if (copy_to_user((int __user *)(uintptr_t)arg, &idx, sizeof(int))) {
	if (copy_to_user((u32 __user *)(uintptr_t)arg, &idx, sizeof(u32))) {
		ret = -EFAULT;
		goto put_chn;
	}
@@ -206,7 +211,7 @@ static int ioctl_sdma_get_chn(struct file *file, unsigned long arg)
	list_del(&list_node->chn_list);
	bitmap_set(pdev->channel_map, idx - share_chns, 1);
	pdev->nr_channel_used--;
	pchannel->cnt_used--;
	pchannel->ida = 0;
unlock:
	spin_unlock(&pdev->channel_lock);
	kfree(list_node);
@@ -221,31 +226,29 @@ static int ioctl_sdma_put_chn(struct file *file, unsigned long arg)
	struct device *dev = &pdev->pdev->dev;
	u32 share_chns = *(g_info.share_chns);
	struct hisi_sdma_channel_list *c, *n;
	int idx;
	u32 idx;

	if (copy_from_user(&idx, (int __user *)(uintptr_t)arg, sizeof(int))) {
	if (copy_from_user(&idx, (u32 __user *)(uintptr_t)arg, sizeof(u32))) {
		dev_err(dev, "put user chn failed\n");
		return -EFAULT;
	}

	if (idx < (int)share_chns || idx >= (int)pdev->nr_channel) {
		dev_err(dev, "put idx = %d is err\n", idx);
	if (idx < share_chns || idx >= pdev->nr_channel) {
		dev_err(dev, "put idx = %u is err\n", idx);
		return -EFAULT;
	}

	spin_lock(&pdev->channel_lock);
	bitmap_set(pdev->channel_map, idx - share_chns, 1);
	pdev->nr_channel_used--;

	list_for_each_entry_safe(c, n, &data->non_share_chn_list, chn_list) {
		if (c->chn_idx == idx) {
			dev_dbg(dev, "sdma put chn %d\n", idx);
			bitmap_set(pdev->channel_map, idx - share_chns, 1);
			pdev->nr_channel_used--;
			dev_dbg(dev, "sdma put chn %u\n", idx);
			list_del(&c->chn_list);
			kfree(c);
			break;
		}
	}

	spin_unlock(&pdev->channel_lock);

	return 0;
@@ -264,8 +267,8 @@ static int cmp(const void *a, const void *b)

static int ioctl_get_near_sdmaid(struct file *file, unsigned long arg)
{
	struct hisi_sdma_numa_domain sdma_numa[HISI_SDMA_MAX_DEVS];
	u32 num = g_info.core_dev->sdma_device_num;
	struct hisi_sdma_numa_domain *sdma_numa;
	struct hisi_sdma_device *sdma_dev;
	struct device *dev;
	int sdma_id = -1;
@@ -274,38 +277,40 @@ static int ioctl_get_near_sdmaid(struct file *file, unsigned long arg)

	nid = numa_node_id();
	if (nid < 0) {
		pr_err("numa_node not reported!\n");
		return 0;
		pr_err("sdma numa_node not reported!\n");
		return -EINVAL;
	}
	if (num <= 0 || num > HISI_SDMA_MAX_DEVS) {
		pr_err("device num wrong, cannot use sdma!\n");
		return -ENOENT;
	}

	sdma_numa = kcalloc(num, sizeof(struct hisi_sdma_numa_domain), GFP_KERNEL);
	if (!sdma_numa)
		return -ENOMEM;

	for (i = 0; i < num; i++) {
		spin_lock(&g_info.core_dev->device_lock);
		sdma_dev = g_info.core_dev->sdma_devices[i];
		dev = &sdma_dev->pdev->dev;
		sdma_numa[i].idx = sdma_dev->idx;
		sdma_numa[i].pxm = sdma_dev->node_idx;
		if (sdma_numa[i].pxm < 0) {
			spin_unlock(&g_info.core_dev->device_lock);
			dev_err(dev, "sdma%d PXM domain not reported!\n", sdma_numa[i].idx);
			goto ret_idx;
			return -ENODATA;
		}
		spin_unlock(&g_info.core_dev->device_lock);
		dev_dbg(dev, "sdma%d PXM = %d\n", sdma_numa[i].idx, sdma_numa[i].pxm);
	}

	sort(sdma_numa, num, sizeof(struct hisi_sdma_numa_domain), cmp, NULL);

	for (i = 0; i < num; i++) {
		if (nid >= sdma_numa[i].pxm) {
			sdma_id = sdma_numa[i].idx;
			goto ret_idx;
			break;
		}
	}
	pr_err("Nearest sdma not found! process nid = %d\n", nid);

ret_idx:
	kfree(sdma_numa);
	if (sdma_id < 0) {
		pr_err("Nearest sdma not found! process nid = %d, sdmaid = %d\n", nid, sdma_id);
		return -ENODATA;
	}
	if (copy_to_user((int __user *)(uintptr_t)arg, &sdma_id, sizeof(int)))
		return -EFAULT;

@@ -333,8 +338,7 @@ static int ioctl_sdma_mpamcfg(struct file *file, unsigned long arg)
	struct hisi_sdma_device *pdev = data->psdma_dev;
	struct hisi_sdma_mpamcfg cfg;

	if (copy_from_user(&cfg,
			   (struct hisi_sdma_mpamcfg __user *)(uintptr_t)arg,
	if (copy_from_user(&cfg, (struct hisi_sdma_mpamcfg __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_mpamcfg)))
		return -EFAULT;

@@ -378,16 +382,19 @@ static int ioctl_sdma_chn_used_refcount(struct file *file, unsigned long arg)
		list_add(&list_node->chn_list, &data->share_chn_list);
		pchannel->cnt_used++;
	}

	if (!share_chn.init_flag && pchannel->cnt_used > 0) {
		list_for_each_entry_safe(c, n, &data->share_chn_list, chn_list) {
			if (c->chn_idx == share_chn.chn_idx) {
				dev_dbg(dev, "release share_chn%d\n", c->chn_idx);
				pchannel->cnt_used--;
				if (pchannel->cnt_used == 0)
					pchannel->sync_info_base->err_cnt = 0;
				dev_dbg(dev, "release share_chn%u\n", c->chn_idx);
				list_del(&c->chn_list);
				kfree(c);
				break;
			}
		}
		pchannel->cnt_used--;
	}
	spin_unlock(&pdev->channel_lock);

@@ -413,7 +420,7 @@ static int ioctl_sdma_add_authority_ht(struct file *file, unsigned long arg)
		dev_err(&pdev->pdev->dev, "Invalid pid_list num:%u\n", list_num);
		return -EINVAL;
	}
	pid_list = kmalloc_node(list_num * sizeof(int), GFP_KERNEL, pdev->node_idx);
	pid_list = kmalloc_node(list_num * sizeof(u32), GFP_KERNEL, pdev->node_idx);
	if (!pid_list)
		return -ENOMEM;

@@ -435,7 +442,7 @@ static int sdma_verify_src_dst(struct file_open_data *data, struct pasid_info *p
{
	struct device *dev = &data->psdma_dev->pdev->dev;
	u32 pid = (u32)current->tgid;
	int ret = -1;
	int ret = -EPERM;

	if (task_list.opcode == HISI_SDMA_HBM_CACHE_PRELOAD_MODE) {
		pasid->src_pasid = data->pasid;
@@ -490,6 +497,20 @@ static void sdma_fill_sqe(struct hisi_sdma_sq_entry *sq_entry, struct hisi_sdma_
	sq_entry->qos             = task->qos;
}

static bool sdma_check_channel_permission(struct hisi_sdma_channel *pchannel, u32 ida, u32 chn)
{
	u32 share_chns = *(g_info.share_chns);

	if (chn < share_chns) {
		return true;
	} else if (chn < HISI_SDMA_DEFAULT_CHANNEL_NUM && pchannel->ida != ida) {
		pr_err("invalid process send task by sdma exclusive channel%u\n", chn);
		return false;
	}

	return true;
}

static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_task_info *task_info,
				 struct hisi_sdma_sqe_task *task_list)
{
@@ -502,9 +523,15 @@ static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_t
	u32 i;

	pchannel = pdev->channels + task_info->chn;
	spin_lock(&pchannel->owner_chn_lock);
	if (!sdma_check_channel_permission(pchannel, (u32)data->ida, task_info->chn)) {
		spin_unlock(&pchannel->owner_chn_lock);
		return -EPERM;
	}
	sq_tail = pchannel->sync_info_base->sq_tail;
	for (i = 0; i < task_info->task_cnt; i++) {
		if (task_info->req_cnt != 0) {
			/* not send/record tasks whose length == 0 */
			if (task_list[i].length == 0) {
				task_info->req_cnt--;
				continue;
@@ -512,6 +539,7 @@ static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_t
		}
		ret = sdma_verify_src_dst(data, &pasid, task_list[i]);
		if (ret < 0) {
			spin_unlock(&pchannel->owner_chn_lock);
			dev_err(&pdev->pdev->dev, "no correct pid\n");
			return ret;
		}
@@ -521,6 +549,43 @@ static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_t
	}
	sdma_channel_set_sq_tail(pchannel, sq_tail);
	pchannel->sync_info_base->sq_tail = sq_tail;
	spin_unlock(&pchannel->owner_chn_lock);

	return 0;
}

static u32 sdma_channel_free_sqe_cnt(struct hisi_sdma_channel *pchannel)
{
	u32 head = sdma_channel_get_sq_head(pchannel);
	u32 tail = sdma_channel_get_sq_tail(pchannel);
	u32 res_cnt;

	if (tail >= head)
		res_cnt = HISI_SDMA_SQ_LENGTH - (tail - head) - 1;
	else
		res_cnt = head - tail - 1;

	return res_cnt;
}

static int sdma_task_info_validate(struct file_open_data *data,
				    struct hisi_sdma_task_info *task_info)
{
	struct hisi_sdma_channel *pchannel;
	u32 free_sqe_cnt;

	if (task_info->chn >= data->psdma_dev->nr_channel) {
		dev_err(&data->psdma_dev->pdev->dev, "Invalid channel num:%u!\n", task_info->chn);
		return -EINVAL;
	}
	pchannel = data->psdma_dev->channels + task_info->chn;
	free_sqe_cnt = sdma_channel_free_sqe_cnt(pchannel);
	if (task_info->task_cnt > HISI_SDMA_MAX_ALLOC_SIZE / sizeof(struct hisi_sdma_sqe_task) ||
	    task_info->task_cnt > free_sqe_cnt || task_info->task_cnt == 0) {
		dev_err(&data->psdma_dev->pdev->dev, "Invalid send task cnt:%u!\n",
			task_info->task_cnt);
		return -EINVAL;
	}

	return 0;
}
@@ -528,25 +593,20 @@ static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_t
static int ioctl_sdma_send_task(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct device *dev = &data->psdma_dev->pdev->dev;
	struct hisi_sdma_task_info task_info;
	struct hisi_sdma_sqe_task *task_list;
	int ret;

	if (copy_from_user(&task_info, (struct hisi_sdma_task_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_task_info))) {
		dev_err(&data->psdma_dev->pdev->dev, "get hisi_sdma_task_info failed\n");
		dev_err(dev, "get hisi_sdma_task_info failed\n");
		return -EFAULT;
	}
	if (task_info.chn < 0 || (u32)task_info.chn >= data->psdma_dev->nr_channel) {
		dev_err(&data->psdma_dev->pdev->dev, "Invalid channel num:%d!\n", task_info.chn);
		return -EINVAL;
	}
	if (task_info.task_cnt > HISI_SDMA_MAX_ALLOC_SIZE / sizeof(struct hisi_sdma_sqe_task) ||
	    task_info.task_cnt == 0) {
		dev_err(&data->psdma_dev->pdev->dev, "Invalid send task cnt:%u!\n",
			task_info.task_cnt);
		return -EINVAL;
	}
	ret = sdma_task_info_validate(data, &task_info);
	if (ret != 0)
		return ret;

	task_list = kcalloc_node(task_info.task_cnt, sizeof(struct hisi_sdma_sqe_task), GFP_KERNEL,
				 data->psdma_dev->node_idx);
	if (!task_list)
@@ -554,17 +614,19 @@ static int ioctl_sdma_send_task(struct file *file, unsigned long arg)

	if (copy_from_user(task_list, (void __user *)task_info.task_addr,
			   task_info.task_cnt * sizeof(struct hisi_sdma_sqe_task))) {
		dev_err(&data->psdma_dev->pdev->dev, "get hisi_sdma_sqe_task failed\n");
		dev_err(dev, "get hisi_sdma_sqe_task failed\n");
		ret = -EFAULT;
		goto free_list;
	}
	ret = sdma_send_task_kernel(data, &task_info, task_list);
	if (ret < 0)
		dev_err(&data->psdma_dev->pdev->dev, "exec sdma_send_task_kernel failed\n");
	if (ret < 0) {
		dev_err(dev, "exec sdma_send_task_kernel failed\n");
		goto free_list;
	}

	if (copy_to_user((struct hisi_sdma_task_info __user *)(uintptr_t)arg, &task_info,
			 sizeof(struct hisi_sdma_task_info))) {
		dev_err(&data->psdma_dev->pdev->dev, "set hisi_sdma_task_info failed\n");
		dev_err(dev, "set hisi_sdma_task_info failed\n");
		ret = -EFAULT;
	}

@@ -587,8 +649,8 @@ static int sdma_operation_reg(struct hisi_sdma_device *pdev, unsigned long arg,
		return -EFAULT;
	}

	if (reg_info.chn < 0 || (u32)reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%d!\n", reg_info.chn);
	if (reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%u!\n", reg_info.chn);
		return -EINVAL;
	}
	pchannel = pdev->channels + reg_info.chn;
@@ -650,19 +712,21 @@ static int ioctl_sdma_dfx_reg(struct file *file, unsigned long arg)
	dev = &pdev->pdev->dev;
	if (copy_from_user(&reg_info, (struct hisi_sdma_reg_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_reg_info))) {
		dev_err(dev, "get hisi_sdma_dfx_reg failed\n");
		dev_err(dev, "dfx_reg copy from user failed\n");
		return -EFAULT;
	}

	if (reg_info.chn < 0 || (u32)reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%d!\n", reg_info.chn);
	if (reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%u!\n", reg_info.chn);
		return -EINVAL;
	}
	pchannel = pdev->channels + reg_info.chn;
	reg_info.reg_value = sdma_channel_get_dfx(pchannel);
	if (copy_to_user((struct hisi_sdma_reg_info __user *)(uintptr_t)arg, &reg_info,
			 sizeof(struct hisi_sdma_reg_info)))
			 sizeof(struct hisi_sdma_reg_info))) {
		dev_err(dev, "dfx_reg copy to user failed\n");
		return -EFAULT;
	}

	return 0;
}
@@ -675,16 +739,16 @@ static int ioctl_sdma_sqe_cnt_reg(struct file *file, unsigned long arg)
	struct hisi_sdma_channel *pchannel;
	struct device *dev;

	if (reg_info.chn < 0 || (u32)reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%d!\n", reg_info.chn);
		return -EINVAL;
	}
	dev = &pdev->pdev->dev;
	if (copy_from_user(&reg_info, (struct hisi_sdma_reg_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_reg_info))) {
		dev_err(dev, "get hisi_sdma_reg_info failed\n");
		return -EFAULT;
	}
	if (reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%u!\n", reg_info.chn);
		return -EINVAL;
	}

	pchannel = pdev->channels + reg_info.chn;
	if (reg_info.type == HISI_SDMA_CLR_NORMAL_SQE_CNT)
@@ -722,7 +786,7 @@ static long sdma_dev_ioctl(struct file *file, unsigned int cmd, unsigned long ar
	int cmd_num;
	int i;

	cmd_num = sizeof(g_ioctl_funcs) / sizeof(struct hisi_sdma_ioctl_func_list);
	cmd_num = ARRAY_SIZE(g_ioctl_funcs);
	for (i = 0; i < cmd_num; i++) {
		if (g_ioctl_funcs[i].cmd == cmd)
			return g_ioctl_funcs[i].ioctl_func(file, arg);
@@ -736,6 +800,7 @@ static int sdma_core_open(struct inode *inode, struct file *file)
	struct hisi_sdma_device *psdma_dev;
	dev_t sdma_dev;
	u32 sdma_idx;
	int ret;

	if (g_info.core_dev->sdma_device_num == 0) {
		pr_err("cannot find a sdma device\n");
@@ -744,11 +809,24 @@ static int sdma_core_open(struct inode *inode, struct file *file)
	sdma_dev = inode->i_rdev;
	sdma_idx = MINOR(sdma_dev);
	if (sdma_idx >= HISI_SDMA_MAX_DEVS) {
		pr_err("secondary device number overflow\n");
		pr_err("wrong id of sdma device\n");
		return -ENODEV;
	}
	spin_lock(&g_info.core_dev->device_lock);
	psdma_dev = g_info.core_dev->sdma_devices[sdma_idx];
	return __do_sdma_open(psdma_dev, file);
	if (!psdma_dev) {
		spin_unlock(&g_info.core_dev->device_lock);
		pr_err("cannot find sdma%u\n", sdma_idx);
		return -ENODEV;
	}
	ret = __do_sdma_open(psdma_dev, file);
	spin_unlock(&g_info.core_dev->device_lock);
	if (ret != 0) {
		pr_err("open sdma failed\n");
		return ret;
	}

	return 0;
}

ssize_t sdma_read_info(struct file *file, char __user *buf, size_t size, loff_t *ppos)
@@ -779,7 +857,7 @@ static int sdma_dev_release(struct inode *inode, struct file *file)

	spin_lock(&pdev->channel_lock);
	list_for_each_entry_safe(c, n, &data->non_share_chn_list, chn_list) {
		dev_dbg(dev, "release non_share_chn%d\n", c->chn_idx);
		dev_dbg(dev, "release non_share_chn%u\n", c->chn_idx);
		bitmap_set(pdev->channel_map, c->chn_idx - share_chns, 1);
		list_del(&c->chn_list);
		kfree(c);
@@ -787,7 +865,7 @@ static int sdma_dev_release(struct inode *inode, struct file *file)
	}

	list_for_each_entry_safe(c, n, &data->share_chn_list, chn_list) {
		dev_dbg(dev, "release share_chn%d\n", c->chn_idx);
		dev_dbg(dev, "release share_chn%u\n", c->chn_idx);
		pchannel = pdev->channels + c->chn_idx;
		pchannel->cnt_used--;
		if (pchannel->sync_info_base->lock != 0 &&
@@ -796,15 +874,15 @@ static int sdma_dev_release(struct inode *inode, struct file *file)
			pchannel->sync_info_base->lock = 0;
			pchannel->sync_info_base->lock_pid = 0;
		}
		if (pchannel->cnt_used == 0)
			pchannel->sync_info_base->err_cnt = 0;
		list_del(&c->chn_list);
		kfree(c);
	}
	spin_unlock(&pdev->channel_lock);

	if (data->handle) {
	if (data->handle)
		iommu_sva_unbind_device(data->handle);
		data->handle = NULL;
	}

	sdma_hash_free_entry(data->ida);
	sdma_free_authority_ht_with_pid(pid);
@@ -814,28 +892,38 @@ static int sdma_dev_release(struct inode *inode, struct file *file)
	return 0;
}

static int remap_addr_range(struct hisi_sdma_device *psdma_dev, u32 chn_num, u64 offset, u64 size)
static int remap_addr_range(u32 chn_num, u64 offset, u64 size)
{
	u64 sync_size;

	sync_size = (u64)((sizeof(struct hisi_sdma_queue_info) + PAGE_SIZE - ALIGN_NUM) /
		    PAGE_SIZE * PAGE_SIZE);

	if (offset >= chn_num * (HISI_SDMA_MMAP_SHMEM + 1))
	if (offset >= chn_num * (HISI_SDMA_MMAP_SHMEM + 1)) {
		pr_err("sdma mmap offset exceed range\n");
		return -EINVAL;
	}

	if (offset < chn_num * HISI_SDMA_MMAP_CQE) {
		pr_err("sdma not support sqe mmap\n");
		return -EINVAL;
	} else if (offset < chn_num * HISI_SDMA_MMAP_IO) {
		if (size > HISI_SDMA_CQ_SIZE)
		if (size > HISI_SDMA_CQ_SIZE) {
			pr_err("sdma mmap size exceed cqe range\n");
			return -EINVAL;
		}
		return HISI_SDMA_MMAP_CQE;
	} else if (offset < chn_num * HISI_SDMA_MMAP_SHMEM)
	} else if (offset < chn_num * HISI_SDMA_MMAP_SHMEM) {
		pr_err("sdma not support io reg mmap\n");
		return -EINVAL;
	if (size > sync_size)
	} else {
		if (size > sync_size) {
			pr_err("sdma mmap size exceed share mem range\n");
			return -EINVAL;
		}
		return HISI_SDMA_MMAP_SHMEM;
	}
}

static int sdma_dev_mmap(struct file *file, struct vm_area_struct *vma)
{
@@ -852,10 +940,10 @@ static int sdma_dev_mmap(struct file *file, struct vm_area_struct *vma)
	io_base = data->psdma_dev->base_addr;
	size = vma->vm_end - vma->vm_start;
	offset = vma->vm_pgoff;
	vma->vm_flags |= VM_DONTEXPAND | VM_WIPEONFORK | VM_DONTCOPY;

	dev_dbg(dev, "sdma total channel num = %u, user mmap offset = 0x%llx", chn_num, offset);

	switch (remap_addr_range(data->psdma_dev, chn_num, offset, size)) {
	switch (remap_addr_range(chn_num, offset, size)) {
	case HISI_SDMA_MMAP_CQE:
		pchan = chn_base + offset - chn_num * HISI_SDMA_MMAP_CQE;
		pfn_start = virt_to_phys(pchan->cq_base) >> PAGE_SHIFT;
@@ -871,9 +959,8 @@ static int sdma_dev_mmap(struct file *file, struct vm_area_struct *vma)
	default:
		return -EINVAL;
	}

	if (ret)
		dev_err(&data->psdma_dev->pdev->dev, "sdma mmap failed!\n");
		dev_err(dev, "sdma mmap failed!\n");

	return ret;
}
@@ -893,9 +980,9 @@ void sdma_cdev_init(struct cdev *cdev)
	cdev->owner = THIS_MODULE;
}

void sdma_info_sync_cdev(struct hisi_sdma_global_info *g_info_input)
void sdma_info_sync_cdev(struct hisi_sdma_core_device *p, u32 *share_chns, struct ida *fd_ida)
{
	g_info.core_dev = g_info_input->core_dev;
	g_info.fd_ida = g_info_input->fd_ida;
	g_info.share_chns = g_info_input->share_chns;
	g_info.core_dev = p;
	g_info.fd_ida = fd_ida;
	g_info.share_chns = share_chns;
}
+5 −1
Original line number Diff line number Diff line
@@ -29,6 +29,9 @@ struct hisi_sdma_channel {
	u16 idx;
	struct hisi_sdma_device *pdev;

	u32 ida;
	spinlock_t owner_chn_lock;

	/* must be page-aligned and continuous physical memory */
	struct hisi_sdma_sq_entry *sq_base;
	struct hisi_sdma_cq_entry *cq_base;
@@ -79,6 +82,7 @@ struct hisi_sdma_device {
struct hisi_sdma_core_device {
	u32 sdma_major;
	u32 sdma_device_num;
	spinlock_t device_lock;
	struct hisi_sdma_device *sdma_devices[HISI_SDMA_MAX_DEVS];
};

@@ -89,7 +93,7 @@ struct hisi_sdma_global_info {
};

void sdma_cdev_init(struct cdev *cdev);
void sdma_info_sync_cdev(struct hisi_sdma_global_info *g_info);
void sdma_info_sync_cdev(struct hisi_sdma_core_device *p, u32 *share_chns, struct ida *fd_ida);

static inline void chn_set_val(struct hisi_sdma_channel *pchan, int reg, u32 val, u32 mask)
{
+8 −6
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ irqreturn_t sdma_chn_ioe_irq_handle(int irq, void *psdma_dev)
	return IRQ_HANDLED;
}

void sdma_irq_init(struct hisi_sdma_device *sdma)
int sdma_irq_init(struct hisi_sdma_device *sdma)
{
	struct platform_device *pdev;
	void __iomem *io_addr;
@@ -61,7 +61,7 @@ void sdma_irq_init(struct hisi_sdma_device *sdma)
	u32 i;

	pdev = sdma->pdev;
	for (i = 0; i < sdma->nr_channel + HISI_STARS_CHN_NUM; i++) {
	for (i = HISI_STARS_CHN_NUM; i < sdma->nr_channel + HISI_STARS_CHN_NUM; i++) {
		io_addr = sdma->io_orig_base + i * HISI_SDMA_CHANNEL_IOMEM_SIZE;
		sdma_channel_set_irq_mask(io_addr, SDMA_IOC_MASKED_STATUS);
	}
@@ -72,27 +72,29 @@ void sdma_irq_init(struct hisi_sdma_device *sdma)
	for (i = 0; i < irq_cnt; i++) {
		vir_irq = platform_get_irq(pdev, i);
		if (vir_irq < 0) {
			dev_err(&pdev->dev, "get vir_irq[idx:%d] failed:%d!\n", i, vir_irq);
			dev_err(&pdev->dev, "get vir_irq[idx:%u] failed:%d!\n", i, vir_irq);
			sdma->irq[i] = -1;
			continue;
		}
		sdma->irq[i] = vir_irq;
	}

	for (i = INT_CH_IOE_SDMAM_0 + HISI_STARS_CHN_NUM; i <= INT_CH_IOE_SDMAM_255; i++) {
		if (sdma->irq[i] == -1)
	for (i = INT_CH_IOE_SDMAM_0 + HISI_STARS_CHN_NUM; i <= INT_CH_IOE_SDMAM_191; i++) {
		if (sdma->irq[i] == -1 || sdma->irq[i] == 0)
			continue;

		ret = devm_request_irq(&sdma->pdev->dev, sdma->irq[i], sdma_chn_ioe_irq_handle,
				       IRQF_ONESHOT, HISI_SDMA_IRQ_FUNC_NAME, sdma);
		if (ret != 0) {
			dev_err(&pdev->dev, "request_irq failed, ret=%d", ret);
			continue;
			return ret;
		}
	}

	for (i = 0; i < SDMA_IOE_NUM_MAX; i++)
		spin_lock_init(&err_set_lock[i]);

	return 0;
}

void sdma_irq_deinit(struct hisi_sdma_device *sdma)
Loading