#include <linux/string.h>
 #include <linux/in.h>
 #include <linux/interrupt.h>
+#include <linux/iopoll.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/sctp.h>
        return ((head <= tail) ? tail : tail + ring->count) - head;
 }
 
+/**
+ * ixgbe_get_vf_idx - provide VF index number based on queue index
+ * @adapter: pointer to the adapter struct
+ * @queue: Tx queue identifier
+ * @vf: output VF index
+ *
+ * Provide VF index number associated to the input queue.
+ *
+ * Returns: 0 if VF provided or error number.
+ */
+static int ixgbe_get_vf_idx(struct ixgbe_adapter *adapter, u16 queue, u16 *vf)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       u8 queue_count;
+       u32 reg;
+
+       if (queue >= adapter->num_tx_queues)
+               return -EINVAL;
+
+       /* Determine number of queues by checking
+        * number of virtual functions
+        */
+       reg = IXGBE_READ_REG(hw, IXGBE_GCR_EXT);
+       switch (reg & IXGBE_GCR_EXT_VT_MODE_MASK) {
+       case IXGBE_GCR_EXT_VT_MODE_64:
+               queue_count = IXGBE_64VFS_QUEUES;
+               break;
+       case IXGBE_GCR_EXT_VT_MODE_32:
+               queue_count = IXGBE_32VFS_QUEUES;
+               break;
+       case IXGBE_GCR_EXT_VT_MODE_16:
+               queue_count = IXGBE_16VFS_QUEUES;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *vf = queue / queue_count;
+
+       return 0;
+}
+
 static bool ixgbe_check_tx_hang(struct ixgbe_ring *tx_ring)
 {
        u32 tx_done = ixgbe_get_tx_completed(tx_ring);
        q_vector->rx.total_packets += pkts;
 }
 
