iPXE
undinet.c File Reference

UNDI network device driver. More...

#include <string.h>
#include <unistd.h>
#include <byteswap.h>
#include <pxe.h>
#include <realmode.h>
#include <pic8259.h>
#include <biosint.h>
#include <pnpbios.h>
#include <basemem_packet.h>
#include <ipxe/io.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/if_ether.h>
#include <ipxe/ethernet.h>
#include <ipxe/pci.h>
#include <ipxe/profile.h>
#include <undi.h>
#include <undinet.h>

Go to the source code of this file.

Data Structures

struct  undi_nic
 An UNDI NIC. More...
struct  undinet_profiler
 A PXE API call breakdown profiler. More...
struct  undinet_irq_broken
 A device with broken support for generating interrupts. More...

Macros

#define EINFO_EPXECALL
#define EPXECALL(status)
#define UNDI_HACK_EB54   0x0001
 Work around Etherboot 5.4 bugs.
#define UNDI_INITIALIZE_RETRY_MAX   10
 Maximum number of times to retry PXENV_UNDI_INITIALIZE.
#define UNDI_INITIALIZE_RETRY_DELAY_MS   200
 Delay between retries of PXENV_UNDI_INITIALIZE.
#define UNDI_RX_QUOTA   4
 Maximum number of received packets per poll.
#define UNDI_RX_ALIGN   16
 Alignment of received frame payload.
#define undinet_params   __use_data16 ( undinet_params )
#define undinet_entry_point   __use_data16 ( undinet_entry_point )
#define RDTSC_IF_PROFILING   ""
#define undiisr_irq   __use_data16 ( undiisr_irq )
#define undiisr_imr   __use_data16 ( undiisr_imr )
#define undiisr_bit   __use_data16 ( undiisr_bit )
#define undiisr_rearm   __use_data16 ( undiisr_rearm )
#define undiisr_next_handler   __use_data16 ( undiisr_next_handler )
#define undiisr_trigger_count   __use_data16 ( undiisr_trigger_count )
#define undinet_tbd   __use_data16 ( undinet_tbd )
#define undinet_destaddr   __use_data16 ( undinet_destaddr )

Functions

 FILE_LICENCE (GPL2_OR_LATER)
static void undinet_close (struct net_device *netdev)
 Close NIC.
static union u_PXENV_ANY __bss16 (undinet_params)
 UNDI parameter block.
SEGOFF16_t __bss16 (undinet_entry_point)
 UNDI entry point.
static const char * undinet_function_name (unsigned int function)
 Name PXE API call.
static struct undinet_profiler * undinet_profiler (unsigned int function)
 Determine applicable profiler pair (for debugging).
static int undinet_call (struct undi_nic *undinic, unsigned int function, void *params, size_t params_len)
 Issue UNDI API call.
void undiisr (void)
 UNDI interrupt service routine.
uint8_t __data16 (undiisr_irq)
 IRQ number.
uint16_t __data16 (undiisr_imr)
 IRQ mask register.
uint8_t __data16 (undiisr_bit)
 IRQ mask bit.
uint8_t __data16 (undiisr_rearm)
 IRQ rearm flag.
struct segoff __data16 (undiisr_next_handler)
 IRQ chain vector.
volatile uint8_t __data16 (undiisr_trigger_count)=0
 IRQ trigger count.
static void undinet_hook_isr (unsigned int irq)
 Hook UNDI interrupt service routine.
static void undinet_unhook_isr (unsigned int irq)
 Unhook UNDI interrupt service routine.
static int undinet_isr_triggered (void)
 Test to see if UNDI ISR has been triggered.
static struct s_PXENV_UNDI_TBD __data16 (undinet_tbd)
 UNDI transmit buffer descriptor.
static uint8_t __data16_array (undinet_destaddr, [ETH_ALEN])
 UNDI transmit destination address.
static int undinet_transmit (struct net_device *netdev, struct io_buffer *iobuf)
 Transmit packet.
static void undinet_poll (struct net_device *netdev)
 Poll for received packets.
static int undinet_open (struct net_device *netdev)
 Open NIC.
static void undinet_irq (struct net_device *netdev, int enable)
 Enable/disable interrupts.
static int undinet_irq_is_broken (struct net_device *netdev)
 Check for devices with broken support for generating interrupts.
int undinet_probe (struct undi_device *undi, struct device *dev)
 Probe UNDI device.
void undinet_remove (struct undi_device *undi)
 Remove UNDI device.
 REQUIRING_SYMBOL (undinet_probe)
 REQUIRE_OBJECT (config_pci)

Variables

static struct profiler undinet_irq_profiler __profiler
 IRQ profiler.
static unsigned int last_trigger_count = 0
 Last observed trigger count.
static struct net_device_operations undinet_operations
 UNDI network device operations.
static const struct undinet_irq_broken undinet_irq_broken_list []
 List of devices with broken support for generating interrupts.

Detailed Description

UNDI network device driver.

Definition in file undinet.c.

Macro Definition Documentation

◆ EINFO_EPXECALL

#define EINFO_EPXECALL
Value:
"External PXE API error" )
#define __einfo_uniqify(einfo_base, uniq, desc)
Declare disambiguated error.
Definition errno.h:224
#define EINFO_EPLATFORM
Platform-generated base error.
Definition errno.h:735

Definition at line 60 of file undinet.c.

60#define EINFO_EPXECALL \
61 __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
62 "External PXE API error" )

◆ EPXECALL

#define EPXECALL ( status)
Value:
uint8_t status
Status.
Definition ena.h:5
#define EPLATFORM(einfo_base, platform,...)
Generate an error based on an external platform error code.
Definition errno.h:292
#define EINFO_EPXECALL
Definition undinet.c:60

Definition at line 63 of file undinet.c.

Referenced by undinet_call().

◆ UNDI_INITIALIZE_RETRY_MAX

#define UNDI_INITIALIZE_RETRY_MAX   10

Maximum number of times to retry PXENV_UNDI_INITIALIZE.

Definition at line 76 of file undinet.c.

Referenced by undinet_probe().

◆ UNDI_INITIALIZE_RETRY_DELAY_MS

#define UNDI_INITIALIZE_RETRY_DELAY_MS   200

Delay between retries of PXENV_UNDI_INITIALIZE.

Definition at line 79 of file undinet.c.

Referenced by undinet_probe().

◆ UNDI_RX_QUOTA

#define UNDI_RX_QUOTA   4

Maximum number of received packets per poll.

Definition at line 82 of file undinet.c.

Referenced by undinet_poll().

◆ UNDI_RX_ALIGN

#define UNDI_RX_ALIGN   16

Alignment of received frame payload.

Definition at line 85 of file undinet.c.

Referenced by undinet_poll().

◆ undinet_params

#define undinet_params   __use_data16 ( undinet_params )

Definition at line 96 of file undinet.c.

Referenced by __bss16(), and undinet_call().

◆ undinet_entry_point

#define undinet_entry_point   __use_data16 ( undinet_entry_point )

Definition at line 105 of file undinet.c.

Referenced by __bss16(), undinet_call(), undinet_probe(), and undinet_remove().

◆ RDTSC_IF_PROFILING

#define RDTSC_IF_PROFILING   ""

Definition at line 111 of file undinet.c.

Referenced by undinet_call().

◆ undiisr_irq

#define undiisr_irq   __use_data16 ( undiisr_irq )

Definition at line 374 of file undinet.c.

Referenced by __data16(), undinet_hook_isr(), and undinet_unhook_isr().

◆ undiisr_imr

#define undiisr_imr   __use_data16 ( undiisr_imr )

Definition at line 378 of file undinet.c.

Referenced by __data16(), and undinet_hook_isr().

◆ undiisr_bit

#define undiisr_bit   __use_data16 ( undiisr_bit )

Definition at line 382 of file undinet.c.

Referenced by __data16(), and undinet_hook_isr().

◆ undiisr_rearm

#define undiisr_rearm   __use_data16 ( undiisr_rearm )

Definition at line 386 of file undinet.c.

Referenced by __data16(), undinet_hook_isr(), and undinet_poll().

◆ undiisr_next_handler

#define undiisr_next_handler   __use_data16 ( undiisr_next_handler )

Definition at line 390 of file undinet.c.

Referenced by __data16(), undinet_hook_isr(), and undinet_unhook_isr().

◆ undiisr_trigger_count

#define undiisr_trigger_count   __use_data16 ( undiisr_trigger_count )

Definition at line 394 of file undinet.c.

Referenced by __data16(), and undinet_isr_triggered().

◆ undinet_tbd

#define undinet_tbd   __use_data16 ( undinet_tbd )

Definition at line 461 of file undinet.c.

Referenced by __data16(), and undinet_transmit().

◆ undinet_destaddr

