Merge branch 'sg' of git://git.kernel.dk/linux-2.6-block
[deliverable/linux.git] / arch / sparc64 / kernel / iommu_common.c
CommitLineData
1da177e4
LT
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.
3 *
4 * Copyright (C) 1999 David S. Miller (davem@redhat.com)
5 */
6
7#include "iommu_common.h"
8
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
12 */
13
14#ifdef VERIFY_SG
24c31eed 15static int verify_lengths(struct scatterlist *sglist, int nents, int npages)
1da177e4
LT
16{
17 int sg_len, dma_len;
18 int i, pgcount;
24c31eed 19 struct scatterlist *sg;
1da177e4
LT
20
21 sg_len = 0;
24c31eed
FT
22 for_each_sg(sglist, sg, nents, i)
23 sg_len += sg->length;
1da177e4
LT
24
25 dma_len = 0;
24c31eed
FT
26 for_each_sg(sglist, sg, nents, i) {
27 if (!sg->dma_length)
28 break;
29 dma_len += sg->dma_length;
30 }
1da177e4
LT
31
32 if (sg_len != dma_len) {
33 printk("verify_lengths: Error, different, sg[%d] dma[%d]\n",
34 sg_len, dma_len);
35 return -1;
36 }
37
38 pgcount = 0;
24c31eed 39 for_each_sg(sglist, sg, nents, i) {
1da177e4
LT
40 unsigned long start, end;
41
24c31eed
FT
42 if (!sg->dma_length)
43 break;
44
45 start = sg->dma_address;
1da177e4
LT
46 start = start & IO_PAGE_MASK;
47
24c31eed 48 end = sg->dma_address + sg->dma_length;
1da177e4
LT
49 end = (end + (IO_PAGE_SIZE - 1)) & IO_PAGE_MASK;
50
51 pgcount += ((end - start) >> IO_PAGE_SHIFT);
52 }
53
54 if (pgcount != npages) {
55 printk("verify_lengths: Error, page count wrong, "
56 "npages[%d] pgcount[%d]\n",
57 npages, pgcount);
58 return -1;
59 }
60
61 /* This test passes... */
62 return 0;
63}
64
65static int verify_one_map(struct scatterlist *dma_sg, struct scatterlist **__sg, int nents, iopte_t **__iopte)
66{
67 struct scatterlist *sg = *__sg;
68 iopte_t *iopte = *__iopte;
69 u32 dlen = dma_sg->dma_length;
70 u32 daddr;
71 unsigned int sglen;
72 unsigned long sgaddr;
73
74 daddr = dma_sg->dma_address;
75 sglen = sg->length;
58b053e4 76 sgaddr = (unsigned long) sg_virt(sg);
1da177e4
LT
77 while (dlen > 0) {
78 unsigned long paddr;
79
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",
84 sgaddr, daddr);
85 nents = -1;
86 goto out;
87 }
88
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);
95 nents = -1;
96 goto out;
97 }
98
99 /* If this SG crosses a page, adjust to that next page
100 * boundary and loop.
101 */
102 if ((sgaddr & IO_PAGE_MASK) ^ ((sgaddr + sglen - 1) & IO_PAGE_MASK)) {
103 unsigned long next_page, diff;
104
105 next_page = (sgaddr + IO_PAGE_SIZE) & IO_PAGE_MASK;
106 diff = next_page - sgaddr;
107 sgaddr += diff;
108 daddr += diff;
109 sglen -= diff;
110 dlen -= diff;
111 if (dlen > 0)
112 iopte++;
113 continue;
114 }
115
116 /* SG wholly consumed within this page. */
117 daddr += sglen;
118 dlen -= sglen;
119
120 if (dlen > 0 && ((daddr & ~IO_PAGE_MASK) == 0))
121 iopte++;
122
24c31eed 123 sg = sg_next(sg);
1da177e4
LT
124 if (--nents <= 0)
125 break;
58b053e4 126 sgaddr = (unsigned long) sg_virt(sg);
1da177e4
LT
127 sglen = sg->length;
128 }
129 if (dlen < 0) {
130 /* Transfer overrun, big problems. */
131 printk("verify_one_map: Transfer overrun by %d bytes.\n",
132 -dlen);
133 nents = -1;
134 } else {
135 /* Advance to next dma_sg implies that the next iopte will
136 * begin it.
137 */
138 iopte++;
139 }
140
141out:
142 *__sg = sg;
143 *__iopte = iopte;
144 return nents;
145}
146
147static int verify_maps(struct scatterlist *sg, int nents, iopte_t *iopte)
148{
149 struct scatterlist *dma_sg = sg;
150 struct scatterlist *orig_dma_sg = dma_sg;
151 int orig_nents = nents;
152
153 for (;;) {
154 nents = verify_one_map(dma_sg, &sg, nents, &iopte);
155 if (nents <= 0)
156 break;
24c31eed 157 dma_sg = sg_next(dma_sg);
1da177e4
LT
158 if (dma_sg->dma_length == 0)
159 break;
160 }
161
162 if (nents > 0) {
163 printk("verify_maps: dma maps consumed by some sgs remain (%d)\n",
164 nents);
165 return -1;
166 }
167
168 if (nents < 0) {
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));
172 return -1;
173 }
174
175 /* This test passes... */
176 return 0;
177}
178
24c31eed 179void verify_sglist(struct scatterlist *sglist, int nents, iopte_t *iopte, int npages)
1da177e4 180{
24c31eed
FT
181 struct scatterlist *sg;
182
183 if (verify_lengths(sglist, nents, npages) < 0 ||
184 verify_maps(sglist, nents, iopte) < 0) {
1da177e4
LT
185 int i;
186
187 printk("verify_sglist: Crap, messed up mappings, dumping, iodma at ");
24c31eed 188 printk("%016lx.\n", sglist->dma_address & IO_PAGE_MASK);
1da177e4 189
24c31eed 190 for_each_sg(sglist, sg, nents, i) {
1da177e4 191 printk("sg(%d): page_addr(%p) off(%x) length(%x) "
24c31eed 192 "dma_address[%016x] dma_length[%016x]\n",
1da177e4 193 i,
58b053e4 194 page_address(sg_page(sg)), sg->offset,
24c31eed
FT
195 sg->length,
196 sg->dma_address, sg->dma_length);
1da177e4
LT
197 }
198 }
199
200 /* Seems to be ok */
201}
202#endif
203
204unsigned long prepare_sg(struct scatterlist *sg, int nents)
205{
206 struct scatterlist *dma_sg = sg;
207 unsigned long prev;
208 u32 dent_addr, dent_len;
209
58b053e4 210 prev = (unsigned long) sg_virt(sg);
1da177e4 211 prev += (unsigned long) (dent_len = sg->length);
58b053e4 212 dent_addr = (u32) ((unsigned long)(sg_virt(sg)) & (IO_PAGE_SIZE - 1UL));
1da177e4
LT
213 while (--nents) {
214 unsigned long addr;
215
24c31eed 216 sg = sg_next(sg);
58b053e4 217 addr = (unsigned long) sg_virt(sg);
1da177e4
LT
218 if (! VCONTIG(prev, addr)) {
219 dma_sg->dma_address = dent_addr;
220 dma_sg->dma_length = dent_len;
24c31eed 221 dma_sg = sg_next(dma_sg);
1da177e4
LT
222
223 dent_addr = ((dent_addr +
224 dent_len +
225 (IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT);
226 dent_addr <<= IO_PAGE_SHIFT;
227 dent_addr += addr & (IO_PAGE_SIZE - 1UL);
228 dent_len = 0;
229 }
230 dent_len += sg->length;
231 prev = addr + sg->length;
232 }
233 dma_sg->dma_address = dent_addr;
234 dma_sg->dma_length = dent_len;
235
cb92ae81
FT
236 if (dma_sg != sg) {
237 dma_sg = next_sg(dma_sg);
238 dma_sg->dma_length = 0;
239 }
240
1da177e4
LT
241 return ((unsigned long) dent_addr +
242 (unsigned long) dent_len +
243 (IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT;
244}
This page took 0.248269 seconds and 5 git commands to generate.