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.

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:181
#define EINFO_EPLATFORM
Platform-generated base error.
Definition errno.h:692

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:249
#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 775 of file undinet.c.

775 {
776 struct undi_nic *undinic = netdev->priv;
777 struct s_PXENV_UNDI_ISR undi_isr;
778 struct s_PXENV_UNDI_CLOSE undi_close;
779 int rc;
780
781 /* Ensure ISR has exited cleanly */
782 while ( undinic->isr_processing ) {
783 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
784 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
785 sizeof ( undi_isr ) ) ) != 0 )
786 break;
787 switch ( undi_isr.FuncFlag ) {
790 /* Continue draining */
791 break;
792 default:
793 /* Stop processing */
794 undinic->isr_processing = 0;
795 break;
796 }
797 }
798
799 /* Close NIC */
800 undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
801 sizeof ( undi_close ) );
802
803 /* Disable interrupt and unhook ISR if applicable */
804 if ( undinic->irq ) {
805 disable_irq ( undinic->irq );
806 undinet_unhook_isr ( undinic->irq );
807 }
808
809 DBGC ( undinic, "UNDINIC %p closed\n", undinic );
810}
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
static struct net_device * netdev
Definition gdbudp.c:53
#define DBGC(...)
Definition compiler.h:505
#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:32
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
#define DBGC_HDA(...)
Definition compiler.h:506
#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 /* Some PXE stacks are unable to cope with P_UNKNOWN, and will
497 * always try to prepend a link-layer header. Work around
498 * these stacks by stripping the existing link-layer header
499 * and allowing the PXE stack to (re)construct the link-layer
500 * header itself.
501 */
502 if ( ( rc = eth_pull ( netdev, iobuf, &ll_dest, &ll_source,
503 &net_proto, &flags ) ) != 0 ) {
504 DBGC ( undinic, "UNDINIC %p could not strip Ethernet header: "
505 "%s\n", undinic, strerror ( rc ) );
506 return rc;
507 }
508 memcpy ( undinet_destaddr, ll_dest, sizeof ( undinet_destaddr ) );
509 switch ( net_proto ) {
510 case htons ( ETH_P_IP ) :
511 protocol = P_IP;
512 break;
513 case htons ( ETH_P_ARP ) :
514 protocol = P_ARP;
515 break;
516 case htons ( ETH_P_RARP ) :
518 break;
519 default:
520 /* Unknown protocol; restore the original link-layer header */
521 iob_push ( iobuf, sizeof ( struct ethhdr ) );
523 break;
524 }
525
526 /* Copy packet to UNDI I/O buffer */
527 len = iob_len ( iobuf );
528 if ( len > sizeof ( basemem_packet ) )
529 len = sizeof ( basemem_packet );
530 memcpy ( &basemem_packet, iobuf->data, len );
531
532 /* Create PXENV_UNDI_TRANSMIT data structure */
533 memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
534 undi_transmit.Protocol = protocol;
535 undi_transmit.XmitFlag = ( ( flags & LL_BROADCAST ) ?
537 undi_transmit.DestAddr.segment = rm_ds;
538 undi_transmit.DestAddr.offset = __from_data16 ( &undinet_destaddr );
539 undi_transmit.TBD.segment = rm_ds;
540 undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
541
542 /* Create PXENV_UNDI_TBD data structure */
543 undinet_tbd.ImmedLength = len;
544 undinet_tbd.Xmit.segment = rm_ds;
545 undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
546
547 /* Issue PXE API call */
548 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT, &undi_transmit,
549 sizeof ( undi_transmit ) ) ) != 0 )
550 goto done;
551
552 /* Free I/O buffer */
553 netdev_tx_complete ( netdev, iobuf );
554 done:
555 return rc;
556}
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_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
#define iob_push(iobuf, len)
Definition iobuf.h:89
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition iobuf.h:160
#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:767
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:53
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(), flags, htons, iob_len(), 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 588 of file undinet.c.