#define undinet_destaddr   __use_data16 ( undinet_destaddr )

Definition at line 465 of file undinet.c.

Referenced by __data16_array(), and undinet_transmit().

Function Documentation

◆ FILE_LICENCE()

FILE_LICENCE ( GPL2_OR_LATER )

◆ undinet_close()

void undinet_close ( struct net_device * netdev)
static

Close NIC.

Parameters
netdevNet device

Definition at line 781 of file undinet.c.

781 {
782 struct undi_nic *undinic = netdev->priv;
783 struct s_PXENV_UNDI_ISR undi_isr;
784 struct s_PXENV_UNDI_CLOSE undi_close;
785 int rc;
786
787 /* Ensure ISR has exited cleanly */
788 while ( undinic->isr_processing ) {
789 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
790 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
791 sizeof ( undi_isr ) ) ) != 0 )
792 break;
793 switch ( undi_isr.FuncFlag ) {
796 /* Continue draining */
797 break;
798 default:
799 /* Stop processing */
800 undinic->isr_processing = 0;
801 break;
802 }
803 }
804
805 /* Close NIC */
806 undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
807 sizeof ( undi_close ) );
808
809 /* Disable interrupt and unhook ISR if applicable */
810 if ( undinic->irq ) {
811 disable_irq ( undinic->irq );
812 undinet_unhook_isr ( undinic->irq );
813 }
814
815 DBGC ( undinic, "UNDINIC %p closed\n", undinic );
816}
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
static struct net_device * netdev
Definition gdbudp.c:53
#define DBGC(...)
Definition compiler.h:530
#define PXENV_UNDI_CLOSE
PXE API function code for pxenv_undi_close().
Definition pxe_api.h:990
#define PXENV_UNDI_ISR_IN_GET_NEXT
Continue processing interrupt.
Definition pxe_api.h:1457
#define PXENV_UNDI_ISR_OUT_RECEIVE
A packet has been received.
Definition pxe_api.h:1467
#define PXENV_UNDI_ISR
PXE API function code for pxenv_undi_isr().
Definition pxe_api.h:1450
#define PXENV_UNDI_ISR_OUT_TRANSMIT
A packet transmission has completed.
Definition pxe_api.h:1465
Parameter block for pxenv_undi_close().
Definition pxe_api.h:993
Parameter block for pxenv_undi_isr().
Definition pxe_api.h:1479
An UNDI NIC.
Definition undinet.c:48
int isr_processing
Currently processing ISR.
Definition undinet.c:54
unsigned int irq
Assigned IRQ number.
Definition undinet.c:52
static int undinet_call(struct undi_nic *undinic, unsigned int function, void *params, size_t params_len)
Issue UNDI API call.
Definition undinet.c:278
static void undinet_unhook_isr(unsigned int irq)
Unhook UNDI interrupt service routine.
Definition undinet.c:422

References DBGC, s_PXENV_UNDI_ISR::FuncFlag, undi_nic::irq, undi_nic::isr_processing, netdev, PXENV_UNDI_CLOSE, PXENV_UNDI_ISR, PXENV_UNDI_ISR_IN_GET_NEXT, PXENV_UNDI_ISR_OUT_RECEIVE, PXENV_UNDI_ISR_OUT_TRANSMIT, rc, undinet_call(), and undinet_unhook_isr().

Referenced by undinet_open().

◆ __bss16() [1/2]

union u_PXENV_ANY __bss16 ( undinet_params )
static

UNDI parameter block.

Used as the parameter block for all UNDI API calls. Resides in base memory.

References __bss16, and undinet_params.

◆ __bss16() [2/2]

SEGOFF16_t __bss16 ( undinet_entry_point )

UNDI entry point.

Used as the indirection vector for all UNDI API calls. Resides in base memory.

References undinet_entry_point.

◆ undinet_function_name()

const char * undinet_function_name ( unsigned int function)
inlinestatic

Name PXE API call.

Parameters
functionAPI call number
Return values
nameAPI call name

Definition at line 189 of file undinet.c.

189 {
190 switch ( function ) {
191 case PXENV_START_UNDI:
192 return "PXENV_START_UNDI";
193 case PXENV_STOP_UNDI:
194 return "PXENV_STOP_UNDI";
196 return "PXENV_UNDI_STARTUP";
198 return "PXENV_UNDI_CLEANUP";
200 return "PXENV_UNDI_INITIALIZE";
202 return "PXENV_UNDI_RESET_ADAPTER";
204 return "PXENV_UNDI_SHUTDOWN";
205 case PXENV_UNDI_OPEN:
206 return "PXENV_UNDI_OPEN";
207 case PXENV_UNDI_CLOSE:
208 return "PXENV_UNDI_CLOSE";
210 return "PXENV_UNDI_TRANSMIT";
212 return "PXENV_UNDI_SET_MCAST_ADDRESS";
214 return "PXENV_UNDI_SET_STATION_ADDRESS";
216 return "PXENV_UNDI_SET_PACKET_FILTER";
218 return "PXENV_UNDI_GET_INFORMATION";
220 return "PXENV_UNDI_GET_STATISTICS";
222 return "PXENV_UNDI_CLEAR_STATISTICS";
224 return "PXENV_UNDI_INITIATE_DIAGS";
226 return "PXENV_UNDI_FORCE_INTERRUPT";
228 return "PXENV_UNDI_GET_MCAST_ADDRESS";
230 return "PXENV_UNDI_GET_NIC_TYPE";
232 return "PXENV_UNDI_GET_IFACE_INFO";
233 /*
234 * Duplicate case value; this is a bug in the PXE specification.
235 *
236 * case PXENV_UNDI_GET_STATE:
237 * return "PXENV_UNDI_GET_STATE";
238 */
239 case PXENV_UNDI_ISR:
240 return "PXENV_UNDI_ISR";
242 return "PXENV_GET_CACHED_INFO";
243 default:
244 return "UNKNOWN API CALL";
245 }
246}
uint8_t function
Function.
Definition edd.h:5
#define PXENV_GET_CACHED_INFO
PXE API function code for pxenv_get_cached_info().
Definition pxe_api.h:270
#define PXENV_START_UNDI
PXE API function code for pxenv_start_undi().
Definition pxe_api.h:435
#define PXENV_STOP_UNDI
PXE API function code for pxenv_stop_undi().
Definition pxe_api.h:505
#define PXENV_UNDI_CLEANUP
PXE API function code for pxenv_undi_cleanup().
Definition pxe_api.h:841
#define PXENV_UNDI_CLEAR_STATISTICS
PXE API function code for pxenv_undi_clear_statistics().
Definition pxe_api.h:1221
#define PXENV_UNDI_FORCE_INTERRUPT
PXE API function code for pxenv_undi_force_interrupt().
Definition pxe_api.h:1259
#define PXENV_UNDI_GET_IFACE_INFO
PXE API function code for pxenv_undi_get_iface_info().
Definition pxe_api.h:1359
#define PXENV_UNDI_GET_INFORMATION
PXE API function code for pxenv_undi_get_information().
Definition pxe_api.h:1155
#define PXENV_UNDI_GET_MCAST_ADDRESS
PXE API function code for pxenv_undi_get_mcast_address().
Definition pxe_api.h:1278
#define PXENV_UNDI_GET_NIC_TYPE
PXE API function code for pxenv_undi_get_nic_type().
Definition pxe_api.h:1299
#define PXENV_UNDI_GET_STATISTICS
PXE API function code for pxenv_undi_get_statistics().
Definition pxe_api.h:1198
#define PXENV_UNDI_INITIALIZE
PXE API function code for pxenv_undi_initialize().
Definition pxe_api.h:860
#define PXENV_UNDI_INITIATE_DIAGS
PXE API function code for pxenv_undi_initiate_diags().
Definition pxe_api.h:1240
#define PXENV_UNDI_OPEN
PXE API function code for pxenv_undi_open().
Definition pxe_api.h:941
#define PXENV_UNDI_RESET_ADAPTER
PXE API function code for pxenv_undi_reset_adapter().
Definition pxe_api.h:888
#define PXENV_UNDI_SET_MCAST_ADDRESS
PXE API function code for pxenv_undi_set_mcast_address().
Definition pxe_api.h:1084
#define PXENV_UNDI_SET_PACKET_FILTER
PXE API function code for pxenv_undi_set_packet_filter().
Definition pxe_api.h:1125
#define PXENV_UNDI_SET_STATION_ADDRESS
PXE API function code for pxenv_undi_set_station_address().
Definition pxe_api.h:1105
#define PXENV_UNDI_SHUTDOWN
PXE API function code for pxenv_undi_shutdown().
Definition pxe_api.h:922
#define PXENV_UNDI_STARTUP
PXE API function code for pxenv_undi_startup().
Definition pxe_api.h:815
#define PXENV_UNDI_TRANSMIT
PXE API function code for pxenv_undi_transmit().
Definition pxe_api.h:1009

