xref: /openbmc/qemu/target/i386/tcg/sysemu/seg_helper.c (revision 3e246da2c3f85298b52f8a1154b832acf36aa656)
130493a03SClaudio Fontana /*
230493a03SClaudio Fontana  *  x86 segmentation related helpers: (sysemu-only code)
330493a03SClaudio Fontana  *  TSS, interrupts, system calls, jumps and call/task gates, descriptors
430493a03SClaudio Fontana  *
530493a03SClaudio Fontana  *  Copyright (c) 2003 Fabrice Bellard
630493a03SClaudio Fontana  *
730493a03SClaudio Fontana  * This library is free software; you can redistribute it and/or
830493a03SClaudio Fontana  * modify it under the terms of the GNU Lesser General Public
930493a03SClaudio Fontana  * License as published by the Free Software Foundation; either
1030493a03SClaudio Fontana  * version 2.1 of the License, or (at your option) any later version.
1130493a03SClaudio Fontana  *
1230493a03SClaudio Fontana  * This library is distributed in the hope that it will be useful,
1330493a03SClaudio Fontana  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1430493a03SClaudio Fontana  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1530493a03SClaudio Fontana  * Lesser General Public License for more details.
1630493a03SClaudio Fontana  *
1730493a03SClaudio Fontana  * You should have received a copy of the GNU Lesser General Public
1830493a03SClaudio Fontana  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1930493a03SClaudio Fontana  */
2030493a03SClaudio Fontana 
2130493a03SClaudio Fontana #include "qemu/osdep.h"
22cd617484SPhilippe Mathieu-Daudé #include "qemu/log.h"
23ec1d32afSPhilippe Mathieu-Daudé #include "qemu/main-loop.h"
2430493a03SClaudio Fontana #include "cpu.h"
2530493a03SClaudio Fontana #include "exec/helper-proto.h"
2630493a03SClaudio Fontana #include "exec/cpu_ldst.h"
2730493a03SClaudio Fontana #include "tcg/helper-tcg.h"
28d76b9c6fSRichard Henderson #include "../seg_helper.h"
2930493a03SClaudio Fontana 
helper_syscall(CPUX86State * env,int next_eip_addend)3030493a03SClaudio Fontana void helper_syscall(CPUX86State *env, int next_eip_addend)
3130493a03SClaudio Fontana {
3230493a03SClaudio Fontana     int selector;
3330493a03SClaudio Fontana 
3430493a03SClaudio Fontana     if (!(env->efer & MSR_EFER_SCE)) {
3530493a03SClaudio Fontana         raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
3630493a03SClaudio Fontana     }
3730493a03SClaudio Fontana     selector = (env->star >> 32) & 0xffff;
3863fd8ef0SPaolo Bonzini #ifdef TARGET_X86_64
3930493a03SClaudio Fontana     if (env->hflags & HF_LMA_MASK) {
4030493a03SClaudio Fontana         int code64;
4130493a03SClaudio Fontana 
4230493a03SClaudio Fontana         env->regs[R_ECX] = env->eip + next_eip_addend;
4330493a03SClaudio Fontana         env->regs[11] = cpu_compute_eflags(env) & ~RF_MASK;
4430493a03SClaudio Fontana 
4530493a03SClaudio Fontana         code64 = env->hflags & HF_CS64_MASK;
4630493a03SClaudio Fontana 
4730493a03SClaudio Fontana         env->eflags &= ~(env->fmask | RF_MASK);
4830493a03SClaudio Fontana         cpu_load_eflags(env, env->eflags, 0);
4930493a03SClaudio Fontana         cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
5030493a03SClaudio Fontana                            0, 0xffffffff,
5130493a03SClaudio Fontana                                DESC_G_MASK | DESC_P_MASK |
5230493a03SClaudio Fontana                                DESC_S_MASK |
5330493a03SClaudio Fontana                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
5430493a03SClaudio Fontana                                DESC_L_MASK);
5530493a03SClaudio Fontana         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
5630493a03SClaudio Fontana                                0, 0xffffffff,
5730493a03SClaudio Fontana                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
5830493a03SClaudio Fontana                                DESC_S_MASK |
5930493a03SClaudio Fontana                                DESC_W_MASK | DESC_A_MASK);
6030493a03SClaudio Fontana         if (code64) {
6130493a03SClaudio Fontana             env->eip = env->lstar;
6230493a03SClaudio Fontana         } else {
6330493a03SClaudio Fontana             env->eip = env->cstar;
6430493a03SClaudio Fontana         }
6563fd8ef0SPaolo Bonzini     } else
6663fd8ef0SPaolo Bonzini #endif
6763fd8ef0SPaolo Bonzini     {
6830493a03SClaudio Fontana         env->regs[R_ECX] = (uint32_t)(env->eip + next_eip_addend);
6930493a03SClaudio Fontana 
7030493a03SClaudio Fontana         env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
7130493a03SClaudio Fontana         cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
7230493a03SClaudio Fontana                            0, 0xffffffff,
7330493a03SClaudio Fontana                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
7430493a03SClaudio Fontana                                DESC_S_MASK |
7530493a03SClaudio Fontana                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
7630493a03SClaudio Fontana         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
7730493a03SClaudio Fontana                                0, 0xffffffff,
7830493a03SClaudio Fontana                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
7930493a03SClaudio Fontana                                DESC_S_MASK |
8030493a03SClaudio Fontana                                DESC_W_MASK | DESC_A_MASK);
8130493a03SClaudio Fontana         env->eip = (uint32_t)env->star;
8230493a03SClaudio Fontana     }
8330493a03SClaudio Fontana }
8430493a03SClaudio Fontana 
handle_even_inj(CPUX86State * env,int intno,int is_int,int error_code,int is_hw,int rm)8530493a03SClaudio Fontana void handle_even_inj(CPUX86State *env, int intno, int is_int,
8630493a03SClaudio Fontana                      int error_code, int is_hw, int rm)
8730493a03SClaudio Fontana {
8830493a03SClaudio Fontana     CPUState *cs = env_cpu(env);
8930493a03SClaudio Fontana     uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
9030493a03SClaudio Fontana                                                           control.event_inj));
9130493a03SClaudio Fontana 
9230493a03SClaudio Fontana     if (!(event_inj & SVM_EVTINJ_VALID)) {
9330493a03SClaudio Fontana         int type;
9430493a03SClaudio Fontana 
9530493a03SClaudio Fontana         if (is_int) {
9630493a03SClaudio Fontana             type = SVM_EVTINJ_TYPE_SOFT;
9730493a03SClaudio Fontana         } else {
9830493a03SClaudio Fontana             type = SVM_EVTINJ_TYPE_EXEPT;
9930493a03SClaudio Fontana         }
10030493a03SClaudio Fontana         event_inj = intno | type | SVM_EVTINJ_VALID;
10130493a03SClaudio Fontana         if (!rm && exception_has_error_code(intno)) {
10230493a03SClaudio Fontana             event_inj |= SVM_EVTINJ_VALID_ERR;
10330493a03SClaudio Fontana             x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
10430493a03SClaudio Fontana                                              control.event_inj_err),
10530493a03SClaudio Fontana                      error_code);
10630493a03SClaudio Fontana         }
10730493a03SClaudio Fontana         x86_stl_phys(cs,
10830493a03SClaudio Fontana                  env->vm_vmcb + offsetof(struct vmcb, control.event_inj),
10930493a03SClaudio Fontana                  event_inj);
11030493a03SClaudio Fontana     }
11130493a03SClaudio Fontana }
11230493a03SClaudio Fontana 
x86_cpu_do_interrupt(CPUState * cs)11330493a03SClaudio Fontana void x86_cpu_do_interrupt(CPUState *cs)
11430493a03SClaudio Fontana {
11530493a03SClaudio Fontana     X86CPU *cpu = X86_CPU(cs);
11630493a03SClaudio Fontana     CPUX86State *env = &cpu->env;
11730493a03SClaudio Fontana 
11830493a03SClaudio Fontana     if (cs->exception_index == EXCP_VMEXIT) {
11930493a03SClaudio Fontana         assert(env->old_exception == -1);
12030493a03SClaudio Fontana         do_vmexit(env);
12130493a03SClaudio Fontana     } else {
12230493a03SClaudio Fontana         do_interrupt_all(cpu, cs->exception_index,
12330493a03SClaudio Fontana                          env->exception_is_int,
12430493a03SClaudio Fontana                          env->error_code,
12530493a03SClaudio Fontana                          env->exception_next_eip, 0);
12630493a03SClaudio Fontana         /* successfully delivered */
12730493a03SClaudio Fontana         env->old_exception = -1;
12830493a03SClaudio Fontana     }
12930493a03SClaudio Fontana }
130d76b9c6fSRichard Henderson 
x86_cpu_exec_halt(CPUState * cpu)131408b2b3dSPeter Maydell bool x86_cpu_exec_halt(CPUState *cpu)
132ec1d32afSPhilippe Mathieu-Daudé {
133ec1d32afSPhilippe Mathieu-Daudé     X86CPU *x86_cpu = X86_CPU(cpu);
134*6dd7d8c6SPaolo Bonzini     CPUX86State *env = &x86_cpu->env;
135ec1d32afSPhilippe Mathieu-Daudé 
136*6dd7d8c6SPaolo Bonzini     if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
137ec1d32afSPhilippe Mathieu-Daudé         bql_lock();
138ec1d32afSPhilippe Mathieu-Daudé         apic_poll_irq(x86_cpu->apic_state);
139ec1d32afSPhilippe Mathieu-Daudé         cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
140ec1d32afSPhilippe Mathieu-Daudé         bql_unlock();
141ec1d32afSPhilippe Mathieu-Daudé     }
142*6dd7d8c6SPaolo Bonzini 
143*6dd7d8c6SPaolo Bonzini     if (!cpu_has_work(cpu)) {
144*6dd7d8c6SPaolo Bonzini         return false;
145*6dd7d8c6SPaolo Bonzini     }
146*6dd7d8c6SPaolo Bonzini 
147*6dd7d8c6SPaolo Bonzini     /* Complete HLT instruction.  */
148*6dd7d8c6SPaolo Bonzini     if (env->eflags & TF_MASK) {
149*6dd7d8c6SPaolo Bonzini         env->dr[6] |= DR6_BS;
150*6dd7d8c6SPaolo Bonzini         do_interrupt_all(x86_cpu, EXCP01_DB, 0, 0, env->eip, 0);
151*6dd7d8c6SPaolo Bonzini     }
152*6dd7d8c6SPaolo Bonzini     return true;
153ec1d32afSPhilippe Mathieu-Daudé }
154ec1d32afSPhilippe Mathieu-Daudé 
x86_need_replay_interrupt(int interrupt_request)1556ae75481SPhilippe Mathieu-Daudé bool x86_need_replay_interrupt(int interrupt_request)
1566ae75481SPhilippe Mathieu-Daudé {
1576ae75481SPhilippe Mathieu-Daudé     /*
1586ae75481SPhilippe Mathieu-Daudé      * CPU_INTERRUPT_POLL is a virtual event which gets converted into a
1596ae75481SPhilippe Mathieu-Daudé      * "real" interrupt event later. It does not need to be recorded for
1606ae75481SPhilippe Mathieu-Daudé      * replay purposes.
1616ae75481SPhilippe Mathieu-Daudé      */
1626ae75481SPhilippe Mathieu-Daudé     return !(interrupt_request & CPU_INTERRUPT_POLL);
1636ae75481SPhilippe Mathieu-Daudé }
1646ae75481SPhilippe Mathieu-Daudé 
x86_cpu_exec_interrupt(CPUState * cs,int interrupt_request)1650792e6c8SPhilippe Mathieu-Daudé bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
1660792e6c8SPhilippe Mathieu-Daudé {
1670792e6c8SPhilippe Mathieu-Daudé     X86CPU *cpu = X86_CPU(cs);
1680792e6c8SPhilippe Mathieu-Daudé     CPUX86State *env = &cpu->env;
1690792e6c8SPhilippe Mathieu-Daudé     int intno;
1700792e6c8SPhilippe Mathieu-Daudé 
1710792e6c8SPhilippe Mathieu-Daudé     interrupt_request = x86_cpu_pending_interrupt(cs, interrupt_request);
1720792e6c8SPhilippe Mathieu-Daudé     if (!interrupt_request) {
1730792e6c8SPhilippe Mathieu-Daudé         return false;
1740792e6c8SPhilippe Mathieu-Daudé     }
1750792e6c8SPhilippe Mathieu-Daudé 
1760792e6c8SPhilippe Mathieu-Daudé     /* Don't process multiple interrupt requests in a single call.
1770792e6c8SPhilippe Mathieu-Daudé      * This is required to make icount-driven execution deterministic.
1780792e6c8SPhilippe Mathieu-Daudé      */
1790792e6c8SPhilippe Mathieu-Daudé     switch (interrupt_request) {
1800792e6c8SPhilippe Mathieu-Daudé     case CPU_INTERRUPT_POLL:
1810792e6c8SPhilippe Mathieu-Daudé         cs->interrupt_request &= ~CPU_INTERRUPT_POLL;
1820792e6c8SPhilippe Mathieu-Daudé         apic_poll_irq(cpu->apic_state);
1830792e6c8SPhilippe Mathieu-Daudé         break;
1840792e6c8SPhilippe Mathieu-Daudé     case CPU_INTERRUPT_SIPI:
1850792e6c8SPhilippe Mathieu-Daudé         do_cpu_sipi(cpu);
1860792e6c8SPhilippe Mathieu-Daudé         break;
1870792e6c8SPhilippe Mathieu-Daudé     case CPU_INTERRUPT_SMI:
1880792e6c8SPhilippe Mathieu-Daudé         cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0);
1890792e6c8SPhilippe Mathieu-Daudé         cs->interrupt_request &= ~CPU_INTERRUPT_SMI;
1900792e6c8SPhilippe Mathieu-Daudé         do_smm_enter(cpu);
1910792e6c8SPhilippe Mathieu-Daudé         break;
1920792e6c8SPhilippe Mathieu-Daudé     case CPU_INTERRUPT_NMI:
1930792e6c8SPhilippe Mathieu-Daudé         cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0);
1940792e6c8SPhilippe Mathieu-Daudé         cs->interrupt_request &= ~CPU_INTERRUPT_NMI;
1950792e6c8SPhilippe Mathieu-Daudé         env->hflags2 |= HF2_NMI_MASK;
1960792e6c8SPhilippe Mathieu-Daudé         do_interrupt_x86_hardirq(env, EXCP02_NMI, 1);
1970792e6c8SPhilippe Mathieu-Daudé         break;
1980792e6c8SPhilippe Mathieu-Daudé     case CPU_INTERRUPT_MCE:
1990792e6c8SPhilippe Mathieu-Daudé         cs->interrupt_request &= ~CPU_INTERRUPT_MCE;
2000792e6c8SPhilippe Mathieu-Daudé         do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0);
2010792e6c8SPhilippe Mathieu-Daudé         break;
2020792e6c8SPhilippe Mathieu-Daudé     case CPU_INTERRUPT_HARD:
2030792e6c8SPhilippe Mathieu-Daudé         cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0);
2040792e6c8SPhilippe Mathieu-Daudé         cs->interrupt_request &= ~(CPU_INTERRUPT_HARD |
2050792e6c8SPhilippe Mathieu-Daudé                                    CPU_INTERRUPT_VIRQ);
2060792e6c8SPhilippe Mathieu-Daudé         intno = cpu_get_pic_interrupt(env);
207346cd004SAlex Bennée         qemu_log_mask(CPU_LOG_INT,
2080792e6c8SPhilippe Mathieu-Daudé                       "Servicing hardware INT=0x%02x\n", intno);
2090792e6c8SPhilippe Mathieu-Daudé         do_interrupt_x86_hardirq(env, intno, 1);
2100792e6c8SPhilippe Mathieu-Daudé         break;
2110792e6c8SPhilippe Mathieu-Daudé     case CPU_INTERRUPT_VIRQ:
2120792e6c8SPhilippe Mathieu-Daudé         cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0);
2130792e6c8SPhilippe Mathieu-Daudé         intno = x86_ldl_phys(cs, env->vm_vmcb
2140792e6c8SPhilippe Mathieu-Daudé                              + offsetof(struct vmcb, control.int_vector));
215346cd004SAlex Bennée         qemu_log_mask(CPU_LOG_INT,
2160792e6c8SPhilippe Mathieu-Daudé                       "Servicing virtual hardware INT=0x%02x\n", intno);
2170792e6c8SPhilippe Mathieu-Daudé         do_interrupt_x86_hardirq(env, intno, 1);
2180792e6c8SPhilippe Mathieu-Daudé         cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
2190792e6c8SPhilippe Mathieu-Daudé         env->int_ctl &= ~V_IRQ_MASK;
2200792e6c8SPhilippe Mathieu-Daudé         break;
2210792e6c8SPhilippe Mathieu-Daudé     }
2220792e6c8SPhilippe Mathieu-Daudé 
2230792e6c8SPhilippe Mathieu-Daudé     /* Ensure that no TB jump will be modified as the program flow was changed.  */
2240792e6c8SPhilippe Mathieu-Daudé     return true;
2250792e6c8SPhilippe Mathieu-Daudé }
2260792e6c8SPhilippe Mathieu-Daudé 
227d76b9c6fSRichard Henderson /* check if Port I/O is allowed in TSS */
helper_check_io(CPUX86State * env,uint32_t addr,uint32_t size)228d76b9c6fSRichard Henderson void helper_check_io(CPUX86State *env, uint32_t addr, uint32_t size)
229d76b9c6fSRichard Henderson {
230d76b9c6fSRichard Henderson     uintptr_t retaddr = GETPC();
231d76b9c6fSRichard Henderson     uint32_t io_offset, val, mask;
232d76b9c6fSRichard Henderson 
233d76b9c6fSRichard Henderson     /* TSS must be a valid 32 bit one */
234d76b9c6fSRichard Henderson     if (!(env->tr.flags & DESC_P_MASK) ||
235d76b9c6fSRichard Henderson         ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
236d76b9c6fSRichard Henderson         env->tr.limit < 103) {
237d76b9c6fSRichard Henderson         goto fail;
238d76b9c6fSRichard Henderson     }
239d76b9c6fSRichard Henderson     io_offset = cpu_lduw_kernel_ra(env, env->tr.base + 0x66, retaddr);
240d76b9c6fSRichard Henderson     io_offset += (addr >> 3);
241d76b9c6fSRichard Henderson     /* Note: the check needs two bytes */
242d76b9c6fSRichard Henderson     if ((io_offset + 1) > env->tr.limit) {
243d76b9c6fSRichard Henderson         goto fail;
244d76b9c6fSRichard Henderson     }
245d76b9c6fSRichard Henderson     val = cpu_lduw_kernel_ra(env, env->tr.base + io_offset, retaddr);
246d76b9c6fSRichard Henderson     val >>= (addr & 7);
247d76b9c6fSRichard Henderson     mask = (1 << size) - 1;
248d76b9c6fSRichard Henderson     /* all bits must be zero to allow the I/O */
249d76b9c6fSRichard Henderson     if ((val & mask) != 0) {
250d76b9c6fSRichard Henderson     fail:
251d76b9c6fSRichard Henderson         raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr);
252d76b9c6fSRichard Henderson     }
253d76b9c6fSRichard Henderson }
254