为什么K3 buildroot v1.0.2默认打开了VTVM

就目前的代码来看,设置hstatus.vtvm=1似乎并没有实际的好处,也没有带来功能上的提升

并且通过模块在每次kvm_riscv_reset_vcpu后覆盖context vtvm为0后qemu kvm仍然可以正常运行

另附内核模块

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/init.h>
#include <linux/kprobes.h>
#include <linux/kvm_host.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stddef.h>
#include <asm/csr.h>
#include <asm/ptrace.h>

static bool patch_enabled = true;
static bool log_each_hit = true;

module_param(patch_enabled, bool, 0644);
MODULE_PARM_DESC(patch_enabled, "Clear HSTATUS.VTVM after kvm_riscv_reset_vcpu returns.");

module_param(log_each_hit, bool, 0644);
MODULE_PARM_DESC(log_each_hit, "Log every kvm_riscv_reset_vcpu hit.");

struct reset_probe_data {
	struct kvm_vcpu *vcpu;
	unsigned long entry_hstatus;
	unsigned long vcpu_id;
};

static int reset_entry_handler(struct kretprobe_instance *ri,
			       struct pt_regs *regs)
{
	struct reset_probe_data *data = (struct reset_probe_data *)ri->data;
	struct kvm_vcpu *vcpu = (struct kvm_vcpu *)regs->a0;

	data->vcpu = vcpu;
	data->entry_hstatus = 0;
	data->vcpu_id = ~0UL;

	if (vcpu) {
		data->entry_hstatus =
			READ_ONCE(vcpu->arch.guest_context.hstatus);
		data->vcpu_id = READ_ONCE(vcpu->vcpu_id);
	}

	return 0;
}

static int reset_ret_handler(struct kretprobe_instance *ri,
			     struct pt_regs *regs)
{
	struct reset_probe_data *data = (struct reset_probe_data *)ri->data;
	struct kvm_vcpu *vcpu = data->vcpu;
	unsigned long before;
	unsigned long after;
	bool changed;

	if (!vcpu)
		return 0;

	before = READ_ONCE(vcpu->arch.guest_context.hstatus);
	after = before;
	if (patch_enabled)
		after &= ~HSTATUS_VTVM;

	changed = before != after;
	if (changed)
		WRITE_ONCE(vcpu->arch.guest_context.hstatus, after);

	if (log_each_hit)
		pr_info("vcpu%lu reset entry_hstatus=0x%lx return_hstatus=0x%lx final_hstatus=0x%lx vtvm:%lu->%lu changed=%d enabled=%d\n",
			data->vcpu_id, data->entry_hstatus, before, after,
			(unsigned long)!!(before & HSTATUS_VTVM),
			(unsigned long)!!(after & HSTATUS_VTVM),
			changed, patch_enabled);

	return 0;
}

static struct kretprobe reset_probe = {
	.kp.symbol_name = "kvm_riscv_reset_vcpu",
	.entry_handler = reset_entry_handler,
	.handler = reset_ret_handler,
	.data_size = sizeof(struct reset_probe_data),
	.maxactive = 64,
};

static int __init vtvm_init_patch_init(void)
{
	int ret;

	pr_info("HSTATUS_VTVM=0x%lx guest_context.hstatus offset=%zu patch_enabled=%d\n",
		(unsigned long)HSTATUS_VTVM,
		offsetof(struct kvm_vcpu, arch.guest_context.hstatus),
		patch_enabled);

	ret = register_kretprobe(&reset_probe);
	if (ret) {
		pr_err("register_kretprobe(%s) failed: %d\n",
		       reset_probe.kp.symbol_name, ret);
		return ret;
	}

	pr_info("registered kretprobe on %s\n", reset_probe.kp.symbol_name);
	return 0;
}

static void __exit vtvm_init_patch_exit(void)
{
	unregister_kretprobe(&reset_probe);
	pr_info("unregistered kretprobe on %s; missed=%d\n",
		reset_probe.kp.symbol_name, reset_probe.nmissed);
}

module_init(vtvm_init_patch_init);
module_exit(vtvm_init_patch_exit);

MODULE_DESCRIPTION("Clear KVM RISC-V guest_context.hstatus VTVM after vCPU reset");
MODULE_AUTHOR("Codex");
MODULE_LICENSE("GPL");