588 {
589 struct undi_nic *undinic = netdev->priv;
590 struct s_PXENV_UNDI_ISR undi_isr;
591 struct io_buffer *iobuf = NULL;
592 unsigned int quota = UNDI_RX_QUOTA;
593 size_t len;
594 size_t reserve_len;
595 size_t frag_len;
596 size_t max_frag_len;
597 int rc;
598
599 if ( ! undinic->isr_processing ) {
600 /* Allow interrupt to occur. Do this even if
601 * interrupts are not known to be supported, since
602 * some cards erroneously report that they do not
603 * support interrupts.
604 */
605 if ( ! undinet_isr_triggered() ) {
606
607 /* Rearm interrupt if needed */
608 if ( undiisr_rearm ) {
609 undiisr_rearm = 0;
610 assert ( undinic->irq != 0 );
611 enable_irq ( undinic->irq );
612 }
613
614 /* Allow interrupt to occur */
615 profile_start ( &undinet_irq_profiler );
616 __asm__ __volatile__ ( "sti\n\t"
617 "nop\n\t"
618 "nop\n\t"
619 "cli\n\t" );
620 profile_stop ( &undinet_irq_profiler );
621
622 /* If interrupts are known to be supported,
623 * then do nothing on this poll; wait for the
624 * interrupt to be triggered.
625 */
626 if ( undinic->irq_supported )
627 return;
628 }
629
630 /* Start ISR processing */
631 undinic->isr_processing = 1;
632 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
633 } else {
634 /* Continue ISR processing */
635 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
636 }
637
638 /* Run through the ISR loop */
639 while ( quota ) {
640 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
641 sizeof ( undi_isr ) ) ) != 0 ) {
643 break;
644 }
645 switch ( undi_isr.FuncFlag ) {
647 /* We don't care about transmit completions */
648 break;
650 /* Packet fragment received */
651 profile_start ( &undinet_rx_profiler );
652 len = undi_isr.FrameLength;
653 frag_len = undi_isr.BufferLength;
654 reserve_len = ( -undi_isr.FrameHeaderLength &
655 ( UNDI_RX_ALIGN - 1 ) );
656 if ( ( len == 0 ) || ( len < frag_len ) ) {
657 /* Don't laugh. VMWare does it. */
658 DBGC ( undinic, "UNDINIC %p reported insane "
659 "fragment (%zd of %zd bytes)\n",
660 undinic, frag_len, len );
662 break;
663 }
664 if ( ! iobuf ) {
665 iobuf = alloc_iob ( reserve_len + len );
666 if ( ! iobuf ) {
667 DBGC ( undinic, "UNDINIC %p could not "
668 "allocate %zd bytes for RX "
669 "buffer\n", undinic, len );
670 /* Fragment will be dropped */
672 goto done;
673 }
674 iob_reserve ( iobuf, reserve_len );
675 }
676 max_frag_len = iob_tailroom ( iobuf );
677 if ( frag_len > max_frag_len ) {
678 DBGC ( undinic, "UNDINIC %p fragment too big "
679 "(%zd+%zd does not fit into %zd)\n",
680 undinic, iob_len ( iobuf ), frag_len,
681 ( iob_len ( iobuf ) + max_frag_len ) );
682 frag_len = max_frag_len;
683 }
684 copy_from_real ( iob_put ( iobuf, frag_len ),
685 undi_isr.Frame.segment,
686 undi_isr.Frame.offset, frag_len );
687 if ( iob_len ( iobuf ) == len ) {
688 /* Whole packet received; deliver it */
689 netdev_rx ( netdev, iob_disown ( iobuf ) );
690 quota--;
691 /* Etherboot 5.4 fails to return all packets
692 * under mild load; pretend it retriggered.
693 */
694 if ( undinic->hacks & UNDI_HACK_EB54 )
696 }
697 profile_stop ( &undinet_rx_profiler );
698 break;
700 /* Processing complete */
701 undinic->isr_processing = 0;
702 goto done;
703 default:
704 /* Should never happen. VMWare does it routinely. */
705 DBGC ( undinic, "UNDINIC %p ISR returned invalid "
706 "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
707 undinic->isr_processing = 0;
708 goto done;
709 }
710 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
711 }
712
713 done:
714 if ( iobuf ) {
715 DBGC ( undinic, "UNDINIC %p returned incomplete packet "
716 "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
717 ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
718 netdev_rx_err ( netdev, iobuf, -EINVAL );
719 }
720}
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
#define EINVAL
Invalid argument.
Definition errno.h:429
#define ENOMEM
Not enough space.
Definition errno.h:535
#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:125
#define iob_disown(iobuf)
Disown an I/O buffer.
Definition iobuf.h:217
#define iob_reserve(iobuf, len)
Definition iobuf.h:72
static size_t iob_tailroom(struct io_buffer *iobuf)
Calculate available space at end of an I/O buffer.
Definition iobuf.h:180
#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:38
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 728 of file undinet.c.

