Commit | Line | Data |
---|---|---|
da7c5cc6 KM |
1 | /* |
2 | * Copyright (c) 1982 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | * | |
3ab9cb88 | 6 | * @(#)if_uba.c 6.7 (Berkeley) %G% |
da7c5cc6 | 7 | */ |
961945a8 SL |
8 | |
9 | #include "../machine/pte.h" | |
0468e5fa | 10 | |
a6e960e7 JB |
11 | #include "param.h" |
12 | #include "systm.h" | |
13 | #include "mbuf.h" | |
14 | #include "map.h" | |
15 | #include "buf.h" | |
16 | #include "cmap.h" | |
17 | #include "vmmac.h" | |
18 | #include "socket.h" | |
3ab9cb88 | 19 | #include "syslog.h" |
eaa60542 | 20 | |
8a13b737 | 21 | #include "../net/if.h" |
eaa60542 BJ |
22 | |
23 | #include "../vax/mtpr.h" | |
a6e960e7 | 24 | #include "if_uba.h" |
eaa60542 BJ |
25 | #include "../vaxuba/ubareg.h" |
26 | #include "../vaxuba/ubavar.h" | |
0468e5fa BJ |
27 | |
28 | /* | |
29 | * Routines supporting UNIBUS network interfaces. | |
30 | * | |
31 | * TODO: | |
32 | * Support interfaces using only one BDP statically. | |
33 | */ | |
34 | ||
35 | /* | |
36 | * Init UNIBUS for interface on uban whose headers of size hlen are to | |
37 | * end on a page boundary. We allocate a UNIBUS map register for the page | |
38 | * with the header, and nmr more UNIBUS map registers for i/o on the adapter, | |
822a4f2c | 39 | * doing this once for each read and once for each write buffer. We also |
0468e5fa BJ |
40 | * allocate page frames in the mbuffer pool for these pages. |
41 | */ | |
822a4f2c MK |
42 | if_ubaminit(ifu, uban, hlen, nmr, ifr, nr, ifw, nw) |
43 | register struct ifubinfo *ifu; | |
44 | int uban, hlen, nmr, nr, nw; | |
45 | register struct ifrw *ifr; | |
46 | register struct ifxmt *ifw; | |
0468e5fa | 47 | { |
822a4f2c MK |
48 | register caddr_t p; |
49 | caddr_t cp; | |
e6822062 | 50 | int i, ncl, off; |
0468e5fa | 51 | |
e6822062 MK |
52 | if (hlen) |
53 | off = CLBYTES - hlen; | |
54 | else | |
55 | off = 0; | |
56 | ncl = clrnd(nmr) / CLSIZE; | |
57 | if (hlen) | |
58 | ncl++; | |
822a4f2c MK |
59 | if (ifr[0].ifrw_addr) |
60 | cp = ifr[0].ifrw_addr - off; | |
176c481f | 61 | else { |
822a4f2c | 62 | cp = m_clalloc((nr + nw) * ncl, MPG_SPACE); |
176c481f BJ |
63 | if (cp == 0) |
64 | return (0); | |
822a4f2c MK |
65 | p = cp; |
66 | for (i = 0; i < nr; i++) { | |
67 | ifr[i].ifrw_addr = p + off; | |
68 | p += ncl * CLBYTES; | |
69 | } | |
70 | for (i = 0; i < nw; i++) { | |
71 | ifw[i].ifw_base = p; | |
72 | ifw[i].ifw_addr = p + off; | |
73 | p += ncl * CLBYTES; | |
74 | } | |
75 | ifu->iff_hlen = hlen; | |
76 | ifu->iff_uban = uban; | |
77 | ifu->iff_uba = uba_hd[uban].uh_uba; | |
78 | } | |
79 | for (i = 0; i < nr; i++) | |
80 | if (if_ubaalloc(ifu, &ifr[i], nmr) == 0) { | |
81 | nr = i; | |
82 | nw = 0; | |
83 | goto bad; | |
84 | } | |
85 | for (i = 0; i < nw; i++) | |
86 | if (if_ubaalloc(ifu, &ifw[i].ifrw, nmr) == 0) { | |
87 | nw = i; | |
88 | goto bad; | |
89 | } | |
90 | while (--nw >= 0) { | |
91 | for (i = 0; i < nmr; i++) | |
92 | ifw[nw].ifw_wmap[i] = ifw[nw].ifw_mr[i]; | |
93 | ifw[nw].ifw_xswapd = 0; | |
3ab9cb88 | 94 | ifw[nw].ifw_flags = IFRW_W; |
176c481f | 95 | } |
0468e5fa | 96 | return (1); |
0468e5fa | 97 | bad: |
822a4f2c MK |
98 | while (--nw >= 0) |
99 | ubarelse(ifu->iff_uban, &ifr[nw].ifrw_info); | |
100 | while (--nr >= 0) | |
101 | ubarelse(ifu->iff_uban, &ifw[nr].ifw_info); | |
102 | m_pgfree(cp, (nr + nw) * ncl); | |
103 | ifr[0].ifrw_addr = 0; | |
0468e5fa BJ |
104 | return (0); |
105 | } | |
106 | ||
107 | /* | |
822a4f2c | 108 | * Setup an ifrw structure by allocating UNIBUS map registers, |
d6391cba SL |
109 | * possibly a buffered data path, and initializing the fields of |
110 | * the ifrw structure to minimize run-time overhead. | |
0468e5fa BJ |
111 | */ |
112 | static | |
b454c3ea | 113 | if_ubaalloc(ifu, ifrw, nmr) |
822a4f2c | 114 | struct ifubinfo *ifu; |
0468e5fa | 115 | register struct ifrw *ifrw; |
b454c3ea | 116 | int nmr; |
0468e5fa BJ |
117 | { |
118 | register int info; | |
119 | ||
120 | info = | |
822a4f2c MK |
121 | uballoc(ifu->iff_uban, ifrw->ifrw_addr, nmr*NBPG + ifu->iff_hlen, |
122 | ifu->iff_flags); | |
0468e5fa | 123 | if (info == 0) |
8a13b737 | 124 | return (0); |
0468e5fa BJ |
125 | ifrw->ifrw_info = info; |
126 | ifrw->ifrw_bdp = UBAI_BDP(info); | |
97da2a42 | 127 | ifrw->ifrw_proto = UBAMR_MRV | (UBAI_BDP(info) << UBAMR_DPSHIFT); |
822a4f2c | 128 | ifrw->ifrw_mr = &ifu->iff_uba->uba_map[UBAI_MR(info) + (ifu->iff_hlen? |
e6822062 | 129 | 1 : 0)]; |
8a13b737 | 130 | return (1); |
0468e5fa BJ |
131 | } |
132 | ||
133 | /* | |
f1b2fa5b BJ |
134 | * Pull read data off a interface. |
135 | * Len is length of data, with local net header stripped. | |
136 | * Off is non-zero if a trailer protocol was used, and | |
137 | * gives the offset of the trailer information. | |
138 | * We copy the trailer information and then all the normal | |
139 | * data into mbufs. When full cluster sized units are present | |
140 | * on the interface on cluster boundaries we can get them more | |
141 | * easily by remapping, and take advantage of this here. | |
25506232 MK |
142 | * Prepend a pointer to the interface structure, |
143 | * so that protocols can determine where incoming packets arrived. | |
3ab9cb88 MK |
144 | * Note: we may be called to receive from a transmit buffer by some |
145 | * devices. In that case, we must force normal mapping of the buffer, | |
146 | * so that the correct data will appear (only unibus maps are | |
147 | * changed when remapping the transmit buffers). | |
0468e5fa BJ |
148 | */ |
149 | struct mbuf * | |
822a4f2c MK |
150 | if_ubaget(ifu, ifr, totlen, off0, ifp) |
151 | struct ifubinfo *ifu; | |
152 | register struct ifrw *ifr; | |
f1b2fa5b | 153 | int totlen, off0; |
25506232 | 154 | struct ifnet *ifp; |
0468e5fa | 155 | { |
822a4f2c MK |
156 | struct mbuf *top, **mp; |
157 | register struct mbuf *m; | |
b454c3ea | 158 | int off = off0, len; |
822a4f2c | 159 | register caddr_t cp = ifr->ifrw_addr + ifu->iff_hlen; |
0468e5fa | 160 | |
f1b2fa5b BJ |
161 | top = 0; |
162 | mp = ⊤ | |
3ab9cb88 MK |
163 | if (ifr->ifrw_flags & IFRW_W) |
164 | rcv_xmtbuf((struct ifxmt *)ifr); | |
f1b2fa5b | 165 | while (totlen > 0) { |
cce93e4b | 166 | MGET(m, M_DONTWAIT, MT_DATA); |
3ab9cb88 MK |
167 | if (m == 0) { |
168 | m_freem(top); | |
169 | top = 0; | |
170 | goto out; | |
171 | } | |
f1b2fa5b BJ |
172 | if (off) { |
173 | len = totlen - off; | |
822a4f2c | 174 | cp = ifr->ifrw_addr + ifu->iff_hlen + off; |
f1b2fa5b BJ |
175 | } else |
176 | len = totlen; | |
25506232 | 177 | if (len >= NBPG) { |
b454c3ea | 178 | struct mbuf *p; |
0468e5fa | 179 | struct pte *cpte, *ppte; |
b454c3ea | 180 | int x, *ip, i; |
0468e5fa | 181 | |
25506232 MK |
182 | /* |
183 | * If doing the first mbuf and | |
184 | * the interface pointer hasn't been put in, | |
185 | * put it in a separate mbuf to preserve alignment. | |
186 | */ | |
187 | if (ifp) { | |
188 | len = 0; | |
189 | goto nopage; | |
190 | } | |
0468e5fa BJ |
191 | MCLGET(p, 1); |
192 | if (p == 0) | |
193 | goto nopage; | |
822a4f2c | 194 | m->m_len = MIN(len, CLBYTES); |
0468e5fa | 195 | m->m_off = (int)p - (int)m; |
b454c3ea | 196 | if (!claligned(cp)) |
0468e5fa BJ |
197 | goto copy; |
198 | ||
199 | /* | |
b454c3ea BJ |
200 | * Switch pages mapped to UNIBUS with new page p, |
201 | * as quick form of copy. Remap UNIBUS and invalidate. | |
0468e5fa | 202 | */ |
b454c3ea BJ |
203 | cpte = &Mbmap[mtocl(cp)*CLSIZE]; |
204 | ppte = &Mbmap[mtocl(p)*CLSIZE]; | |
822a4f2c MK |
205 | x = btop(cp - ifr->ifrw_addr); |
206 | ip = (int *)&ifr->ifrw_mr[x]; | |
0468e5fa BJ |
207 | for (i = 0; i < CLSIZE; i++) { |
208 | struct pte t; | |
b454c3ea | 209 | t = *ppte; *ppte++ = *cpte; *cpte = t; |
0468e5fa | 210 | *ip++ = |
822a4f2c | 211 | cpte++->pg_pfnum|ifr->ifrw_proto; |
0468e5fa | 212 | mtpr(TBIS, cp); |
8a13b737 | 213 | cp += NBPG; |
0468e5fa | 214 | mtpr(TBIS, (caddr_t)p); |
8a13b737 | 215 | p += NBPG / sizeof (*p); |
0468e5fa BJ |
216 | } |
217 | goto nocopy; | |
218 | } | |
219 | nopage: | |
0468e5fa | 220 | m->m_off = MMINOFF; |
25506232 MK |
221 | if (ifp) { |
222 | /* | |
223 | * Leave room for ifp. | |
224 | */ | |
225 | m->m_len = MIN(MLEN - sizeof(ifp), len); | |
226 | m->m_off += sizeof(ifp); | |
227 | } else | |
228 | m->m_len = MIN(MLEN, len); | |
0468e5fa BJ |
229 | copy: |
230 | bcopy(cp, mtod(m, caddr_t), (unsigned)m->m_len); | |
231 | cp += m->m_len; | |
232 | nocopy: | |
f1b2fa5b BJ |
233 | *mp = m; |
234 | mp = &m->m_next; | |
235 | if (off) { | |
b454c3ea | 236 | /* sort of an ALGOL-W style for statement... */ |
f1b2fa5b BJ |
237 | off += m->m_len; |
238 | if (off == totlen) { | |
822a4f2c | 239 | cp = ifr->ifrw_addr + ifu->iff_hlen; |
f1b2fa5b | 240 | off = 0; |
97da2a42 | 241 | totlen = off0; |
f1b2fa5b | 242 | } |
97da2a42 | 243 | } else |
c7a4fdbf | 244 | totlen -= m->m_len; |
25506232 MK |
245 | if (ifp) { |
246 | /* | |
247 | * Prepend interface pointer to first mbuf. | |
248 | */ | |
249 | m->m_len += sizeof(ifp); | |
250 | m->m_off -= sizeof(ifp); | |
251 | *(mtod(m, struct ifnet **)) = ifp; | |
252 | ifp = (struct ifnet *)0; | |
253 | } | |
0468e5fa | 254 | } |
3ab9cb88 MK |
255 | out: |
256 | if (ifr->ifrw_flags & IFRW_W) | |
257 | restor_xmtbuf((struct ifxmt *)ifr); | |
0468e5fa | 258 | return (top); |
3ab9cb88 MK |
259 | } |
260 | ||
261 | /* | |
262 | * Change the mapping on a transmit buffer so that if_ubaget may | |
263 | * receive from that buffer. Copy data from any pages mapped to Unibus | |
264 | * into the pages mapped to normal kernel virtual memory, so that | |
265 | * they can be accessed and swapped as usual. We take advantage | |
266 | * of the fact that clusters are placed on the xtofree list | |
267 | * in inverse order, finding the last one. | |
268 | */ | |
269 | static | |
270 | rcv_xmtbuf(ifw) | |
271 | register struct ifxmt *ifw; | |
272 | { | |
273 | register struct mbuf *m; | |
274 | struct mbuf **mprev; | |
275 | register i; | |
276 | int t; | |
277 | char *cp; | |
278 | ||
279 | while (i = ffs(ifw->ifw_xswapd)) { | |
280 | cp = ifw->ifw_base + i * CLBYTES; | |
281 | i--; | |
282 | ifw->ifw_xswapd &= ~(1<<i); | |
283 | i *= CLSIZE; | |
284 | mprev = &ifw->ifw_xtofree; | |
285 | for (m = ifw->ifw_xtofree; m && m->m_next; m = m->m_next) | |
286 | mprev = &m->m_next; | |
287 | if (m == NULL) | |
288 | panic("rcv_xmtbuf"); | |
289 | bcopy(mtod(m, caddr_t), cp, CLBYTES); | |
290 | m_free(m); | |
291 | *mprev = NULL; | |
292 | for (t = 0; t < CLSIZE; t++) { | |
293 | ifw->ifw_mr[i] = ifw->ifw_wmap[i]; | |
294 | i++; | |
295 | } | |
296 | } | |
297 | } | |
298 | ||
299 | /* | |
300 | * Put a transmit buffer back together after doing an if_ubaget on it, | |
301 | * which may have swapped pages. | |
302 | */ | |
303 | static | |
304 | restor_xmtbuf(ifw) | |
305 | register struct ifxmt *ifw; | |
306 | { | |
307 | register i; | |
308 | ||
309 | for (i = 0; i < IF_MAXNUBAMR; i++) | |
310 | ifw->ifw_wmap[i] = ifw->ifw_mr[i]; | |
0468e5fa BJ |
311 | } |
312 | ||
313 | /* | |
314 | * Map a chain of mbufs onto a network interface | |
315 | * in preparation for an i/o operation. | |
316 | * The argument chain of mbufs includes the local network | |
317 | * header which is copied to be in the mapped, aligned | |
318 | * i/o space. | |
319 | */ | |
822a4f2c MK |
320 | if_ubaput(ifu, ifw, m) |
321 | struct ifubinfo *ifu; | |
322 | register struct ifxmt *ifw; | |
0468e5fa BJ |
323 | register struct mbuf *m; |
324 | { | |
325 | register struct mbuf *mp; | |
326 | register caddr_t cp, dp; | |
327 | register int i; | |
b454c3ea | 328 | int xswapd = 0; |
e6822062 | 329 | int x, cc, t; |
0468e5fa | 330 | |
822a4f2c | 331 | cp = ifw->ifw_addr; |
0468e5fa BJ |
332 | while (m) { |
333 | dp = mtod(m, char *); | |
25506232 MK |
334 | if (claligned(cp) && claligned(dp) && |
335 | (m->m_len == CLBYTES || m->m_next == (struct mbuf *)0)) { | |
0468e5fa | 336 | struct pte *pte; int *ip; |
b454c3ea | 337 | pte = &Mbmap[mtocl(dp)*CLSIZE]; |
822a4f2c MK |
338 | x = btop(cp - ifw->ifw_addr); |
339 | ip = (int *)&ifw->ifw_mr[x]; | |
0468e5fa BJ |
340 | for (i = 0; i < CLSIZE; i++) |
341 | *ip++ = | |
822a4f2c | 342 | ifw->ifw_proto | pte++->pg_pfnum; |
97da2a42 | 343 | xswapd |= 1 << (x>>(CLSHIFT-PGSHIFT)); |
b454c3ea | 344 | mp = m->m_next; |
822a4f2c MK |
345 | m->m_next = ifw->ifw_xtofree; |
346 | ifw->ifw_xtofree = m; | |
b454c3ea BJ |
347 | cp += m->m_len; |
348 | } else { | |
0468e5fa | 349 | bcopy(mtod(m, caddr_t), cp, (unsigned)m->m_len); |
b454c3ea BJ |
350 | cp += m->m_len; |
351 | MFREE(m, mp); | |
352 | } | |
0468e5fa BJ |
353 | m = mp; |
354 | } | |
b454c3ea BJ |
355 | |
356 | /* | |
822a4f2c | 357 | * Xswapd is the set of clusters we just mapped out. Ifu->iff_xswapd |
b454c3ea BJ |
358 | * is the set of clusters mapped out from before. We compute |
359 | * the number of clusters involved in this operation in x. | |
360 | * Clusters mapped out before and involved in this operation | |
361 | * should be unmapped so original pages will be accessed by the device. | |
362 | */ | |
822a4f2c MK |
363 | cc = cp - ifw->ifw_addr; |
364 | x = ((cc - ifu->iff_hlen) + CLBYTES - 1) >> CLSHIFT; | |
365 | ifw->ifw_xswapd &= ~xswapd; | |
366 | while (i = ffs(ifw->ifw_xswapd)) { | |
97da2a42 BJ |
367 | i--; |
368 | if (i >= x) | |
369 | break; | |
822a4f2c | 370 | ifw->ifw_xswapd &= ~(1<<i); |
97da2a42 | 371 | i *= CLSIZE; |
e6822062 | 372 | for (t = 0; t < CLSIZE; t++) { |
822a4f2c | 373 | ifw->ifw_mr[i] = ifw->ifw_wmap[i]; |
97da2a42 | 374 | i++; |
0468e5fa | 375 | } |
97da2a42 | 376 | } |
822a4f2c | 377 | ifw->ifw_xswapd |= xswapd; |
b454c3ea | 378 | return (cc); |
0468e5fa | 379 | } |