References function, PXENV_GET_CACHED_INFO, PXENV_START_UNDI, PXENV_STOP_UNDI, PXENV_UNDI_CLEANUP, PXENV_UNDI_CLEAR_STATISTICS, PXENV_UNDI_CLOSE, PXENV_UNDI_FORCE_INTERRUPT, PXENV_UNDI_GET_IFACE_INFO, PXENV_UNDI_GET_INFORMATION, PXENV_UNDI_GET_MCAST_ADDRESS, PXENV_UNDI_GET_NIC_TYPE, PXENV_UNDI_GET_STATISTICS, PXENV_UNDI_INITIALIZE, PXENV_UNDI_INITIATE_DIAGS, PXENV_UNDI_ISR, PXENV_UNDI_OPEN, PXENV_UNDI_RESET_ADAPTER, PXENV_UNDI_SET_MCAST_ADDRESS, PXENV_UNDI_SET_PACKET_FILTER, PXENV_UNDI_SET_STATION_ADDRESS, PXENV_UNDI_SHUTDOWN, PXENV_UNDI_STARTUP, and PXENV_UNDI_TRANSMIT.

Referenced by undinet_call().

◆ undinet_profiler()

struct undinet_profiler * undinet_profiler ( unsigned int function)
static

Determine applicable profiler pair (for debugging).

Parameters
functionAPI call number
Return values
profilerProfiler

Definition at line 254 of file undinet.c.

254 {
255
256 /* Determine applicable profiler */
257 switch ( function ) {
259 return &undinet_tx_profiler;
260 case PXENV_UNDI_ISR:
261 return &undinet_isr_profiler;
262 case PXENV_UNKNOWN:
263 return &undinet_unknown_profiler;
264 default:
265 return &undinet_misc_profiler;
266 }
267}
#define PXENV_UNKNOWN
PXE API invalid function code.
Definition pxe.h:13

References function, PXENV_UNDI_ISR, PXENV_UNDI_TRANSMIT, and PXENV_UNKNOWN.

Referenced by undinet_call().

◆ undinet_call()

int undinet_call ( struct undi_nic * undinic,
unsigned int function,
void * params,
size_t params_len )
static

Issue UNDI API call.

Parameters
undinicUNDI NIC
functionAPI call number
paramsPXE parameter block
params_lenLength of PXE parameter block
Return values
rcReturn status code

Definition at line 278 of file undinet.c.

279 {
281 PXENV_EXIT_t exit;
284 uint32_t stopped;
286 int discard_D;
287 int rc;
288
289 /* Copy parameter block and entry point */
290 assert ( params_len <= sizeof ( undinet_params ) );
291 memcpy ( &undinet_params, params, params_len );
292
293 /* Call real-mode entry point. This calling convention will
294 * work with both the !PXE and the PXENV+ entry points.
295 */
296 profile_start ( &profiler->total );
297 __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
299 "pushl %%eax\n\t"
300 "pushw %%es\n\t"
301 "pushw %%di\n\t"
302 "pushw %%bx\n\t"
303 "lcall *undinet_entry_point\n\t"
304 "movw %%ax, %%bx\n\t"
306 "addw $6, %%sp\n\t"
307 "popl %%edx\n\t"
308 "popl %%ebp\n\t" /* gcc bug */ )
309 : "=a" ( stopped ), "=d" ( started ),
310 "=b" ( exit ), "=D" ( discard_D )
311 : "b" ( function ),
312 "D" ( __from_data16 ( &undinet_params ) )
313 : "ecx", "esi" );
314 profile_stop ( &profiler->total );
315 before = profile_started ( &profiler->total );
316 after = profile_stopped ( &profiler->total );
320 profile_stop_at ( &profiler->ext, stopped );
321 profile_start_at ( &profiler->r2p, stopped );
322 profile_stop_at ( &profiler->r2p, after );
323
324 /* Determine return status code based on PXENV_EXIT and
325 * PXENV_STATUS
326 */
327 rc = ( ( exit == PXENV_EXIT_SUCCESS ) ?
328 0 : -EPXECALL ( undinet_params.Status ) );
329
330 /* If anything goes wrong, print as much debug information as
331 * it's possible to give.
332 */
333 if ( rc != 0 ) {
334 SEGOFF16_t rm_params = {
335 .segment = rm_ds,
336 .offset = __from_data16 ( &undinet_params ),
337 };
338
339 DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic,
341 DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length "
342 "%#02zx, entry point at %04x:%04x\n", undinic,
343 rm_params.segment, rm_params.offset, params_len,
344 undinet_entry_point.segment,
345 undinet_entry_point.offset );
346 DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic );
347 DBGC_HDA ( undinic, rm_params, params, params_len );
348 DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic );
349 DBGC_HDA ( undinic, rm_params, &undinet_params, params_len );
350 }
351
352 /* Copy parameter block back */
353 memcpy ( params, &undinet_params, params_len );
354
355 return rc;
356}
__asm__ __volatile__("call *%9" :"=a"(result), "=c"(discard_ecx), "=d"(discard_edx) :"d"(0), "a"(code), "b"(0), "c"(in_phys), "D"(0), "S"(out_phys), "m"(hypercall))
unsigned int uint32_t
Definition stdint.h:12
void * discard_D
Definition bigint.h:146
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:61
#define DBGC_HDA(...)
Definition compiler.h:531
#define PXENV_EXIT_SUCCESS
No error occurred.
Definition pxe_types.h:45
UINT16_t PXENV_EXIT_t
A PXE exit code.
Definition pxe_types.h:44
static void profile_stop_at(struct profiler *profiler, unsigned long stopped)
Stop profiling.
Definition profile.h:146
static void profile_stop(struct profiler *profiler)
Stop profiling.
Definition profile.h:174
static unsigned long profile_started(struct profiler *profiler)
Get start time.
Definition profile.h:80
static void profile_start_at(struct profiler *profiler, unsigned long started)
Start profiling.
Definition profile.h:132
static void profile_start(struct profiler *profiler)
Start profiling.
Definition profile.h:161
static unsigned long profile_stopped(struct profiler *profiler)
Get stop time.
Definition profile.h:97
void * memcpy(void *dest, const void *src, size_t len) __nonnull
static int started
"startup() has been called" flag
Definition init.c:38
#define rm_ds
Definition libkir.h:39
#define REAL_CODE(asm_code_str)
Definition libkir.h:226
#define __from_data16(pointer)
Definition libkir.h:22
__asm__(".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" ".size private_key_data, ( . - private_key_data )\n\t" ".equ private_key_len, ( . - private_key_data )\n\t" ".previous\n\t")
char * strerror(int errno)
Retrieve string representation of error number.
Definition strerror.c:79
A data structure for storing profiling information.
Definition profile.h:27
A PXE API call breakdown profiler.
Definition undinet.c:123
int32_t after
Final microcode version.
Definition ucode.h:7
int32_t before
Initial microcode version.
Definition ucode.h:5
static struct undinet_profiler * undinet_profiler(unsigned int function)
Determine applicable profiler pair (for debugging).
Definition undinet.c:254
#define RDTSC_IF_PROFILING
Definition undinet.c:111
#define undinet_params
Definition undinet.c:96
#define EPXECALL(status)
Definition undinet.c:63
#define undinet_entry_point
Definition undinet.c:105
static const char * undinet_function_name(unsigned int function)
Name PXE API call.
Definition undinet.c:189

References __asm__(), __from_data16, __volatile__(), after, assert, before, DBGC, DBGC_HDA, discard_D, EPXECALL, function, memcpy(), profile_start(), profile_start_at(), profile_started(), profile_stop(), profile_stop_at(), profile_stopped(), PXENV_EXIT_SUCCESS, rc, RDTSC_IF_PROFILING, REAL_CODE, rm_ds, started, strerror(), undinet_entry_point, undinet_function_name(), undinet_params, and undinet_profiler().

Referenced by undinet_close(), undinet_open(), undinet_poll(), undinet_probe(), undinet_remove(), and undinet_transmit().

◆ undiisr()

void undiisr ( void )
extern

UNDI interrupt service routine.

The UNDI ISR increments a counter (trigger_count) and exits.

Referenced by undinet_hook_isr(), and undinet_unhook_isr().

◆ __data16() [1/7]

uint8_t __data16 ( undiisr_irq )

IRQ number.

References undiisr_irq.

◆ __data16() [2/7]

uint16_t __data16 ( undiisr_imr )

IRQ mask register.

References undiisr_imr.

◆ __data16() [3/7]

uint8_t __data16 ( undiisr_bit )

IRQ mask bit.

References undiisr_bit.

◆ __data16() [4/7]

uint8_t __data16 ( undiisr_rearm )

IRQ rearm flag.

References undiisr_rearm.

◆ __data16() [5/7]

struct segoff __data16 ( undiisr_next_handler )

IRQ chain vector.

References __data16, and undiisr_next_handler.

◆ __data16() [6/7]

volatile uint8_t __data16 ( undiisr_trigger_count )
pure virtual

IRQ trigger count.

References undiisr_trigger_count.

◆ undinet_hook_isr()

void undinet_hook_isr ( unsigned int irq)
static

Hook UNDI interrupt service routine.

Parameters
irqIRQ number

Definition at line 404 of file undinet.c.

404 {
405
406 assert ( irq <= IRQ_MAX );
407 assert ( undiisr_irq == 0 );
408
409 undiisr_irq = irq;
410 undiisr_imr = IMR_REG ( irq );
411 undiisr_bit = IMR_BIT ( irq );
412 undiisr_rearm = 0;
413 hook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ),
415}
unsigned long intptr_t
Definition stdint.h:21
void hook_bios_interrupt(unsigned int interrupt, unsigned int handler, struct segoff *chain_vector)
Hook INT vector.
Definition biosint.c:25
#define IMR_REG(x)
Definition pic8259.h:48
#define IRQ_INT(irq)
Definition pic8259.h:57
#define IRQ_MAX
Definition pic8259.h:60
#define IMR_BIT(x)
Definition pic8259.h:49
#define undiisr_bit
Definition undinet.c:382
#define undiisr_rearm
Definition undinet.c:386
#define undiisr_imr
Definition undinet.c:378
#define undiisr_next_handler
Definition undinet.c:390
void undiisr(void)
UNDI interrupt service routine.
#define undiisr_irq
Definition undinet.c:374

References assert, hook_bios_interrupt(), IMR_BIT, IMR_REG, IRQ_INT, IRQ_MAX, undiisr(), undiisr_bit, undiisr_imr, undiisr_irq, undiisr_next_handler, and undiisr_rearm.

Referenced by undinet_open().

◆ undinet_unhook_isr()

void undinet_unhook_isr ( unsigned int irq)
static

Unhook UNDI interrupt service routine.

Parameters
irqIRQ number

Definition at line 422 of file undinet.c.

422 {
423
424 assert ( irq <= IRQ_MAX );
425
428 undiisr_irq = 0;
429}
int unhook_bios_interrupt(unsigned int interrupt, unsigned int handler, struct segoff *chain_vector)
Unhook INT vector.
Definition biosint.c:70

References assert, IRQ_INT, IRQ_MAX, undiisr(), undiisr_irq, undiisr_next_handler, and unhook_bios_interrupt().

Referenced by undinet_close().

◆ undinet_isr_triggered()

int undinet_isr_triggered ( void )
static

Test to see if UNDI ISR has been triggered.

Return values
triggeredISR has been triggered since last check

Definition at line 436 of file undinet.c.

436 {
437 unsigned int this_trigger_count;
438
439 /* Read trigger_count. Do this only once; it is volatile */
440 this_trigger_count = undiisr_trigger_count;
441
442 if ( this_trigger_count == last_trigger_count ) {
443 /* Not triggered */
444 return 0;
445 } else {
446 /* Triggered */
447 last_trigger_count = this_trigger_count;
448 return 1;
449 }
450}
#define undiisr_trigger_count
Definition undinet.c:394
static unsigned int last_trigger_count
Last observed trigger count.
Definition undinet.c:397

References last_trigger_count, and undiisr_trigger_count.

Referenced by undinet_poll().

◆ __data16() [7/7]

struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd )
static

UNDI transmit buffer descriptor.

References __data16, and undinet_tbd.

◆ __data16_array()

uint8_t __data16_array ( undinet_destaddr )
static

UNDI transmit destination address.

References ETH_ALEN, and undinet_destaddr.

◆ undinet_transmit()

int undinet_transmit ( struct net_device * netdev,
struct io_buffer * iobuf )
static

Transmit packet.

Parameters
netdevNetwork device
iobufI/O buffer
Return values
rcReturn status code

Definition at line 474 of file undinet.c.

475 {
476 struct undi_nic *undinic = netdev->priv;
477 struct s_PXENV_UNDI_TRANSMIT undi_transmit;
478 const void *ll_dest;
479 const void *ll_source;
480 uint16_t net_proto;
481 unsigned int flags;
483 size_t len;
484 int rc;
485
486 /* Technically, we ought to make sure that the previous
487 * transmission has completed before we re-use the buffer.
488 * However, many PXE stacks (including at least some Intel PXE
489 * stacks and Etherboot 5.4) fail to generate TX completions.
490 * In practice this won't be a problem, since our TX datapath
491 * has a very low packet volume and we can get away with
492 * assuming that a TX will be complete by the time we want to
493 * transmit the next packet.
494 */
495
496 /* Pad to minimum Ethernet length, to work around underlying
497 * drivers that do not correctly handle frame padding
498 * themselves.
499 */
500 iob_pad ( iobuf, ETH_ZLEN );
501
502 /* Some PXE stacks are unable to cope with P_UNKNOWN, and will
503 * always try to prepend a link-layer header. Work around
504 * these stacks by stripping the existing link-layer header
505 * and allowing the PXE stack to (re)construct the link-layer
506 * header itself.
507 */
508 if ( ( rc = eth_pull ( netdev, iobuf, &ll_dest, &ll_source,
509 &net_proto, &flags ) ) != 0 ) {
510 DBGC ( undinic, "UNDINIC %p could not strip Ethernet header: "
511 "%s\n", undinic, strerror ( rc ) );
512 return rc;
513 }
514 memcpy ( undinet_destaddr, ll_dest, sizeof ( undinet_destaddr ) );
515 switch ( net_proto ) {
516 case htons ( ETH_P_IP ) :
517 protocol = P_IP;
518 break;
519 case htons ( ETH_P_ARP ) :
520 protocol = P_ARP;
521 break;
522 case htons ( ETH_P_RARP ) :
524 break;
525 default:
526 /* Unknown protocol; restore the original link-layer header */
527 iob_push ( iobuf, sizeof ( struct ethhdr ) );
529 break;
530 }
531
532 /* Copy packet to UNDI I/O buffer */
533 len = iob_len ( iobuf );
534 if ( len > sizeof ( basemem_packet ) )
535 len = sizeof ( basemem_packet );
536 memcpy ( &basemem_packet, iobuf->data, len );
537
538 /* Create PXENV_UNDI_TRANSMIT data structure */
539 memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
540 undi_transmit.Protocol = protocol;
541 undi_transmit.XmitFlag = ( ( flags & LL_BROADCAST ) ?
543 undi_transmit.DestAddr.segment = rm_ds;
544 undi_transmit.DestAddr.offset = __from_data16 ( &undinet_destaddr );
545 undi_transmit.TBD.segment = rm_ds;
546 undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
547
548 /* Create PXENV_UNDI_TBD data structure */
549 undinet_tbd.ImmedLength = len;
550 undinet_tbd.Xmit.segment = rm_ds;
551 undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
552
553 /* Issue PXE API call */
554 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT, &undi_transmit,
555 sizeof ( undi_transmit ) ) ) != 0 )
556 goto done;
557
558 /* Free I/O buffer */
559 netdev_tx_complete ( netdev, iobuf );
560 done:
561 return rc;
562}
unsigned short uint16_t
Definition stdint.h:11
unsigned char uint8_t
Definition stdint.h:10
#define basemem_packet
struct bofm_section_header done
Definition bofm_test.c:46
ring len
Length.
Definition dwmac.h:226
uint8_t flags
Flags.
Definition ena.h:7
int eth_pull(struct net_device *netdev __unused, struct io_buffer *iobuf, const void **ll_dest, const void **ll_source, uint16_t *net_proto, unsigned int *flags)
Remove Ethernet link-layer header.
Definition ethernet.c:102
#define P_RARP
RARP protocol.
Definition pxe_api.h:1014
#define P_IP
IP protocol.
Definition pxe_api.h:1012
#define XMT_DESTADDR
Unicast packet.
Definition pxe_api.h:1017
#define P_UNKNOWN
Media header already filled in.
Definition pxe_api.h:1011
#define XMT_BROADCAST
Broadcast packet.
Definition pxe_api.h:1018
#define P_ARP
ARP protocol.
Definition pxe_api.h:1013
#define ETH_ZLEN
Definition if_ether.h:11
#define ETH_P_RARP
Definition if_ether.h:21
#define ETH_P_IP
Definition if_ether.h:19
#define ETH_P_ARP
Definition if_ether.h:20
#define htons(value)
Definition byteswap.h:136
void * memset(void *dest, int character, size_t len) __nonnull
void iob_pad(struct io_buffer *iobuf, size_t min_len)
Pad I/O buffer.
Definition iobpad.c:50
#define iob_push(iobuf, len)
Definition iobuf.h:149
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition iobuf.h:220
#define LL_BROADCAST
Packet is a broadcast packet.
Definition netdevice.h:109
static void netdev_tx_complete(struct net_device *netdev, struct io_buffer *iobuf)
Complete network transmission.
Definition netdevice.h:770
uint16_t protocol
Protocol ID.
Definition stp.h:7
An Ethernet link-layer header.
Definition if_ether.h:32
void * data
Start of data.
Definition iobuf.h:113
Parameter block for pxenv_undi_transmit().
Definition pxe_api.h:1049
#define undinet_tbd
Definition undinet.c:461
#define undinet_destaddr
Definition undinet.c:465

