/* * plex86: run multiple x86 operating systems concurrently * Copyright (C) 1999-2001 Kevin P. Lawton * * nexus.S: code to transition between host and monitor/guest * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ .text /* This module consists of relocatable code and data necessary to * effect transitions between the host <--> guest. This information * is purposely stored in this single page, so that we have access * to it during our transitions between the monitor interrupt handler, * and our host. * * I coded the relevant parts to use completely relocatable * accesses to the following fields. This is necessary, so that * we can float this code page anywhere in the monitor's linear * address space. */ /* =============================================================== * NOTE: If you modify ANY of the following fields, you must also * update the corresponding entries in the C typedef 'nexus_t'. * That construct is used from C land to access values in this * relocatable page. */ .globl __nexus_start __nexus_start: __vm: ;.skip 4, 0 __host_gdt_info: ;.skip 6, 0 __host_idt_info: ;.skip 6, 0 __host_jmp_info: ;.skip 6, 0 __host_stack_info: ;.skip 6, 0 __host_ldt_sel: ;.skip 2, 0 __host_tss_sel: ;.skip 2, 0 __host_cr0: ;.skip 4, 0 __host_cr2: ;.skip 4, 0 __host_cr3: ;.skip 4, 0 __host_cr4: ;.skip 4, 0 __mon_gdt_info: ;.skip 6, 0 __mon_idt_info: ;.skip 6, 0 __mon_jmp_info: ;.skip 6, 0 __mon_stack_info: ;.skip 6, 0 __mon_ldt_sel: ;.skip 2, 0 __mon_tss_sel: ;.skip 2, 0 __mon_base: ;.skip 4, 0 __mon_cr0: ;.skip 4, 0 __mon_cr3: ;.skip 4, 0 __mon_cr4: ;.skip 4, 0 __mon_eflags: ;.skip 4, 0 __transition_pde: ;.skip 4, 0 __transition_pde_p_host: ;.skip 4, 0 __transition_pde_p_mon: ;.skip 4, 0 __transition_laddr: ;.skip 4, 0 /* =============================================================== * End NOTE. */ #define OFFSET_OF(field) [field - __nexus_start] /* These are the offsets of the structures above, from the */ /* beginning of this section. */ #define HOST_GDT_INFO OFFSET_OF(__host_gdt_info) #define HOST_IDT_INFO OFFSET_OF(__host_idt_info) #define HOST_JMP_INFO OFFSET_OF(__host_jmp_info) #define HOST_STACK_INFO OFFSET_OF(__host_stack_info) #define HOST_LDT_SEL OFFSET_OF(__host_ldt_sel) #define HOST_TSS_SEL OFFSET_OF(__host_tss_sel) #define HOST_CR0 OFFSET_OF(__host_cr0) #define HOST_CR2 OFFSET_OF(__host_cr2) #define HOST_CR3 OFFSET_OF(__host_cr3) #define HOST_CR4 OFFSET_OF(__host_cr4) #define MON_GDT_INFO OFFSET_OF(__mon_gdt_info) #define MON_IDT_INFO OFFSET_OF(__mon_idt_info) #define MON_JMP_INFO OFFSET_OF(__mon_jmp_info) #define MON_STACK_INFO OFFSET_OF(__mon_stack_info) #define MON_LDT_SEL OFFSET_OF(__mon_ldt_sel) #define MON_TSS_SEL OFFSET_OF(__mon_tss_sel) #define MON_CR0 OFFSET_OF(__mon_cr0) #define MON_CR3 OFFSET_OF(__mon_cr3) #define MON_CR4 OFFSET_OF(__mon_cr4) #define MON_BASE OFFSET_OF(__mon_base) #define TRANSITION_PDE OFFSET_OF(__transition_pde) #define TRANSITION_PDE_P_HOST OFFSET_OF(__transition_pde_p_host) #define TRANSITION_PDE_P_MON OFFSET_OF(__transition_pde_p_mon) #define TRANSITION_LADDR OFFSET_OF(__transition_laddr) /* To make this code page and data accesses to the fields above */ /* relocatable, I use the following conventions. I load EBX with */ /* a pointer to the beginning of this page, to be used with an */ /* access through the CS: segment. We can easily get the */ /* current EIP with a call/pop EBX, so the combination of CS:EBX, */ /* accesses this page no matter where it is located. */ /* ================================================================== */ .globl __host2mon /* Start function __host2mon() */ __host2mon: /* Save host context first, so it can be restored later */ pushfl /* Save host flags */ pushal /* Save host general regs */ pushl %es /* Save host segments */ pushl %ds pushl %fs pushl %gs /* Put EIP of beginning of this section in EBX to be used to */ /* access data. */ call null_call null_call: popl %ebx subl $(OFFSET_OF(null_call)), %ebx /* Create identity mapping of this page into the monitor context */ movl (TRANSITION_PDE_P_HOST)(%ebx), %eax movl (TRANSITION_PDE)(%ebx), %ebp xchgl %ebp, (%eax) /* old PDE saved in %ebp to be restored below */ /* Save host GDT, LDT, IDT, and TSS */ sgdt (HOST_GDT_INFO)(%ebx) sidt (HOST_IDT_INFO)(%ebx) sldt (HOST_LDT_SEL)(%ebx) str (HOST_TSS_SEL)(%ebx) movl %esp, (HOST_STACK_INFO)(%ebx) /* Save host SS:ESP */ movw %ss, (4+HOST_STACK_INFO)(%ebx) /* for later restore */ leal (OFFSET_OF(__host_cs))(%ebx), %eax /* Save the CS:EIP for monitor to */ movl %eax, (HOST_JMP_INFO)(%ebx) /* jump to when reloading host CS. */ movw %cs, (4+HOST_JMP_INFO)(%ebx) /* See __guest2host below. */ /* Save host CRx values */ movl %cr0, %eax movl %cr2, %ecx movl %cr4, %edx movl %cr3, %esi movl %eax, (HOST_CR0)(%ebx) movl %ecx, (HOST_CR2)(%ebx) movl %edx, (HOST_CR4)(%ebx) movl %esi, (HOST_CR3)(%ebx) /* Compute monitor CRx values */ movl (MON_CR0)(%ebx), %eax movl (MON_CR4)(%ebx), %edx movl (MON_CR3)(%ebx), %esi /* Before changing the PSE bit in CR4, we have to switch over */ /* to the new CR3 (this page identity mapped anyways). Otherwise */ /* the processor could flush the TLB, and reload the entry for */ /* this page, only to find it's marked with a 4Meg Page, but we */ /* have that support turned off, before we actually */ /* reloaded CR3! */ movl %esi, %cr3 /* Set monitor CR3 */ movl %eax, %cr0 /* Set monitor CR0 */ movl %edx, %cr4 /* Set monitor CR4 */ movl %esi, %cr3 /* Set monitor CR3 */ jmp null_jmp0 null_jmp0: /* Switch to monitor GDT, LDT, and IDT */ lgdt (MON_GDT_INFO)(%ebx) lidt (MON_IDT_INFO)(%ebx) lldt (MON_LDT_SEL)(%ebx) /* Switch to monitor stack and CS */ /* and jump to the monitor-side nexus page */ lss (MON_STACK_INFO)(%ebx), %esp ljmp (MON_JMP_INFO)(%ebx) .globl __mon_cs __mon_cs: /* Reset DS:EBX to point to the monitor-side nexus page */ movw %ss, %ax movw %ax, %ds /* copy SS to DS */ movw %ax, %es /* copy SS to ES */ movl %esp, %ebx andl $0xfffff000, %ebx /* Clear busy bit of the monitor TSS and switch to it */ movzwl (MON_TSS_SEL)(%ebx), %eax andl $0xfffffff8, %eax addl (MON_GDT_INFO+2)(%ebx), %eax subl (MON_BASE)(%ebx), %eax andl $0xfffffdff, 4(%eax) ltr (MON_TSS_SEL)(%ebx) /* We no longer need the nexus page identity mapped. Fix the mapping */ /* back to the way it should be, in case guest code uses it. */ movl (TRANSITION_PDE_P_MON)(%ebx), %eax movl %ebp, (%eax) /* %ebp still contains the original value */ movl (TRANSITION_LADDR)(%ebx), %eax invlpg (%eax) /* Tell TLB about the change */ /* +++ xxx fix this, need to convert pure laddr to offset */ movl %cr3, %eax /* +++ xxx */ movl %eax, %cr3 /* +++ xxx */ /* */ /* We can now restore the monitor context from it's stack. */ /* */ popl %gs popl %fs popal /* Restore mon general registers */ popfl /* Restore mon eflags */ ret /* Resume execution in monitor exception handler code. */ /* ================================================================== */ .globl __mon2host /* Start function __mon2host() */ __mon2host: pushfl /* Save mon flags */ pushal /* Save mon general registers */ pushl %fs pushl %gs /* Set EBX to point to this nexus page */ movl %esp, %ebx andl $0xfffff000, %ebx movl %esp, (MON_STACK_INFO)(%ebx) /* Save mon ESP */ /* Identity map this code page to host address space. */ movl (TRANSITION_PDE_P_MON)(%ebx), %eax movl (TRANSITION_PDE)(%ebx), %ebp xchgl %ebp, (%eax) /* old PDE saved in %ebp to be restored below */ movl (TRANSITION_LADDR)(%ebx), %eax /* Switch EBX to point to the identity mapped copy of */ /* the nexus page, and jump to the copy of this code there. */ subl (MON_BASE)(%ebx), %eax invlpg (%eax) /* Tell TLB about the change */ movl %eax, %ebx leal (OFFSET_OF(__mon_nexus_jmp))(%ebx), %eax jmp *%eax __mon_nexus_jmp: /* We are still in the monitor context, but are running at the */ /* same CS.base+EIP location in either host or monitor context, */ /* and this page is identity mapped between the 2 contexts. */ /* We can now switch to the host CR3, and be sure that execution */ /* will resume at the next instruction. */ /* NOTE: Don't try to access the stack after CR3 was reloaded */ /* but before we switched back to the host stack! */ /* Restore host CRx values */ movl (HOST_CR0)(%ebx), %eax movl (HOST_CR2)(%ebx), %ecx movl (HOST_CR4)(%ebx), %edx movl (HOST_CR3)(%ebx), %esi movl %eax, %cr0 movl %ecx, %cr2 movl %edx, %cr4 movl %esi, %cr3 jmp null_jmp1 null_jmp1: /* Switch to host GDT, LDT, and IDT */ lgdt (HOST_GDT_INFO)(%ebx) lidt (HOST_IDT_INFO)(%ebx) lldt (HOST_LDT_SEL)(%ebx) /* Restore host stack and CS */ lss (HOST_STACK_INFO)(%ebx), %esp ljmp (HOST_JMP_INFO)(%ebx) __host_cs: /* Clear busy bit of the host TSS and switch to it */ /* Note that DS is still the monitor segment with base (MON_BASE). */ movzwl (HOST_TSS_SEL)(%ebx), %eax andl $0xfffffff8, %eax addl (HOST_GDT_INFO+2)(%ebx), %eax subl (MON_BASE)(%ebx), %eax andl $0xfffffdff, 4(%eax) ltr (HOST_TSS_SEL)(%ebx) /* We no longer need the nexus page identity mapped, so we clean */ /* up the monitor page directory in case the host looks at it. */ /* Note that SS is already the host segment. */ movl (TRANSITION_PDE_P_HOST)(%ebx), %eax ss; movl %ebp, (%eax) /* %ebp still contains the original value */ /* Now we can restore the rest of */ /* the host context from the host stack. Look at __host2guest */ /* for the format of the values stored on the host stack. */ popl %gs popl %fs popl %ds popl %es popal popfl ret .globl __nexus_end __nexus_end: