xref: /haiku/src/system/boot/platform/pxe_ia32/network.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
1 /*
2  * Copyright 2006, Marcus Overhagen <marcus@overhagen.de. All rights reserved.
3  * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 #include <new>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <KernelExport.h>
13 
14 #include <boot/platform.h>
15 
16 #include "network.h"
17 #include "pxe_undi.h"
18 
19 //#define TRACE_NETWORK
20 #ifdef TRACE_NETWORK
21 #	define TRACE(x...) dprintf(x)
22 #else
23 #	define TRACE(x...)
24 #endif
25 
26 #ifdef TRACE_NETWORK
27 
28 static void
29 hex_dump(const void *_data, int length)
30 {
31 	uint8 *data = (uint8*)_data;
32 	for (int i = 0; i < length; i++) {
33 		if (i % 4 == 0) {
34 			if (i % 32 == 0) {
35 				if (i != 0)
36 					TRACE("\n");
37 				TRACE("%03x: ", i);
38 			} else
39 				TRACE(" ");
40 		}
41 
42 		TRACE("%02x", data[i]);
43 	}
44 	TRACE("\n");
45 }
46 
47 #else	// !TRACE_NETWORK
48 
49 #define hex_dump(data, length)
50 
51 #endif	// !TRACE_NETWORK
52 
53 
54 // #pragma mark - PXEService
55 
56 
57 PXEService::PXEService()
58 	: fPxeData(NULL),
59 	  fClientIP(0),
60 	  fServerIP(0),
61 	  fRootPath(NULL)
62 {
63 }
64 
65 
66 PXEService::~PXEService()
67 {
68 	free(fRootPath);
69 }
70 
71 
72 status_t
73 PXEService::Init()
74 {
75 	// get !PXE struct
76 	fPxeData = pxe_undi_find_data();
77 	if (!fPxeData) {
78 		panic("can't find !PXE structure");
79 		return B_ERROR;
80 	}
81 
82 	dprintf("PXE API entrypoint at %04x:%04x\n", fPxeData->EntryPointSP.seg, fPxeData->EntryPointSP.ofs);
83 
84 	// get cached info
85 	PXENV_GET_CACHED_INFO cached_info;
86 	cached_info.PacketType = PXENV_PACKET_TYPE_CACHED_REPLY;
87 	cached_info.BufferSize = 0;
88 	cached_info.BufferLimit = 0;
89 	cached_info.Buffer.seg = 0;
90 	cached_info.Buffer.ofs = 0;
91 	uint16 res = call_pxe_bios(fPxeData, GET_CACHED_INFO, &cached_info);
92 	if (res != 0 || cached_info.Status != 0) {
93 		char s[100];
94 		snprintf(s, sizeof(s), "Can't determine IP address! PXENV_GET_CACHED_INFO res %x, status %x\n", res, cached_info.Status);
95 		panic("%s", s);
96 		return B_ERROR;
97 	}
98 
99 	char *buf = (char *)(cached_info.Buffer.seg * 16 + cached_info.Buffer.ofs);
100 	fClientIP = ntohl(*(ip_addr_t *)(buf + 16));
101 	fServerIP = ntohl(*(ip_addr_t *)(buf + 20));
102 	fMACAddress = mac_addr_t((uint8*)(buf + 28));
103 
104 	uint8* options = (uint8*)buf + 236;
105 	int optionsLen = int(cached_info.BufferSize) - 236;
106 
107 	// check magic
108 	if (optionsLen < 4 || options[0] != 0x63 || options[1] != 0x82
109 		|| options[2] != 0x53 || options[3] != 0x63) {
110 		return B_OK;
111 	}
112 	options += 4;
113 	optionsLen -= 4;
114 
115 	// parse DHCP options
116 	while (optionsLen > 0) {
117 		int option = *(options++);
118 		optionsLen--;
119 
120 		// check end or pad option
121 		if (option == 0xff || optionsLen < 0)
122 			break;
123 		if (option == 0x00)
124 			continue;
125 
126 		// other options have a len field
127 		int len = *(options++);
128 		optionsLen--;
129 		if (len > optionsLen)
130 			break;
131 
132 		// root path option
133 		if (option == 17) {
134 			dprintf("root path option: \"%.*s\"\n", len, (char*)options);
135 			free(fRootPath);
136 			fRootPath = (char*)malloc(len + 1);
137 			if (!fRootPath)
138 				return B_NO_MEMORY;
139 			memcpy(fRootPath, options, len);
140 			fRootPath[len] = '\0';
141 		}
142 
143 		options += len;
144 		optionsLen -= len;
145 	}
146 
147 	return B_OK;
148 }
149 
150 
151 // #pragma mark - UNDI
152 
153 
154 UNDI::UNDI()
155  :	fRxFinished(true)
156 {
157 	TRACE("UNDI::UNDI\n");
158 }
159 
160 
161 UNDI::~UNDI()
162 {
163 	TRACE("UNDI::~UNDI\n");
164 }
165 
166 
167 status_t
168 UNDI::Init()
169 {
170 	TRACE("UNDI::Init\n");
171 
172 	PXENV_UNDI_GET_INFORMATION get_info;
173 	PXENV_UNDI_GET_STATE get_state;
174 	PXENV_UNDI_OPEN undi_open;
175 	uint16 res;
176 
177 	status_t error = PXEService::Init();
178 	if (error != B_OK)
179 		return error;
180 
181 	dprintf("client-ip: %u.%u.%u.%u, server-ip: %u.%u.%u.%u\n",
182 		(fClientIP >> 24) & 0xff, (fClientIP >> 16) & 0xff, (fClientIP >> 8) & 0xff, fClientIP & 0xff,
183 		(fServerIP >> 24) & 0xff, (fServerIP >> 16) & 0xff, (fServerIP >> 8) & 0xff, fServerIP & 0xff);
184 
185 	SetIPAddress(fClientIP);
186 
187 	undi_open.OpenFlag = 0;
188 	undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS;
189 	undi_open.R_Mcast_Buf.MCastAddrCount = 0;
190 
191 	res = call_pxe_bios(fPxeData, UNDI_OPEN, &undi_open);
192 	if (res != 0 || undi_open.Status != 0) {
193 		dprintf("PXENV_UNDI_OPEN failed, res %x, status %x, ignoring\n", res, undi_open.Status);
194 	}
195 
196 	res = call_pxe_bios(fPxeData, UNDI_GET_STATE, &get_state);
197 	if (res != 0 || get_state.Status != 0) {
198 		dprintf("PXENV_UNDI_GET_STATE failed, res %x, status %x, ignoring\n", res, get_state.Status);
199 	} else {
200 		switch (get_state.UNDIstate) {
201 			case PXE_UNDI_GET_STATE_STARTED:
202 				TRACE("PXE_UNDI_GET_STATE_STARTED\n");
203 				break;
204 			case PXE_UNDI_GET_STATE_INITIALIZED:
205 				TRACE("PXE_UNDI_GET_STATE_INITIALIZED\n");
206 				break;
207 			case PXE_UNDI_GET_STATE_OPENED:
208 				TRACE("PXE_UNDI_GET_STATE_OPENED\n");
209 				break;
210 			default:
211 				TRACE("unknown undi state 0x%02x\n", get_state.UNDIstate);
212 				break;
213 		}
214 	}
215 
216 	res = call_pxe_bios(fPxeData, UNDI_GET_INFORMATION, &get_info);
217 	if (res != 0 || get_info.Status != 0) {
218 		dprintf("PXENV_UNDI_GET_INFORMATION failed, res %x, status %x\n", res, get_info.Status);
219 		return B_ERROR;
220 	}
221 
222 	TRACE("Status = %x\n", get_info.Status);
223 	TRACE("BaseIo = %x\n", get_info.BaseIo);
224 	TRACE("IntNumber = %x\n", get_info.IntNumber);
225 	TRACE("MaxTranUnit = %x\n", get_info.MaxTranUnit);
226 	TRACE("HwType = %x\n", get_info.HwType);
227 	TRACE("HwAddrLen = %x\n", get_info.HwAddrLen);
228 	TRACE("RxBufCt = %x\n", get_info.RxBufCt);
229 	TRACE("TxBufCt = %x\n", get_info.TxBufCt);
230 
231 	fMACAddress = get_info.CurrentNodeAddress;
232 
233 	TRACE("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", fMACAddress[0], fMACAddress[1], fMACAddress[2], fMACAddress[3], fMACAddress[4], fMACAddress[5]);
234 
235 	return B_OK;
236 }
237 
238 
239 mac_addr_t
240 UNDI::MACAddress() const
241 {
242 	return fMACAddress;
243 }
244 
245 
246 void *
247 UNDI::AllocateSendReceiveBuffer(size_t size)
248 {
249 	TRACE("UNDI::AllocateSendReceiveBuffer, size %ld\n", size);
250 	if (size > 0x3000)
251 		return NULL;
252 
253 	return (void *)0x500;
254 }
255 
256 
257 void
258 UNDI::FreeSendReceiveBuffer(void *buffer)
259 {
260 	TRACE("UNDI::FreeSendReceiveBuffer\n");
261 }
262 
263 
264 ssize_t
265 UNDI::Send(const void *buffer, size_t size)
266 {
267 	TRACE("UNDI::Send, buffer %p, size %ld\n", buffer, size);
268 
269 //	hex_dump(buffer, size);
270 
271 	PXENV_UNDI_TRANSMIT undi_tx;
272 	PXENV_UNDI_TBD undi_tbd;
273 
274 	undi_tx.Protocol = P_UNKNOWN;
275 	undi_tx.XmitFlag = XMT_DESTADDR;
276 	undi_tx.DestAddr.seg = SEG((char *)buffer + 16);
277 	undi_tx.DestAddr.ofs = OFS((char *)buffer + 16);
278 	undi_tx.TBD.seg = SEG(&undi_tbd);
279 	undi_tx.TBD.ofs = OFS(&undi_tbd);
280 
281 	undi_tbd.ImmedLength = size;
282 	undi_tbd.Xmit.seg = SEG(buffer);
283 	undi_tbd.Xmit.ofs = OFS(buffer);
284 	undi_tbd.DataBlkCount = 0;
285 
286 	uint16 res = call_pxe_bios(fPxeData, UNDI_TRANSMIT, &undi_tx);
287 	if (res != 0 || undi_tx.Status != 0) {
288 		dprintf("UNDI_TRANSMIT failed, res %x, status %x\n", res, undi_tx.Status);
289 		return 0;
290 	}
291 
292 	TRACE("UNDI_TRANSMIT success\n");
293 
294 	return size;
295 }
296 
297 
298 ssize_t
299 UNDI::Receive(void *buffer, size_t size)
300 {
301 	//TRACE("UNDI::Receive, buffer %p, size %ld\n", buffer, size);
302 
303 	PXENV_UNDI_ISR undi_isr;
304 	uint16 res;
305 
306 	if (!fRxFinished) {
307 		TRACE("continue receive...\n");
308 
309 		undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
310 		res = call_pxe_bios(fPxeData, UNDI_ISR, &undi_isr);
311 		if (res != 0 || undi_isr.Status != 0) {
312 			dprintf("PXENV_UNDI_ISR_IN_GET_NEXT failed, res %x, status %x\n", res, undi_isr.Status);
313 			fRxFinished = true;
314 			return 0;
315 		}
316 
317 	} else {
318 
319 		undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
320 
321 		res = call_pxe_bios(fPxeData, UNDI_ISR, &undi_isr);
322 		if (res != 0 || undi_isr.Status != 0) {
323 			dprintf("PXENV_UNDI_ISR_IN_START failed, res %x, status %x\n", res, undi_isr.Status);
324 			return -1;
325 		}
326 
327 		if (undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_OURS) {
328 //			TRACE("not ours\n");
329 			return -1;
330 		}
331 
332 		// send EOI to pic ?
333 
334 //		TRACE("PXENV_UNDI_ISR_OUT_OURS\n");
335 
336 		undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
337 		res = call_pxe_bios(fPxeData, UNDI_ISR, &undi_isr);
338 		if (res != 0 || undi_isr.Status != 0) {
339 			dprintf("PXENV_UNDI_ISR_IN_PROCESS failed, res %x, status %x\n", res, undi_isr.Status);
340 			return -1;
341 		}
342 	}
343 
344 	switch (undi_isr.FuncFlag) {
345 		case PXENV_UNDI_ISR_OUT_TRANSMIT:
346 			TRACE("PXENV_UNDI_ISR_OUT_TRANSMIT\n");
347 			fRxFinished = false;
348 			return 0;
349 
350 		case PXENV_UNDI_ISR_OUT_RECEIVE:
351 			TRACE("PXENV_UNDI_ISR_OUT_RECEIVE\n");
352 //			TRACE("BufferLength %d\n", undi_isr.BufferLength);
353 //			TRACE("FrameLength %d\n", undi_isr.FrameLength);
354 //			TRACE("FrameHeaderLength %d\n", undi_isr.FrameHeaderLength);
355 			if (undi_isr.FrameLength > undi_isr.BufferLength)
356 				panic("UNDI::Receive: multi buffer frames not supported");
357 			if (size > undi_isr.BufferLength)
358 				size = undi_isr.BufferLength;
359 			memcpy(buffer, (const void *)(undi_isr.Frame.seg * 16 + undi_isr.Frame.ofs), size);
360 //			hex_dump(buffer, size);
361 			fRxFinished = false;
362 			return size;
363 
364 		case PXENV_UNDI_ISR_OUT_BUSY:
365 			TRACE("PXENV_UNDI_ISR_OUT_BUSY\n");
366 			fRxFinished = true;
367 			return -1;
368 
369 		case PXENV_UNDI_ISR_OUT_DONE:
370 			TRACE("PXENV_UNDI_ISR_OUT_DONE\n");
371 			fRxFinished = true;
372 			return -1;
373 
374 		default:
375 			TRACE("default!!!\n");
376 			return -1;
377 	}
378 }
379 
380 
381 // #pragma mark - TFTP
382 
383 TFTP::TFTP()
384 {
385 }
386 
387 
388 TFTP::~TFTP()
389 {
390 }
391 
392 
393 status_t
394 TFTP::Init()
395 {
396 	status_t error = PXEService::Init();
397 	if (error != B_OK)
398 		return error;
399 
400 
401 
402 	return B_OK;
403 }
404 
405 
406 uint16
407 TFTP::ServerPort() const
408 {
409 	return 69;
410 }
411 
412 
413 status_t
414 TFTP::ReceiveFile(const char* fileName, uint8** data, size_t* size)
415 {
416 	// get file size
417 	pxenv_tftp_get_fsize getFileSize;
418 	getFileSize.server_ip.num = htonl(fServerIP);
419 	getFileSize.gateway_ip.num = 0;
420 	strlcpy(getFileSize.file_name, fileName, sizeof(getFileSize.file_name));
421 
422 	uint16 res = call_pxe_bios(fPxeData, TFTP_GET_FILE_SIZE, &getFileSize);
423 	if (res != 0 || getFileSize.status != 0) {
424 		dprintf("TFTP_GET_FILE_SIZE failed, res %x, status %x\n", res,
425 			getFileSize.status);
426 
427 		return B_ERROR;
428 	}
429 
430 	uint32 fileSize = getFileSize.file_size;
431 	dprintf("size of boot archive \"%s\": %u\n", fileName, fileSize);
432 
433 	// allocate memory for the data
434 	uint8* fileData = NULL;
435 	if (platform_allocate_region((void**)&fileData, fileSize,
436 			B_READ_AREA | B_WRITE_AREA, false) != B_OK) {
437 		TRACE(("TFTP: allocating memory for file data failed\n"));
438 		return B_NO_MEMORY;
439 	}
440 
441 	// open TFTP connection
442 	pxenv_tftp_open openConnection;
443 	openConnection.server_ip.num = htonl(fServerIP);
444 	openConnection.gateway_ip.num = 0;
445 	strlcpy(openConnection.file_name, fileName, sizeof(getFileSize.file_name));
446 	openConnection.port = htons(ServerPort());
447 	openConnection.packet_size = 1456;
448 
449 	res = call_pxe_bios(fPxeData, TFTP_OPEN, &openConnection);
450 	if (res != 0 || openConnection.status != 0) {
451 		dprintf("TFTP_OPEN failed, res %x, status %x\n", res,
452 			openConnection.status);
453 
454 		platform_free_region(fileData, fileSize);
455 		return B_ERROR;
456 	}
457 
458 	uint16 packetSize = openConnection.packet_size;
459 	dprintf("successfully opened TFTP connection, packet size %u\n",
460 		packetSize);
461 
462 	// check, if the file is too big for the TFTP protocol
463 	if (fileSize > 0xffff * (uint32)packetSize) {
464 		dprintf("TFTP: File is too big to be transferred via TFTP\n");
465 		_Close();
466 		platform_free_region(fileData, fileSize);
467 		return B_ERROR;
468 	}
469 
470 	// transfer the file
471 	status_t error = B_OK;
472 	uint32 remainingBytes = fileSize;
473 	uint8* buffer = fileData;
474 	while (remainingBytes > 0) {
475 		void* scratchBuffer = (void*)0x07C00;
476 		pxenv_tftp_read readPacket;
477 		readPacket.buffer.seg = SEG(scratchBuffer);
478 		readPacket.buffer.ofs = OFS(scratchBuffer);
479 
480 		res = call_pxe_bios(fPxeData, TFTP_READ, &readPacket);
481 		if (res != 0 || readPacket.status != 0) {
482 			dprintf("TFTP_READ failed, res %x, status %x\n", res,
483 				readPacket.status);
484 			error = B_ERROR;
485 			break;
486 		}
487 
488 		uint32 bytesRead = readPacket.buffer_size;
489 		if (bytesRead > remainingBytes) {
490 			dprintf("TFTP: Read more bytes than should be remaining!");
491 			error = B_ERROR;
492 			break;
493 		}
494 
495 		memcpy(buffer, scratchBuffer, bytesRead);
496 		buffer += bytesRead;
497 		remainingBytes -= bytesRead;
498 	}
499 
500 	// close TFTP connection
501 	_Close();
502 
503 	if (error == B_OK) {
504 		dprintf("TFTP: Successfully received file\n");
505 		*data = fileData;
506 		*size = fileSize;
507 	} else {
508 		platform_free_region(fileData, fileSize);
509 	}
510 
511 	return error;
512 }
513 
514 status_t
515 TFTP::_Close()
516 {
517 	// close TFTP connection
518 	pxenv_tftp_close closeConnection;
519 	uint16 res = call_pxe_bios(fPxeData, TFTP_CLOSE, &closeConnection);
520 	if (res != 0 || closeConnection.status != 0) {
521 		dprintf("TFTP_CLOSE failed, res %x, status %x\n", res,
522 			closeConnection.status);
523 		return B_ERROR;
524 	}
525 
526 	return B_OK;
527 }
528 
529 
530 
531 // #pragma mark -
532 
533 
534 status_t
535 platform_net_stack_init()
536 {
537 	TRACE("platform_net_stack_init\n");
538 
539 	UNDI *interface = new(nothrow) UNDI;
540 	if (!interface)
541 		return B_NO_MEMORY;
542 
543 	status_t error = interface->Init();
544 	if (error != B_OK) {
545 		TRACE("platform_net_stack_init: interface init failed\n");
546 		delete interface;
547 		return error;
548 	}
549 
550 	error = NetStack::Default()->AddEthernetInterface(interface);
551 	if (error != B_OK) {
552 		delete interface;
553 		return error;
554 	}
555 
556 	return B_OK;
557 }
558