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