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.

librm.S 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. /*
  2. * librm: a library for interfacing to real-mode code
  3. *
  4. * Michael Brown <mbrown@fensystems.co.uk>
  5. *
  6. */
  7. /* Drag in local definitions */
  8. #include "librm.h"
  9. /* Drag in FREE_BASEMEM_HEADER_SIZE */
  10. #include "basemem.h"
  11. /****************************************************************************
  12. * This file defines librm: a block of code that is designed to reside
  13. * permanently in base memory and provide the interface between
  14. * real-mode code running in base memory and protected-mode code
  15. * running in high memory. It provides the following functions:
  16. *
  17. * real_to_prot & switch between real and protected mode
  18. * prot_to_real while running in base memory, preserving
  19. * all non-segment registers
  20. *
  21. * real_call issue a call to a real-mode routine from
  22. * protected-mode code running in high memory
  23. *
  24. * prot_call issue a call to a protected-mode routine from
  25. * real-mode code running in base memory
  26. *
  27. * librm requires the following functions to be present in the
  28. * protected-mode code:
  29. *
  30. * _phys_to_virt Switch from physical to virtual addressing. This
  31. * routine must be position-independent and must
  32. * *not* assume that it is genuinely running with
  33. * flat physical addresses
  34. *
  35. * _virt_to_phys Switch from virtual to physical addresses.
  36. *
  37. * gateA20_set Enable the A20 line to permit access to the odd
  38. * megabytes of RAM. (This function will be called
  39. * with virtual addresses set up).
  40. *
  41. * librm needs to be linked against the protected-mode binary so that
  42. * it can import the symbols for these functions.
  43. *
  44. * librm requires that the protected-mode code set up the following
  45. * segments:
  46. *
  47. * PHYSICAL_CS 32-bit pmode code and data segments with flat
  48. * PHYSICAL_DS physical addresses.
  49. *
  50. * VIRTUAL_CS 32-bit pmode code segment with virtual
  51. * addressing, such that a protected-mode routine
  52. * can always be found at $VIRTUAL_CS:routine.
  53. *
  54. * These segments must be set as #define constants when compiling
  55. * librm. Edit librm.h to change the values.
  56. *
  57. * librm does not know the location of the code executing in high
  58. * memory. It relies on the code running in high memory setting up a
  59. * GDT such that the high-memory code is accessible at virtual
  60. * addresses fixed at compile-time.
  61. *
  62. * librm symbols are exported as absolute values and represent offsets
  63. * into librm. This is the most useful form of the symbols, since
  64. * librm is basically a binary blob that you place somewhere in base
  65. * memory.
  66. *
  67. * librm.h provides convenient ways to use these symbols: you simply
  68. * set the pointer ( char * ) installed_librm to point to wherever
  69. * librm is installed, and can then use e.g. inst_rm_stack just like
  70. * any other variable and have it automatically refer to the value of
  71. * rm_stack in the installed librm. Macro trickery makes this
  72. * completely transparent, and the resulting assembler code is
  73. * amazingly efficient.
  74. *
  75. * Note that librm must be called in genuine real mode, not 16:16 or
  76. * 16:32 protected mode. It makes the assumption that
  77. * physical_address = 16*segment+offset, and also that it can use
  78. * OFFSET(%bp) to access stack variables. The former assumption will
  79. * break in either protected mode, the latter may break in 16:32
  80. * protected mode.
  81. ****************************************************************************
  82. */
  83. /*
  84. * Default values for pmode segments if not defined
  85. */
  86. #ifndef PHYSICAL_CS
  87. #warning "Assuming PHYSICAL_CS = 0x08"
  88. #define PHYSICAL_CS 0x08
  89. #endif
  90. #ifndef PHYSICAL_DS
  91. #warning "Assuming PHYSICAL_DS = 0x10"
  92. #define PHYSICAL_DS 0x10
  93. #endif
  94. #ifndef VIRTUAL_CS
  95. #warning "Assuming VIRTUAL_CS = 0x18"
  96. #define VIRTUAL_CS 0x18
  97. #endif
  98. /* For switches to/from protected mode */
  99. #define CR0_PE 1
  100. /* Size of various C data structures */
  101. #define SIZEOF_I386_SEG_REGS 12
  102. #define SIZEOF_I386_REGS 32
  103. #define SIZEOF_I386_ALL_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
  104. #define SIZEOF_I386_FLAGS 4
  105. #define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_ALL_REGS + SIZEOF_I386_FLAGS )
  106. #define SIZEOF_SEGOFF_T 4
  107. #define SIZEOF_REAL_CALL_PARAMS ( SIZEOF_I386_ALL_REGS + 2 * SIZEOF_SEGOFF_T )
  108. .text
  109. .arch i386
  110. .section ".librm", "awx", @progbits
  111. .align 16
  112. .globl librm
  113. librm:
  114. _librm_start:
  115. #undef OFFSET
  116. #define OFFSET(sym) ( sym - _librm_start )
  117. #undef EXPORT
  118. #define EXPORT(sym) \
  119. .globl sym ; \
  120. .globl _ ## sym ; \
  121. .equ _ ## sym, OFFSET(sym) ; \
  122. sym
  123. /****************************************************************************
  124. * Note that the first sizeof(struct free_base_memory_header) bytes of
  125. * librm will get vapourised by free_base_memory(). Since we need
  126. * librm to continue working even when this happens, we put some
  127. * padding here.
  128. *
  129. * We must also ensure that the total size of librm is <1kB, otherwise
  130. * free_base_memory() will stomp somewhere in the middle of us as
  131. * well...
  132. ****************************************************************************
  133. */
  134. .fill FREE_BASEMEM_HEADER_SIZE, 1, 0
  135. /****************************************************************************
  136. * GDT for initial transition to protected mode
  137. *
  138. * PHYSICAL_CS and PHYSICAL_DS are defined in an external header file.
  139. * We use only those selectors, and construct our GDT to match the
  140. * selector values we're asked to use. Use PHYSICAL_CS=0x08 and
  141. * PHYSICAL_DS=0x10 to minimise the space occupied by this GDT.
  142. *
  143. * Note: pm_gdt is also used to store the location of the
  144. * protected-mode GDT as recorded on entry to prot_to_real.
  145. ****************************************************************************
  146. */
  147. .align 16
  148. pm_gdt:
  149. pm_gdt_limit: .word pm_gdt_length - 1
  150. pm_gdt_addr: .long 0
  151. .word 0 /* padding */
  152. .org pm_gdt + PHYSICAL_CS
  153. pm_gdt_pm_cs:
  154. /* 32 bit protected mode code segment, physical addresses */
  155. .word 0xffff, 0
  156. .byte 0, 0x9f, 0xcf, 0
  157. .org pm_gdt + PHYSICAL_DS
  158. pm_gdt_pm_ds:
  159. /* 32 bit protected mode data segment, physical addresses */
  160. .word 0xffff,0
  161. .byte 0,0x93,0xcf,0
  162. pm_gdt_end:
  163. .equ pm_gdt_length, pm_gdt_end - pm_gdt
  164. /****************************************************************************
  165. * GDT for transition to real mode
  166. *
  167. * This is used primarily to set 64kB segment limits. Define
  168. * FLATTEN_REAL_MODE if you want to use so-called "flat real mode"
  169. * with 4GB limits instead. The base address of each of the segments
  170. * will be adjusted at run-time.
  171. *
  172. * NOTE: This must be located before prot_to_real, otherwise gas
  173. * throws a "can't handle non absolute segment in `ljmp'" error due to
  174. * not knowing the value of RM_CS when the ljmp is encountered.
  175. *
  176. * Note also that putting ".word rm_gdt_end - rm_gdt - 1" directly
  177. * into rm_gdt_limit, rather than going via rm_gdt_length, will also
  178. * produce the "non absolute segment" error. This is most probably a
  179. * bug in gas.
  180. ****************************************************************************
  181. */
  182. #ifdef FLATTEN_REAL_MODE
  183. #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
  184. #else
  185. #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
  186. #endif
  187. .align 16
  188. rm_gdt:
  189. rm_gdt_limit: .word rm_gdt_length - 1
  190. rm_gdt_base: .long 0
  191. .word 0 /* padding */
  192. rm_gdt_rm_cs: /* 16 bit real mode code segment */
  193. .equ RM_CS, rm_gdt_rm_cs - rm_gdt
  194. .word 0xffff,(0&0xffff)
  195. .byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
  196. rm_gdt_rm_ds: /* 16 bit real mode data segment */
  197. .equ RM_DS, rm_gdt_rm_ds - rm_gdt
  198. .word 0xffff,(0&0xffff)
  199. .byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
  200. rm_gdt_end:
  201. .equ rm_gdt_length, rm_gdt_end - rm_gdt
  202. /****************************************************************************
  203. * real_to_prot (real-mode far call)
  204. *
  205. * Switch from 16-bit real-mode to 32-bit protected mode with flat
  206. * physical addresses. %esp is restored from the saved pm_esp. All
  207. * segment registers are set to flat physical-mode values. All other
  208. * registers are preserved. Interrupts are disabled.
  209. *
  210. * Note that this routine can be called *without* having first set up
  211. * a stored pm_esp or stored GDT. If you do this, real_to_prot will
  212. * return with a temporary stack that is only *FOUR BYTES* in size.
  213. * This is just enough to enable you to do a "call 1f; popl %ebp"
  214. * sequence in order to find out your physical address and then load a
  215. * proper 32-bit protected-mode stack pointer. Do *NOT* use more than
  216. * four bytes since this will overwrite code in librm!
  217. *
  218. * Parameters: none
  219. ****************************************************************************
  220. */
  221. .code16
  222. EXPORT(real_to_prot):
  223. /* Disable interrupts */
  224. cli
  225. /* Set %ds = %cs, for easier access to variables */
  226. pushw %cs
  227. popw %ds
  228. /* Preserve registers */
  229. movl %eax, %ds:OFFSET(save_eax)
  230. movl %ebx, %ds:OFFSET(save_ebx)
  231. /* Extract real-mode far return address from stack */
  232. popl %ds:OFFSET(save_retaddr)
  233. /* Record real-mode stack pointer */
  234. movw %sp, %ds:OFFSET(rm_sp)
  235. pushw %ss
  236. popw %ds:OFFSET(rm_ss)
  237. /* Physical base address of librm to %ebx */
  238. xorl %ebx, %ebx
  239. movw %cs, %bx
  240. shll $4, %ebx
  241. /* Check base address of stored protected-mode GDT. If it's
  242. * zero, set it up to use our internal GDT (with physical
  243. * segments only).
  244. */
  245. movl %ds:OFFSET(pm_gdt_addr), %eax
  246. testl %eax, %eax
  247. jnz 1f
  248. /* Use internal GDT */
  249. movl %ebx, %eax
  250. addl $OFFSET(pm_gdt), %eax
  251. movl %eax, %ds:OFFSET(pm_gdt_addr)
  252. 1:
  253. /* Set up protected-mode continuation address on real-mode stack */
  254. pushl $PHYSICAL_CS
  255. movl %ebx, %eax
  256. addl $OFFSET(1f), %eax
  257. pushl %eax
  258. /* Restore protected-mode GDT */
  259. data32 lgdt %ds:OFFSET(pm_gdt)
  260. /* Switch to protected mode */
  261. movl %cr0, %eax
  262. orb $CR0_PE, %al
  263. movl %eax, %cr0
  264. /* Flush prefetch queue and reload %cs:eip */
  265. data32 lret
  266. 1: .code32
  267. /* Set up protected-mode stack and data segments */
  268. movw $PHYSICAL_DS, %ax
  269. movw %ax, %ds
  270. movw %ax, %es
  271. movw %ax, %fs
  272. movw %ax, %gs
  273. movw %ax, %ss
  274. /* Switch to saved protected-mode stack. Note that there may
  275. * not actually *be* a saved protected-mode stack.
  276. */
  277. movl OFFSET(pm_esp)(%ebx), %esp
  278. testl %esp, %esp
  279. jnz 1f
  280. /* No stack - use save_retaddr as a 4-byte temporary stack */
  281. leal OFFSET(save_retaddr+4)(%ebx), %esp
  282. 1:
  283. /* Convert real-mode far return address to physical address
  284. * and place on stack
  285. */
  286. pushl OFFSET(save_retaddr)(%ebx)
  287. xorl %eax, %eax
  288. xchgw 2(%esp), %ax
  289. shll $4, %eax
  290. addl %eax, 0(%esp)
  291. /* Restore registers and return */
  292. movl OFFSET(save_eax)(%ebx), %eax
  293. movl OFFSET(save_ebx)(%ebx), %ebx
  294. ret
  295. /****************************************************************************
  296. * prot_to_real (protected-mode near call, physical addresses)
  297. *
  298. * Switch from 32-bit protected mode with flat physical addresses to
  299. * 16-bit real mode. %ss:sp is restored from the saved rm_ss and
  300. * rm_sp. %cs is set such that %cs:0000 is the start of librm. All
  301. * other segment registers are set to %ss. All other registers are
  302. * preserved. Interrupts are *not* enabled, since we want to be able
  303. * to use this routine inside an ISR.
  304. *
  305. * Note that since %cs:0000 points to the start of librm on exit, it
  306. * follows that the code calling prot_to_real must be located within
  307. * 64kB of the start of librm.
  308. *
  309. * Parameters: none
  310. ****************************************************************************
  311. */
  312. .code32
  313. EXPORT(prot_to_real):
  314. /* Calculate physical base address of librm in %ebx, preserve
  315. * original %eax and %ebx in save_eax and save_ebx
  316. */
  317. pushl %ebx
  318. call 1f
  319. 1: popl %ebx
  320. subl $OFFSET(1b), %ebx
  321. popl OFFSET(save_ebx)(%ebx)
  322. movl %eax, OFFSET(save_eax)(%ebx)
  323. /* Extract return address from the stack, convert to offset
  324. * within librm and save in save_retaddr
  325. */
  326. popl %eax
  327. subl %ebx, %eax
  328. movl %eax, OFFSET(save_retaddr)(%ebx)
  329. /* Record protected-mode stack pointer */
  330. movl %esp, OFFSET(pm_esp)(%ebx)
  331. /* Record protected-mode GDT */
  332. sgdt OFFSET(pm_gdt)(%ebx)
  333. /* Set up real-mode GDT */
  334. leal OFFSET(rm_gdt)(%ebx), %eax
  335. movl %eax, OFFSET(rm_gdt_base)(%ebx)
  336. movl %ebx, %eax
  337. rorl $16, %eax
  338. movw %bx, OFFSET(rm_gdt_rm_cs+2)(%ebx)
  339. movb %al, OFFSET(rm_gdt_rm_cs+4)(%ebx)
  340. movw %bx, OFFSET(rm_gdt_rm_ds+2)(%ebx)
  341. movb %al, OFFSET(rm_gdt_rm_ds+4)(%ebx)
  342. /* Switch to real-mode GDT and reload segment registers to get
  343. * 64kB limits. Stack is invalidated by this process.
  344. */
  345. lgdt OFFSET(rm_gdt)(%ebx)
  346. ljmp $RM_CS, $1f
  347. 1: .code16
  348. movw $RM_DS, %ax
  349. movw %ax, %ds
  350. movw %ax, %es
  351. movw %ax, %fs
  352. movw %ax, %gs
  353. movw %ax, %ss
  354. /* Calculate real-mode code segment in %ax and store in ljmp
  355. * instruction
  356. */
  357. movl %ebx, %eax
  358. shrl $4, %eax
  359. movw %ax, OFFSET(p2r_ljmp) + 3
  360. /* Switch to real mode */
  361. movl %cr0, %ebx
  362. andb $0!CR0_PE, %bl
  363. movl %ebx, %cr0
  364. /* Intersegment jump to flush prefetch queue and reload
  365. * %cs:eip. The segment gets filled in by the above code. We
  366. * can't just use lret to achieve this, because we have no
  367. * stack at the moment.
  368. */
  369. p2r_ljmp:
  370. ljmp $0, $OFFSET(1f)
  371. 1:
  372. /* Set %ds to point to code segment for easier data access */
  373. movw %ax, %ds
  374. /* Restore registers */
  375. movl OFFSET(save_eax), %eax
  376. movl OFFSET(save_ebx), %ebx
  377. /* Set up real-mode data segments and stack */
  378. movw OFFSET(rm_ss), %ss
  379. movw OFFSET(rm_sp), %sp
  380. pushw %ss
  381. pushw %ss
  382. pushw %ss
  383. pushw %ss
  384. popw %ds
  385. popw %es
  386. popw %fs
  387. popw %gs
  388. /* Set up return address on stack and return */
  389. pushw %cs:OFFSET(save_retaddr)
  390. ret
  391. /****************************************************************************
  392. * prot_call (real-mode far call)
  393. *
  394. * Call a specific C function in the protected-mode code. The
  395. * prototype of the C function must be
  396. * void function ( struct real_mode_regs *rm_regs,
  397. * void (*retaddr) (void) );
  398. * rm_regs will point to a struct containing the real-mode registers
  399. * at entry to prot_call. retaddr will point to the (virtual) return
  400. * address from "function". This return address will point into
  401. * librm. It is included so that "function" may, if desired, relocate
  402. * librm and return via the new copy. It must not be directly called
  403. * as a function, i.e. you may not do "*retaddr()"; you must instead
  404. * do something like:
  405. * *retaddr += ( new_librm_location - old_librm_location );
  406. * return;
  407. *
  408. * All registers will be preserved across prot_call(), unless the C
  409. * function explicitly overwrites values in rm_regs. Interrupt status
  410. * will also be preserved. Gate A20 will be enabled.
  411. *
  412. * Parameters:
  413. * function : virtual address of protected-mode function to call
  414. *
  415. * Example usage:
  416. * pushl $pxe_api_call
  417. * lcall $LIBRM_SEGMENT, $prot_call
  418. * addw $4, %sp
  419. * to call in to the C function
  420. * void pxe_api_call ( struct real_mode_regs *rm_regs );
  421. ****************************************************************************
  422. */
  423. #define PC_OFFSET_RM_REGS ( 0 )
  424. #define PC_OFFSET_RETADDR ( PC_OFFSET_RM_REGS + SIZEOF_REAL_MODE_REGS )
  425. #define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
  426. .code16
  427. EXPORT(prot_call):
  428. /* Preserve registers and flags on RM stack */
  429. pushfl
  430. pushal
  431. pushw %gs
  432. pushw %fs
  433. pushw %es
  434. pushw %ds
  435. pushw %ss
  436. pushw %cs
  437. /* Record RM stack pointer */
  438. xorl %ebp, %ebp
  439. movw %sp, %bp
  440. /* Physical address of RM stack pointer to %esi */
  441. xorl %esi, %esi
  442. pushw %ss
  443. popw %si
  444. shll $4, %esi
  445. addl %ebp, %esi
  446. /* Address of pmode function to %ebx */
  447. movl %ss:(PC_OFFSET_FUNCTION)(%bp), %ebx
  448. /* Switch to protected mode */
  449. pushw %cs
  450. call real_to_prot
  451. .code32
  452. /* Copy rm_regs from RM stack to PM stack */
  453. movl $SIZEOF_REAL_MODE_REGS, %ecx
  454. subl %ecx, %esp
  455. movl %esp, %edi
  456. pushl %esi
  457. cld
  458. rep movsb
  459. popl %edi /* %edi = phys addr of RM copy of rm_regs */
  460. /* Switch to virtual addresses. */
  461. call 1f
  462. jmp 2f
  463. 1: ljmp $VIRTUAL_CS, $_phys_to_virt
  464. 2:
  465. /* Enable A20 line */
  466. pushal
  467. lcall $VIRTUAL_CS, $gateA20_set
  468. popl %eax /* discard */
  469. popal
  470. /* Push &rm_regs and &retaddr on the stack, and call function */
  471. movl %esp, %ebp
  472. pushl %esp
  473. subl $12, 0(%esp)
  474. pushl %ebp
  475. call *%ebx
  476. popl %eax /* discard */
  477. popl %eax /* discard */
  478. /* Switch to physical addresses, discard PM register store */
  479. lcall $VIRTUAL_CS, $_virt_to_phys
  480. popl %eax /* discard */
  481. /* Copy rm_regs from PM stack to RM stack, and remove rm_regs
  482. * from PM stack. (%edi still contains physical address of
  483. * rm_regs on RM stack from earlier, since C code preserves
  484. * %edi).
  485. */
  486. movl %esp, %esi
  487. movl $SIZEOF_REAL_MODE_REGS, %ecx
  488. cld
  489. rep movsb
  490. movl %esi, %esp /* remove rm_regs from PM stack */
  491. /* Switch to real mode */
  492. call prot_to_real
  493. .code16
  494. /* Restore registers and flags, and return */
  495. popw %ax /* skip %cs */
  496. popw %ax /* skip %ss */
  497. popw %ds
  498. popw %es
  499. popw %fs
  500. popw %gs
  501. popal
  502. popfl
  503. lret
  504. /****************************************************************************
  505. * real_call (protected-mode near call, virtual addresses)
  506. *
  507. * Call a real-mode function from protected-mode code.
  508. *
  509. * The non-segment register values will be passed directly to the
  510. * real-mode code. The segment registers will be set as per
  511. * prot_to_real. The non-segment register values set by the real-mode
  512. * function will be passed back to the protected-mode caller. A
  513. * result of this is that this routine cannot be called directly from
  514. * C code, since it clobbers registers that the C ABI expects the
  515. * callee to preserve. Gate A20 will be re-enabled in case the
  516. * real-mode routine disabled it.
  517. *
  518. * librm.h defines two convenient macros for using real_call:
  519. * REAL_CALL and REAL_EXEC. See librm.h and realmode.h for details
  520. * and examples.
  521. *
  522. * Parameters:
  523. * far pointer to real-mode function to call
  524. *
  525. * Returns: none
  526. ****************************************************************************
  527. */
  528. #define RC_OFFSET_PRESERVE_REGS ( 0 )
  529. #define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + 8 )
  530. #define RC_OFFSET_RM_FUNCTION ( RC_OFFSET_RETADDR + 4 )
  531. .code32
  532. EXPORT(real_call):
  533. /* Preserve registers */
  534. pushl %ebp
  535. pushl %eax
  536. /* Switch to physical addresses */
  537. lcall $VIRTUAL_CS, $_virt_to_phys
  538. addl $4, %esp
  539. /* Extract real-mode function address and store in ljmp instruction */
  540. call 1f
  541. 1: popl %ebp
  542. movl RC_OFFSET_RM_FUNCTION(%esp), %eax
  543. movl %eax, (rc_ljmp + 1 - 1b)(%ebp)
  544. /* Restore registers */
  545. popl %eax
  546. popl %ebp
  547. /* Switch to real mode, preserving non-segment registers */
  548. call prot_to_real
  549. .code16
  550. /* Far call to real-mode routine */
  551. pushw %cs
  552. call rc_ljmp
  553. jmp 2f
  554. rc_ljmp:
  555. ljmp $0, $0 /* address filled in by above code */
  556. 2:
  557. /* Switch to protected mode */
  558. pushw %cs
  559. call real_to_prot
  560. .code32
  561. /* Switch to virtual addresses */
  562. call 1f
  563. jmp 2f
  564. 1: ljmp $VIRTUAL_CS, $_phys_to_virt
  565. 2:
  566. /* Enable A20 line */
  567. pushal
  568. lcall $VIRTUAL_CS, $gateA20_set
  569. popl %eax /* discard */
  570. popal
  571. /* Return */
  572. ret
  573. /****************************************************************************
  574. * Relocation lock counter
  575. *
  576. * librm may be moved in base memory only when this counter is zero.
  577. * The counter gets incremented whenever a reference to librm is
  578. * generated (e.g. a real_call is made, resulting in a return address
  579. * pointing to librm being placed on the stack), and decremented when
  580. * the reference goes out of scope (e.g. the real_call returns).
  581. ****************************************************************************
  582. */
  583. EXPORT(librm_ref_count): .byte 0
  584. /****************************************************************************
  585. * Stored real-mode and protected-mode stack pointers
  586. *
  587. * The real-mode stack pointer is stored here whenever real_to_prot
  588. * is called and restored whenever prot_to_real is called. The
  589. * converse happens for the protected-mode stack pointer.
  590. *
  591. * Despite initial appearances this scheme is, in fact re-entrant,
  592. * because program flow dictates that we always return via the point
  593. * we left by. For example:
  594. * PXE API call entry
  595. * 1 real => prot
  596. * ...
  597. * Print a text string
  598. * ...
  599. * 2 prot => real
  600. * INT 10
  601. * 3 real => prot
  602. * ...
  603. * ...
  604. * 4 prot => real
  605. * PXE API call exit
  606. *
  607. * At point 1, the RM mode stack value, say RPXE, is stored in
  608. * rm_ss,sp. We want this value to still be present in rm_ss,sp when
  609. * we reach point 4.
  610. *
  611. * At point 2, the RM stack value is restored from RPXE. At point 3,
  612. * the RM stack value is again stored in rm_ss,sp. This *does*
  613. * overwrite the RPXE that we have stored there, but it's the same
  614. * value, since the code between points 2 and 3 has managed to return
  615. * to us.
  616. ****************************************************************************
  617. */
  618. EXPORT(rm_stack): /* comprises rm_ss and rm_sp */
  619. rm_sp: .word 0
  620. rm_ss: .word 0
  621. EXPORT(pm_stack):
  622. pm_esp: .long 0
  623. /****************************************************************************
  624. * Temporary variables
  625. ****************************************************************************
  626. */
  627. save_eax: .long 0
  628. save_ebx: .long 0
  629. save_retaddr: .long 0
  630. /****************************************************************************
  631. * End of librm
  632. ****************************************************************************
  633. */
  634. _librm_end:
  635. .globl _librm_size
  636. .equ _librm_size, _librm_end - _librm_start