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: %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 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\": %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 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