1 /*
2  * pv/iret.c
3  *
4  * iret hypercall handling code
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms and conditions of the GNU General Public
8  * License, version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; If not, see
17  * <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <xen/guest_access.h>
21 #include <xen/lib.h>
22 #include <xen/sched.h>
23 
24 #include <asm/current.h>
25 #include <asm/traps.h>
26 
27 /* Override macros from asm/page.h to make them work with mfn_t */
28 #undef mfn_to_page
29 #define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn))
30 #undef page_to_mfn
31 #define page_to_mfn(pg) _mfn(__page_to_mfn(pg))
32 
do_iret(void)33 unsigned long do_iret(void)
34 {
35     struct cpu_user_regs *regs = guest_cpu_user_regs();
36     struct iret_context iret_saved;
37     struct vcpu *v = current;
38 
39     if ( unlikely(copy_from_user(&iret_saved, (void *)regs->rsp,
40                                  sizeof(iret_saved))) )
41     {
42         gprintk(XENLOG_ERR,
43                 "Fault while reading IRET context from guest stack\n");
44         goto exit_and_crash;
45     }
46 
47     /* Returning to user mode? */
48     if ( (iret_saved.cs & 3) == 3 )
49     {
50         if ( unlikely(pagetable_is_null(v->arch.guest_table_user)) )
51         {
52             gprintk(XENLOG_ERR,
53                     "Guest switching to user mode with no user page tables\n");
54             goto exit_and_crash;
55         }
56         toggle_guest_mode(v);
57     }
58 
59     if ( VM_ASSIST(v->domain, architectural_iopl) )
60         v->arch.pv_vcpu.iopl = iret_saved.rflags & X86_EFLAGS_IOPL;
61 
62     regs->rip    = iret_saved.rip;
63     regs->cs     = iret_saved.cs | 3; /* force guest privilege */
64     regs->rflags = ((iret_saved.rflags & ~(X86_EFLAGS_IOPL|X86_EFLAGS_VM))
65                     | X86_EFLAGS_IF);
66     regs->rsp    = iret_saved.rsp;
67     regs->ss     = iret_saved.ss | 3; /* force guest privilege */
68 
69     if ( !(iret_saved.flags & VGCF_in_syscall) )
70     {
71         regs->entry_vector &= ~TRAP_syscall;
72         regs->r11 = iret_saved.r11;
73         regs->rcx = iret_saved.rcx;
74     }
75 
76     /* Restore upcall mask from supplied EFLAGS.IF. */
77     vcpu_info(v, evtchn_upcall_mask) = !(iret_saved.rflags & X86_EFLAGS_IF);
78 
79     async_exception_cleanup(v);
80 
81     /* Saved %rax gets written back to regs->rax in entry.S. */
82     return iret_saved.rax;
83 
84  exit_and_crash:
85     domain_crash(v->domain);
86     return 0;
87 }
88 
compat_iret(void)89 unsigned int compat_iret(void)
90 {
91     struct cpu_user_regs *regs = guest_cpu_user_regs();
92     struct vcpu *v = current;
93     u32 eflags;
94 
95     /* Trim stack pointer to 32 bits. */
96     regs->rsp = (u32)regs->rsp;
97 
98     /* Restore EAX (clobbered by hypercall). */
99     if ( unlikely(__get_user(regs->eax, (u32 *)regs->rsp)) )
100     {
101         domain_crash(v->domain);
102         return 0;
103     }
104 
105     /* Restore CS and EIP. */
106     if ( unlikely(__get_user(regs->eip, (u32 *)regs->rsp + 1)) ||
107         unlikely(__get_user(regs->cs, (u32 *)regs->rsp + 2)) )
108     {
109         domain_crash(v->domain);
110         return 0;
111     }
112 
113     /*
114      * Fix up and restore EFLAGS. We fix up in a local staging area
115      * to avoid firing the BUG_ON(IOPL) check in arch_get_info_guest.
116      */
117     if ( unlikely(__get_user(eflags, (u32 *)regs->rsp + 3)) )
118     {
119         domain_crash(v->domain);
120         return 0;
121     }
122 
123     if ( VM_ASSIST(v->domain, architectural_iopl) )
124         v->arch.pv_vcpu.iopl = eflags & X86_EFLAGS_IOPL;
125 
126     regs->eflags = (eflags & ~X86_EFLAGS_IOPL) | X86_EFLAGS_IF;
127 
128     if ( unlikely(eflags & X86_EFLAGS_VM) )
129     {
130         /*
131          * Cannot return to VM86 mode: inject a GP fault instead. Note that
132          * the GP fault is reported on the first VM86 mode instruction, not on
133          * the IRET (which is why we can simply leave the stack frame as-is
134          * (except for perhaps having to copy it), which in turn seems better
135          * than teaching create_bounce_frame() to needlessly deal with vm86
136          * mode frames).
137          */
138         const struct trap_info *ti;
139         u32 x, ksp = v->arch.pv_vcpu.kernel_sp - 40;
140         unsigned int i;
141         int rc = 0;
142 
143         gdprintk(XENLOG_ERR, "VM86 mode unavailable (ksp:%08X->%08X)\n",
144                  regs->esp, ksp);
145         if ( ksp < regs->esp )
146         {
147             for (i = 1; i < 10; ++i)
148             {
149                 rc |= __get_user(x, (u32 *)regs->rsp + i);
150                 rc |= __put_user(x, (u32 *)(unsigned long)ksp + i);
151             }
152         }
153         else if ( ksp > regs->esp )
154         {
155             for ( i = 9; i > 0; --i )
156             {
157                 rc |= __get_user(x, (u32 *)regs->rsp + i);
158                 rc |= __put_user(x, (u32 *)(unsigned long)ksp + i);
159             }
160         }
161         if ( rc )
162         {
163             domain_crash(v->domain);
164             return 0;
165         }
166         regs->esp = ksp;
167         regs->ss = v->arch.pv_vcpu.kernel_ss;
168 
169         ti = &v->arch.pv_vcpu.trap_ctxt[TRAP_gp_fault];
170         if ( TI_GET_IF(ti) )
171             eflags &= ~X86_EFLAGS_IF;
172         regs->eflags &= ~(X86_EFLAGS_VM|X86_EFLAGS_RF|
173                           X86_EFLAGS_NT|X86_EFLAGS_TF);
174         if ( unlikely(__put_user(0, (u32 *)regs->rsp)) )
175         {
176             domain_crash(v->domain);
177             return 0;
178         }
179         regs->eip = ti->address;
180         regs->cs = ti->cs;
181     }
182     else if ( unlikely(ring_0(regs)) )
183     {
184         domain_crash(v->domain);
185         return 0;
186     }
187     else if ( ring_1(regs) )
188         regs->esp += 16;
189     /* Return to ring 2/3: restore ESP and SS. */
190     else if ( __get_user(regs->ss, (u32 *)regs->rsp + 5) ||
191               __get_user(regs->esp, (u32 *)regs->rsp + 4) )
192     {
193         domain_crash(v->domain);
194         return 0;
195     }
196 
197     /* Restore upcall mask from supplied EFLAGS.IF. */
198     vcpu_info(v, evtchn_upcall_mask) = !(eflags & X86_EFLAGS_IF);
199 
200     async_exception_cleanup(v);
201 
202     /*
203      * The hypercall exit path will overwrite EAX with this return
204      * value.
205      */
206     return regs->eax;
207 }
208 
209 /*
210  * Local variables:
211  * mode: C
212  * c-file-style: "BSD"
213  * c-basic-offset: 4
214  * tab-width: 4
215  * indent-tabs-mode: nil
216  * End:
217  */
218