Example #1
0
/**
 * FILE OPEN
 *
 * @v file_open				Pointer to a struct s_PXENV_FILE_OPEN
 * @v s_PXENV_FILE_OPEN::FileName	URL of file to open
 * @ret #PXENV_EXIT_SUCCESS		File was opened
 * @ret #PXENV_EXIT_FAILURE		File was not opened
 * @ret s_PXENV_FILE_OPEN::Status	PXE status code
 * @ret s_PXENV_FILE_OPEN::FileHandle	Handle of opened file
 *
 */
PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ) {
	userptr_t filename;
	size_t filename_len;
	int fd;

	DBG ( "PXENV_FILE_OPEN" );

	/* Copy name from external program, and open it */
	filename = real_to_user ( file_open->FileName.segment,
			      file_open->FileName.offset );
	filename_len = strlen_user ( filename, 0 );
	{
		char uri_string[ filename_len + 1 ];

		copy_from_user ( uri_string, filename, 0,
				 sizeof ( uri_string ) );
		DBG ( " %s", uri_string );
		fd = open ( uri_string );
	}

	if ( fd < 0 ) {
		file_open->Status = PXENV_STATUS ( fd );
		return PXENV_EXIT_FAILURE;
	}

	DBG ( " as file %d", fd );

	file_open->FileHandle = fd;
	file_open->Status = PXENV_STATUS_SUCCESS;
	return PXENV_EXIT_SUCCESS;
}
Example #2
0
/**
 * Deallocate space on the real-mode stack, optionally copying back
 * data to a user buffer.
 *
 * @v data		User buffer
 * @v size		Size of stack data
 */
void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
	if ( data ) {
		userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
		memcpy_user ( rm_stack, 0, data, 0, size );
	}
	rm_sp += size;
};
Example #3
0
/**
 * FILE EXEC
 *
 * @v file_exec				Pointer to a struct s_PXENV_FILE_EXEC
 * @v s_PXENV_FILE_EXEC::Command	Command to execute
 * @ret #PXENV_EXIT_SUCCESS		Command was executed successfully
 * @ret #PXENV_EXIT_FAILURE		Command was not executed successfully
 * @ret s_PXENV_FILE_EXEC::Status	PXE status code
 *
 */
PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) {
	userptr_t command;
	size_t command_len;
	int rc;

	DBG ( "PXENV_FILE_EXEC" );

	/* Copy name from external program, and exec it */
	command = real_to_user ( file_exec->Command.segment,
				 file_exec->Command.offset );
	command_len = strlen_user ( command, 0 );
	{
		char command_string[ command_len + 1 ];

		copy_from_user ( command_string, command, 0,
				 sizeof ( command_string ) );
		DBG ( " %s", command_string );

		if ( ( rc = system ( command_string ) ) != 0 ) {
			file_exec->Status = PXENV_STATUS ( rc );
			return PXENV_EXIT_FAILURE;
		}
	}

	file_exec->Status = PXENV_STATUS_SUCCESS;
	return PXENV_EXIT_SUCCESS;
}
Example #4
0
/**
 * Perform a series of memory copies from a list in low memory
 */
static void shuffle ( unsigned int list_segment, unsigned int list_offset, unsigned int count )
{
	comboot_shuffle_descriptor shuf[COMBOOT_MAX_SHUFFLE_DESCRIPTORS];
	unsigned int i;

	/* Copy shuffle descriptor list so it doesn't get overwritten */
	copy_from_user ( shuf, real_to_user ( list_segment, list_offset ), 0,
	                 count * sizeof( comboot_shuffle_descriptor ) );

	/* Do the copies */
	for ( i = 0; i < count; i++ ) {
		userptr_t src_u = phys_to_user ( shuf[ i ].src );
		userptr_t dest_u = phys_to_user ( shuf[ i ].dest );

		if ( shuf[ i ].src == 0xFFFFFFFF ) {
			/* Fill with 0 instead of copying */
			memset_user ( dest_u, 0, 0, shuf[ i ].len );
		} else if ( shuf[ i ].dest == 0xFFFFFFFF ) {
			/* Copy new list of descriptors */
			count = shuf[ i ].len / sizeof( comboot_shuffle_descriptor );
			assert ( count <= COMBOOT_MAX_SHUFFLE_DESCRIPTORS );
			copy_from_user ( shuf, src_u, 0, shuf[ i ].len );
			i = -1;
		} else {
			/* Regular copy */
			memmove_user ( dest_u, 0, src_u, 0, shuf[ i ].len );
		}
	}
}
Example #5
0
/**
 * Allocate space on the real-mode stack and copy data there from a
 * user buffer
 *
 * @v data		User buffer
 * @v size		Size of stack data
 * @ret sp		New value of real-mode stack pointer
 */
uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
	userptr_t rm_stack;
	rm_sp -= size;
	rm_stack = real_to_user ( rm_ss, rm_sp );
	memcpy_user ( rm_stack, 0, data, 0, size );
	return rm_sp;
};
Example #6
0
/**
 * Execute PXE image
 *
 * @v image		PXE image
 * @ret rc		Return status code
 */
static int pxe_exec ( struct image *image ) {
	userptr_t buffer = real_to_user ( 0, 0x7c00 );
	struct net_device *netdev;
	int rc;

	/* Verify and prepare segment */
	if ( ( rc = prep_segment ( buffer, image->len, image->len ) ) != 0 ) {
		DBGC ( image, "IMAGE %p could not prepare segment: %s\n",
		       image, strerror ( rc ) );
		return rc;
	}

	/* Copy image to segment */
	memcpy_user ( buffer, 0, image->data, 0, image->len );

	/* Arbitrarily pick the most recently opened network device */
	if ( ( netdev = last_opened_netdev() ) == NULL ) {
		DBGC ( image, "IMAGE %p could not locate PXE net device\n",
		       image );
		return -ENODEV;
	}
	netdev_get ( netdev );

	/* Activate PXE */
	pxe_activate ( netdev );

	/* Construct fake DHCP packets */
	pxe_fake_cached_info();

	/* Set PXE command line */
	pxe_cmdline = image->cmdline;

	/* Reset console since PXE NBP will probably use it */
	console_reset();

	/* Disable IRQ, if applicable */
	if ( netdev_irq_supported ( netdev ) && netdev->dev->desc.irq )
		disable_irq ( netdev->dev->desc.irq );

	/* Start PXE NBP */
	rc = pxe_start_nbp();

	/* Clear PXE command line */
	pxe_cmdline = NULL;

	/* Deactivate PXE */
	pxe_deactivate();

	/* Try to reopen network device.  Ignore errors, since the NBP
	 * may have called PXENV_STOP_UNDI.
	 */
	netdev_open ( netdev );
	netdev_put ( netdev );

	return rc;
}
Example #7
0
/**
 * Print a string with a particular terminator
 */
static void print_user_string ( unsigned int segment, unsigned int offset, char terminator ) {
	int i = 0;
	char c;
	userptr_t str = real_to_user ( segment, offset );
	for ( ; ; ) {
		copy_from_user ( &c, str, i, 1 );
		if ( c == terminator ) break;
		putchar ( c );
		i++;
	}
}
Example #8
0
/**
 * Receive PXE UDP data
 *
 * @v pxe_udp			PXE UDP connection
 * @v iobuf			I/O buffer
 * @v meta			Data transfer metadata
 * @ret rc			Return status code
 *
 * Receives a packet as part of the current pxenv_udp_read()
 * operation.
 */