References __from_data16, basemem_packet, io_buffer::data, DBGC, s_PXENV_UNDI_TRANSMIT::DestAddr, done, ETH_P_ARP, ETH_P_IP, ETH_P_RARP, eth_pull(), ETH_ZLEN, flags, htons, iob_len(), iob_pad(), iob_push, len, LL_BROADCAST, memcpy(), memset(), netdev, netdev_tx_complete(), P_ARP, P_IP, P_RARP, P_UNKNOWN, s_PXENV_UNDI_TRANSMIT::Protocol, protocol, PXENV_UNDI_TRANSMIT, rc, rm_ds, strerror(), s_PXENV_UNDI_TRANSMIT::TBD, undinet_call(), undinet_destaddr, undinet_tbd, s_PXENV_UNDI_TRANSMIT::XmitFlag, XMT_BROADCAST, and XMT_DESTADDR.

◆ undinet_poll()

void undinet_poll ( struct net_device * netdev)
static

Poll for received packets.

Parameters
netdevNetwork device

Fun, fun, fun. UNDI drivers don't use polling; they use interrupts. We therefore cheat and pretend that an interrupt has occurred every time undinet_poll() is called. This isn't too much of a hack; PCI devices share IRQs and so the first thing that a proper ISR should do is call PXENV_UNDI_ISR to determine whether or not the UNDI NIC generated the interrupt; there is no harm done by spurious calls to PXENV_UNDI_ISR. Similarly, we wouldn't be handling them any more rapidly than the usual rate of undinet_poll() being called even if we did implement a full ISR. So it should work. Ha!

Addendum (21/10/03). Some cards don't play nicely with this trick, so instead of doing it the easy way we have to go to all the hassle of installing a genuine interrupt service routine and dealing with the wonderful 8259 Programmable Interrupt Controller. Joy.

Addendum (10/07/07). When doing things such as iSCSI boot, in which we have to co-operate with a running OS, we can't get away with the "ISR-just-increments-a-counter-and-returns" trick at all, because it involves tying up the PIC for far too long, and other interrupt-dependent components (e.g. local disks) start breaking. We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR from within interrupt context in order to deassert the device interrupt, and sends EOI if applicable.

Definition at line 594 of file undinet.c.

594 {
595 struct undi_nic *undinic = netdev->priv;
596 struct s_PXENV_UNDI_ISR undi_isr;
597 struct io_buffer *iobuf = NULL;
598 unsigned int quota = UNDI_RX_QUOTA;
599 size_t len;
600 size_t reserve_len;
601 size_t frag_len;
602 size_t max_frag_len;
603 int rc;
604
605 if ( ! undinic->isr_processing ) {
606 /* Allow interrupt to occur. Do this even if
607 * interrupts are not known to be supported, since
608 * some cards erroneously report that they do not
609 * support interrupts.
610 */
611 if ( ! undinet_isr_triggered() ) {
612
613 /* Rearm interrupt if needed */
614 if ( undiisr_rearm ) {
615 undiisr_rearm = 0;
616 assert ( undinic->irq != 0 );
617 enable_irq ( undinic->irq );
618 }
619
620 /* Allow interrupt to occur */
621 profile_start ( &undinet_irq_profiler );
622 __asm__ __volatile__ ( "sti\n\t"
623 "nop\n\t"
624 "nop\n\t"
625 "cli\n\t" );
626 profile_stop ( &undinet_irq_profiler );
627
628 /* If interrupts are known to be supported,
629 * then do nothing on this poll; wait for the
630 * interrupt to be triggered.
631 */
632 if ( undinic->irq_supported )
633 return;
634 }
635
636 /* Start ISR processing */
637 undinic->isr_processing = 1;
638 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
639 } else {
640 /* Continue ISR processing */
641 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
642 }
643
644 /* Run through the ISR loop */
645 while ( quota ) {
646 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
647 sizeof ( undi_isr ) ) ) != 0 ) {
649 break;
650 }
651 switch ( undi_isr.FuncFlag ) {
653 /* We don't care about transmit completions */
654 break;
656 /* Packet fragment received */
657 profile_start ( &undinet_rx_profiler );
658 len = undi_isr.FrameLength;
659 frag_len = undi_isr.BufferLength;
660 reserve_len = ( -undi_isr.FrameHeaderLength &
661 ( UNDI_RX_ALIGN - 1 ) );
662 if ( ( len == 0 ) || ( len < frag_len ) ) {
663 /* Don't laugh. VMWare does it. */
664 DBGC ( undinic, "UNDINIC %p reported insane "
665 "fragment (%zd of %zd bytes)\n",
666 undinic, frag_len, len );
668 break;
669 }
670 if ( ! iobuf ) {
671 iobuf = alloc_iob ( reserve_len + len );
672 if ( ! iobuf ) {
673 DBGC ( undinic, "UNDINIC %p could not "
674 "allocate %zd bytes for RX "
675 "buffer\n", undinic, len );
676 /* Fragment will be dropped */
678 goto done;
679 }
680 iob_reserve ( iobuf, reserve_len );
681 }
682 max_frag_len = iob_tailroom ( iobuf );
683 if ( frag_len > max_frag_len ) {
684 DBGC ( undinic, "UNDINIC %p fragment too big "
685 "(%zd+%zd does not fit into %zd)\n",
686 undinic, iob_len ( iobuf ), frag_len,
687 ( iob_len ( iobuf ) + max_frag_len ) );
688 frag_len = max_frag_len;
689 }
690 copy_from_real ( iob_put ( iobuf, frag_len ),
691 undi_isr.Frame.segment,
692 undi_isr.Frame.offset, frag_len );
693 if ( iob_len ( iobuf ) == len ) {
694 /* Whole packet received; deliver it */
695 netdev_rx ( netdev, iob_disown ( iobuf ) );
696 quota--;
697 /* Etherboot 5.4 fails to return all packets
698 * under mild load; pretend it retriggered.
699 */
700 if ( undinic->hacks & UNDI_HACK_EB54 )
702 }
703 profile_stop ( &undinet_rx_profiler );
704 break;
706 /* Processing complete */
707 undinic->isr_processing = 0;
708 goto done;
709 default:
710 /* Should never happen. VMWare does it routinely. */
711 DBGC ( undinic, "UNDINIC %p ISR returned invalid "
712 "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
713 undinic->isr_processing = 0;
714 goto done;
715 }
716 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
717 }
718
719 done:
720 if ( iobuf ) {
721 DBGC ( undinic, "UNDINIC %p returned incomplete packet "
722 "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
723 ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
724 netdev_rx_err ( netdev, iobuf, -EINVAL );
725 }
726}
#define NULL
NULL pointer (VOID *).
Definition Base.h:321
#define EINVAL
Invalid argument.
Definition errno.h:472
#define ENOMEM
Not enough space.
Definition errno.h:578
#define PXENV_UNDI_ISR_OUT_DONE
Finished processing interrupt.
Definition pxe_api.h:1463
#define PXENV_UNDI_ISR_IN_PROCESS
Start processing interrupt.
Definition pxe_api.h:1455
#define UNDI_HACK_EB54
Work around Etherboot 5.4 bugs.
Definition undinet.c:71
struct io_buffer * alloc_iob(size_t len)
Allocate I/O buffer.
Definition iobuf.c:131
#define iob_put(iobuf, len)
Definition iobuf.h:185
#define iob_disown(iobuf)
Disown an I/O buffer.
Definition iobuf.h:277
#define iob_reserve(iobuf, len)
Definition iobuf.h:132
static size_t iob_tailroom(struct io_buffer *iobuf)
Calculate available space at end of an I/O buffer.
Definition iobuf.h:240
#define copy_from_real
Definition libkir.h:79
void netdev_rx(struct net_device *netdev, struct io_buffer *iobuf)
Add packet to receive queue.
Definition netdevice.c:549
void netdev_rx_err(struct net_device *netdev, struct io_buffer *iobuf, int rc)
Discard received packet.
Definition netdevice.c:587
A persistent I/O buffer.
Definition iobuf.h:98
int hacks
Bug workarounds.
Definition undinet.c:56
int irq_supported
Device supports IRQs.
Definition undinet.c:50
#define UNDI_RX_QUOTA
Maximum number of received packets per poll.
Definition undinet.c:82
static int undinet_isr_triggered(void)
Test to see if UNDI ISR has been triggered.
Definition undinet.c:436
#define UNDI_RX_ALIGN
Alignment of received frame payload.
Definition undinet.c:85

