b43: Add DMA poisoning patches
[openwrt.git] / package / mac80211 / patches / 510-b43-poison-rx-buffers.patch
1 This patch adds poisoning and sanity checking to the RX DMA buffers.
2 This is used for protection against buggy hardware/firmware that raises
3 RX interrupts without doing an actual DMA transfer.
4
5 This mechanism protects against rare "bad packets" (due to uninitialized skb data)
6 and rare kernel crashes due to uninitialized RX headers.
7
8 The poison is selected to not match on valid frames and to be cheap for checking.
9
10 The poison check mechanism _might_ trigger incorrectly, if we are voluntarily
11 receiving frames with bad PLCP headers. However, this is nonfatal, because the
12 chance of such a match is basically zero and in case it happens it just results
13 in dropping the packet.
14 Bad-PLCP RX defaults to off, and you should leave it off unless you want to listen
15 to the latest news broadcasted by your microwave oven.
16
17 This patch also moves the initialization of the RX-header "length" field in front of
18 the mapping of the DMA buffer. The CPU should not touch the buffer after we mapped it.
19
20 Cc: stable@kernel.org
21 Reported-by: Francesco Gringoli <francesco.gringoli@ing.unibs.it>
22 Signed-off-by: Michael Buesch <mb@bu3sch.de>
23
24 ---
25
26 Index: compat-wireless-2009-03-31/drivers/net/wireless/b43/dma.c
27 ===================================================================
28 --- compat-wireless-2009-03-31.orig/drivers/net/wireless/b43/dma.c 2009-04-06 18:52:25.000000000 +0200
29 +++ compat-wireless-2009-03-31/drivers/net/wireless/b43/dma.c 2009-04-06 18:52:30.000000000 +0200
30 @@ -555,11 +555,32 @@ address_error:
31 return 1;
32 }
33
34 +static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb)
35 +{
36 + unsigned char *f = skb->data + ring->frameoffset;
37 +
38 + return ((f[0] & f[1] & f[2] & f[3] & f[4] & f[5] & f[6] & f[7]) == 0xFF);
39 +}
40 +
41 +static void b43_poison_rx_buffer(struct b43_dmaring *ring, struct sk_buff *skb)
42 +{
43 + struct b43_rxhdr_fw4 *rxhdr;
44 + unsigned char *frame;
45 +
46 + /* This poisons the RX buffer to detect DMA failures. */
47 +
48 + rxhdr = (struct b43_rxhdr_fw4 *)(skb->data);
49 + rxhdr->frame_len = 0;
50 +
51 + B43_WARN_ON(ring->rx_buffersize < ring->frameoffset + sizeof(struct b43_plcp_hdr6) + 2);
52 + frame = skb->data + ring->frameoffset;
53 + memset(frame, 0xFF, sizeof(struct b43_plcp_hdr6) + 2 /* padding */);
54 +}
55 +
56 static int setup_rx_descbuffer(struct b43_dmaring *ring,
57 struct b43_dmadesc_generic *desc,
58 struct b43_dmadesc_meta *meta, gfp_t gfp_flags)
59 {
60 - struct b43_rxhdr_fw4 *rxhdr;
61 dma_addr_t dmaaddr;
62 struct sk_buff *skb;
63
64 @@ -568,6 +589,7 @@ static int setup_rx_descbuffer(struct b4
65 skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags);
66 if (unlikely(!skb))
67 return -ENOMEM;
68 + b43_poison_rx_buffer(ring, skb);
69 dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0);
70 if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) {
71 /* ugh. try to realloc in zone_dma */
72 @@ -578,6 +600,7 @@ static int setup_rx_descbuffer(struct b4
73 skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags);
74 if (unlikely(!skb))
75 return -ENOMEM;
76 + b43_poison_rx_buffer(ring, skb);
77 dmaaddr = map_descbuffer(ring, skb->data,
78 ring->rx_buffersize, 0);
79 if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) {
80 @@ -592,9 +615,6 @@ static int setup_rx_descbuffer(struct b4
81 ring->ops->fill_descriptor(ring, desc, dmaaddr,
82 ring->rx_buffersize, 0, 0, 0);
83
84 - rxhdr = (struct b43_rxhdr_fw4 *)(skb->data);
85 - rxhdr->frame_len = 0;
86 -
87 return 0;
88 }
89
90 @@ -1489,6 +1509,15 @@ static void dma_rx(struct b43_dmaring *r
91 goto drop;
92 }
93 }
94 + if (unlikely(b43_rx_buffer_is_poisoned(ring, skb))) {
95 + /* Something went wrong with the DMA.
96 + * The device did not touch the buffer and did not overwrite the poison. */
97 + b43dbg(ring->dev->wl, "DMA RX: Dropping poisoned buffer.\n");
98 + /* recycle the descriptor buffer. */
99 + sync_descbuffer_for_device(ring, meta->dmaaddr,
100 + ring->rx_buffersize);
101 + goto drop;
102 + }
103 if (unlikely(len > ring->rx_buffersize)) {
104 /* The data did not fit into one descriptor buffer
105 * and is split over multiple buffers.
This page took 0.04623 seconds and 5 git commands to generate.