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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  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. * Record of the current physical location of the installed copy.
  137. * Used by prot_call in order to return via the current installed copy
  138. * even if Etherboot has been relocated during the protected-mode
  139. * call.
  140. ****************************************************************************
  141. */
  142. EXPORT(librm_base):
  143. librm_base: .long 0
  144. /****************************************************************************
  145. * GDT for initial transition to protected mode
  146. *
  147. * PHYSICAL_CS and PHYSICAL_DS are defined in an external header file.
  148. * We use only those selectors, and construct our GDT to match the
  149. * selector values we're asked to use. Use PHYSICAL_CS=0x08 and
  150. * PHYSICAL_DS=0x10 to minimise the space occupied by this GDT.
  151. *
  152. * Note: pm_gdt is also used to store the location of the
  153. * protected-mode GDT as recorded on entry to prot_to_real.
  154. ****************************************************************************
  155. */
  156. .align 16
  157. pm_gdt:
  158. pm_gdt_limit: .word pm_gdt_length - 1
  159. pm_gdt_addr: .long 0
  160. .word 0 /* padding */
  161. .org pm_gdt + PHYSICAL_CS
  162. pm_gdt_pm_cs:
  163. /* 32 bit protected mode code segment, physical addresses */
  164. .word 0xffff, 0
  165. .byte 0, 0x9f, 0xcf, 0
  166. .org pm_gdt + PHYSICAL_DS
  167. pm_gdt_pm_ds:
  168. /* 32 bit protected mode data segment, physical addresses */
  169. .word 0xffff,0
  170. .byte 0,0x93,0xcf,0
  171. pm_gdt_end:
  172. .equ pm_gdt_length, pm_gdt_end - pm_gdt
  173. /****************************************************************************
  174. * GDT for transition to real mode
  175. *
  176. * This is used primarily to set 64kB segment limits. Define
  177. * FLATTEN_REAL_MODE if you want to use so-called "flat real mode"
  178. * with 4GB limits instead. The base address of each of the segments
  179. * will be adjusted at run-time.
  180. *
  181. * NOTE: This must be located before prot_to_real, otherwise gas
  182. * throws a "can't handle non absolute segment in `ljmp'" error due to
  183. * not knowing the value of RM_CS when the ljmp is encountered.
  184. *
  185. * Note also that putting ".word rm_gdt_end - rm_gdt - 1" directly
  186. * into rm_gdt_limit, rather than going via rm_gdt_length, will also
  187. * produce the "non absolute segment" error. This is most probably a
  188. * bug in gas.
  189. ****************************************************************************
  190. */
  191. #ifdef FLATTEN_REAL_MODE
  192. #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
  193. #else
  194. #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
  195. #endif
  196. .align 16
  197. rm_gdt:
  198. rm_gdt_limit: .word rm_gdt_length - 1
  199. rm_gdt_base: .long 0
  200. .word 0 /* padding */
  201. rm_gdt_rm_cs: /* 16 bit real mode code segment */
  202. .equ RM_CS, rm_gdt_rm_cs - rm_gdt
  203. .word 0xffff,(0&0xffff)
  204. .byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
  205. rm_gdt_rm_ds: /* 16 bit real mode data segment */
  206. .equ RM_DS, rm_gdt_rm_ds - rm_gdt
  207. .word 0xffff,(0&0xffff)
  208. .byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
  209. rm_gdt_end:
  210. .equ rm_gdt_length, rm_gdt_end - rm_gdt
  211. /****************************************************************************
  212. * real_to_prot (real-mode far call)
  213. *
  214. * Switch from 16-bit real-mode to 32-bit protected mode with flat
  215. * physical addresses. %esp is restored from the saved pm_esp. All
  216. * segment registers are set to flat physical-mode values. All other
  217. * registers are preserved. Interrupts are disabled.
  218. *
  219. * Note that this routine can be called *without* having first set up
  220. * a stored pm_esp or stored GDT. If you do this, real_to_prot will
  221. * return with a temporary stack that is only *FOUR BYTES* in size.
  222. * This is just enough to enable you to do a "call 1f; popl %ebp"
  223. * sequence in order to find out your physical address and then load a
  224. * proper 32-bit protected-mode stack pointer. Do *NOT* use more than
  225. * four bytes since this will overwrite code in librm!
  226. *
  227. * Parameters: none
  228. ****************************************************************************
  229. */
  230. .code16
  231. EXPORT(real_to_prot):
  232. /* Disable interrupts */
  233. cli
  234. /* Set %ds = %cs, for easier access to variables */
  235. pushw %cs
  236. popw %ds
  237. /* Preserve registers */
  238. movl %eax, %ds:OFFSET(save_eax)
  239. movl %ebx, %ds:OFFSET(save_ebx)
  240. /* Extract real-mode far return address from stack */
  241. popl %ds:OFFSET(save_retaddr)
  242. /* Record real-mode stack pointer */
  243. movw %sp, %ds:OFFSET(rm_sp)
  244. pushw %ss
  245. popw %ds:OFFSET(rm_ss)
  246. /* Physical base address of librm to %ebx */
  247. xorl %ebx, %ebx
  248. movw %cs, %bx
  249. shll $4, %ebx
  250. /* Record physical base address of librm */
  251. movl %ebx, %ds:OFFSET(librm_base)
  252. /* Check base address of stored protected-mode GDT. If it's
  253. * zero, set it up to use our internal GDT (with physical
  254. * segments only).
  255. */
  256. movl %ds:OFFSET(pm_gdt_addr), %eax
  257. testl %eax, %eax
  258. jnz 1f
  259. /* Use internal GDT */
  260. movl %ebx, %eax
  261. addl $OFFSET(pm_gdt), %eax
  262. movl %eax, %ds:OFFSET(pm_gdt_addr)
  263. 1:
  264. /* Set up protected-mode continuation address on real-mode stack */
  265. pushl $PHYSICAL_CS
  266. movl %ebx, %eax
  267. addl $OFFSET(1f), %eax
  268. pushl %eax
  269. /* Restore protected-mode GDT */
  270. data32 lgdt %ds:OFFSET(pm_gdt)
  271. /* Switch to protected mode */
  272. movl %cr0, %eax
  273. orb $CR0_PE, %al
  274. movl %eax, %cr0
  275. /* Flush prefetch queue and reload %cs:eip */
  276. data32 lret
  277. 1: .code32
  278. /* Set up protected-mode stack and data segments */
  279. movw $PHYSICAL_DS, %ax
  280. movw %ax, %ds
  281. movw %ax, %es
  282. movw %ax, %fs
  283. movw %ax, %gs
  284. movw %ax, %ss
  285. /* Switch to saved protected-mode stack. Note that there may
  286. * not actually *be* a saved protected-mode stack.
  287. */
  288. movl OFFSET(pm_esp)(%ebx), %esp
  289. testl %esp, %esp
  290. jnz 1f
  291. /* No stack - use save_retaddr as a 4-byte temporary stack */
  292. leal OFFSET(save_retaddr+4)(%ebx), %esp
  293. 1:
  294. /* Convert real-mode far return address to physical address
  295. * and place on stack
  296. */
  297. pushl OFFSET(save_retaddr)(%ebx)
  298. xorl %eax, %eax
  299. xchgw 2(%esp), %ax
  300. shll $4, %eax
  301. addl %eax, 0(%esp)
  302. /* Restore registers and return */
  303. movl OFFSET(save_eax)(%ebx), %eax
  304. movl OFFSET(save_ebx)(%ebx), %ebx
  305. ret
  306. /****************************************************************************
  307. * prot_to_real (protected-mode near call, physical addresses)
  308. *
  309. * Switch from 32-bit protected mode with flat physical addresses to
  310. * 16-bit real mode. %ss:sp is restored from the saved rm_ss and
  311. * rm_sp. %cs is set such that %cs:0000 is the start of librm. All
  312. * other segment registers are set to %ss. All other registers are
  313. * preserved. Interrupts are *not* enabled, since we want to be able
  314. * to use this routine inside an ISR.
  315. *
  316. * Note that since %cs:0000 points to the start of librm on exit, it
  317. * follows that the code calling prot_to_real must be located within
  318. * 64kB of the start of librm.
  319. *
  320. * Parameters: none
  321. ****************************************************************************
  322. */
  323. .code32
  324. EXPORT(prot_to_real):
  325. /* Calculate physical base address of librm in %ebx, preserve
  326. * original %eax and %ebx in save_eax and save_ebx
  327. */
  328. pushl %ebx
  329. call 1f
  330. 1: popl %ebx
  331. subl $OFFSET(1b), %ebx
  332. popl OFFSET(save_ebx)(%ebx)
  333. movl %eax, OFFSET(save_eax)(%ebx)
  334. /* Record physical base address of librm */
  335. movl %ebx, OFFSET(librm_base)(%ebx)
  336. /* Extract return address from the stack, convert to offset
  337. * within librm and save in save_retaddr
  338. */
  339. popl %eax
  340. subl %ebx, %eax
  341. movl %eax, OFFSET(save_retaddr)(%ebx)
  342. /* Record protected-mode stack pointer */
  343. movl %esp, OFFSET(pm_esp)(%ebx)
  344. /* Record protected-mode GDT */
  345. sgdt OFFSET(pm_gdt)(%ebx)
  346. /* Set up real-mode GDT */
  347. leal OFFSET(rm_gdt)(%ebx), %eax
  348. movl %eax, OFFSET(rm_gdt_base)(%ebx)
  349. movl %ebx, %eax
  350. rorl $16, %eax
  351. movw %bx, OFFSET(rm_gdt_rm_cs+2)(%ebx)
  352. movb %al, OFFSET(rm_gdt_rm_cs+4)(%ebx)
  353. movw %bx, OFFSET(rm_gdt_rm_ds+2)(%ebx)
  354. movb %al, OFFSET(rm_gdt_rm_ds+4)(%ebx)
  355. /* Switch to real-mode GDT and reload segment registers to get
  356. * 64kB limits. Stack is invalidated by this process.
  357. */
  358. lgdt OFFSET(rm_gdt)(%ebx)
  359. ljmp $RM_CS, $1f
  360. 1: .code16
  361. movw $RM_DS, %ax
  362. movw %ax, %ds
  363. movw %ax, %es
  364. movw %ax, %fs
  365. movw %ax, %gs
  366. movw %ax, %ss
  367. /* Calculate real-mode code segment in %ax and store in ljmp
  368. * instruction
  369. */
  370. movl %ebx, %eax
  371. shrl $4, %eax
  372. movw %ax, OFFSET(p2r_ljmp) + 3
  373. /* Switch to real mode */
  374. movl %cr0, %ebx
  375. andb $0!CR0_PE, %bl
  376. movl %ebx, %cr0
  377. /* Intersegment jump to flush prefetch queue and reload
  378. * %cs:eip. The segment gets filled in by the above code. We
  379. * can't just use lret to achieve this, because we have no
  380. * stack at the moment.
  381. */
  382. p2r_ljmp:
  383. ljmp $0, $OFFSET(1f)
  384. 1:
  385. /* Set %ds to point to code segment for easier data access */
  386. movw %ax, %ds
  387. /* Restore registers */
  388. movl OFFSET(save_eax), %eax
  389. movl OFFSET(save_ebx), %ebx
  390. /* Set up real-mode data segments and stack */
  391. movw OFFSET(rm_ss), %ss
  392. movw OFFSET(rm_sp), %sp
  393. pushw %ss
  394. pushw %ss
  395. pushw %ss
  396. pushw %ss
  397. popw %ds
  398. popw %es
  399. popw %fs
  400. popw %gs
  401. /* Set up return address on stack and return */
  402. pushw %cs:OFFSET(save_retaddr)
  403. ret
  404. /****************************************************************************
  405. * prot_call (real-mode far call)
  406. *
  407. * Call a specific C function in the protected-mode code. The
  408. * prototype of the C function must be
  409. * void function ( struct real_mode_regs *rm_regs );
  410. * rm_regs will point to a struct containing the real-mode registers
  411. * at entry to prot_call.
  412. *
  413. * All registers will be preserved across prot_call(), unless the C
  414. * function explicitly overwrites values in rm_regs. Interrupt status
  415. * will also be preserved. Gate A20 will be enabled.
  416. *
  417. * The protected-mode code may install librm to a new location. If it
  418. * does so, it must update librm_base in *this* copy of librm to point
  419. * to the new physical location. prot_call will then return via the
  420. * newly installed copy.
  421. *
  422. * Note that when Etherboot performs its initial relocation, "*this*"
  423. * copy in the above paragraph will refer to the "master" copy, since
  424. * that is the initial installed copy. Etherboot will return to
  425. * prot_call using a virtual address, so will return to the master
  426. * copy in high memory (rather than the original copy in base memory).
  427. * The master copy in high memory will have the physical address of
  428. * the newly installed copy in librm_base, since install_librm()
  429. * writes it there. Thus, Etherboot's initialise() function will
  430. * return to the master copy of prot_call(), which will then jump to
  431. * the installed copy.
  432. *
  433. * It works, trust me.
  434. *
  435. * Parameters:
  436. * function : virtual address of protected-mode function to call
  437. *
  438. * Example usage:
  439. * pushl $pxe_api_call
  440. * lcall $LIBRM_SEGMENT, $prot_call
  441. * addw $4, %sp
  442. * to call in to the C function
  443. * void pxe_api_call ( struct real_mode_regs *rm_regs );
  444. ****************************************************************************
  445. */
  446. #define PC_OFFSET_RM_REGS ( 0 )
  447. #define PC_OFFSET_RETADDR ( PC_OFFSET_RM_REGS + SIZEOF_REAL_MODE_REGS )
  448. #define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
  449. .code16
  450. EXPORT(prot_call):
  451. /* Preserve registers and flags on RM stack */
  452. pushfl
  453. pushal
  454. pushw %gs
  455. pushw %fs
  456. pushw %es
  457. pushw %ds
  458. pushw %ss
  459. pushw %cs
  460. /* Record RM stack pointer */
  461. xorl %ebp, %ebp
  462. movw %sp, %bp
  463. /* Physical address of RM stack pointer to %esi */
  464. xorl %esi, %esi
  465. pushw %ss
  466. popw %si
  467. shll $4, %esi
  468. addl %ebp, %esi
  469. /* Address of pmode function to %ebx */
  470. movl %ss:(PC_OFFSET_FUNCTION)(%bp), %ebx
  471. /* Switch to protected mode */
  472. pushw %cs
  473. call real_to_prot
  474. .code32
  475. /* Copy rm_regs from RM stack to PM stack */
  476. movl $SIZEOF_REAL_MODE_REGS, %ecx
  477. subl %ecx, %esp
  478. movl %esp, %edi
  479. pushl %esi
  480. cld
  481. rep movsb
  482. popl %edi /* %edi = phys addr of RM copy of rm_regs */
  483. /* Switch to virtual addresses. */
  484. call 1f
  485. jmp 2f
  486. 1: ljmp $VIRTUAL_CS, $_phys_to_virt
  487. 2:
  488. /* Enable A20 line */
  489. pushal
  490. lcall $VIRTUAL_CS, $gateA20_set
  491. popl %eax /* discard */
  492. popal
  493. /* Push &rm_regs on the stack, and call function */
  494. pushl %esp
  495. call *%ebx
  496. popl %eax /* discard */
  497. /* Switch to physical addresses, discard PM register store */
  498. lcall $VIRTUAL_CS, $_virt_to_phys
  499. popl %eax /* discard */
  500. /* Copy rm_regs from PM stack to RM stack, and remove rm_regs
  501. * from PM stack. (%edi still contains physical address of
  502. * rm_regs on RM stack from earlier, since C code preserves
  503. * %edi).
  504. */
  505. movl %esp, %esi
  506. movl $SIZEOF_REAL_MODE_REGS, %ecx
  507. cld
  508. rep movsb
  509. movl %esi, %esp /* remove rm_regs from PM stack */
  510. /* Obtain physical base address of installed copy of librm in
  511. * %ebx. (It's possible that this *isn't* the physical base
  512. * address of the copy we're currently executing in, because
  513. * the protected-mode call could have moved librm. If it does
  514. * so, it must update librm_base in our copy to reflect the
  515. * new location.
  516. */
  517. call 1f
  518. 1: popl %ebp
  519. movl OFFSET(librm_base-1b)(%ebp), %ebx
  520. /* Jump to running in installed copy of librm */
  521. addl $OFFSET(1f), %ebx
  522. jmp *%ebx
  523. 1:
  524. /* Switch to real mode */
  525. call prot_to_real
  526. .code16
  527. /* Restore registers and flags, and return */
  528. popw %ax /* skip %cs */
  529. popw %ax /* skip %ss */
  530. popw %ds
  531. popw %es
  532. popw %fs
  533. popw %gs
  534. popal
  535. popfl
  536. lret
  537. /****************************************************************************
  538. * real_call (protected-mode near call, virtual addresses)
  539. *
  540. * Call a real-mode function from protected-mode code.
  541. *
  542. * The non-segment register values will be passed directly to the
  543. * real-mode code. The segment registers will be set as per
  544. * prot_to_real. The non-segment register values set by the real-mode
  545. * function will be passed back to the protected-mode caller. A
  546. * result of this is that this routine cannot be called directly from
  547. * C code, since it clobbers registers that the C ABI expects the
  548. * callee to preserve. Gate A20 will be re-enabled in case the
  549. * real-mode routine disabled it.
  550. *
  551. * librm.h defines two convenient macros for using real_call:
  552. * REAL_CALL and REAL_EXEC. See librm.h and realmode.h for details
  553. * and examples.
  554. *
  555. * Parameters:
  556. * far pointer to real-mode function to call
  557. *
  558. * Returns: none
  559. ****************************************************************************
  560. */
  561. #define RC_OFFSET_PRESERVE_REGS ( 0 )
  562. #define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + 8 )
  563. #define RC_OFFSET_RM_FUNCTION ( RC_OFFSET_RETADDR + 4 )
  564. .code32
  565. EXPORT(real_call):
  566. /* Preserve registers */
  567. pushl %ebp
  568. pushl %eax
  569. /* Switch to physical addresses */
  570. lcall $VIRTUAL_CS, $_virt_to_phys
  571. addl $4, %esp
  572. /* Extract real-mode function address and store in ljmp instruction */
  573. call 1f
  574. 1: popl %ebp
  575. movl RC_OFFSET_RM_FUNCTION(%esp), %eax
  576. movl %eax, (rc_ljmp + 1 - 1b)(%ebp)
  577. /* Restore registers */
  578. popl %eax
  579. popl %ebp
  580. /* Switch to real mode, preserving non-segment registers */
  581. call prot_to_real
  582. .code16
  583. /* Far call to real-mode routine */
  584. pushw %cs
  585. call rc_ljmp
  586. jmp 2f
  587. rc_ljmp:
  588. ljmp $0, $0 /* address filled in by above code */
  589. 2:
  590. /* Switch to protected mode */
  591. pushw %cs
  592. call real_to_prot
  593. .code32
  594. /* Switch to virtual addresses */
  595. call 1f
  596. jmp 2f
  597. 1: ljmp $VIRTUAL_CS, $_phys_to_virt
  598. 2:
  599. /* Enable A20 line */
  600. pushal
  601. lcall $VIRTUAL_CS, $gateA20_set
  602. popl %eax /* discard */
  603. popal
  604. /* Return */
  605. ret
  606. /****************************************************************************
  607. * Relocation lock counter
  608. *
  609. * librm may be moved in base memory only when this counter is zero.
  610. * The counter gets incremented whenever a reference to librm is
  611. * generated (e.g. a real_call is made, resulting in a return address
  612. * pointing to librm being placed on the stack), and decremented when
  613. * the reference goes out of scope (e.g. the real_call returns).
  614. ****************************************************************************
  615. */
  616. EXPORT(librm_ref_count): .byte 0
  617. /****************************************************************************
  618. * Stored real-mode and protected-mode stack pointers
  619. *
  620. * The real-mode stack pointer is stored here whenever real_to_prot
  621. * is called and restored whenever prot_to_real is called. The
  622. * converse happens for the protected-mode stack pointer.
  623. *
  624. * Despite initial appearances this scheme is, in fact re-entrant,
  625. * because program flow dictates that we always return via the point
  626. * we left by. For example:
  627. * PXE API call entry
  628. * 1 real => prot
  629. * ...
  630. * Print a text string
  631. * ...
  632. * 2 prot => real
  633. * INT 10
  634. * 3 real => prot
  635. * ...
  636. * ...
  637. * 4 prot => real
  638. * PXE API call exit
  639. *
  640. * At point 1, the RM mode stack value, say RPXE, is stored in
  641. * rm_ss,sp. We want this value to still be present in rm_ss,sp when
  642. * we reach point 4.
  643. *
  644. * At point 2, the RM stack value is restored from RPXE. At point 3,
  645. * the RM stack value is again stored in rm_ss,sp. This *does*
  646. * overwrite the RPXE that we have stored there, but it's the same
  647. * value, since the code between points 2 and 3 has managed to return
  648. * to us.
  649. ****************************************************************************
  650. */
  651. EXPORT(rm_stack): /* comprises rm_ss and rm_sp */
  652. rm_sp: .word 0
  653. rm_ss: .word 0
  654. EXPORT(pm_stack):
  655. pm_esp: .long 0
  656. /****************************************************************************
  657. * Temporary variables
  658. ****************************************************************************
  659. */
  660. save_eax: .long 0
  661. save_ebx: .long 0
  662. save_retaddr: .long 0
  663. /****************************************************************************
  664. * End of librm
  665. ****************************************************************************
  666. */
  667. _librm_end:
  668. .globl _librm_size
  669. .equ _librm_size, _librm_end - _librm_start