You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

undinet.c 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  1. /*
  2. * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  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.
  8. *
  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.
  13. *
  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., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. */
  19. FILE_LICENCE ( GPL2_OR_LATER );
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include <byteswap.h>
  23. #include <pxe.h>
  24. #include <realmode.h>
  25. #include <pic8259.h>
  26. #include <biosint.h>
  27. #include <pnpbios.h>
  28. #include <basemem_packet.h>
  29. #include <ipxe/io.h>
  30. #include <ipxe/iobuf.h>
  31. #include <ipxe/netdevice.h>
  32. #include <ipxe/if_ether.h>
  33. #include <ipxe/ethernet.h>
  34. #include <ipxe/profile.h>
  35. #include <undi.h>
  36. #include <undinet.h>
  37. /** @file
  38. *
  39. * UNDI network device driver
  40. *
  41. */
  42. /** An UNDI NIC */
  43. struct undi_nic {
  44. /** Device supports IRQs */
  45. int irq_supported;
  46. /** Assigned IRQ number */
  47. unsigned int irq;
  48. /** Currently processing ISR */
  49. int isr_processing;
  50. /** Bug workarounds */
  51. int hacks;
  52. };
  53. /* Disambiguate the various error causes */
  54. #define EINFO_EPXECALL \
  55. __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
  56. "External PXE API error" )
  57. #define EPXECALL( status ) EPLATFORM ( EINFO_EPXECALL, status )
  58. /**
  59. * @defgroup undi_hacks UNDI workarounds
  60. * @{
  61. */
  62. /** Work around Etherboot 5.4 bugs */
  63. #define UNDI_HACK_EB54 0x0001
  64. /** @} */
  65. /** Maximum number of times to retry PXENV_UNDI_INITIALIZE */
  66. #define UNDI_INITIALIZE_RETRY_MAX 10
  67. /** Delay between retries of PXENV_UNDI_INITIALIZE */
  68. #define UNDI_INITIALIZE_RETRY_DELAY_MS 200
  69. /** Maximum number of received packets per poll */
  70. #define UNDI_RX_QUOTA 4
  71. /** Alignment of received frame payload */
  72. #define UNDI_RX_ALIGN 16
  73. static void undinet_close ( struct net_device *netdev );
  74. /**
  75. * UNDI parameter block
  76. *
  77. * Used as the parameter block for all UNDI API calls. Resides in
  78. * base memory.
  79. */
  80. static union u_PXENV_ANY __bss16 ( undinet_params );
  81. #define undinet_params __use_data16 ( undinet_params )
  82. /**
  83. * UNDI entry point
  84. *
  85. * Used as the indirection vector for all UNDI API calls. Resides in
  86. * base memory.
  87. */
  88. SEGOFF16_t __bss16 ( undinet_entry_point );
  89. #define undinet_entry_point __use_data16 ( undinet_entry_point )
  90. /** IRQ profiler */
  91. static struct profiler undinet_irq_profiler __profiler =
  92. { .name = "undinet.irq" };
  93. /** Receive profiler */
  94. static struct profiler undinet_rx_profiler __profiler =
  95. { .name = "undinet.rx" };
  96. /** A PXE API call breakdown profiler */
  97. struct undinet_profiler {
  98. /** Total time spent performing REAL_CALL() */
  99. struct profiler total;
  100. /** Time spent transitioning to real mode */
  101. struct profiler p2r;
  102. /** Time spent in external code */
  103. struct profiler ext;
  104. /** Time spent transitioning back to protected mode */
  105. struct profiler r2p;
  106. };
  107. /** PXENV_UNDI_TRANSMIT profiler */
  108. static struct undinet_profiler undinet_tx_profiler __profiler = {
  109. { .name = "undinet.tx" },
  110. { .name = "undinet.tx_p2r" },
  111. { .name = "undinet.tx_ext" },
  112. { .name = "undinet.tx_r2p" },
  113. };
  114. /** PXENV_UNDI_ISR profiler
  115. *
  116. * Note that this profiler will not see calls to
  117. * PXENV_UNDI_ISR_IN_START, which are handled by the UNDI ISR and do
  118. * not go via undinet_call().
  119. */
  120. static struct undinet_profiler undinet_isr_profiler __profiler = {
  121. { .name = "undinet.isr" },
  122. { .name = "undinet.isr_p2r" },
  123. { .name = "undinet.isr_ext" },
  124. { .name = "undinet.isr_r2p" },
  125. };
  126. /** PXE unknown API call profiler
  127. *
  128. * This profiler can be used to measure the overhead of a dummy PXE
  129. * API call.
  130. */
  131. static struct undinet_profiler undinet_unknown_profiler __profiler = {
  132. { .name = "undinet.unknown" },
  133. { .name = "undinet.unknown_p2r" },
  134. { .name = "undinet.unknown_ext" },
  135. { .name = "undinet.unknown_r2p" },
  136. };
  137. /** Miscellaneous PXE API call profiler */
  138. static struct undinet_profiler undinet_misc_profiler __profiler = {
  139. { .name = "undinet.misc" },
  140. { .name = "undinet.misc_p2r" },
  141. { .name = "undinet.misc_ext" },
  142. { .name = "undinet.misc_r2p" },
  143. };
  144. /*****************************************************************************
  145. *
  146. * UNDI API call
  147. *
  148. *****************************************************************************
  149. */
  150. /**
  151. * Name PXE API call
  152. *
  153. * @v function API call number
  154. * @ret name API call name
  155. */
  156. static inline __attribute__ (( always_inline )) const char *
  157. undinet_function_name ( unsigned int function ) {
  158. switch ( function ) {
  159. case PXENV_START_UNDI:
  160. return "PXENV_START_UNDI";
  161. case PXENV_STOP_UNDI:
  162. return "PXENV_STOP_UNDI";
  163. case PXENV_UNDI_STARTUP:
  164. return "PXENV_UNDI_STARTUP";
  165. case PXENV_UNDI_CLEANUP:
  166. return "PXENV_UNDI_CLEANUP";
  167. case PXENV_UNDI_INITIALIZE:
  168. return "PXENV_UNDI_INITIALIZE";
  169. case PXENV_UNDI_RESET_ADAPTER:
  170. return "PXENV_UNDI_RESET_ADAPTER";
  171. case PXENV_UNDI_SHUTDOWN:
  172. return "PXENV_UNDI_SHUTDOWN";
  173. case PXENV_UNDI_OPEN:
  174. return "PXENV_UNDI_OPEN";
  175. case PXENV_UNDI_CLOSE:
  176. return "PXENV_UNDI_CLOSE";
  177. case PXENV_UNDI_TRANSMIT:
  178. return "PXENV_UNDI_TRANSMIT";
  179. case PXENV_UNDI_SET_MCAST_ADDRESS:
  180. return "PXENV_UNDI_SET_MCAST_ADDRESS";
  181. case PXENV_UNDI_SET_STATION_ADDRESS:
  182. return "PXENV_UNDI_SET_STATION_ADDRESS";
  183. case PXENV_UNDI_SET_PACKET_FILTER:
  184. return "PXENV_UNDI_SET_PACKET_FILTER";
  185. case PXENV_UNDI_GET_INFORMATION:
  186. return "PXENV_UNDI_GET_INFORMATION";
  187. case PXENV_UNDI_GET_STATISTICS:
  188. return "PXENV_UNDI_GET_STATISTICS";
  189. case PXENV_UNDI_CLEAR_STATISTICS:
  190. return "PXENV_UNDI_CLEAR_STATISTICS";
  191. case PXENV_UNDI_INITIATE_DIAGS:
  192. return "PXENV_UNDI_INITIATE_DIAGS";
  193. case PXENV_UNDI_FORCE_INTERRUPT:
  194. return "PXENV_UNDI_FORCE_INTERRUPT";
  195. case PXENV_UNDI_GET_MCAST_ADDRESS:
  196. return "PXENV_UNDI_GET_MCAST_ADDRESS";
  197. case PXENV_UNDI_GET_NIC_TYPE:
  198. return "PXENV_UNDI_GET_NIC_TYPE";
  199. case PXENV_UNDI_GET_IFACE_INFO:
  200. return "PXENV_UNDI_GET_IFACE_INFO";
  201. /*
  202. * Duplicate case value; this is a bug in the PXE specification.
  203. *
  204. * case PXENV_UNDI_GET_STATE:
  205. * return "PXENV_UNDI_GET_STATE";
  206. */
  207. case PXENV_UNDI_ISR:
  208. return "PXENV_UNDI_ISR";
  209. case PXENV_GET_CACHED_INFO:
  210. return "PXENV_GET_CACHED_INFO";
  211. default:
  212. return "UNKNOWN API CALL";
  213. }
  214. }
  215. /**
  216. * Determine applicable profiler pair (for debugging)
  217. *
  218. * @v function API call number
  219. * @ret profiler Profiler
  220. */
  221. static struct undinet_profiler * undinet_profiler ( unsigned int function ) {
  222. /* Determine applicable profiler */
  223. switch ( function ) {
  224. case PXENV_UNDI_TRANSMIT:
  225. return &undinet_tx_profiler;
  226. case PXENV_UNDI_ISR:
  227. return &undinet_isr_profiler;
  228. case PXENV_UNKNOWN:
  229. return &undinet_unknown_profiler;
  230. default:
  231. return &undinet_misc_profiler;
  232. }
  233. }
  234. /**
  235. * Issue UNDI API call
  236. *
  237. * @v undinic UNDI NIC
  238. * @v function API call number
  239. * @v params PXE parameter block
  240. * @v params_len Length of PXE parameter block
  241. * @ret rc Return status code
  242. */
  243. static int undinet_call ( struct undi_nic *undinic, unsigned int function,
  244. void *params, size_t params_len ) {
  245. struct undinet_profiler *profiler = undinet_profiler ( function );
  246. PXENV_EXIT_t exit;
  247. uint32_t before;
  248. uint32_t started;
  249. uint32_t stopped;
  250. uint32_t after;
  251. int discard_D;
  252. int rc;
  253. /* Copy parameter block and entry point */
  254. assert ( params_len <= sizeof ( undinet_params ) );
  255. memcpy ( &undinet_params, params, params_len );
  256. /* Call real-mode entry point. This calling convention will
  257. * work with both the !PXE and the PXENV+ entry points.
  258. */
  259. profile_start ( &profiler->total );
  260. __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
  261. "rdtsc\n\t"
  262. "pushl %%eax\n\t"
  263. "pushw %%es\n\t"
  264. "pushw %%di\n\t"
  265. "pushw %%bx\n\t"
  266. "lcall *undinet_entry_point\n\t"
  267. "movw %%ax, %%bx\n\t"
  268. "rdtsc\n\t"
  269. "addw $6, %%sp\n\t"
  270. "popl %%edx\n\t"
  271. "popl %%ebp\n\t" /* gcc bug */ )
  272. : "=a" ( stopped ), "=d" ( started ),
  273. "=b" ( exit ), "=D" ( discard_D )
  274. : "b" ( function ),
  275. "D" ( __from_data16 ( &undinet_params ) )
  276. : "ecx", "esi" );
  277. profile_stop ( &profiler->total );
  278. before = profile_started ( &profiler->total );
  279. after = profile_stopped ( &profiler->total );
  280. profile_start_at ( &profiler->p2r, before );
  281. profile_stop_at ( &profiler->p2r, started );
  282. profile_start_at ( &profiler->ext, started );
  283. profile_stop_at ( &profiler->ext, stopped );
  284. profile_start_at ( &profiler->r2p, stopped );
  285. profile_stop_at ( &profiler->r2p, after );
  286. /* Determine return status code based on PXENV_EXIT and
  287. * PXENV_STATUS
  288. */
  289. rc = ( ( exit == PXENV_EXIT_SUCCESS ) ?
  290. 0 : -EPXECALL ( undinet_params.Status ) );
  291. /* If anything goes wrong, print as much debug information as
  292. * it's possible to give.
  293. */
  294. if ( rc != 0 ) {
  295. SEGOFF16_t rm_params = {
  296. .segment = rm_ds,
  297. .offset = __from_data16 ( &undinet_params ),
  298. };
  299. DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic,
  300. undinet_function_name ( function ), strerror ( rc ) );
  301. DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length "
  302. "%#02zx, entry point at %04x:%04x\n", undinic,
  303. rm_params.segment, rm_params.offset, params_len,
  304. undinet_entry_point.segment,
  305. undinet_entry_point.offset );
  306. DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic );
  307. DBGC_HDA ( undinic, rm_params, params, params_len );
  308. DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic );
  309. DBGC_HDA ( undinic, rm_params, &undinet_params, params_len );
  310. }
  311. /* Copy parameter block back */
  312. memcpy ( params, &undinet_params, params_len );
  313. return rc;
  314. }
  315. /*****************************************************************************
  316. *
  317. * UNDI interrupt service routine
  318. *
  319. *****************************************************************************
  320. */
  321. /**
  322. * UNDI interrupt service routine
  323. *
  324. * The UNDI ISR increments a counter (@c trigger_count) and exits.
  325. */
  326. extern void undiisr ( void );
  327. /** IRQ number */
  328. uint8_t __data16 ( undiisr_irq );
  329. #define undiisr_irq __use_data16 ( undiisr_irq )
  330. /** IRQ chain vector */
  331. struct segoff __data16 ( undiisr_next_handler );
  332. #define undiisr_next_handler __use_data16 ( undiisr_next_handler )
  333. /** IRQ trigger count */
  334. volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
  335. #define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
  336. /** Last observed trigger count */
  337. static unsigned int last_trigger_count = 0;
  338. /**
  339. * Hook UNDI interrupt service routine
  340. *
  341. * @v irq IRQ number
  342. */
  343. static void undinet_hook_isr ( unsigned int irq ) {
  344. assert ( irq <= IRQ_MAX );
  345. assert ( undiisr_irq == 0 );
  346. undiisr_irq = irq;
  347. hook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ),
  348. &undiisr_next_handler );
  349. }
  350. /**
  351. * Unhook UNDI interrupt service routine
  352. *
  353. * @v irq IRQ number
  354. */
  355. static void undinet_unhook_isr ( unsigned int irq ) {
  356. assert ( irq <= IRQ_MAX );
  357. unhook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ),
  358. &undiisr_next_handler );
  359. undiisr_irq = 0;
  360. }
  361. /**
  362. * Test to see if UNDI ISR has been triggered
  363. *
  364. * @ret triggered ISR has been triggered since last check
  365. */
  366. static int undinet_isr_triggered ( void ) {
  367. unsigned int this_trigger_count;
  368. /* Read trigger_count. Do this only once; it is volatile */
  369. this_trigger_count = undiisr_trigger_count;
  370. if ( this_trigger_count == last_trigger_count ) {
  371. /* Not triggered */
  372. return 0;
  373. } else {
  374. /* Triggered */
  375. last_trigger_count = this_trigger_count;
  376. return 1;
  377. }
  378. }
  379. /*****************************************************************************
  380. *
  381. * UNDI network device interface
  382. *
  383. *****************************************************************************
  384. */
  385. /** UNDI transmit buffer descriptor */
  386. static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
  387. #define undinet_tbd __use_data16 ( undinet_tbd )
  388. /** UNDI transmit destination address */
  389. static uint8_t __data16_array ( undinet_destaddr, [ETH_ALEN] );
  390. #define undinet_destaddr __use_data16 ( undinet_destaddr )
  391. /**
  392. * Transmit packet
  393. *
  394. * @v netdev Network device
  395. * @v iobuf I/O buffer
  396. * @ret rc Return status code
  397. */
  398. static int undinet_transmit ( struct net_device *netdev,
  399. struct io_buffer *iobuf ) {
  400. struct undi_nic *undinic = netdev->priv;
  401. struct s_PXENV_UNDI_TRANSMIT undi_transmit;
  402. const void *ll_dest;
  403. const void *ll_source;
  404. uint16_t net_proto;
  405. unsigned int flags;
  406. uint8_t protocol;
  407. size_t len;
  408. int rc;
  409. /* Technically, we ought to make sure that the previous
  410. * transmission has completed before we re-use the buffer.
  411. * However, many PXE stacks (including at least some Intel PXE
  412. * stacks and Etherboot 5.4) fail to generate TX completions.
  413. * In practice this won't be a problem, since our TX datapath
  414. * has a very low packet volume and we can get away with
  415. * assuming that a TX will be complete by the time we want to
  416. * transmit the next packet.
  417. */
  418. /* Some PXE stacks are unable to cope with P_UNKNOWN, and will
  419. * always try to prepend a link-layer header. Work around
  420. * these stacks by stripping the existing link-layer header
  421. * and allowing the PXE stack to (re)construct the link-layer
  422. * header itself.
  423. */
  424. if ( ( rc = eth_pull ( netdev, iobuf, &ll_dest, &ll_source,
  425. &net_proto, &flags ) ) != 0 ) {
  426. DBGC ( undinic, "UNDINIC %p could not strip Ethernet header: "
  427. "%s\n", undinic, strerror ( rc ) );
  428. return rc;
  429. }
  430. memcpy ( undinet_destaddr, ll_dest, sizeof ( undinet_destaddr ) );
  431. switch ( net_proto ) {
  432. case htons ( ETH_P_IP ) :
  433. protocol = P_IP;
  434. break;
  435. case htons ( ETH_P_ARP ) :
  436. protocol = P_ARP;
  437. break;
  438. case htons ( ETH_P_RARP ) :
  439. protocol = P_RARP;
  440. break;
  441. default:
  442. /* Unknown protocol; restore the original link-layer header */
  443. iob_push ( iobuf, sizeof ( struct ethhdr ) );
  444. protocol = P_UNKNOWN;
  445. break;
  446. }
  447. /* Copy packet to UNDI I/O buffer */
  448. len = iob_len ( iobuf );
  449. if ( len > sizeof ( basemem_packet ) )
  450. len = sizeof ( basemem_packet );
  451. memcpy ( &basemem_packet, iobuf->data, len );
  452. /* Create PXENV_UNDI_TRANSMIT data structure */
  453. memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
  454. undi_transmit.Protocol = protocol;
  455. undi_transmit.XmitFlag = ( ( flags & LL_BROADCAST ) ?
  456. XMT_BROADCAST : XMT_DESTADDR );
  457. undi_transmit.DestAddr.segment = rm_ds;
  458. undi_transmit.DestAddr.offset = __from_data16 ( &undinet_destaddr );
  459. undi_transmit.TBD.segment = rm_ds;
  460. undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
  461. /* Create PXENV_UNDI_TBD data structure */
  462. undinet_tbd.ImmedLength = len;
  463. undinet_tbd.Xmit.segment = rm_ds;
  464. undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
  465. /* Issue PXE API call */
  466. if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT, &undi_transmit,
  467. sizeof ( undi_transmit ) ) ) != 0 )
  468. goto done;
  469. /* Free I/O buffer */
  470. netdev_tx_complete ( netdev, iobuf );
  471. done:
  472. return rc;
  473. }
  474. /**
  475. * Poll for received packets
  476. *
  477. * @v netdev Network device
  478. *
  479. * Fun, fun, fun. UNDI drivers don't use polling; they use
  480. * interrupts. We therefore cheat and pretend that an interrupt has
  481. * occurred every time undinet_poll() is called. This isn't too much
  482. * of a hack; PCI devices share IRQs and so the first thing that a
  483. * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
  484. * not the UNDI NIC generated the interrupt; there is no harm done by
  485. * spurious calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
  486. * handling them any more rapidly than the usual rate of
  487. * undinet_poll() being called even if we did implement a full ISR.
  488. * So it should work. Ha!
  489. *
  490. * Addendum (21/10/03). Some cards don't play nicely with this trick,
  491. * so instead of doing it the easy way we have to go to all the hassle
  492. * of installing a genuine interrupt service routine and dealing with
  493. * the wonderful 8259 Programmable Interrupt Controller. Joy.
  494. *
  495. * Addendum (10/07/07). When doing things such as iSCSI boot, in
  496. * which we have to co-operate with a running OS, we can't get away
  497. * with the "ISR-just-increments-a-counter-and-returns" trick at all,
  498. * because it involves tying up the PIC for far too long, and other
  499. * interrupt-dependent components (e.g. local disks) start breaking.
  500. * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
  501. * from within interrupt context in order to deassert the device
  502. * interrupt, and sends EOI if applicable.
  503. */
  504. static void undinet_poll ( struct net_device *netdev ) {
  505. struct undi_nic *undinic = netdev->priv;
  506. struct s_PXENV_UNDI_ISR undi_isr;
  507. struct io_buffer *iobuf = NULL;
  508. unsigned int quota = UNDI_RX_QUOTA;
  509. size_t len;
  510. size_t reserve_len;
  511. size_t frag_len;
  512. size_t max_frag_len;
  513. int rc;
  514. if ( ! undinic->isr_processing ) {
  515. /* Allow interrupt to occur. Do this even if
  516. * interrupts are not known to be supported, since
  517. * some cards erroneously report that they do not
  518. * support interrupts.
  519. */
  520. if ( ! undinet_isr_triggered() ) {
  521. /* Allow interrupt to occur */
  522. profile_start ( &undinet_irq_profiler );
  523. __asm__ __volatile__ ( "sti\n\t"
  524. "nop\n\t"
  525. "nop\n\t"
  526. "cli\n\t" );
  527. profile_stop ( &undinet_irq_profiler );
  528. /* If interrupts are known to be supported,
  529. * then do nothing on this poll; wait for the
  530. * interrupt to be triggered.
  531. */
  532. if ( undinic->irq_supported )
  533. return;
  534. }
  535. /* Start ISR processing */
  536. undinic->isr_processing = 1;
  537. undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
  538. } else {
  539. /* Continue ISR processing */
  540. undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
  541. }
  542. /* Run through the ISR loop */
  543. while ( quota ) {
  544. if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
  545. sizeof ( undi_isr ) ) ) != 0 ) {
  546. netdev_rx_err ( netdev, NULL, rc );
  547. break;
  548. }
  549. switch ( undi_isr.FuncFlag ) {
  550. case PXENV_UNDI_ISR_OUT_TRANSMIT:
  551. /* We don't care about transmit completions */
  552. break;
  553. case PXENV_UNDI_ISR_OUT_RECEIVE:
  554. /* Packet fragment received */
  555. profile_start ( &undinet_rx_profiler );
  556. len = undi_isr.FrameLength;
  557. frag_len = undi_isr.BufferLength;
  558. reserve_len = ( -undi_isr.FrameHeaderLength &
  559. ( UNDI_RX_ALIGN - 1 ) );
  560. if ( ( len == 0 ) || ( len < frag_len ) ) {
  561. /* Don't laugh. VMWare does it. */
  562. DBGC ( undinic, "UNDINIC %p reported insane "
  563. "fragment (%zd of %zd bytes)\n",
  564. undinic, frag_len, len );
  565. netdev_rx_err ( netdev, NULL, -EINVAL );
  566. break;
  567. }
  568. if ( ! iobuf ) {
  569. iobuf = alloc_iob ( reserve_len + len );
  570. if ( ! iobuf ) {
  571. DBGC ( undinic, "UNDINIC %p could not "
  572. "allocate %zd bytes for RX "
  573. "buffer\n", undinic, len );
  574. /* Fragment will be dropped */
  575. netdev_rx_err ( netdev, NULL, -ENOMEM );
  576. goto done;
  577. }
  578. iob_reserve ( iobuf, reserve_len );
  579. }
  580. max_frag_len = iob_tailroom ( iobuf );
  581. if ( frag_len > max_frag_len ) {
  582. DBGC ( undinic, "UNDINIC %p fragment too big "
  583. "(%zd+%zd does not fit into %zd)\n",
  584. undinic, iob_len ( iobuf ), frag_len,
  585. ( iob_len ( iobuf ) + max_frag_len ) );
  586. frag_len = max_frag_len;
  587. }
  588. copy_from_real ( iob_put ( iobuf, frag_len ),
  589. undi_isr.Frame.segment,
  590. undi_isr.Frame.offset, frag_len );
  591. if ( iob_len ( iobuf ) == len ) {
  592. /* Whole packet received; deliver it */
  593. netdev_rx ( netdev, iob_disown ( iobuf ) );
  594. quota--;
  595. /* Etherboot 5.4 fails to return all packets
  596. * under mild load; pretend it retriggered.
  597. */
  598. if ( undinic->hacks & UNDI_HACK_EB54 )
  599. --last_trigger_count;
  600. }
  601. profile_stop ( &undinet_rx_profiler );
  602. break;
  603. case PXENV_UNDI_ISR_OUT_DONE:
  604. /* Processing complete */
  605. undinic->isr_processing = 0;
  606. goto done;
  607. default:
  608. /* Should never happen. VMWare does it routinely. */
  609. DBGC ( undinic, "UNDINIC %p ISR returned invalid "
  610. "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
  611. undinic->isr_processing = 0;
  612. goto done;
  613. }
  614. undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
  615. }
  616. done:
  617. if ( iobuf ) {
  618. DBGC ( undinic, "UNDINIC %p returned incomplete packet "
  619. "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
  620. ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
  621. netdev_rx_err ( netdev, iobuf, -EINVAL );
  622. }
  623. }
  624. /**
  625. * Open NIC
  626. *
  627. * @v netdev Net device
  628. * @ret rc Return status code
  629. */
  630. static int undinet_open ( struct net_device *netdev ) {
  631. struct undi_nic *undinic = netdev->priv;
  632. struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
  633. struct s_PXENV_UNDI_OPEN undi_open;
  634. int rc;
  635. /* Hook interrupt service routine and enable interrupt if applicable */
  636. if ( undinic->irq ) {
  637. undinet_hook_isr ( undinic->irq );
  638. enable_irq ( undinic->irq );
  639. send_eoi ( undinic->irq );
  640. }
  641. /* Set station address. Required for some PXE stacks; will
  642. * spuriously fail on others. Ignore failures. We only ever
  643. * use it to set the MAC address to the card's permanent value
  644. * anyway.
  645. */
  646. memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
  647. sizeof ( undi_set_address.StationAddress ) );
  648. undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
  649. &undi_set_address, sizeof ( undi_set_address ) );
  650. /* Open NIC. We ask for promiscuous operation, since it's the
  651. * only way to ask for all multicast addresses. On any
  652. * switched network, it shouldn't really make a difference to
  653. * performance.
  654. */
  655. memset ( &undi_open, 0, sizeof ( undi_open ) );
  656. undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
  657. if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
  658. sizeof ( undi_open ) ) ) != 0 )
  659. goto err;
  660. DBGC ( undinic, "UNDINIC %p opened\n", undinic );
  661. return 0;
  662. err:
  663. undinet_close ( netdev );
  664. return rc;
  665. }
  666. /**
  667. * Close NIC
  668. *
  669. * @v netdev Net device
  670. */
  671. static void undinet_close ( struct net_device *netdev ) {
  672. struct undi_nic *undinic = netdev->priv;
  673. struct s_PXENV_UNDI_ISR undi_isr;
  674. struct s_PXENV_UNDI_CLOSE undi_close;
  675. int rc;
  676. /* Ensure ISR has exited cleanly */
  677. while ( undinic->isr_processing ) {
  678. undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
  679. if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
  680. sizeof ( undi_isr ) ) ) != 0 )
  681. break;
  682. switch ( undi_isr.FuncFlag ) {
  683. case PXENV_UNDI_ISR_OUT_TRANSMIT:
  684. case PXENV_UNDI_ISR_OUT_RECEIVE:
  685. /* Continue draining */
  686. break;
  687. default:
  688. /* Stop processing */
  689. undinic->isr_processing = 0;
  690. break;
  691. }
  692. }
  693. /* Close NIC */
  694. undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
  695. sizeof ( undi_close ) );
  696. /* Disable interrupt and unhook ISR if applicable */
  697. if ( undinic->irq ) {
  698. disable_irq ( undinic->irq );
  699. undinet_unhook_isr ( undinic->irq );
  700. }
  701. DBGC ( undinic, "UNDINIC %p closed\n", undinic );
  702. }
  703. /**
  704. * Enable/disable interrupts
  705. *
  706. * @v netdev Net device
  707. * @v enable Interrupts should be enabled
  708. */
  709. static void undinet_irq ( struct net_device *netdev, int enable ) {
  710. struct undi_nic *undinic = netdev->priv;
  711. /* Cannot support interrupts yet */
  712. DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
  713. undinic, ( enable ? "enable" : "disable" ) );
  714. }
  715. /** UNDI network device operations */
  716. static struct net_device_operations undinet_operations = {
  717. .open = undinet_open,
  718. .close = undinet_close,
  719. .transmit = undinet_transmit,
  720. .poll = undinet_poll,
  721. .irq = undinet_irq,
  722. };
  723. /** A device with broken support for generating interrupts */
  724. struct undinet_irq_broken {
  725. /** PCI vendor ID */
  726. uint16_t pci_vendor;
  727. /** PCI device ID */
  728. uint16_t pci_device;
  729. };
  730. /**
  731. * List of devices with broken support for generating interrupts
  732. *
  733. * Some PXE stacks are known to claim that IRQs are supported, but
  734. * then never generate interrupts. No satisfactory solution has been
  735. * found to this problem; the workaround is to add the PCI vendor and
  736. * device IDs to this list. This is something of a hack, since it
  737. * will generate false positives for identical devices with a working
  738. * PXE stack (e.g. those that have been reflashed with iPXE), but it's
  739. * an improvement on the current situation.
  740. */
  741. static const struct undinet_irq_broken undinet_irq_broken_list[] = {
  742. /* HP XX70x laptops */
  743. { .pci_vendor = 0x8086, .pci_device = 0x1502 },
  744. { .pci_vendor = 0x8086, .pci_device = 0x1503 },
  745. /* HP 745 G3 laptop */
  746. { .pci_vendor = 0x14e4, .pci_device = 0x1687 },
  747. };
  748. /**
  749. * Check for devices with broken support for generating interrupts
  750. *
  751. * @v desc Device description
  752. * @ret irq_is_broken Interrupt support is broken; no interrupts are generated
  753. */
  754. static int undinet_irq_is_broken ( struct device_description *desc ) {
  755. const struct undinet_irq_broken *broken;
  756. unsigned int i;
  757. for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) /
  758. sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) {
  759. broken = &undinet_irq_broken_list[i];
  760. if ( ( desc->bus_type == BUS_TYPE_PCI ) &&
  761. ( desc->vendor == broken->pci_vendor ) &&
  762. ( desc->device == broken->pci_device ) ) {
  763. return 1;
  764. }
  765. }
  766. return 0;
  767. }
  768. /**
  769. * Probe UNDI device
  770. *
  771. * @v undi UNDI device
  772. * @v dev Underlying generic device
  773. * @ret rc Return status code
  774. */
  775. int undinet_probe ( struct undi_device *undi, struct device *dev ) {
  776. struct net_device *netdev;
  777. struct undi_nic *undinic;
  778. struct s_PXENV_START_UNDI start_undi;
  779. struct s_PXENV_UNDI_STARTUP undi_startup;
  780. struct s_PXENV_UNDI_INITIALIZE undi_init;
  781. struct s_PXENV_UNDI_GET_INFORMATION undi_info;
  782. struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
  783. struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
  784. struct s_PXENV_UNDI_CLEANUP undi_cleanup;
  785. struct s_PXENV_STOP_UNDI stop_undi;
  786. unsigned int retry;
  787. int rc;
  788. /* Allocate net device */
  789. netdev = alloc_etherdev ( sizeof ( *undinic ) );
  790. if ( ! netdev )
  791. return -ENOMEM;
  792. netdev_init ( netdev, &undinet_operations );
  793. undinic = netdev->priv;
  794. undi_set_drvdata ( undi, netdev );
  795. netdev->dev = dev;
  796. memset ( undinic, 0, sizeof ( *undinic ) );
  797. undinet_entry_point = undi->entry;
  798. DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
  799. /* Hook in UNDI stack */
  800. if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
  801. memset ( &start_undi, 0, sizeof ( start_undi ) );
  802. start_undi.AX = undi->pci_busdevfn;
  803. start_undi.BX = undi->isapnp_csn;
  804. start_undi.DX = undi->isapnp_read_port;
  805. start_undi.ES = BIOS_SEG;
  806. start_undi.DI = find_pnp_bios();
  807. if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
  808. &start_undi,
  809. sizeof ( start_undi ) ) ) != 0 )
  810. goto err_start_undi;
  811. }
  812. undi->flags |= UNDI_FL_STARTED;
  813. /* Bring up UNDI stack */
  814. if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
  815. memset ( &undi_startup, 0, sizeof ( undi_startup ) );
  816. if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
  817. &undi_startup,
  818. sizeof ( undi_startup ) ) ) != 0 )
  819. goto err_undi_startup;
  820. /* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail
  821. * due to a transient condition (e.g. media test
  822. * failing because the link has only just come out of
  823. * reset). We may therefore need to retry this call
  824. * several times.
  825. */
  826. for ( retry = 0 ; ; ) {
  827. memset ( &undi_init, 0, sizeof ( undi_init ) );
  828. if ( ( rc = undinet_call ( undinic,
  829. PXENV_UNDI_INITIALIZE,
  830. &undi_init,
  831. sizeof ( undi_init ) ) ) ==0)
  832. break;
  833. if ( ++retry > UNDI_INITIALIZE_RETRY_MAX )
  834. goto err_undi_initialize;
  835. DBGC ( undinic, "UNDINIC %p retrying "
  836. "PXENV_UNDI_INITIALIZE (retry %d)\n",
  837. undinic, retry );
  838. /* Delay to allow link to settle if necessary */
  839. mdelay ( UNDI_INITIALIZE_RETRY_DELAY_MS );
  840. }
  841. }
  842. undi->flags |= UNDI_FL_INITIALIZED;
  843. /* Get device information */
  844. memset ( &undi_info, 0, sizeof ( undi_info ) );
  845. if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
  846. &undi_info, sizeof ( undi_info ) ) ) != 0 )
  847. goto err_undi_get_information;
  848. memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
  849. memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN );
  850. undinic->irq = undi_info.IntNumber;
  851. if ( undinic->irq > IRQ_MAX ) {
  852. DBGC ( undinic, "UNDINIC %p ignoring invalid IRQ %d\n",
  853. undinic, undinic->irq );
  854. undinic->irq = 0;
  855. }
  856. DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
  857. undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
  858. /* Get interface information */
  859. memset ( &undi_iface, 0, sizeof ( undi_iface ) );
  860. if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
  861. &undi_iface, sizeof ( undi_iface ) ) ) != 0 )
  862. goto err_undi_get_iface_info;
  863. DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
  864. undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
  865. undi_iface.ServiceFlags );
  866. if ( ( undi_iface.ServiceFlags & SUPPORTED_IRQ ) &&
  867. ( undinic->irq != 0 ) ) {
  868. undinic->irq_supported = 1;
  869. }
  870. DBGC ( undinic, "UNDINIC %p using %s mode\n", undinic,
  871. ( undinic->irq_supported ? "interrupt" : "polling" ) );
  872. if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
  873. sizeof ( undi_iface.IfaceType ) ) == 0 ) {
  874. DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
  875. undinic );
  876. undinic->hacks |= UNDI_HACK_EB54;
  877. }
  878. if ( undinet_irq_is_broken ( &dev->desc ) ) {
  879. DBGC ( undinic, "UNDINIC %p forcing polling mode due to "
  880. "broken interrupts\n", undinic );
  881. undinic->irq_supported = 0;
  882. }
  883. /* Register network device */
  884. if ( ( rc = register_netdev ( netdev ) ) != 0 )
  885. goto err_register;
  886. /* Mark as link up; we don't handle link state */
  887. netdev_link_up ( netdev );
  888. DBGC ( undinic, "UNDINIC %p added\n", undinic );
  889. return 0;
  890. err_register:
  891. err_undi_get_iface_info:
  892. err_undi_get_information:
  893. err_undi_initialize:
  894. /* Shut down UNDI stack */
  895. memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
  896. undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
  897. sizeof ( undi_shutdown ) );
  898. memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
  899. undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
  900. sizeof ( undi_cleanup ) );
  901. undi->flags &= ~UNDI_FL_INITIALIZED;
  902. err_undi_startup:
  903. /* Unhook UNDI stack */
  904. memset ( &stop_undi, 0, sizeof ( stop_undi ) );
  905. undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
  906. sizeof ( stop_undi ) );
  907. undi->flags &= ~UNDI_FL_STARTED;
  908. err_start_undi:
  909. netdev_nullify ( netdev );
  910. netdev_put ( netdev );
  911. undi_set_drvdata ( undi, NULL );
  912. return rc;
  913. }
  914. /**
  915. * Remove UNDI device
  916. *
  917. * @v undi UNDI device
  918. */
  919. void undinet_remove ( struct undi_device *undi ) {
  920. struct net_device *netdev = undi_get_drvdata ( undi );
  921. struct undi_nic *undinic = netdev->priv;
  922. struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
  923. struct s_PXENV_UNDI_CLEANUP undi_cleanup;
  924. struct s_PXENV_STOP_UNDI stop_undi;
  925. /* Unregister net device */
  926. unregister_netdev ( netdev );
  927. /* If we are preparing for an OS boot, or if we cannot exit
  928. * via the PXE stack, then shut down the PXE stack.
  929. */
  930. if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
  931. /* Shut down UNDI stack */
  932. memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
  933. undinet_call ( undinic, PXENV_UNDI_SHUTDOWN,
  934. &undi_shutdown, sizeof ( undi_shutdown ) );
  935. memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
  936. undinet_call ( undinic, PXENV_UNDI_CLEANUP,
  937. &undi_cleanup, sizeof ( undi_cleanup ) );
  938. undi->flags &= ~UNDI_FL_INITIALIZED;
  939. /* Unhook UNDI stack */
  940. memset ( &stop_undi, 0, sizeof ( stop_undi ) );
  941. undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
  942. sizeof ( stop_undi ) );
  943. undi->flags &= ~UNDI_FL_STARTED;
  944. }
  945. /* Clear entry point */
  946. memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) );
  947. /* Free network device */
  948. netdev_nullify ( netdev );
  949. netdev_put ( netdev );
  950. DBGC ( undinic, "UNDINIC %p removed\n", undinic );
  951. }