728 {
729 struct undi_nic *undinic = netdev->priv;
730 struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
731 struct s_PXENV_UNDI_OPEN undi_open;
732 int rc;
733
734 /* Hook interrupt service routine and enable interrupt if applicable */
735 if ( undinic->irq ) {
736 undinet_hook_isr ( undinic->irq );
737 enable_irq ( undinic->irq );
738 send_eoi ( undinic->irq );
739 }
740
741 /* Set station address. Required for some PXE stacks; will
742 * spuriously fail on others. Ignore failures. We only ever
743 * use it to set the MAC address to the card's permanent value
744 * anyway.
745 */
746 memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
747 sizeof ( undi_set_address.StationAddress ) );
749 &undi_set_address, sizeof ( undi_set_address ) );
750
751 /* Open NIC. We ask for promiscuous operation, since it's the
752 * only way to ask for all multicast addresses. On any
753 * switched network, it shouldn't really make a difference to
754 * performance.
755 */
756 memset ( &undi_open, 0, sizeof ( undi_open ) );
757 undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
758 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
759 sizeof ( undi_open ) ) ) != 0 )
760 goto err;
761
762 DBGC ( undinic, "UNDINIC %p opened\n", undinic );
763 return 0;
764
765 err:
767 return rc;
768}
#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:775

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 818 of file undinet.c.

818 {
819 struct undi_nic *undinic = netdev->priv;
820
821 /* Cannot support interrupts yet */
822 DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
823 undinic, ( enable ? "enable" : "disable" ) );
824}

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 874 of file undinet.c.

874 {
875 struct undi_nic *undinic = netdev->priv;
876 struct device_description *desc = &netdev->dev->desc;
877 const struct undinet_irq_broken *broken;
878 struct pci_device pci;
879 uint16_t subsys_vendor;
880 uint16_t subsys;
881 unsigned int i;
882
883 /* Ignore non-PCI devices */
884 if ( desc->bus_type != BUS_TYPE_PCI )
885 return 0;
886
887 /* Read subsystem IDs */
888 pci_init ( &pci, desc->location );
889 pci_read_config_word ( &pci, PCI_SUBSYSTEM_VENDOR_ID, &subsys_vendor );
890 pci_read_config_word ( &pci, PCI_SUBSYSTEM_ID, &subsys );
891
892 /* Check for a match against the broken device list */
893 for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) /
894 sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) {
895 broken = &undinet_irq_broken_list[i];
896 if ( ( broken->pci_vendor == desc->vendor ) &&
897 ( broken->pci_device == desc->device ) &&
898 ( ( broken->pci_subsys_vendor == subsys_vendor ) ||
899 ( broken->pci_subsys_vendor == PCI_ANY_ID ) ) &&
900 ( ( broken->pci_subsys == subsys ) ||
901 ( broken->pci_subsys == PCI_ANY_ID ) ) ) {
902 DBGC ( undinic, "UNDINIC %p %04x:%04x subsys "
903 "%04x:%04x has broken interrupts\n",
904 undinic, desc->vendor, desc->device,
905 subsys_vendor, subsys );
906 return 1;
907 }
908 }
909
910 /* Check for a PCI Express capability. Given the number of
911 * issues found with legacy INTx emulation on PCIe systems, we
912 * assume that there is a high chance of interrupts not
913 * working on any PCIe device.
914 */
915 if ( pci_find_capability ( &pci, PCI_CAP_ID_EXP ) ) {
916 DBGC ( undinic, "UNDINIC %p is PCI Express: assuming "
917 "interrupts are unreliable\n", undinic );
918 return 1;
919 }
920
921 return 0;
922}
#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:187
#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:341
#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:211
A device with broken support for generating interrupts.
Definition undinet.c:836
uint16_t pci_subsys_vendor
PCI subsystem vendor ID.
Definition undinet.c:842
uint16_t pci_subsys
PCI subsystem ID.
Definition undinet.c:844
uint16_t pci_device
PCI device ID.
Definition undinet.c:840
uint16_t pci_vendor
PCI vendor ID.
Definition undinet.c:838
static const struct undinet_irq_broken undinet_irq_broken_list[]
List of devices with broken support for generating interrupts.
Definition undinet.c:858

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 931 of file undinet.c.

