就目前的代码来看,设置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");