From: Vitaly Kuznetsov <vkuznets@redhat.com>
To: Paolo Bonzini <pbonzini@redhat.com>, kvm@vger.kernel.org
Cc: "Radim Krčmář" <rkrcmar@redhat.com>,
"Liran Alon" <liran.alon@oracle.com>,
"Sean Christopherson" <sean.j.christopherson@intel.com>,
"Jim Mattson" <jmattson@google.com>,
linux-kernel@vger.kernel.org
Subject: Re: [RFC] selftests: kvm: add a selftest for SMM
Date: Wed, 10 Apr 2019 12:32:49 +0200 [thread overview]
Message-ID: <87mukygnfy.fsf@vitty.brq.redhat.com> (raw)
In-Reply-To: <c315341a-aa8f-c52f-e02d-8104ba5c0051@redhat.com>
Paolo Bonzini <pbonzini@redhat.com> writes:
> On 10/04/19 11:38, Vitaly Kuznetsov wrote:
>> Early RFC, based on state_test.
>>
>> Add a simplistic test for SMM. Currently it fails with
>> "Unexpected result from KVM_SET_NESTED_STATE" even when a patch fixing
>> rsm after VMXON is added. There's likey some other issue in nested
>> save/restore when SMM os on.
>>
>> The test implements its own sync between the guest and the host as using
>> our ucall library seems to be too cumbersome: SMI handler is happening in
>> real-address mode.
>>
>> Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
>> ---
>> tools/testing/selftests/kvm/Makefile | 1 +
>> .../selftests/kvm/include/x86_64/processor.h | 27 +++
>> tools/testing/selftests/kvm/x86_64/smm_test.c | 159 ++++++++++++++++++
>> 3 files changed, 187 insertions(+)
>> create mode 100644 tools/testing/selftests/kvm/x86_64/smm_test.c
>>
>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
>> index 3c1f4bdf9000..257955593cdf 100644
>> --- a/tools/testing/selftests/kvm/Makefile
>> +++ b/tools/testing/selftests/kvm/Makefile
>> @@ -17,6 +17,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/state_test
>> TEST_GEN_PROGS_x86_64 += x86_64/evmcs_test
>> TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid
>> TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
>> +TEST_GEN_PROGS_x86_64 += x86_64/smm_test
>> TEST_GEN_PROGS_x86_64 += dirty_log_test
>> TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
>>
>> diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
>> index e2884c2b81ff..6063d5b2f356 100644
>> --- a/tools/testing/selftests/kvm/include/x86_64/processor.h
>> +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
>> @@ -778,6 +778,33 @@ void vcpu_set_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index,
>> #define MSR_IA32_APICBASE_ENABLE (1<<11)
>> #define MSR_IA32_APICBASE_BASE (0xfffff<<12)
>>
>> +#define APIC_BASE_MSR 0x800
>> +#define X2APIC_ENABLE (1UL << 10)
>> +#define APIC_ICR 0x300
>> +#define APIC_DEST_SELF 0x40000
>> +#define APIC_DEST_ALLINC 0x80000
>> +#define APIC_DEST_ALLBUT 0xC0000
>> +#define APIC_ICR_RR_MASK 0x30000
>> +#define APIC_ICR_RR_INVALID 0x00000
>> +#define APIC_ICR_RR_INPROG 0x10000
>> +#define APIC_ICR_RR_VALID 0x20000
>> +#define APIC_INT_LEVELTRIG 0x08000
>> +#define APIC_INT_ASSERT 0x04000
>> +#define APIC_ICR_BUSY 0x01000
>> +#define APIC_DEST_LOGICAL 0x00800
>> +#define APIC_DEST_PHYSICAL 0x00000
>> +#define APIC_DM_FIXED 0x00000
>> +#define APIC_DM_FIXED_MASK 0x00700
>> +#define APIC_DM_LOWEST 0x00100
>> +#define APIC_DM_SMI 0x00200
>> +#define APIC_DM_REMRD 0x00300
>> +#define APIC_DM_NMI 0x00400
>> +#define APIC_DM_INIT 0x00500
>> +#define APIC_DM_STARTUP 0x00600
>> +#define APIC_DM_EXTINT 0x00700
>> +#define APIC_VECTOR_MASK 0x000FF
>> +#define APIC_ICR2 0x310
>> +
>> #define MSR_IA32_TSCDEADLINE 0x000006e0
>>
>> #define MSR_IA32_UCODE_WRITE 0x00000079
>> diff --git a/tools/testing/selftests/kvm/x86_64/smm_test.c b/tools/testing/selftests/kvm/x86_64/smm_test.c
>> new file mode 100644
>> index 000000000000..3452b592588a
>> --- /dev/null
>> +++ b/tools/testing/selftests/kvm/x86_64/smm_test.c
>> @@ -0,0 +1,159 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2018, Red Hat, Inc.
>> + *
>> + * Tests for SMM.
>> + */
>> +#define _GNU_SOURCE /* for program_invocation_short_name */
>> +#include <fcntl.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <stdint.h>
>> +#include <string.h>
>> +#include <sys/ioctl.h>
>> +
>> +#include "test_util.h"
>> +
>> +#include "kvm_util.h"
>> +
>> +#include "vmx.h"
>> +
>> +#define VCPU_ID 1
>> +
>> +#define PAGE_SIZE 4096
>> +
>> +#define SMRAM_SIZE (16 * PAGE_SIZE)
>> +#define SMRAM_GPA 0x1000000
>> +#define SMRAM_STAGE 0xfe
>> +
>> +#define STR(x) #x
>> +#define XSTR(s) STR(s)
>> +
>> +#define SYNC_PORT 0xe
>> +#define DONE 0xff
>> +
>> +/*
>> + * This is compiled as normal 64-bit code, however, SMI handler is executed
>> + * in real-address mode. To stay simple we're limiting ourselves to a mode
>> + * independent subset of asm here.
>> + * SMI handler always report back fixed stage SMRAM_STAGE.
>> + */
>> +void smi_handler(void)
>> +{
>> + asm volatile("mov $" XSTR(SMRAM_STAGE) ", %al \n"
>> + "in $" XSTR(SYNC_PORT) ", %al \n"
>> + "rsm \n");
>> +}
>> +/* This should change if the code above changes in length */
>> +#define SMI_HANDLER_SIZE 6
>
> Here I had similar code but I just wrote hex instead. :)
>
I enjoyed playing the game "find the right instruction" so we don't need
to have a separate .s file. It seems we're good with the 3-instruction
stub.
>> +
>> +void sync_with_host(uint64_t phase)
>> +{
>> + asm volatile("in $" XSTR(SYNC_PORT)", %%al \n"
>> + : : "a" (phase));
>> +}
>> +
>> +void self_smi(void)
>> +{
>> + wrmsr(APIC_BASE_MSR + (APIC_ICR >> 4),
>> + APIC_DEST_SELF | APIC_INT_ASSERT | APIC_DM_SMI);
>> +}
>> +
>> +void guest_code(struct vmx_pages *vmx_pages)
>> +{
>> + uint64_t apicbase = rdmsr(MSR_IA32_APICBASE);
>> +
>> + sync_with_host(1);
>> +
>> + wrmsr(MSR_IA32_APICBASE, apicbase | X2APIC_ENABLE);
>> +
>> + sync_with_host(2);
>> +
>> + self_smi();
>> +
>> + sync_with_host(4);
>> +
>> + if (vmx_pages) {
>> + GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
>> +
>> + sync_with_host(5);
>> +
>> + self_smi();
>> +
>> + sync_with_host(7);
>> + }
>> +
>> + sync_with_host(DONE);
>> +}
>> +
>> +int main(int argc, char *argv[])
>> +{
>> + struct vmx_pages *vmx_pages = NULL;
>> + vm_vaddr_t vmx_pages_gva = 0;
>> +
>> + struct kvm_regs regs;
>> + struct kvm_vm *vm;
>> + struct kvm_run *run;
>> + struct kvm_x86_state *state;
>> + int stage, stage_reported;
>> +
>> + /* Create VM */
>> + vm = vm_create_default(VCPU_ID, 0, guest_code);
>> +
>> + vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
>> +
>> + run = vcpu_state(vm, VCPU_ID);
>> +
>> + vcpu_regs_get(vm, VCPU_ID, ®s);
>
> Unused?
>
Yes, probably a leftover from state_test
>> +
>> + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, SMRAM_GPA,
>> + 1 << 16 | 1, SMRAM_SIZE/PAGE_SIZE, 0);
>
> Better add a vm_phy_pages_alloc here.
>
> I'll debug the remaining failure and post it again.
Thanks!
>
> Paolo
>
>> + memset(addr_gpa2hva(vm, SMRAM_GPA), 0x0, SMRAM_SIZE);
>> + memcpy(addr_gpa2hva(vm, SMRAM_GPA) + 0x8000, smi_handler,
>> + SMI_HANDLER_SIZE);
>> +
>> + vcpu_set_msr(vm, VCPU_ID, MSR_IA32_SMBASE, SMRAM_GPA);
>> +
>> + if (kvm_check_cap(KVM_CAP_NESTED_STATE)) {
>> + vmx_pages = vcpu_alloc_vmx(vm, &vmx_pages_gva);
>> + vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva);
>> + } else {
>> + printf("will skip SMM test with VMX enabled\n");
>> + vcpu_args_set(vm, VCPU_ID, 1, 0);
>> + }
>> +
>> + for (stage = 1;; stage++) {
>> + _vcpu_run(vm, VCPU_ID);
>> + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
>> + "Stage %d: unexpected exit reason: %u (%s),\n",
>> + stage, run->exit_reason,
>> + exit_reason_str(run->exit_reason));
>> +
>> + memset(®s, 0, sizeof(regs));
>> + vcpu_regs_get(vm, VCPU_ID, ®s);
>> +
>> + stage_reported = regs.rax & 0xff;
>> +
>> + if (stage_reported == DONE)
>> + goto done;
>> +
>> + TEST_ASSERT(stage_reported == stage ||
>> + stage_reported == SMRAM_STAGE,
>> + "Unexpected stage: #%x, got %x",
>> + stage, stage_reported);
>> +
>> + /* This piece of code needs sharing with state_test/evmcs_test */
>> + state = vcpu_save_state(vm, VCPU_ID);
>> + kvm_vm_release(vm);
>> + kvm_vm_restart(vm, O_RDWR);
>> + vm_vcpu_add(vm, VCPU_ID, 0, 0);
>> + vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
>> + vcpu_load_state(vm, VCPU_ID, state);
>> + run = vcpu_state(vm, VCPU_ID);
>> + free(state);
>> + }
>> +
>> +done:
>> + kvm_vm_free(vm);
>> +}
>>
>
--
Vitaly
prev parent reply other threads:[~2019-04-10 10:32 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-03-26 13:07 [PATCH] KVM: x86: nVMX: allow RSM to restore VMXE CR4 flag Vitaly Kuznetsov
2019-03-26 13:11 ` Liran Alon
2019-03-26 13:48 ` Vitaly Kuznetsov
2019-03-26 15:02 ` Liran Alon
2019-03-27 10:08 ` Vitaly Kuznetsov
[not found] ` <20190327192946.19128-1-sean.j.christopherson@intel.com>
[not found] ` <87va03ml7n.fsf@vitty.brq.redhat.com>
[not found] ` <CALMp9eQPwFy5GvjDbE9wQQYEDdYfHzEwm6n1XZgQ_hCuk9vp+Q@mail.gmail.com>
2019-04-09 8:21 ` Vitaly Kuznetsov
2019-04-09 16:31 ` Paolo Bonzini
2019-04-10 9:38 ` [RFC] selftests: kvm: add a selftest for SMM Vitaly Kuznetsov
2019-04-10 10:10 ` Paolo Bonzini
2019-04-10 10:32 ` Vitaly Kuznetsov [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87mukygnfy.fsf@vitty.brq.redhat.com \
--to=vkuznets@redhat.com \
--cc=jmattson@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=liran.alon@oracle.com \
--cc=pbonzini@redhat.com \
--cc=rkrcmar@redhat.com \
--cc=sean.j.christopherson@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.