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_call.c 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /*
  2. * Copyright (C) 2006 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 <ipxe/uaccess.h>
  21. #include <ipxe/init.h>
  22. #include <ipxe/profile.h>
  23. #include <setjmp.h>
  24. #include <registers.h>
  25. #include <biosint.h>
  26. #include <pxe.h>
  27. #include <pxe_call.h>
  28. /** @file
  29. *
  30. * PXE API entry point
  31. */
  32. /* Disambiguate the various error causes */
  33. #define EINFO_EPXENBP \
  34. __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
  35. "External PXE NBP error" )
  36. #define EPXENBP( status ) EPLATFORM ( EINFO_EPXENBP, status )
  37. /** Vector for chaining INT 1A */
  38. extern struct segoff __text16 ( pxe_int_1a_vector );
  39. #define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector )
  40. /** INT 1A handler */
  41. extern void pxe_int_1a ( void );
  42. /** INT 1A hooked flag */
  43. static int int_1a_hooked = 0;
  44. /** PXENV_UNDI_TRANSMIT API call profiler */
  45. static struct profiler pxe_api_tx_profiler __profiler =
  46. { .name = "pxeapi.tx" };
  47. /** PXENV_UNDI_ISR API call profiler */
  48. static struct profiler pxe_api_isr_profiler __profiler =
  49. { .name = "pxeapi.isr" };
  50. /** PXE unknown API call profiler
  51. *
  52. * This profiler can be used to measure the overhead of a dummy PXE
  53. * API call.
  54. */
  55. static struct profiler pxe_api_unknown_profiler __profiler =
  56. { .name = "pxeapi.unknown" };
  57. /** Miscellaneous PXE API call profiler */
  58. static struct profiler pxe_api_misc_profiler __profiler =
  59. { .name = "pxeapi.misc" };
  60. /**
  61. * Handle an unknown PXE API call
  62. *
  63. * @v pxenv_unknown Pointer to a struct s_PXENV_UNKNOWN
  64. * @ret #PXENV_EXIT_FAILURE Always
  65. * @err #PXENV_STATUS_UNSUPPORTED Always
  66. */
  67. static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) {
  68. pxenv_unknown->Status = PXENV_STATUS_UNSUPPORTED;
  69. return PXENV_EXIT_FAILURE;
  70. }
  71. /** Unknown PXE API call list */
  72. struct pxe_api_call pxenv_unknown_api __pxe_api_call =
  73. PXE_API_CALL ( PXENV_UNKNOWN, pxenv_unknown, struct s_PXENV_UNKNOWN );
  74. /**
  75. * Locate PXE API call
  76. *
  77. * @v opcode Opcode
  78. * @ret call PXE API call, or NULL
  79. */
  80. static struct pxe_api_call * find_pxe_api_call ( uint16_t opcode ) {
  81. struct pxe_api_call *call;
  82. for_each_table_entry ( call, PXE_API_CALLS ) {
  83. if ( call->opcode == opcode )
  84. return call;
  85. }
  86. return NULL;
  87. }
  88. /**
  89. * Determine applicable profiler (for debugging)
  90. *
  91. * @v opcode PXE opcode
  92. * @ret profiler Profiler
  93. */
  94. static struct profiler * pxe_api_profiler ( unsigned int opcode ) {
  95. /* Determine applicable profiler */
  96. switch ( opcode ) {
  97. case PXENV_UNDI_TRANSMIT:
  98. return &pxe_api_tx_profiler;
  99. case PXENV_UNDI_ISR:
  100. return &pxe_api_isr_profiler;
  101. case PXENV_UNKNOWN:
  102. return &pxe_api_unknown_profiler;
  103. default:
  104. return &pxe_api_misc_profiler;
  105. }
  106. }
  107. /**
  108. * Dispatch PXE API call
  109. *
  110. * @v bx PXE opcode
  111. * @v es:di Address of PXE parameter block
  112. * @ret ax PXE exit code
  113. */
  114. __asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) {
  115. uint16_t opcode = ix86->regs.bx;
  116. userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
  117. struct profiler *profiler = pxe_api_profiler ( opcode );
  118. struct pxe_api_call *call;
  119. union u_PXENV_ANY params;
  120. PXENV_EXIT_t ret;
  121. /* Start profiling */
  122. profile_start ( profiler );
  123. /* Locate API call */
  124. call = find_pxe_api_call ( opcode );
  125. if ( ! call ) {
  126. DBGC ( &pxe_netdev, "PXENV_UNKNOWN_%04x\n", opcode );
  127. call = &pxenv_unknown_api;
  128. }
  129. /* Copy parameter block from caller */
  130. copy_from_user ( &params, uparams, 0, call->params_len );
  131. /* Set default status in case child routine fails to do so */
  132. params.Status = PXENV_STATUS_FAILURE;
  133. /* Hand off to relevant API routine */
  134. ret = call->entry ( &params );
  135. /* Copy modified parameter block back to caller and return */
  136. copy_to_user ( uparams, 0, &params, call->params_len );
  137. ix86->regs.ax = ret;
  138. /* Stop profiling, if applicable */
  139. profile_stop ( profiler );
  140. }
  141. /**
  142. * Dispatch weak PXE API call with PXE stack available
  143. *
  144. * @v ix86 Registers for PXE call
  145. * @ret present Zero (PXE stack present)
  146. */
  147. int pxe_api_call_weak ( struct i386_all_regs *ix86 ) {
  148. pxe_api_call ( ix86 );
  149. return 0;
  150. }
  151. /**
  152. * Dispatch PXE loader call
  153. *
  154. * @v es:di Address of PXE parameter block
  155. * @ret ax PXE exit code
  156. */
  157. __asmcall void pxe_loader_call ( struct i386_all_regs *ix86 ) {
  158. userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
  159. struct s_UNDI_LOADER params;
  160. PXENV_EXIT_t ret;
  161. /* Copy parameter block from caller */
  162. copy_from_user ( &params, uparams, 0, sizeof ( params ) );
  163. /* Fill in ROM segment address */
  164. ppxe.UNDIROMID.segment = ix86->segs.ds;
  165. /* Set default status in case child routine fails to do so */
  166. params.Status = PXENV_STATUS_FAILURE;
  167. /* Call UNDI loader */
  168. ret = undi_loader ( &params );
  169. /* Copy modified parameter block back to caller and return */
  170. copy_to_user ( uparams, 0, &params, sizeof ( params ) );
  171. ix86->regs.ax = ret;
  172. }
  173. /**
  174. * Calculate byte checksum as used by PXE
  175. *
  176. * @v data Data
  177. * @v size Length of data
  178. * @ret sum Checksum
  179. */
  180. static uint8_t pxe_checksum ( void *data, size_t size ) {
  181. uint8_t *bytes = data;
  182. uint8_t sum = 0;
  183. while ( size-- ) {
  184. sum += *bytes++;
  185. }
  186. return sum;
  187. }
  188. /**
  189. * Initialise !PXE and PXENV+ structures
  190. *
  191. */
  192. static void pxe_init_structures ( void ) {
  193. uint32_t rm_cs_phys = ( rm_cs << 4 );
  194. uint32_t rm_ds_phys = ( rm_ds << 4 );
  195. /* Fill in missing segment fields */
  196. ppxe.EntryPointSP.segment = rm_cs;
  197. ppxe.EntryPointESP.segment = rm_cs;
  198. ppxe.Stack.segment_address = rm_ds;
  199. ppxe.Stack.Physical_address = rm_ds_phys;
  200. ppxe.UNDIData.segment_address = rm_ds;
  201. ppxe.UNDIData.Physical_address = rm_ds_phys;
  202. ppxe.UNDICode.segment_address = rm_cs;
  203. ppxe.UNDICode.Physical_address = rm_cs_phys;
  204. ppxe.UNDICodeWrite.segment_address = rm_cs;
  205. ppxe.UNDICodeWrite.Physical_address = rm_cs_phys;
  206. pxenv.RMEntry.segment = rm_cs;
  207. pxenv.StackSeg = rm_ds;
  208. pxenv.UNDIDataSeg = rm_ds;
  209. pxenv.UNDICodeSeg = rm_cs;
  210. pxenv.PXEPtr.segment = rm_cs;
  211. /* Update checksums */
  212. ppxe.StructCksum -= pxe_checksum ( &ppxe, sizeof ( ppxe ) );
  213. pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) );
  214. }
  215. /** PXE structure initialiser */
  216. struct init_fn pxe_init_fn __init_fn ( INIT_NORMAL ) = {
  217. .initialise = pxe_init_structures,
  218. };
  219. /**
  220. * Activate PXE stack
  221. *
  222. * @v netdev Net device to use as PXE net device
  223. */
  224. void pxe_activate ( struct net_device *netdev ) {
  225. /* Ensure INT 1A is hooked */
  226. if ( ! int_1a_hooked ) {
  227. hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
  228. &pxe_int_1a_vector );
  229. devices_get();
  230. int_1a_hooked = 1;
  231. }
  232. /* Set PXE network device */
  233. pxe_set_netdev ( netdev );
  234. }
  235. /**
  236. * Deactivate PXE stack
  237. *
  238. * @ret rc Return status code
  239. */
  240. int pxe_deactivate ( void ) {
  241. int rc;
  242. /* Clear PXE network device */
  243. pxe_set_netdev ( NULL );
  244. /* Ensure INT 1A is unhooked, if possible */
  245. if ( int_1a_hooked ) {
  246. if ( ( rc = unhook_bios_interrupt ( 0x1a,
  247. (unsigned int) pxe_int_1a,
  248. &pxe_int_1a_vector ))!= 0){
  249. DBG ( "Could not unhook INT 1A: %s\n",
  250. strerror ( rc ) );
  251. return rc;
  252. }
  253. devices_put();
  254. int_1a_hooked = 0;
  255. }
  256. return 0;
  257. }
  258. /** Jump buffer for PXENV_RESTART_TFTP */
  259. rmjmp_buf pxe_restart_nbp;
  260. /**
  261. * Start PXE NBP at 0000:7c00
  262. *
  263. * @ret rc Return status code
  264. */
  265. int pxe_start_nbp ( void ) {
  266. int jmp;
  267. int discard_b, discard_c, discard_d, discard_D;
  268. uint16_t status;
  269. /* Allow restarting NBP via PXENV_RESTART_TFTP */
  270. jmp = rmsetjmp ( pxe_restart_nbp );
  271. if ( jmp )
  272. DBG ( "Restarting NBP (%x)\n", jmp );
  273. /* Far call to PXE NBP */
  274. __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
  275. "movw %%cx, %%es\n\t"
  276. "pushw %%es\n\t"
  277. "pushw %%di\n\t"
  278. "sti\n\t"
  279. "lcall $0, $0x7c00\n\t"
  280. "popl %%ebp\n\t" /* discard */
  281. "popl %%ebp\n\t" /* gcc bug */ )
  282. : "=a" ( status ), "=b" ( discard_b ),
  283. "=c" ( discard_c ), "=d" ( discard_d ),
  284. "=D" ( discard_D )
  285. : "a" ( 0 ), "b" ( __from_text16 ( &pxenv ) ),
  286. "c" ( rm_cs ),
  287. "d" ( virt_to_phys ( &pxenv ) ),
  288. "D" ( __from_text16 ( &ppxe ) )
  289. : "esi", "memory" );
  290. if ( status )
  291. return -EPXENBP ( status );
  292. return 0;
  293. }
  294. REQUIRE_OBJECT ( pxe_preboot );
  295. REQUIRE_OBJECT ( pxe_undi );
  296. REQUIRE_OBJECT ( pxe_udp );
  297. REQUIRE_OBJECT ( pxe_tftp );
  298. REQUIRE_OBJECT ( pxe_file );