162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kvm_host.h> 562306a36Sopenharmony_ci#include "x86.h" 662306a36Sopenharmony_ci#include "kvm_cache_regs.h" 762306a36Sopenharmony_ci#include "kvm_emulate.h" 862306a36Sopenharmony_ci#include "smm.h" 962306a36Sopenharmony_ci#include "cpuid.h" 1062306a36Sopenharmony_ci#include "trace.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define CHECK_SMRAM32_OFFSET(field, offset) \ 1362306a36Sopenharmony_ci ASSERT_STRUCT_OFFSET(struct kvm_smram_state_32, field, offset - 0xFE00) 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define CHECK_SMRAM64_OFFSET(field, offset) \ 1662306a36Sopenharmony_ci ASSERT_STRUCT_OFFSET(struct kvm_smram_state_64, field, offset - 0xFE00) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic void check_smram_offsets(void) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci /* 32 bit SMRAM image */ 2162306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(reserved1, 0xFE00); 2262306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(smbase, 0xFEF8); 2362306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(smm_revision, 0xFEFC); 2462306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(io_inst_restart, 0xFF00); 2562306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(auto_hlt_restart, 0xFF02); 2662306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(io_restart_rdi, 0xFF04); 2762306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(io_restart_rcx, 0xFF08); 2862306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(io_restart_rsi, 0xFF0C); 2962306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(io_restart_rip, 0xFF10); 3062306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(cr4, 0xFF14); 3162306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(reserved2, 0xFF18); 3262306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(int_shadow, 0xFF1A); 3362306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(reserved3, 0xFF1B); 3462306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(ds, 0xFF2C); 3562306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(fs, 0xFF38); 3662306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(gs, 0xFF44); 3762306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(idtr, 0xFF50); 3862306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(tr, 0xFF5C); 3962306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(gdtr, 0xFF6C); 4062306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(ldtr, 0xFF78); 4162306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(es, 0xFF84); 4262306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(cs, 0xFF90); 4362306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(ss, 0xFF9C); 4462306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(es_sel, 0xFFA8); 4562306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(cs_sel, 0xFFAC); 4662306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(ss_sel, 0xFFB0); 4762306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(ds_sel, 0xFFB4); 4862306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(fs_sel, 0xFFB8); 4962306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(gs_sel, 0xFFBC); 5062306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(ldtr_sel, 0xFFC0); 5162306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(tr_sel, 0xFFC4); 5262306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(dr7, 0xFFC8); 5362306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(dr6, 0xFFCC); 5462306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(gprs, 0xFFD0); 5562306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(eip, 0xFFF0); 5662306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(eflags, 0xFFF4); 5762306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(cr3, 0xFFF8); 5862306a36Sopenharmony_ci CHECK_SMRAM32_OFFSET(cr0, 0xFFFC); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* 64 bit SMRAM image */ 6162306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(es, 0xFE00); 6262306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(cs, 0xFE10); 6362306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(ss, 0xFE20); 6462306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(ds, 0xFE30); 6562306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(fs, 0xFE40); 6662306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(gs, 0xFE50); 6762306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(gdtr, 0xFE60); 6862306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(ldtr, 0xFE70); 6962306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(idtr, 0xFE80); 7062306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(tr, 0xFE90); 7162306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(io_restart_rip, 0xFEA0); 7262306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(io_restart_rcx, 0xFEA8); 7362306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(io_restart_rsi, 0xFEB0); 7462306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(io_restart_rdi, 0xFEB8); 7562306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(io_restart_dword, 0xFEC0); 7662306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(reserved1, 0xFEC4); 7762306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(io_inst_restart, 0xFEC8); 7862306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(auto_hlt_restart, 0xFEC9); 7962306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(amd_nmi_mask, 0xFECA); 8062306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(int_shadow, 0xFECB); 8162306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(reserved2, 0xFECC); 8262306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(efer, 0xFED0); 8362306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(svm_guest_flag, 0xFED8); 8462306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(svm_guest_vmcb_gpa, 0xFEE0); 8562306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(svm_guest_virtual_int, 0xFEE8); 8662306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(reserved3, 0xFEF0); 8762306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(smm_revison, 0xFEFC); 8862306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(smbase, 0xFF00); 8962306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(reserved4, 0xFF04); 9062306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(ssp, 0xFF18); 9162306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(svm_guest_pat, 0xFF20); 9262306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(svm_host_efer, 0xFF28); 9362306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(svm_host_cr4, 0xFF30); 9462306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(svm_host_cr3, 0xFF38); 9562306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(svm_host_cr0, 0xFF40); 9662306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(cr4, 0xFF48); 9762306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(cr3, 0xFF50); 9862306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(cr0, 0xFF58); 9962306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(dr7, 0xFF60); 10062306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(dr6, 0xFF68); 10162306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(rflags, 0xFF70); 10262306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(rip, 0xFF78); 10362306a36Sopenharmony_ci CHECK_SMRAM64_OFFSET(gprs, 0xFF80); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(union kvm_smram) != 512); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#undef CHECK_SMRAM64_OFFSET 10962306a36Sopenharmony_ci#undef CHECK_SMRAM32_OFFSET 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_civoid kvm_smm_changed(struct kvm_vcpu *vcpu, bool entering_smm) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci trace_kvm_smm_transition(vcpu->vcpu_id, vcpu->arch.smbase, entering_smm); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (entering_smm) { 11762306a36Sopenharmony_ci vcpu->arch.hflags |= HF_SMM_MASK; 11862306a36Sopenharmony_ci } else { 11962306a36Sopenharmony_ci vcpu->arch.hflags &= ~(HF_SMM_MASK | HF_SMM_INSIDE_NMI_MASK); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Process a latched INIT or SMI, if any. */ 12262306a36Sopenharmony_ci kvm_make_request(KVM_REQ_EVENT, vcpu); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * Even if KVM_SET_SREGS2 loaded PDPTRs out of band, 12662306a36Sopenharmony_ci * on SMM exit we still need to reload them from 12762306a36Sopenharmony_ci * guest memory 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci vcpu->arch.pdptrs_from_userspace = false; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci kvm_mmu_reset_context(vcpu); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid process_smi(struct kvm_vcpu *vcpu) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci vcpu->arch.smi_pending = true; 13862306a36Sopenharmony_ci kvm_make_request(KVM_REQ_EVENT, vcpu); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic u32 enter_smm_get_segment_flags(struct kvm_segment *seg) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci u32 flags = 0; 14462306a36Sopenharmony_ci flags |= seg->g << 23; 14562306a36Sopenharmony_ci flags |= seg->db << 22; 14662306a36Sopenharmony_ci flags |= seg->l << 21; 14762306a36Sopenharmony_ci flags |= seg->avl << 20; 14862306a36Sopenharmony_ci flags |= seg->present << 15; 14962306a36Sopenharmony_ci flags |= seg->dpl << 13; 15062306a36Sopenharmony_ci flags |= seg->s << 12; 15162306a36Sopenharmony_ci flags |= seg->type << 8; 15262306a36Sopenharmony_ci return flags; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void enter_smm_save_seg_32(struct kvm_vcpu *vcpu, 15662306a36Sopenharmony_ci struct kvm_smm_seg_state_32 *state, 15762306a36Sopenharmony_ci u32 *selector, int n) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct kvm_segment seg; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci kvm_get_segment(vcpu, &seg, n); 16262306a36Sopenharmony_ci *selector = seg.selector; 16362306a36Sopenharmony_ci state->base = seg.base; 16462306a36Sopenharmony_ci state->limit = seg.limit; 16562306a36Sopenharmony_ci state->flags = enter_smm_get_segment_flags(&seg); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci#ifdef CONFIG_X86_64 16962306a36Sopenharmony_cistatic void enter_smm_save_seg_64(struct kvm_vcpu *vcpu, 17062306a36Sopenharmony_ci struct kvm_smm_seg_state_64 *state, 17162306a36Sopenharmony_ci int n) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct kvm_segment seg; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci kvm_get_segment(vcpu, &seg, n); 17662306a36Sopenharmony_ci state->selector = seg.selector; 17762306a36Sopenharmony_ci state->attributes = enter_smm_get_segment_flags(&seg) >> 8; 17862306a36Sopenharmony_ci state->limit = seg.limit; 17962306a36Sopenharmony_ci state->base = seg.base; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci#endif 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void enter_smm_save_state_32(struct kvm_vcpu *vcpu, 18462306a36Sopenharmony_ci struct kvm_smram_state_32 *smram) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct desc_ptr dt; 18762306a36Sopenharmony_ci unsigned long val; 18862306a36Sopenharmony_ci int i; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci smram->cr0 = kvm_read_cr0(vcpu); 19162306a36Sopenharmony_ci smram->cr3 = kvm_read_cr3(vcpu); 19262306a36Sopenharmony_ci smram->eflags = kvm_get_rflags(vcpu); 19362306a36Sopenharmony_ci smram->eip = kvm_rip_read(vcpu); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci for (i = 0; i < 8; i++) 19662306a36Sopenharmony_ci smram->gprs[i] = kvm_register_read_raw(vcpu, i); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci kvm_get_dr(vcpu, 6, &val); 19962306a36Sopenharmony_ci smram->dr6 = (u32)val; 20062306a36Sopenharmony_ci kvm_get_dr(vcpu, 7, &val); 20162306a36Sopenharmony_ci smram->dr7 = (u32)val; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci enter_smm_save_seg_32(vcpu, &smram->tr, &smram->tr_sel, VCPU_SREG_TR); 20462306a36Sopenharmony_ci enter_smm_save_seg_32(vcpu, &smram->ldtr, &smram->ldtr_sel, VCPU_SREG_LDTR); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci static_call(kvm_x86_get_gdt)(vcpu, &dt); 20762306a36Sopenharmony_ci smram->gdtr.base = dt.address; 20862306a36Sopenharmony_ci smram->gdtr.limit = dt.size; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci static_call(kvm_x86_get_idt)(vcpu, &dt); 21162306a36Sopenharmony_ci smram->idtr.base = dt.address; 21262306a36Sopenharmony_ci smram->idtr.limit = dt.size; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci enter_smm_save_seg_32(vcpu, &smram->es, &smram->es_sel, VCPU_SREG_ES); 21562306a36Sopenharmony_ci enter_smm_save_seg_32(vcpu, &smram->cs, &smram->cs_sel, VCPU_SREG_CS); 21662306a36Sopenharmony_ci enter_smm_save_seg_32(vcpu, &smram->ss, &smram->ss_sel, VCPU_SREG_SS); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci enter_smm_save_seg_32(vcpu, &smram->ds, &smram->ds_sel, VCPU_SREG_DS); 21962306a36Sopenharmony_ci enter_smm_save_seg_32(vcpu, &smram->fs, &smram->fs_sel, VCPU_SREG_FS); 22062306a36Sopenharmony_ci enter_smm_save_seg_32(vcpu, &smram->gs, &smram->gs_sel, VCPU_SREG_GS); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci smram->cr4 = kvm_read_cr4(vcpu); 22362306a36Sopenharmony_ci smram->smm_revision = 0x00020000; 22462306a36Sopenharmony_ci smram->smbase = vcpu->arch.smbase; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci smram->int_shadow = static_call(kvm_x86_get_interrupt_shadow)(vcpu); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci#ifdef CONFIG_X86_64 23062306a36Sopenharmony_cistatic void enter_smm_save_state_64(struct kvm_vcpu *vcpu, 23162306a36Sopenharmony_ci struct kvm_smram_state_64 *smram) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct desc_ptr dt; 23462306a36Sopenharmony_ci unsigned long val; 23562306a36Sopenharmony_ci int i; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci for (i = 0; i < 16; i++) 23862306a36Sopenharmony_ci smram->gprs[15 - i] = kvm_register_read_raw(vcpu, i); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci smram->rip = kvm_rip_read(vcpu); 24162306a36Sopenharmony_ci smram->rflags = kvm_get_rflags(vcpu); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci kvm_get_dr(vcpu, 6, &val); 24562306a36Sopenharmony_ci smram->dr6 = val; 24662306a36Sopenharmony_ci kvm_get_dr(vcpu, 7, &val); 24762306a36Sopenharmony_ci smram->dr7 = val; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci smram->cr0 = kvm_read_cr0(vcpu); 25062306a36Sopenharmony_ci smram->cr3 = kvm_read_cr3(vcpu); 25162306a36Sopenharmony_ci smram->cr4 = kvm_read_cr4(vcpu); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci smram->smbase = vcpu->arch.smbase; 25462306a36Sopenharmony_ci smram->smm_revison = 0x00020064; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci smram->efer = vcpu->arch.efer; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci enter_smm_save_seg_64(vcpu, &smram->tr, VCPU_SREG_TR); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci static_call(kvm_x86_get_idt)(vcpu, &dt); 26162306a36Sopenharmony_ci smram->idtr.limit = dt.size; 26262306a36Sopenharmony_ci smram->idtr.base = dt.address; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci enter_smm_save_seg_64(vcpu, &smram->ldtr, VCPU_SREG_LDTR); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci static_call(kvm_x86_get_gdt)(vcpu, &dt); 26762306a36Sopenharmony_ci smram->gdtr.limit = dt.size; 26862306a36Sopenharmony_ci smram->gdtr.base = dt.address; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci enter_smm_save_seg_64(vcpu, &smram->es, VCPU_SREG_ES); 27162306a36Sopenharmony_ci enter_smm_save_seg_64(vcpu, &smram->cs, VCPU_SREG_CS); 27262306a36Sopenharmony_ci enter_smm_save_seg_64(vcpu, &smram->ss, VCPU_SREG_SS); 27362306a36Sopenharmony_ci enter_smm_save_seg_64(vcpu, &smram->ds, VCPU_SREG_DS); 27462306a36Sopenharmony_ci enter_smm_save_seg_64(vcpu, &smram->fs, VCPU_SREG_FS); 27562306a36Sopenharmony_ci enter_smm_save_seg_64(vcpu, &smram->gs, VCPU_SREG_GS); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci smram->int_shadow = static_call(kvm_x86_get_interrupt_shadow)(vcpu); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci#endif 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_civoid enter_smm(struct kvm_vcpu *vcpu) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct kvm_segment cs, ds; 28462306a36Sopenharmony_ci struct desc_ptr dt; 28562306a36Sopenharmony_ci unsigned long cr0; 28662306a36Sopenharmony_ci union kvm_smram smram; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci check_smram_offsets(); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci memset(smram.bytes, 0, sizeof(smram.bytes)); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#ifdef CONFIG_X86_64 29362306a36Sopenharmony_ci if (guest_cpuid_has(vcpu, X86_FEATURE_LM)) 29462306a36Sopenharmony_ci enter_smm_save_state_64(vcpu, &smram.smram64); 29562306a36Sopenharmony_ci else 29662306a36Sopenharmony_ci#endif 29762306a36Sopenharmony_ci enter_smm_save_state_32(vcpu, &smram.smram32); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * Give enter_smm() a chance to make ISA-specific changes to the vCPU 30162306a36Sopenharmony_ci * state (e.g. leave guest mode) after we've saved the state into the 30262306a36Sopenharmony_ci * SMM state-save area. 30362306a36Sopenharmony_ci * 30462306a36Sopenharmony_ci * Kill the VM in the unlikely case of failure, because the VM 30562306a36Sopenharmony_ci * can be in undefined state in this case. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci if (static_call(kvm_x86_enter_smm)(vcpu, &smram)) 30862306a36Sopenharmony_ci goto error; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci kvm_smm_changed(vcpu, true); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (kvm_vcpu_write_guest(vcpu, vcpu->arch.smbase + 0xfe00, &smram, sizeof(smram))) 31362306a36Sopenharmony_ci goto error; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (static_call(kvm_x86_get_nmi_mask)(vcpu)) 31662306a36Sopenharmony_ci vcpu->arch.hflags |= HF_SMM_INSIDE_NMI_MASK; 31762306a36Sopenharmony_ci else 31862306a36Sopenharmony_ci static_call(kvm_x86_set_nmi_mask)(vcpu, true); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci kvm_set_rflags(vcpu, X86_EFLAGS_FIXED); 32162306a36Sopenharmony_ci kvm_rip_write(vcpu, 0x8000); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci static_call(kvm_x86_set_interrupt_shadow)(vcpu, 0); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci cr0 = vcpu->arch.cr0 & ~(X86_CR0_PE | X86_CR0_EM | X86_CR0_TS | X86_CR0_PG); 32662306a36Sopenharmony_ci static_call(kvm_x86_set_cr0)(vcpu, cr0); 32762306a36Sopenharmony_ci vcpu->arch.cr0 = cr0; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci static_call(kvm_x86_set_cr4)(vcpu, 0); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Undocumented: IDT limit is set to zero on entry to SMM. */ 33262306a36Sopenharmony_ci dt.address = dt.size = 0; 33362306a36Sopenharmony_ci static_call(kvm_x86_set_idt)(vcpu, &dt); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (WARN_ON_ONCE(kvm_set_dr(vcpu, 7, DR7_FIXED_1))) 33662306a36Sopenharmony_ci goto error; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci cs.selector = (vcpu->arch.smbase >> 4) & 0xffff; 33962306a36Sopenharmony_ci cs.base = vcpu->arch.smbase; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci ds.selector = 0; 34262306a36Sopenharmony_ci ds.base = 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci cs.limit = ds.limit = 0xffffffff; 34562306a36Sopenharmony_ci cs.type = ds.type = 0x3; 34662306a36Sopenharmony_ci cs.dpl = ds.dpl = 0; 34762306a36Sopenharmony_ci cs.db = ds.db = 0; 34862306a36Sopenharmony_ci cs.s = ds.s = 1; 34962306a36Sopenharmony_ci cs.l = ds.l = 0; 35062306a36Sopenharmony_ci cs.g = ds.g = 1; 35162306a36Sopenharmony_ci cs.avl = ds.avl = 0; 35262306a36Sopenharmony_ci cs.present = ds.present = 1; 35362306a36Sopenharmony_ci cs.unusable = ds.unusable = 0; 35462306a36Sopenharmony_ci cs.padding = ds.padding = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci kvm_set_segment(vcpu, &cs, VCPU_SREG_CS); 35762306a36Sopenharmony_ci kvm_set_segment(vcpu, &ds, VCPU_SREG_DS); 35862306a36Sopenharmony_ci kvm_set_segment(vcpu, &ds, VCPU_SREG_ES); 35962306a36Sopenharmony_ci kvm_set_segment(vcpu, &ds, VCPU_SREG_FS); 36062306a36Sopenharmony_ci kvm_set_segment(vcpu, &ds, VCPU_SREG_GS); 36162306a36Sopenharmony_ci kvm_set_segment(vcpu, &ds, VCPU_SREG_SS); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci#ifdef CONFIG_X86_64 36462306a36Sopenharmony_ci if (guest_cpuid_has(vcpu, X86_FEATURE_LM)) 36562306a36Sopenharmony_ci if (static_call(kvm_x86_set_efer)(vcpu, 0)) 36662306a36Sopenharmony_ci goto error; 36762306a36Sopenharmony_ci#endif 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci kvm_update_cpuid_runtime(vcpu); 37062306a36Sopenharmony_ci kvm_mmu_reset_context(vcpu); 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_cierror: 37362306a36Sopenharmony_ci kvm_vm_dead(vcpu->kvm); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic void rsm_set_desc_flags(struct kvm_segment *desc, u32 flags) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci desc->g = (flags >> 23) & 1; 37962306a36Sopenharmony_ci desc->db = (flags >> 22) & 1; 38062306a36Sopenharmony_ci desc->l = (flags >> 21) & 1; 38162306a36Sopenharmony_ci desc->avl = (flags >> 20) & 1; 38262306a36Sopenharmony_ci desc->present = (flags >> 15) & 1; 38362306a36Sopenharmony_ci desc->dpl = (flags >> 13) & 3; 38462306a36Sopenharmony_ci desc->s = (flags >> 12) & 1; 38562306a36Sopenharmony_ci desc->type = (flags >> 8) & 15; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci desc->unusable = !desc->present; 38862306a36Sopenharmony_ci desc->padding = 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int rsm_load_seg_32(struct kvm_vcpu *vcpu, 39262306a36Sopenharmony_ci const struct kvm_smm_seg_state_32 *state, 39362306a36Sopenharmony_ci u16 selector, int n) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct kvm_segment desc; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci desc.selector = selector; 39862306a36Sopenharmony_ci desc.base = state->base; 39962306a36Sopenharmony_ci desc.limit = state->limit; 40062306a36Sopenharmony_ci rsm_set_desc_flags(&desc, state->flags); 40162306a36Sopenharmony_ci kvm_set_segment(vcpu, &desc, n); 40262306a36Sopenharmony_ci return X86EMUL_CONTINUE; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci#ifdef CONFIG_X86_64 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int rsm_load_seg_64(struct kvm_vcpu *vcpu, 40862306a36Sopenharmony_ci const struct kvm_smm_seg_state_64 *state, 40962306a36Sopenharmony_ci int n) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct kvm_segment desc; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci desc.selector = state->selector; 41462306a36Sopenharmony_ci rsm_set_desc_flags(&desc, state->attributes << 8); 41562306a36Sopenharmony_ci desc.limit = state->limit; 41662306a36Sopenharmony_ci desc.base = state->base; 41762306a36Sopenharmony_ci kvm_set_segment(vcpu, &desc, n); 41862306a36Sopenharmony_ci return X86EMUL_CONTINUE; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci#endif 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int rsm_enter_protected_mode(struct kvm_vcpu *vcpu, 42362306a36Sopenharmony_ci u64 cr0, u64 cr3, u64 cr4) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci int bad; 42662306a36Sopenharmony_ci u64 pcid; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* In order to later set CR4.PCIDE, CR3[11:0] must be zero. */ 42962306a36Sopenharmony_ci pcid = 0; 43062306a36Sopenharmony_ci if (cr4 & X86_CR4_PCIDE) { 43162306a36Sopenharmony_ci pcid = cr3 & 0xfff; 43262306a36Sopenharmony_ci cr3 &= ~0xfff; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci bad = kvm_set_cr3(vcpu, cr3); 43662306a36Sopenharmony_ci if (bad) 43762306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* 44062306a36Sopenharmony_ci * First enable PAE, long mode needs it before CR0.PG = 1 is set. 44162306a36Sopenharmony_ci * Then enable protected mode. However, PCID cannot be enabled 44262306a36Sopenharmony_ci * if EFER.LMA=0, so set it separately. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci bad = kvm_set_cr4(vcpu, cr4 & ~X86_CR4_PCIDE); 44562306a36Sopenharmony_ci if (bad) 44662306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci bad = kvm_set_cr0(vcpu, cr0); 44962306a36Sopenharmony_ci if (bad) 45062306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (cr4 & X86_CR4_PCIDE) { 45362306a36Sopenharmony_ci bad = kvm_set_cr4(vcpu, cr4); 45462306a36Sopenharmony_ci if (bad) 45562306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 45662306a36Sopenharmony_ci if (pcid) { 45762306a36Sopenharmony_ci bad = kvm_set_cr3(vcpu, cr3 | pcid); 45862306a36Sopenharmony_ci if (bad) 45962306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return X86EMUL_CONTINUE; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int rsm_load_state_32(struct x86_emulate_ctxt *ctxt, 46862306a36Sopenharmony_ci const struct kvm_smram_state_32 *smstate) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct kvm_vcpu *vcpu = ctxt->vcpu; 47162306a36Sopenharmony_ci struct desc_ptr dt; 47262306a36Sopenharmony_ci int i, r; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ctxt->eflags = smstate->eflags | X86_EFLAGS_FIXED; 47562306a36Sopenharmony_ci ctxt->_eip = smstate->eip; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci for (i = 0; i < 8; i++) 47862306a36Sopenharmony_ci *reg_write(ctxt, i) = smstate->gprs[i]; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (kvm_set_dr(vcpu, 6, smstate->dr6)) 48162306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 48262306a36Sopenharmony_ci if (kvm_set_dr(vcpu, 7, smstate->dr7)) 48362306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci rsm_load_seg_32(vcpu, &smstate->tr, smstate->tr_sel, VCPU_SREG_TR); 48662306a36Sopenharmony_ci rsm_load_seg_32(vcpu, &smstate->ldtr, smstate->ldtr_sel, VCPU_SREG_LDTR); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci dt.address = smstate->gdtr.base; 48962306a36Sopenharmony_ci dt.size = smstate->gdtr.limit; 49062306a36Sopenharmony_ci static_call(kvm_x86_set_gdt)(vcpu, &dt); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci dt.address = smstate->idtr.base; 49362306a36Sopenharmony_ci dt.size = smstate->idtr.limit; 49462306a36Sopenharmony_ci static_call(kvm_x86_set_idt)(vcpu, &dt); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci rsm_load_seg_32(vcpu, &smstate->es, smstate->es_sel, VCPU_SREG_ES); 49762306a36Sopenharmony_ci rsm_load_seg_32(vcpu, &smstate->cs, smstate->cs_sel, VCPU_SREG_CS); 49862306a36Sopenharmony_ci rsm_load_seg_32(vcpu, &smstate->ss, smstate->ss_sel, VCPU_SREG_SS); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci rsm_load_seg_32(vcpu, &smstate->ds, smstate->ds_sel, VCPU_SREG_DS); 50162306a36Sopenharmony_ci rsm_load_seg_32(vcpu, &smstate->fs, smstate->fs_sel, VCPU_SREG_FS); 50262306a36Sopenharmony_ci rsm_load_seg_32(vcpu, &smstate->gs, smstate->gs_sel, VCPU_SREG_GS); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci vcpu->arch.smbase = smstate->smbase; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci r = rsm_enter_protected_mode(vcpu, smstate->cr0, 50762306a36Sopenharmony_ci smstate->cr3, smstate->cr4); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (r != X86EMUL_CONTINUE) 51062306a36Sopenharmony_ci return r; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci static_call(kvm_x86_set_interrupt_shadow)(vcpu, 0); 51362306a36Sopenharmony_ci ctxt->interruptibility = (u8)smstate->int_shadow; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return r; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci#ifdef CONFIG_X86_64 51962306a36Sopenharmony_cistatic int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, 52062306a36Sopenharmony_ci const struct kvm_smram_state_64 *smstate) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct kvm_vcpu *vcpu = ctxt->vcpu; 52362306a36Sopenharmony_ci struct desc_ptr dt; 52462306a36Sopenharmony_ci int i, r; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci for (i = 0; i < 16; i++) 52762306a36Sopenharmony_ci *reg_write(ctxt, i) = smstate->gprs[15 - i]; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ctxt->_eip = smstate->rip; 53062306a36Sopenharmony_ci ctxt->eflags = smstate->rflags | X86_EFLAGS_FIXED; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (kvm_set_dr(vcpu, 6, smstate->dr6)) 53362306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 53462306a36Sopenharmony_ci if (kvm_set_dr(vcpu, 7, smstate->dr7)) 53562306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci vcpu->arch.smbase = smstate->smbase; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (kvm_set_msr(vcpu, MSR_EFER, smstate->efer & ~EFER_LMA)) 54062306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci rsm_load_seg_64(vcpu, &smstate->tr, VCPU_SREG_TR); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci dt.size = smstate->idtr.limit; 54562306a36Sopenharmony_ci dt.address = smstate->idtr.base; 54662306a36Sopenharmony_ci static_call(kvm_x86_set_idt)(vcpu, &dt); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci rsm_load_seg_64(vcpu, &smstate->ldtr, VCPU_SREG_LDTR); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci dt.size = smstate->gdtr.limit; 55162306a36Sopenharmony_ci dt.address = smstate->gdtr.base; 55262306a36Sopenharmony_ci static_call(kvm_x86_set_gdt)(vcpu, &dt); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci r = rsm_enter_protected_mode(vcpu, smstate->cr0, smstate->cr3, smstate->cr4); 55562306a36Sopenharmony_ci if (r != X86EMUL_CONTINUE) 55662306a36Sopenharmony_ci return r; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci rsm_load_seg_64(vcpu, &smstate->es, VCPU_SREG_ES); 55962306a36Sopenharmony_ci rsm_load_seg_64(vcpu, &smstate->cs, VCPU_SREG_CS); 56062306a36Sopenharmony_ci rsm_load_seg_64(vcpu, &smstate->ss, VCPU_SREG_SS); 56162306a36Sopenharmony_ci rsm_load_seg_64(vcpu, &smstate->ds, VCPU_SREG_DS); 56262306a36Sopenharmony_ci rsm_load_seg_64(vcpu, &smstate->fs, VCPU_SREG_FS); 56362306a36Sopenharmony_ci rsm_load_seg_64(vcpu, &smstate->gs, VCPU_SREG_GS); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci static_call(kvm_x86_set_interrupt_shadow)(vcpu, 0); 56662306a36Sopenharmony_ci ctxt->interruptibility = (u8)smstate->int_shadow; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return X86EMUL_CONTINUE; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci#endif 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ciint emulator_leave_smm(struct x86_emulate_ctxt *ctxt) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct kvm_vcpu *vcpu = ctxt->vcpu; 57562306a36Sopenharmony_ci unsigned long cr0; 57662306a36Sopenharmony_ci union kvm_smram smram; 57762306a36Sopenharmony_ci u64 smbase; 57862306a36Sopenharmony_ci int ret; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci smbase = vcpu->arch.smbase; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ret = kvm_vcpu_read_guest(vcpu, smbase + 0xfe00, smram.bytes, sizeof(smram)); 58362306a36Sopenharmony_ci if (ret < 0) 58462306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if ((vcpu->arch.hflags & HF_SMM_INSIDE_NMI_MASK) == 0) 58762306a36Sopenharmony_ci static_call(kvm_x86_set_nmi_mask)(vcpu, false); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci kvm_smm_changed(vcpu, false); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * Get back to real mode, to prepare a safe state in which to load 59362306a36Sopenharmony_ci * CR0/CR3/CR4/EFER. It's all a bit more complicated if the vCPU 59462306a36Sopenharmony_ci * supports long mode. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci#ifdef CONFIG_X86_64 59762306a36Sopenharmony_ci if (guest_cpuid_has(vcpu, X86_FEATURE_LM)) { 59862306a36Sopenharmony_ci struct kvm_segment cs_desc; 59962306a36Sopenharmony_ci unsigned long cr4; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Zero CR4.PCIDE before CR0.PG. */ 60262306a36Sopenharmony_ci cr4 = kvm_read_cr4(vcpu); 60362306a36Sopenharmony_ci if (cr4 & X86_CR4_PCIDE) 60462306a36Sopenharmony_ci kvm_set_cr4(vcpu, cr4 & ~X86_CR4_PCIDE); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* A 32-bit code segment is required to clear EFER.LMA. */ 60762306a36Sopenharmony_ci memset(&cs_desc, 0, sizeof(cs_desc)); 60862306a36Sopenharmony_ci cs_desc.type = 0xb; 60962306a36Sopenharmony_ci cs_desc.s = cs_desc.g = cs_desc.present = 1; 61062306a36Sopenharmony_ci kvm_set_segment(vcpu, &cs_desc, VCPU_SREG_CS); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci#endif 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* For the 64-bit case, this will clear EFER.LMA. */ 61562306a36Sopenharmony_ci cr0 = kvm_read_cr0(vcpu); 61662306a36Sopenharmony_ci if (cr0 & X86_CR0_PE) 61762306a36Sopenharmony_ci kvm_set_cr0(vcpu, cr0 & ~(X86_CR0_PG | X86_CR0_PE)); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci#ifdef CONFIG_X86_64 62062306a36Sopenharmony_ci if (guest_cpuid_has(vcpu, X86_FEATURE_LM)) { 62162306a36Sopenharmony_ci unsigned long cr4, efer; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* Clear CR4.PAE before clearing EFER.LME. */ 62462306a36Sopenharmony_ci cr4 = kvm_read_cr4(vcpu); 62562306a36Sopenharmony_ci if (cr4 & X86_CR4_PAE) 62662306a36Sopenharmony_ci kvm_set_cr4(vcpu, cr4 & ~X86_CR4_PAE); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* And finally go back to 32-bit mode. */ 62962306a36Sopenharmony_ci efer = 0; 63062306a36Sopenharmony_ci kvm_set_msr(vcpu, MSR_EFER, efer); 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci#endif 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* 63562306a36Sopenharmony_ci * Give leave_smm() a chance to make ISA-specific changes to the vCPU 63662306a36Sopenharmony_ci * state (e.g. enter guest mode) before loading state from the SMM 63762306a36Sopenharmony_ci * state-save area. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ci if (static_call(kvm_x86_leave_smm)(vcpu, &smram)) 64062306a36Sopenharmony_ci return X86EMUL_UNHANDLEABLE; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci#ifdef CONFIG_X86_64 64362306a36Sopenharmony_ci if (guest_cpuid_has(vcpu, X86_FEATURE_LM)) 64462306a36Sopenharmony_ci return rsm_load_state_64(ctxt, &smram.smram64); 64562306a36Sopenharmony_ci else 64662306a36Sopenharmony_ci#endif 64762306a36Sopenharmony_ci return rsm_load_state_32(ctxt, &smram.smram32); 64862306a36Sopenharmony_ci} 649