xref: /qemu/target/i386/tcg/excp_helper.c (revision 8905770b27be326d12a704629f3cb715642db6cc)
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 
28*8905770bSMarc-André Lureau G_NORETURN void helper_raise_interrupt(CPUX86State *env, int intno,
29b82055aeSRichard Henderson                                           int next_eip_addend)
30599b9a5aSBlue Swirl {
31599b9a5aSBlue Swirl     raise_interrupt(env, intno, 1, 0, next_eip_addend);
32599b9a5aSBlue Swirl }
33599b9a5aSBlue Swirl 
34*8905770bSMarc-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  */
90*8905770bSMarc-André Lureau static G_NORETURN
91*8905770bSMarc-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 
115*8905770bSMarc-André Lureau G_NORETURN void raise_interrupt(CPUX86State *env, int intno, int is_int,
116599b9a5aSBlue Swirl                                 int error_code, int next_eip_addend)
117599b9a5aSBlue Swirl {
11891980095SPavel Dovgalyuk     raise_interrupt2(env, intno, is_int, error_code, next_eip_addend, 0);
119599b9a5aSBlue Swirl }
120599b9a5aSBlue Swirl 
121*8905770bSMarc-André Lureau G_NORETURN void raise_exception_err(CPUX86State *env, int exception_index,
122599b9a5aSBlue Swirl                                     int error_code)
123599b9a5aSBlue Swirl {
12491980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, error_code, 0, 0);
12591980095SPavel Dovgalyuk }
12691980095SPavel Dovgalyuk 
127*8905770bSMarc-André Lureau G_NORETURN void raise_exception_err_ra(CPUX86State *env, int exception_index,
12891980095SPavel Dovgalyuk                                        int error_code, uintptr_t retaddr)
12991980095SPavel Dovgalyuk {
13091980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, error_code, 0, retaddr);
131599b9a5aSBlue Swirl }
132599b9a5aSBlue Swirl 
133*8905770bSMarc-André Lureau G_NORETURN void raise_exception(CPUX86State *env, int exception_index)
134599b9a5aSBlue Swirl {
13591980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, 0, 0, 0);
13691980095SPavel Dovgalyuk }
13791980095SPavel Dovgalyuk 
138*8905770bSMarc-André Lureau G_NORETURN void raise_exception_ra(CPUX86State *env, int exception_index,
139b82055aeSRichard Henderson                                    uintptr_t retaddr)
14091980095SPavel Dovgalyuk {
14191980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, 0, 0, retaddr);
142599b9a5aSBlue Swirl }
143