Commit 7eff82b0 authored by Yuval Mintz's avatar Yuval Mintz Committed by David S. Miller

qed: Handle malicious VFs events

Malicious VFs might be caught in several different methods:
  - Misusing their bar permission and being blocked by hardware.
  - Misusing their fastpath logic and being blocked by firmware.
  - Misusing their interaction with their PF via hw-channel,
    and being blocked by PF driver.

On the first two items, firmware would indicate to driver that
the VF is to be considered malicious, but would sometime still
allow the VF to communicate with the PF [depending on the exact
nature of the malicious activity done by the VF].
The current existing logic on the PF side lacks handling of such events,
and might allow the PF to perform some incorrect configuration on behalf
of a VF that was previously indicated as malicious.

The new scheme is simple -
Once the PF determines a VF is malicious it would:
 a. Ignore any further requests on behalf of the VF-driver.
 b. Prevent any configurations initiated by the hyperuser for
    the malicious VF, as firmware isn't willing to serve such.

The malicious indication would be cleared upon the VF flr,
after which it would become usable once again.
Signed-off-by: default avatarYuval Mintz <Yuval.Mintz@caviumnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c59f5291
...@@ -109,7 +109,8 @@ static int qed_sp_vf_stop(struct qed_hwfn *p_hwfn, ...@@ -109,7 +109,8 @@ static int qed_sp_vf_stop(struct qed_hwfn *p_hwfn,
} }
static bool qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn, static bool qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn,
int rel_vf_id, bool b_enabled_only) int rel_vf_id,
bool b_enabled_only, bool b_non_malicious)
{ {
if (!p_hwfn->pf_iov_info) { if (!p_hwfn->pf_iov_info) {
DP_NOTICE(p_hwfn->cdev, "No iov info\n"); DP_NOTICE(p_hwfn->cdev, "No iov info\n");
...@@ -124,6 +125,10 @@ static bool qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn, ...@@ -124,6 +125,10 @@ static bool qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn,
b_enabled_only) b_enabled_only)
return false; return false;
if ((p_hwfn->pf_iov_info->vfs_array[rel_vf_id].b_malicious) &&
b_non_malicious)
return false;
return true; return true;
} }
...@@ -138,7 +143,8 @@ static struct qed_vf_info *qed_iov_get_vf_info(struct qed_hwfn *p_hwfn, ...@@ -138,7 +143,8 @@ static struct qed_vf_info *qed_iov_get_vf_info(struct qed_hwfn *p_hwfn,
return NULL; return NULL;
} }
if (qed_iov_is_valid_vfid(p_hwfn, relative_vf_id, b_enabled_only)) if (qed_iov_is_valid_vfid(p_hwfn, relative_vf_id,
b_enabled_only, false))
vf = &p_hwfn->pf_iov_info->vfs_array[relative_vf_id]; vf = &p_hwfn->pf_iov_info->vfs_array[relative_vf_id];
else else
DP_ERR(p_hwfn, "qed_iov_get_vf_info: VF[%d] is not enabled\n", DP_ERR(p_hwfn, "qed_iov_get_vf_info: VF[%d] is not enabled\n",
...@@ -542,7 +548,8 @@ int qed_iov_hw_info(struct qed_hwfn *p_hwfn) ...@@ -542,7 +548,8 @@ int qed_iov_hw_info(struct qed_hwfn *p_hwfn)
return 0; return 0;
} }
static bool qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, int vfid) bool _qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn,
int vfid, bool b_fail_malicious)
{ {
/* Check PF supports sriov */ /* Check PF supports sriov */
if (IS_VF(p_hwfn->cdev) || !IS_QED_SRIOV(p_hwfn->cdev) || if (IS_VF(p_hwfn->cdev) || !IS_QED_SRIOV(p_hwfn->cdev) ||
...@@ -550,12 +557,17 @@ static bool qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, int vfid) ...@@ -550,12 +557,17 @@ static bool qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, int vfid)
return false; return false;
/* Check VF validity */ /* Check VF validity */
if (!qed_iov_is_valid_vfid(p_hwfn, vfid, true)) if (!qed_iov_is_valid_vfid(p_hwfn, vfid, true, b_fail_malicious))
return false; return false;
return true; return true;
} }
bool qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, int vfid)
{
return _qed_iov_pf_sanity_check(p_hwfn, vfid, true);
}
static void qed_iov_set_vf_to_disable(struct qed_dev *cdev, static void qed_iov_set_vf_to_disable(struct qed_dev *cdev,
u16 rel_vf_id, u8 to_disable) u16 rel_vf_id, u8 to_disable)
{ {
...@@ -652,6 +664,9 @@ static int qed_iov_enable_vf_access(struct qed_hwfn *p_hwfn, ...@@ -652,6 +664,9 @@ static int qed_iov_enable_vf_access(struct qed_hwfn *p_hwfn,
qed_iov_vf_igu_reset(p_hwfn, p_ptt, vf); qed_iov_vf_igu_reset(p_hwfn, p_ptt, vf);
/* It's possible VF was previously considered malicious */
vf->b_malicious = false;
rc = qed_mcp_config_vf_msix(p_hwfn, p_ptt, vf->abs_vf_id, vf->num_sbs); rc = qed_mcp_config_vf_msix(p_hwfn, p_ptt, vf->abs_vf_id, vf->num_sbs);
if (rc) if (rc)
return rc; return rc;
...@@ -2804,6 +2819,13 @@ qed_iov_execute_vf_flr_cleanup(struct qed_hwfn *p_hwfn, ...@@ -2804,6 +2819,13 @@ qed_iov_execute_vf_flr_cleanup(struct qed_hwfn *p_hwfn,
return rc; return rc;
} }
/* Workaround to make VF-PF channel ready, as FW
* doesn't do that as a part of FLR.
*/
REG_WR(p_hwfn,
GTT_BAR0_MAP_REG_USDM_RAM +
USTORM_VF_PF_CHANNEL_READY_OFFSET(vfid), 1);
/* VF_STOPPED has to be set only after final cleanup /* VF_STOPPED has to be set only after final cleanup
* but prior to re-enabling the VF. * but prior to re-enabling the VF.
*/ */
...@@ -2942,7 +2964,8 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, ...@@ -2942,7 +2964,8 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn,
mbx->first_tlv = mbx->req_virt->first_tlv; mbx->first_tlv = mbx->req_virt->first_tlv;
/* check if tlv type is known */ /* check if tlv type is known */
if (qed_iov_tlv_supported(mbx->first_tlv.tl.type)) { if (qed_iov_tlv_supported(mbx->first_tlv.tl.type) &&
!p_vf->b_malicious) {
switch (mbx->first_tlv.tl.type) { switch (mbx->first_tlv.tl.type) {
case CHANNEL_TLV_ACQUIRE: case CHANNEL_TLV_ACQUIRE:
qed_iov_vf_mbx_acquire(p_hwfn, p_ptt, p_vf); qed_iov_vf_mbx_acquire(p_hwfn, p_ptt, p_vf);
...@@ -2984,6 +3007,15 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, ...@@ -2984,6 +3007,15 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn,
qed_iov_vf_mbx_release(p_hwfn, p_ptt, p_vf); qed_iov_vf_mbx_release(p_hwfn, p_ptt, p_vf);
break; break;
} }
} else if (qed_iov_tlv_supported(mbx->first_tlv.tl.type)) {
DP_VERBOSE(p_hwfn, QED_MSG_IOV,
"VF [%02x] - considered malicious; Ignoring TLV [%04x]\n",
p_vf->abs_vf_id, mbx->first_tlv.tl.type);
qed_iov_prepare_resp(p_hwfn, p_ptt, p_vf,
mbx->first_tlv.tl.type,
sizeof(struct pfvf_def_resp_tlv),
PFVF_STATUS_MALICIOUS);
} else { } else {
/* unknown TLV - this may belong to a VF driver from the future /* unknown TLV - this may belong to a VF driver from the future
* - a version written after this PF driver was written, which * - a version written after this PF driver was written, which
...@@ -3033,20 +3065,30 @@ static void qed_iov_pf_get_and_clear_pending_events(struct qed_hwfn *p_hwfn, ...@@ -3033,20 +3065,30 @@ static void qed_iov_pf_get_and_clear_pending_events(struct qed_hwfn *p_hwfn,
memset(p_pending_events, 0, sizeof(u64) * QED_VF_ARRAY_LENGTH); memset(p_pending_events, 0, sizeof(u64) * QED_VF_ARRAY_LENGTH);
} }
static int qed_sriov_vfpf_msg(struct qed_hwfn *p_hwfn, static struct qed_vf_info *qed_sriov_get_vf_from_absid(struct qed_hwfn *p_hwfn,
u16 abs_vfid, struct regpair *vf_msg) u16 abs_vfid)
{ {
u8 min = (u8)p_hwfn->cdev->p_iov_info->first_vf_in_pf; u8 min = (u8) p_hwfn->cdev->p_iov_info->first_vf_in_pf;
struct qed_vf_info *p_vf;
if (!qed_iov_pf_sanity_check(p_hwfn, (int)abs_vfid - min)) { if (!_qed_iov_pf_sanity_check(p_hwfn, (int)abs_vfid - min, false)) {
DP_VERBOSE(p_hwfn, DP_VERBOSE(p_hwfn,
QED_MSG_IOV, QED_MSG_IOV,
"Got a message from VF [abs 0x%08x] that cannot be handled by PF\n", "Got indication for VF [abs 0x%08x] that cannot be handled by PF\n",
abs_vfid); abs_vfid);
return 0; return NULL;
} }
p_vf = &p_hwfn->pf_iov_info->vfs_array[(u8)abs_vfid - min];
return &p_hwfn->pf_iov_info->vfs_array[(u8) abs_vfid - min];
}
static int qed_sriov_vfpf_msg(struct qed_hwfn *p_hwfn,
u16 abs_vfid, struct regpair *vf_msg)
{
struct qed_vf_info *p_vf = qed_sriov_get_vf_from_absid(p_hwfn,
abs_vfid);
if (!p_vf)
return 0;
/* List the physical address of the request so that handler /* List the physical address of the request so that handler
* could later on copy the message from it. * could later on copy the message from it.
...@@ -3060,6 +3102,23 @@ static int qed_sriov_vfpf_msg(struct qed_hwfn *p_hwfn, ...@@ -3060,6 +3102,23 @@ static int qed_sriov_vfpf_msg(struct qed_hwfn *p_hwfn,
return 0; return 0;
} }
static void qed_sriov_vfpf_malicious(struct qed_hwfn *p_hwfn,
struct malicious_vf_eqe_data *p_data)
{
struct qed_vf_info *p_vf;
p_vf = qed_sriov_get_vf_from_absid(p_hwfn, p_data->vf_id);
if (!p_vf)
return;
DP_INFO(p_hwfn,
"VF [%d] - Malicious behavior [%02x]\n",
p_vf->abs_vf_id, p_data->err_id);
p_vf->b_malicious = true;
}
int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn,
u8 opcode, __le16 echo, union event_ring_data *data) u8 opcode, __le16 echo, union event_ring_data *data)
{ {
...@@ -3067,6 +3126,9 @@ int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, ...@@ -3067,6 +3126,9 @@ int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn,
case COMMON_EVENT_VF_PF_CHANNEL: case COMMON_EVENT_VF_PF_CHANNEL:
return qed_sriov_vfpf_msg(p_hwfn, le16_to_cpu(echo), return qed_sriov_vfpf_msg(p_hwfn, le16_to_cpu(echo),
&data->vf_pf_channel.msg_addr); &data->vf_pf_channel.msg_addr);
case COMMON_EVENT_MALICIOUS_VF:
qed_sriov_vfpf_malicious(p_hwfn, &data->malicious_vf);
return 0;
default: default:
DP_INFO(p_hwfn->cdev, "Unknown sriov eqe event 0x%02x\n", DP_INFO(p_hwfn->cdev, "Unknown sriov eqe event 0x%02x\n",
opcode); opcode);
...@@ -3083,7 +3145,7 @@ u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn, u16 rel_vf_id) ...@@ -3083,7 +3145,7 @@ u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn, u16 rel_vf_id)
goto out; goto out;
for (i = rel_vf_id; i < p_iov->total_vfs; i++) for (i = rel_vf_id; i < p_iov->total_vfs; i++)
if (qed_iov_is_valid_vfid(p_hwfn, rel_vf_id, true)) if (qed_iov_is_valid_vfid(p_hwfn, rel_vf_id, true, false))
return i; return i;
out: out:
...@@ -3130,6 +3192,12 @@ static void qed_iov_bulletin_set_forced_mac(struct qed_hwfn *p_hwfn, ...@@ -3130,6 +3192,12 @@ static void qed_iov_bulletin_set_forced_mac(struct qed_hwfn *p_hwfn,
return; return;
} }
if (vf_info->b_malicious) {
DP_NOTICE(p_hwfn->cdev,
"Can't set forced MAC to malicious VF [%d]\n", vfid);
return;
}
feature = 1 << MAC_ADDR_FORCED; feature = 1 << MAC_ADDR_FORCED;
memcpy(vf_info->bulletin.p_virt->mac, mac, ETH_ALEN); memcpy(vf_info->bulletin.p_virt->mac, mac, ETH_ALEN);
...@@ -3153,6 +3221,12 @@ static void qed_iov_bulletin_set_forced_vlan(struct qed_hwfn *p_hwfn, ...@@ -3153,6 +3221,12 @@ static void qed_iov_bulletin_set_forced_vlan(struct qed_hwfn *p_hwfn,
return; return;
} }
if (vf_info->b_malicious) {
DP_NOTICE(p_hwfn->cdev,
"Can't set forced vlan to malicious VF [%d]\n", vfid);
return;
}
feature = 1 << VLAN_ADDR_FORCED; feature = 1 << VLAN_ADDR_FORCED;
vf_info->bulletin.p_virt->pvid = pvid; vf_info->bulletin.p_virt->pvid = pvid;
if (pvid) if (pvid)
...@@ -3367,7 +3441,7 @@ int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled) ...@@ -3367,7 +3441,7 @@ int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled)
qed_for_each_vf(hwfn, j) { qed_for_each_vf(hwfn, j) {
int k; int k;
if (!qed_iov_is_valid_vfid(hwfn, j, true)) if (!qed_iov_is_valid_vfid(hwfn, j, true, false))
continue; continue;
/* Wait until VF is disabled before releasing */ /* Wait until VF is disabled before releasing */
...@@ -3425,7 +3499,7 @@ static int qed_sriov_enable(struct qed_dev *cdev, int num) ...@@ -3425,7 +3499,7 @@ static int qed_sriov_enable(struct qed_dev *cdev, int num)
num_sbs = min_t(int, sb_cnt_info.sb_free_blk, limit); num_sbs = min_t(int, sb_cnt_info.sb_free_blk, limit);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
if (!qed_iov_is_valid_vfid(hwfn, i, false)) if (!qed_iov_is_valid_vfid(hwfn, i, false, true))
continue; continue;
rc = qed_iov_init_hw_for_vf(hwfn, rc = qed_iov_init_hw_for_vf(hwfn,
...@@ -3477,7 +3551,7 @@ static int qed_sriov_pf_set_mac(struct qed_dev *cdev, u8 *mac, int vfid) ...@@ -3477,7 +3551,7 @@ static int qed_sriov_pf_set_mac(struct qed_dev *cdev, u8 *mac, int vfid)
return -EINVAL; return -EINVAL;
} }
if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true)) { if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true, true)) {
DP_VERBOSE(cdev, QED_MSG_IOV, DP_VERBOSE(cdev, QED_MSG_IOV,
"Cannot set VF[%d] MAC (VF is not active)\n", vfid); "Cannot set VF[%d] MAC (VF is not active)\n", vfid);
return -EINVAL; return -EINVAL;
...@@ -3509,7 +3583,7 @@ static int qed_sriov_pf_set_vlan(struct qed_dev *cdev, u16 vid, int vfid) ...@@ -3509,7 +3583,7 @@ static int qed_sriov_pf_set_vlan(struct qed_dev *cdev, u16 vid, int vfid)
return -EINVAL; return -EINVAL;
} }
if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true)) { if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true, true)) {
DP_VERBOSE(cdev, QED_MSG_IOV, DP_VERBOSE(cdev, QED_MSG_IOV,
"Cannot set VF[%d] MAC (VF is not active)\n", vfid); "Cannot set VF[%d] MAC (VF is not active)\n", vfid);
return -EINVAL; return -EINVAL;
...@@ -3543,7 +3617,7 @@ static int qed_get_vf_config(struct qed_dev *cdev, ...@@ -3543,7 +3617,7 @@ static int qed_get_vf_config(struct qed_dev *cdev,
if (IS_VF(cdev)) if (IS_VF(cdev))
return -EINVAL; return -EINVAL;
if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true)) { if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true, false)) {
DP_VERBOSE(cdev, QED_MSG_IOV, DP_VERBOSE(cdev, QED_MSG_IOV,
"VF index [%d] isn't active\n", vf_id); "VF index [%d] isn't active\n", vf_id);
return -EINVAL; return -EINVAL;
...@@ -3647,7 +3721,7 @@ static int qed_set_vf_link_state(struct qed_dev *cdev, ...@@ -3647,7 +3721,7 @@ static int qed_set_vf_link_state(struct qed_dev *cdev,
if (IS_VF(cdev)) if (IS_VF(cdev))
return -EINVAL; return -EINVAL;
if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true)) { if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true, true)) {
DP_VERBOSE(cdev, QED_MSG_IOV, DP_VERBOSE(cdev, QED_MSG_IOV,
"VF index [%d] isn't active\n", vf_id); "VF index [%d] isn't active\n", vf_id);
return -EINVAL; return -EINVAL;
......
...@@ -132,6 +132,7 @@ struct qed_vf_info { ...@@ -132,6 +132,7 @@ struct qed_vf_info {
struct qed_iov_vf_mbx vf_mbx; struct qed_iov_vf_mbx vf_mbx;
enum vf_state state; enum vf_state state;
bool b_init; bool b_init;
bool b_malicious;
u8 to_disable; u8 to_disable;
struct qed_bulletin bulletin; struct qed_bulletin bulletin;
......
...@@ -40,6 +40,7 @@ enum { ...@@ -40,6 +40,7 @@ enum {
PFVF_STATUS_NOT_SUPPORTED, PFVF_STATUS_NOT_SUPPORTED,
PFVF_STATUS_NO_RESOURCE, PFVF_STATUS_NO_RESOURCE,
PFVF_STATUS_FORCED, PFVF_STATUS_FORCED,
PFVF_STATUS_MALICIOUS,
}; };
/* vf pf channel tlvs */ /* vf pf channel tlvs */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment