1 /* $Id: iommu_common.c,v 1.6.2.1 2001/12/11 22:47:27 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
verify_lengths(struct scatterlist * sg,int nents,int npages)15 static int verify_lengths(struct scatterlist *sg, int nents, int npages)
16 {
17 	int sg_len, dma_len;
18 	int i, pgcount;
19 
20 	sg_len = 0;
21 	for (i = 0; i < nents; i++)
22 		sg_len += sg[i].length;
23 
24 	dma_len = 0;
25 	for (i = 0; i < nents && sg[i].dma_length; i++)
26 		dma_len += sg[i].dma_length;
27 
28 	if (sg_len != dma_len) {
29 		printk("verify_lengths: Error, different, sg[%d] dma[%d]\n",
30 		       sg_len, dma_len);
31 		return -1;
32 	}
33 
34 	pgcount = 0;
35 	for (i = 0; i < nents && sg[i].dma_length; i++) {
36 		unsigned long start, end;
37 
38 		start = sg[i].dma_address;
39 		start = start & IO_PAGE_MASK;
40 
41 		end = sg[i].dma_address + sg[i].dma_length;
42 		end = (end + (IO_PAGE_SIZE - 1)) & IO_PAGE_MASK;
43 
44 		pgcount += ((end - start) >> IO_PAGE_SHIFT);
45 	}
46 
47 	if (pgcount != npages) {
48 		printk("verify_lengths: Error, page count wrong, "
49 		       "npages[%d] pgcount[%d]\n",
50 		       npages, pgcount);
51 		return -1;
52 	}
53 
54 	/* This test passes... */
55 	return 0;
56 }
57 
verify_one_map(struct scatterlist * dma_sg,struct scatterlist ** __sg,int nents,iopte_t ** __iopte)58 static int verify_one_map(struct scatterlist *dma_sg, struct scatterlist **__sg, int nents, iopte_t **__iopte)
59 {
60 	struct scatterlist *sg = *__sg;
61 	iopte_t *iopte = *__iopte;
62 	u32 dlen = dma_sg->dma_length;
63 	u32 daddr;
64 	unsigned int sglen;
65 	unsigned long sgaddr;
66 
67 	daddr = dma_sg->dma_address;
68 	sglen = sg->length;
69 	sgaddr = (unsigned long) (sg->address ?
70 				  sg->address :
71 				  page_address(sg->page) + sg->offset);
72 	while (dlen > 0) {
73 		unsigned long paddr;
74 
75 		/* SG and DMA_SG must begin at the same sub-page boundary. */
76 		if ((sgaddr & ~IO_PAGE_MASK) != (daddr & ~IO_PAGE_MASK)) {
77 			printk("verify_one_map: Wrong start offset "
78 			       "sg[%08lx] dma[%08x]\n",
79 			       sgaddr, daddr);
80 			nents = -1;
81 			goto out;
82 		}
83 
84 		/* Verify the IOPTE points to the right page. */
85 		paddr = iopte_val(*iopte) & IOPTE_PAGE;
86 		if ((paddr + PAGE_OFFSET) != (sgaddr & IO_PAGE_MASK)) {
87 			printk("verify_one_map: IOPTE[%08lx] maps the "
88 			       "wrong page, should be [%08lx]\n",
89 			       iopte_val(*iopte), (sgaddr & IO_PAGE_MASK) - PAGE_OFFSET);
90 			nents = -1;
91 			goto out;
92 		}
93 
94 		/* If this SG crosses a page, adjust to that next page
95 		 * boundary and loop.
96 		 */
97 		if ((sgaddr & IO_PAGE_MASK) ^ ((sgaddr + sglen - 1) & IO_PAGE_MASK)) {
98 			unsigned long next_page, diff;
99 
100 			next_page = (sgaddr + IO_PAGE_SIZE) & IO_PAGE_MASK;
101 			diff = next_page - sgaddr;
102 			sgaddr += diff;
103 			daddr += diff;
104 			sglen -= diff;
105 			dlen -= diff;
106 			if (dlen > 0)
107 				iopte++;
108 			continue;
109 		}
110 
111 		/* SG wholly consumed within this page. */
112 		daddr += sglen;
113 		dlen -= sglen;
114 
115 		if (dlen > 0 && ((daddr & ~IO_PAGE_MASK) == 0))
116 			iopte++;
117 
118 		sg++;
119 		if (--nents <= 0)
120 			break;
121 		sgaddr = (unsigned long) (sg->address ?
122 					  sg->address :
123 					  page_address(sg->page) + sg->offset);
124 		sglen = sg->length;
125 	}
126 	if (dlen < 0) {
127 		/* Transfer overrun, big problems. */
128 		printk("verify_one_map: Transfer overrun by %d bytes.\n",
129 		       -dlen);
130 		nents = -1;
131 	} else {
132 		/* Advance to next dma_sg implies that the next iopte will
133 		 * begin it.
134 		 */
135 		iopte++;
136 	}
137 
138 out:
139 	*__sg = sg;
140 	*__iopte = iopte;
141 	return nents;
142 }
143 
verify_maps(struct scatterlist * sg,int nents,iopte_t * iopte)144 static int verify_maps(struct scatterlist *sg, int nents, iopte_t *iopte)
145 {
146 	struct scatterlist *dma_sg = sg;
147 	struct scatterlist *orig_dma_sg = dma_sg;
148 	int orig_nents = nents;
149 
150 	for (;;) {
151 		nents = verify_one_map(dma_sg, &sg, nents, &iopte);
152 		if (nents <= 0)
153 			break;
154 		dma_sg++;
155 		if (dma_sg->dma_length == 0)
156 			break;
157 	}
158 
159 	if (nents > 0) {
160 		printk("verify_maps: dma maps consumed by some sgs remain (%d)\n",
161 		       nents);
162 		return -1;
163 	}
164 
165 	if (nents < 0) {
166 		printk("verify_maps: Error, messed up mappings, "
167 		       "at sg %d dma_sg %d\n",
168 		       (int) (orig_nents + nents), (int) (dma_sg - orig_dma_sg));
169 		return -1;
170 	}
171 
172 	/* This test passes... */
173 	return 0;
174 }
175 
verify_sglist(struct scatterlist * sg,int nents,iopte_t * iopte,int npages)176 void verify_sglist(struct scatterlist *sg, int nents, iopte_t *iopte, int npages)
177 {
178 	if (verify_lengths(sg, nents, npages) < 0 ||
179 	    verify_maps(sg, nents, iopte) < 0) {
180 		int i;
181 
182 		printk("verify_sglist: Crap, messed up mappings, dumping, iodma at ");
183 		printk("%016lx.\n", sg->dma_address & IO_PAGE_MASK);
184 
185 		for (i = 0; i < nents; i++) {
186 			printk("sg(%d): address(%p) length(%x) "
187 			       "dma_address[%016lx] dma_length[%016lx]\n",
188 			       i,
189 			       sg[i].address, sg[i].length,
190 			       sg[i].dma_address, sg[i].dma_length);
191 		}
192 	}
193 
194 	/* Seems to be ok */
195 }
196 #endif
197 
prepare_sg(struct scatterlist * sg,int nents)198 unsigned long prepare_sg(struct scatterlist *sg, int nents)
199 {
200 	struct scatterlist *dma_sg = sg;
201 	unsigned long prev;
202 	u32 dent_addr, dent_len;
203 
204 	prev  = (unsigned long) (sg->address ?
205 				 sg->address :
206 				 page_address(sg->page) + sg->offset);
207 	prev += (unsigned long) (dent_len = sg->length);
208 	dent_addr = (u32) ((unsigned long)(sg->address ?
209 					   sg->address :
210 					   page_address(sg->page) + sg->offset)
211 			   & (IO_PAGE_SIZE - 1UL));
212 	while (--nents) {
213 		unsigned long addr;
214 
215 		sg++;
216 		addr = (unsigned long) (sg->address ?
217 					sg->address :
218 					page_address(sg->page) + sg->offset);
219 		if (! VCONTIG(prev, addr)) {
220 			dma_sg->dma_address = dent_addr;
221 			dma_sg->dma_length = dent_len;
222 			dma_sg++;
223 
224 			dent_addr = ((dent_addr +
225 				      dent_len +
226 				      (IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT);
227 			dent_addr <<= IO_PAGE_SHIFT;
228 			dent_addr += addr & (IO_PAGE_SIZE - 1UL);
229 			dent_len = 0;
230 		}
231 		dent_len += sg->length;
232 		prev = addr + sg->length;
233 	}
234 	dma_sg->dma_address = dent_addr;
235 	dma_sg->dma_length = dent_len;
236 
237 	return ((unsigned long) dent_addr +
238 		(unsigned long) dent_len +
239 		(IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT;
240 }
241