References __asm__(), __volatile__(), alloc_iob(), assert, s_PXENV_UNDI_ISR::BufferLength, copy_from_real, DBGC, done, EINVAL, ENOMEM, s_PXENV_UNDI_ISR::Frame, s_PXENV_UNDI_ISR::FrameHeaderLength, s_PXENV_UNDI_ISR::FrameLength, s_PXENV_UNDI_ISR::FuncFlag, undi_nic::hacks, iob_disown, iob_len(), iob_put, iob_reserve, iob_tailroom(), undi_nic::irq, undi_nic::irq_supported, undi_nic::isr_processing, last_trigger_count, len, netdev, netdev_rx(), netdev_rx_err(), NULL, profile_start(), profile_stop(), PXENV_UNDI_ISR, PXENV_UNDI_ISR_IN_GET_NEXT, PXENV_UNDI_ISR_IN_PROCESS, PXENV_UNDI_ISR_OUT_DONE, PXENV_UNDI_ISR_OUT_RECEIVE, PXENV_UNDI_ISR_OUT_TRANSMIT, rc, UNDI_HACK_EB54, UNDI_RX_ALIGN, UNDI_RX_QUOTA, undiisr_rearm, undinet_call(), and undinet_isr_triggered().

◆ undinet_open()

int undinet_open ( struct net_device * netdev)
static

Open NIC.

Parameters
netdevNet device
Return values
rcReturn status code

Definition at line 734 of file undinet.c.

734 {
735 struct undi_nic *undinic = netdev->priv;
736 struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
737 struct s_PXENV_UNDI_OPEN undi_open;
738 int rc;
739
740 /* Hook interrupt service routine and enable interrupt if applicable */
741 if ( undinic->irq ) {
742 undinet_hook_isr ( undinic->irq );
743 enable_irq ( undinic->irq );
744 send_eoi ( undinic->irq );
745 }
746
747 /* Set station address. Required for some PXE stacks; will
748 * spuriously fail on others. Ignore failures. We only ever
749 * use it to set the MAC address to the card's permanent value
750 * anyway.
751 */
752 memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
753 sizeof ( undi_set_address.StationAddress ) );
755 &undi_set_address, sizeof ( undi_set_address ) );
756
757 /* Open NIC. We ask for promiscuous operation, since it's the
758 * only way to ask for all multicast addresses. On any
759 * switched network, it shouldn't really make a difference to
760 * performance.
761 */
762 memset ( &undi_open, 0, sizeof ( undi_open ) );
763 undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
764 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
765 sizeof ( undi_open ) ) ) != 0 )
766 goto err;
767
768 DBGC ( undinic, "UNDINIC %p opened\n", undinic );
769 return 0;
770
771 err:
773 return rc;
774}
#define FLTR_PRMSCS
Accept all packets; listen in promiscuous mode.
Definition pxe_api.h:953
#define FLTR_BRDCST
Accept broadcast packets.
Definition pxe_api.h:951
#define FLTR_DIRECTED
Accept "directed" packets.
Definition pxe_api.h:949
void send_eoi(unsigned int irq)
Send End-Of-Interrupt to the PIC.
Definition pic8259.c:65
Parameter block for pxenv_undi_open().
Definition pxe_api.h:958
Parameter block for pxenv_undi_set_station_address().
Definition pxe_api.h:1108
static void undinet_hook_isr(unsigned int irq)
Hook UNDI interrupt service routine.
Definition undinet.c:404
static void undinet_close(struct net_device *netdev)
Close NIC.
Definition undinet.c:781

References DBGC, FLTR_BRDCST, FLTR_DIRECTED, FLTR_PRMSCS, undi_nic::irq, memcpy(), memset(), netdev, s_PXENV_UNDI_OPEN::PktFilter, PXENV_UNDI_OPEN, PXENV_UNDI_SET_STATION_ADDRESS, rc, send_eoi(), s_PXENV_UNDI_SET_STATION_ADDRESS::StationAddress, undinet_call(), undinet_close(), and undinet_hook_isr().

◆ undinet_irq()

void undinet_irq ( struct net_device * netdev,
int enable )
static

Enable/disable interrupts.

Parameters
netdevNet device
enableInterrupts should be enabled

Definition at line 824 of file undinet.c.

824 {
825 struct undi_nic *undinic = netdev->priv;
826
827 /* Cannot support interrupts yet */
828 DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
829 undinic, ( enable ? "enable" : "disable" ) );
830}

References DBGC, and netdev.

◆ undinet_irq_is_broken()

int undinet_irq_is_broken ( struct net_device * netdev)
static

Check for devices with broken support for generating interrupts.

Parameters
netdevNet device
Return values
irq_is_brokenInterrupt support is broken; no interrupts are generated

Definition at line 880 of file undinet.c.

880 {
881 struct undi_nic *undinic = netdev->priv;
882 struct device_description *desc = &netdev->dev->desc;
883 const struct undinet_irq_broken *broken;
884 struct pci_device pci;
885 uint16_t subsys_vendor;
886 uint16_t subsys;
887 unsigned int i;
888
889 /* Ignore non-PCI devices */
890 if ( desc->bus_type != BUS_TYPE_PCI )
891 return 0;
892
893 /* Read subsystem IDs */
894 pci_init ( &pci, desc->location );
895 pci_read_config_word ( &pci, PCI_SUBSYSTEM_VENDOR_ID, &subsys_vendor );
896 pci_read_config_word ( &pci, PCI_SUBSYSTEM_ID, &subsys );
897
898 /* Check for a match against the broken device list */
899 for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) /
900 sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) {
901 broken = &undinet_irq_broken_list[i];
902 if ( ( broken->pci_vendor == desc->vendor ) &&
903 ( broken->pci_device == desc->device ) &&
904 ( ( broken->pci_subsys_vendor == subsys_vendor ) ||
905 ( broken->pci_subsys_vendor == PCI_ANY_ID ) ) &&
906 ( ( broken->pci_subsys == subsys ) ||
907 ( broken->pci_subsys == PCI_ANY_ID ) ) ) {
908 DBGC ( undinic, "UNDINIC %p %04x:%04x subsys "
909 "%04x:%04x has broken interrupts\n",
910 undinic, desc->vendor, desc->device,
911 subsys_vendor, subsys );
912 return 1;
913 }
914 }
915
916 /* Check for a PCI Express capability. Given the number of
917 * issues found with legacy INTx emulation on PCIe systems, we
918 * assume that there is a high chance of interrupts not
919 * working on any PCIe device.
920 */
921 if ( pci_find_capability ( &pci, PCI_CAP_ID_EXP ) ) {
922 DBGC ( undinic, "UNDINIC %p is PCI Express: assuming "
923 "interrupts are unreliable\n", undinic );
924 return 1;
925 }
926
927 return 0;
928}
#define BUS_TYPE_PCI
PCI bus type.
Definition device.h:44
struct ena_llq_option desc
Descriptor counts.
Definition ena.h:9
int pci_read_config_word(struct pci_device *pci, unsigned int where, uint16_t *value)
Read 16-bit word from PCI configuration space.
#define PCI_ANY_ID
Match-anything ID.
Definition pci.h:190
#define PCI_SUBSYSTEM_ID
PCI subsystem ID.
Definition pci.h:79
static void pci_init(struct pci_device *pci, unsigned int busdevfn)
Initialise PCI device.
Definition pci.h:345
#define PCI_CAP_ID_EXP
PCI Express.
Definition pci.h:98
#define PCI_SUBSYSTEM_VENDOR_ID
PCI subsystem vendor ID.
Definition pci.h:76
int pci_find_capability(struct pci_device *pci, int cap)
Look for a PCI capability.
Definition pciextra.c:39
A hardware device description.
Definition device.h:20
A PCI device.
Definition pci.h:214
A device with broken support for generating interrupts.
Definition undinet.c:842
uint16_t pci_subsys_vendor
PCI subsystem vendor ID.
Definition undinet.c:848
uint16_t pci_subsys
PCI subsystem ID.
Definition undinet.c:850
uint16_t pci_device
PCI device ID.
Definition undinet.c:846
uint16_t pci_vendor
PCI vendor ID.
Definition undinet.c:844
static const struct undinet_irq_broken undinet_irq_broken_list[]
List of devices with broken support for generating interrupts.
Definition undinet.c:864

