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
28static void
29hex_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
57PXEService::PXEService()
58	: fPxeData(NULL),
59	  fClientIP(0),
60	  fServerIP(0),
61	  fRootPath(NULL)
62{
63}
64
65
66PXEService::~PXEService()
67{
68	free(fRootPath);
69}
70
71
72status_t
73PXEService::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
154UNDI::UNDI()
155 :	fRxFinished(true)
156{
157	TRACE("UNDI::UNDI\n");
158}
159
160
161UNDI::~UNDI()
162{
163	TRACE("UNDI::~UNDI\n");
164}
165
166
167status_t
168UNDI::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: %lu.%lu.%lu.%lu, server-ip: %lu.%lu.%lu.%lu\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
239mac_addr_t
240UNDI::MACAddress() const
241{
242	return fMACAddress;
243}
244
245
246void *
247UNDI::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
257void
258UNDI::FreeSendReceiveBuffer(void *buffer)
259{
260	TRACE("UNDI::FreeSendReceiveBuffer\n");
261}
262
263
264ssize_t
265UNDI::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
298ssize_t
299UNDI::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
383TFTP::TFTP()
384{
385}
386
387
388TFTP::~TFTP()
389{
390}
391
392
393status_t
394TFTP::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
406uint16
407TFTP::ServerPort() const
408{
409	return 69;
410}
411
412
413status_t
414TFTP::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\": %lu\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
514status_t
515TFTP::_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
534status_t
535platform_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