1 /* $Id: iommu_common.c,v 1.9 2001/12/17 07:05:09 davem Exp $
2 * iommu_common.c: UltraSparc SBUS/PCI common iommu code.
4 * Copyright (C) 1999 David S. Miller (davem@redhat.com)
7 #include "iommu_common.h"
9 /* You are _strongly_ advised to enable the following debugging code
10 * any time you make changes to the sg code below, run it for a while
11 * with filesystems mounted read-only before buying the farm... -DaveM
15 static int verify_lengths(struct scatterlist
*sglist
, int nents
, int npages
)
19 struct scatterlist
*sg
;
22 for_each_sg(sglist
, sg
, nents
, i
)
26 for_each_sg(sglist
, sg
, nents
, i
) {
29 dma_len
+= sg
->dma_length
;
32 if (sg_len
!= dma_len
) {
33 printk("verify_lengths: Error, different, sg[%d] dma[%d]\n",
39 for_each_sg(sglist
, sg
, nents
, i
) {
40 unsigned long start
, end
;
45 start
= sg
->dma_address
;
46 start
= start
& IO_PAGE_MASK
;
48 end
= sg
->dma_address
+ sg
->dma_length
;
49 end
= (end
+ (IO_PAGE_SIZE
- 1)) & IO_PAGE_MASK
;
51 pgcount
+= ((end
- start
) >> IO_PAGE_SHIFT
);
54 if (pgcount
!= npages
) {
55 printk("verify_lengths: Error, page count wrong, "
56 "npages[%d] pgcount[%d]\n",
61 /* This test passes... */
65 static int verify_one_map(struct scatterlist
*dma_sg
, struct scatterlist
**__sg
, int nents
, iopte_t
**__iopte
)
67 struct scatterlist
*sg
= *__sg
;
68 iopte_t
*iopte
= *__iopte
;
69 u32 dlen
= dma_sg
->dma_length
;
74 daddr
= dma_sg
->dma_address
;
76 sgaddr
= (unsigned long) sg_virt(sg
);
80 /* SG and DMA_SG must begin at the same sub-page boundary. */
81 if ((sgaddr
& ~IO_PAGE_MASK
) != (daddr
& ~IO_PAGE_MASK
)) {
82 printk("verify_one_map: Wrong start offset "
83 "sg[%08lx] dma[%08x]\n",
89 /* Verify the IOPTE points to the right page. */
90 paddr
= iopte_val(*iopte
) & IOPTE_PAGE
;
91 if ((paddr
+ PAGE_OFFSET
) != (sgaddr
& IO_PAGE_MASK
)) {
92 printk("verify_one_map: IOPTE[%08lx] maps the "
93 "wrong page, should be [%08lx]\n",
94 iopte_val(*iopte
), (sgaddr
& IO_PAGE_MASK
) - PAGE_OFFSET
);
99 /* If this SG crosses a page, adjust to that next page
102 if ((sgaddr
& IO_PAGE_MASK
) ^ ((sgaddr
+ sglen
- 1) & IO_PAGE_MASK
)) {
103 unsigned long next_page
, diff
;
105 next_page
= (sgaddr
+ IO_PAGE_SIZE
) & IO_PAGE_MASK
;
106 diff
= next_page
- sgaddr
;
116 /* SG wholly consumed within this page. */
120 if (dlen
> 0 && ((daddr
& ~IO_PAGE_MASK
) == 0))
126 sgaddr
= (unsigned long) sg_virt(sg
);
130 /* Transfer overrun, big problems. */
131 printk("verify_one_map: Transfer overrun by %d bytes.\n",
135 /* Advance to next dma_sg implies that the next iopte will
147 static int verify_maps(struct scatterlist
*sg
, int nents
, iopte_t
*iopte
)
149 struct scatterlist
*dma_sg
= sg
;
150 struct scatterlist
*orig_dma_sg
= dma_sg
;
151 int orig_nents
= nents
;
154 nents
= verify_one_map(dma_sg
, &sg
, nents
, &iopte
);
157 dma_sg
= sg_next(dma_sg
);
158 if (dma_sg
->dma_length
== 0)
163 printk("verify_maps: dma maps consumed by some sgs remain (%d)\n",
169 printk("verify_maps: Error, messed up mappings, "
170 "at sg %d dma_sg %d\n",
171 (int) (orig_nents
+ nents
), (int) (dma_sg
- orig_dma_sg
));
175 /* This test passes... */
179 void verify_sglist(struct scatterlist
*sglist
, int nents
, iopte_t
*iopte
, int npages
)
181 struct scatterlist
*sg
;
183 if (verify_lengths(sglist
, nents
, npages
) < 0 ||
184 verify_maps(sglist
, nents
, iopte
) < 0) {
187 printk("verify_sglist: Crap, messed up mappings, dumping, iodma at ");
188 printk("%016lx.\n", sglist
->dma_address
& IO_PAGE_MASK
);
190 for_each_sg(sglist
, sg
, nents
, i
) {
191 printk("sg(%d): page_addr(%p) off(%x) length(%x) "
192 "dma_address[%016x] dma_length[%016x]\n",
194 page_address(sg_page(sg
)), sg
->offset
,
196 sg
->dma_address
, sg
->dma_length
);
204 unsigned long prepare_sg(struct scatterlist
*sg
, int nents
)
206 struct scatterlist
*dma_sg
= sg
;
208 u32 dent_addr
, dent_len
;
210 prev
= (unsigned long) sg_virt(sg
);
211 prev
+= (unsigned long) (dent_len
= sg
->length
);
212 dent_addr
= (u32
) ((unsigned long)(sg_virt(sg
)) & (IO_PAGE_SIZE
- 1UL));
217 addr
= (unsigned long) sg_virt(sg
);
218 if (! VCONTIG(prev
, addr
)) {
219 dma_sg
->dma_address
= dent_addr
;
220 dma_sg
->dma_length
= dent_len
;
221 dma_sg
= sg_next(dma_sg
);
223 dent_addr
= ((dent_addr
+
225 (IO_PAGE_SIZE
- 1UL)) >> IO_PAGE_SHIFT
);
226 dent_addr
<<= IO_PAGE_SHIFT
;
227 dent_addr
+= addr
& (IO_PAGE_SIZE
- 1UL);
230 dent_len
+= sg
->length
;
231 prev
= addr
+ sg
->length
;
233 dma_sg
->dma_address
= dent_addr
;
234 dma_sg
->dma_length
= dent_len
;
237 dma_sg
= sg_next(dma_sg
);
238 dma_sg
->dma_length
= 0;
241 return ((unsigned long) dent_addr
+
242 (unsigned long) dent_len
+
243 (IO_PAGE_SIZE
- 1UL)) >> IO_PAGE_SHIFT
;