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.

virtaddr.S 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /*
  2. * Functions to support the virtual addressing method of relocation
  3. * that Etherboot uses.
  4. *
  5. */
  6. #include "virtaddr.h"
  7. .arch i386
  8. /****************************************************************************
  9. * GDT for initial transition to protected mode
  10. *
  11. * The segment values, PHYSICAL_CS et al, are defined in an external
  12. * header file virtaddr.h, since they need to be shared with librm.
  13. ****************************************************************************
  14. */
  15. .data
  16. .align 16
  17. gdt:
  18. gdt_limit: .word gdt_length - 1
  19. gdt_addr: .long 0
  20. .word 0 /* padding */
  21. .org gdt + PHYSICAL_CS
  22. physical_cs:
  23. /* 32 bit protected mode code segment, physical addresses */
  24. .word 0xffff,0
  25. .byte 0,0x9f,0xcf,0
  26. .org gdt + PHYSICAL_DS
  27. physical_ds:
  28. /* 32 bit protected mode data segment, physical addresses */
  29. .word 0xffff,0
  30. .byte 0,0x93,0xcf,0
  31. .org gdt + VIRTUAL_CS
  32. virtual_cs:
  33. /* 32 bit protected mode code segment, virtual addresses */
  34. .word 0xffff,0
  35. .byte 0,0x9f,0xcf,0
  36. .org gdt + VIRTUAL_DS
  37. virtual_ds:
  38. /* 32 bit protected mode data segment, virtual addresses */
  39. .word 0xffff,0
  40. .byte 0,0x93,0xcf,0
  41. #ifdef CONFIG_X86_64
  42. .org gdt + LONG_CS
  43. long_cs:
  44. /* 64bit long mode code segment, base 0 */
  45. .word 0xffff, 0
  46. .byte 0x00, 0x9f, 0xaf , 0x00
  47. .org gdt + LONG_DS
  48. long_ds:
  49. /* 64bit long mode data segment, base 0 */
  50. .word 0xffff, 0
  51. .byte 0x00, 0x93, 0xcf, 0x00
  52. #endif /* CONFIG_X86_64 */
  53. gdt_end:
  54. .equ gdt_length, gdt_end - gdt
  55. /* The virtual address offset */
  56. .globl virt_offset
  57. virt_offset: .long 0
  58. .text
  59. .code32
  60. /****************************************************************************
  61. * run_here (flat physical addressing, position-independent)
  62. *
  63. * Set up a GDT to run Etherboot at the current location with virtual
  64. * addressing. This call does not switch to virtual addresses or move
  65. * the stack pointer. The GDT will be located within the copy of
  66. * Etherboot. All registers are preserved.
  67. *
  68. * This gets called at startup and at any subsequent relocation of
  69. * Etherboot.
  70. *
  71. * Parameters: none
  72. ****************************************************************************
  73. */
  74. .globl run_here
  75. run_here:
  76. /* Preserve registers */
  77. pushl %eax
  78. pushl %ebp
  79. /* Find out where we're running */
  80. call 1f
  81. 1: popl %ebp
  82. subl $1b, %ebp
  83. /* Store as virt_offset */
  84. movl %ebp, virt_offset(%ebp)
  85. /* Set segment base addresses in GDT */
  86. leal virtual_cs(%ebp), %eax
  87. pushl %eax
  88. pushl %ebp
  89. call set_seg_base
  90. popl %eax /* discard */
  91. popl %eax /* discard */
  92. /* Set physical location of GDT */
  93. leal gdt(%ebp), %eax
  94. movl %eax, gdt_addr(%ebp)
  95. /* Load the new GDT */
  96. lgdt gdt(%ebp)
  97. /* Reload new flat physical segment registers */
  98. movl $PHYSICAL_DS, %eax
  99. movl %eax, %ds
  100. movl %eax, %es
  101. movl %eax, %fs
  102. movl %eax, %gs
  103. movl %eax, %ss
  104. /* Restore registers, convert return address to far return
  105. * address.
  106. */
  107. popl %ebp
  108. movl $PHYSICAL_CS, %eax
  109. xchgl %eax, 4(%esp) /* cs now on stack, ret offset now in eax */
  110. xchgl %eax, 0(%esp) /* ret offset now on stack, eax restored */
  111. /* Return to caller, reloading %cs with new value */
  112. lret
  113. /****************************************************************************
  114. * set_seg_base (any addressing, position-independent)
  115. *
  116. * Set the base address of a pair of segments in the GDT. This relies
  117. * on the layout of the GDT being (CS,DS) pairs.
  118. *
  119. * Parameters:
  120. * uint32_t base_address
  121. * struct gdt_entry * code_segment
  122. * Returns:
  123. * none
  124. ****************************************************************************
  125. */
  126. .globl set_seg_base
  127. set_seg_base:
  128. pushl %eax
  129. pushl %ebx
  130. movl 12(%esp), %eax /* %eax = base address */
  131. movl 16(%esp), %ebx /* %ebx = &code_descriptor */
  132. movw %ax, (0+2)(%ebx) /* CS base bits 0-15 */
  133. movw %ax, (8+2)(%ebx) /* DS base bits 0-15 */
  134. shrl $16, %eax
  135. movb %al, (0+4)(%ebx) /* CS base bits 16-23 */
  136. movb %al, (8+4)(%ebx) /* DS base bits 16-23 */
  137. movb %ah, (0+7)(%ebx) /* CS base bits 24-31 */
  138. movb %ah, (8+7)(%ebx) /* DS base bits 24-31 */
  139. popl %ebx
  140. popl %eax
  141. ret
  142. /****************************************************************************
  143. * _virt_to_phys (virtual addressing)
  144. *
  145. * Switch from virtual to flat physical addresses. %esp is adjusted
  146. * to a physical value. Segment registers are set to flat physical
  147. * selectors. All other registers are preserved. Flags are
  148. * preserved.
  149. *
  150. * Parameters: none
  151. * Returns: none
  152. ****************************************************************************
  153. */
  154. .globl _virt_to_phys
  155. _virt_to_phys:
  156. /* Preserve registers and flags */
  157. pushfl
  158. pushl %eax
  159. pushl %ebp
  160. /* Change return address to a physical address */
  161. movl virt_offset, %ebp
  162. addl %ebp, 12(%esp)
  163. /* Switch to physical code segment */
  164. pushl $PHYSICAL_CS
  165. leal 1f(%ebp), %eax
  166. pushl %eax
  167. lret
  168. 1:
  169. /* Reload other segment registers and adjust %esp */
  170. movl $PHYSICAL_DS, %eax
  171. movl %eax, %ds
  172. movl %eax, %es
  173. movl %eax, %fs
  174. movl %eax, %gs
  175. movl %eax, %ss
  176. addl %ebp, %esp
  177. /* Restore registers and flags, and return */
  178. popl %ebp
  179. popl %eax
  180. popfl
  181. ret
  182. /****************************************************************************
  183. * _phys_to_virt (flat physical addressing)
  184. *
  185. * Switch from flat physical to virtual addresses. %esp is adjusted
  186. * to a virtual value. Segment registers are set to virtual
  187. * selectors. All other registers are preserved. Flags are
  188. * preserved.
  189. *
  190. * Note that this depends on the GDT already being correctly set up
  191. * (e.g. by a call to run_here()).
  192. *
  193. * Parameters: none
  194. * Returns: none
  195. ****************************************************************************
  196. */
  197. .globl _phys_to_virt
  198. _phys_to_virt:
  199. /* Preserve registers and flags */
  200. pushfl
  201. pushl %eax
  202. pushl %ebp
  203. /* Switch to virtual code segment */
  204. ljmp $VIRTUAL_CS, $1f
  205. 1:
  206. /* Reload data segment registers */
  207. movl $VIRTUAL_DS, %eax
  208. movl %eax, %ds
  209. movl %eax, %es
  210. movl %eax, %fs
  211. movl %eax, %gs
  212. /* Reload stack segment and adjust %esp */
  213. movl virt_offset, %ebp
  214. movl %eax, %ss
  215. subl %ebp, %esp
  216. /* Change the return address to a virtual address */
  217. subl %ebp, 12(%esp)
  218. /* Restore registers and flags, and return */
  219. popl %ebp
  220. popl %eax
  221. popfl
  222. ret
  223. /****************************************************************************
  224. * relocate_to (virtual addressing)
  225. *
  226. * Relocate Etherboot to the specified address. The runtime image
  227. * (excluding the prefix, decompressor and compressed image) is copied
  228. * to a new location, and execution continues in the new copy. This
  229. * routine is designed to be called from C code.
  230. *
  231. * Parameters:
  232. * uint32_t new_phys_addr
  233. ****************************************************************************
  234. */
  235. .globl relocate_to
  236. relocate_to:
  237. /* Save the callee save registers */
  238. pushl %ebp
  239. pushl %esi
  240. pushl %edi
  241. /* Compute the physical source address and data length */
  242. movl $_text, %esi
  243. movl $_end, %ecx
  244. subl %esi, %ecx
  245. addl virt_offset, %esi
  246. /* Compute the physical destination address */
  247. movl 16(%esp), %edi
  248. /* Switch to flat physical addressing */
  249. call _virt_to_phys
  250. /* Do the copy */
  251. cld
  252. rep movsb
  253. /* Calculate offset to new image */
  254. subl %esi, %edi
  255. /* Switch to executing in new image */
  256. call 1f
  257. 1: popl %ebp
  258. leal (2f-1b)(%ebp,%edi), %eax
  259. jmpl *%eax
  260. 2:
  261. /* Switch to stack in new image */
  262. addl %edi, %esp
  263. /* Call run_here() to set up GDT */
  264. call run_here
  265. /* Switch to virtual addressing */
  266. call _phys_to_virt
  267. /* Restore the callee save registers */
  268. popl %edi
  269. popl %esi
  270. popl %ebp
  271. /* return */
  272. ret