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_callbacks.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /* PXE callback mechanisms. This file contains only the portions
  2. * specific to i386: i.e. the low-level mechanisms for calling in from
  3. * an NBP to the PXE stack and for starting an NBP from the PXE stack.
  4. */
  5. #ifdef PXE_EXPORT
  6. #include "etherboot.h"
  7. #include "callbacks.h"
  8. #include "realmode.h"
  9. #include "pxe.h"
  10. #include "pxe_callbacks.h"
  11. #include "pxe_export.h"
  12. #include "hidemem.h"
  13. #include <stdarg.h>
  14. #define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) \
  15. - &pxe_callback_interface \
  16. + (void*)&pxe_stack->arch_data ) )
  17. #define pxe_intercept_int1a INSTALLED(_pxe_intercept_int1a)
  18. #define pxe_intercepted_int1a INSTALLED(_pxe_intercepted_int1a)
  19. #define pxe_pxenv_location INSTALLED(_pxe_pxenv_location)
  20. #define INT1A_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x1a ) ) )
  21. /* The overall size of the PXE stack is ( sizeof(pxe_stack_t) +
  22. * pxe_callback_interface_size + rm_callback_interface_size ).
  23. * Unfortunately, this isn't a compile-time constant, since
  24. * {pxe,rm}_callback_interface_size depend on the length of the
  25. * assembly code in these interfaces.
  26. *
  27. * We used to have a function pxe_stack_size() which returned this
  28. * value. However, it actually needs to be a link-time constant, so
  29. * that it can appear in the UNDIROMID structure in romprefix.S. We
  30. * therefore export the three component sizes as absolute linker
  31. * symbols, get the linker to add them together and generate a new
  32. * absolute symbol _pxe_stack_size. We then import this value into a
  33. * C variable pxe_stack_size, for access from C code.
  34. */
  35. /* gcc won't let us use extended asm outside a function (compiler
  36. * bug), ao we have to put these asm statements inside a dummy
  37. * function.
  38. */
  39. static void work_around_gcc_bug ( void ) __attribute__ ((used));
  40. static void work_around_gcc_bug ( void ) {
  41. /* Export sizeof(pxe_stack_t) as absolute linker symbol */
  42. __asm__ ( ".globl _pxe_stack_t_size" );
  43. __asm__ ( ".equ _pxe_stack_t_size, %c0"
  44. : : "i" (sizeof(pxe_stack_t)) );
  45. }
  46. /* Import _pxe_stack_size absolute linker symbol into C variable */
  47. extern int pxe_stack_size;
  48. __asm__ ( "pxe_stack_size: .long _pxe_stack_size" );
  49. /* Utility routine: byte checksum
  50. */
  51. uint8_t byte_checksum ( void *address, size_t size ) {
  52. unsigned int i, sum = 0;
  53. for ( i = 0; i < size; i++ ) {
  54. sum += ((uint8_t*)address)[i];
  55. }
  56. return (uint8_t)sum;
  57. }
  58. /* install_pxe_stack(): install PXE stack.
  59. *
  60. * Use base = NULL for auto-allocation of base memory
  61. *
  62. * IMPORTANT: no further allocation of base memory should take place
  63. * before the PXE stack is removed. This is to work around a small
  64. * but important deficiency in the PXE specification.
  65. */
  66. pxe_stack_t * install_pxe_stack ( void *base ) {
  67. pxe_t *pxe;
  68. pxenv_t *pxenv;
  69. void *pxe_callback_code;
  70. void (*pxe_in_call_far)(void);
  71. void (*pxenv_in_call_far)(void);
  72. void *rm_callback_code;
  73. void *e820mangler_code;
  74. void *end;
  75. /* If already installed, just return */
  76. if ( pxe_stack != NULL ) return pxe_stack;
  77. /* Allocate base memory if requested to do so
  78. */
  79. if ( base == NULL ) {
  80. base = allot_base_memory ( pxe_stack_size );
  81. if ( base == NULL ) return NULL;
  82. }
  83. /* Round address up to 16-byte physical alignment */
  84. pxe_stack = (pxe_stack_t *)
  85. ( phys_to_virt ( ( virt_to_phys(base) + 0xf ) & ~0xf ) );
  86. /* Zero out allocated stack */
  87. memset ( pxe_stack, 0, sizeof(*pxe_stack) );
  88. /* Calculate addresses for portions of the stack */
  89. pxe = &(pxe_stack->pxe);
  90. pxenv = &(pxe_stack->pxenv);
  91. pxe_callback_code = &(pxe_stack->arch_data);
  92. pxe_in_call_far = _pxe_in_call_far +
  93. ( pxe_callback_code - &pxe_callback_interface );
  94. pxenv_in_call_far = _pxenv_in_call_far +
  95. ( pxe_callback_code - &pxe_callback_interface );
  96. rm_callback_code = pxe_callback_code + pxe_callback_interface_size;
  97. e820mangler_code = (void*)(((int)rm_callback_code +
  98. rm_callback_interface_size + 0xf ) & ~0xf);
  99. end = e820mangler_code + e820mangler_size;
  100. /* Initialise !PXE data structures */
  101. memcpy ( pxe->Signature, "!PXE", 4 );
  102. pxe->StructLength = sizeof(*pxe);
  103. pxe->StructRev = 0;
  104. pxe->reserved_1 = 0;
  105. /* We don't yet have an UNDI ROM ID structure */
  106. pxe->UNDIROMID.segment = 0;
  107. pxe->UNDIROMID.offset = 0;
  108. /* or a BC ROM ID structure */
  109. pxe->BaseROMID.segment = 0;
  110. pxe->BaseROMID.offset = 0;
  111. pxe->EntryPointSP.segment = SEGMENT(pxe_stack);
  112. pxe->EntryPointSP.offset = (void*)pxe_in_call_far - (void*)pxe_stack;
  113. /* No %esp-compatible entry point yet */
  114. pxe->EntryPointESP.segment = 0;
  115. pxe->EntryPointESP.offset = 0;
  116. pxe->StatusCallout.segment = -1;
  117. pxe->StatusCallout.offset = -1;
  118. pxe->reserved_2 = 0;
  119. pxe->SegDescCn = 7;
  120. pxe->FirstSelector = 0;
  121. /* PXE specification doesn't say anything about when the stack
  122. * space should get freed. We work around this by claiming it
  123. * as our data segment as well.
  124. */
  125. pxe->Stack.Seg_Addr = pxe->UNDIData.Seg_Addr = real_mode_stack >> 4;
  126. pxe->Stack.Phy_Addr = pxe->UNDIData.Phy_Addr = real_mode_stack;
  127. pxe->Stack.Seg_Size = pxe->UNDIData.Seg_Size = real_mode_stack_size;
  128. /* Code segment has to be the one containing the data structures... */
  129. pxe->UNDICode.Seg_Addr = SEGMENT(pxe_stack);
  130. pxe->UNDICode.Phy_Addr = virt_to_phys(pxe_stack);
  131. pxe->UNDICode.Seg_Size = end - (void*)pxe_stack;
  132. /* No base code loaded */
  133. pxe->BC_Data.Seg_Addr = 0;
  134. pxe->BC_Data.Phy_Addr = 0;
  135. pxe->BC_Data.Seg_Size = 0;
  136. pxe->BC_Code.Seg_Addr = 0;
  137. pxe->BC_Code.Phy_Addr = 0;
  138. pxe->BC_Code.Seg_Size = 0;
  139. pxe->BC_CodeWrite.Seg_Addr = 0;
  140. pxe->BC_CodeWrite.Phy_Addr = 0;
  141. pxe->BC_CodeWrite.Seg_Size = 0;
  142. pxe->StructCksum -= byte_checksum ( pxe, sizeof(*pxe) );
  143. /* Initialise PXENV+ data structures */
  144. memcpy ( pxenv->Signature, "PXENV+", 6 );
  145. pxenv->Version = 0x201;
  146. pxenv->Length = sizeof(*pxenv);
  147. pxenv->RMEntry.segment = SEGMENT(pxe_stack);
  148. pxenv->RMEntry.offset = (void*)pxenv_in_call_far - (void*)pxe_stack;
  149. pxenv->PMOffset = 0; /* "Do not use" says the PXE spec */
  150. pxenv->PMSelector = 0; /* "Do not use" says the PXE spec */
  151. pxenv->StackSeg = pxenv->UNDIDataSeg = real_mode_stack >> 4;
  152. pxenv->StackSize = pxenv->UNDIDataSize = real_mode_stack_size;
  153. pxenv->BC_CodeSeg = 0;
  154. pxenv->BC_CodeSize = 0;
  155. pxenv->BC_DataSeg = 0;
  156. pxenv->BC_DataSize = 0;
  157. /* UNDIData{Seg,Size} set above */
  158. pxenv->UNDICodeSeg = SEGMENT(pxe_stack);
  159. pxenv->UNDICodeSize = end - (void*)pxe_stack;
  160. pxenv->PXEPtr.segment = SEGMENT(pxe);
  161. pxenv->PXEPtr.offset = OFFSET(pxe);
  162. pxenv->Checksum -= byte_checksum ( pxenv, sizeof(*pxenv) );
  163. /* Mark stack as inactive */
  164. pxe_stack->state = CAN_UNLOAD;
  165. /* Install PXE and RM callback code and E820 mangler */
  166. memcpy ( pxe_callback_code, &pxe_callback_interface,
  167. pxe_callback_interface_size );
  168. install_rm_callback_interface ( rm_callback_code, 0 );
  169. install_e820mangler ( e820mangler_code );
  170. return pxe_stack;
  171. }
  172. /* Use the UNDI data segment as our real-mode stack. This is for when
  173. * we have been loaded via the UNDI loader
  174. */
  175. void use_undi_ds_for_rm_stack ( uint16_t ds ) {
  176. forget_real_mode_stack();
  177. real_mode_stack = virt_to_phys ( VIRTUAL ( ds, 0 ) );
  178. lock_real_mode_stack = 1;
  179. }
  180. /* Activate PXE stack (i.e. hook interrupt vectors). The PXE stack
  181. * *can* be used before it is activated, but it really shoudln't.
  182. */
  183. int hook_pxe_stack ( void ) {
  184. if ( pxe_stack == NULL ) return 0;
  185. if ( pxe_stack->state >= MIDWAY ) return 1;
  186. /* Hook INT15 handler */
  187. hide_etherboot();
  188. /* Hook INT1A handler */
  189. *pxe_intercepted_int1a = *INT1A_VECTOR;
  190. pxe_pxenv_location->segment = SEGMENT(pxe_stack);
  191. pxe_pxenv_location->offset = (void*)&pxe_stack->pxenv
  192. - (void*)pxe_stack;
  193. INT1A_VECTOR->segment = SEGMENT(&pxe_stack->arch_data);
  194. INT1A_VECTOR->offset = (void*)pxe_intercept_int1a
  195. - (void*)&pxe_stack->arch_data;
  196. /* Mark stack as active */
  197. pxe_stack->state = MIDWAY;
  198. return 1;
  199. }
  200. /* Deactivate the PXE stack (i.e. unhook interrupt vectors).
  201. */
  202. int unhook_pxe_stack ( void ) {
  203. if ( pxe_stack == NULL ) return 0;
  204. if ( pxe_stack->state <= CAN_UNLOAD ) return 1;
  205. /* Restore original INT15 and INT1A handlers */
  206. *INT1A_VECTOR = *pxe_intercepted_int1a;
  207. if ( !unhide_etherboot() ) {
  208. /* Cannot unhook INT15. We're up the creek without
  209. * even a suitable log out of which to fashion a
  210. * paddle. There are some very badly behaved NBPs
  211. * that will ignore plaintive pleas such as
  212. * PXENV_KEEP_UNDI and just zero out our code anyway.
  213. * This means they end up vapourising an active INT15
  214. * handler, which is generally not a good thing to do.
  215. */
  216. return 0;
  217. }
  218. /* Mark stack as inactive */
  219. pxe_stack->state = CAN_UNLOAD;
  220. return 1;
  221. }
  222. /* remove_pxe_stack(): remove PXE stack installed by install_pxe_stack()
  223. */
  224. void remove_pxe_stack ( void ) {
  225. /* Ensure stack is deactivated, then free up the memory */
  226. if ( ensure_pxe_state ( CAN_UNLOAD ) ) {
  227. forget_base_memory ( pxe_stack, pxe_stack_size );
  228. pxe_stack = NULL;
  229. } else {
  230. printf ( "Cannot remove PXE stack!\n" );
  231. }
  232. }
  233. /* xstartpxe(): start up a PXE image
  234. */
  235. int xstartpxe ( void ) {
  236. int nbp_exit;
  237. struct {
  238. reg16_t bx;
  239. reg16_t es;
  240. segoff_t pxe;
  241. } PACKED in_stack;
  242. /* Set up registers and stack parameters to pass to PXE NBP */
  243. in_stack.es.word = SEGMENT(&(pxe_stack->pxenv));
  244. in_stack.bx.word = OFFSET(&(pxe_stack->pxenv));
  245. in_stack.pxe.segment = SEGMENT(&(pxe_stack->pxe));
  246. in_stack.pxe.offset = OFFSET(&(pxe_stack->pxe));
  247. /* Real-mode trampoline fragment used to jump to PXE NBP
  248. */
  249. RM_FRAGMENT(jump_to_pxe_nbp,
  250. "popw %bx\n\t"
  251. "popw %es\n\t"
  252. "lcall $" RM_STR(PXE_LOAD_SEGMENT) ", $" RM_STR(PXE_LOAD_OFFSET) "\n\t"
  253. );
  254. /* Call to PXE image */
  255. gateA20_unset();
  256. nbp_exit = real_call ( jump_to_pxe_nbp, &in_stack, NULL );
  257. gateA20_set();
  258. return nbp_exit;
  259. }
  260. int pxe_in_call ( in_call_data_t *in_call_data, va_list params ) {
  261. /* i386 calling conventions; the only two defined by Intel's
  262. * PXE spec.
  263. *
  264. * Assembly code must pass a long containing the PXE version
  265. * code (i.e. 0x201 for !PXE, 0x200 for PXENV+) as the first
  266. * parameter after the in_call opcode. This is used to decide
  267. * whether to take parameters from the stack (!PXE) or from
  268. * registers (PXENV+).
  269. */
  270. uint32_t api_version = va_arg ( params, typeof(api_version) );
  271. uint16_t opcode;
  272. segoff_t segoff;
  273. t_PXENV_ANY *structure;
  274. if ( api_version >= 0x201 ) {
  275. /* !PXE calling convention */
  276. pxe_call_params_t pxe_params
  277. = va_arg ( params, typeof(pxe_params) );
  278. opcode = pxe_params.opcode;
  279. segoff = pxe_params.segoff;
  280. } else {
  281. /* PXENV+ calling convention */
  282. opcode = in_call_data->pm->regs.bx;
  283. segoff.segment = in_call_data->rm->seg_regs.es;
  284. segoff.offset = in_call_data->pm->regs.di;
  285. }
  286. structure = VIRTUAL ( segoff.segment, segoff.offset );
  287. return pxe_api_call ( opcode, structure );
  288. }
  289. #ifdef TEST_EXCLUDE_ALGORITHM
  290. /* This code retained because it's a difficult algorithm to tweak with
  291. * confidence
  292. */
  293. int ___test_exclude ( int start, int len, int estart, int elen, int fixbase );
  294. void __test_exclude ( int start, int len, int estart, int elen, int fixbase ) {
  295. int newrange = ___test_exclude ( start, len, estart, elen, fixbase );
  296. int newstart = ( newrange >> 16 ) & 0xffff;
  297. int newlen = ( newrange & 0xffff );
  298. printf ( "[%x,%x): excluding [%x,%x) %s gives [%x,%x)\n",
  299. start, start + len,
  300. estart, estart + elen,
  301. ( fixbase == 0 ) ? " " : "fb",
  302. newstart, newstart + newlen );
  303. }
  304. void _test_exclude ( int start, int len, int estart, int elen ) {
  305. __test_exclude ( start, len, estart, elen, 0 );
  306. __test_exclude ( start, len, estart, elen, 1 );
  307. }
  308. void test_exclude ( void ) {
  309. _test_exclude ( 0x8000, 0x1000, 0x0400, 0x200 ); /* before */
  310. _test_exclude ( 0x8000, 0x1000, 0x9000, 0x200 ); /* after */
  311. _test_exclude ( 0x8000, 0x1000, 0x7f00, 0x200 ); /* before overlap */
  312. _test_exclude ( 0x8000, 0x1000, 0x8f00, 0x200 ); /* after overlap */
  313. _test_exclude ( 0x8000, 0x1000, 0x8000, 0x200 ); /* align start */
  314. _test_exclude ( 0x8000, 0x1000, 0x8e00, 0x200 ); /* align end */
  315. _test_exclude ( 0x8000, 0x1000, 0x8100, 0x200 ); /* early overlap */
  316. _test_exclude ( 0x8000, 0x1000, 0x8d00, 0x200 ); /* late overlap */
  317. _test_exclude ( 0x8000, 0x1000, 0x7000, 0x3000 ); /* total overlap */
  318. _test_exclude ( 0x8000, 0x1000, 0x8000, 0x1000 ); /* exact overlap */
  319. }
  320. #endif /* TEST_EXCLUDE_ALGORITHM */
  321. #else /* PXE_EXPORT */
  322. /* Define symbols used by the linker scripts, to prevent link errors */
  323. __asm__ ( ".globl _pxe_stack_t_size" );
  324. __asm__ ( ".equ _pxe_stack_t_size, 0" );
  325. #endif /* PXE_EXPORT */