Commit 9528b921 authored by Ben Hutchings's avatar Ben Hutchings

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: default avatarBen Hutchings <bhutchings@solarflare.com>
parent c5bb0e98
...@@ -67,7 +67,7 @@ void efx_mcdi_init(struct efx_nic *efx) ...@@ -67,7 +67,7 @@ void efx_mcdi_init(struct efx_nic *efx)
} }
static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd, static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
const u8 *inbuf, size_t inlen) const efx_dword_t *inbuf, size_t inlen)
{ {
struct efx_mcdi_iface *mcdi = efx_mcdi(efx); struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); 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,9 +75,10 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
unsigned int i; unsigned int i;
efx_dword_t hdr; efx_dword_t hdr;
u32 xflags, seqno; u32 xflags, seqno;
unsigned int inlen_dw = DIV_ROUND_UP(inlen, 4);
BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
BUG_ON(inlen & 3 || inlen >= MC_SMEM_PDU_LEN); BUG_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V1);
seqno = mcdi->seqno & SEQ_MASK; seqno = mcdi->seqno & SEQ_MASK;
xflags = 0; xflags = 0;
...@@ -94,8 +95,8 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd, ...@@ -94,8 +95,8 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
efx_writed(efx, &hdr, pdu); efx_writed(efx, &hdr, pdu);
for (i = 0; i < inlen; i += 4) for (i = 0; i < inlen_dw; i++)
_efx_writed(efx, *((__le32 *)(inbuf + i)), pdu + 4 + i); efx_writed(efx, &inbuf[i], pdu + 4 + 4 * i);
/* Ensure the payload is written out before the header */ /* Ensure the payload is written out before the header */
wmb(); wmb();
...@@ -104,17 +105,19 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd, ...@@ -104,17 +105,19 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
_efx_writed(efx, (__force __le32) 0x45789abc, doorbell); _efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
} }
static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen) static void
efx_mcdi_copyout(struct efx_nic *efx, efx_dword_t *outbuf, size_t outlen)
{ {
struct efx_mcdi_iface *mcdi = efx_mcdi(efx); struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
unsigned int outlen_dw = DIV_ROUND_UP(outlen, 4);
int i; int i;
BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
BUG_ON(outlen & 3 || outlen >= MC_SMEM_PDU_LEN); BUG_ON(outlen > MCDI_CTL_SDU_LEN_MAX_V1);
for (i = 0; i < outlen; i += 4) for (i = 0; i < outlen_dw; i++)
*((__le32 *)(outbuf + i)) = _efx_readd(efx, pdu + 4 + i); efx_readd(efx, &outbuf[i], pdu + 4 + 4 * i);
} }
static int efx_mcdi_poll(struct efx_nic *efx) static 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,7 +331,8 @@ static void efx_mcdi_ev_cpl(struct efx_nic *efx, unsigned int seqno,
} }
int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
const u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen, const efx_dword_t *inbuf, size_t inlen,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual) size_t *outlen_actual)
{ {
efx_mcdi_rpc_start(efx, cmd, inbuf, inlen); efx_mcdi_rpc_start(efx, cmd, inbuf, inlen);
...@@ -336,8 +340,8 @@ int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, ...@@ -336,8 +340,8 @@ int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
outbuf, outlen, outlen_actual); outbuf, outlen, outlen_actual);
} }
void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
size_t inlen) const efx_dword_t *inbuf, size_t inlen)
{ {
struct efx_mcdi_iface *mcdi = efx_mcdi(efx); struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
...@@ -354,7 +358,8 @@ void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, ...@@ -354,7 +358,8 @@ void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, const u8 *inbuf,
} }
int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
u8 *outbuf, size_t outlen, size_t *outlen_actual) efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual)
{ {
struct efx_mcdi_iface *mcdi = efx_mcdi(efx); struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
int rc; int rc;
...@@ -393,7 +398,7 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, ...@@ -393,7 +398,7 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
if (rc == 0) { if (rc == 0) {
efx_mcdi_copyout(efx, outbuf, efx_mcdi_copyout(efx, outbuf,
min(outlen, mcdi->resplen + 3) & ~0x3); min(outlen, mcdi->resplen));
if (outlen_actual != NULL) if (outlen_actual != NULL)
*outlen_actual = resplen; *outlen_actual = resplen;
} else if (cmd == MC_CMD_REBOOT && rc == -EIO) } else if (cmd == MC_CMD_REBOOT && rc == -EIO)
......
...@@ -67,16 +67,18 @@ struct efx_mcdi_mon { ...@@ -67,16 +67,18 @@ struct efx_mcdi_mon {
extern void efx_mcdi_init(struct efx_nic *efx); extern void efx_mcdi_init(struct efx_nic *efx);
extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
size_t inlen, u8 *outbuf, size_t outlen, const efx_dword_t *inbuf, size_t inlen,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual); size_t *outlen_actual);
extern void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, extern void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
const u8 *inbuf, size_t inlen); const efx_dword_t *inbuf, size_t inlen);
extern int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, extern int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
u8 *outbuf, size_t outlen, efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual); size_t *outlen_actual);
extern int efx_mcdi_poll_reboot(struct efx_nic *efx); extern int efx_mcdi_poll_reboot(struct efx_nic *efx);
extern void efx_mcdi_mode_poll(struct efx_nic *efx); extern void efx_mcdi_mode_poll(struct efx_nic *efx);
extern void efx_mcdi_mode_event(struct efx_nic *efx); extern void efx_mcdi_mode_event(struct efx_nic *efx);
...@@ -85,14 +87,21 @@ extern void efx_mcdi_process_event(struct efx_channel *channel, ...@@ -85,14 +87,21 @@ extern void efx_mcdi_process_event(struct efx_channel *channel,
efx_qword_t *event); efx_qword_t *event);
extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
/* We expect that 16- and 32-bit fields in MCDI requests and responses
* are appropriately aligned. Also, on Siena we must copy to the MC
* shared memory strictly 32 bits at a time, so add any necessary
* padding.
*/
#define MCDI_DECLARE_BUF(_name, _len) \ #define MCDI_DECLARE_BUF(_name, _len) \
u8 _name[ALIGN(_len, 4)] efx_dword_t _name[DIV_ROUND_UP(_len, 4)]
#define _MCDI_PTR(_buf, _offset) \ #define _MCDI_PTR(_buf, _offset) \
((u8 *)(_buf) + (_offset)) ((u8 *)(_buf) + (_offset))
#define MCDI_PTR(_buf, _field) \ #define MCDI_PTR(_buf, _field) \
_MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST) _MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST)
#define _MCDI_CHECK_ALIGN(_ofst, _align) \
((_ofst) + BUILD_BUG_ON_ZERO((_ofst) & (_align - 1)))
#define _MCDI_DWORD(_buf, _field) \ #define _MCDI_DWORD(_buf, _field) \
((efx_dword_t *)MCDI_PTR(_buf, _field)) ((_buf) + (_MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, 4) >> 2))
#define MCDI_SET_DWORD(_buf, _field, _value) \ #define MCDI_SET_DWORD(_buf, _field, _value) \
EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0, _value) 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,22 +118,23 @@ extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
(MC_CMD_ ## _type ## _ ## _field ## _LBN & 0x1f) + \ (MC_CMD_ ## _type ## _ ## _field ## _LBN & 0x1f) + \
MC_CMD_ ## _type ## _ ## _field ## _WIDTH - 1) MC_CMD_ ## _type ## _ ## _field ## _WIDTH - 1)
#define _MCDI_ARRAY_PTR(_buf, _field, _index) \ #define _MCDI_ARRAY_PTR(_buf, _field, _index, _align) \
(MCDI_PTR(_buf, _field) + \ (_MCDI_PTR(_buf, _MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, _align))\
(_index) * MC_CMD_ ## _field ## _LEN) + (_index) * _MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _LEN, _align))
#define MCDI_DECLARE_STRUCT_PTR(_name) \ #define MCDI_DECLARE_STRUCT_PTR(_name) \
u8 *_name efx_dword_t *_name
#define MCDI_ARRAY_STRUCT_PTR _MCDI_ARRAY_PTR #define MCDI_ARRAY_STRUCT_PTR(_buf, _field, _index) \
((efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4))
#define MCDI_VAR_ARRAY_LEN(_len, _field) \ #define MCDI_VAR_ARRAY_LEN(_len, _field) \
min_t(size_t, MC_CMD_ ## _field ## _MAXNUM, \ min_t(size_t, MC_CMD_ ## _field ## _MAXNUM, \
((_len) - MC_CMD_ ## _field ## _OFST) / MC_CMD_ ## _field ## _LEN) ((_len) - MC_CMD_ ## _field ## _OFST) / MC_CMD_ ## _field ## _LEN)
#define MCDI_ARRAY_WORD(_buf, _field, _index) \ #define MCDI_ARRAY_WORD(_buf, _field, _index) \
(BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \ (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \
le16_to_cpu(*(__force const __le16 *) \ le16_to_cpu(*(__force const __le16 *) \
_MCDI_ARRAY_PTR(_buf, _field, _index))) _MCDI_ARRAY_PTR(_buf, _field, _index, 2)))
#define _MCDI_ARRAY_DWORD(_buf, _field, _index) \ #define _MCDI_ARRAY_DWORD(_buf, _field, _index) \
(BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 4) + \ (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 4) + \
(efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index)) (efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4))
#define MCDI_SET_ARRAY_DWORD(_buf, _field, _index, _value) \ #define MCDI_SET_ARRAY_DWORD(_buf, _field, _index, _value) \
EFX_SET_DWORD_FIELD(*_MCDI_ARRAY_DWORD(_buf, _field, _index), \ EFX_SET_DWORD_FIELD(*_MCDI_ARRAY_DWORD(_buf, _field, _index), \
EFX_DWORD_0, _value) EFX_DWORD_0, _value)
......
...@@ -571,9 +571,8 @@ static int efx_ptp_xmit_skb(struct efx_nic *efx, struct sk_buff *skb) ...@@ -571,9 +571,8 @@ static int efx_ptp_xmit_skb(struct efx_nic *efx, struct sk_buff *skb)
struct efx_ptp_data *ptp_data = efx->ptp_data; struct efx_ptp_data *ptp_data = efx->ptp_data;
struct skb_shared_hwtstamps timestamps; struct skb_shared_hwtstamps timestamps;
int rc = -EIO; int rc = -EIO;
/* MCDI driver requires word aligned lengths */
size_t len = ALIGN(MC_CMD_PTP_IN_TRANSMIT_LEN(skb->len), 4);
MCDI_DECLARE_BUF(txtime, MC_CMD_PTP_OUT_TRANSMIT_LEN); MCDI_DECLARE_BUF(txtime, MC_CMD_PTP_OUT_TRANSMIT_LEN);
size_t len;
MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_OP, MC_CMD_PTP_OP_TRANSMIT); MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_OP, MC_CMD_PTP_OP_TRANSMIT);
MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_TRANSMIT_LENGTH, skb->len); 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,9 +590,10 @@ static int efx_ptp_xmit_skb(struct efx_nic *efx, struct sk_buff *skb)
skb_copy_from_linear_data(skb, skb_copy_from_linear_data(skb,
MCDI_PTR(ptp_data->txbuf, MCDI_PTR(ptp_data->txbuf,
PTP_IN_TRANSMIT_PACKET), PTP_IN_TRANSMIT_PACKET),
len); skb->len);
rc = efx_mcdi_rpc(efx, MC_CMD_PTP, ptp_data->txbuf, len, txtime, rc = efx_mcdi_rpc(efx, MC_CMD_PTP,
sizeof(txtime), &len); ptp_data->txbuf, MC_CMD_PTP_IN_TRANSMIT_LEN(skb->len),
txtime, sizeof(txtime), &len);
if (rc != 0) if (rc != 0)
goto fail; goto fail;
......
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