2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 FILE_LICENCE ( GPL2_OR_LATER
);
27 #include <basemem_packet.h>
29 #include <gpxe/iobuf.h>
30 #include <gpxe/netdevice.h>
31 #include <gpxe/if_ether.h>
32 #include <gpxe/ethernet.h>
35 #include <pxeparent.h>
40 * UNDI network device driver
46 /** Assigned IRQ number */
48 /** Currently processing ISR */
50 /** Bug workarounds */
55 * @defgroup undi_hacks UNDI workarounds
59 /** Work around Etherboot 5.4 bugs */
60 #define UNDI_HACK_EB54 0x0001
64 static void undinet_close ( struct net_device
*netdev
);
66 /** Address of UNDI entry point */
67 static SEGOFF16_t undinet_entry
;
69 /*****************************************************************************
71 * UNDI interrupt service routine
73 *****************************************************************************
77 * UNDI interrupt service routine
79 * The UNDI ISR increments a counter (@c trigger_count) and exits.
81 extern void undiisr ( void );
84 uint8_t __data16 ( undiisr_irq
);
85 #define undiisr_irq __use_data16 ( undiisr_irq )
87 /** IRQ chain vector */
88 struct segoff
__data16 ( undiisr_next_handler
);
89 #define undiisr_next_handler __use_data16 ( undiisr_next_handler )
91 /** IRQ trigger count */
92 volatile uint8_t __data16 ( undiisr_trigger_count
) = 0;
93 #define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
95 /** Last observed trigger count */
96 static unsigned int last_trigger_count
= 0;
99 * Hook UNDI interrupt service routine
103 static void undinet_hook_isr ( unsigned int irq
) {
105 assert ( irq
<= IRQ_MAX
);
106 assert ( undiisr_irq
== 0 );
109 hook_bios_interrupt ( IRQ_INT ( irq
),
110 ( ( unsigned int ) undiisr
),
111 &undiisr_next_handler
);
115 * Unhook UNDI interrupt service routine
119 static void undinet_unhook_isr ( unsigned int irq
) {
121 assert ( irq
<= IRQ_MAX
);
123 unhook_bios_interrupt ( IRQ_INT ( irq
),
124 ( ( unsigned int ) undiisr
),
125 &undiisr_next_handler
);
130 * Test to see if UNDI ISR has been triggered
132 * @ret triggered ISR has been triggered since last check
134 static int undinet_isr_triggered ( void ) {
135 unsigned int this_trigger_count
;
137 /* Read trigger_count. Do this only once; it is volatile */
138 this_trigger_count
= undiisr_trigger_count
;
140 if ( this_trigger_count
== last_trigger_count
) {
145 last_trigger_count
= this_trigger_count
;
150 /*****************************************************************************
152 * UNDI network device interface
154 *****************************************************************************
157 /** UNDI transmit buffer descriptor */
158 static struct s_PXENV_UNDI_TBD
__data16 ( undinet_tbd
);
159 #define undinet_tbd __use_data16 ( undinet_tbd )
164 * @v netdev Network device
165 * @v iobuf I/O buffer
166 * @ret rc Return status code
168 static int undinet_transmit ( struct net_device
*netdev
,
169 struct io_buffer
*iobuf
) {
170 struct s_PXENV_UNDI_TRANSMIT undi_transmit
;
171 size_t len
= iob_len ( iobuf
);
174 /* Technically, we ought to make sure that the previous
175 * transmission has completed before we re-use the buffer.
176 * However, many PXE stacks (including at least some Intel PXE
177 * stacks and Etherboot 5.4) fail to generate TX completions.
178 * In practice this won't be a problem, since our TX datapath
179 * has a very low packet volume and we can get away with
180 * assuming that a TX will be complete by the time we want to
181 * transmit the next packet.
184 /* Copy packet to UNDI I/O buffer */
185 if ( len
> sizeof ( basemem_packet
) )
186 len
= sizeof ( basemem_packet
);
187 memcpy ( &basemem_packet
, iobuf
->data
, len
);
189 /* Create PXENV_UNDI_TRANSMIT data structure */
190 memset ( &undi_transmit
, 0, sizeof ( undi_transmit
) );
191 undi_transmit
.DestAddr
.segment
= rm_ds
;
192 undi_transmit
.DestAddr
.offset
= __from_data16 ( &undinet_tbd
);
193 undi_transmit
.TBD
.segment
= rm_ds
;
194 undi_transmit
.TBD
.offset
= __from_data16 ( &undinet_tbd
);
196 /* Create PXENV_UNDI_TBD data structure */
197 undinet_tbd
.ImmedLength
= len
;
198 undinet_tbd
.Xmit
.segment
= rm_ds
;
199 undinet_tbd
.Xmit
.offset
= __from_data16 ( basemem_packet
);
201 /* Issue PXE API call */
202 if ( ( rc
= pxeparent_call ( undinet_entry
, PXENV_UNDI_TRANSMIT
,
204 sizeof ( undi_transmit
) ) ) != 0 )
207 /* Free I/O buffer */
208 netdev_tx_complete ( netdev
, iobuf
);
215 * Poll for received packets
217 * @v netdev Network device
219 * Fun, fun, fun. UNDI drivers don't use polling; they use
220 * interrupts. We therefore cheat and pretend that an interrupt has
221 * occurred every time undinet_poll() is called. This isn't too much
222 * of a hack; PCI devices share IRQs and so the first thing that a
223 * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
224 * not the UNDI NIC generated the interrupt; there is no harm done by
225 * spurious calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
226 * handling them any more rapidly than the usual rate of
227 * undinet_poll() being called even if we did implement a full ISR.
228 * So it should work. Ha!
230 * Addendum (21/10/03). Some cards don't play nicely with this trick,
231 * so instead of doing it the easy way we have to go to all the hassle
232 * of installing a genuine interrupt service routine and dealing with
233 * the wonderful 8259 Programmable Interrupt Controller. Joy.
235 * Addendum (10/07/07). When doing things such as iSCSI boot, in
236 * which we have to co-operate with a running OS, we can't get away
237 * with the "ISR-just-increments-a-counter-and-returns" trick at all,
238 * because it involves tying up the PIC for far too long, and other
239 * interrupt-dependent components (e.g. local disks) start breaking.
240 * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
241 * from within interrupt context in order to deassert the device
242 * interrupt, and sends EOI if applicable.
244 static void undinet_poll ( struct net_device
*netdev
) {
245 struct undi_nic
*undinic
= netdev
->priv
;
246 struct s_PXENV_UNDI_ISR undi_isr
;
247 struct io_buffer
*iobuf
= NULL
;
253 if ( ! undinic
->isr_processing
) {
254 /* Do nothing unless ISR has been triggered */
255 if ( ! undinet_isr_triggered() ) {
256 /* Allow interrupt to occur */
257 __asm__
__volatile__ ( REAL_CODE ( "sti\n\t"
264 /* Start ISR processing */
265 undinic
->isr_processing
= 1;
266 undi_isr
.FuncFlag
= PXENV_UNDI_ISR_IN_PROCESS
;
268 /* Continue ISR processing */
269 undi_isr
.FuncFlag
= PXENV_UNDI_ISR_IN_GET_NEXT
;
272 /* Run through the ISR loop */
274 if ( ( rc
= pxeparent_call ( undinet_entry
, PXENV_UNDI_ISR
,
276 sizeof ( undi_isr
) ) ) != 0 )
278 switch ( undi_isr
.FuncFlag
) {
279 case PXENV_UNDI_ISR_OUT_TRANSMIT
:
280 /* We don't care about transmit completions */
282 case PXENV_UNDI_ISR_OUT_RECEIVE
:
283 /* Packet fragment received */
284 len
= undi_isr
.FrameLength
;
285 frag_len
= undi_isr
.BufferLength
;
286 if ( ( len
== 0 ) || ( len
< frag_len
) ) {
287 /* Don't laugh. VMWare does it. */
288 DBGC ( undinic
, "UNDINIC %p reported insane "
289 "fragment (%zd of %zd bytes)\n",
290 undinic
, frag_len
, len
);
291 netdev_rx_err ( netdev
, NULL
, -EINVAL
);
295 iobuf
= alloc_iob ( len
);
297 DBGC ( undinic
, "UNDINIC %p could not "
298 "allocate %zd bytes for RX buffer\n",
300 /* Fragment will be dropped */
301 netdev_rx_err ( netdev
, NULL
, -ENOMEM
);
304 max_frag_len
= iob_tailroom ( iobuf
);
305 if ( frag_len
> max_frag_len
) {
306 DBGC ( undinic
, "UNDINIC %p fragment too big "
307 "(%zd+%zd does not fit into %zd)\n",
308 undinic
, iob_len ( iobuf
), frag_len
,
309 ( iob_len ( iobuf
) + max_frag_len
) );
310 frag_len
= max_frag_len
;
312 copy_from_real ( iob_put ( iobuf
, frag_len
),
313 undi_isr
.Frame
.segment
,
314 undi_isr
.Frame
.offset
, frag_len
);
315 if ( iob_len ( iobuf
) == len
) {
316 /* Whole packet received; deliver it */
317 netdev_rx ( netdev
, iob_disown ( iobuf
) );
318 /* Etherboot 5.4 fails to return all packets
319 * under mild load; pretend it retriggered.
321 if ( undinic
->hacks
& UNDI_HACK_EB54
)
322 --last_trigger_count
;
325 case PXENV_UNDI_ISR_OUT_DONE
:
326 /* Processing complete */
327 undinic
->isr_processing
= 0;
330 /* Should never happen. VMWare does it routinely. */
331 DBGC ( undinic
, "UNDINIC %p ISR returned invalid "
332 "FuncFlag %04x\n", undinic
, undi_isr
.FuncFlag
);
333 undinic
->isr_processing
= 0;
336 undi_isr
.FuncFlag
= PXENV_UNDI_ISR_IN_GET_NEXT
;
341 DBGC ( undinic
, "UNDINIC %p returned incomplete packet "
342 "(%zd of %zd)\n", undinic
, iob_len ( iobuf
),
343 ( iob_len ( iobuf
) + iob_tailroom ( iobuf
) ) );
344 netdev_rx_err ( netdev
, iobuf
, -EINVAL
);
351 * @v netdev Net device
352 * @ret rc Return status code
354 static int undinet_open ( struct net_device
*netdev
) {
355 struct undi_nic
*undinic
= netdev
->priv
;
356 struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address
;
357 struct s_PXENV_UNDI_OPEN undi_open
;
360 /* Hook interrupt service routine and enable interrupt */
361 undinet_hook_isr ( undinic
->irq
);
362 enable_irq ( undinic
->irq
);
363 send_eoi ( undinic
->irq
);
365 /* Set station address. Required for some PXE stacks; will
366 * spuriously fail on others. Ignore failures. We only ever
367 * use it to set the MAC address to the card's permanent value
370 memcpy ( undi_set_address
.StationAddress
, netdev
->ll_addr
,
371 sizeof ( undi_set_address
.StationAddress
) );
372 pxeparent_call ( undinet_entry
, PXENV_UNDI_SET_STATION_ADDRESS
,
373 &undi_set_address
, sizeof ( undi_set_address
) );
375 /* Open NIC. We ask for promiscuous operation, since it's the
376 * only way to ask for all multicast addresses. On any
377 * switched network, it shouldn't really make a difference to
380 memset ( &undi_open
, 0, sizeof ( undi_open
) );
381 undi_open
.PktFilter
= ( FLTR_DIRECTED
| FLTR_BRDCST
| FLTR_PRMSCS
);
382 if ( ( rc
= pxeparent_call ( undinet_entry
, PXENV_UNDI_OPEN
,
383 &undi_open
, sizeof ( undi_open
) ) ) != 0 )
386 DBGC ( undinic
, "UNDINIC %p opened\n", undinic
);
390 undinet_close ( netdev
);
397 * @v netdev Net device
399 static void undinet_close ( struct net_device
*netdev
) {
400 struct undi_nic
*undinic
= netdev
->priv
;
401 struct s_PXENV_UNDI_ISR undi_isr
;
402 struct s_PXENV_UNDI_CLOSE undi_close
;
405 /* Ensure ISR has exited cleanly */
406 while ( undinic
->isr_processing
) {
407 undi_isr
.FuncFlag
= PXENV_UNDI_ISR_IN_GET_NEXT
;
408 if ( ( rc
= pxeparent_call ( undinet_entry
, PXENV_UNDI_ISR
,
410 sizeof ( undi_isr
) ) ) != 0 )
412 switch ( undi_isr
.FuncFlag
) {
413 case PXENV_UNDI_ISR_OUT_TRANSMIT
:
414 case PXENV_UNDI_ISR_OUT_RECEIVE
:
415 /* Continue draining */
418 /* Stop processing */
419 undinic
->isr_processing
= 0;
425 pxeparent_call ( undinet_entry
, PXENV_UNDI_CLOSE
,
426 &undi_close
, sizeof ( undi_close
) );
428 /* Disable interrupt and unhook ISR */
429 disable_irq ( undinic
->irq
);
430 undinet_unhook_isr ( undinic
->irq
);
432 DBGC ( undinic
, "UNDINIC %p closed\n", undinic
);
436 * Enable/disable interrupts
438 * @v netdev Net device
439 * @v enable Interrupts should be enabled
441 static void undinet_irq ( struct net_device
*netdev
, int enable
) {
442 struct undi_nic
*undinic
= netdev
->priv
;
444 /* Cannot support interrupts yet */
445 DBGC ( undinic
, "UNDINIC %p cannot %s interrupts\n",
446 undinic
, ( enable
? "enable" : "disable" ) );
449 /** UNDI network device operations */
450 static struct net_device_operations undinet_operations
= {
451 .open
= undinet_open
,
452 .close
= undinet_close
,
453 .transmit
= undinet_transmit
,
454 .poll
= undinet_poll
,
461 * @v undi UNDI device
462 * @ret rc Return status code
464 int undinet_probe ( struct undi_device
*undi
) {
465 struct net_device
*netdev
;
466 struct undi_nic
*undinic
;
467 struct s_PXENV_START_UNDI start_undi
;
468 struct s_PXENV_UNDI_STARTUP undi_startup
;
469 struct s_PXENV_UNDI_INITIALIZE undi_initialize
;
470 struct s_PXENV_UNDI_GET_INFORMATION undi_info
;
471 struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface
;
472 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown
;
473 struct s_PXENV_UNDI_CLEANUP undi_cleanup
;
474 struct s_PXENV_STOP_UNDI stop_undi
;
477 /* Allocate net device */
478 netdev
= alloc_etherdev ( sizeof ( *undinic
) );
481 netdev_init ( netdev
, &undinet_operations
);
482 undinic
= netdev
->priv
;
483 undi_set_drvdata ( undi
, netdev
);
484 netdev
->dev
= &undi
->dev
;
485 memset ( undinic
, 0, sizeof ( *undinic
) );
486 undinet_entry
= undi
->entry
;
487 DBGC ( undinic
, "UNDINIC %p using UNDI %p\n", undinic
, undi
);
489 /* Hook in UNDI stack */
490 if ( ! ( undi
->flags
& UNDI_FL_STARTED
) ) {
491 memset ( &start_undi
, 0, sizeof ( start_undi
) );
492 start_undi
.AX
= undi
->pci_busdevfn
;
493 start_undi
.BX
= undi
->isapnp_csn
;
494 start_undi
.DX
= undi
->isapnp_read_port
;
495 start_undi
.ES
= BIOS_SEG
;
496 start_undi
.DI
= find_pnp_bios();
497 if ( ( rc
= pxeparent_call ( undinet_entry
, PXENV_START_UNDI
,
499 sizeof ( start_undi
) ) ) != 0 )
502 undi
->flags
|= UNDI_FL_STARTED
;
504 /* Bring up UNDI stack */
505 if ( ! ( undi
->flags
& UNDI_FL_INITIALIZED
) ) {
506 memset ( &undi_startup
, 0, sizeof ( undi_startup
) );
507 if ( ( rc
= pxeparent_call ( undinet_entry
, PXENV_UNDI_STARTUP
,
509 sizeof ( undi_startup
) ) ) != 0 )
510 goto err_undi_startup
;
511 memset ( &undi_initialize
, 0, sizeof ( undi_initialize
) );
512 if ( ( rc
= pxeparent_call ( undinet_entry
,
513 PXENV_UNDI_INITIALIZE
,
515 sizeof ( undi_initialize
))) != 0 )
516 goto err_undi_initialize
;
518 undi
->flags
|= UNDI_FL_INITIALIZED
;
520 /* Get device information */
521 memset ( &undi_info
, 0, sizeof ( undi_info
) );
522 if ( ( rc
= pxeparent_call ( undinet_entry
, PXENV_UNDI_GET_INFORMATION
,
523 &undi_info
, sizeof ( undi_info
) ) ) != 0 )
524 goto err_undi_get_information
;
525 memcpy ( netdev
->hw_addr
, undi_info
.PermNodeAddress
, ETH_ALEN
);
526 undinic
->irq
= undi_info
.IntNumber
;
527 if ( undinic
->irq
> IRQ_MAX
) {
528 DBGC ( undinic
, "UNDINIC %p invalid IRQ %d\n",
529 undinic
, undinic
->irq
);
532 DBGC ( undinic
, "UNDINIC %p is %s on IRQ %d\n",
533 undinic
, eth_ntoa ( netdev
->hw_addr
), undinic
->irq
);
535 /* Get interface information */
536 memset ( &undi_iface
, 0, sizeof ( undi_iface
) );
537 if ( ( rc
= pxeparent_call ( undinet_entry
, PXENV_UNDI_GET_IFACE_INFO
,
539 sizeof ( undi_iface
) ) ) != 0 )
540 goto err_undi_get_iface_info
;
541 DBGC ( undinic
, "UNDINIC %p has type %s, speed %d, flags %08x\n",
542 undinic
, undi_iface
.IfaceType
, undi_iface
.LinkSpeed
,
543 undi_iface
.ServiceFlags
);
544 if ( strncmp ( ( ( char * ) undi_iface
.IfaceType
), "Etherboot",
545 sizeof ( undi_iface
.IfaceType
) ) == 0 ) {
546 DBGC ( undinic
, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
548 undinic
->hacks
|= UNDI_HACK_EB54
;
551 /* Mark as link up; we don't handle link state */
552 netdev_link_up ( netdev
);
554 /* Register network device */
555 if ( ( rc
= register_netdev ( netdev
) ) != 0 )
558 DBGC ( undinic
, "UNDINIC %p added\n", undinic
);
562 err_undi_get_iface_info
:
564 err_undi_get_information
:
566 /* Shut down UNDI stack */
567 memset ( &undi_shutdown
, 0, sizeof ( undi_shutdown
) );
568 pxeparent_call ( undinet_entry
, PXENV_UNDI_SHUTDOWN
, &undi_shutdown
,
569 sizeof ( undi_shutdown
) );
570 memset ( &undi_cleanup
, 0, sizeof ( undi_cleanup
) );
571 pxeparent_call ( undinet_entry
, PXENV_UNDI_CLEANUP
, &undi_cleanup
,
572 sizeof ( undi_cleanup
) );
573 undi
->flags
&= ~UNDI_FL_INITIALIZED
;
575 /* Unhook UNDI stack */
576 memset ( &stop_undi
, 0, sizeof ( stop_undi
) );
577 pxeparent_call ( undinet_entry
, PXENV_STOP_UNDI
, &stop_undi
,
578 sizeof ( stop_undi
) );
579 undi
->flags
&= ~UNDI_FL_STARTED
;
581 netdev_nullify ( netdev
);
582 netdev_put ( netdev
);
583 undi_set_drvdata ( undi
, NULL
);
590 * @v undi UNDI device
592 void undinet_remove ( struct undi_device
*undi
) {
593 struct net_device
*netdev
= undi_get_drvdata ( undi
);
594 struct undi_nic
*undinic
= netdev
->priv
;
595 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown
;
596 struct s_PXENV_UNDI_CLEANUP undi_cleanup
;
597 struct s_PXENV_STOP_UNDI stop_undi
;
599 /* Unregister net device */
600 unregister_netdev ( netdev
);
602 /* If we are preparing for an OS boot, or if we cannot exit
603 * via the PXE stack, then shut down the PXE stack.
605 if ( ! ( undi
->flags
& UNDI_FL_KEEP_ALL
) ) {
607 /* Shut down UNDI stack */
608 memset ( &undi_shutdown
, 0, sizeof ( undi_shutdown
) );
609 pxeparent_call ( undinet_entry
, PXENV_UNDI_SHUTDOWN
,
610 &undi_shutdown
, sizeof ( undi_shutdown
) );
611 memset ( &undi_cleanup
, 0, sizeof ( undi_cleanup
) );
612 pxeparent_call ( undinet_entry
, PXENV_UNDI_CLEANUP
,
613 &undi_cleanup
, sizeof ( undi_cleanup
) );
614 undi
->flags
&= ~UNDI_FL_INITIALIZED
;
616 /* Unhook UNDI stack */
617 memset ( &stop_undi
, 0, sizeof ( stop_undi
) );
618 pxeparent_call ( undinet_entry
, PXENV_STOP_UNDI
, &stop_undi
,
619 sizeof ( stop_undi
) );
620 undi
->flags
&= ~UNDI_FL_STARTED
;
623 /* Clear entry point */
624 memset ( &undinet_entry
, 0, sizeof ( undinet_entry
) );
626 /* Free network device */
627 netdev_nullify ( netdev
);
628 netdev_put ( netdev
);
630 DBGC ( undinic
, "UNDINIC %p removed\n", undinic
);