Commit b9b5cb5e authored by Zhiqi Song's avatar Zhiqi Song
Browse files

crypto: hisilicon/qm - save capability registers in qm init process

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I7BANJ


CVE: NA

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

We find that in the reset scenario, if the reset failed and the MSE
is disabled, the value of capability registers will became invalid.
When we remove the device under this situation, the unregister process
will read the related irq vector from the capability register directly
with the mask. Then we will get an invalid value which is out of range
and can not be used to get the right irq number by pci_irq_vector().
This will cause the following call trace:

	| Call trace:
	|  pci_irq_vector+0xfc/0x140
	|  hisi_qm_uninit+0x278/0x3b0 [hisi_qm]
	|  hpre_remove+0x16c/0x1c0 [hisi_hpre]
	|  pci_device_remove+0x6c/0x264
	|  device_release_driver_internal+0x1ec/0x3e0
	|  device_release_driver+0x3c/0x60
	|  pci_stop_bus_device+0xfc/0x22c
	|  pci_stop_and_remove_bus_device+0x38/0x70
	|  pci_iov_remove_virtfn+0x108/0x1c0
	|  sriov_disable+0x7c/0x1e4
	|  pci_disable_sriov+0x4c/0x6c
	|  hisi_qm_sriov_disable+0x90/0x160 [hisi_qm]
	|  hpre_remove+0x1a8/0x1c0 [hisi_hpre]
	|  pci_device_remove+0x6c/0x264
	|  device_release_driver_internal+0x1ec/0x3e0
	|  driver_detach+0x168/0x2d0
	|  bus_remove_driver+0xc0/0x230
	|  driver_unregister+0x58/0xdc
	|  pci_unregister_driver+0x40/0x220
	|  hpre_exit+0x34/0x64 [hisi_hpre]
	|  __arm64_sys_delete_module+0x374/0x620
	[...]

	| Call trace:
	|  free_msi_irqs+0x25c/0x300
	|  pci_disable_msi+0x19c/0x264
	|  pci_free_irq_vectors+0x4c/0x70
	|  hisi_qm_pci_uninit+0x44/0x90 [hisi_qm]
	|  hisi_qm_uninit+0x28c/0x3b0 [hisi_qm]
	|  hpre_remove+0x16c/0x1c0 [hisi_hpre]
	|  pci_device_remove+0x6c/0x264
	[...]
So we pre-store the valid value of the capability register to a global
array in qm init process, and read the register value from this array
when we need it. This ensures we can always get valid values.

Signed-off-by: default avatarZhiqi Song <songzhiqi1@huawei.com>
Signed-off-by: default avatarJiangShui Yang <yangjiangshui@h-partners.com>
parent 04e35199
Loading
Loading
Loading
Loading
+35 −8
Original line number Diff line number Diff line
@@ -306,6 +306,13 @@ enum qm_basic_type {
	QM_VF_IRQ_NUM_CAP,
};

enum qm_irq_type_caps_idx {
	QM_EQ_IRQ_TYPE_CAP_IDX,
	QM_AEQ_IRQ_TYPE_CAP_IDX,
	QM_ABN_IRQ_TYPE_CAP_IDX,
	QM_PF2VF_IRQ_TYPE_CAP_IDX
};

static const struct hisi_qm_cap_info qm_cap_info_comm[] = {
	{QM_SUPPORT_DB_ISOLATION, 0x30,   0, BIT(0),  0x0, 0x0, 0x0},
	{QM_SUPPORT_FUNC_QOS,     0x3100, 0, BIT(8),  0x0, 0x0, 0x1},
@@ -335,6 +342,13 @@ static const struct hisi_qm_cap_info qm_basic_info[] = {
	{QM_VF_IRQ_NUM_CAP,     0x311c,   0,  GENMASK(15, 0), 0x1,       0x2,       0x3},
};

static struct hisi_qm_cap_record qm_irq_type_caps[] = {
	{QM_EQ_IRQ_TYPE_CAP,    0x10000},
	{QM_AEQ_IRQ_TYPE_CAP,   0x10001},
	{QM_ABN_IRQ_TYPE_CAP,   0x10003},
	{QM_PF2VF_IRQ_TYPE_CAP, 0x10002},
};

struct qm_mailbox {
	__le16 w0;
	__le16 queue_num;
@@ -5102,7 +5116,7 @@ static void qm_unregister_abnormal_irq(struct hisi_qm *qm)
	if (qm->fun_type == QM_HW_VF)
		return;

	val = hisi_qm_get_hw_info(qm, qm_basic_info, QM_ABN_IRQ_TYPE_CAP, qm->cap_ver);
	val = qm_irq_type_caps[QM_ABN_IRQ_TYPE_CAP_IDX].cap_val;
	if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_ABN_IRQ_TYPE_MASK))
		return;

