b43: Add DMA poisoning patches

SVN-Revision: 15121
This commit is contained in:
Michael Büsch 2009-04-06 17:15:19 +00:00
parent 3072d928f8
commit cb70a62be3
3 changed files with 186 additions and 7 deletions

View file

@ -0,0 +1,105 @@
This patch adds poisoning and sanity checking to the RX DMA buffers.
This is used for protection against buggy hardware/firmware that raises
RX interrupts without doing an actual DMA transfer.
This mechanism protects against rare "bad packets" (due to uninitialized skb data)
and rare kernel crashes due to uninitialized RX headers.
The poison is selected to not match on valid frames and to be cheap for checking.
The poison check mechanism _might_ trigger incorrectly, if we are voluntarily
receiving frames with bad PLCP headers. However, this is nonfatal, because the
chance of such a match is basically zero and in case it happens it just results
in dropping the packet.
Bad-PLCP RX defaults to off, and you should leave it off unless you want to listen
to the latest news broadcasted by your microwave oven.
This patch also moves the initialization of the RX-header "length" field in front of
the mapping of the DMA buffer. The CPU should not touch the buffer after we mapped it.
Cc: stable@kernel.org
Reported-by: Francesco Gringoli <francesco.gringoli@ing.unibs.it>
Signed-off-by: Michael Buesch <mb@bu3sch.de>
---
Index: compat-wireless-2009-03-31/drivers/net/wireless/b43/dma.c
===================================================================
--- compat-wireless-2009-03-31.orig/drivers/net/wireless/b43/dma.c 2009-04-06 18:52:25.000000000 +0200
+++ compat-wireless-2009-03-31/drivers/net/wireless/b43/dma.c 2009-04-06 18:52:30.000000000 +0200
@@ -555,11 +555,32 @@ address_error:
return 1;
}
+static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb)
+{
+ unsigned char *f = skb->data + ring->frameoffset;
+
+ return ((f[0] & f[1] & f[2] & f[3] & f[4] & f[5] & f[6] & f[7]) == 0xFF);
+}
+
+static void b43_poison_rx_buffer(struct b43_dmaring *ring, struct sk_buff *skb)
+{
+ struct b43_rxhdr_fw4 *rxhdr;
+ unsigned char *frame;
+
+ /* This poisons the RX buffer to detect DMA failures. */
+
+ rxhdr = (struct b43_rxhdr_fw4 *)(skb->data);
+ rxhdr->frame_len = 0;
+
+ B43_WARN_ON(ring->rx_buffersize < ring->frameoffset + sizeof(struct b43_plcp_hdr6) + 2);
+ frame = skb->data + ring->frameoffset;
+ memset(frame, 0xFF, sizeof(struct b43_plcp_hdr6) + 2 /* padding */);
+}
+
static int setup_rx_descbuffer(struct b43_dmaring *ring,
struct b43_dmadesc_generic *desc,
struct b43_dmadesc_meta *meta, gfp_t gfp_flags)
{
- struct b43_rxhdr_fw4 *rxhdr;
dma_addr_t dmaaddr;
struct sk_buff *skb;
@@ -568,6 +589,7 @@ static int setup_rx_descbuffer(struct b4
skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags);
if (unlikely(!skb))
return -ENOMEM;
+ b43_poison_rx_buffer(ring, skb);
dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0);
if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) {
/* ugh. try to realloc in zone_dma */
@@ -578,6 +600,7 @@ static int setup_rx_descbuffer(struct b4
skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags);
if (unlikely(!skb))
return -ENOMEM;
+ b43_poison_rx_buffer(ring, skb);
dmaaddr = map_descbuffer(ring, skb->data,
ring->rx_buffersize, 0);
if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) {
@@ -592,9 +615,6 @@ static int setup_rx_descbuffer(struct b4
ring->ops->fill_descriptor(ring, desc, dmaaddr,
ring->rx_buffersize, 0, 0, 0);
- rxhdr = (struct b43_rxhdr_fw4 *)(skb->data);
- rxhdr->frame_len = 0;
-
return 0;
}
@@ -1489,6 +1509,15 @@ static void dma_rx(struct b43_dmaring *r
goto drop;
}
}
+ if (unlikely(b43_rx_buffer_is_poisoned(ring, skb))) {
+ /* Something went wrong with the DMA.
+ * The device did not touch the buffer and did not overwrite the poison. */
+ b43dbg(ring->dev->wl, "DMA RX: Dropping poisoned buffer.\n");
+ /* recycle the descriptor buffer. */
+ sync_descbuffer_for_device(ring, meta->dmaaddr,
+ ring->rx_buffersize);
+ goto drop;
+ }
if (unlikely(len > ring->rx_buffersize)) {
/* The data did not fit into one descriptor buffer
* and is split over multiple buffers.

View file

@ -0,0 +1,68 @@
The RX buffer poison needs to be refreshed, if we recycle an RX buffer,
because it might be (partially) overwritten by some DMA operations.
Cc: stable@kernel.org
Cc: Francesco Gringoli <francesco.gringoli@ing.unibs.it>
Signed-off-by: Michael Buesch <mb@bu3sch.de>
---
Index: compat-wireless-2009-03-31/drivers/net/wireless/b43/dma.c
===================================================================
--- compat-wireless-2009-03-31.orig/drivers/net/wireless/b43/dma.c 2009-04-06 18:52:30.000000000 +0200
+++ compat-wireless-2009-03-31/drivers/net/wireless/b43/dma.c 2009-04-06 18:53:04.000000000 +0200
@@ -1503,20 +1503,16 @@ static void dma_rx(struct b43_dmaring *r
len = le16_to_cpu(rxhdr->frame_len);
} while (len == 0 && i++ < 5);
if (unlikely(len == 0)) {
- /* recycle the descriptor buffer. */
- sync_descbuffer_for_device(ring, meta->dmaaddr,
- ring->rx_buffersize);
- goto drop;
+ dmaaddr = meta->dmaaddr;
+ goto drop_recycle_buffer;
}
}
if (unlikely(b43_rx_buffer_is_poisoned(ring, skb))) {
/* Something went wrong with the DMA.
* The device did not touch the buffer and did not overwrite the poison. */
b43dbg(ring->dev->wl, "DMA RX: Dropping poisoned buffer.\n");
- /* recycle the descriptor buffer. */
- sync_descbuffer_for_device(ring, meta->dmaaddr,
- ring->rx_buffersize);
- goto drop;
+ dmaaddr = meta->dmaaddr;
+ goto drop_recycle_buffer;
}
if (unlikely(len > ring->rx_buffersize)) {
/* The data did not fit into one descriptor buffer
@@ -1530,6 +1526,7 @@ static void dma_rx(struct b43_dmaring *r
while (1) {
desc = ops->idx2desc(ring, *slot, &meta);
/* recycle the descriptor buffer. */
+ b43_poison_rx_buffer(ring, meta->skb);
sync_descbuffer_for_device(ring, meta->dmaaddr,
ring->rx_buffersize);
*slot = next_slot(ring, *slot);
@@ -1548,8 +1545,7 @@ static void dma_rx(struct b43_dmaring *r
err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC);
if (unlikely(err)) {
b43dbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer() failed\n");
- sync_descbuffer_for_device(ring, dmaaddr, ring->rx_buffersize);
- goto drop;
+ goto drop_recycle_buffer;
}
unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0);
@@ -1559,6 +1555,11 @@ static void dma_rx(struct b43_dmaring *r
b43_rx(ring->dev, skb, rxhdr);
drop:
return;
+
+drop_recycle_buffer:
+ /* Poison and recycle the RX buffer. */
+ b43_poison_rx_buffer(ring, skb);
+ sync_descbuffer_for_device(ring, dmaaddr, ring->rx_buffersize);
}
void b43_dma_rx(struct b43_dmaring *ring)

