xref: /qemu/target/i386/tcg/excp_helper.c (revision cf83f140059f21d4629ae4b61d468c3baef2bb4c)
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
9599b9a5aSBlue Swirl  * version 2 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"
249c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
252ef6175aSRichard Henderson #include "exec/helper-proto.h"
26599b9a5aSBlue Swirl 
27599b9a5aSBlue Swirl void helper_raise_interrupt(CPUX86State *env, int intno, int next_eip_addend)
28599b9a5aSBlue Swirl {
29599b9a5aSBlue Swirl     raise_interrupt(env, intno, 1, 0, next_eip_addend);
30599b9a5aSBlue Swirl }
31599b9a5aSBlue Swirl 
32599b9a5aSBlue Swirl void helper_raise_exception(CPUX86State *env, int exception_index)
33599b9a5aSBlue Swirl {
34599b9a5aSBlue Swirl     raise_exception(env, exception_index);
35599b9a5aSBlue Swirl }
36599b9a5aSBlue Swirl 
37599b9a5aSBlue Swirl /*
38599b9a5aSBlue Swirl  * Check nested exceptions and change to double or triple fault if
39599b9a5aSBlue Swirl  * needed. It should only be called, if this is not an interrupt.
40599b9a5aSBlue Swirl  * Returns the new exception number.
41599b9a5aSBlue Swirl  */
4265c9d60aSPaolo Bonzini static int check_exception(CPUX86State *env, int intno, int *error_code,
4365c9d60aSPaolo Bonzini                            uintptr_t retaddr)
44599b9a5aSBlue Swirl {
45599b9a5aSBlue Swirl     int first_contributory = env->old_exception == 0 ||
46599b9a5aSBlue Swirl                               (env->old_exception >= 10 &&
47599b9a5aSBlue Swirl                                env->old_exception <= 13);
48599b9a5aSBlue Swirl     int second_contributory = intno == 0 ||
49599b9a5aSBlue Swirl                                (intno >= 10 && intno <= 13);
50599b9a5aSBlue Swirl 
51599b9a5aSBlue Swirl     qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n",
52599b9a5aSBlue Swirl                 env->old_exception, intno);
53599b9a5aSBlue Swirl 
54599b9a5aSBlue Swirl #if !defined(CONFIG_USER_ONLY)
55599b9a5aSBlue Swirl     if (env->old_exception == EXCP08_DBLE) {
56599b9a5aSBlue Swirl         if (env->hflags & HF_SVMI_MASK) {
5765c9d60aSPaolo Bonzini             cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0, retaddr); /* does not return */
58599b9a5aSBlue Swirl         }
59599b9a5aSBlue Swirl 
60599b9a5aSBlue Swirl         qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
61599b9a5aSBlue Swirl 
62cf83f140SEric Blake         qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
63599b9a5aSBlue Swirl         return EXCP_HLT;
64599b9a5aSBlue Swirl     }
65599b9a5aSBlue Swirl #endif
66599b9a5aSBlue Swirl 
67599b9a5aSBlue Swirl     if ((first_contributory && second_contributory)
68599b9a5aSBlue Swirl         || (env->old_exception == EXCP0E_PAGE &&
69599b9a5aSBlue Swirl             (second_contributory || (intno == EXCP0E_PAGE)))) {
70599b9a5aSBlue Swirl         intno = EXCP08_DBLE;
71599b9a5aSBlue Swirl         *error_code = 0;
72599b9a5aSBlue Swirl     }
73599b9a5aSBlue Swirl 
74599b9a5aSBlue Swirl     if (second_contributory || (intno == EXCP0E_PAGE) ||
75599b9a5aSBlue Swirl         (intno == EXCP08_DBLE)) {
76599b9a5aSBlue Swirl         env->old_exception = intno;
77599b9a5aSBlue Swirl     }
78599b9a5aSBlue Swirl 
79599b9a5aSBlue Swirl     return intno;
80599b9a5aSBlue Swirl }
81599b9a5aSBlue Swirl 
82599b9a5aSBlue Swirl /*
83599b9a5aSBlue Swirl  * Signal an interruption. It is executed in the main CPU loop.
84599b9a5aSBlue Swirl  * is_int is TRUE if coming from the int instruction. next_eip is the
85a78d0eabSliguang  * env->eip value AFTER the interrupt instruction. It is only relevant if
86599b9a5aSBlue Swirl  * is_int is TRUE.
87599b9a5aSBlue Swirl  */
88599b9a5aSBlue Swirl static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno,
89599b9a5aSBlue Swirl                                            int is_int, int error_code,
9091980095SPavel Dovgalyuk                                            int next_eip_addend,
9191980095SPavel Dovgalyuk                                            uintptr_t retaddr)
92599b9a5aSBlue Swirl {
9327103424SAndreas Färber     CPUState *cs = CPU(x86_env_get_cpu(env));
9427103424SAndreas Färber 
95599b9a5aSBlue Swirl     if (!is_int) {
96599b9a5aSBlue Swirl         cpu_svm_check_intercept_param(env, SVM_EXIT_EXCP_BASE + intno,
9765c9d60aSPaolo Bonzini                                       error_code, retaddr);
9865c9d60aSPaolo Bonzini         intno = check_exception(env, intno, &error_code, retaddr);
99599b9a5aSBlue Swirl     } else {
10065c9d60aSPaolo Bonzini         cpu_svm_check_intercept_param(env, SVM_EXIT_SWINT, 0, retaddr);
101599b9a5aSBlue Swirl     }
102599b9a5aSBlue Swirl 
10327103424SAndreas Färber     cs->exception_index = intno;
104599b9a5aSBlue Swirl     env->error_code = error_code;
105599b9a5aSBlue Swirl     env->exception_is_int = is_int;
106599b9a5aSBlue Swirl     env->exception_next_eip = env->eip + next_eip_addend;
10791980095SPavel Dovgalyuk     cpu_loop_exit_restore(cs, retaddr);
108599b9a5aSBlue Swirl }
109599b9a5aSBlue Swirl 
110599b9a5aSBlue Swirl /* shortcuts to generate exceptions */
111599b9a5aSBlue Swirl 
112599b9a5aSBlue Swirl void QEMU_NORETURN raise_interrupt(CPUX86State *env, int intno, int is_int,
113599b9a5aSBlue Swirl                                    int error_code, int next_eip_addend)
114599b9a5aSBlue Swirl {
11591980095SPavel Dovgalyuk     raise_interrupt2(env, intno, is_int, error_code, next_eip_addend, 0);
116599b9a5aSBlue Swirl }
117599b9a5aSBlue Swirl 
118599b9a5aSBlue Swirl void raise_exception_err(CPUX86State *env, int exception_index,
119599b9a5aSBlue Swirl                          int error_code)
120599b9a5aSBlue Swirl {
12191980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, error_code, 0, 0);
12291980095SPavel Dovgalyuk }
12391980095SPavel Dovgalyuk 
12491980095SPavel Dovgalyuk void raise_exception_err_ra(CPUX86State *env, int exception_index,
12591980095SPavel Dovgalyuk                             int error_code, uintptr_t retaddr)
12691980095SPavel Dovgalyuk {
12791980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, error_code, 0, retaddr);
128599b9a5aSBlue Swirl }
129599b9a5aSBlue Swirl 
130599b9a5aSBlue Swirl void raise_exception(CPUX86State *env, int exception_index)
131599b9a5aSBlue Swirl {
13291980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, 0, 0, 0);
13391980095SPavel Dovgalyuk }
13491980095SPavel Dovgalyuk 
13591980095SPavel Dovgalyuk void raise_exception_ra(CPUX86State *env, int exception_index, uintptr_t retaddr)
13691980095SPavel Dovgalyuk {
13791980095SPavel Dovgalyuk     raise_interrupt2(env, exception_index, 0, 0, 0, retaddr);
138599b9a5aSBlue Swirl }
139