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