static int pxe_udp_deliver ( struct pxe_udp_connection *pxe_udp,
			     struct io_buffer *iobuf,
			     struct xfer_metadata *meta ) {
	struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read;
	struct sockaddr_in *sin_src;
	struct sockaddr_in *sin_dest;
	userptr_t buffer;
	size_t len;
	int rc = 0;

	if ( ! pxenv_udp_read ) {
		DBG ( "PXE discarded UDP packet\n" );
		rc = -ENOBUFS;
		goto done;
	}

	/* Copy packet to buffer and record length */
	buffer = real_to_user ( pxenv_udp_read->buffer.segment,
				pxenv_udp_read->buffer.offset );
	len = iob_len ( iobuf );
	if ( len > pxenv_udp_read->buffer_size )
		len = pxenv_udp_read->buffer_size;
	copy_to_user ( buffer, 0, iobuf->data, len );
	pxenv_udp_read->buffer_size = len;

	/* Fill in source/dest information */
	assert ( meta );
	sin_src = ( struct sockaddr_in * ) meta->src;
	assert ( sin_src );
	assert ( sin_src->sin_family == AF_INET );
	pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr;
	pxenv_udp_read->s_port = sin_src->sin_port;
	sin_dest = ( struct sockaddr_in * ) meta->dest;
	assert ( sin_dest );
	assert ( sin_dest->sin_family == AF_INET );
	pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr;
	pxenv_udp_read->d_port = sin_dest->sin_port;

	/* Mark as received */
	pxe_udp->pxenv_udp_read = NULL;

 done:
	free_iob ( iobuf );
	return rc;
}
Example #9
0
/**
 * FILE READ
 *
 * @v file_read				Pointer to a struct s_PXENV_FILE_READ
 * @v s_PXENV_FILE_READ::FileHandle	File handle
 * @v s_PXENV_FILE_READ::BufferSize	Size of data buffer
 * @v s_PXENV_FILE_READ::Buffer		Data buffer
 * @ret #PXENV_EXIT_SUCCESS		Data has been read from file
 * @ret #PXENV_EXIT_FAILURE		Data has not been read from file
 * @ret s_PXENV_FILE_READ::Status	PXE status code
 * @ret s_PXENV_FILE_READ::Ready	Indication of readiness
 * @ret s_PXENV_FILE_READ::BufferSize	Length of data read
 *
 */
PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read ) {
	userptr_t buffer;
	ssize_t len;

	DBG ( "PXENV_FILE_READ %d to %04x:%04x+%04x", file_read->FileHandle,
	      file_read->Buffer.segment, file_read->Buffer.offset,
	      file_read->BufferSize );

	buffer = real_to_user ( file_read->Buffer.segment,
				file_read->Buffer.offset );
	if ( ( len = read_user ( file_read->FileHandle, buffer, 0,
				file_read->BufferSize ) ) < 0 ) {
		file_read->Status = PXENV_STATUS ( len );
		return PXENV_EXIT_FAILURE;
	}

	DBG ( " read %04zx", ( ( size_t ) len ) );

	file_read->BufferSize = len;
	file_read->Status = PXENV_STATUS_SUCCESS;
	return PXENV_EXIT_SUCCESS;
}
Example #10
0
/**
 * FILE CMDLINE
 *
 * @v file_cmdline			Pointer to a struct s_PXENV_FILE_CMDLINE
 * @v s_PXENV_FILE_CMDLINE::Buffer	Buffer to contain command line
 * @v s_PXENV_FILE_CMDLINE::BufferSize	Size of buffer
 * @ret #PXENV_EXIT_SUCCESS		Command was executed successfully
 * @ret #PXENV_EXIT_FAILURE		Command was not executed successfully
 * @ret s_PXENV_FILE_EXEC::Status	PXE status code
 * @ret s_PXENV_FILE_EXEC::BufferSize	Length of command line (including NUL)
 *
 */
static PXENV_EXIT_t
pxenv_file_cmdline ( struct s_PXENV_FILE_CMDLINE *file_cmdline ) {
	userptr_t buffer;
	size_t max_len;
	size_t len;

	DBG ( "PXENV_FILE_CMDLINE to %04x:%04x+%04x \"%s\"\n",
	      file_cmdline->Buffer.segment, file_cmdline->Buffer.offset,
	      file_cmdline->BufferSize, pxe_cmdline );

	buffer = real_to_user ( file_cmdline->Buffer.segment,
				file_cmdline->Buffer.offset );
	len = file_cmdline->BufferSize;
	max_len = ( pxe_cmdline ?
		    ( strlen ( pxe_cmdline ) + 1 /* NUL */ ) : 0 );
	if ( len > max_len )
		len = max_len;
	copy_to_user ( buffer, 0, pxe_cmdline, len );
	file_cmdline->BufferSize = max_len;

	file_cmdline->Status = PXENV_STATUS_SUCCESS;
	return PXENV_EXIT_SUCCESS;
}
Example #11
0
/**
 * Load PXE image into memory
 *
 * @v image		PXE file
 * @ret rc		Return status code
 */
