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 20KB

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