switcher.S 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /*P:900 This is the Switcher: code which sits at 0xFFC00000 to do the low-level
  2. * Guest<->Host switch. It is as simple as it can be made, but it's naturally
  3. * very specific to x86.
  4. *
  5. * You have now completed Preparation. If this has whet your appetite; if you
  6. * are feeling invigorated and refreshed then the next, more challenging stage
  7. * can be found in "make Guest". :*/
  8. #include <linux/linkage.h>
  9. #include <asm/asm-offsets.h>
  10. #include "lg.h"
  11. .text
  12. ENTRY(start_switcher_text)
  13. /* %eax points to lguest pages for this CPU. %ebx contains cr3 value.
  14. All normal registers can be clobbered! */
  15. ENTRY(switch_to_guest)
  16. /* Save host segments on host stack. */
  17. pushl %es
  18. pushl %ds
  19. pushl %gs
  20. pushl %fs
  21. /* With CONFIG_FRAME_POINTER, gcc doesn't let us clobber this! */
  22. pushl %ebp
  23. /* Save host stack. */
  24. movl %esp, LGUEST_PAGES_host_sp(%eax)
  25. /* Switch to guest stack: if we get NMI we expect to be there. */
  26. movl %eax, %edx
  27. addl $LGUEST_PAGES_regs, %edx
  28. movl %edx, %esp
  29. /* Switch to guest's GDT, IDT. */
  30. lgdt LGUEST_PAGES_guest_gdt_desc(%eax)
  31. lidt LGUEST_PAGES_guest_idt_desc(%eax)
  32. /* Switch to guest's TSS while GDT still writable. */
  33. movl $(GDT_ENTRY_TSS*8), %edx
  34. ltr %dx
  35. /* Set host's TSS GDT entry to available (clear byte 5 bit 2). */
  36. movl (LGUEST_PAGES_host_gdt_desc+2)(%eax), %edx
  37. andb $0xFD, (GDT_ENTRY_TSS*8 + 5)(%edx)
  38. /* Switch to guest page tables: lguest_pages->state now read-only. */
  39. movl %ebx, %cr3
  40. /* Restore guest regs */
  41. popl %ebx
  42. popl %ecx
  43. popl %edx
  44. popl %esi
  45. popl %edi
  46. popl %ebp
  47. popl %gs
  48. popl %eax
  49. popl %fs
  50. popl %ds
  51. popl %es
  52. /* Skip error code and trap number */
  53. addl $8, %esp
  54. iret
  55. #define SWITCH_TO_HOST \
  56. /* Save guest state */ \
  57. pushl %es; \
  58. pushl %ds; \
  59. pushl %fs; \
  60. pushl %eax; \
  61. pushl %gs; \
  62. pushl %ebp; \
  63. pushl %edi; \
  64. pushl %esi; \
  65. pushl %edx; \
  66. pushl %ecx; \
  67. pushl %ebx; \
  68. /* Load lguest ds segment for convenience. */ \
  69. movl $(LGUEST_DS), %eax; \
  70. movl %eax, %ds; \
  71. /* Figure out where we are, based on stack (at top of regs). */ \
  72. movl %esp, %eax; \
  73. subl $LGUEST_PAGES_regs, %eax; \
  74. /* Put trap number in %ebx before we switch cr3 and lose it. */ \
  75. movl LGUEST_PAGES_regs_trapnum(%eax), %ebx; \
  76. /* Switch to host page tables (host GDT, IDT and stack are in host \
  77. mem, so need this first) */ \
  78. movl LGUEST_PAGES_host_cr3(%eax), %edx; \
  79. movl %edx, %cr3; \
  80. /* Set guest's TSS to available (clear byte 5 bit 2). */ \
  81. andb $0xFD, (LGUEST_PAGES_guest_gdt+GDT_ENTRY_TSS*8+5)(%eax); \
  82. /* Switch to host's GDT & IDT. */ \
  83. lgdt LGUEST_PAGES_host_gdt_desc(%eax); \
  84. lidt LGUEST_PAGES_host_idt_desc(%eax); \
  85. /* Switch to host's stack. */ \
  86. movl LGUEST_PAGES_host_sp(%eax), %esp; \
  87. /* Switch to host's TSS */ \
  88. movl $(GDT_ENTRY_TSS*8), %edx; \
  89. ltr %dx; \
  90. popl %ebp; \
  91. popl %fs; \
  92. popl %gs; \
  93. popl %ds; \
  94. popl %es
  95. /* Return to run_guest_once. */
  96. return_to_host:
  97. SWITCH_TO_HOST
  98. iret
  99. deliver_to_host:
  100. SWITCH_TO_HOST
  101. /* Decode IDT and jump to hosts' irq handler. When that does iret, it
  102. * will return to run_guest_once. This is a feature. */
  103. movl (LGUEST_PAGES_host_idt_desc+2)(%eax), %edx
  104. leal (%edx,%ebx,8), %eax
  105. movzwl (%eax),%edx
  106. movl 4(%eax), %eax
  107. xorw %ax, %ax
  108. orl %eax, %edx
  109. jmp *%edx
  110. /* Real hardware interrupts are delivered straight to the host. Others
  111. cause us to return to run_guest_once so it can decide what to do. Note
  112. that some of these are overridden by the guest to deliver directly, and
  113. never enter here (see load_guest_idt_entry). */
  114. .macro IRQ_STUB N TARGET
  115. .data; .long 1f; .text; 1:
  116. /* Make an error number for most traps, which don't have one. */
  117. .if (\N <> 8) && (\N < 10 || \N > 14) && (\N <> 17)
  118. pushl $0
  119. .endif
  120. pushl $\N
  121. jmp \TARGET
  122. ALIGN
  123. .endm
  124. .macro IRQ_STUBS FIRST LAST TARGET
  125. irq=\FIRST
  126. .rept \LAST-\FIRST+1
  127. IRQ_STUB irq \TARGET
  128. irq=irq+1
  129. .endr
  130. .endm
  131. /* We intercept every interrupt, because we may need to switch back to
  132. * host. Unfortunately we can't tell them apart except by entry
  133. * point, so we need 256 entry points.
  134. */
  135. .data
  136. .global default_idt_entries
  137. default_idt_entries:
  138. .text
  139. IRQ_STUBS 0 1 return_to_host /* First two traps */
  140. IRQ_STUB 2 handle_nmi /* NMI */
  141. IRQ_STUBS 3 31 return_to_host /* Rest of traps */
  142. IRQ_STUBS 32 127 deliver_to_host /* Real interrupts */
  143. IRQ_STUB 128 return_to_host /* System call (overridden) */
  144. IRQ_STUBS 129 255 deliver_to_host /* Other real interrupts */
  145. /* We ignore NMI and return. */
  146. handle_nmi:
  147. addl $8, %esp
  148. iret
  149. ENTRY(end_switcher_text)