Commit | Line | Data |
---|---|---|
da7c5cc6 | 1 | /* |
5ec5a918 | 2 | * Copyright (c) 1982, 1986, 1988 Regents of the University of California. |
5f9369d6 | 3 | * All rights reserved. |
da7c5cc6 | 4 | * |
af359dea C |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
5f9369d6 | 20 | * |
af359dea C |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | * | |
33 | * @(#)if_uba.c 7.16 (Berkeley) 12/16/90 | |
da7c5cc6 | 34 | */ |
961945a8 | 35 | |
b28b3a13 KB |
36 | #include "sys/param.h" |
37 | #include "sys/systm.h" | |
38 | #include "sys/malloc.h" | |
39 | #include "sys/mbuf.h" | |
40 | #include "sys/map.h" | |
41 | #include "sys/buf.h" | |
42 | #include "sys/cmap.h" | |
43 | #include "sys/vmmac.h" | |
44 | #include "sys/socket.h" | |
45 | #include "sys/syslog.h" | |
eaa60542 | 46 | |
b28b3a13 | 47 | #include "net/if.h" |
eaa60542 | 48 | |
b28b3a13 KB |
49 | #include "../include/pte.h" |
50 | #include "../include/mtpr.h" | |
a6e960e7 | 51 | #include "if_uba.h" |
b28b3a13 KB |
52 | #include "../uba/ubareg.h" |
53 | #include "../uba/ubavar.h" | |
0468e5fa BJ |
54 | |
55 | /* | |
56 | * Routines supporting UNIBUS network interfaces. | |
57 | * | |
58 | * TODO: | |
59 | * Support interfaces using only one BDP statically. | |
60 | */ | |
61 | ||
62 | /* | |
63 | * Init UNIBUS for interface on uban whose headers of size hlen are to | |
64 | * end on a page boundary. We allocate a UNIBUS map register for the page | |
65 | * with the header, and nmr more UNIBUS map registers for i/o on the adapter, | |
822a4f2c | 66 | * doing this once for each read and once for each write buffer. We also |
0468e5fa BJ |
67 | * allocate page frames in the mbuffer pool for these pages. |
68 | */ | |
822a4f2c MK |
69 | if_ubaminit(ifu, uban, hlen, nmr, ifr, nr, ifw, nw) |
70 | register struct ifubinfo *ifu; | |
71 | int uban, hlen, nmr, nr, nw; | |
72 | register struct ifrw *ifr; | |
73 | register struct ifxmt *ifw; | |
0468e5fa | 74 | { |
822a4f2c MK |
75 | register caddr_t p; |
76 | caddr_t cp; | |
8e313c90 | 77 | int i, nclbytes, off; |
0468e5fa | 78 | |
e6822062 | 79 | if (hlen) |
5ec5a918 | 80 | off = MCLBYTES - hlen; |
e6822062 MK |
81 | else |
82 | off = 0; | |
5ec5a918 | 83 | nclbytes = roundup(nmr * NBPG, MCLBYTES); |
e6822062 | 84 | if (hlen) |
5ec5a918 | 85 | nclbytes += MCLBYTES; |
822a4f2c MK |
86 | if (ifr[0].ifrw_addr) |
87 | cp = ifr[0].ifrw_addr - off; | |
176c481f | 88 | else { |
ca67e7b4 C |
89 | cp = (caddr_t)malloc((u_long)((nr + nw) * nclbytes), M_DEVBUF, |
90 | M_NOWAIT); | |
176c481f BJ |
91 | if (cp == 0) |
92 | return (0); | |
822a4f2c MK |
93 | p = cp; |
94 | for (i = 0; i < nr; i++) { | |
95 | ifr[i].ifrw_addr = p + off; | |
8e313c90 | 96 | p += nclbytes; |
822a4f2c MK |
97 | } |
98 | for (i = 0; i < nw; i++) { | |
99 | ifw[i].ifw_base = p; | |
100 | ifw[i].ifw_addr = p + off; | |
8e313c90 | 101 | p += nclbytes; |
822a4f2c MK |
102 | } |
103 | ifu->iff_hlen = hlen; | |
104 | ifu->iff_uban = uban; | |
105 | ifu->iff_uba = uba_hd[uban].uh_uba; | |
ca67e7b4 | 106 | ifu->iff_ubamr = uba_hd[uban].uh_mr; |
822a4f2c MK |
107 | } |
108 | for (i = 0; i < nr; i++) | |
109 | if (if_ubaalloc(ifu, &ifr[i], nmr) == 0) { | |
110 | nr = i; | |
111 | nw = 0; | |
112 | goto bad; | |
113 | } | |
114 | for (i = 0; i < nw; i++) | |
115 | if (if_ubaalloc(ifu, &ifw[i].ifrw, nmr) == 0) { | |
116 | nw = i; | |
117 | goto bad; | |
118 | } | |
119 | while (--nw >= 0) { | |
120 | for (i = 0; i < nmr; i++) | |
121 | ifw[nw].ifw_wmap[i] = ifw[nw].ifw_mr[i]; | |
122 | ifw[nw].ifw_xswapd = 0; | |
3ab9cb88 | 123 | ifw[nw].ifw_flags = IFRW_W; |
92441f6e | 124 | ifw[nw].ifw_nmr = nmr; |
176c481f | 125 | } |
0468e5fa | 126 | return (1); |
0468e5fa | 127 | bad: |
822a4f2c | 128 | while (--nw >= 0) |
2f6055c3 | 129 | ubarelse(ifu->iff_uban, &ifw[nw].ifw_info); |
822a4f2c | 130 | while (--nr >= 0) |
2f6055c3 | 131 | ubarelse(ifu->iff_uban, &ifr[nr].ifrw_info); |
8e313c90 | 132 | free(cp, M_DEVBUF); |
822a4f2c | 133 | ifr[0].ifrw_addr = 0; |
0468e5fa BJ |
134 | return (0); |
135 | } | |
136 | ||
137 | /* | |
822a4f2c | 138 | * Setup an ifrw structure by allocating UNIBUS map registers, |
d6391cba SL |
139 | * possibly a buffered data path, and initializing the fields of |
140 | * the ifrw structure to minimize run-time overhead. | |
0468e5fa BJ |
141 | */ |
142 | static | |
b454c3ea | 143 | if_ubaalloc(ifu, ifrw, nmr) |
822a4f2c | 144 | struct ifubinfo *ifu; |
0468e5fa | 145 | register struct ifrw *ifrw; |
b454c3ea | 146 | int nmr; |
0468e5fa BJ |
147 | { |
148 | register int info; | |
149 | ||
150 | info = | |
822a4f2c MK |
151 | uballoc(ifu->iff_uban, ifrw->ifrw_addr, nmr*NBPG + ifu->iff_hlen, |
152 | ifu->iff_flags); | |
0468e5fa | 153 | if (info == 0) |
8a13b737 | 154 | return (0); |
0468e5fa BJ |
155 | ifrw->ifrw_info = info; |
156 | ifrw->ifrw_bdp = UBAI_BDP(info); | |
97da2a42 | 157 | ifrw->ifrw_proto = UBAMR_MRV | (UBAI_BDP(info) << UBAMR_DPSHIFT); |
ca67e7b4 | 158 | ifrw->ifrw_mr = &ifu->iff_ubamr[UBAI_MR(info) + (ifu->iff_hlen? 1 : 0)]; |
8a13b737 | 159 | return (1); |
0468e5fa BJ |
160 | } |
161 | ||
162 | /* | |
f1b2fa5b | 163 | * Pull read data off a interface. |
193bee4c | 164 | * Totlen is length of data, with local net header stripped. |
f1b2fa5b BJ |
165 | * Off is non-zero if a trailer protocol was used, and |
166 | * gives the offset of the trailer information. | |
193bee4c | 167 | * We copy the header from the trailer and then all the normal |
f1b2fa5b BJ |
168 | * data into mbufs. When full cluster sized units are present |
169 | * on the interface on cluster boundaries we can get them more | |
170 | * easily by remapping, and take advantage of this here. | |
193bee4c | 171 | * Save a pointer to the interface structure and the total length, |
25506232 | 172 | * so that protocols can determine where incoming packets arrived. |
3ab9cb88 MK |
173 | * Note: we may be called to receive from a transmit buffer by some |
174 | * devices. In that case, we must force normal mapping of the buffer, | |
175 | * so that the correct data will appear (only unibus maps are | |
176 | * changed when remapping the transmit buffers). | |
0468e5fa BJ |
177 | */ |
178 | struct mbuf * | |
193bee4c | 179 | if_ubaget(ifu, ifr, totlen, off, ifp) |
822a4f2c MK |
180 | struct ifubinfo *ifu; |
181 | register struct ifrw *ifr; | |
193bee4c MK |
182 | register int totlen; |
183 | int off; | |
25506232 | 184 | struct ifnet *ifp; |
0468e5fa | 185 | { |
822a4f2c MK |
186 | struct mbuf *top, **mp; |
187 | register struct mbuf *m; | |
2fdf5fa3 | 188 | register caddr_t cp = ifr->ifrw_addr + ifu->iff_hlen, pp; |
193bee4c MK |
189 | register int len; |
190 | caddr_t epkt = cp + totlen; | |
0468e5fa | 191 | |
f1b2fa5b BJ |
192 | top = 0; |
193 | mp = ⊤ | |
193bee4c MK |
194 | /* |
195 | * Skip the trailer header (type and trailer length). | |
196 | */ | |
197 | if (off) { | |
198 | off += 2 * sizeof(u_short); | |
199 | totlen -= 2 * sizeof(u_short); | |
200 | cp += off; | |
201 | } | |
202 | MGETHDR(m, M_DONTWAIT, MT_DATA); | |
203 | if (m == 0) | |
204 | return ((struct mbuf *)NULL); | |
205 | m->m_pkthdr.rcvif = ifp; | |
206 | m->m_pkthdr.len = totlen; | |
207 | m->m_len = MHLEN; | |
208 | ||
3ab9cb88 MK |
209 | if (ifr->ifrw_flags & IFRW_W) |
210 | rcv_xmtbuf((struct ifxmt *)ifr); | |
193bee4c | 211 | |
f1b2fa5b | 212 | while (totlen > 0) { |
193bee4c | 213 | if (top) { |
5ec5a918 | 214 | MGET(m, M_DONTWAIT, MT_DATA); |
193bee4c MK |
215 | if (m == 0) { |
216 | m_freem(top); | |
217 | top = 0; | |
218 | goto out; | |
219 | } | |
5ec5a918 | 220 | m->m_len = MLEN; |
193bee4c MK |
221 | } |
222 | len = min(totlen, epkt - cp); | |
5ec5a918 | 223 | if (len >= MINCLSIZE) { |
0468e5fa | 224 | struct pte *cpte, *ppte; |
b454c3ea | 225 | int x, *ip, i; |
0468e5fa | 226 | |
5ec5a918 MK |
227 | MCLGET(m, M_DONTWAIT); |
228 | if ((m->m_flags & M_EXT) == 0) | |
25506232 | 229 | goto nopage; |
193bee4c | 230 | len = min(len, MCLBYTES); |
5ec5a918 | 231 | m->m_len = len; |
b454c3ea | 232 | if (!claligned(cp)) |
0468e5fa BJ |
233 | goto copy; |
234 | ||
235 | /* | |
2fdf5fa3 | 236 | * Switch pages mapped to UNIBUS with new page pp, |
b454c3ea | 237 | * as quick form of copy. Remap UNIBUS and invalidate. |
0468e5fa | 238 | */ |
2fdf5fa3 | 239 | pp = mtod(m, char *); |
1ae120d5 MK |
240 | cpte = kvtopte(cp); |
241 | ppte = kvtopte(pp); | |
822a4f2c MK |
242 | x = btop(cp - ifr->ifrw_addr); |
243 | ip = (int *)&ifr->ifrw_mr[x]; | |
5ec5a918 | 244 | for (i = 0; i < MCLBYTES/NBPG; i++) { |
0468e5fa | 245 | struct pte t; |
b454c3ea | 246 | t = *ppte; *ppte++ = *cpte; *cpte = t; |
1ae120d5 | 247 | *ip++ = cpte++->pg_pfnum|ifr->ifrw_proto; |
c63e9630 | 248 | mtpr(TBIS, cp); |
8a13b737 | 249 | cp += NBPG; |
2fdf5fa3 MK |
250 | mtpr(TBIS, (caddr_t)pp); |
251 | pp += NBPG; | |
0468e5fa BJ |
252 | } |
253 | goto nocopy; | |
254 | } | |
255 | nopage: | |
5ec5a918 | 256 | if (len < m->m_len) { |
25506232 | 257 | /* |
5ec5a918 | 258 | * Place initial small packet/header at end of mbuf. |
25506232 | 259 | */ |
5ec5a918 MK |
260 | if (top == 0 && len + max_linkhdr <= m->m_len) |
261 | m->m_data += max_linkhdr; | |
262 | m->m_len = len; | |
263 | } else | |
264 | len = m->m_len; | |
0468e5fa | 265 | copy: |
5ec5a918 MK |
266 | bcopy(cp, mtod(m, caddr_t), (unsigned)len); |
267 | cp += len; | |
0468e5fa | 268 | nocopy: |
f1b2fa5b BJ |
269 | *mp = m; |
270 | mp = &m->m_next; | |
193bee4c MK |
271 | totlen -= len; |
272 | if (cp == epkt) | |
273 | cp = ifr->ifrw_addr + ifu->iff_hlen; | |
4f02060d | 274 | } |
3ab9cb88 MK |
275 | out: |
276 | if (ifr->ifrw_flags & IFRW_W) | |
277 | restor_xmtbuf((struct ifxmt *)ifr); | |
0468e5fa | 278 | return (top); |
3ab9cb88 MK |
279 | } |
280 | ||
281 | /* | |
282 | * Change the mapping on a transmit buffer so that if_ubaget may | |
283 | * receive from that buffer. Copy data from any pages mapped to Unibus | |
284 | * into the pages mapped to normal kernel virtual memory, so that | |
285 | * they can be accessed and swapped as usual. We take advantage | |
286 | * of the fact that clusters are placed on the xtofree list | |
287 | * in inverse order, finding the last one. | |
288 | */ | |
289 | static | |
290 | rcv_xmtbuf(ifw) | |
291 | register struct ifxmt *ifw; | |
292 | { | |
293 | register struct mbuf *m; | |
294 | struct mbuf **mprev; | |
295 | register i; | |
3ab9cb88 MK |
296 | char *cp; |
297 | ||
8011f5df | 298 | while (i = ffs((long)ifw->ifw_xswapd)) { |
5ec5a918 | 299 | cp = ifw->ifw_base + i * MCLBYTES; |
3ab9cb88 MK |
300 | i--; |
301 | ifw->ifw_xswapd &= ~(1<<i); | |
3ab9cb88 MK |
302 | mprev = &ifw->ifw_xtofree; |
303 | for (m = ifw->ifw_xtofree; m && m->m_next; m = m->m_next) | |
304 | mprev = &m->m_next; | |
305 | if (m == NULL) | |
44477143 | 306 | break; |
5ec5a918 | 307 | bcopy(mtod(m, caddr_t), cp, MCLBYTES); |
8011f5df | 308 | (void) m_free(m); |
3ab9cb88 | 309 | *mprev = NULL; |
3ab9cb88 | 310 | } |
44477143 MK |
311 | ifw->ifw_xswapd = 0; |
312 | for (i = 0; i < ifw->ifw_nmr; i++) | |
313 | ifw->ifw_mr[i] = ifw->ifw_wmap[i]; | |
3ab9cb88 MK |
314 | } |
315 | ||
316 | /* | |
317 | * Put a transmit buffer back together after doing an if_ubaget on it, | |
318 | * which may have swapped pages. | |
319 | */ | |
320 | static | |
321 | restor_xmtbuf(ifw) | |
322 | register struct ifxmt *ifw; | |
323 | { | |
324 | register i; | |
325 | ||
92441f6e | 326 | for (i = 0; i < ifw->ifw_nmr; i++) |
3ab9cb88 | 327 | ifw->ifw_wmap[i] = ifw->ifw_mr[i]; |
0468e5fa BJ |
328 | } |
329 | ||
330 | /* | |
331 | * Map a chain of mbufs onto a network interface | |
332 | * in preparation for an i/o operation. | |
333 | * The argument chain of mbufs includes the local network | |
334 | * header which is copied to be in the mapped, aligned | |
335 | * i/o space. | |
336 | */ | |
822a4f2c MK |
337 | if_ubaput(ifu, ifw, m) |
338 | struct ifubinfo *ifu; | |
339 | register struct ifxmt *ifw; | |
0468e5fa BJ |
340 | register struct mbuf *m; |
341 | { | |
342 | register struct mbuf *mp; | |
343 | register caddr_t cp, dp; | |
344 | register int i; | |
b454c3ea | 345 | int xswapd = 0; |
e6822062 | 346 | int x, cc, t; |
0468e5fa | 347 | |
822a4f2c | 348 | cp = ifw->ifw_addr; |
0468e5fa BJ |
349 | while (m) { |
350 | dp = mtod(m, char *); | |
25506232 | 351 | if (claligned(cp) && claligned(dp) && |
5ec5a918 | 352 | (m->m_len == MCLBYTES || m->m_next == (struct mbuf *)0)) { |
1ae120d5 MK |
353 | struct pte *pte; |
354 | int *ip; | |
355 | ||
356 | pte = kvtopte(dp); | |
822a4f2c MK |
357 | x = btop(cp - ifw->ifw_addr); |
358 | ip = (int *)&ifw->ifw_mr[x]; | |
5ec5a918 | 359 | for (i = 0; i < MCLBYTES/NBPG; i++) |
1ae120d5 | 360 | *ip++ = ifw->ifw_proto | pte++->pg_pfnum; |
5ec5a918 | 361 | xswapd |= 1 << (x>>(MCLSHIFT-PGSHIFT)); |
b454c3ea | 362 | mp = m->m_next; |
822a4f2c MK |
363 | m->m_next = ifw->ifw_xtofree; |
364 | ifw->ifw_xtofree = m; | |
b454c3ea BJ |
365 | cp += m->m_len; |
366 | } else { | |
0468e5fa | 367 | bcopy(mtod(m, caddr_t), cp, (unsigned)m->m_len); |
b454c3ea BJ |
368 | cp += m->m_len; |
369 | MFREE(m, mp); | |
370 | } | |
0468e5fa BJ |
371 | m = mp; |
372 | } | |
b454c3ea BJ |
373 | |
374 | /* | |
822a4f2c | 375 | * Xswapd is the set of clusters we just mapped out. Ifu->iff_xswapd |
b454c3ea BJ |
376 | * is the set of clusters mapped out from before. We compute |
377 | * the number of clusters involved in this operation in x. | |
378 | * Clusters mapped out before and involved in this operation | |
379 | * should be unmapped so original pages will be accessed by the device. | |
380 | */ | |
822a4f2c | 381 | cc = cp - ifw->ifw_addr; |
5ec5a918 | 382 | x = ((cc - ifu->iff_hlen) + MCLBYTES - 1) >> MCLSHIFT; |
822a4f2c | 383 | ifw->ifw_xswapd &= ~xswapd; |
8011f5df | 384 | while (i = ffs((long)ifw->ifw_xswapd)) { |
97da2a42 BJ |
385 | i--; |
386 | if (i >= x) | |
387 | break; | |
822a4f2c | 388 | ifw->ifw_xswapd &= ~(1<<i); |
5ec5a918 MK |
389 | i *= MCLBYTES/NBPG; |
390 | for (t = 0; t < MCLBYTES/NBPG; t++) { | |
822a4f2c | 391 | ifw->ifw_mr[i] = ifw->ifw_wmap[i]; |
97da2a42 | 392 | i++; |
0468e5fa | 393 | } |
97da2a42 | 394 | } |
822a4f2c | 395 | ifw->ifw_xswapd |= xswapd; |
b454c3ea | 396 | return (cc); |
0468e5fa | 397 | } |