xref: /qemu/target/i386/tcg/excp_helper.c (revision 73fb7b3c4983e48f3081fca00013a996abf659c0)
1599b9a5aSBlue Swirl /*
2599b9a5aSBlue Swirl  *  x86 exception helpers
3599b9a5aSBlue Swirl  *
4599b9a5aSBlue Swirl  *  Copyright (c) 2003 Fabrice Bellard
5599b9a5aSBlue Swirl  *
6599b9a5aSBlue Swirl  * This library is free software; you can redistribute it and/or
7599b9a5aSBlue Swirl  * modify it under the terms of the GNU Lesser General Public
8599b9a5aSBlue Swirl  * License as published by the Free Software Foundation; either
9d9ff33adSChetan Pant  * version 2.1 of the License, or (at your option) any later version.
10599b9a5aSBlue Swirl  *
11599b9a5aSBlue Swirl  * This library is distributed in the hope that it will be useful,
12599b9a5aSBlue Swirl  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13599b9a5aSBlue Swirl  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14599b9a5aSBlue Swirl  * Lesser General Public License for more details.
15599b9a5aSBlue Swirl  *
16599b9a5aSBlue Swirl  * You should have received a copy of the GNU Lesser General Public
17599b9a5aSBlue Swirl  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18599b9a5aSBlue Swirl  */
19599b9a5aSBlue Swirl 
20b6a0aa05SPeter Maydell #include "qemu/osdep.h"
21599b9a5aSBlue Swirl #include "cpu.h"
2263c91552SPaolo Bonzini #include "exec/exec-all.h"
231de7afc9SPaolo Bonzini #include "qemu/log.h"
2454d31236SMarkus Armbruster #include "sysemu/runstate.h"
252ef6175aSRichard Henderson #include "exec/helper-proto.h"
26ed69e831SClaudio Fontana #include "helper-tcg.h"
27599b9a5aSBlue Swirl 
288905770bSMarc-André Lureau G_NORETURN void helper_raise_interrupt(CPUX86State *env, int intno,
29b82055aeSRichard Henderson                                           int next_eip_addend)
30599b9a5aSBlue Swirl {
3183280f6aSPaolo Bonzini     raise_interrupt(env, intno, next_eip_addend);
32599b9a5aSBlue Swirl }
33599b9a5aSBlue Swirl 
348905770bSMarc-André Lureau G_NORETURN void helper_raise_exception(CPUX86State *env, int exception_index)
35599b9a5aSBlue Swirl {
36599b9a5aSBlue Swirl     raise_exception(env, exception_index);
37599b9a5aSBlue Swirl }
38599b9a5aSBlue Swirl 
39599b9a5aSBlue Swirl /*
40599b9a5aSBlue Swirl  * Check nested exceptions and change to double or triple fault if
41599b9a5aSBlue Swirl  * needed. It should only be called, if this is not an interrupt.
42599b9a5aSBlue Swirl  * Returns the new exception number.
43599b9a5aSBlue Swirl  */
4465c9d60aSPaolo Bonzini static int check_exception(CPUX86State *env, int intno, int *error_code,
4565c9d60aSPaolo Bonzini                            uintptr_t retaddr)
46599b9a5aSBlue Swirl {
47599b9a5aSBlue Swirl     int first_contributory = env->old_exception == 0 ||
48599b9a5aSBlue Swirl                               (env->old_exception >= 10 &&
49599b9a5aSBlue Swirl                                env->old_exception <= 13);
50599b9a5aSBlue Swirl     int second_contributory = intno == 0 ||
51599b9a5aSBlue Swirl                                (intno >= 10 && intno <= 13);
52599b9a5aSBlue Swirl 
53599b9a5aSBlue Swirl     qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n",
54599b9a5aSBlue Swirl                 env->old_exception, intno);
55599b9a5aSBlue Swirl 
56599b9a5aSBlue Swirl #if !defined(CONFIG_USER_ONLY)
57599b9a5aSBlue Swirl     if (env->old_exception == EXCP08_DBLE) {
58f8dc4c64SPaolo Bonzini         if (env->hflags & HF_GUEST_MASK) {
5965c9d60aSPaolo Bonzini             cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0, retaddr); /* does not return */
60599b9a5aSBlue Swirl         }
61599b9a5aSBlue Swirl 
62599b9a5aSBlue Swirl         qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
63599b9a5aSBlue Swirl 
64cf83f140SEric Blake         qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
65599b9a5aSBlue Swirl         return EXCP_HLT;
66599b9a5aSBlue Swirl     }
67599b9a5aSBlue Swirl #endif
68599b9a5aSBlue Swirl 
69599b9a5aSBlue Swirl     if ((first_contributory && second_contributory)
70599b9a5aSBlue Swirl         || (env->old_exception == EXCP0E_PAGE &&
71599b9a5aSBlue Swirl             (second_contributory || (intno == EXCP0E_PAGE)))) {
72599b9a5aSBlue Swirl         intno = EXCP08_DBLE;
73599b9a5aSBlue Swirl         *error_code = 0;
74599b9a5aSBlue Swirl     }
75599b9a5aSBlue Swirl 
76599b9a5aSBlue Swirl     if (second_contributory || (intno == EXCP0E_PAGE) ||
77599b9a5aSBlue Swirl         (intno == EXCP08_DBLE)) {
78599b9a5aSBlue Swirl         env->old_exception = intno;
79599b9a5aSBlue Swirl     }
80599b9a5aSBlue Swirl 
81599b9a5aSBlue Swirl     return intno;
82599b9a5aSBlue Swirl }
83599b9a5aSBlue Swirl 
84599b9a5aSBlue Swirl /*
85599b9a5aSBlue Swirl  * Signal an interruption. It is executed in the main CPU loop.
86599b9a5aSBlue Swirl  * is_int is TRUE if coming from the int instruction. next_eip is the
87a78d0eabSliguang  * env->eip value AFTER the interrupt instruction. It is only relevant if
88599b9a5aSBlue Swirl  * is_int is TRUE.
89599b9a5aSBlue Swirl  */
908905770bSMarc-André Lureau static G_NORETURN
918905770bSMarc-André Lureau void raise_interrupt2(CPUX86State *env, int intno,
92599b9a5aSBlue Swirl                       int is_int, int error_code,
9391980095SPavel Dovgalyuk                       int next_eip_addend,
9491980095SPavel Dovgalyuk                       uintptr_t retaddr)
95599b9a5aSBlue Swirl {
966aa9e42fSRichard Henderson     CPUState *cs = env_cpu(env);
9727103424SAndreas Färber 
98599b9a5aSBlue Swirl     if (!is_int) {
99599b9a5aSBlue Swirl         cpu_svm_check_intercept_param(env, SVM_EXIT_EXCP_BASE + intno,
10065c9d60aSPaolo Bonzini                                       error_code, retaddr);
10165c9d60aSPaolo Bonzini         intno = check_exception(env, intno, &error_code, retaddr);
102599b9a5aSBlue Swirl     } else {
10365c9d60aSPaolo Bonzini         cpu_svm_check_intercept_param(env, SVM_EXIT_SWINT, 0, retaddr);
104599b9a5aSBlue Swirl     }
105599b9a5aSBlue Swirl 
10627103424SAndreas Färber     cs->exception_index = intno;
107599b9a5aSBlue Swirl     env->error_code = error_code;
108599b9a5aSBlue Swirl     env->exception_is_int = is_int;
109599b9a5aSBlue Swirl     env->exception_next_eip = env->eip + next_eip_addend;
11091980095SPavel Dovgalyuk     cpu_loop_exit_restore(cs, retaddr);
111599b9a5aSBlue Swirl }
112599b9a5aSBlue Swirl 
113599b9a5aSBlue Swirl /* shortcuts to generate exceptions */
114599b9a5aSBlue Swirl 
11583280f6aSPaolo Bonzini G_NORETURN void raise_interrupt(CPUX86State *env, int intno, int next_eip_addend)
116599b9a5aSBlue Swirl {
11783280f6aSPaolo Bonzini     raise_interrupt2(env, intno, 1, 0, next_eip_addend, 0);
118599b9a5aSBlue Swirl }
119599b9a5aSBlue Swirl 
1208905770bSMarc-André Lureau G_NORETURN void raise_exception_err(CPUX86State *env, int exception_index,
121599b9a5aSBlue Swirl                                     int error_code)
122599b9a5aSBlue Swirl {
12391980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, error_code, 0, 0);
12491980095SPavel Dovgalyuk }
12591980095SPavel Dovgalyuk 
1268905770bSMarc-André Lureau G_NORETURN void raise_exception_err_ra(CPUX86State *env, int exception_index,
12791980095SPavel Dovgalyuk                                        int error_code, uintptr_t retaddr)
12891980095SPavel Dovgalyuk {
12991980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, error_code, 0, retaddr);
130599b9a5aSBlue Swirl }
131599b9a5aSBlue Swirl 
1328905770bSMarc-André Lureau G_NORETURN void raise_exception(CPUX86State *env, int exception_index)
133599b9a5aSBlue Swirl {
13491980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, 0, 0, 0);
13591980095SPavel Dovgalyuk }
13691980095SPavel Dovgalyuk 
1378905770bSMarc-André Lureau G_NORETURN void raise_exception_ra(CPUX86State *env, int exception_index,
138b82055aeSRichard Henderson                                    uintptr_t retaddr)
13991980095SPavel Dovgalyuk {
14091980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, 0, 0, retaddr);
141599b9a5aSBlue Swirl }
142958e1dd1SPaolo Bonzini 
143*73fb7b3cSPaolo Bonzini G_NORETURN void helper_icebp(CPUX86State *env)
144*73fb7b3cSPaolo Bonzini {
145*73fb7b3cSPaolo Bonzini     CPUState *cs = env_cpu(env);
146*73fb7b3cSPaolo Bonzini 
147*73fb7b3cSPaolo Bonzini     do_end_instruction(env);
148*73fb7b3cSPaolo Bonzini 
149*73fb7b3cSPaolo Bonzini     /*
150*73fb7b3cSPaolo Bonzini      * INT1 aka ICEBP generates a trap-like #DB, but it is pretty special.
151*73fb7b3cSPaolo Bonzini      *
152*73fb7b3cSPaolo Bonzini      * "Although the ICEBP instruction dispatches through IDT vector 1,
153*73fb7b3cSPaolo Bonzini      * that event is not interceptable by means of the #DB exception
154*73fb7b3cSPaolo Bonzini      * intercept".  Instead there is a separate fault-like ICEBP intercept.
155*73fb7b3cSPaolo Bonzini      */
156*73fb7b3cSPaolo Bonzini     cs->exception_index = EXCP01_DB;
157*73fb7b3cSPaolo Bonzini     env->error_code = 0;
158*73fb7b3cSPaolo Bonzini     env->exception_is_int = 0;
159*73fb7b3cSPaolo Bonzini     env->exception_next_eip = env->eip;
160*73fb7b3cSPaolo Bonzini     cpu_loop_exit(cs);
161*73fb7b3cSPaolo Bonzini }
162*73fb7b3cSPaolo Bonzini 
163958e1dd1SPaolo Bonzini G_NORETURN void handle_unaligned_access(CPUX86State *env, vaddr vaddr,
164958e1dd1SPaolo Bonzini                                         MMUAccessType access_type,
165958e1dd1SPaolo Bonzini                                         uintptr_t retaddr)
166958e1dd1SPaolo Bonzini {
167958e1dd1SPaolo Bonzini     /*
168958e1dd1SPaolo Bonzini      * Unaligned accesses are currently only triggered by SSE/AVX
169958e1dd1SPaolo Bonzini      * instructions that impose alignment requirements on memory
170958e1dd1SPaolo Bonzini      * operands. These instructions raise #GP(0) upon accessing an
171958e1dd1SPaolo Bonzini      * unaligned address.
172958e1dd1SPaolo Bonzini      */
173958e1dd1SPaolo Bonzini     raise_exception_ra(env, EXCP0D_GPF, retaddr);
174958e1dd1SPaolo Bonzini }
175