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
hex_dump(const void * _data,int length)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
PXEService()57 PXEService::PXEService()
58 : fPxeData(NULL),
59 fClientIP(0),
60 fServerIP(0),
61 fRootPath(NULL)
62 {
63 }
64
65
~PXEService()66 PXEService::~PXEService()
67 {
68 free(fRootPath);
69 }
70
71
72 status_t
Init()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
UNDI()154 UNDI::UNDI()
155 : fRxFinished(true)
156 {
157 TRACE("UNDI::UNDI\n");
158 }
159
160
~UNDI()161 UNDI::~UNDI()
162 {
163 TRACE("UNDI::~UNDI\n");
164 }
165
166
167 status_t
Init()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
MACAddress() const240 UNDI::MACAddress() const
241 {
242 return fMACAddress;
243 }
244
245
246 void *
AllocateSendReceiveBuffer(size_t size)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
FreeSendReceiveBuffer(void * buffer)258 UNDI::FreeSendReceiveBuffer(void *buffer)
259 {
260 TRACE("UNDI::FreeSendReceiveBuffer\n");
261 }
262
263
264 ssize_t
Send(const void * buffer,size_t size)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
Receive(void * buffer,size_t size)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
TFTP()383 TFTP::TFTP()
384 {
385 }
386
387
~TFTP()388 TFTP::~TFTP()
389 {
390 }
391
392
393 status_t
Init()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
ServerPort() const407 TFTP::ServerPort() const
408 {
409 return 69;
410 }
411
412
413 status_t
ReceiveFile(const char * fileName,uint8 ** data,size_t * size)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
_Close()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
platform_net_stack_init()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