From 3e40b51a62b08dc27dcaa7fbec630e047713aba1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 26 Mar 2018 17:14:19 -0700 Subject: plugin: allow retrieving and setting VM and VCPU states This change allows plugin to retrieve and set various VM and VCPU states: interrupt controller, PIT, LAPIC and MP state. BUG=b:76083711 TEST=cargo test -p kvm Change-Id: Ie32a67b0cd4a1f0a19ccd826a6e1c9dc25670f95 Signed-off-by: Dmitry Torokhov Reviewed-on: https://chromium-review.googlesource.com/986511 Reviewed-by: Zach Reizner --- tests/plugin_vm_state.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/plugins.rs | 91 ++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 tests/plugin_vm_state.c (limited to 'tests') diff --git a/tests/plugin_vm_state.c b/tests/plugin_vm_state.c new file mode 100644 index 0000000..6026e29 --- /dev/null +++ b/tests/plugin_vm_state.c @@ -0,0 +1,119 @@ +/* + * Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include +#include + +#include "crosvm.h" + +int main(int argc, char** argv) { + struct crosvm *crosvm; + int ret = crosvm_connect(&crosvm); + if (ret) { + fprintf(stderr, "failed to connect to crosvm: %d\n", ret); + return 1; + } + + struct kvm_pic_state pic_state; + ret = crosvm_get_pic_state(crosvm, false, &pic_state); + if (ret < 0) { + fprintf(stderr, "failed to get initial PIC1 state: %d\n", ret); + return 1; + } + + if (pic_state.auto_eoi) { + fprintf(stderr, "unexpected value of auto_eoi flag\n"); + return 1; + } + + pic_state.auto_eoi = true; + ret = crosvm_set_pic_state(crosvm, false, &pic_state); + if (ret < 0) { + fprintf(stderr, "failed to update PIC1 state: %d\n", ret); + return 1; + } + + ret = crosvm_get_pic_state(crosvm, false, &pic_state); + if (ret < 0) { + fprintf(stderr, "failed to get updated PIC1 state: %d\n", ret); + return 1; + } + + if (!pic_state.auto_eoi) { + fprintf(stderr, "unexpected value of auto_eoi flag after update\n"); + return 1; + } + + // Test retrieving and setting IOAPIC state. + struct kvm_ioapic_state ioapic_state; + ret = crosvm_get_ioapic_state(crosvm, &ioapic_state); + if (ret < 0) { + fprintf(stderr, "failed to get initial PIC1 state: %d\n", ret); + return 1; + } + + fprintf(stderr, "IOAPIC ID: %d\n", ioapic_state.id); + + if (ioapic_state.id != 0) { + fprintf(stderr, "unexpected value of IOAPIC ID: %d\n", ioapic_state.id); + return 1; + } + + ioapic_state.id = 1; + ret = crosvm_set_ioapic_state(crosvm, &ioapic_state); + if (ret < 0) { + fprintf(stderr, "failed to update PIC1 state: %d\n", ret); + return 1; + } + + ret = crosvm_get_ioapic_state(crosvm, &ioapic_state); + if (ret < 0) { + fprintf(stderr, "failed to get updated PIC1 state: %d\n", ret); + return 1; + } + + if (ioapic_state.id != 1) { + fprintf(stderr, "unexpected value of IOAPIC ID after update: %d\n", + ioapic_state.id); + return 1; + } + + // Test retrieving and setting PIT state. + struct kvm_pit_state2 pit_state; + ret = crosvm_get_pit_state(crosvm, &pit_state); + if (ret < 0) { + fprintf(stderr, "failed to get initial PIT state: %d\n", ret); + return 1; + } + + if (pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY) { + fprintf(stderr, "unexpected value of KVM_PIT_FLAGS_HPET_LEGACY flag\n"); + return 1; + } + + pit_state.flags |= KVM_PIT_FLAGS_HPET_LEGACY; + ret = crosvm_set_pit_state(crosvm, &pit_state); + if (ret < 0) { + fprintf(stderr, "failed to update PIT state: %d\n", ret); + return 1; + } + + ret = crosvm_get_pit_state(crosvm, &pit_state); + if (ret < 0) { + fprintf(stderr, "failed to get updated PIT state: %d\n", ret); + return 1; + } + + if (!(pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY)) { + fprintf(stderr, + "unexpected value of KVM_PIT_FLAGS_HPET_LEGACY after update\n"); + return 1; + } + + return 0; +} diff --git a/tests/plugins.rs b/tests/plugins.rs index e858c9a..cb956bb 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -241,6 +241,11 @@ fn test_supported_cpuid() { test_plugin(include_str!("plugin_supported_cpuid.c")); } +#[test] +fn test_vm_state_manipulation() { + test_plugin(include_str!("plugin_vm_state.c")); +} + #[test] fn test_vcpu_pause() { test_plugin(include_str!("plugin_vcpu_pause.c")); @@ -525,3 +530,89 @@ fn test_cpuid() { }; test_mini_plugin(&mini_plugin); } + +#[test] +fn test_vcpu_state_manipulation() { + let mini_plugin = MiniPlugin { + assembly_src: "org 0x1000 + bits 16 + mov byte [0x3000], 1", + src: r#" + #define KILL_ADDRESS 0x3000 + + int g_kill_evt; + bool success = false; + + int setup_vm(struct crosvm *crosvm, void *mem) { + g_kill_evt = crosvm_get_shutdown_eventfd(crosvm); + crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1); + return 0; + } + + int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs, + struct kvm_sregs *sregs) + { + int ret; + + struct kvm_lapic_state lapic; + ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic); + if (ret < 0) { + fprintf(stderr, "failed to get initial LAPIC state: %d\n", ret); + return 1; + } + + ret = crosvm_vcpu_set_lapic_state(vcpu, &lapic); + if (ret < 0) { + fprintf(stderr, "failed to update LAPIC state: %d\n", ret); + return 1; + } + + ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic); + if (ret < 0) { + fprintf(stderr, "failed to get updated LAPIC state: %d\n", ret); + return 1; + } + + struct kvm_mp_state mp_state; + ret = crosvm_vcpu_get_mp_state(vcpu, &mp_state); + if (ret < 0) { + fprintf(stderr, "failed to get initial MP state: %d\n", ret); + return 1; + } + + ret = crosvm_vcpu_set_mp_state(vcpu, &mp_state); + if (ret < 0) { + fprintf(stderr, "failed to update MP state: %d\n", ret); + return 1; + } + + success = true; + return 0; + } + + int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) { + if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS && + evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO && + evt.io_access.address == KILL_ADDRESS && + evt.io_access.is_write && + evt.io_access.length == 1 && + evt.io_access.data[0] == 1) + { + uint64_t dummy = 1; + write(g_kill_evt, &dummy, sizeof(dummy)); + return 1; + } + return 0; + } + + int check_result(struct crosvm *vcpu, void *mem) { + if (!success) { + fprintf(stderr, "test failed\n"); + return 1; + } + return 0; + }"#, + ..Default::default() + }; + test_mini_plugin(&mini_plugin); +} -- cgit 1.4.1