diff --git a/src/include/pxe_api.h b/src/include/pxe_api.h index 60d5b30a..da19afb4 100644 --- a/src/include/pxe_api.h +++ b/src/include/pxe_api.h @@ -1631,4 +1631,26 @@ extern PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ); /** @} */ /* pxe */ +/** @page pxe_notes Etherboot PXE implementation notes + +@section pxe_x86_modes x86 processor mode restrictions + +On the x86 platform, different PXE API calls have different +restrictions on the processor modes (real or protected) that can be +used. See the individual API call descriptions for the restrictions +that apply to any particular call. + +@subsection pxe_x86_pmode16 Real mode, or protected-mode with 16-bit stack + +The PXE specification states that the API function can be called in +protected mode only if the s_PXE::StatusCallout field is set to a +non-zero value, and that the API function cannot be called with a +32-bit stack segment. + +Etherboot does not enforce either of these restrictions; they seem (as +with so much of the PXE specification) to be artifacts of the Intel +implementation. + +*/ + #endif /* PXE_API_H */ diff --git a/src/interface/pxe/pxe_udp.c b/src/interface/pxe/pxe_udp.c index f46971ea..ace1e30b 100644 --- a/src/interface/pxe/pxe_udp.c +++ b/src/interface/pxe/pxe_udp.c @@ -43,23 +43,20 @@ * pxenv_udp_read(). (If s_PXENV_UDP_OPEN::src_ip is 0.0.0.0, the * local station's IP address will remain unchanged.) * - * You can have multiple UDP connections open simultaneously (and - * even open concurrently with TFTP connections), provided that + * You can only have one open UDP connection at a time. You cannot + * have a UDP connection open at the same time as a TFTP connection. + * (This is not strictly true for Etherboot; see the relevant @ref + * pxe_note_udp "implementation note" for more details.) * - * - they all have the same local IP address, and + * 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.) * - * - you take the multiple connections into account when calling - * pxenv_udp_read(). - * - * On x86, you can call pxenv_udp_open() in real mode, 16-bit - * protected mode with a 16-bit stack segment, 16-bit protected mode - * with a 32-bit stack segment, or V86 mode. The pxe::StatusCallout - * field may be zero even in protected mode. - * - * @note The PXE specification states that you have only one UDP - * connection open at a time, and that you cannot have a UDP - * connection open simultaneously with a TFTP connection. Etherboot - * does not enforce this unnecessary restriction. + * @note The PXE specification does not make it clear whether the IP + * address supplied in s_PXENV_UDP_OPEN::src_ip should be used only + * for this UDP connection, or retained for all future communication. + * The latter seems more consistent with typical PXE stack behaviour. * */ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) { @@ -85,21 +82,19 @@ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) { * @ret s_PXENV_UDP_CLOSE::Status PXE status code * @err None - * - * Closes a UDP "connection" opened with pxenv_udp_open(). Since UDP - * is a connectionless protocol, this is a no-op. + * Closes a UDP "connection" opened with pxenv_udp_open(). * - * You can call pxenv_udp_close() even if there is another active UDP - * or TFTP connection, since it has no effect on anything. + * You can only have one open UDP connection at a time. You cannot + * have a UDP connection open at the same time as a TFTP connection. + * You cannot use pxenv_udp_close() to close a TFTP connection; use + * pxenv_tftp_close() instead. (This is not strictly true for + * Etherboot; see the relevant @ref pxe_note_udp "implementation note" + * for more details.) * - * You can call pxenv_udp_close() in real mode, 16-bit protected mode - * with a 16-bit stack segment, 16-bit protected mode with a 32-bit - * stack segment, or V86 mode. The pxe::StatusCallout field may be - * zero even in protected mode. - * - * @note The PXE specification states that you have only one UDP - * connection open at a time, and that you cannot have a UDP - * connection open simultaneously with a TFTP connection. Etherboot - * does not enforce this unnecessary restriction. + * 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.) * */ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) { @@ -119,7 +114,7 @@ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) { * @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 transmitter + * @ret #PXENV_EXIT_FAILURE Packet could not be transmitted * @ret s_PXENV_UDP_WRITE::Status PXE status code * @err #PXENV_STATUS_UNDI_INVALID_STATE NIC could not be initialised * @err #PXENV_STATUS_OUT_OF_RESOURCES Packet was too large to transmit @@ -138,20 +133,15 @@ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) { * * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used. * - * It is not necessary to call pxenv_udp_open() before using - * pxenv_udp_write(), unless you want to change the local station's IP - * address. pxenv_udp_write() can be called even if there is another - * active UDP or TFTP connection,. + * You must have opened a UDP connection with pxenv_udp_open() before + * calling pxenv_udp_write(). (This is not strictly true for + * Etherboot; see the relevant @ref pxe_note_udp "implementation note" + * for more details.) * - * You can call pxenv_udp_write() in real mode, 16-bit protected mode - * with a 16-bit stack segment, 16-bit protected mode with a 32-bit - * stack segment, or V86 mode. The pxe::StatusCallout field may be - * zero even in protected mode. - * - * @note The PXE specification states that you have only one UDP - * connection open at a time, and that you cannot have a UDP - * connection open simultaneously with a TFTP connection. Etherboot - * does not enforce this unnecessary restriction. + * 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.) * * @bug s_PXENV_UDP_WRITE::gw is ignored; the default routing table is * always used. @@ -278,26 +268,20 @@ static int await_pxe_udp ( int ival __unused, void *ptr, * 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. + * 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. + * port will be accepted and may be returned to the caller. * - * It is not necessary to call pxenv_udp_open() before using - * pxenv_udp_read(). pxenv_udp_read() can be called even if there is - * another active UDP or TFTP connection, but be aware that you might - * then receive (or cause to be lost) a packet belonging to another - * connection. + * You must have opened a UDP connection with pxenv_udp_open() before + * calling pxenv_udp_read(). (This is not strictly true for + * Etherboot; see the relevant @ref pxe_note_udp "implementation note" + * for more details.) * - * You can call pxenv_udp_read() in real mode, 16-bit protected mode - * with a 16-bit stack segment, 16-bit protected mode with a 32-bit - * stack segment, or V86 mode. The pxe::StatusCallout field may be - * zero even in protected mode. - * - * @note The PXE specification states that you have only one UDP - * connection open at a time, and that you cannot have a UDP - * connection open simultaneously with a TFTP connection. Etherboot - * does not enforce this unnecessary restriction. + * 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 @@ -319,3 +303,31 @@ PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *udp_read ) { udp_read->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; } + +/** @page pxe_notes PXE implementation notes + +@section pxe_note_udp The connectionless nature of UDP + +The PXE specification states that it is possible to have only one open +UDP or TFTP connection at any one time. Etherboot does not +rigourously enforce this restriction, on the UNIX principle that the +code should not prevent the user from doing stupid things, because +that would also prevent the user from doing clever things. Since UDP +is a connectionless protocol, it is perfectly possible to have +multiple concurrent UDP "connections" open, provided that you take the +multiplicity of connections into account when calling +pxenv_udp_read(). Similarly, there is no technical reason that +prevents you from calling pxenv_udp_write() in the middle of a TFTP +download. + +Etherboot will therefore never return error codes indicating "a +connection is already open", such as #PXENV_STATUS_UDP_OPEN. If you +want to have multiple concurrent connections, go for it (but don't +expect your perfectly sensible code to work with any other PXE stack). + +Since Etherboot treats UDP as the connectionless protocol that it +really is, pxenv_udp_close() is actually a no-op, and there is no need +to call pxenv_udp_open() before using pxenv_udp_write() or +pxenv_udp_read(). + +*/