931 {
932 struct net_device *netdev;
933 struct undi_nic *undinic;
934 struct s_PXENV_START_UNDI start_undi;
935 struct s_PXENV_UNDI_STARTUP undi_startup;
936 struct s_PXENV_UNDI_INITIALIZE undi_init;
937 struct s_PXENV_UNDI_GET_INFORMATION undi_info;
938 struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
939 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
940 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
941 struct s_PXENV_STOP_UNDI stop_undi;
942 unsigned int retry;
943 int rc;
944
945 /* Allocate net device */
946 netdev = alloc_etherdev ( sizeof ( *undinic ) );
947 if ( ! netdev )
948 return -ENOMEM;
950 undinic = netdev->priv;
951 undi_set_drvdata ( undi, netdev );
952 netdev->dev = dev;
953 memset ( undinic, 0, sizeof ( *undinic ) );
955 DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
956
957 /* Hook in UNDI stack */
958 if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
959 memset ( &start_undi, 0, sizeof ( start_undi ) );
960 start_undi.AX = undi->pci_busdevfn;
961 start_undi.BX = undi->isapnp_csn;
962 start_undi.DX = undi->isapnp_read_port;
963 start_undi.ES = BIOS_SEG;
964 start_undi.DI = find_pnp_bios();
965 if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
966 &start_undi,
967 sizeof ( start_undi ) ) ) != 0 )
968 goto err_start_undi;
969 }
970 undi->flags |= UNDI_FL_STARTED;
971
972 /* Bring up UNDI stack */
973 if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
974 memset ( &undi_startup, 0, sizeof ( undi_startup ) );
975 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
976 &undi_startup,
977 sizeof ( undi_startup ) ) ) != 0 )
978 goto err_undi_startup;
979 /* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail
980 * due to a transient condition (e.g. media test
981 * failing because the link has only just come out of
982 * reset). We may therefore need to retry this call
983 * several times.
984 */
985 for ( retry = 0 ; ; ) {
986 memset ( &undi_init, 0, sizeof ( undi_init ) );
987 if ( ( rc = undinet_call ( undinic,
989 &undi_init,
990 sizeof ( undi_init ) ) ) ==0)
991 break;
992 if ( ++retry > UNDI_INITIALIZE_RETRY_MAX )
993 goto err_undi_initialize;
994 DBGC ( undinic, "UNDINIC %p retrying "
995 "PXENV_UNDI_INITIALIZE (retry %d)\n",
996 undinic, retry );
997 /* Delay to allow link to settle if necessary */
999 }
1000 }
1001 undi->flags |= UNDI_FL_INITIALIZED;
1002
1003 /* Get device information */
1004 memset ( &undi_info, 0, sizeof ( undi_info ) );
1005 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
1006 &undi_info, sizeof ( undi_info ) ) ) != 0 )
1007 goto err_undi_get_information;
1008 memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
1009 memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN );
1010 undinic->irq = undi_info.IntNumber;
1011 if ( undinic->irq > IRQ_MAX ) {
1012 DBGC ( undinic, "UNDINIC %p ignoring invalid IRQ %d\n",
1013 undinic, undinic->irq );
1014 undinic->irq = 0;
1015 }
1016 DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
1017 undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
1018 if ( undinic->irq ) {
1019 /* Sanity check - prefix should have disabled the IRQ */
1020 assert ( ! irq_enabled ( undinic->irq ) );
1021 }
1022
1023 /* Get interface information */
1024 memset ( &undi_iface, 0, sizeof ( undi_iface ) );
1025 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
1026 &undi_iface, sizeof ( undi_iface ) ) ) != 0 )
1027 goto err_undi_get_iface_info;
1028 DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
1029 undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
1030 undi_iface.ServiceFlags );
1031 if ( ( undi_iface.ServiceFlags & SUPPORTED_IRQ ) &&
1032 ( undinic->irq != 0 ) ) {
1033 undinic->irq_supported = 1;
1034 }
1035 DBGC ( undinic, "UNDINIC %p using %s mode\n", undinic,
1036 ( undinic->irq_supported ? "interrupt" : "polling" ) );
1037 if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
1038 sizeof ( undi_iface.IfaceType ) ) == 0 ) {
1039 DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
1040 undinic );
1041 undinic->hacks |= UNDI_HACK_EB54;
1042 }
1043 if ( undinet_irq_is_broken ( netdev ) ) {
1044 DBGC ( undinic, "UNDINIC %p forcing polling mode due to "
1045 "broken interrupts\n", undinic );
1046 undinic->irq_supported = 0;
1047 }
1048
1049 /* Register network device */
1050 if ( ( rc = register_netdev ( netdev ) ) != 0 )
1051 goto err_register;
1052
1053 /* Mark as link up; we don't handle link state */
1055
1056 DBGC ( undinic, "UNDINIC %p added\n", undinic );
1057 return 0;
1058
1059 err_register:
1060 err_undi_get_iface_info:
1061 err_undi_get_information:
1062 err_undi_initialize:
1063 /* Shut down UNDI stack */
1064 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
1065 undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
1066 sizeof ( undi_shutdown ) );
1067 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
1068 undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
1069 sizeof ( undi_cleanup ) );
1070 undi->flags &= ~UNDI_FL_INITIALIZED;
1071 err_undi_startup:
1072 /* Unhook UNDI stack */
1073 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
1074 undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
1075 sizeof ( stop_undi ) );
1076 undi->flags &= ~UNDI_FL_STARTED;
1077 err_start_undi:
1079 netdev_put ( netdev );
1080 undi_set_drvdata ( undi, NULL );
1081 return rc;
1082}
struct net_device * alloc_etherdev(size_t priv_size)
Allocate Ethernet device.
Definition ethernet.c:265
const char * eth_ntoa(const void *ll_addr)
Transcribe Ethernet address.
Definition ethernet.c:176
#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:789
static void netdev_init(struct net_device *netdev, struct net_device_operations *op)
Initialise a network device.
Definition netdevice.h:519
static void netdev_nullify(struct net_device *netdev)
Stop using a network device.
Definition netdevice.h:532
static void netdev_put(struct net_device *netdev)
Drop reference to network device.
Definition netdevice.h:576
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:827
#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:874

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 undibus_probe(), and undipci_probe().

