1ca987d46SWarner Losh /*-
2ca987d46SWarner Losh * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
3ca987d46SWarner Losh * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
4ca987d46SWarner Losh * All rights reserved.
52e43efd0SJohn Baldwin * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
6ca987d46SWarner Losh *
7ca987d46SWarner Losh * Redistribution and use in source and binary forms, with or without
8ca987d46SWarner Losh * modification, are permitted provided that the following conditions
9ca987d46SWarner Losh * are met:
10ca987d46SWarner Losh * 1. Redistributions of source code must retain the above copyright
11ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer.
12ca987d46SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright
13ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer in the
14ca987d46SWarner Losh * documentation and/or other materials provided with the distribution.
15ca987d46SWarner Losh *
16ca987d46SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17ca987d46SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18ca987d46SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19ca987d46SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20ca987d46SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21ca987d46SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22ca987d46SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23ca987d46SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24ca987d46SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25ca987d46SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26ca987d46SWarner Losh * SUCH DAMAGE.
27ca987d46SWarner Losh */
28ca987d46SWarner Losh
29ca987d46SWarner Losh #include <stand.h>
303daa8e16SKyle Evans #include <errno.h>
313daa8e16SKyle Evans #include <stdbool.h>
32ca987d46SWarner Losh #include <stddef.h>
33ca987d46SWarner Losh #include <string.h>
34ca987d46SWarner Losh #include <stdarg.h>
35ca987d46SWarner Losh #include <sys/param.h>
36ca987d46SWarner Losh
37ca987d46SWarner Losh #include <net/ethernet.h>
38ca987d46SWarner Losh #include <netinet/in_systm.h>
39ca987d46SWarner Losh #include <netinet/in.h>
40ca987d46SWarner Losh #include <netinet/ip.h>
41ca987d46SWarner Losh #include <netinet/udp.h>
42ca987d46SWarner Losh
43ca987d46SWarner Losh #include <net.h>
44ca987d46SWarner Losh #include <netif.h>
45ca987d46SWarner Losh #include <nfsv2.h>
46ca987d46SWarner Losh #include <iodesc.h>
47ca987d46SWarner Losh
48ca987d46SWarner Losh #include <bootp.h>
49ca987d46SWarner Losh #include <bootstrap.h>
5075772fa2SToomas Soome #include "libi386.h"
51ca987d46SWarner Losh #include "btxv86.h"
52ca987d46SWarner Losh #include "pxe.h"
53ca987d46SWarner Losh
54ca987d46SWarner Losh static pxenv_t *pxenv_p = NULL; /* PXENV+ */
55ca987d46SWarner Losh static pxe_t *pxe_p = NULL; /* !PXE */
56ca987d46SWarner Losh
57ca987d46SWarner Losh #ifdef PXE_DEBUG
58ca987d46SWarner Losh static int pxe_debug = 0;
59ca987d46SWarner Losh #endif
60ca987d46SWarner Losh
61ca987d46SWarner Losh void pxe_enable(void *pxeinfo);
6275772fa2SToomas Soome static void (*pxe_call)(int func, void *ptr);
6375772fa2SToomas Soome static void pxenv_call(int func, void *ptr);
6475772fa2SToomas Soome static void bangpxe_call(int func, void *ptr);
65ca987d46SWarner Losh
66ca987d46SWarner Losh static int pxe_init(void);
67ca987d46SWarner Losh static int pxe_print(int verbose);
68ca987d46SWarner Losh static void pxe_cleanup(void);
69ca987d46SWarner Losh
70ca987d46SWarner Losh static void pxe_perror(int error);
71ca987d46SWarner Losh static int pxe_netif_match(struct netif *nif, void *machdep_hint);
72ca987d46SWarner Losh static int pxe_netif_probe(struct netif *nif, void *machdep_hint);
73ca987d46SWarner Losh static void pxe_netif_init(struct iodesc *desc, void *machdep_hint);
74ca987d46SWarner Losh static ssize_t pxe_netif_get(struct iodesc *, void **, time_t);
75ca987d46SWarner Losh static ssize_t pxe_netif_put(struct iodesc *desc, void *pkt, size_t len);
76ca987d46SWarner Losh static void pxe_netif_end(struct netif *nif);
77ca987d46SWarner Losh
78ca987d46SWarner Losh extern struct netif_stats pxe_st[];
7956e53cb8SWarner Losh extern uint16_t __bangpxeseg;
8056e53cb8SWarner Losh extern uint16_t __bangpxeoff;
81ca987d46SWarner Losh extern void __bangpxeentry(void);
8256e53cb8SWarner Losh extern uint16_t __pxenvseg;
8356e53cb8SWarner Losh extern uint16_t __pxenvoff;
84ca987d46SWarner Losh extern void __pxenventry(void);
85ca987d46SWarner Losh
86ca987d46SWarner Losh struct netif_dif pxe_ifs[] = {
87ca987d46SWarner Losh /* dif_unit dif_nsel dif_stats dif_private */
88ca987d46SWarner Losh {0, 1, &pxe_st[0], 0}
89ca987d46SWarner Losh };
90ca987d46SWarner Losh
91ca987d46SWarner Losh struct netif_stats pxe_st[nitems(pxe_ifs)];
92ca987d46SWarner Losh
93ca987d46SWarner Losh struct netif_driver pxenetif = {
94ca987d46SWarner Losh .netif_bname = "pxenet",
95ca987d46SWarner Losh .netif_match = pxe_netif_match,
96ca987d46SWarner Losh .netif_probe = pxe_netif_probe,
97ca987d46SWarner Losh .netif_init = pxe_netif_init,
98ca987d46SWarner Losh .netif_get = pxe_netif_get,
99ca987d46SWarner Losh .netif_put = pxe_netif_put,
100ca987d46SWarner Losh .netif_end = pxe_netif_end,
101ca987d46SWarner Losh .netif_ifs = pxe_ifs,
102ca987d46SWarner Losh .netif_nifs = nitems(pxe_ifs)
103ca987d46SWarner Losh };
104ca987d46SWarner Losh
105ca987d46SWarner Losh struct netif_driver *netif_drivers[] = {
106ca987d46SWarner Losh &pxenetif,
107ca987d46SWarner Losh NULL
108ca987d46SWarner Losh };
109ca987d46SWarner Losh
110ca987d46SWarner Losh struct devsw pxedisk = {
111ca987d46SWarner Losh .dv_name = "net",
112ca987d46SWarner Losh .dv_type = DEVT_NET,
113ca987d46SWarner Losh .dv_init = pxe_init,
114ca987d46SWarner Losh .dv_strategy = NULL, /* Will be set in pxe_init */
115ca987d46SWarner Losh .dv_open = NULL, /* Will be set in pxe_init */
116ca987d46SWarner Losh .dv_close = NULL, /* Will be set in pxe_init */
117ca987d46SWarner Losh .dv_ioctl = noioctl,
118ca987d46SWarner Losh .dv_print = pxe_print,
119*e98f952cSWarner Losh .dv_cleanup = pxe_cleanup,
120ca987d46SWarner Losh };
121ca987d46SWarner Losh
122ca987d46SWarner Losh /*
123ca987d46SWarner Losh * This function is called by the loader to enable PXE support if we
124ca987d46SWarner Losh * are booted by PXE. The passed in pointer is a pointer to the PXENV+
125ca987d46SWarner Losh * structure.
126ca987d46SWarner Losh */
127ca987d46SWarner Losh void
pxe_enable(void * pxeinfo)128ca987d46SWarner Losh pxe_enable(void *pxeinfo)
129ca987d46SWarner Losh {
130ca987d46SWarner Losh pxenv_p = (pxenv_t *)pxeinfo;
131ca987d46SWarner Losh pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 +
132ca987d46SWarner Losh pxenv_p->PXEPtr.offset);
133ca987d46SWarner Losh pxe_call = NULL;
134ca987d46SWarner Losh }
135ca987d46SWarner Losh
136ca987d46SWarner Losh /*
137ca987d46SWarner Losh * return true if pxe structures are found/initialized,
138ca987d46SWarner Losh * also figures out our IP information via the pxe cached info struct
139ca987d46SWarner Losh */
140ca987d46SWarner Losh static int
pxe_init(void)141ca987d46SWarner Losh pxe_init(void)
142ca987d46SWarner Losh {
143ca987d46SWarner Losh t_PXENV_GET_CACHED_INFO *gci_p;
144ca987d46SWarner Losh int counter;
145ca987d46SWarner Losh uint8_t checksum;
146ca987d46SWarner Losh uint8_t *checkptr;
147ca987d46SWarner Losh extern struct devsw netdev;
148ca987d46SWarner Losh
149ca987d46SWarner Losh if (pxenv_p == NULL)
150ca987d46SWarner Losh return (0);
151ca987d46SWarner Losh
152ca987d46SWarner Losh /* look for "PXENV+" */
153ca987d46SWarner Losh if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) {
154ca987d46SWarner Losh pxenv_p = NULL;
155ca987d46SWarner Losh return (0);
156ca987d46SWarner Losh }
157ca987d46SWarner Losh
158ca987d46SWarner Losh /* make sure the size is something we can handle */
159ca987d46SWarner Losh if (pxenv_p->Length > sizeof(*pxenv_p)) {
160ca987d46SWarner Losh printf("PXENV+ structure too large, ignoring\n");
161ca987d46SWarner Losh pxenv_p = NULL;
162ca987d46SWarner Losh return (0);
163ca987d46SWarner Losh }
164ca987d46SWarner Losh
165ca987d46SWarner Losh /*
166ca987d46SWarner Losh * do byte checksum:
167ca987d46SWarner Losh * add up each byte in the structure, the total should be 0
168ca987d46SWarner Losh */
169ca987d46SWarner Losh checksum = 0;
170ca987d46SWarner Losh checkptr = (uint8_t *) pxenv_p;
171ca987d46SWarner Losh for (counter = 0; counter < pxenv_p->Length; counter++)
172ca987d46SWarner Losh checksum += *checkptr++;
173ca987d46SWarner Losh if (checksum != 0) {
174ca987d46SWarner Losh printf("PXENV+ structure failed checksum, ignoring\n");
175ca987d46SWarner Losh pxenv_p = NULL;
176ca987d46SWarner Losh return (0);
177ca987d46SWarner Losh }
178ca987d46SWarner Losh
179ca987d46SWarner Losh /*
180ca987d46SWarner Losh * PXENV+ passed, so use that if !PXE is not available or
181ca987d46SWarner Losh * the checksum fails.
182ca987d46SWarner Losh */
183ca987d46SWarner Losh pxe_call = pxenv_call;
184ca987d46SWarner Losh if (pxenv_p->Version >= 0x0200) {
185ca987d46SWarner Losh for (;;) {
186ca987d46SWarner Losh if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) {
187ca987d46SWarner Losh pxe_p = NULL;
188ca987d46SWarner Losh break;
189ca987d46SWarner Losh }
190ca987d46SWarner Losh checksum = 0;
191ca987d46SWarner Losh checkptr = (uint8_t *)pxe_p;
192ca987d46SWarner Losh for (counter = 0; counter < pxe_p->StructLength;
193ca987d46SWarner Losh counter++)
194ca987d46SWarner Losh checksum += *checkptr++;
195ca987d46SWarner Losh if (checksum != 0) {
196ca987d46SWarner Losh pxe_p = NULL;
197ca987d46SWarner Losh break;
198ca987d46SWarner Losh }
199ca987d46SWarner Losh pxe_call = bangpxe_call;
200ca987d46SWarner Losh break;
201ca987d46SWarner Losh }
202ca987d46SWarner Losh }
203ca987d46SWarner Losh
204ca987d46SWarner Losh pxedisk.dv_open = netdev.dv_open;
205ca987d46SWarner Losh pxedisk.dv_close = netdev.dv_close;
206ca987d46SWarner Losh pxedisk.dv_strategy = netdev.dv_strategy;
207ca987d46SWarner Losh
208ca987d46SWarner Losh printf("\nPXE version %d.%d, real mode entry point ",
209ca987d46SWarner Losh (uint8_t) (pxenv_p->Version >> 8),
210ca987d46SWarner Losh (uint8_t) (pxenv_p->Version & 0xFF));
211ca987d46SWarner Losh if (pxe_call == bangpxe_call)
212ca987d46SWarner Losh printf("@%04x:%04x\n",
213ca987d46SWarner Losh pxe_p->EntryPointSP.segment,
214ca987d46SWarner Losh pxe_p->EntryPointSP.offset);
215ca987d46SWarner Losh else
216ca987d46SWarner Losh printf("@%04x:%04x\n",
217ca987d46SWarner Losh pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset);
218ca987d46SWarner Losh
21975772fa2SToomas Soome gci_p = bio_alloc(sizeof(*gci_p));
22075772fa2SToomas Soome if (gci_p == NULL) {
22175772fa2SToomas Soome pxe_p = NULL;
22275772fa2SToomas Soome return (0);
22375772fa2SToomas Soome }
224ca987d46SWarner Losh bzero(gci_p, sizeof(*gci_p));
225ca987d46SWarner Losh gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY;
22675772fa2SToomas Soome pxe_call(PXENV_GET_CACHED_INFO, gci_p);
227ca987d46SWarner Losh if (gci_p->Status != 0) {
228ca987d46SWarner Losh pxe_perror(gci_p->Status);
22975772fa2SToomas Soome bio_free(gci_p, sizeof(*gci_p));
230ca987d46SWarner Losh pxe_p = NULL;
231ca987d46SWarner Losh return (0);
232ca987d46SWarner Losh }
233ca987d46SWarner Losh free(bootp_response);
234ca987d46SWarner Losh if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) {
235ca987d46SWarner Losh bootp_response_size = gci_p->BufferSize;
236ca987d46SWarner Losh bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset),
237ca987d46SWarner Losh bootp_response, bootp_response_size);
238ca987d46SWarner Losh }
23975772fa2SToomas Soome bio_free(gci_p, sizeof(*gci_p));
240ca987d46SWarner Losh return (1);
241ca987d46SWarner Losh }
242ca987d46SWarner Losh
243ca987d46SWarner Losh static int
pxe_print(int verbose)244ca987d46SWarner Losh pxe_print(int verbose)
245ca987d46SWarner Losh {
246ca987d46SWarner Losh if (pxe_call == NULL)
247ca987d46SWarner Losh return (0);
248ca987d46SWarner Losh
249ca987d46SWarner Losh printf("%s devices:", pxedisk.dv_name);
250ca987d46SWarner Losh if (pager_output("\n") != 0)
251ca987d46SWarner Losh return (1);
252ca987d46SWarner Losh printf(" %s0:", pxedisk.dv_name);
253ca987d46SWarner Losh if (verbose) {
254ca987d46SWarner Losh printf(" %s:%s", inet_ntoa(rootip), rootpath);
255ca987d46SWarner Losh }
256ca987d46SWarner Losh return (pager_output("\n"));
257ca987d46SWarner Losh }
258ca987d46SWarner Losh
259ca987d46SWarner Losh static void
pxe_cleanup(void)260ca987d46SWarner Losh pxe_cleanup(void)
261ca987d46SWarner Losh {
26275772fa2SToomas Soome t_PXENV_UNLOAD_STACK *unload_stack_p;
26375772fa2SToomas Soome t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p;
264ca987d46SWarner Losh
265ca987d46SWarner Losh if (pxe_call == NULL)
266ca987d46SWarner Losh return;
267ca987d46SWarner Losh
26875772fa2SToomas Soome undi_shutdown_p = bio_alloc(sizeof(*undi_shutdown_p));
26975772fa2SToomas Soome if (undi_shutdown_p != NULL) {
27075772fa2SToomas Soome bzero(undi_shutdown_p, sizeof(*undi_shutdown_p));
27175772fa2SToomas Soome pxe_call(PXENV_UNDI_SHUTDOWN, undi_shutdown_p);
272ca987d46SWarner Losh
273ca987d46SWarner Losh #ifdef PXE_DEBUG
274ca987d46SWarner Losh if (pxe_debug && undi_shutdown_p->Status != 0)
275ca987d46SWarner Losh printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n",
276ca987d46SWarner Losh undi_shutdown_p->Status);
277ca987d46SWarner Losh #endif
27875772fa2SToomas Soome bio_free(undi_shutdown_p, sizeof(*undi_shutdown_p));
27975772fa2SToomas Soome }
280ca987d46SWarner Losh
28175772fa2SToomas Soome unload_stack_p = bio_alloc(sizeof(*unload_stack_p));
28275772fa2SToomas Soome if (unload_stack_p != NULL) {
28375772fa2SToomas Soome bzero(unload_stack_p, sizeof(*unload_stack_p));
28475772fa2SToomas Soome pxe_call(PXENV_UNLOAD_STACK, unload_stack_p);
285ca987d46SWarner Losh
286ca987d46SWarner Losh #ifdef PXE_DEBUG
287ca987d46SWarner Losh if (pxe_debug && unload_stack_p->Status != 0)
288ca987d46SWarner Losh printf("pxe_cleanup: UNLOAD_STACK failed %x\n",
289ca987d46SWarner Losh unload_stack_p->Status);
290ca987d46SWarner Losh #endif
29175772fa2SToomas Soome bio_free(unload_stack_p, sizeof(*unload_stack_p));
29275772fa2SToomas Soome }
293ca987d46SWarner Losh }
294ca987d46SWarner Losh
295ca987d46SWarner Losh void
pxe_perror(int err)296ca987d46SWarner Losh pxe_perror(int err)
297ca987d46SWarner Losh {
298ca987d46SWarner Losh return;
299ca987d46SWarner Losh }
300ca987d46SWarner Losh
301ca987d46SWarner Losh void
pxenv_call(int func,void * ptr)30275772fa2SToomas Soome pxenv_call(int func, void *ptr)
303ca987d46SWarner Losh {
304ca987d46SWarner Losh #ifdef PXE_DEBUG
305ca987d46SWarner Losh if (pxe_debug)
306ca987d46SWarner Losh printf("pxenv_call %x\n", func);
307ca987d46SWarner Losh #endif
308ca987d46SWarner Losh
309ca987d46SWarner Losh bzero(&v86, sizeof(v86));
310ca987d46SWarner Losh
311ca987d46SWarner Losh __pxenvseg = pxenv_p->RMEntry.segment;
312ca987d46SWarner Losh __pxenvoff = pxenv_p->RMEntry.offset;
313ca987d46SWarner Losh
314ca987d46SWarner Losh v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
31575772fa2SToomas Soome v86.es = VTOPSEG(ptr);
31675772fa2SToomas Soome v86.edi = VTOPOFF(ptr);
317ca987d46SWarner Losh v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry);
318ca987d46SWarner Losh v86.ebx = func;
319ca987d46SWarner Losh v86int();
320ca987d46SWarner Losh v86.ctl = V86_FLAGS;
321ca987d46SWarner Losh }
322ca987d46SWarner Losh
323ca987d46SWarner Losh void
bangpxe_call(int func,void * ptr)32475772fa2SToomas Soome bangpxe_call(int func, void *ptr)
325ca987d46SWarner Losh {
326ca987d46SWarner Losh #ifdef PXE_DEBUG
327ca987d46SWarner Losh if (pxe_debug)
328ca987d46SWarner Losh printf("bangpxe_call %x\n", func);
329ca987d46SWarner Losh #endif
330ca987d46SWarner Losh
331ca987d46SWarner Losh bzero(&v86, sizeof(v86));
332ca987d46SWarner Losh
333ca987d46SWarner Losh __bangpxeseg = pxe_p->EntryPointSP.segment;
334ca987d46SWarner Losh __bangpxeoff = pxe_p->EntryPointSP.offset;
335ca987d46SWarner Losh
336ca987d46SWarner Losh v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
33775772fa2SToomas Soome v86.edx = VTOPSEG(ptr);
33875772fa2SToomas Soome v86.eax = VTOPOFF(ptr);
339ca987d46SWarner Losh v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry);
340ca987d46SWarner Losh v86.ebx = func;
341ca987d46SWarner Losh v86int();
342ca987d46SWarner Losh v86.ctl = V86_FLAGS;
343ca987d46SWarner Losh }
344ca987d46SWarner Losh
345ca987d46SWarner Losh
346ca987d46SWarner Losh static int
pxe_netif_match(struct netif * nif,void * machdep_hint)347ca987d46SWarner Losh pxe_netif_match(struct netif *nif, void *machdep_hint)
348ca987d46SWarner Losh {
349ca987d46SWarner Losh return (1);
350ca987d46SWarner Losh }
351ca987d46SWarner Losh
352ca987d46SWarner Losh static int
pxe_netif_probe(struct netif * nif,void * machdep_hint)353ca987d46SWarner Losh pxe_netif_probe(struct netif *nif, void *machdep_hint)
354ca987d46SWarner Losh {
355ca987d46SWarner Losh if (pxe_call == NULL)
356ca987d46SWarner Losh return (-1);
357ca987d46SWarner Losh
358ca987d46SWarner Losh return (0);
359ca987d46SWarner Losh }
360ca987d46SWarner Losh
361ca987d46SWarner Losh static void
pxe_netif_end(struct netif * nif)362ca987d46SWarner Losh pxe_netif_end(struct netif *nif)
363ca987d46SWarner Losh {
364ca987d46SWarner Losh t_PXENV_UNDI_CLOSE *undi_close_p;
365ca987d46SWarner Losh
36675772fa2SToomas Soome undi_close_p = bio_alloc(sizeof(*undi_close_p));
36775772fa2SToomas Soome if (undi_close_p != NULL) {
368ca987d46SWarner Losh bzero(undi_close_p, sizeof(*undi_close_p));
36975772fa2SToomas Soome pxe_call(PXENV_UNDI_CLOSE, undi_close_p);
370ca987d46SWarner Losh if (undi_close_p->Status != 0)
371ca987d46SWarner Losh printf("undi close failed: %x\n", undi_close_p->Status);
37275772fa2SToomas Soome bio_free(undi_close_p, sizeof(*undi_close_p));
37375772fa2SToomas Soome }
374ca987d46SWarner Losh }
375ca987d46SWarner Losh
376ca987d46SWarner Losh static void
pxe_netif_init(struct iodesc * desc,void * machdep_hint)377ca987d46SWarner Losh pxe_netif_init(struct iodesc *desc, void *machdep_hint)
378ca987d46SWarner Losh {
379ca987d46SWarner Losh t_PXENV_UNDI_GET_INFORMATION *undi_info_p;
380ca987d46SWarner Losh t_PXENV_UNDI_OPEN *undi_open_p;
381ca987d46SWarner Losh uint8_t *mac;
382ca987d46SWarner Losh int i, len;
383ca987d46SWarner Losh
38475772fa2SToomas Soome undi_info_p = bio_alloc(sizeof(*undi_info_p));
38575772fa2SToomas Soome if (undi_info_p == NULL)
38675772fa2SToomas Soome return;
38775772fa2SToomas Soome
388ca987d46SWarner Losh bzero(undi_info_p, sizeof(*undi_info_p));
38975772fa2SToomas Soome pxe_call(PXENV_UNDI_GET_INFORMATION, undi_info_p);
390ca987d46SWarner Losh if (undi_info_p->Status != 0) {
391ca987d46SWarner Losh printf("undi get info failed: %x\n", undi_info_p->Status);
39275772fa2SToomas Soome bio_free(undi_info_p, sizeof(*undi_info_p));
393ca987d46SWarner Losh return;
394ca987d46SWarner Losh }
395ca987d46SWarner Losh
396ca987d46SWarner Losh /* Make sure the CurrentNodeAddress is valid. */
397ca987d46SWarner Losh for (i = 0; i < undi_info_p->HwAddrLen; ++i) {
398ca987d46SWarner Losh if (undi_info_p->CurrentNodeAddress[i] != 0)
399ca987d46SWarner Losh break;
400ca987d46SWarner Losh }
401ca987d46SWarner Losh if (i < undi_info_p->HwAddrLen) {
402ca987d46SWarner Losh for (i = 0; i < undi_info_p->HwAddrLen; ++i) {
403ca987d46SWarner Losh if (undi_info_p->CurrentNodeAddress[i] != 0xff)
404ca987d46SWarner Losh break;
405ca987d46SWarner Losh }
406ca987d46SWarner Losh }
407ca987d46SWarner Losh if (i < undi_info_p->HwAddrLen)
408ca987d46SWarner Losh mac = undi_info_p->CurrentNodeAddress;
409ca987d46SWarner Losh else
410ca987d46SWarner Losh mac = undi_info_p->PermNodeAddress;
411ca987d46SWarner Losh
412ca987d46SWarner Losh len = min(sizeof (desc->myea), undi_info_p->HwAddrLen);
413ca987d46SWarner Losh for (i = 0; i < len; ++i)
414ca987d46SWarner Losh desc->myea[i] = mac[i];
415ca987d46SWarner Losh
416ca987d46SWarner Losh if (bootp_response != NULL)
417ca987d46SWarner Losh desc->xid = bootp_response->bp_xid;
418ca987d46SWarner Losh else
419ca987d46SWarner Losh desc->xid = 0;
420ca987d46SWarner Losh
42175772fa2SToomas Soome bio_free(undi_info_p, sizeof(*undi_info_p));
42275772fa2SToomas Soome undi_open_p = bio_alloc(sizeof(*undi_open_p));
42375772fa2SToomas Soome if (undi_open_p == NULL)
42475772fa2SToomas Soome return;
425ca987d46SWarner Losh bzero(undi_open_p, sizeof(*undi_open_p));
426ca987d46SWarner Losh undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
42775772fa2SToomas Soome pxe_call(PXENV_UNDI_OPEN, undi_open_p);
428ca987d46SWarner Losh if (undi_open_p->Status != 0)
429ca987d46SWarner Losh printf("undi open failed: %x\n", undi_open_p->Status);
43075772fa2SToomas Soome bio_free(undi_open_p, sizeof(*undi_open_p));
431ca987d46SWarner Losh }
432ca987d46SWarner Losh
433ca987d46SWarner Losh static int
pxe_netif_receive_isr(t_PXENV_UNDI_ISR * isr,void ** pkt,ssize_t * retsize)4343daa8e16SKyle Evans pxe_netif_receive_isr(t_PXENV_UNDI_ISR *isr, void **pkt, ssize_t *retsize)
435ca987d46SWarner Losh {
4363daa8e16SKyle Evans static bool data_pending;
437ca987d46SWarner Losh char *buf, *ptr, *frame;
438ca987d46SWarner Losh size_t size, rsize;
439ca987d46SWarner Losh
4403daa8e16SKyle Evans buf = NULL;
4413daa8e16SKyle Evans size = rsize = 0;
442ca987d46SWarner Losh
4433daa8e16SKyle Evans /*
4443daa8e16SKyle Evans * We can save ourselves the next two pxe calls because we already know
4453daa8e16SKyle Evans * we weren't done grabbing everything.
4463daa8e16SKyle Evans */
4473daa8e16SKyle Evans if (data_pending) {
4483daa8e16SKyle Evans data_pending = false;
4493daa8e16SKyle Evans goto nextbuf;
4503daa8e16SKyle Evans }
4513daa8e16SKyle Evans
4523daa8e16SKyle Evans /*
4533daa8e16SKyle Evans * We explicitly don't check for OURS/NOT_OURS as a result of START;
4543daa8e16SKyle Evans * it's been reported that some cards are known to mishandle these.
4553daa8e16SKyle Evans */
456ca987d46SWarner Losh bzero(isr, sizeof(*isr));
45775772fa2SToomas Soome isr->FuncFlag = PXENV_UNDI_ISR_IN_START;
45875772fa2SToomas Soome pxe_call(PXENV_UNDI_ISR, isr);
4593daa8e16SKyle Evans /* We could translate Status... */
46075772fa2SToomas Soome if (isr->Status != 0) {
4613daa8e16SKyle Evans return (ENXIO);
46275772fa2SToomas Soome }
46375772fa2SToomas Soome
46475772fa2SToomas Soome bzero(isr, sizeof(*isr));
46575772fa2SToomas Soome isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
46675772fa2SToomas Soome pxe_call(PXENV_UNDI_ISR, isr);
46775772fa2SToomas Soome if (isr->Status != 0) {
4683daa8e16SKyle Evans return (ENXIO);
46975772fa2SToomas Soome }
4703daa8e16SKyle Evans if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_BUSY) {
471ca987d46SWarner Losh /*
4723daa8e16SKyle Evans * Let the caller decide if we need to be restarted. It will
4733daa8e16SKyle Evans * currently blindly restart us, but it could check timeout in
4743daa8e16SKyle Evans * the future.
475ca987d46SWarner Losh */
4763daa8e16SKyle Evans return (ERESTART);
47775772fa2SToomas Soome }
478ca987d46SWarner Losh
4793daa8e16SKyle Evans /*
4803daa8e16SKyle Evans * By design, we'll hardly ever hit this terminal condition unless we
4813daa8e16SKyle Evans * pick up nothing but tx interrupts here. More frequently, we will
4823daa8e16SKyle Evans * process rx buffers until we hit the terminal condition in the middle.
4833daa8e16SKyle Evans */
4843daa8e16SKyle Evans while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_DONE) {
4853daa8e16SKyle Evans /*
4863daa8e16SKyle Evans * This might have given us PXENV_UNDI_ISR_OUT_TRANSMIT, in
4873daa8e16SKyle Evans * which case we can just disregard and move on to the next
4883daa8e16SKyle Evans * buffer/frame.
4893daa8e16SKyle Evans */
4903daa8e16SKyle Evans if (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE)
4913daa8e16SKyle Evans goto nextbuf;
492ca987d46SWarner Losh
4933daa8e16SKyle Evans if (buf == NULL) {
4943daa8e16SKyle Evans /*
4953daa8e16SKyle Evans * Grab size from the first Frame that we picked up,
4963daa8e16SKyle Evans * allocate an rx buf to hold. Careful here, as we may
4973daa8e16SKyle Evans * see a fragmented frame that's spread out across
4983daa8e16SKyle Evans * multiple GET_NEXT calls.
4993daa8e16SKyle Evans */
500ca987d46SWarner Losh size = isr->FrameLength;
501aaeffe5bSToomas Soome buf = malloc(size + ETHER_ALIGN);
5023daa8e16SKyle Evans if (buf == NULL)
5033daa8e16SKyle Evans return (ENOMEM);
504ca987d46SWarner Losh
5053daa8e16SKyle Evans ptr = buf + ETHER_ALIGN;
5063daa8e16SKyle Evans }
5073daa8e16SKyle Evans
508ca987d46SWarner Losh frame = (char *)((uintptr_t)isr->Frame.segment << 4);
509ca987d46SWarner Losh frame += isr->Frame.offset;
510ca987d46SWarner Losh bcopy(PTOV(frame), ptr, isr->BufferLength);
511ca987d46SWarner Losh ptr += isr->BufferLength;
512ca987d46SWarner Losh rsize += isr->BufferLength;
513ca987d46SWarner Losh
5143daa8e16SKyle Evans /*
5153daa8e16SKyle Evans * Stop here before we risk catching the start of another frame.
5163daa8e16SKyle Evans * It would be nice to continue reading until we actually get a
5173daa8e16SKyle Evans * PXENV_UNDI_ISR_OUT_DONE, but our network stack in libsa isn't
5183daa8e16SKyle Evans * suitable for reading more than one packet at a time.
5193daa8e16SKyle Evans */
5203daa8e16SKyle Evans if (rsize >= size) {
5213daa8e16SKyle Evans data_pending = true;
5223daa8e16SKyle Evans break;
5233daa8e16SKyle Evans }
5243daa8e16SKyle Evans
5253daa8e16SKyle Evans nextbuf:
526ca987d46SWarner Losh bzero(isr, sizeof(*isr));
527ca987d46SWarner Losh isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
52875772fa2SToomas Soome pxe_call(PXENV_UNDI_ISR, isr);
529ca987d46SWarner Losh if (isr->Status != 0) {
530ca987d46SWarner Losh free(buf);
5313daa8e16SKyle Evans return (ENXIO);
5323daa8e16SKyle Evans }
533ca987d46SWarner Losh }
534ca987d46SWarner Losh
5353daa8e16SKyle Evans /*
5363daa8e16SKyle Evans * We may have never picked up a frame at all (all tx), in which case
5373daa8e16SKyle Evans * the caller should restart us.
5383daa8e16SKyle Evans */
5393daa8e16SKyle Evans if (rsize == 0) {
5403daa8e16SKyle Evans return (ERESTART);
541ca987d46SWarner Losh }
542ca987d46SWarner Losh
543ca987d46SWarner Losh *pkt = buf;
5443daa8e16SKyle Evans *retsize = rsize;
5453daa8e16SKyle Evans return (0);
5463daa8e16SKyle Evans }
5473daa8e16SKyle Evans
5483daa8e16SKyle Evans static int
pxe_netif_receive(void ** pkt,ssize_t * size)5493daa8e16SKyle Evans pxe_netif_receive(void **pkt, ssize_t *size)
5503daa8e16SKyle Evans {
5513daa8e16SKyle Evans t_PXENV_UNDI_ISR *isr;
5523daa8e16SKyle Evans int ret;
5533daa8e16SKyle Evans
5543daa8e16SKyle Evans isr = bio_alloc(sizeof(*isr));
5553daa8e16SKyle Evans if (isr == NULL)
5563daa8e16SKyle Evans return (ENOMEM);
5573daa8e16SKyle Evans
5583daa8e16SKyle Evans /*
5593daa8e16SKyle Evans * This completely ignores the timeout specified in pxe_netif_get(), but
5603daa8e16SKyle Evans * we shouldn't be running long enough here for that to make a
5613daa8e16SKyle Evans * difference.
5623daa8e16SKyle Evans */
5633daa8e16SKyle Evans for (;;) {
5643daa8e16SKyle Evans /* We'll only really re-enter for PXENV_UNDI_ISR_OUT_BUSY. */
5653daa8e16SKyle Evans ret = pxe_netif_receive_isr(isr, pkt, size);
5663daa8e16SKyle Evans if (ret != ERESTART)
5673daa8e16SKyle Evans break;
5683daa8e16SKyle Evans }
5693daa8e16SKyle Evans
57075772fa2SToomas Soome bio_free(isr, sizeof(*isr));
5713daa8e16SKyle Evans return (ret);
572ca987d46SWarner Losh }
573ca987d46SWarner Losh
574ca987d46SWarner Losh static ssize_t
pxe_netif_get(struct iodesc * desc,void ** pkt,time_t timeout)575ca987d46SWarner Losh pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout)
576ca987d46SWarner Losh {
577ca987d46SWarner Losh time_t t;
578ca987d46SWarner Losh void *ptr;
579ca987d46SWarner Losh int ret = -1;
5803daa8e16SKyle Evans ssize_t size;
581ca987d46SWarner Losh
582ca987d46SWarner Losh t = getsecs();
5833daa8e16SKyle Evans size = 0;
584ca987d46SWarner Losh while ((getsecs() - t) < timeout) {
5853daa8e16SKyle Evans ret = pxe_netif_receive(&ptr, &size);
586ca987d46SWarner Losh if (ret != -1) {
587ca987d46SWarner Losh *pkt = ptr;
588ca987d46SWarner Losh break;
589ca987d46SWarner Losh }
590ca987d46SWarner Losh }
5913daa8e16SKyle Evans
5923daa8e16SKyle Evans return (ret == 0 ? size : -1);
593ca987d46SWarner Losh }
594ca987d46SWarner Losh
595ca987d46SWarner Losh static ssize_t
pxe_netif_put(struct iodesc * desc,void * pkt,size_t len)596ca987d46SWarner Losh pxe_netif_put(struct iodesc *desc, void *pkt, size_t len)
597ca987d46SWarner Losh {
598ca987d46SWarner Losh t_PXENV_UNDI_TRANSMIT *trans_p;
599ca987d46SWarner Losh t_PXENV_UNDI_TBD *tbd_p;
600ca987d46SWarner Losh char *data;
60175772fa2SToomas Soome ssize_t rv = -1;
602ca987d46SWarner Losh
60375772fa2SToomas Soome trans_p = bio_alloc(sizeof(*trans_p));
60475772fa2SToomas Soome tbd_p = bio_alloc(sizeof(*tbd_p));
60575772fa2SToomas Soome data = bio_alloc(len);
60675772fa2SToomas Soome
60775772fa2SToomas Soome if (trans_p != NULL && tbd_p != NULL && data != NULL) {
608ca987d46SWarner Losh bzero(trans_p, sizeof(*trans_p));
609ca987d46SWarner Losh bzero(tbd_p, sizeof(*tbd_p));
610ca987d46SWarner Losh
611ca987d46SWarner Losh trans_p->TBD.segment = VTOPSEG(tbd_p);
612ca987d46SWarner Losh trans_p->TBD.offset = VTOPOFF(tbd_p);
613ca987d46SWarner Losh
614ca987d46SWarner Losh tbd_p->ImmedLength = len;
615ca987d46SWarner Losh tbd_p->Xmit.segment = VTOPSEG(data);
616ca987d46SWarner Losh tbd_p->Xmit.offset = VTOPOFF(data);
617ca987d46SWarner Losh bcopy(pkt, data, len);
618ca987d46SWarner Losh
61975772fa2SToomas Soome pxe_call(PXENV_UNDI_TRANSMIT, trans_p);
62075772fa2SToomas Soome if (trans_p->Status == 0)
62175772fa2SToomas Soome rv = len;
622ca987d46SWarner Losh }
623ca987d46SWarner Losh
62475772fa2SToomas Soome bio_free(data, len);
62575772fa2SToomas Soome bio_free(tbd_p, sizeof(*tbd_p));
62675772fa2SToomas Soome bio_free(trans_p, sizeof(*trans_p));
62775772fa2SToomas Soome return (rv);
628ca987d46SWarner Losh }
629