]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net: dsa: qca8k: fix inband mgmt for big-endian systems
authorChristian Marangi <ansuelsmth@gmail.com>
Wed, 12 Oct 2022 17:18:36 +0000 (19:18 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 14 Oct 2022 07:22:28 +0000 (08:22 +0100)
The header and the data of the skb for the inband mgmt requires
to be in little-endian. This is problematic for big-endian system
as the mgmt header is written in the cpu byte order.

Fix this by converting each value for the mgmt header and data to
little-endian, and convert to cpu byte order the mgmt header and
data sent by the switch.

Fixes: 6f1bf00c8a2b ("net: dsa: qca8k: add support for mgmt read/write in Ethernet packet")
Tested-by: Pawel Dembicki <paweldembicki@gmail.com>
Tested-by: Lech Perczak <lech.perczak@gmail.com>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Reviewed-by: Lech Perczak <lech.perczak@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/qca/qca8k-8xxx.c
include/linux/dsa/tag_qca.h

index 5669c92c93f7a5b0aae501fbc2db577edd035457..644338ca0510a4f6a50a5e4bc8a861a33f8eaddb 100644 (file)
@@ -137,27 +137,42 @@ static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
        struct qca8k_mgmt_eth_data *mgmt_eth_data;
        struct qca8k_priv *priv = ds->priv;
        struct qca_mgmt_ethhdr *mgmt_ethhdr;
+       u32 command;
        u8 len, cmd;
+       int i;
 
        mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
        mgmt_eth_data = &priv->mgmt_eth_data;
 
-       cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
-       len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
+       command = get_unaligned_le32(&mgmt_ethhdr->command);
+       cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command);
+       len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command);
 
        /* Make sure the seq match the requested packet */
-       if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
+       if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq)
                mgmt_eth_data->ack = true;
 
        if (cmd == MDIO_READ) {
-               mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
+               u32 *val = mgmt_eth_data->data;
+
+               *val = get_unaligned_le32(&mgmt_ethhdr->mdio_data);
 
                /* Get the rest of the 12 byte of data.
                 * The read/write function will extract the requested data.
                 */
-               if (len > QCA_HDR_MGMT_DATA1_LEN)
-                       memcpy(mgmt_eth_data->data + 1, skb->data,
-                              QCA_HDR_MGMT_DATA2_LEN);
+               if (len > QCA_HDR_MGMT_DATA1_LEN) {
+                       __le32 *data2 = (__le32 *)skb->data;
+                       int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
+                                            len - QCA_HDR_MGMT_DATA1_LEN);
+
+                       val++;
+
+                       for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
+                               *val = get_unaligned_le32(data2);
+                               val++;
+                               data2++;
+                       }
+               }
        }
 
        complete(&mgmt_eth_data->rw_done);
@@ -169,8 +184,10 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
        struct qca_mgmt_ethhdr *mgmt_ethhdr;
        unsigned int real_len;
        struct sk_buff *skb;
-       u32 *data2;
+       __le32 *data2;
+       u32 command;
        u16 hdr;
+       int i;
 
        skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
        if (!skb)
@@ -199,20 +216,32 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
        hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
        hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
 
-       mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
-       mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
-       mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
-       mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
+       command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
+       command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
+       command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
+       command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
                                           QCA_HDR_MGMT_CHECK_CODE_VAL);
 
+       put_unaligned_le32(command, &mgmt_ethhdr->command);
+
        if (cmd == MDIO_WRITE)
-               mgmt_ethhdr->mdio_data = *val;
+               put_unaligned_le32(*val, &mgmt_ethhdr->mdio_data);
 
        mgmt_ethhdr->hdr = htons(hdr);
 
        data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
-       if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN)
-               memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
+       if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) {
+               int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
+                                    len - QCA_HDR_MGMT_DATA1_LEN);
+
+               val++;
+
+               for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
+                       put_unaligned_le32(*val, data2);
+                       data2++;
+                       val++;
+               }
+       }
 
        return skb;
 }
@@ -220,9 +249,11 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
 static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
 {
        struct qca_mgmt_ethhdr *mgmt_ethhdr;
+       u32 seq;
 
+       seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
        mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
-       mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
+       put_unaligned_le32(seq, &mgmt_ethhdr->seq);
 }
 
 static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
index 50be7cbd93a5b1ca08d6c51a4b4aca3ad97d54b3..0e176da1e43f438b02bf8a23ceda75989caef3e7 100644 (file)
@@ -61,9 +61,9 @@ struct sk_buff;
 
 /* Special struct emulating a Ethernet header */
 struct qca_mgmt_ethhdr {
-       u32 command;            /* command bit 31:0 */
-       u32 seq;                /* seq 63:32 */
-       u32 mdio_data;          /* first 4byte mdio */
+       __le32 command;         /* command bit 31:0 */
+       __le32 seq;             /* seq 63:32 */
+       __le32 mdio_data;               /* first 4byte mdio */
        __be16 hdr;             /* qca hdr */
 } __packed;