References BUS_TYPE_PCI, DBGC, desc, netdev, PCI_ANY_ID, PCI_CAP_ID_EXP, undinet_irq_broken::pci_device, pci_find_capability(), pci_init(), pci_read_config_word(), undinet_irq_broken::pci_subsys, undinet_irq_broken::pci_subsys_vendor, PCI_SUBSYSTEM_ID, PCI_SUBSYSTEM_VENDOR_ID, undinet_irq_broken::pci_vendor, and undinet_irq_broken_list.

Referenced by undinet_probe().

◆ undinet_probe()

int undinet_probe ( struct undi_device * undi,
struct device * dev )

Probe UNDI device.

Parameters
undiUNDI device
devUnderlying generic device
Return values
rcReturn status code

Definition at line 937 of file undinet.c.

937 {
938 struct net_device *netdev;
939 struct undi_nic *undinic;
940 struct s_PXENV_START_UNDI start_undi;
941 struct s_PXENV_UNDI_STARTUP undi_startup;
942 struct s_PXENV_UNDI_INITIALIZE undi_init;
943 struct s_PXENV_UNDI_GET_INFORMATION undi_info;
944 struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
945 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
946 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
947 struct s_PXENV_STOP_UNDI stop_undi;
948 unsigned int retry;
949 int rc;
950
951 /* Allocate net device */
952 netdev = alloc_etherdev ( sizeof ( *undinic ) );
953 if ( ! netdev )
954 return -ENOMEM;
956 undinic = netdev->priv;
957 undi_set_drvdata ( undi, netdev );
958 netdev->dev = dev;
959 memset ( undinic, 0, sizeof ( *undinic ) );
961 DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
962
963 /* Hook in UNDI stack */
964 if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
965 memset ( &start_undi, 0, sizeof ( start_undi ) );
966 start_undi.AX = undi->pci_busdevfn;
967 start_undi.BX = undi->isapnp_csn;
968 start_undi.DX = undi->isapnp_read_port;
969 start_undi.ES = BIOS_SEG;
970 start_undi.DI = find_pnp_bios();
971 if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
972 &start_undi,
973 sizeof ( start_undi ) ) ) != 0 )
974 goto err_start_undi;
975 }
976 undi->flags |= UNDI_FL_STARTED;
977
978 /* Bring up UNDI stack */
979 if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
980 memset ( &undi_startup, 0, sizeof ( undi_startup ) );
981 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
982 &undi_startup,
983 sizeof ( undi_startup ) ) ) != 0 )
984 goto err_undi_startup;
985 /* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail
986 * due to a transient condition (e.g. media test
987 * failing because the link has only just come out of
988 * reset). We may therefore need to retry this call
989 * several times.
990 */
991 for ( retry = 0 ; ; ) {
992 memset ( &undi_init, 0, sizeof ( undi_init ) );
993 if ( ( rc = undinet_call ( undinic,
995 &undi_init,
996 sizeof ( undi_init ) ) ) ==0)
997 break;
998 if ( ++retry > UNDI_INITIALIZE_RETRY_MAX )
999 goto err_undi_initialize;
1000 DBGC ( undinic, "UNDINIC %p retrying "
1001 "PXENV_UNDI_INITIALIZE (retry %d)\n",
1002 undinic, retry );
1003 /* Delay to allow link to settle if necessary */
1005 }
1006 }
1007 undi->flags |= UNDI_FL_INITIALIZED;
1008
1009 /* Get device information */
1010 memset ( &undi_info, 0, sizeof ( undi_info ) );
1011 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
1012 &undi_info, sizeof ( undi_info ) ) ) != 0 )
1013 goto err_undi_get_information;
1014 memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
1015 memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN );
1016 undinic->irq = undi_info.IntNumber;
1017 if ( undinic->irq > IRQ_MAX ) {
1018 DBGC ( undinic, "UNDINIC %p ignoring invalid IRQ %d\n",
1019 undinic, undinic->irq );
1020 undinic->irq = 0;
1021 }
1022 DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
1023 undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
1024 if ( undinic->irq ) {
1025 /* Sanity check - prefix should have disabled the IRQ */
1026 assert ( ! irq_enabled ( undinic->irq ) );
1027 }
1028
1029 /* Get interface information */
1030 memset ( &undi_iface, 0, sizeof ( undi_iface ) );
1031 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
1032 &undi_iface, sizeof ( undi_iface ) ) ) != 0 )
1033 goto err_undi_get_iface_info;
1034 DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
1035 undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
1036 undi_iface.ServiceFlags );
1037 if ( ( undi_iface.ServiceFlags & SUPPORTED_IRQ ) &&
1038 ( undinic->irq != 0 ) ) {
1039 undinic->irq_supported = 1;
1040 }
1041 DBGC ( undinic, "UNDINIC %p using %s mode\n", undinic,
1042 ( undinic->irq_supported ? "interrupt" : "polling" ) );
1043 if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
1044 sizeof ( undi_iface.IfaceType ) ) == 0 ) {
1045 DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
1046 undinic );
1047 undinic->hacks |= UNDI_HACK_EB54;
1048 }
1049 if ( undinet_irq_is_broken ( netdev ) ) {
1050 DBGC ( undinic, "UNDINIC %p forcing polling mode due to "
1051 "broken interrupts\n", undinic );
1052 undinic->irq_supported = 0;
1053 }
1054
1055 /* Register network device */
1056 if ( ( rc = register_netdev ( netdev ) ) != 0 )
1057 goto err_register;
1058
1059 /* Mark as link up; we don't handle link state */
1061
1062 DBGC ( undinic, "UNDINIC %p added\n", undinic );
1063 return 0;
1064
1065 err_register:
1066 err_undi_get_iface_info:
1067 err_undi_get_information:
1068 err_undi_initialize:
1069 /* Shut down UNDI stack */
1070 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
1071 undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
1072 sizeof ( undi_shutdown ) );
1073 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
1074 undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
1075 sizeof ( undi_cleanup ) );
1076 undi->flags &= ~UNDI_FL_INITIALIZED;
1077 err_undi_startup:
1078 /* Unhook UNDI stack */
1079 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
1080 undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
1081 sizeof ( stop_undi ) );
1082 undi->flags &= ~UNDI_FL_STARTED;
1083 err_start_undi:
1085 netdev_put ( netdev );
1086 undi_set_drvdata ( undi, NULL );
1087 return rc;
1088}
struct net_device * alloc_etherdev(size_t priv_size)
Allocate Ethernet device.
Definition ethernet.c:266
const char * eth_ntoa(const void *ll_addr)
Transcribe Ethernet address.
Definition ethernet.c:177
#define SUPPORTED_IRQ
Interrupt Request supported.
Definition pxe_api.h:1378
#define ETH_ALEN
Definition if_ether.h:9
int register_netdev(struct net_device *netdev)
Register network device.
Definition netdevice.c:760
static void netdev_link_up(struct net_device *netdev)
Mark network device as having link up.
Definition netdevice.h:792
static void netdev_init(struct net_device *netdev, struct net_device_operations *op)
Initialise a network device.
Definition netdevice.h:522
static void netdev_nullify(struct net_device *netdev)
Stop using a network device.
Definition netdevice.h:535
static void netdev_put(struct net_device *netdev)
Drop reference to network device.
Definition netdevice.h:579
int find_pnp_bios(void)
Locate Plug-and-Play BIOS.
Definition pnpbios.c:101
#define BIOS_SEG
Definition pnpbios.h:13
int strncmp(const char *first, const char *second, size_t max)
Compare strings.
Definition string.c:187
A network device.
Definition netdevice.h:353
Parameter block for pxenv_start_undi().
Definition pxe_api.h:438
Parameter block for pxenv_stop_undi().
Definition pxe_api.h:508
Parameter block for pxenv_undi_cleanup().
Definition pxe_api.h:844
Parameter block for pxenv_undi_get_iface_info().
Definition pxe_api.h:1381
Parameter block for pxenv_undi_get_information().
Definition pxe_api.h:1166
Parameter block for pxenv_undi_initialize().
Definition pxe_api.h:863
Parameter block for pxenv_undi_shutdown().
Definition pxe_api.h:925
Parameter block for pxenv_undi_startup().
Definition pxe_api.h:825
UINT16_t pci_busdevfn
PCI bus:dev.fn, or UNDI_NO_PCI_BUSDEVFN.
Definition undi.h:34
UINT16_t isapnp_csn
ISAPnP card select number, or UNDI_NO_ISAPNP_CSN.
Definition undi.h:36
UINT16_t isapnp_read_port
ISAPnP read port, or UNDI_NO_ISAPNP_READ_PORT.
Definition undi.h:38
UINT16_t flags
Flags.
Definition undi.h:54
SEGOFF16_t entry
Entry point.
Definition undi.h:28
void mdelay(unsigned long msecs)
Delay for a fixed number of milliseconds.
Definition timer.c:79
static void undi_set_drvdata(struct undi_device *undi, void *priv)
Set UNDI driver-private data.
Definition undi.h:70
#define UNDI_FL_INITIALIZED
UNDI flag: UNDI_STARTUP and UNDI_INITIALIZE have been called.
Definition undi.h:99
#define UNDI_FL_STARTED
UNDI flag: START_UNDI has been called.
Definition undi.h:96
static struct net_device_operations undinet_operations
UNDI network device operations.
Definition undinet.c:833
#define UNDI_INITIALIZE_RETRY_DELAY_MS
Delay between retries of PXENV_UNDI_INITIALIZE.
Definition undinet.c:79
#define UNDI_INITIALIZE_RETRY_MAX
Maximum number of times to retry PXENV_UNDI_INITIALIZE.
Definition undinet.c:76
static int undinet_irq_is_broken(struct net_device *netdev)
Check for devices with broken support for generating interrupts.
Definition undinet.c:880