◆ undinet_remove()

void undinet_remove ( struct undi_device * undi)

Remove UNDI device.

Parameters
undiUNDI device

Definition at line 1089 of file undinet.c.

1089 {
1090 struct net_device *netdev = undi_get_drvdata ( undi );
1091 struct undi_nic *undinic = netdev->priv;
1092 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
1093 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
1094 struct s_PXENV_STOP_UNDI stop_undi;
1095
1096 /* Unregister net device */
1098
1099 /* If we are preparing for an OS boot, or if we cannot exit
1100 * via the PXE stack, then shut down the PXE stack.
1101 */
1102 if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
1103
1104 /* Shut down UNDI stack */
1105 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
1107 &undi_shutdown, sizeof ( undi_shutdown ) );
1108 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
1110 &undi_cleanup, sizeof ( undi_cleanup ) );
1111 undi->flags &= ~UNDI_FL_INITIALIZED;
1112
1113 /* Unhook UNDI stack */
1114 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
1115 undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
1116 sizeof ( stop_undi ) );
1117 undi->flags &= ~UNDI_FL_STARTED;
1118 }
1119
1120 /* Clear entry point */
1122
1123 /* Free network device */
1125 netdev_put ( netdev );
1126
1127 DBGC ( undinic, "UNDINIC %p removed\n", undinic );
1128}
void unregister_netdev(struct net_device *netdev)
Unregister network device.
Definition netdevice.c:942
#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().

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.

116 { .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:588
static int undinet_open(struct net_device *netdev)
Open NIC.
Definition undinet.c:728
static void undinet_irq(struct net_device *netdev, int enable)
Enable/disable interrupts.
Definition undinet.c:818

UNDI network device operations.

Definition at line 827 of file undinet.c.

827 {
828 .open = undinet_open,
829 .close = undinet_close,
830 .transmit = undinet_transmit,
831 .poll = undinet_poll,
832 .irq = undinet_irq,
833};

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 858 of file undinet.c.

858 {
859 /* HP XX70x laptops */
860 { 0x8086, 0x1502, PCI_ANY_ID, PCI_ANY_ID },
861 { 0x8086, 0x1503, PCI_ANY_ID, PCI_ANY_ID },
862 /* HP 745 G3 laptop */
863 { 0x14e4, 0x1687, PCI_ANY_ID, PCI_ANY_ID },
864 /* ASUSTeK KNPA-U16 server */
865 { 0x8086, 0x1521, 0x1043, PCI_ANY_ID },
866};

Referenced by undinet_irq_is_broken().