pxe.c revision 346476
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: stable/11/stand/i386/libi386/pxe.c 346476 2019-04-21 03:36:05Z kevans $");
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
55static pxenv_t *pxenv_p = NULL;	/* PXENV+ */
56static pxe_t *pxe_p = NULL;		/* !PXE */
57
58#ifdef PXE_DEBUG
59static int	pxe_debug = 0;
60#endif
61
62void		pxe_enable(void *pxeinfo);
63static void	(*pxe_call)(int func, void *ptr);
64static void	pxenv_call(int func, void *ptr);
65static void	bangpxe_call(int func, void *ptr);
66
67static int	pxe_init(void);
68static int	pxe_print(int verbose);
69static void	pxe_cleanup(void);
70
71static void	pxe_perror(int error);
72static int	pxe_netif_match(struct netif *nif, void *machdep_hint);
73static int	pxe_netif_probe(struct netif *nif, void *machdep_hint);
74static void	pxe_netif_init(struct iodesc *desc, void *machdep_hint);
75static ssize_t	pxe_netif_get(struct iodesc *, void **, time_t);
76static ssize_t	pxe_netif_put(struct iodesc *desc, void *pkt, size_t len);
77static void	pxe_netif_end(struct netif *nif);
78
79extern struct netif_stats	pxe_st[];
80extern uint16_t			__bangpxeseg;
81extern uint16_t			__bangpxeoff;
82extern void			__bangpxeentry(void);
83extern uint16_t			__pxenvseg;
84extern uint16_t			__pxenvoff;
85extern void			__pxenventry(void);
86
87struct netif_dif pxe_ifs[] = {
88/*	dif_unit        dif_nsel        dif_stats       dif_private     */
89	{0,             1,              &pxe_st[0],     0}
90};
91
92struct netif_stats pxe_st[nitems(pxe_ifs)];
93
94struct 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
106struct netif_driver *netif_drivers[] = {
107	&pxenetif,
108	NULL
109};
110
111struct 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 */
128void
129pxe_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 */
141static int
142pxe_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
244static int
245pxe_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
260static void
261pxe_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
296void
297pxe_perror(int err)
298{
299	return;
300}
301
302void
303pxenv_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
324void
325bangpxe_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
347static int
348pxe_netif_match(struct netif *nif, void *machdep_hint)
349{
350	return (1);
351}
352
353static int
354pxe_netif_probe(struct netif *nif, void *machdep_hint)
355{
356	if (pxe_call == NULL)
357		return (-1);
358
359	return (0);
360}
361
362static void
363pxe_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
377static void
378pxe_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
434static int
435pxe_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
522static ssize_t
523pxe_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
540static ssize_t
541pxe_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}
574