References alloc_etherdev(), assert, s_PXENV_START_UNDI::AX, BIOS_SEG, s_PXENV_START_UNDI::BX, s_PXENV_UNDI_GET_INFORMATION::CurrentNodeAddress, DBGC, s_PXENV_START_UNDI::DI, s_PXENV_START_UNDI::DX, ENOMEM, undi_device::entry, s_PXENV_START_UNDI::ES, ETH_ALEN, eth_ntoa(), find_pnp_bios(), undi_device::flags, undi_nic::hacks, s_PXENV_UNDI_GET_IFACE_INFO::IfaceType, s_PXENV_UNDI_GET_INFORMATION::IntNumber, undi_nic::irq, IRQ_MAX, undi_nic::irq_supported, undi_device::isapnp_csn, undi_device::isapnp_read_port, s_PXENV_UNDI_GET_IFACE_INFO::LinkSpeed, mdelay(), memcpy(), memset(), netdev, netdev_init(), netdev_link_up(), netdev_nullify(), netdev_put(), NULL, undi_device::pci_busdevfn, s_PXENV_UNDI_GET_INFORMATION::PermNodeAddress, PXENV_START_UNDI, PXENV_STOP_UNDI, PXENV_UNDI_CLEANUP, PXENV_UNDI_GET_IFACE_INFO, PXENV_UNDI_GET_INFORMATION, PXENV_UNDI_INITIALIZE, PXENV_UNDI_SHUTDOWN, PXENV_UNDI_STARTUP, rc, register_netdev(), s_PXENV_UNDI_GET_IFACE_INFO::ServiceFlags, strncmp(), SUPPORTED_IRQ, UNDI_FL_INITIALIZED, UNDI_FL_STARTED, UNDI_HACK_EB54, UNDI_INITIALIZE_RETRY_DELAY_MS, UNDI_INITIALIZE_RETRY_MAX, undi_set_drvdata(), undinet_call(), undinet_entry_point, undinet_irq_is_broken(), and undinet_operations.

Referenced by REQUIRING_SYMBOL(), undibus_probe(), and undipci_probe().

◆ undinet_remove()

void undinet_remove ( struct undi_device * undi)

Remove UNDI device.

Parameters
undiUNDI device

Definition at line 1095 of file undinet.c.

1095 {
1096 struct net_device *netdev = undi_get_drvdata ( undi );
1097 struct undi_nic *undinic = netdev->priv;
1098 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
1099 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
1100 struct s_PXENV_STOP_UNDI stop_undi;
1101
1102 /* Unregister net device */
1104
1105 /* If we are preparing for an OS boot, or if we cannot exit
1106 * via the PXE stack, then shut down the PXE stack.
1107 */
1108 if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
1109
1110 /* Shut down UNDI stack */
1111 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
1113 &undi_shutdown, sizeof ( undi_shutdown ) );
1114 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
1116 &undi_cleanup, sizeof ( undi_cleanup ) );
1117 undi->flags &= ~UNDI_FL_INITIALIZED;
1118
1119 /* Unhook UNDI stack */
1120 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
1121 undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
1122 sizeof ( stop_undi ) );
1123 undi->flags &= ~UNDI_FL_STARTED;
1124 }
1125
1126 /* Clear entry point */
1128
1129 /* Free network device */
1131 netdev_put ( netdev );
1132
1133 DBGC ( undinic, "UNDINIC %p removed\n", undinic );
1134}
void unregister_netdev(struct net_device *netdev)
Unregister network device.
Definition netdevice.c:946
#define UNDI_FL_KEEP_ALL
UNDI flag: keep stack resident.
Definition undi.h:102
static void * undi_get_drvdata(struct undi_device *undi)
Get UNDI driver-private data.
Definition undi.h:80

References DBGC, undi_device::flags, memset(), netdev, netdev_nullify(), netdev_put(), PXENV_STOP_UNDI, PXENV_UNDI_CLEANUP, PXENV_UNDI_SHUTDOWN, UNDI_FL_INITIALIZED, UNDI_FL_KEEP_ALL, UNDI_FL_STARTED, undi_get_drvdata(), undinet_call(), undinet_entry_point, and unregister_netdev().

Referenced by undibus_remove(), and undipci_remove().

◆ REQUIRING_SYMBOL()

REQUIRING_SYMBOL ( undinet_probe )

References undinet_probe().

◆ REQUIRE_OBJECT()

REQUIRE_OBJECT ( config_pci )

Variable Documentation

◆ __profiler

struct undinet_profiler undinet_misc_profiler __profiler
static
Initial value:
=
{ .name = "undinet.irq" }

IRQ profiler.

Miscellaneous PXE API call profiler.

PXE unknown API call profiler.

PXENV_UNDI_ISR profiler.

PXENV_UNDI_TRANSMIT profiler.

Receive profiler.

Note that this profiler will not see calls to PXENV_UNDI_ISR_IN_START, which are handled by the UNDI ISR and do not go via undinet_call().

This profiler can be used to measure the overhead of a dummy PXE API call.

Definition at line 115 of file undinet.c.

115 { .name = "undinet.irq" };

◆ last_trigger_count

unsigned int last_trigger_count = 0
static

Last observed trigger count.

Definition at line 397 of file undinet.c.

Referenced by undinet_isr_triggered(), and undinet_poll().

◆ undinet_operations

struct net_device_operations undinet_operations
static
Initial value:
= {
.open = undinet_open,
.close = undinet_close,
.transmit = undinet_transmit,
.poll = undinet_poll,
.irq = undinet_irq,
}
static int undinet_transmit(struct net_device *netdev, struct io_buffer *iobuf)
Transmit packet.
Definition undinet.c:474
static void undinet_poll(struct net_device *netdev)
Poll for received packets.
Definition undinet.c:594
static int undinet_open(struct net_device *netdev)
Open NIC.
Definition undinet.c:734
static void undinet_irq(struct net_device *netdev, int enable)
Enable/disable interrupts.
Definition undinet.c:824

UNDI network device operations.

Definition at line 833 of file undinet.c.

833 {
834 .open = undinet_open,
835 .close = undinet_close,
836 .transmit = undinet_transmit,
837 .poll = undinet_poll,
838 .irq = undinet_irq,
839};

Referenced by undinet_probe().

◆ undinet_irq_broken_list

const struct undinet_irq_broken undinet_irq_broken_list[]
static
Initial value:
= {
{ 0x8086, 0x1502, PCI_ANY_ID, PCI_ANY_ID },
{ 0x8086, 0x1503, PCI_ANY_ID, PCI_ANY_ID },
{ 0x14e4, 0x1687, PCI_ANY_ID, PCI_ANY_ID },
{ 0x8086, 0x1521, 0x1043, PCI_ANY_ID },
}

List of devices with broken support for generating interrupts.

Some PXE stacks are known to claim that IRQs are supported, but then never generate interrupts. No satisfactory solution has been found to this problem; the workaround is to add the PCI vendor and device IDs to this list. This is something of a hack, since it will generate false positives for identical devices with a working PXE stack (e.g. those that have been reflashed with iPXE), but it's an improvement on the current situation.

Definition at line 864 of file undinet.c.

864 {
865 /* HP XX70x laptops */
866 { 0x8086, 0x1502, PCI_ANY_ID, PCI_ANY_ID },
867 { 0x8086, 0x1503, PCI_ANY_ID, PCI_ANY_ID },
868 /* HP 745 G3 laptop */
869 { 0x14e4, 0x1687, PCI_ANY_ID, PCI_ANY_ID },
870 /* ASUSTeK KNPA-U16 server */
871 { 0x8086, 0x1521, 0x1043, PCI_ANY_ID },
872};

Referenced by undinet_irq_is_broken().