/*! @file i386/boot1pxe/boot.c @copyright 2007,2009 David F. Elliott @abstract Contains main booter function of "remote.0" style PXE booter */ #include "libsaio.h" #include #include "pxe.h" typedef struct b1pxe0_asm { uint8_t jmp_instructions[3]; uint8_t mov_ax_opcode; uint16_t mov_ax_operand; } __attribute__((packed)) b1pxe0_asm; static void zeroBSS() { extern char _DATA__bss__begin, _DATA__bss__end; extern char _DATA__common__begin, _DATA__common__end; bzero( &_DATA__bss__begin, (&_DATA__bss__end - &_DATA__bss__begin) ); bzero( &_DATA__common__begin, (&_DATA__common__end - &_DATA__common__begin) ); } static void sleep5s() { int i; for(i=5; i>0; --i) { printf("%d...", i); sleep(1); } printf("0\n"); } extern SEGOFF16 pxenv_segoff; extern SEGOFF16 pxe_segoff; char const BOOT2_FILENAME[] = "boot"; uint16_t boot(uint32_t biosdev) { zeroBSS(); // NOTE: Don't enable A20 printf("Darwin/x86 first-stage NBP\n"); printf("'PXENV+' should be at %04x:%04xh\n", pxenv_segoff.segment, pxenv_segoff.offset); printf("'!PXE' should be at %04x:%04xh\n", pxe_segoff.segment, pxe_segoff.offset); initPxeFromBootstrap( (void*)ADDR32(pxe_segoff.segment, pxe_segoff.offset), (void*)ADDR32(pxenv_segoff.segment, pxenv_segoff.offset)); #if 0 PXEStartup(); #endif //sleep5s(); uint16_t pxenv_exit; t_PXENV_GET_CACHED_INFO cachedInfoData; BOOTPLAYER *dhcp_discover_packet = NULL; BOOTPLAYER const *dhcp_ack_packet = NULL; printf("Retrieving DHCP Discover info\n"); if((pxenv_exit = pxenvGetCachedInfo(&cachedInfoData, PXENV_PACKET_TYPE_DHCP_DISCOVER)) == PXENV_EXIT_SUCCESS) dhcp_discover_packet = getPxeCachedBuffer(&cachedInfoData); #if 1 printf("opcode\t%d\n", dhcp_discover_packet->opcode); printf("MAC %02x:%02x:%02x:%02x:%02x:%02x\n" , dhcp_discover_packet->CAddr[0] , dhcp_discover_packet->CAddr[1] , dhcp_discover_packet->CAddr[2] , dhcp_discover_packet->CAddr[3] , dhcp_discover_packet->CAddr[4] , dhcp_discover_packet->CAddr[5] ); #endif printf("Retrieving DHCP ACK info\n"); #if 1 if((pxenv_exit = pxenvGetCachedInfo(&cachedInfoData, PXENV_PACKET_TYPE_DHCP_ACK)) == PXENV_EXIT_SUCCESS) dhcp_ack_packet = getPxeCachedBuffer(&cachedInfoData); printf("Client IP %d.%d.%d.%d\n" , dhcp_ack_packet->cip.array[0] , dhcp_ack_packet->cip.array[1] , dhcp_ack_packet->cip.array[2] , dhcp_ack_packet->cip.array[3] ); printf("'Your' IP %d.%d.%d.%d\n" , dhcp_ack_packet->yip.array[0] , dhcp_ack_packet->yip.array[1] , dhcp_ack_packet->yip.array[2] , dhcp_ack_packet->yip.array[3] ); printf("Server IP %d.%d.%d.%d\n" , dhcp_ack_packet->sip.array[0] , dhcp_ack_packet->sip.array[1] , dhcp_ack_packet->sip.array[2] , dhcp_ack_packet->sip.array[3] ); printf("Gateway IP %d.%d.%d.%d\n" , dhcp_ack_packet->gip.array[0] , dhcp_ack_packet->gip.array[1] , dhcp_ack_packet->gip.array[2] , dhcp_ack_packet->gip.array[3] ); #endif // NOTE: Depending on BIOS executing this next call may invalidate the previous buffer. // I have nothing to back this up I just would suspect as much out of a BIOS call. printf("Retrieving Boot Server DHCP reply\n"); if((pxenv_exit = pxenvGetCachedInfo(&cachedInfoData, PXENV_PACKET_TYPE_CACHED_REPLY)) == PXENV_EXIT_SUCCESS) dhcp_ack_packet = getPxeCachedBuffer(&cachedInfoData); #if 1 printf("Server IP %d.%d.%d.%d\n" , dhcp_ack_packet->sip.array[0] , dhcp_ack_packet->sip.array[1] , dhcp_ack_packet->sip.array[2] , dhcp_ack_packet->sip.array[3] ); #endif t_PXENV_TFTP_GET_FSIZE tftpGetFsizeCommand; bzero(&tftpGetFsizeCommand, sizeof(tftpGetFsizeCommand)); // FIXME: TFTP server can be different and comes from DHCP options tftpGetFsizeCommand.ServerIPAddress = dhcp_ack_packet->sip; char const *in; char const *lastslash = NULL; for(in = (char const*)dhcp_ack_packet->bootfile; in < (char const*)dhcp_ack_packet->bootfile + sizeof(dhcp_ack_packet->bootfile) && *in != '\0'; ++in) { if(*in == '/') lastslash = in; } char *out = tftpGetFsizeCommand.FileName; if(lastslash != NULL) for(in = (char const*)dhcp_ack_packet->bootfile; in <= lastslash && out < tftpGetFsizeCommand.FileName + sizeof(tftpGetFsizeCommand.FileName); ++in, ++out) *out = *in; if(out + sizeof(BOOT2_FILENAME) < tftpGetFsizeCommand.FileName + sizeof(tftpGetFsizeCommand.FileName)) { for(in = BOOT2_FILENAME; in < BOOT2_FILENAME + sizeof(BOOT2_FILENAME); ++in, ++out) *out = *in; } else { printf("Computed boot filename is too long. Consider shortening the path.\n"); return -1; } printf("Getting size of TFTP file \"%s\"\n", tftpGetFsizeCommand.FileName); pxenv_exit = pxeCall(PXENV_TFTP_GET_FSIZE, &tftpGetFsizeCommand); if(pxenv_exit != PXENV_EXIT_SUCCESS) { printf("TFTP Get Fsize call failed with exit %d\n", pxenv_exit); sleep(4); return -1; } if(tftpGetFsizeCommand.Status != PXENV_STATUS_SUCCESS) { printf("TFTP Get Fsize failed with status %d\n", tftpGetFsizeCommand.Status); sleep(4); return -1; } printf("TFTP returned file size of %ld bytes\n", tftpGetFsizeCommand.FileSize); //sleep5s(); ////////////////////////////////////////////////////// printf("Opening TFTP file \"%s\"\n", tftpGetFsizeCommand.FileName); t_PXENV_TFTP_OPEN tftpOpenCommand; bzero(&tftpOpenCommand, sizeof(tftpOpenCommand)); // FIXME: TFTP server can be different and comes from DHCP options tftpOpenCommand.ServerIPAddress = dhcp_ack_packet->sip; strcpy(tftpOpenCommand.FileName, tftpGetFsizeCommand.FileName); tftpOpenCommand.TFTPPort = 69 << 8; // byte-swapped.. yeah.. it's in network byte order! tftpOpenCommand.PacketSize = 1440; pxenv_exit = pxeCall(PXENV_TFTP_OPEN, &tftpOpenCommand); if(pxenv_exit != PXENV_EXIT_SUCCESS) { printf("TFTP Open call failed with exit %d\n", pxenv_exit); sleep(4); return -1; } if(tftpOpenCommand.Status != PXENV_STATUS_SUCCESS) { printf("TFTP Open failed with status %d\n", tftpOpenCommand.Status); sleep(4); return -1; } printf("Suggested packet size is %d bytes\n", tftpOpenCommand.PacketSize); //sleep5s(); ////////////////////////////////////////////////////// printf("We would transfer the file now.\n"); t_PXENV_TFTP_READ tftpReadCommand; bzero(&tftpReadCommand, sizeof(tftpReadCommand)); uint32_t BytesRemaining = tftpGetFsizeCommand.FileSize; char *Buffer = (char*)(uint32_t)(BOOT2_ADDR); // Make buffer size 32k which should be a safe value tftpReadCommand.BufferSize = 32 * 1024; for(;BytesRemaining > 0;) { tftpReadCommand.Buffer.segment = NORMALIZED_SEGMENT((uint32_t)Buffer); tftpReadCommand.Buffer.offset = NORMALIZED_OFFSET((uint32_t)Buffer); // BufferSize is set to the amount read each time through. pxenv_exit = pxeCall(PXENV_TFTP_READ, &tftpReadCommand); if(pxenv_exit != PXENV_EXIT_SUCCESS) { printf("TFTP Read call failed with exit %d\n", pxenv_exit); sleep(4); return -1; } if(tftpReadCommand.Status != PXENV_STATUS_SUCCESS) { printf("TFTP Read failed with status %d\n", tftpOpenCommand.Status); sleep(4); return -1; } BytesRemaining -= tftpReadCommand.BufferSize; Buffer += tftpReadCommand.BufferSize; } ////////////////////////////////////////////////////// tftpOpenCommand.Status = 0; pxenv_exit = pxeCall(PXENV_TFTP_CLOSE, &tftpOpenCommand); if(pxenv_exit != PXENV_EXIT_SUCCESS) { printf("TFTP Close failed with exit %d\n", pxenv_exit); return -1; } if(tftpOpenCommand.Status != PXENV_STATUS_SUCCESS) { printf("TFTP Close failed with status %d\n", tftpOpenCommand.Status); return -1; } printf("Seemed to close okay!\n"); //sleep5s(); (void)&sleep5s; return 0; }