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.

pxe_undi.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /** @file
  2. *
  3. * PXE UNDI API
  4. *
  5. */
  6. /*
  7. * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License as
  11. * published by the Free Software Foundation; either version 2 of the
  12. * License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22. */
  23. #include <stdint.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <byteswap.h>
  27. #include <basemem_packet.h>
  28. #include <gpxe/netdevice.h>
  29. #include <gpxe/iobuf.h>
  30. #include <gpxe/device.h>
  31. #include <gpxe/pci.h>
  32. #include <gpxe/if_ether.h>
  33. #include <gpxe/ip.h>
  34. #include <gpxe/arp.h>
  35. #include <gpxe/rarp.h>
  36. #include <gpxe/shutdown.h>
  37. #include "pxe.h"
  38. /* PXENV_UNDI_STARTUP
  39. *
  40. * Status: working
  41. */
  42. PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
  43. DBG ( "PXENV_UNDI_STARTUP" );
  44. undi_startup->Status = PXENV_STATUS_SUCCESS;
  45. return PXENV_EXIT_SUCCESS;
  46. }
  47. /* PXENV_UNDI_CLEANUP
  48. *
  49. * Status: working
  50. */
  51. PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
  52. DBG ( "PXENV_UNDI_CLEANUP" );
  53. undi_cleanup->Status = PXENV_STATUS_SUCCESS;
  54. return PXENV_EXIT_SUCCESS;
  55. }
  56. /* PXENV_UNDI_INITIALIZE
  57. *
  58. * Status: working
  59. */
  60. PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
  61. *undi_initialize ) {
  62. DBG ( "PXENV_UNDI_INITIALIZE" );
  63. undi_initialize->Status = PXENV_STATUS_SUCCESS;
  64. return PXENV_EXIT_SUCCESS;
  65. }
  66. /* PXENV_UNDI_RESET_ADAPTER
  67. *
  68. * Status: working
  69. */
  70. PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
  71. *undi_reset_adapter ) {
  72. DBG ( "PXENV_UNDI_RESET_ADAPTER" );
  73. undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
  74. return PXENV_EXIT_SUCCESS;
  75. }
  76. /* PXENV_UNDI_SHUTDOWN
  77. *
  78. * Status: working
  79. */
  80. PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
  81. *undi_shutdown ) {
  82. DBG ( "PXENV_UNDI_SHUTDOWN" );
  83. shutdown();
  84. undi_shutdown->Status = PXENV_STATUS_SUCCESS;
  85. return PXENV_EXIT_SUCCESS;
  86. }
  87. /* PXENV_UNDI_OPEN
  88. *
  89. * Status: working
  90. */
  91. PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
  92. DBG ( "PXENV_UNDI_OPEN" );
  93. #if 0
  94. /* PXESPEC: This is where we choose to enable interrupts.
  95. * Can't actually find where we're meant to in the PXE spec,
  96. * but this should work.
  97. */
  98. eth_irq ( ENABLE );
  99. #endif
  100. undi_open->Status = PXENV_STATUS_SUCCESS;
  101. return PXENV_EXIT_SUCCESS;
  102. }
  103. /* PXENV_UNDI_CLOSE
  104. *
  105. * Status: working
  106. */
  107. PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
  108. DBG ( "PXENV_UNDI_CLOSE" );
  109. undi_close->Status = PXENV_STATUS_SUCCESS;
  110. return PXENV_EXIT_SUCCESS;
  111. }
  112. /* PXENV_UNDI_TRANSMIT
  113. *
  114. * Status: working
  115. */
  116. PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
  117. *undi_transmit ) {
  118. struct s_PXENV_UNDI_TBD tbd;
  119. struct DataBlk *datablk;
  120. struct io_buffer *iobuf;
  121. struct net_protocol *net_protocol;
  122. char destaddr[MAX_LL_ADDR_LEN];
  123. const void *ll_dest;
  124. size_t len;
  125. unsigned int i;
  126. int rc;
  127. DBG ( "PXENV_UNDI_TRANSMIT" );
  128. /* Identify network-layer protocol */
  129. switch ( undi_transmit->Protocol ) {
  130. case P_IP: net_protocol = &ipv4_protocol; break;
  131. case P_ARP: net_protocol = &arp_protocol; break;
  132. case P_RARP: net_protocol = &rarp_protocol; break;
  133. case P_UNKNOWN: net_protocol = NULL; break;
  134. default:
  135. undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
  136. return PXENV_EXIT_FAILURE;
  137. }
  138. /* Calculate total packet length */
  139. copy_from_real ( &tbd, undi_transmit->TBD.segment,
  140. undi_transmit->TBD.offset, sizeof ( tbd ) );
  141. len = tbd.ImmedLength;
  142. for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
  143. datablk = &tbd.DataBlock[i];
  144. len += datablk->TDDataLen;
  145. }
  146. /* Allocate and fill I/O buffer */
  147. iobuf = alloc_iob ( len );
  148. if ( ! iobuf ) {
  149. undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
  150. return PXENV_EXIT_FAILURE;
  151. }
  152. copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
  153. tbd.Xmit.offset, tbd.ImmedLength );
  154. for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
  155. datablk = &tbd.DataBlock[i];
  156. copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
  157. datablk->TDDataPtr.segment,
  158. datablk->TDDataPtr.offset,
  159. datablk->TDDataLen );
  160. }
  161. /* Transmit packet */
  162. if ( net_protocol == NULL ) {
  163. /* Link-layer header already present */
  164. rc = netdev_tx ( pxe_netdev, iobuf );
  165. } else {
  166. /* Calculate destination address */
  167. if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
  168. copy_from_real ( destaddr,
  169. undi_transmit->DestAddr.segment,
  170. undi_transmit->DestAddr.offset,
  171. pxe_netdev->ll_protocol->ll_addr_len );
  172. ll_dest = destaddr;
  173. } else {
  174. ll_dest = pxe_netdev->ll_protocol->ll_broadcast;
  175. }
  176. rc = net_tx ( iobuf, pxe_netdev, net_protocol, ll_dest );
  177. }
  178. #warning "TX completion?"
  179. undi_transmit->Status = PXENV_STATUS ( rc );
  180. return ( ( rc == 0 ) ? PXENV_EXIT_SUCCESS : PXENV_EXIT_FAILURE );
  181. }
  182. /* PXENV_UNDI_SET_MCAST_ADDRESS
  183. *
  184. * Status: stub (no PXE multicast support)
  185. */
  186. PXENV_EXIT_t
  187. pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
  188. *undi_set_mcast_address ) {
  189. DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
  190. undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
  191. return PXENV_EXIT_FAILURE;
  192. }
  193. /* PXENV_UNDI_SET_STATION_ADDRESS
  194. *
  195. * Status: working
  196. */
  197. PXENV_EXIT_t
  198. pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
  199. *undi_set_station_address ) {
  200. DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
  201. /* If adapter is open, the change will have no effect; return
  202. * an error
  203. */
  204. if ( pxe_netdev->state & NETDEV_OPEN ) {
  205. undi_set_station_address->Status =
  206. PXENV_STATUS_UNDI_INVALID_STATE;
  207. return PXENV_EXIT_FAILURE;
  208. }
  209. /* Update MAC address */
  210. memcpy ( pxe_netdev->ll_addr,
  211. &undi_set_station_address->StationAddress,
  212. pxe_netdev->ll_protocol->ll_addr_len );
  213. undi_set_station_address = PXENV_STATUS_SUCCESS;
  214. return PXENV_EXIT_SUCCESS;
  215. }
  216. /* PXENV_UNDI_SET_PACKET_FILTER
  217. *
  218. * Status: won't implement (would require driver API changes for no
  219. * real benefit)
  220. */
  221. PXENV_EXIT_t
  222. pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
  223. *undi_set_packet_filter ) {
  224. DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
  225. undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
  226. return PXENV_EXIT_FAILURE;
  227. }
  228. /* PXENV_UNDI_GET_INFORMATION
  229. *
  230. * Status: working
  231. */
  232. PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
  233. *undi_get_information ) {
  234. struct device *dev = pxe_netdev->dev;
  235. struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
  236. DBG ( "PXENV_UNDI_GET_INFORMATION" );
  237. undi_get_information->BaseIo = dev->desc.ioaddr;
  238. undi_get_information->IntNumber = dev->desc.irq;
  239. /* Cheat: assume all cards can cope with this */
  240. undi_get_information->MaxTranUnit = ETH_MAX_MTU;
  241. undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
  242. undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
  243. /* Cheat: assume card is always configured with its permanent
  244. * node address. This is a valid assumption within Etherboot
  245. * at the time of writing.
  246. */
  247. memcpy ( &undi_get_information->CurrentNodeAddress,
  248. pxe_netdev->ll_addr,
  249. sizeof ( undi_get_information->CurrentNodeAddress ) );
  250. memcpy ( &undi_get_information->PermNodeAddress,
  251. pxe_netdev->ll_addr,
  252. sizeof ( undi_get_information->PermNodeAddress ) );
  253. undi_get_information->ROMAddress = 0;
  254. /* nic.rom_info->rom_segment; */
  255. /* We only provide the ability to receive or transmit a single
  256. * packet at a time. This is a bootloader, not an OS.
  257. */
  258. undi_get_information->RxBufCt = 1;
  259. undi_get_information->TxBufCt = 1;
  260. undi_get_information->Status = PXENV_STATUS_SUCCESS;
  261. return PXENV_EXIT_SUCCESS;
  262. }
  263. /* PXENV_UNDI_GET_STATISTICS
  264. *
  265. * Status: won't implement (would require driver API changes for no
  266. * real benefit)
  267. */
  268. PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
  269. *undi_get_statistics ) {
  270. DBG ( "PXENV_UNDI_GET_STATISTICS" );
  271. undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
  272. return PXENV_EXIT_FAILURE;
  273. }
  274. /* PXENV_UNDI_CLEAR_STATISTICS
  275. *
  276. * Status: won't implement (would require driver API changes for no
  277. * real benefit)
  278. */
  279. PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
  280. *undi_clear_statistics ) {
  281. DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
  282. undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
  283. return PXENV_EXIT_FAILURE;
  284. }
  285. /* PXENV_UNDI_INITIATE_DIAGS
  286. *
  287. * Status: won't implement (would require driver API changes for no
  288. * real benefit)
  289. */
  290. PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
  291. *undi_initiate_diags ) {
  292. DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
  293. undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
  294. return PXENV_EXIT_FAILURE;
  295. }
  296. /* PXENV_UNDI_FORCE_INTERRUPT
  297. *
  298. * Status: working
  299. */
  300. PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
  301. *undi_force_interrupt ) {
  302. DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
  303. #if 0
  304. eth_irq ( FORCE );
  305. #endif
  306. undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
  307. return PXENV_EXIT_SUCCESS;
  308. }
  309. /* PXENV_UNDI_GET_MCAST_ADDRESS
  310. *
  311. * Status: stub (no PXE multicast support)
  312. */
  313. PXENV_EXIT_t
  314. pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
  315. *undi_get_mcast_address ) {
  316. DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
  317. undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
  318. return PXENV_EXIT_FAILURE;
  319. }
  320. /* PXENV_UNDI_GET_NIC_TYPE
  321. *
  322. * Status: working
  323. */
  324. PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
  325. *undi_get_nic_type ) {
  326. struct device *dev = pxe_netdev->dev;
  327. DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
  328. memset ( &undi_get_nic_type->info, 0,
  329. sizeof ( undi_get_nic_type->info ) );
  330. switch ( dev->desc.bus_type ) {
  331. case BUS_TYPE_PCI: {
  332. struct pci_nic_info *info = &undi_get_nic_type->info.pci;
  333. undi_get_nic_type->NicType = PCI_NIC;
  334. info->Vendor_ID = dev->desc.vendor;
  335. info->Dev_ID = dev->desc.device;
  336. info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
  337. info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
  338. info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
  339. info->BusDevFunc = dev->desc.location;
  340. /* Cheat: remaining fields are probably unnecessary,
  341. * and would require adding extra code to pci.c.
  342. */
  343. undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
  344. undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
  345. break; }
  346. case BUS_TYPE_ISAPNP: {
  347. struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
  348. undi_get_nic_type->NicType = PnP_NIC;
  349. info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
  350. dev->desc.device );
  351. info->CardSelNum = dev->desc.location;
  352. /* Cheat: remaining fields are probably unnecessary,
  353. * and would require adding extra code to isapnp.c.
  354. */
  355. break; }
  356. default:
  357. undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
  358. return PXENV_EXIT_FAILURE;
  359. }
  360. undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
  361. return PXENV_EXIT_SUCCESS;
  362. }
  363. /* PXENV_UNDI_GET_IFACE_INFO
  364. *
  365. * Status: working
  366. */
  367. PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
  368. *undi_get_iface_info ) {
  369. DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
  370. /* Just hand back some info, doesn't really matter what it is.
  371. * Most PXE stacks seem to take this approach.
  372. */
  373. snprintf ( ( char * ) undi_get_iface_info->IfaceType,
  374. sizeof ( undi_get_iface_info->IfaceType ), "Etherboot" );
  375. undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
  376. undi_get_iface_info->ServiceFlags = 0;
  377. memset ( undi_get_iface_info->Reserved, 0,
  378. sizeof(undi_get_iface_info->Reserved) );
  379. undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
  380. return PXENV_EXIT_SUCCESS;
  381. }
  382. /* PXENV_UNDI_GET_STATE
  383. *
  384. * Status: impossible
  385. */
  386. PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
  387. *undi_get_state ) {
  388. DBG ( "PXENV_UNDI_GET_STATE" );
  389. undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
  390. return PXENV_EXIT_FAILURE;
  391. };
  392. /* PXENV_UNDI_ISR
  393. *
  394. * Status: working
  395. */
  396. PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
  397. struct io_buffer *iobuf;
  398. size_t len;
  399. DBG ( "PXENV_UNDI_ISR" );
  400. /* Just in case some idiot actually looks at these fields when
  401. * we weren't meant to fill them in...
  402. */
  403. undi_isr->BufferLength = 0;
  404. undi_isr->FrameLength = 0;
  405. undi_isr->FrameHeaderLength = 0;
  406. undi_isr->ProtType = 0;
  407. undi_isr->PktType = 0;
  408. switch ( undi_isr->FuncFlag ) {
  409. case PXENV_UNDI_ISR_IN_START :
  410. DBG ( " START" );
  411. /* Call poll(). This should acknowledge the device
  412. * interrupt and queue up any received packet.
  413. */
  414. if ( netdev_poll ( pxe_netdev, -1U ) ) {
  415. /* Packet waiting in queue */
  416. DBG ( " OURS" );
  417. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
  418. } else {
  419. DBG ( " NOT_OURS" );
  420. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
  421. }
  422. break;
  423. case PXENV_UNDI_ISR_IN_PROCESS :
  424. case PXENV_UNDI_ISR_IN_GET_NEXT :
  425. DBG ( " PROCESS/GET_NEXT" );
  426. /* Remove first packet from netdev RX queue */
  427. iobuf = netdev_rx_dequeue ( pxe_netdev );
  428. if ( ! iobuf ) {
  429. /* No more packets remaining */
  430. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
  431. break;
  432. }
  433. /* Copy packet to base memory buffer */
  434. len = iob_len ( iobuf );
  435. DBG ( " RECEIVE %zd", len );
  436. if ( len > sizeof ( basemem_packet ) ) {
  437. /* Should never happen */
  438. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
  439. undi_isr->Status = PXENV_STATUS_OUT_OF_RESOURCES;
  440. return PXENV_EXIT_FAILURE;
  441. }
  442. memcpy ( basemem_packet, iobuf->data, len );
  443. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
  444. undi_isr->BufferLength = len;
  445. undi_isr->FrameLength = len;
  446. undi_isr->FrameHeaderLength =
  447. pxe_netdev->ll_protocol->ll_header_len;
  448. undi_isr->Frame.segment = rm_ds;
  449. undi_isr->Frame.offset =
  450. ( ( unsigned ) & __from_data16 ( basemem_packet ) );
  451. /* Probably ought to fill in packet type */
  452. undi_isr->ProtType = P_UNKNOWN;
  453. undi_isr->PktType = XMT_DESTADDR;
  454. break;
  455. default :
  456. DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
  457. /* Should never happen */
  458. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
  459. undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
  460. return PXENV_EXIT_FAILURE;
  461. }
  462. undi_isr->Status = PXENV_STATUS_SUCCESS;
  463. return PXENV_EXIT_SUCCESS;
  464. }