int pxe_load ( struct image *image ) {
	userptr_t buffer = real_to_user ( 0, 0x7c00 );
	size_t filesz = image->len;
	size_t memsz = image->len;
	int rc;

	/* Images too large to fit in base memory cannot be PXE
	 * images.  We include this check to help prevent unrecognised
	 * images from being marked as PXE images, since PXE images
	 * have no signature we can check against.
	 */
	if ( filesz > ( 0xa0000 - 0x7c00 ) )
		return -ENOEXEC;

	/* Rejecting zero-length images is also useful, since these
	 * end up looking to the user like bugs in gPXE.
	 */
	if ( ! filesz )
		return -ENOEXEC;

	/* There are no signature checks for PXE; we will accept anything */
	if ( ! image->type )
		image->type = &pxe_image_type;

	/* Verify and prepare segment */
	if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
		DBGC ( image, "IMAGE %p could not prepare segment: %s\n",
		       image, strerror ( rc ) );
		return rc;
	}

	/* Copy image to segment */
	memcpy_user ( buffer, 0, image->data, 0, filesz );

	return 0;
}
Example #12
0
/**
 * Read and verify El Torito Boot Record Volume Descriptor
 *
 * @v image		El Torito file
 * @ret catalog_offset	Offset of Boot Catalog
 * @ret rc		Return status code
 */
static int eltorito_read_voldesc ( struct image *image,
				   unsigned long *catalog_offset ) {
	static const struct eltorito_vol_desc vol_desc_signature = {
		.record_indicator = 0,
		.iso9660_id = "CD001",
		.version = 1,
		.system_indicator = "EL TORITO SPECIFICATION",
	};
	struct eltorito_vol_desc vol_desc;

	/* Sanity check */
	if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) {
		DBGC ( image, "ElTorito %p too short\n", image );
		return -ENOEXEC;
	}

	/* Read and verify Boot Record Volume Descriptor */
	copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET,
			 sizeof ( vol_desc ) );
	if ( memcmp ( &vol_desc, &vol_desc_signature,
		      offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) {
		DBGC ( image, "ElTorito %p invalid Boot Record Volume "
		       "Descriptor\n", image );
		return -ENOEXEC;
	}
	*catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE );

	DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n",
	       image, *catalog_offset );

	return 0;
}

/**
 * Read and verify El Torito Boot Catalog
 *
 * @v image		El Torito file
 * @v catalog_offset	Offset of Boot Catalog
 * @ret boot_entry	El Torito boot entry
 * @ret rc		Return status code
 */
static int eltorito_read_catalog ( struct image *image,
				   unsigned long catalog_offset,
				   struct eltorito_boot_entry *boot_entry ) {
	struct eltorito_validation_entry validation_entry;

	/* Sanity check */
	if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) {
		DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n",
		       image, catalog_offset );
		return -ENOEXEC;
	}

	/* Read and verify the Validation Entry of the Boot Catalog */
	copy_from_user ( &validation_entry, image->data, catalog_offset,
			 sizeof ( validation_entry ) );
	if ( word_checksum ( &validation_entry,
			     sizeof ( validation_entry ) ) != 0 ) {
		DBGC ( image, "ElTorito %p bad Validation Entry checksum\n",
		       image );
		return -ENOEXEC;
	}

	/* Read and verify the Initial/Default entry */
	copy_from_user ( boot_entry, image->data,
			 ( catalog_offset + sizeof ( validation_entry ) ),
			 sizeof ( *boot_entry ) );
	if ( boot_entry->indicator != ELTORITO_BOOTABLE ) {
		DBGC ( image, "ElTorito %p not bootable\n", image );
		return -ENOEXEC;
	}
	if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) {
		DBGC ( image, "ElTorito %p cannot support media type %d\n",
		       image, boot_entry->media_type );
		return -ENOTSUP;
	}

	DBGC ( image, "ElTorito %p media type %d segment %04x\n",
	       image, boot_entry->media_type, boot_entry->load_segment );

	return 0;
}

/**
 * Load El Torito virtual disk image into memory
 *
 * @v image		El Torito file
 * @v boot_entry	El Torito boot entry
 * @ret rc		Return status code
 */
