/** Set the current PXE handle. * @param handle Handle to set to. If NULL, current will be closed. * @return Status code describing the result of the operation. */ static status_t set_current_handle(pxe_handle_t *handle) { if (current_pxe_handle) { pxenv_tftp_close_t close; pxe_call(PXENV_TFTP_CLOSE, &close); current_pxe_handle = NULL; } if (handle) { pxe_device_t *device = container_of(handle->handle.mount, pxe_device_t, mount); pxenv_tftp_open_t open; strcpy((char *)open.filename, handle->path); memcpy(&open.server_ip, &device->net.server_ip, sizeof(open.server_ip)); memcpy(&open.gateway_ip, &device->net.gateway_ip, sizeof(open.gateway_ip)); open.udp_port = cpu_to_be16(device->net.server_port); open.packet_size = PXENV_TFTP_PACKET_SIZE; if (pxe_call(PXENV_TFTP_OPEN, &open) != PXENV_EXIT_SUCCESS || open.status) { if (open.status == PXENV_STATUS_TFTP_NOT_FOUND) { return STATUS_NOT_FOUND; } else { dprintf("pxe: open request for '%s' failed: 0x%x\n", handle->path, open.status); return STATUS_DEVICE_ERROR; } } handle->packet_size = open.packet_size; handle->packet_number = 0; current_pxe_handle = handle; } return STATUS_SUCCESS; }
/** * Shut down PXE before booting an OS */ static void shutdown_pxe(void) { if (pxe_call(PXENV_UNDI_SHUTDOWN, (void *)BIOS_MEM_BASE) != PXENV_EXIT_SUCCESS) { dprintf("pxe: warning: PXENV_UNDI_SHUTDOWN failed\n"); } if (pxe_call(PXENV_UNLOAD_STACK, (void *)BIOS_MEM_BASE) != PXENV_EXIT_SUCCESS) { dprintf("pxe: warning: PXENV_UNLOAD_STACK failed\n"); } if (pxe_call(PXENV_STOP_UNDI, (void *)BIOS_MEM_BASE) != PXENV_EXIT_SUCCESS) { dprintf("pxe: warning: PXENV_STOP_UNDI failed\n"); } }
static void gpxecmd(const char **args) { char *q; struct s_PXENV_FILE_EXEC *fx; fx = lmalloc(sizeof *fx); if (!fx) return; q = (char *)(fx + 1); fx->Status = 1; fx->Command.offs = OFFS(q); fx->Command.seg = SEG(q); while (*args) { q = stpcpy(q, *args); *q++ = ' '; args++; } *--q = '\0'; pxe_call(PXENV_FILE_EXEC, fx); /* This should not return... */ }
/** * Get a fresh packet from a gPXE socket * @param: inode -> Inode pointer * */ static void gpxe_get_packet(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); static __lowmem struct s_PXENV_FILE_READ file_read; int err; while (1) { file_read.FileHandle = socket->tftp_remoteport; file_read.Buffer = FAR_PTR(packet_buf); file_read.BufferSize = PKTBUF_SIZE; err = pxe_call(PXENV_FILE_READ, &file_read); if (!err) /* successed */ break; if (file_read.Status != PXENV_STATUS_TFTP_OPEN) kaboom(); } memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize); socket->tftp_dataptr = socket->tftp_pktbuf; socket->tftp_bytesleft = file_read.BufferSize; socket->tftp_filepos += file_read.BufferSize; if (socket->tftp_bytesleft == 0) inode->size = socket->tftp_filepos; /* if we're done here, close the file */ if (inode->size > socket->tftp_filepos) return; /* Got EOF, close it */ socket->tftp_goteof = 1; gpxe_close_file(inode); }
/* Caller must leave room for ethernet, ip, and udp headers in front!! */ ssize_t pxesendudp(struct iodesc *d, void *pkt, size_t len) { t_PXENV_UDP_WRITE *uw = (void *) pxe_command_buf; uw->status = 0; uw->ip = d->destip.s_addr; uw->gw = gateip.s_addr; uw->src_port = d->myport; uw->dst_port = d->destport; uw->buffer_size = len; uw->buffer.segment = VTOPSEG(pkt); uw->buffer.offset = VTOPOFF(pkt); pxe_call(PXENV_UDP_WRITE); if (uw->status != PXENV_STATUS_SUCCESS) { /* XXX This happens a lot; it shouldn't. */ if (uw->status != PXENV_STATUS_FAILURE) printf("sendudp: PXENV_UDP_WRITE failed: 0x%x\n", uw->status); return -1; } return len; }
int pxe_read(IP4_t * src, UDP_PORT_t * port, void * buf, int len, int block) { t_PXENV_UDP_READ * pxe_read; bzero (pxebuf, sizeof(t_PXENV_UDP_READ)); pxe_read = (t_PXENV_UDP_READ *)pxebuf; retry: pxe_read->dest_ip = htonl(*src); pxe_read->d_port = htons(*port); pxe_read->buffer_size = len; pxe_read->buffer.segment = SEG(buf); pxe_read->buffer.offset = OFF(buf); pxe_op = (uint16_t)PXENV_UDP_READ; pxe_addr = (uint16_t *)pxe_read; if (pxe_call() != 0) { if (block) goto retry; return(1); } *src = ntohl(pxe_read->src_ip); *port = ntohs(pxe_read->s_port); return(0); }
int pxe_write(IP4_t dst, UDP_PORT_t port, void * buf, int len) { t_PXENV_UDP_WRITE * pxe_write; int i = 0; bzero (pxebuf, sizeof(t_PXENV_UDP_WRITE)); pxe_write = (t_PXENV_UDP_WRITE *)pxebuf; retry: pxe_write->ip = htonl(dst); pxe_write->dst_port = htons(port); pxe_write->src_port = htons(port); pxe_write->buffer_size = len; pxe_write->buffer.segment = SEG(buf); pxe_write->buffer.offset = OFF(buf); pxe_op = (uint16_t)PXENV_UDP_WRITE; pxe_addr = (uint16_t *)pxe_write; if (pxe_call() != 0) { i++; if (i < 10) goto retry; return(1); } return(0); }
static void gpxe_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); static __lowmem struct s_PXENV_FILE_CLOSE file_close; file_close.FileHandle = socket->tftp_remoteport; pxe_call(PXENV_FILE_CLOSE, &file_close); }
/** * Get the BOOTP reply packet. * * @return Pointer to BOOTP reply packet. */ static bootp_packet_t *get_bootp_packet(void) { pxenv_get_cached_info_t ci; /* Obtain the BOOTP reply packet. */ ci.packet_type = PXENV_PACKET_TYPE_CACHED_REPLY; ci.buffer = 0; ci.buffer_size = 0; if (pxe_call(PXENV_GET_CACHED_INFO, &ci) != PXENV_EXIT_SUCCESS) boot_error("Failed to get PXE BOOTP packet (0x%x)", ci.status); return (bootp_packet_t *)segoff_to_linear(ci.buffer); }
/** * Read the next packet from a TFTP file. * * @note Reads to BIOS_MEM_BASE. * @param handle Handle to read from. * @return Status code describing the result of the operation. */ static status_t read_packet(pxe_handle_t *handle) { pxenv_tftp_read_t read; read.buffer = BIOS_MEM_BASE; read.buffer_size = handle->packet_size; if (pxe_call(PXENV_TFTP_READ, &read) != PXENV_EXIT_SUCCESS || read.status) { dprintf("pxe: reading packet %u in '%s' failed: 0x%x\n", handle->packet_number, handle->path, read.status); return STATUS_DEVICE_ERROR; } handle->packet_number++; return STATUS_SUCCESS; }
int pxe_close(void) { t_PXENV_UDP_CLOSE * pxe_close; bzero (pxebuf, sizeof(t_PXENV_UDP_CLOSE)); pxe_close = (t_PXENV_UDP_CLOSE *)pxebuf; pxe_op = (uint16_t)PXENV_UDP_CLOSE; pxe_addr = (uint16_t *)pxe_close; if (pxe_call() != 0) { #ifndef SILENT printf("PXE UDP close failed\n"); #endif return(1); } return(0); }
/** * Open a path on the filesystem. * * @param mount Mount to open from. * @param path Path to file/directory to open (can be modified). * @param from Handle on this FS to open relative to. * @param _handle Where to store pointer to opened handle. * @return Status code describing the result of the operation. */ static status_t pxe_fs_open_path(fs_mount_t *mount, char *path, fs_handle_t *from, fs_handle_t **_handle) { pxe_device_t *device = container_of(mount, pxe_device_t, mount); pxenv_tftp_get_fsize_t fsize; pxe_handle_t *handle; size_t len; status_t ret; if (from) { return STATUS_NOT_SUPPORTED; } len = strlen(path); if (len >= PXENV_TFTP_PATH_SIZE) { return STATUS_NOT_FOUND; } /* Get the file size. I'm not actually sure whether it's necessary to close * the current handle beforehand, but I'm doing it to be on the safe side. */ set_current_handle(NULL); strcpy((char *)fsize.filename, path); memcpy(&fsize.server_ip, &device->net.server_ip, sizeof(fsize.server_ip)); memcpy(&fsize.gateway_ip, &device->net.gateway_ip, sizeof(fsize.gateway_ip)); if (pxe_call(PXENV_TFTP_GET_FSIZE, &fsize) != PXENV_EXIT_SUCCESS || fsize.status) { if (fsize.status == PXENV_STATUS_TFTP_NOT_FOUND) { return STATUS_NOT_FOUND; } else { dprintf("pxe: file size request for '%s' failed: 0x%x\n", path, fsize.status); return STATUS_DEVICE_ERROR; } } handle = malloc(sizeof(*handle) + len + 1); fs_handle_init(&handle->handle, mount, FILE_TYPE_REGULAR, fsize.file_size); strcpy(handle->path, path); /* Try to open the file as the current. */ ret = set_current_handle(handle); if (ret != STATUS_SUCCESS) { free(handle); return ret; } *_handle = &handle->handle; return STATUS_SUCCESS; }
int pxe_open(void) { t_PXENV_UDP_OPEN * pxe_open; bzero (pxebuf, sizeof(t_PXENV_UDP_OPEN)); pxe_open = (t_PXENV_UDP_OPEN *)pxebuf; pxe_op = (uint16_t)PXENV_UDP_OPEN; pxe_addr = (uint16_t *)pxe_open; pxe_open->src_ip = bootp->yip; if (pxe_call() != 0) { #ifndef SILENT printf("PXE UDP open failed\n"); #endif return(1); } return(0); }
int pxe_netif_open() { t_PXENV_UDP_OPEN *uo = (void *) pxe_command_buf; #ifdef NETIF_DEBUG printf("pxe_netif_open()\n"); #endif if (!pxe_inited) { if (pxe_init(0) != 0) return -1; pxe_inited = 1; } /* BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); */ bzero(uo, sizeof(*uo)); uo->src_ip = bootplayer.yip; pxe_call(PXENV_UDP_OPEN); if (uo->status != PXENV_STATUS_SUCCESS) { printf("\npxe_netif_open: PXENV_UDP_OPEN failed: 0x%x\n", uo->status); return -1; } bcopy(bootplayer.CAddr, desc.myea, ETHER_ADDR_LEN); bootmac = bootplayer.CAddr; /* * Since the PXE BIOS has already done DHCP, make sure we * don't reuse any of its transaction IDs. */ desc.xid = bootplayer.ident; return 0; }
/** * Open a url using gpxe * * @param:inode, the inode to store our state in * @param:url, the url we want to open * * @out: open_file_t structure, stores in file->open_file * @out: the lenght of this file, stores in file->file_len * */ void gpxe_open(struct inode *inode, const char *url) { static __lowmem struct s_PXENV_FILE_OPEN file_open; static char lowurl[2*FILENAME_MAX]; struct pxe_pvt_inode *socket = PVT(inode); int err; socket->tftp_pktbuf = malloc(PKTBUF_SIZE); if (!socket->tftp_pktbuf) return; snprintf(lowurl, sizeof lowurl, "%s", url); file_open.Status = PXENV_STATUS_BAD_FUNC; file_open.FileName = FAR_PTR(lowurl); err = pxe_call(PXENV_FILE_OPEN, &file_open); if (err) return; socket->fill_buffer = gpxe_get_packet; socket->close = gpxe_close_file; socket->tftp_remoteport = file_open.FileHandle; inode->size = -1; /* This is not an error */ }
/* * Receive a UDP packet and validate it for us. * Caller leaves room for the headers (Ether, IP, UDP). */ ssize_t pxereadudp(struct iodesc *d, void *pkt, size_t len, time_t tleft) { t_PXENV_UDP_READ *ur = (void *) pxe_command_buf; struct udphdr *uh; struct ip *ip; uh = (struct udphdr *)pkt - 1; ip = (struct ip *)uh - 1; bzero(ur, sizeof(*ur)); ur->dest_ip = d->myip.s_addr; ur->d_port = d->myport; ur->buffer_size = len; ur->buffer.segment = VTOPSEG(pkt); ur->buffer.offset = VTOPOFF(pkt); /* XXX Timeout unused. */ pxe_call(PXENV_UDP_READ); if (ur->status != PXENV_STATUS_SUCCESS) { /* XXX This happens a lot; it shouldn't. */ if (ur->status != PXENV_STATUS_FAILURE) printf("readudp: PXENV_UDP_READ_failed: 0x%0x\n", ur->status); return -1; } ip->ip_src.s_addr = ur->src_ip; uh->uh_sport = ur->s_port; uh->uh_dport = d->myport; return ur->buffer_size; }
int pxe_init(IP4_t * myaddr, int * sendsize) { pxenv_t * pxe; pxe_t * pxe2; t_PXENV_GET_CACHED_INFO * pxe_cache; t_PXENV_UNDI_GET_INFORMATION * pxe_info; bios_pxe(); if (pxe_op != PXE_VN) return(1); /* Find PXENV+ structure */ pxe = (pxenv_t *)((uintptr_t)((pxe_seg << 4) + pxe_off)); /* Find !PXE structure */ pxe2 = (pxe_t *)((uintptr_t)((pxe->PXEPtr.segment << 4) + pxe->PXEPtr.offset)); #ifndef SILENT printf("PXE version: %d.%d entry: %x:%x\n", pxe->Version >> 8, pxe->Version & 0xFF, pxe2->EntryPointSP.segment, pxe2->EntryPointSP.offset); #endif /* Save the PXE entry point */ pxe_seg = pxe2->EntryPointSP.segment; pxe_off = pxe2->EntryPointSP.offset; /* Get UNDI info */ bzero (pxebuf, sizeof(pxebuf)); pxe_info = (t_PXENV_UNDI_GET_INFORMATION *)pxebuf; pxe_op = (uint16_t)PXENV_UNDI_GET_INFORMATION; pxe_addr = (uint16_t *)pxe_info; if (pxe_call() != 0) { #ifndef SILENT printf("PXE get UNDI info failed\n"); #endif return(1); } #ifndef SILENT printf("pxe0 at 0x%x irq %d\n", pxe_info->BaseIo, pxe_info->IntNumber); #endif *sendsize = pxe_info->TxBufCt; /* Now find our IP address */ bzero (pxebuf, sizeof(pxebuf)); pxe_cache = (t_PXENV_GET_CACHED_INFO *)pxebuf; pxe_cache->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; pxe_op = (uint16_t)PXENV_GET_CACHED_INFO; pxe_addr = (uint16_t *)pxe_cache; pxe_call(); if (pxe_call() != 0) { #ifndef SILENT printf("PXE get cache failed\n"); #endif return(1); } bootp = (BOOTPLAYER *)((uintptr_t)((pxe_cache->Buffer.segment << 4) + pxe_cache->Buffer.offset)); #ifndef SILENT printf("My IP address: %d.%d.%d.%d\n", IP_ARGS(ntohl(bootp->yip))); printf("My eth address: %02x:%02x:%02x:%02x:%02x:%02x\n", bootp->CAddr[0], bootp->CAddr[1], bootp->CAddr[2], bootp->CAddr[3], bootp->CAddr[4], bootp->CAddr[5]); #endif *myaddr = ntohl(bootp->yip); return (0); }