View file

@ -4,9 +4,11 @@ This patch is submitted upstream and can be removed when it hits compat-wireless
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -624,9 +624,11 @@ struct b43_wl {
Index: compat-wireless-2009-03-31/drivers/net/wireless/b43/b43.h
===================================================================
--- compat-wireless-2009-03-31.orig/drivers/net/wireless/b43/b43.h 2009-04-06 18:52:21.000000000 +0200
+++ compat-wireless-2009-03-31/drivers/net/wireless/b43/b43.h 2009-04-06 18:53:19.000000000 +0200
@@ -625,9 +625,11 @@ struct b43_wl {
/* Stats about the wireless interface */
struct ieee80211_low_level_stats ieee_stats;
@ -19,8 +21,10 @@ This patch is submitted upstream and can be removed when it hits compat-wireless
/* The RF-kill button */
struct b43_rfkill rfkill;
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
Index: compat-wireless-2009-03-31/drivers/net/wireless/b43/main.c
===================================================================
--- compat-wireless-2009-03-31.orig/drivers/net/wireless/b43/main.c 2009-04-06 18:52:28.000000000 +0200
+++ compat-wireless-2009-03-31/drivers/net/wireless/b43/main.c 2009-04-06 18:53:19.000000000 +0200
@@ -2982,6 +2982,7 @@ static void b43_security_init(struct b43
b43_clear_keys(dev);
}
@ -60,8 +64,10 @@ This patch is submitted upstream and can be removed when it hits compat-wireless
return err;
}
--- a/config.mk
+++ b/config.mk
Index: compat-wireless-2009-03-31/config.mk
===================================================================
--- compat-wireless-2009-03-31.orig/config.mk 2009-04-06 18:52:21.000000000 +0200
+++ compat-wireless-2009-03-31/config.mk 2009-04-06 18:53:19.000000000 +0200
@@ -148,6 +148,7 @@ CONFIG_ATH9K_DEBUG=y
# CONFIG_B43_PIO=y
# CONFIG_B43_LEDS=y