+/**
+ * ixgbe_pf_handle_tx_hang - handle Tx hang on PF
+ * @tx_ring: tx ring number
+ * @next: next ring
+ *
+ * Prints a message containing details about the tx hang.
+ */
+static void ixgbe_pf_handle_tx_hang(struct ixgbe_ring *tx_ring,
+                                   unsigned int next)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(tx_ring->netdev);
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       e_err(drv, "Detected Tx Unit Hang%s\n"
+                  "  Tx Queue             <%d>\n"
+                  "  TDH, TDT             <%x>, <%x>\n"
+                  "  next_to_use          <%x>\n"
+                  "  next_to_clean        <%x>\n"
+                  "tx_buffer_info[next_to_clean]\n"
+                  "  time_stamp           <%lx>\n"
+                  "  jiffies              <%lx>\n",
+             ring_is_xdp(tx_ring) ? " (XDP)" : "",
+             tx_ring->queue_index,
+             IXGBE_READ_REG(hw, IXGBE_TDH(tx_ring->reg_idx)),
+             IXGBE_READ_REG(hw, IXGBE_TDT(tx_ring->reg_idx)),
+             tx_ring->next_to_use, next,
+             tx_ring->tx_buffer_info[next].time_stamp, jiffies);
+
+       if (!ring_is_xdp(tx_ring))
+               netif_stop_subqueue(tx_ring->netdev,
+                                   tx_ring->queue_index);
+}
+
+/**
+ * ixgbe_vf_handle_tx_hang - handle Tx hang on VF
+ * @adapter: structure containing ring specific data
+ * @vf: VF index
+ *
+ * Print a message containing details about malicious driver detection.
+ * Set malicious VF link down if the detection happened several times.
+ */
+static void ixgbe_vf_handle_tx_hang(struct ixgbe_adapter *adapter, u16 vf)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       if (adapter->hw.mac.type != ixgbe_mac_e610)
+               return;
+
+       e_warn(drv,
+              "Malicious Driver Detection tx hang detected on PF %d VF %d MAC: %pM",
+              hw->bus.func, vf, adapter->vfinfo[vf].vf_mac_addresses);
+
+       adapter->tx_hang_count[vf]++;
+       if (adapter->tx_hang_count[vf] == IXGBE_MAX_TX_VF_HANGS) {
+               ixgbe_set_vf_link_state(adapter, vf,
+                                       IFLA_VF_LINK_STATE_DISABLE);
+               adapter->tx_hang_count[vf] = 0;
+       }
+}
+
+static u32 ixgbe_poll_tx_icache(struct ixgbe_hw *hw, u16 queue, u16 idx)
+{
+       IXGBE_WRITE_REG(hw, IXGBE_TXDESCIC, queue * idx);
+       return IXGBE_READ_REG(hw, IXGBE_TXDESCIC);
+}
+
+/**
+ * ixgbe_check_illegal_queue - search for queue with illegal packet
+ * @adapter: structure containing ring specific data
+ * @queue: queue index
+ *
+ * Check if tx descriptor connected with input queue
+ * contains illegal packet.
+ *
+ * Returns: true if queue contain illegal packet.
+ */
+static bool ixgbe_check_illegal_queue(struct ixgbe_adapter *adapter,
+                                     u16 queue)
+{
+       u32 hdr_len_reg, mss_len_reg, type_reg;
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 mss_len, header_len, reg;
+
+       for (u16 i = 0; i < IXGBE_MAX_TX_DESCRIPTORS; i++) {
+               /* HW will clear bit IXGBE_TXDESCIC_READY when address
+                * is written to address field. HW will set this bit
+                * when iCache read is done, and data is ready at TIC_DWx.
+                * Set descriptor address.
+                */
+               read_poll_timeout(ixgbe_poll_tx_icache, reg,
+                                 !(reg & IXGBE_TXDESCIC_READY), 0, 0, false,
+                                 hw, queue, i);
+
+               /* read tx descriptor access registers */
+               hdr_len_reg = IXGBE_READ_REG(hw, IXGBE_TIC_DW2(IXGBE_VLAN_MACIP_LENS_REG));
+               type_reg = IXGBE_READ_REG(hw, IXGBE_TIC_DW2(IXGBE_TYPE_TUCMD_MLHL));
+               mss_len_reg = IXGBE_READ_REG(hw, IXGBE_TIC_DW2(IXGBE_MSS_L4LEN_IDX));
+
+               /* check if Advanced Context Descriptor */
+               if (FIELD_GET(IXGBE_ADVTXD_DTYP_MASK, type_reg) !=
+                   IXGBE_ADVTXD_DTYP_CTXT)
+                       continue;
+
+               /* check for illegal MSS and Header length */
+               mss_len = FIELD_GET(IXGBE_ADVTXD_MSS_MASK, mss_len_reg);
+               header_len = FIELD_GET(IXGBE_ADVTXD_HEADER_LEN_MASK,
+                                      hdr_len_reg);
+               if ((mss_len + header_len) > SZ_16K) {
+                       e_warn(probe, "mss len + header len too long\n");
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+/**
+ * ixgbe_handle_mdd_event - handle mdd event
+ * @adapter: structure containing ring specific data
+ * @tx_ring: tx descriptor ring to handle
+ *
+ * Reset VF driver if malicious vf detected or
+ * illegal packet in an any queue detected.
+ */
+static void ixgbe_handle_mdd_event(struct ixgbe_adapter *adapter,
+                                  struct ixgbe_ring *tx_ring)
+{
+       u16 vf, q;
+
+       if (adapter->vfinfo && ixgbe_check_mdd_event(adapter)) {
+               /* vf mdd info and malicious vf detected */
+               if (!ixgbe_get_vf_idx(adapter, tx_ring->queue_index, &vf))
+                       ixgbe_vf_handle_tx_hang(adapter, vf);
+       } else {
+               /* malicious vf not detected */
+               for (q = 0; q < IXGBE_MAX_TX_QUEUES; q++) {
+                       if (ixgbe_check_illegal_queue(adapter, q) &&
+                           !ixgbe_get_vf_idx(adapter, q, &vf))
+                               /* illegal queue detected */
+                               ixgbe_vf_handle_tx_hang(adapter, vf);
+               }
+       }
+}
+
 /**
  * ixgbe_clean_tx_irq - Reclaim resources after transmit completes
  * @q_vector: structure containing interrupt and ring information
        adapter->tx_ipsec += total_ipsec;
 
        if (check_for_tx_hang(tx_ring) && ixgbe_check_tx_hang(tx_ring)) {
-               /* schedule immediate reset if we believe we hung */
-               struct ixgbe_hw *hw = &adapter->hw;
-               e_err(drv, "Detected Tx Unit Hang %s\n"
-                       "  Tx Queue             <%d>\n"
-                       "  TDH, TDT             <%x>, <%x>\n"
-                       "  next_to_use          <%x>\n"
-                       "  next_to_clean        <%x>\n"
-                       "tx_buffer_info[next_to_clean]\n"
-                       "  time_stamp           <%lx>\n"
-                       "  jiffies              <%lx>\n",
-                       ring_is_xdp(tx_ring) ? "(XDP)" : "",
-                       tx_ring->queue_index,
-                       IXGBE_READ_REG(hw, IXGBE_TDH(tx_ring->reg_idx)),
-                       IXGBE_READ_REG(hw, IXGBE_TDT(tx_ring->reg_idx)),
-                       tx_ring->next_to_use, i,
-                       tx_ring->tx_buffer_info[i].time_stamp, jiffies);
-
-               if (!ring_is_xdp(tx_ring))
-                       netif_stop_subqueue(tx_ring->netdev,
-                                           tx_ring->queue_index);
+               if (adapter->hw.mac.type == ixgbe_mac_e610)
+                       ixgbe_handle_mdd_event(adapter, tx_ring);
+
+               ixgbe_pf_handle_tx_hang(tx_ring, i);
 
                e_info(probe,
                       "tx hang %d detected on queue %d, resetting adapter\n",
 
 #define IXGBE_GCR_EXT_VT_MODE_16        0x00000001
 #define IXGBE_GCR_EXT_VT_MODE_32        0x00000002
 #define IXGBE_GCR_EXT_VT_MODE_64        0x00000003
+#define IXGBE_GCR_EXT_VT_MODE_MASK      0x00000003
 #define IXGBE_GCR_EXT_SRIOV             (IXGBE_GCR_EXT_MSIX_EN | \
                                         IXGBE_GCR_EXT_VT_MODE_64)
 
        __le32 mss_l4len_idx;
 };
 
+enum {
+       IXGBE_VLAN_MACIP_LENS_REG       = 0,
+       IXGBE_FCEOF_SAIDX_REG           = 1,
+       IXGBE_TYPE_TUCMD_MLHL           = 2,
+       IXGBE_MSS_L4LEN_IDX             = 3,
+};
+
 /* Adv Transmit Descriptor Config Masks */
 #define IXGBE_ADVTXD_DTALEN_MASK      0x0000FFFF /* Data buf length(bytes) */
 #define IXGBE_ADVTXD_MAC_LINKSEC      0x00040000 /* Insert LinkSec */
 #define IXGBE_ADVTXD_IPSEC_SA_INDEX_MASK   0x000003FF /* IPSec SA index */
 #define IXGBE_ADVTXD_IPSEC_ESP_LEN_MASK    0x000001FF /* IPSec ESP length */
 #define IXGBE_ADVTXD_DTYP_MASK  0x00F00000 /* DTYP mask */
-#define IXGBE_ADVTXD_DTYP_CTXT  0x00200000 /* Advanced Context Desc */
+#define IXGBE_ADVTXD_DTYP_CTXT 0x2 /* Advanced Context Desc */
 #define IXGBE_ADVTXD_DTYP_DATA  0x00300000 /* Advanced Data Descriptor */
 #define IXGBE_ADVTXD_DCMD_EOP   IXGBE_TXD_CMD_EOP  /* End of Packet */
 #define IXGBE_ADVTXD_DCMD_IFCS  IXGBE_TXD_CMD_IFCS /* Insert FCS */
 #define IXGBE_ADVTXD_FCOEF_EOF_MASK  (3u << 10)  /* FC EOF index */
 #define IXGBE_ADVTXD_L4LEN_SHIFT     8  /* Adv ctxt L4LEN shift */
 #define IXGBE_ADVTXD_MSS_SHIFT       16  /* Adv ctxt MSS shift */
+#define IXGBE_ADVTXD_MSS_MASK          GENMASK(31, IXGBE_ADVTXD_MSS_SHIFT)
+#define IXGBE_ADVTXD_HEADER_LEN_MASK   GENMASK(8, 0)
 
 /* Autonegotiation advertised speeds */
 typedef u32 ixgbe_autoneg_advertised;