aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/sfc
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2012-09-14 12:31:41 -0400
committerBen Hutchings <bhutchings@solarflare.com>2013-08-21 11:35:27 -0400
commit9528b9219348e0a013f4b587958a8ba9c96d7e20 (patch)
treef6244c5dccb81e29aa3fb8e2a5bbfcebd20333c0 /drivers/net/ethernet/sfc
parentc5bb0e9891ba1f7c871adc09d9ef727e1c0c1c1e (diff)
sfc: Ensure MCDI buffers, but not lengths, are dword aligned
We currently require that MCDI request and response lengths are multiples of 4 bytes, because we will copy dwords in and out of shared memory and we want to be sure we won't read or write out of bounds. But all we really need to know is that there is sufficient padding for that. Also, we should ensure that buffers are dword-aligned, as on some architectures misaligned access will result in data corruption or a crash. Change the buffer type to array-of-efx_dword_t and remove the requirement that the lengths are multiples of 4. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Diffstat (limited to 'drivers/net/ethernet/sfc')
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c31
-rw-r--r--drivers/net/ethernet/sfc/mcdi.h36
-rw-r--r--drivers/net/ethernet/sfc/ptp.c10
3 files changed, 46 insertions, 31 deletions
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c
index d6d1ff19c918..3f55b16265fc 100644
--- a/drivers/net/ethernet/sfc/mcdi.c
+++ b/drivers/net/ethernet/sfc/mcdi.c
@@ -67,7 +67,7 @@ void efx_mcdi_init(struct efx_nic *efx)
67} 67}
68 68
69static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd, 69static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
70 const u8 *inbuf, size_t inlen) 70 const efx_dword_t *inbuf, size_t inlen)
71{ 71{
72 struct efx_mcdi_iface *mcdi = efx_mcdi(efx); 72 struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
73 unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); 73 unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
@@ -75,9 +75,10 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
75 unsigned int i; 75 unsigned int i;
76 efx_dword_t hdr; 76 efx_dword_t hdr;
77 u32 xflags, seqno; 77 u32 xflags, seqno;
78 unsigned int inlen_dw = DIV_ROUND_UP(inlen, 4);
78 79
79 BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); 80 BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
80 BUG_ON(inlen & 3 || inlen >= MC_SMEM_PDU_LEN); 81 BUG_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V1);
81 82
82 seqno = mcdi->seqno & SEQ_MASK; 83 seqno = mcdi->seqno & SEQ_MASK;
83 xflags = 0; 84 xflags = 0;
@@ -94,8 +95,8 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
94 95
95 efx_writed(efx, &hdr, pdu); 96 efx_writed(efx, &hdr, pdu);
96 97
97 for (i = 0; i < inlen; i += 4) 98 for (i = 0; i < inlen_dw; i++)
98 _efx_writed(efx, *((__le32 *)(inbuf + i)), pdu + 4 + i); 99 efx_writed(efx, &inbuf[i], pdu + 4 + 4 * i);
99 100
100 /* Ensure the payload is written out before the header */ 101 /* Ensure the payload is written out before the header */
101 wmb(); 102 wmb();
@@ -104,17 +105,19 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
104 _efx_writed(efx, (__force __le32) 0x45789abc, doorbell); 105 _efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
105} 106}
106 107
107static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen) 108static void
109efx_mcdi_copyout(struct efx_nic *efx, efx_dword_t *outbuf, size_t outlen)
108{ 110{
109 struct efx_mcdi_iface *mcdi = efx_mcdi(efx); 111 struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
110 unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); 112 unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
113 unsigned int outlen_dw = DIV_ROUND_UP(outlen, 4);
111 int i; 114 int i;
112 115
113 BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); 116 BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
114 BUG_ON(outlen & 3 || outlen >= MC_SMEM_PDU_LEN); 117 BUG_ON(outlen > MCDI_CTL_SDU_LEN_MAX_V1);
115 118
116 for (i = 0; i < outlen; i += 4) 119 for (i = 0; i < outlen_dw; i++)
117 *((__le32 *)(outbuf + i)) = _efx_readd(efx, pdu + 4 + i); 120 efx_readd(efx, &outbuf[i], pdu + 4 + 4 * i);
118} 121}
119 122
120static int efx_mcdi_poll(struct efx_nic *efx) 123static int efx_mcdi_poll(struct efx_nic *efx)
@@ -328,7 +331,8 @@ static void efx_mcdi_ev_cpl(struct efx_nic *efx, unsigned int seqno,
328} 331}
329 332
330int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, 333int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
331 const u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen, 334 const efx_dword_t *inbuf, size_t inlen,
335 efx_dword_t *outbuf, size_t outlen,
332 size_t *outlen_actual) 336 size_t *outlen_actual)
333{ 337{
334 efx_mcdi_rpc_start(efx, cmd, inbuf, inlen); 338 efx_mcdi_rpc_start(efx, cmd, inbuf, inlen);
@@ -336,8 +340,8 @@ int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
336 outbuf, outlen, outlen_actual); 340 outbuf, outlen, outlen_actual);
337} 341}
338 342
339void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, 343void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
340 size_t inlen) 344 const efx_dword_t *inbuf, size_t inlen)
341{ 345{
342 struct efx_mcdi_iface *mcdi = efx_mcdi(efx); 346 struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
343 347
@@ -354,7 +358,8 @@ void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, const u8 *inbuf,
354} 358}
355 359
356int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, 360int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
357 u8 *outbuf, size_t outlen, size_t *outlen_actual) 361 efx_dword_t *outbuf, size_t outlen,
362 size_t *outlen_actual)
358{ 363{
359 struct efx_mcdi_iface *mcdi = efx_mcdi(efx); 364 struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
360 int rc; 365 int rc;
@@ -393,7 +398,7 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
393 398
394 if (rc == 0) { 399 if (rc == 0) {
395 efx_mcdi_copyout(efx, outbuf, 400 efx_mcdi_copyout(efx, outbuf,
396 min(outlen, mcdi->resplen + 3) & ~0x3); 401 min(outlen, mcdi->resplen));
397 if (outlen_actual != NULL) 402 if (outlen_actual != NULL)
398 *outlen_actual = resplen; 403 *outlen_actual = resplen;
399 } else if (cmd == MC_CMD_REBOOT && rc == -EIO) 404 } else if (cmd == MC_CMD_REBOOT && rc == -EIO)
diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h
index f8ab64f0efb1..28657a186761 100644
--- a/drivers/net/ethernet/sfc/mcdi.h
+++ b/drivers/net/ethernet/sfc/mcdi.h
@@ -67,16 +67,18 @@ struct efx_mcdi_mon {
67 67
68extern void efx_mcdi_init(struct efx_nic *efx); 68extern void efx_mcdi_init(struct efx_nic *efx);
69 69
70extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, 70extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
71 size_t inlen, u8 *outbuf, size_t outlen, 71 const efx_dword_t *inbuf, size_t inlen,
72 efx_dword_t *outbuf, size_t outlen,
72 size_t *outlen_actual); 73 size_t *outlen_actual);
73 74
74extern void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, 75extern void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
75 const u8 *inbuf, size_t inlen); 76 const efx_dword_t *inbuf, size_t inlen);
76extern int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, 77extern int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
77 u8 *outbuf, size_t outlen, 78 efx_dword_t *outbuf, size_t outlen,
78 size_t *outlen_actual); 79 size_t *outlen_actual);
79 80
81
80extern int efx_mcdi_poll_reboot(struct efx_nic *efx); 82extern int efx_mcdi_poll_reboot(struct efx_nic *efx);
81extern void efx_mcdi_mode_poll(struct efx_nic *efx); 83extern void efx_mcdi_mode_poll(struct efx_nic *efx);
82extern void efx_mcdi_mode_event(struct efx_nic *efx); 84extern void efx_mcdi_mode_event(struct efx_nic *efx);
@@ -85,14 +87,21 @@ extern void efx_mcdi_process_event(struct efx_channel *channel,
85 efx_qword_t *event); 87 efx_qword_t *event);
86extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); 88extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
87 89
90/* We expect that 16- and 32-bit fields in MCDI requests and responses
91 * are appropriately aligned. Also, on Siena we must copy to the MC
92 * shared memory strictly 32 bits at a time, so add any necessary
93 * padding.
94 */
88#define MCDI_DECLARE_BUF(_name, _len) \ 95#define MCDI_DECLARE_BUF(_name, _len) \
89 u8 _name[ALIGN(_len, 4)] 96 efx_dword_t _name[DIV_ROUND_UP(_len, 4)]
90#define _MCDI_PTR(_buf, _offset) \ 97#define _MCDI_PTR(_buf, _offset) \
91 ((u8 *)(_buf) + (_offset)) 98 ((u8 *)(_buf) + (_offset))
92#define MCDI_PTR(_buf, _field) \ 99#define MCDI_PTR(_buf, _field) \
93 _MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST) 100 _MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST)
101#define _MCDI_CHECK_ALIGN(_ofst, _align) \
102 ((_ofst) + BUILD_BUG_ON_ZERO((_ofst) & (_align - 1)))
94#define _MCDI_DWORD(_buf, _field) \ 103#define _MCDI_DWORD(_buf, _field) \
95 ((efx_dword_t *)MCDI_PTR(_buf, _field)) 104 ((_buf) + (_MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, 4) >> 2))
96 105
97#define MCDI_SET_DWORD(_buf, _field, _value) \ 106#define MCDI_SET_DWORD(_buf, _field, _value) \
98 EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0, _value) 107 EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0, _value)
@@ -109,22 +118,23 @@ extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
109 (MC_CMD_ ## _type ## _ ## _field ## _LBN & 0x1f) + \ 118 (MC_CMD_ ## _type ## _ ## _field ## _LBN & 0x1f) + \
110 MC_CMD_ ## _type ## _ ## _field ## _WIDTH - 1) 119 MC_CMD_ ## _type ## _ ## _field ## _WIDTH - 1)
111 120
112#define _MCDI_ARRAY_PTR(_buf, _field, _index) \ 121#define _MCDI_ARRAY_PTR(_buf, _field, _index, _align) \
113 (MCDI_PTR(_buf, _field) + \ 122 (_MCDI_PTR(_buf, _MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, _align))\
114 (_index) * MC_CMD_ ## _field ## _LEN) 123 + (_index) * _MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _LEN, _align))
115#define MCDI_DECLARE_STRUCT_PTR(_name) \ 124#define MCDI_DECLARE_STRUCT_PTR(_name) \
116 u8 *_name 125 efx_dword_t *_name
117#define MCDI_ARRAY_STRUCT_PTR _MCDI_ARRAY_PTR 126#define MCDI_ARRAY_STRUCT_PTR(_buf, _field, _index) \
127 ((efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4))
118#define MCDI_VAR_ARRAY_LEN(_len, _field) \ 128#define MCDI_VAR_ARRAY_LEN(_len, _field) \
119 min_t(size_t, MC_CMD_ ## _field ## _MAXNUM, \ 129 min_t(size_t, MC_CMD_ ## _field ## _MAXNUM, \
120 ((_len) - MC_CMD_ ## _field ## _OFST) / MC_CMD_ ## _field ## _LEN) 130 ((_len) - MC_CMD_ ## _field ## _OFST) / MC_CMD_ ## _field ## _LEN)
121#define MCDI_ARRAY_WORD(_buf, _field, _index) \ 131#define MCDI_ARRAY_WORD(_buf, _field, _index) \
122 (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \ 132 (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \
123 le16_to_cpu(*(__force const __le16 *) \ 133 le16_to_cpu(*(__force const __le16 *) \
124 _MCDI_ARRAY_PTR(_buf, _field, _index))) 134 _MCDI_ARRAY_PTR(_buf, _field, _index, 2)))
125#define _MCDI_ARRAY_DWORD(_buf, _field, _index) \ 135#define _MCDI_ARRAY_DWORD(_buf, _field, _index) \
126 (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 4) + \ 136 (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 4) + \
127 (efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index)) 137 (efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4))
128#define MCDI_SET_ARRAY_DWORD(_buf, _field, _index, _value) \ 138#define MCDI_SET_ARRAY_DWORD(_buf, _field, _index, _value) \
129 EFX_SET_DWORD_FIELD(*_MCDI_ARRAY_DWORD(_buf, _field, _index), \ 139 EFX_SET_DWORD_FIELD(*_MCDI_ARRAY_DWORD(_buf, _field, _index), \
130 EFX_DWORD_0, _value) 140 EFX_DWORD_0, _value)
diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c
index ec2cf4d7c9b7..5612021d960a 100644
--- a/drivers/net/ethernet/sfc/ptp.c
+++ b/drivers/net/ethernet/sfc/ptp.c
@@ -571,9 +571,8 @@ static int efx_ptp_xmit_skb(struct efx_nic *efx, struct sk_buff *skb)
571 struct efx_ptp_data *ptp_data = efx->ptp_data; 571 struct efx_ptp_data *ptp_data = efx->ptp_data;
572 struct skb_shared_hwtstamps timestamps; 572 struct skb_shared_hwtstamps timestamps;
573 int rc = -EIO; 573 int rc = -EIO;
574 /* MCDI driver requires word aligned lengths */
575 size_t len = ALIGN(MC_CMD_PTP_IN_TRANSMIT_LEN(skb->len), 4);
576 MCDI_DECLARE_BUF(txtime, MC_CMD_PTP_OUT_TRANSMIT_LEN); 574 MCDI_DECLARE_BUF(txtime, MC_CMD_PTP_OUT_TRANSMIT_LEN);
575 size_t len;
577 576
578 MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_OP, MC_CMD_PTP_OP_TRANSMIT); 577 MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_OP, MC_CMD_PTP_OP_TRANSMIT);
579 MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_TRANSMIT_LENGTH, skb->len); 578 MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_TRANSMIT_LENGTH, skb->len);
@@ -591,9 +590,10 @@ static int efx_ptp_xmit_skb(struct efx_nic *efx, struct sk_buff *skb)
591 skb_copy_from_linear_data(skb, 590 skb_copy_from_linear_data(skb,
592 MCDI_PTR(ptp_data->txbuf, 591 MCDI_PTR(ptp_data->txbuf,
593 PTP_IN_TRANSMIT_PACKET), 592 PTP_IN_TRANSMIT_PACKET),
594 len); 593 skb->len);
595 rc = efx_mcdi_rpc(efx, MC_CMD_PTP, ptp_data->txbuf, len, txtime, 594 rc = efx_mcdi_rpc(efx, MC_CMD_PTP,
596 sizeof(txtime), &len); 595 ptp_data->txbuf, MC_CMD_PTP_IN_TRANSMIT_LEN(skb->len),
596 txtime, sizeof(txtime), &len);
597 if (rc != 0) 597 if (rc != 0)
598 goto fail; 598 goto fail;
599 599