@@ -5119,7 +5133,7 @@ static int qm_register_abnormal_irq(struct hisi_qm *qm)
	if (qm->fun_type == QM_HW_VF)
		return 0;

	val = hisi_qm_get_hw_info(qm, qm_basic_info, QM_ABN_IRQ_TYPE_CAP, qm->cap_ver);
	val = qm_irq_type_caps[QM_ABN_IRQ_TYPE_CAP_IDX].cap_val;
	if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_ABN_IRQ_TYPE_MASK))
		return 0;

@@ -5136,7 +5150,7 @@ static void qm_unregister_mb_cmd_irq(struct hisi_qm *qm)
	struct pci_dev *pdev = qm->pdev;
	u32 irq_vector, val;

	val = hisi_qm_get_hw_info(qm, qm_basic_info, QM_PF2VF_IRQ_TYPE_CAP, qm->cap_ver);
	val = qm_irq_type_caps[QM_PF2VF_IRQ_TYPE_CAP_IDX].cap_val;
	if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_IRQ_TYPE_MASK))
		return;

@@ -5150,7 +5164,7 @@ static int qm_register_mb_cmd_irq(struct hisi_qm *qm)
	u32 irq_vector, val;
	int ret;

	val = hisi_qm_get_hw_info(qm, qm_basic_info, QM_PF2VF_IRQ_TYPE_CAP, qm->cap_ver);
	val = qm_irq_type_caps[QM_PF2VF_IRQ_TYPE_CAP_IDX].cap_val;
	if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_IRQ_TYPE_MASK))
		return 0;

@@ -5167,7 +5181,7 @@ static void qm_unregister_aeq_irq(struct hisi_qm *qm)
	struct pci_dev *pdev = qm->pdev;
	u32 irq_vector, val;

	val = hisi_qm_get_hw_info(qm, qm_basic_info, QM_AEQ_IRQ_TYPE_CAP, qm->cap_ver);
	val = qm_irq_type_caps[QM_AEQ_IRQ_TYPE_CAP_IDX].cap_val;
	if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_IRQ_TYPE_MASK))
		return;

@@ -5181,7 +5195,7 @@ static int qm_register_aeq_irq(struct hisi_qm *qm)
	u32 irq_vector, val;
	int ret;

	val = hisi_qm_get_hw_info(qm, qm_basic_info, QM_AEQ_IRQ_TYPE_CAP, qm->cap_ver);
	val = qm_irq_type_caps[QM_AEQ_IRQ_TYPE_CAP_IDX].cap_val;
	if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_IRQ_TYPE_MASK))
		return 0;

@@ -5199,7 +5213,7 @@ static void qm_unregister_eq_irq(struct hisi_qm *qm)
	struct pci_dev *pdev = qm->pdev;
	u32 irq_vector, val;

	val = hisi_qm_get_hw_info(qm, qm_basic_info, QM_EQ_IRQ_TYPE_CAP, qm->cap_ver);
	val = qm_irq_type_caps[QM_EQ_IRQ_TYPE_CAP_IDX].cap_val;
	if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_IRQ_TYPE_MASK))
		return;

@@ -5213,7 +5227,7 @@ static int qm_register_eq_irq(struct hisi_qm *qm)
	u32 irq_vector, val;
	int ret;

	val = hisi_qm_get_hw_info(qm, qm_basic_info, QM_EQ_IRQ_TYPE_CAP, qm->cap_ver);
	val = qm_irq_type_caps[QM_EQ_IRQ_TYPE_CAP_IDX].cap_val;
	if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_IRQ_TYPE_MASK))
		return 0;

@@ -5300,6 +5314,16 @@ static int qm_get_qp_num(struct hisi_qm *qm)
	return 0;
}

static void qm_pre_store_irq_type_caps(struct hisi_qm *qm)
{
	int i, size;

	size = ARRAY_SIZE(qm_irq_type_caps);
	for (i = 0; i < size; i++)
		qm_irq_type_caps[i].cap_val = hisi_qm_get_hw_info(qm, qm_basic_info,
					      qm_irq_type_caps[i].type, qm->cap_ver);
}

static void qm_get_hw_caps(struct hisi_qm *qm)
{
	const struct hisi_qm_cap_info *cap_info = qm->fun_type == QM_HW_PF ?
@@ -5331,6 +5355,9 @@ static void qm_get_hw_caps(struct hisi_qm *qm)
		if (val)
			set_bit(cap_info[i].type, &qm->caps);
	}

	/* Fetch and save the value of irq type related capability registers */
	qm_pre_store_irq_type_caps(qm);
}

static int qm_get_pci_res(struct hisi_qm *qm)
+5 −0
Original line number Diff line number Diff line
@@ -287,6 +287,11 @@ struct hisi_qm_cap_info {
	u32 v3_val;
};

struct hisi_qm_cap_record {
	u32 type;
	u32 cap_val;
};

struct hisi_qm_list {
	struct mutex lock;
	struct list_head list;