]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/i386/libi386/pxe.c
MFC: r342619, r342626
[FreeBSD/FreeBSD.git] / stand / i386 / libi386 / pxe.c
1 /*-
2  * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
3  * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
4  * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <stand.h>
33 #include <stddef.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <sys/param.h>
37
38 #include <net/ethernet.h>
39 #include <netinet/in_systm.h>
40 #include <netinet/in.h>
41 #include <netinet/ip.h>
42 #include <netinet/udp.h>
43
44 #include <net.h>
45 #include <netif.h>
46 #include <nfsv2.h>
47 #include <iodesc.h>
48
49 #include <bootp.h>
50 #include <bootstrap.h>
51 #include "libi386.h"
52 #include "btxv86.h"
53 #include "pxe.h"
54
55 static pxenv_t *pxenv_p = NULL; /* PXENV+ */
56 static pxe_t *pxe_p = NULL;             /* !PXE */
57
58 #ifdef PXE_DEBUG
59 static int      pxe_debug = 0;
60 #endif
61
62 void            pxe_enable(void *pxeinfo);
63 static void     (*pxe_call)(int func, void *ptr);
64 static void     pxenv_call(int func, void *ptr);
65 static void     bangpxe_call(int func, void *ptr);
66
67 static int      pxe_init(void);
68 static int      pxe_print(int verbose);
69 static void     pxe_cleanup(void);
70
71 static void     pxe_perror(int error);
72 static int      pxe_netif_match(struct netif *nif, void *machdep_hint);
73 static int      pxe_netif_probe(struct netif *nif, void *machdep_hint);
74 static void     pxe_netif_init(struct iodesc *desc, void *machdep_hint);
75 static ssize_t  pxe_netif_get(struct iodesc *, void **, time_t);
76 static ssize_t  pxe_netif_put(struct iodesc *desc, void *pkt, size_t len);
77 static void     pxe_netif_end(struct netif *nif);
78
79 extern struct netif_stats       pxe_st[];
80 extern uint16_t                 __bangpxeseg;
81 extern uint16_t                 __bangpxeoff;
82 extern void                     __bangpxeentry(void);
83 extern uint16_t                 __pxenvseg;
84 extern uint16_t                 __pxenvoff;
85 extern void                     __pxenventry(void);
86
87 struct netif_dif pxe_ifs[] = {
88 /*      dif_unit        dif_nsel        dif_stats       dif_private     */
89         {0,             1,              &pxe_st[0],     0}
90 };
91
92 struct netif_stats pxe_st[nitems(pxe_ifs)];
93
94 struct netif_driver pxenetif = {
95         .netif_bname = "pxenet",
96         .netif_match = pxe_netif_match,
97         .netif_probe = pxe_netif_probe,
98         .netif_init = pxe_netif_init,
99         .netif_get = pxe_netif_get,
100         .netif_put = pxe_netif_put,
101         .netif_end = pxe_netif_end,
102         .netif_ifs = pxe_ifs,
103         .netif_nifs = nitems(pxe_ifs)
104 };
105
106 struct netif_driver *netif_drivers[] = {
107         &pxenetif,
108         NULL
109 };
110
111 struct devsw pxedisk = {
112         .dv_name = "net",
113         .dv_type = DEVT_NET,
114         .dv_init = pxe_init,
115         .dv_strategy = NULL,    /* Will be set in pxe_init */
116         .dv_open = NULL,        /* Will be set in pxe_init */
117         .dv_close = NULL,       /* Will be set in pxe_init */
118         .dv_ioctl = noioctl,
119         .dv_print = pxe_print,
120         .dv_cleanup = pxe_cleanup
121 };
122
123 /*
124  * This function is called by the loader to enable PXE support if we
125  * are booted by PXE. The passed in pointer is a pointer to the PXENV+
126  * structure.
127  */
128 void
129 pxe_enable(void *pxeinfo)
130 {
131         pxenv_p  = (pxenv_t *)pxeinfo;
132         pxe_p    = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 +
133                                  pxenv_p->PXEPtr.offset);
134         pxe_call = NULL;
135 }
136
137 /*
138  * return true if pxe structures are found/initialized,
139  * also figures out our IP information via the pxe cached info struct
140  */
141 static int
142 pxe_init(void)
143 {
144         t_PXENV_GET_CACHED_INFO *gci_p;
145         int counter;
146         uint8_t checksum;
147         uint8_t *checkptr;
148         extern struct devsw netdev;
149
150         if (pxenv_p == NULL)
151                 return (0);
152
153         /* look for "PXENV+" */
154         if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) {
155                 pxenv_p = NULL;
156                 return (0);
157         }
158
159         /* make sure the size is something we can handle */
160         if (pxenv_p->Length > sizeof(*pxenv_p)) {
161                 printf("PXENV+ structure too large, ignoring\n");
162                 pxenv_p = NULL;
163                 return (0);
164         }
165
166         /*
167          * do byte checksum:
168          * add up each byte in the structure, the total should be 0
169          */
170         checksum = 0;
171         checkptr = (uint8_t *) pxenv_p;
172         for (counter = 0; counter < pxenv_p->Length; counter++)
173                 checksum += *checkptr++;
174         if (checksum != 0) {
175                 printf("PXENV+ structure failed checksum, ignoring\n");
176                 pxenv_p = NULL;
177                 return (0);
178         }
179
180         /*
181          * PXENV+ passed, so use that if !PXE is not available or
182          * the checksum fails.
183          */
184         pxe_call = pxenv_call;
185         if (pxenv_p->Version >= 0x0200) {
186                 for (;;) {
187                         if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) {
188                                 pxe_p = NULL;
189                                 break;
190                         }
191                         checksum = 0;
192                         checkptr = (uint8_t *)pxe_p;
193                         for (counter = 0; counter < pxe_p->StructLength;
194                             counter++)
195                                 checksum += *checkptr++;
196                         if (checksum != 0) {
197                                 pxe_p = NULL;
198                                 break;
199                         }
200                         pxe_call = bangpxe_call;
201                         break;
202                 }
203         }
204
205         pxedisk.dv_open = netdev.dv_open;
206         pxedisk.dv_close = netdev.dv_close;
207         pxedisk.dv_strategy = netdev.dv_strategy;
208
209         printf("\nPXE version %d.%d, real mode entry point ",
210             (uint8_t) (pxenv_p->Version >> 8),
211             (uint8_t) (pxenv_p->Version & 0xFF));
212         if (pxe_call == bangpxe_call)
213                 printf("@%04x:%04x\n",
214                     pxe_p->EntryPointSP.segment,
215                     pxe_p->EntryPointSP.offset);
216         else
217                 printf("@%04x:%04x\n",
218                     pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset);
219
220         gci_p = bio_alloc(sizeof(*gci_p));
221         if (gci_p == NULL) {
222                 pxe_p = NULL;
223                 return (0);
224         }
225         bzero(gci_p, sizeof(*gci_p));
226         gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY;
227         pxe_call(PXENV_GET_CACHED_INFO, gci_p);
228         if (gci_p->Status != 0) {
229                 pxe_perror(gci_p->Status);
230                 bio_free(gci_p, sizeof(*gci_p));
231                 pxe_p = NULL;
232                 return (0);
233         }
234         free(bootp_response);
235         if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) {
236                 bootp_response_size = gci_p->BufferSize;
237                 bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset),
238                     bootp_response, bootp_response_size);
239         }
240         bio_free(gci_p, sizeof(*gci_p));
241         return (1);
242 }
243
244 static int
245 pxe_print(int verbose)
246 {
247         if (pxe_call == NULL)
248                 return (0);
249
250         printf("%s devices:", pxedisk.dv_name);
251         if (pager_output("\n") != 0)
252                 return (1);
253         printf("    %s0:", pxedisk.dv_name);
254         if (verbose) {
255                 printf("    %s:%s", inet_ntoa(rootip), rootpath);
256         }
257         return (pager_output("\n"));
258 }
259
260 static void
261 pxe_cleanup(void)
262 {
263         t_PXENV_UNLOAD_STACK *unload_stack_p;
264         t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p;
265
266         if (pxe_call == NULL)
267                 return;
268
269         undi_shutdown_p = bio_alloc(sizeof(*undi_shutdown_p));
270         if (undi_shutdown_p != NULL) {
271                 bzero(undi_shutdown_p, sizeof(*undi_shutdown_p));
272                 pxe_call(PXENV_UNDI_SHUTDOWN, undi_shutdown_p);
273
274 #ifdef PXE_DEBUG
275                 if (pxe_debug && undi_shutdown_p->Status != 0)
276                         printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n",
277                             undi_shutdown_p->Status);
278 #endif
279                 bio_free(undi_shutdown_p, sizeof(*undi_shutdown_p));
280         }
281
282         unload_stack_p = bio_alloc(sizeof(*unload_stack_p));
283         if (unload_stack_p != NULL) {
284                 bzero(unload_stack_p, sizeof(*unload_stack_p));
285                 pxe_call(PXENV_UNLOAD_STACK, unload_stack_p);
286
287 #ifdef PXE_DEBUG
288                 if (pxe_debug && unload_stack_p->Status != 0)
289                         printf("pxe_cleanup: UNLOAD_STACK failed %x\n",
290                             unload_stack_p->Status);
291 #endif
292                 bio_free(unload_stack_p, sizeof(*unload_stack_p));
293         }
294 }
295
296 void
297 pxe_perror(int err)
298 {
299         return;
300 }
301
302 void
303 pxenv_call(int func, void *ptr)
304 {
305 #ifdef PXE_DEBUG
306         if (pxe_debug)
307                 printf("pxenv_call %x\n", func);
308 #endif
309         
310         bzero(&v86, sizeof(v86));
311
312         __pxenvseg = pxenv_p->RMEntry.segment;
313         __pxenvoff = pxenv_p->RMEntry.offset;
314         
315         v86.ctl  = V86_ADDR | V86_CALLF | V86_FLAGS;
316         v86.es   = VTOPSEG(ptr);
317         v86.edi  = VTOPOFF(ptr);
318         v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry);
319         v86.ebx  = func;
320         v86int();
321         v86.ctl  = V86_FLAGS;
322 }
323
324 void
325 bangpxe_call(int func, void *ptr)
326 {
327 #ifdef PXE_DEBUG
328         if (pxe_debug)
329                 printf("bangpxe_call %x\n", func);
330 #endif
331
332         bzero(&v86, sizeof(v86));
333
334         __bangpxeseg = pxe_p->EntryPointSP.segment;
335         __bangpxeoff = pxe_p->EntryPointSP.offset;
336
337         v86.ctl  = V86_ADDR | V86_CALLF | V86_FLAGS;
338         v86.edx  = VTOPSEG(ptr);
339         v86.eax  = VTOPOFF(ptr);
340         v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry);
341         v86.ebx  = func;
342         v86int();
343         v86.ctl  = V86_FLAGS;
344 }
345
346
347 static int
348 pxe_netif_match(struct netif *nif, void *machdep_hint)
349 {
350         return (1);
351 }
352
353 static int
354 pxe_netif_probe(struct netif *nif, void *machdep_hint)
355 {
356         if (pxe_call == NULL)
357                 return (-1);
358
359         return (0);
360 }
361
362 static void
363 pxe_netif_end(struct netif *nif)
364 {
365         t_PXENV_UNDI_CLOSE *undi_close_p;
366
367         undi_close_p = bio_alloc(sizeof(*undi_close_p));
368         if (undi_close_p != NULL) {
369                 bzero(undi_close_p, sizeof(*undi_close_p));
370                 pxe_call(PXENV_UNDI_CLOSE, undi_close_p);
371                 if (undi_close_p->Status != 0)
372                         printf("undi close failed: %x\n", undi_close_p->Status);
373                 bio_free(undi_close_p, sizeof(*undi_close_p));
374         }
375 }
376
377 static void
378 pxe_netif_init(struct iodesc *desc, void *machdep_hint)
379 {
380         t_PXENV_UNDI_GET_INFORMATION *undi_info_p;
381         t_PXENV_UNDI_OPEN *undi_open_p;
382         uint8_t *mac;
383         int i, len;
384
385         undi_info_p = bio_alloc(sizeof(*undi_info_p));
386         if (undi_info_p == NULL)
387                 return;
388
389         bzero(undi_info_p, sizeof(*undi_info_p));
390         pxe_call(PXENV_UNDI_GET_INFORMATION, undi_info_p);
391         if (undi_info_p->Status != 0) {
392                 printf("undi get info failed: %x\n", undi_info_p->Status);
393                 bio_free(undi_info_p, sizeof(*undi_info_p));
394                 return;
395         }
396
397         /* Make sure the CurrentNodeAddress is valid. */
398         for (i = 0; i < undi_info_p->HwAddrLen; ++i) {
399                 if (undi_info_p->CurrentNodeAddress[i] != 0)
400                         break;
401         }
402         if (i < undi_info_p->HwAddrLen) {
403                 for (i = 0; i < undi_info_p->HwAddrLen; ++i) {
404                         if (undi_info_p->CurrentNodeAddress[i] != 0xff)
405                                 break;
406                 }
407         }
408         if (i < undi_info_p->HwAddrLen)
409                 mac = undi_info_p->CurrentNodeAddress;
410         else
411                 mac = undi_info_p->PermNodeAddress;
412
413         len = min(sizeof (desc->myea), undi_info_p->HwAddrLen);
414         for (i = 0; i < len; ++i)
415                 desc->myea[i] = mac[i];
416
417         if (bootp_response != NULL)
418                 desc->xid = bootp_response->bp_xid;
419         else
420                 desc->xid = 0;
421
422         bio_free(undi_info_p, sizeof(*undi_info_p));
423         undi_open_p = bio_alloc(sizeof(*undi_open_p));
424         if (undi_open_p == NULL)
425                 return;
426         bzero(undi_open_p, sizeof(*undi_open_p));
427         undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
428         pxe_call(PXENV_UNDI_OPEN, undi_open_p);
429         if (undi_open_p->Status != 0)
430                 printf("undi open failed: %x\n", undi_open_p->Status);
431         bio_free(undi_open_p, sizeof(*undi_open_p));
432 }
433
434 static int
435 pxe_netif_receive(void **pkt)
436 {
437         t_PXENV_UNDI_ISR *isr;
438         char *buf, *ptr, *frame;
439         size_t size, rsize;
440
441         isr = bio_alloc(sizeof(*isr));
442         if (isr == NULL)
443                 return (-1);
444
445         bzero(isr, sizeof(*isr));
446         isr->FuncFlag = PXENV_UNDI_ISR_IN_START;
447         pxe_call(PXENV_UNDI_ISR, isr);
448         if (isr->Status != 0) {
449                 bio_free(isr, sizeof(*isr));
450                 return (-1);
451         }
452
453         bzero(isr, sizeof(*isr));
454         isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
455         pxe_call(PXENV_UNDI_ISR, isr);
456         if (isr->Status != 0) {
457                 bio_free(isr, sizeof(*isr));
458                 return (-1);
459         }
460
461         while (isr->FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) {
462                 /*
463                  * Wait till transmit is done.
464                  */
465                 bzero(isr, sizeof(*isr));
466                 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
467                 pxe_call(PXENV_UNDI_ISR, isr);
468                 if (isr->Status != 0 ||
469                     isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) {
470                         bio_free(isr, sizeof(*isr));
471                         return (-1);
472                 }
473         }
474
475         while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) {
476                 if (isr->Status != 0 ||
477                     isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) {
478                         bio_free(isr, sizeof(*isr));
479                         return (-1);
480                 }
481                 bzero(isr, sizeof(*isr));
482                 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
483                 pxe_call(PXENV_UNDI_ISR, isr);
484         }
485
486         size = isr->FrameLength;
487         buf = malloc(size + ETHER_ALIGN);
488         if (buf == NULL) {
489                 bio_free(isr, sizeof(*isr));
490                 return (-1);
491         }
492         ptr = buf + ETHER_ALIGN;
493         rsize = 0;
494
495         while (rsize < size) {
496                 frame = (char *)((uintptr_t)isr->Frame.segment << 4);
497                 frame += isr->Frame.offset;
498                 bcopy(PTOV(frame), ptr, isr->BufferLength);
499                 ptr += isr->BufferLength;
500                 rsize += isr->BufferLength;
501
502                 bzero(isr, sizeof(*isr));
503                 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
504                 pxe_call(PXENV_UNDI_ISR, isr);
505                 if (isr->Status != 0) {
506                         bio_free(isr, sizeof(*isr));
507                         free(buf);
508                         return (-1);
509                 }
510
511                 /* Did we got another update? */
512                 if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE)
513                         continue;
514                 break;
515         }
516
517         *pkt = buf;
518         bio_free(isr, sizeof(*isr));
519         return (rsize);
520 }
521
522 static ssize_t
523 pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout)
524 {
525         time_t t;
526         void *ptr;
527         int ret = -1;
528
529         t = getsecs();
530         while ((getsecs() - t) < timeout) {
531                 ret = pxe_netif_receive(&ptr);
532                 if (ret != -1) {
533                         *pkt = ptr;
534                         break;
535                 }
536         }
537         return (ret);
538 }
539
540 static ssize_t
541 pxe_netif_put(struct iodesc *desc, void *pkt, size_t len)
542 {
543         t_PXENV_UNDI_TRANSMIT *trans_p;
544         t_PXENV_UNDI_TBD *tbd_p;
545         char *data;
546         ssize_t rv = -1;
547
548         trans_p = bio_alloc(sizeof(*trans_p));
549         tbd_p = bio_alloc(sizeof(*tbd_p));
550         data = bio_alloc(len);
551
552         if (trans_p != NULL && tbd_p != NULL && data != NULL) {
553                 bzero(trans_p, sizeof(*trans_p));
554                 bzero(tbd_p, sizeof(*tbd_p));
555
556                 trans_p->TBD.segment = VTOPSEG(tbd_p);
557                 trans_p->TBD.offset  = VTOPOFF(tbd_p);
558
559                 tbd_p->ImmedLength = len;
560                 tbd_p->Xmit.segment = VTOPSEG(data);
561                 tbd_p->Xmit.offset  = VTOPOFF(data);
562                 bcopy(pkt, data, len);
563
564                 pxe_call(PXENV_UNDI_TRANSMIT, trans_p);
565                 if (trans_p->Status == 0)
566                         rv = len;
567         }
568
569         bio_free(data, len);
570         bio_free(tbd_p, sizeof(*tbd_p));
571         bio_free(trans_p, sizeof(*trans_p));
572         return (rv);
573 }