static int eltorito_load_disk ( struct image *image,
				struct eltorito_boot_entry *boot_entry ) {
	unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE );
	unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE );
	unsigned int load_segment;
	userptr_t buffer;
	int rc;

	/* Sanity check */
	if ( image->len < ( start + length ) ) {
		DBGC ( image, "ElTorito %p virtual disk lies outside image\n",
		       image );
		return -ENOEXEC;
	}
	DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n",
	       image, start, length );

	/* Calculate load address */
	load_segment = boot_entry->load_segment;
	buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) );

	/* Verify and prepare segment */
	if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) {
		DBGC ( image, "ElTorito %p could not prepare segment: %s\n",
		       image, strerror ( rc ) );
		return rc;
	}

	/* Copy image to segment */
	memcpy_user ( buffer, 0, image->data, start, length );

	return 0;
}
Example #13
0
/**
 * UDP WRITE
 *
 * @v pxenv_udp_write			Pointer to a struct s_PXENV_UDP_WRITE
 * @v s_PXENV_UDP_WRITE::ip		Destination IP address
 * @v s_PXENV_UDP_WRITE::gw		Relay agent IP address, or 0.0.0.0
 * @v s_PXENV_UDP_WRITE::src_port	Source UDP port, or 0
 * @v s_PXENV_UDP_WRITE::dst_port	Destination UDP port
 * @v s_PXENV_UDP_WRITE::buffer_size	Length of the UDP payload
 * @v s_PXENV_UDP_WRITE::buffer		Address of the UDP payload
 * @ret #PXENV_EXIT_SUCCESS		Packet was transmitted successfully
 * @ret #PXENV_EXIT_FAILURE		Packet could not be transmitted
 * @ret s_PXENV_UDP_WRITE::Status	PXE status code
 * @err #PXENV_STATUS_UDP_CLOSED	UDP connection is not open
 * @err #PXENV_STATUS_UNDI_TRANSMIT_ERROR Could not transmit packet
 *
 * Transmits a single UDP packet.  A valid IP and UDP header will be
 * prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer
 * should not contain precomputed IP and UDP headers, nor should it
 * contain space allocated for these headers.  The first byte of the
 * buffer will be transmitted as the first byte following the UDP
 * header.
 *
 * If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take
 * place.  See the relevant @ref pxe_routing "implementation note" for
 * more details.
 *
 * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used.
 *
 * You must have opened a UDP connection with pxenv_udp_open() before
 * calling pxenv_udp_write().
 *
 * On x86, you must set the s_PXE::StatusCallout field to a nonzero
 * value before calling this function in protected mode.  You cannot
 * call this function with a 32-bit stack segment.  (See the relevant
 * @ref pxe_x86_pmode16 "implementation note" for more details.)
 *
 * @note Etherboot currently ignores the s_PXENV_UDP_WRITE::gw
 * parameter.
 *
 */
PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
	struct sockaddr_in dest;
	struct xfer_metadata meta = {
		.src = ( struct sockaddr * ) &pxe_udp.local,
		.dest = ( struct sockaddr * ) &dest,
		.netdev = pxe_netdev,
	};
	size_t len;
	struct io_buffer *iobuf;
	userptr_t buffer;
	int rc;

	DBG ( "PXENV_UDP_WRITE" );

	/* Construct destination socket address */
	memset ( &dest, 0, sizeof ( dest ) );
	dest.sin_family = AF_INET;
	dest.sin_addr.s_addr = pxenv_udp_write->ip;
	dest.sin_port = pxenv_udp_write->dst_port;

	/* Set local (source) port.  PXE spec says source port is 2069
	 * if not specified.  Really, this ought to be set at UDP open
	 * time but hey, we didn't design this API.
	 */
	pxe_udp.local.sin_port = pxenv_udp_write->src_port;
	if ( ! pxe_udp.local.sin_port )
		pxe_udp.local.sin_port = htons ( 2069 );

	/* FIXME: we ignore the gateway specified, since we're
	 * confident of being able to do our own routing.  We should
	 * probably allow for multiple gateways.
	 */

	/* Allocate and fill data buffer */
	len = pxenv_udp_write->buffer_size;
	iobuf = xfer_alloc_iob ( &pxe_udp.xfer, len );
	if ( ! iobuf ) {
		DBG ( " out of memory\n" );
		pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
		return PXENV_EXIT_FAILURE;
	}
	buffer = real_to_user ( pxenv_udp_write->buffer.segment,
				pxenv_udp_write->buffer.offset );
	copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len );

	DBG ( " %04x:%04x+%x %d->%s:%d\n", pxenv_udp_write->buffer.segment,
	      pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size,
	      ntohs ( pxenv_udp_write->src_port ),
	      inet_ntoa ( dest.sin_addr ),
	      ntohs ( pxenv_udp_write->dst_port ) );
	
	/* Transmit packet */
	if ( ( rc = xfer_deliver ( &pxe_udp.xfer, iobuf, &meta ) ) != 0 ) {
		DBG ( "PXENV_UDP_WRITE could not transmit: %s\n",
		      strerror ( rc ) );
		pxenv_udp_write->Status = PXENV_STATUS ( rc );
		return PXENV_EXIT_FAILURE;
	}

	pxenv_udp_write->Status = PXENV_STATUS_SUCCESS;
	return PXENV_EXIT_SUCCESS;
}

/**
 * UDP READ
 *
 * @v pxenv_udp_read			Pointer to a struct s_PXENV_UDP_READ
 * @v s_PXENV_UDP_READ::dest_ip		Destination IP address, or 0.0.0.0
 * @v s_PXENV_UDP_READ::d_port		Destination UDP port, or 0
 * @v s_PXENV_UDP_READ::buffer_size	Size of the UDP payload buffer
 * @v s_PXENV_UDP_READ::buffer		Address of the UDP payload buffer
 * @ret #PXENV_EXIT_SUCCESS		A packet has been received
 * @ret #PXENV_EXIT_FAILURE		No packet has been received
 * @ret s_PXENV_UDP_READ::Status	PXE status code
 * @ret s_PXENV_UDP_READ::src_ip	Source IP address
 * @ret s_PXENV_UDP_READ::dest_ip	Destination IP address
 * @ret s_PXENV_UDP_READ::s_port	Source UDP port
 * @ret s_PXENV_UDP_READ::d_port	Destination UDP port
 * @ret s_PXENV_UDP_READ::buffer_size	Length of UDP payload
 * @err #PXENV_STATUS_UDP_CLOSED	UDP connection is not open
 * @err #PXENV_STATUS_FAILURE		No packet was ready to read
 *
 * Receive a single UDP packet.  This is a non-blocking call; if no
 * packet is ready to read, the call will return instantly with
 * s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE.
 *
 * If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to
 * any IP address will be accepted and may be returned to the caller.
 *
 * If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP
 * port will be accepted and may be returned to the caller.
 *
 * You must have opened a UDP connection with pxenv_udp_open() before
 * calling pxenv_udp_read().
 *
 * On x86, you must set the s_PXE::StatusCallout field to a nonzero
 * value before calling this function in protected mode.  You cannot
 * call this function with a 32-bit stack segment.  (See the relevant
 * @ref pxe_x86_pmode16 "implementation note" for more details.)
 *
 * @note The PXE specification (version 2.1) does not state that we
 * should fill in s_PXENV_UDP_READ::dest_ip and
 * s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program
 * expects us to do so, and will fail if we don't.
 *
 */
PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
	struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip };
	struct in_addr dest_ip;
	uint16_t d_port_wanted = pxenv_udp_read->d_port;
	uint16_t d_port;

	/* Try receiving a packet */
	pxe_udp.pxenv_udp_read = pxenv_udp_read;
	step();
	if ( pxe_udp.pxenv_udp_read ) {
		/* No packet received */
		DBG2 ( "PXENV_UDP_READ\n" );
		pxe_udp.pxenv_udp_read = NULL;
		goto no_packet;
	}
	dest_ip.s_addr = pxenv_udp_read->dest_ip;
	d_port = pxenv_udp_read->d_port;
	DBG ( "PXENV_UDP_READ" );

	/* Filter on destination address and/or port */
	if ( dest_ip_wanted.s_addr &&
	     ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) {
		DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) );
		DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) );
		goto no_packet;
	}
	if ( d_port_wanted && ( d_port_wanted != d_port ) ) {
		DBG ( " wrong port %d", htons ( d_port ) );
		DBG ( " (wanted %d)\n", htons ( d_port_wanted ) );
		goto no_packet;
	}

	DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment,
	      pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size,
	      inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) ));
	DBG ( "%d<-%s:%d\n",  ntohs ( pxenv_udp_read->s_port ),
	      inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ),
	      ntohs ( pxenv_udp_read->d_port ) );

	pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
	return PXENV_EXIT_SUCCESS;

 no_packet:
	pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
	return PXENV_EXIT_FAILURE;
}