From 6bc2674f40b53257ccaa7ddca20a245c5544d5a0 Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 1 Mar 2024 13:52:13 +0800 Subject: [PATCH 01/17] update mk --- eBPF_Supermarket/kvm_watcher/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBPF_Supermarket/kvm_watcher/Makefile b/eBPF_Supermarket/kvm_watcher/Makefile index b330f8503..24e5bc390 100644 --- a/eBPF_Supermarket/kvm_watcher/Makefile +++ b/eBPF_Supermarket/kvm_watcher/Makefile @@ -7,7 +7,7 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') APP = src/kvm_watcher -OPTIONS = -f -w -n -d -c '-e -s' +OPTIONS = -f -w -n -d -c -h -e # 共同规则1 define common_rules1 From 244a659d76fece658f0bded463235c403c2ace7a Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 1 Mar 2024 13:55:25 +0800 Subject: [PATCH 02/17] update mk --- eBPF_Supermarket/kvm_watcher/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eBPF_Supermarket/kvm_watcher/Makefile b/eBPF_Supermarket/kvm_watcher/Makefile index 24e5bc390..fd2b1da4b 100644 --- a/eBPF_Supermarket/kvm_watcher/Makefile +++ b/eBPF_Supermarket/kvm_watcher/Makefile @@ -44,11 +44,11 @@ ifeq ($(MAKECMDGOALS),test) ifeq ($(shell grep -Eoc '(vmx|svm)' /proc/cpuinfo),0) $(error "The CPU in your device does not support virtualization!") endif + $(common_rules1) + $(common_rules2) ifeq ($(wildcard ./cirros-0.5.2-x86_64-disk.img),) wget https://gitee.com/nan-shuaibo/cirros/releases/download/0.5.2/cirros-0.5.2-x86_64-disk.img endif - $(common_rules1) - $(common_rules2) # 安装 qemu $(INSTALL_QEMU) # 启动虚拟机 From 3ca0f8d042e147233db47bf1a6f353ab6ed61b49 Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 1 Mar 2024 14:01:38 +0800 Subject: [PATCH 03/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/kvm_watcher.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/kvm_watcher.yml b/.github/workflows/kvm_watcher.yml index 49b22a6c0..12a57d6cf 100644 --- a/.github/workflows/kvm_watcher.yml +++ b/.github/workflows/kvm_watcher.yml @@ -22,6 +22,7 @@ jobs: - name: Test program execution run: | + sudo modprobe kvm && sudo modprobe kvm-intel cd eBPF_Supermarket/kvm_watcher/ make test From ed2f1775b576f02e65279f56bd88c5a8e0fb55ff Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 1 Mar 2024 14:09:06 +0800 Subject: [PATCH 04/17] update yml --- .github/workflows/kvm_watcher.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/kvm_watcher.yml b/.github/workflows/kvm_watcher.yml index 12a57d6cf..0a3f7525a 100644 --- a/.github/workflows/kvm_watcher.yml +++ b/.github/workflows/kvm_watcher.yml @@ -22,7 +22,6 @@ jobs: - name: Test program execution run: | - sudo modprobe kvm && sudo modprobe kvm-intel cd eBPF_Supermarket/kvm_watcher/ - make test + make From 2b84b8675d464cecf4f6085fbf9d390e921437f2 Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 1 Mar 2024 14:11:28 +0800 Subject: [PATCH 05/17] update mk --- eBPF_Supermarket/kvm_watcher/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eBPF_Supermarket/kvm_watcher/Makefile b/eBPF_Supermarket/kvm_watcher/Makefile index fd2b1da4b..66e6f6737 100644 --- a/eBPF_Supermarket/kvm_watcher/Makefile +++ b/eBPF_Supermarket/kvm_watcher/Makefile @@ -13,8 +13,6 @@ OPTIONS = -f -w -n -d -c -h -e define common_rules1 # 安装依赖 sudo apt install clang libelf1 libelf-dev zlib1g-dev libbpf-dev linux-tools-$$(uname -r) linux-cloud-tools-$$(uname -r) - # 加载KVM模块 - sudo modprobe kvm && sudo modprobe kvm-intel # 生成vmlinux.h文件 bpftool btf dump file /sys/kernel/btf/kvm format c > ./include/vmlinux.h endef @@ -44,6 +42,8 @@ ifeq ($(MAKECMDGOALS),test) ifeq ($(shell grep -Eoc '(vmx|svm)' /proc/cpuinfo),0) $(error "The CPU in your device does not support virtualization!") endif + # 加载KVM模块 + sudo modprobe kvm && sudo modprobe kvm-intel $(common_rules1) $(common_rules2) ifeq ($(wildcard ./cirros-0.5.2-x86_64-disk.img),) From 3bfa9a481b7e77d3c75f13d15b3aa623a91a907e Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 1 Mar 2024 16:32:03 +0800 Subject: [PATCH 06/17] =?UTF-8?q?=E6=B7=BB=E5=8A=A0hypercall=EF=BC=88?= =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=B0=83=E7=94=A8=EF=BC=89=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kvm_watcher/include/kvm_hypercall.h | 98 ++++++ .../kvm_watcher/include/kvm_watcher.h | 39 ++- .../kvm_watcher/src/kvm_watcher.bpf.c | 8 +- .../kvm_watcher/src/kvm_watcher.c | 329 +++++++++++++----- 4 files changed, 382 insertions(+), 92 deletions(-) create mode 100644 eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h b/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h new file mode 100644 index 000000000..dba8bf0d8 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h @@ -0,0 +1,98 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// author: nanshuaibo811@163.com +// +// Kernel space BPF program used for monitoring data for KVM HYPERCALL. + +#ifndef __KVM_HYPERCALL_H +#define __KVM_HYPERCALL_H + +#include "kvm_watcher.h" +#include "vmlinux.h" +#include +#include +#include + +// 定义宏从寄存器读取超级调用信息 +// 代码来源:arch/x86/kvm/kvm_cache_regs.h +#define BUILD_KVM_GPR_ACCESSORS(lname, uname) \ + static __always_inline unsigned long kvm_##lname##_read( \ + struct kvm_vcpu *vcpu) { \ + return vcpu->arch.regs[VCPU_REGS_##uname]; \ + } + +BUILD_KVM_GPR_ACCESSORS(rax, RAX) +BUILD_KVM_GPR_ACCESSORS(rbx, RBX) +BUILD_KVM_GPR_ACCESSORS(rcx, RCX) +BUILD_KVM_GPR_ACCESSORS(rdx, RDX) +BUILD_KVM_GPR_ACCESSORS(rsi, RSI) + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct hc_key); + __type(value, struct hc_value); +} hc_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct hc_key); + __type(value, u32); +} hc_count SEC(".maps"); + +static int entry_emulate_hypercall(struct kvm_vcpu *vcpu, void *rb, + struct common_event *e, pid_t vm_pid) { + CHECK_PID(vm_pid); + u64 nr, a0, a1, a2, a3; + nr = kvm_rax_read(vcpu); // 超级调用号 + // 超级调用参数 + a0 = kvm_rbx_read(vcpu); + a1 = kvm_rcx_read(vcpu); + a2 = kvm_rdx_read(vcpu); + a3 = kvm_rsi_read(vcpu); + RESERVE_RINGBUF_ENTRY(rb, e); + e->process.pid = pid; + e->process.tid = (u32)bpf_get_current_pid_tgid(); + e->time = bpf_ktime_get_ns(); + bpf_get_current_comm(&e->process.comm, sizeof(e->process.comm)); + e->hypercall_data.a0 = a0; + e->hypercall_data.a1 = a1; + e->hypercall_data.a2 = a2; + e->hypercall_data.a3 = a3; + e->hypercall_data.vcpu_id = vcpu->vcpu_id; + e->hypercall_data.hc_nr = nr; + e->hypercall_data.hypercalls = vcpu->stat.hypercalls; + bpf_ringbuf_submit(e, 0); + struct hc_key hc_key = {.pid = pid, .nr = nr, .vcpu_id = vcpu->vcpu_id}; + struct hc_value hc_value = {.a0 = a0, + .a1 = a1, + .a2 = a2, + .a3 = a3, + .counts = 1, + .hypercalls = vcpu->stat.hypercalls}; + u32 *count; + count = bpf_map_lookup_elem(&hc_count, &hc_key); + if (count) { + __sync_fetch_and_add(count, 1); + hc_value.counts = *count; + } else { + bpf_map_update_elem(&hc_count, &hc_key, &hc_value.counts, BPF_NOEXIST); + } + bpf_map_update_elem(&hc_map, &hc_key, &hc_value, BPF_ANY); + return 0; +} + +#endif /* __KVM_HYPERCALL_H */ \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h b/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h index 55d55343b..f2ac6b8b9 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h @@ -29,11 +29,11 @@ #define NS_TO_MS_WITH_DECIMAL(ns) ((double)(ns) / NS_TO_MS_FACTOR) #define MICROSECONDS_IN_SECOND 1000000 -#define OUTPUT_INTERVAL_SECONDS 0.5 +#define OUTPUT_INTERVAL_SECONDS 2 #define OUTPUT_INTERVAL(us) usleep((__u32)(us * MICROSECONDS_IN_SECOND)) -#define OPTIONS_LIST "-w, -p, -d, -f, -c, -i, or -e" +#define OPTIONS_LIST "-w, -p, -d, -f, -c, -i, ,-h or -e" #define PFERR_PRESENT_BIT 0 #define PFERR_WRITE_BIT 1 @@ -70,7 +70,7 @@ } while (0) // 定义清屏宏 -#define CLEAR_SCREEN() printf("\033[2J\033[H") +#define CLEAR_SCREEN() printf("\033[2J\033[H\n") #define RING_BUFFER_TIMEOUT_MS 100 @@ -88,11 +88,6 @@ return 0; \ } -struct ExitReason { - __u32 number; - const char *name; -}; - struct reason_info { __u64 time; __u64 reason; @@ -107,6 +102,22 @@ struct dirty_page_info { __u32 pid; }; +struct hc_value { + __u64 a0; + __u64 a1; + __u64 a2; + __u64 a3; + __u64 hypercalls; // vcpu上hypercall发生的次数 + __u32 counts; // 特定hypercall发生的次数 + __u32 pad; +}; + +struct hc_key { + __u64 nr; + pid_t pid; + __u32 vcpu_id; +}; + struct process { __u32 pid; __u32 tid; @@ -122,6 +133,7 @@ enum EventType { PAGE_FAULT, IRQCHIP, IRQ_INJECT, + HYPERCALL, } event_type; struct common_event { @@ -200,6 +212,17 @@ struct common_event { __u64 injections; // IRQ_INJECT 特有成员 } irq_inject_data; + + struct { + __u64 hc_nr; + __u64 a0; + __u64 a1; + __u64 a2; + __u64 a3; + __u64 hypercalls; + __u32 vcpu_id; + // HYPERCALL 特有成员 + } hypercall_data; }; }; diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c index 0564e4704..4118983fc 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c @@ -24,6 +24,7 @@ #include "../include/kvm_vcpu.h" #include "../include/kvm_mmu.h" #include "../include/kvm_irq.h" +#include "../include/kvm_hypercall.h" #include "../include/kvm_watcher.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; @@ -138,4 +139,9 @@ int BPF_PROG(fentry_vmx_inject_irq, struct kvm_vcpu *vcpu, bool reinjected) { SEC("fexit/vmx_inject_irq") int BPF_PROG(fexit_vmx_inject_irq, struct kvm_vcpu *vcpu, bool reinjected) { return exit_vmx_inject_irq(vcpu, &rb, e); -} \ No newline at end of file +} + +SEC("fentry/kvm_emulate_hypercall") +int BPF_PROG(fentry_emulate_hypercall, struct kvm_vcpu *vcpu) { + return entry_emulate_hypercall(vcpu, &rb, e, vm_pid); +} diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c index 127b5c69c..300540f64 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c @@ -30,71 +30,119 @@ #include "../include/kvm_watcher.h" #include "kvm_watcher.skel.h" -// 定义具体的退出原因 arch/x86/include/uapi/asm/vmx.h -struct ExitReason exitReasons[] = {{0, "EXCEPTION_NMI"}, - {1, "EXTERNAL_INTERRUPT"}, - {2, "TRIPLE_FAULT"}, - {3, "INIT_SIGNAL"}, - {4, "SIPI_SIGNAL"}, - {7, "INTERRUPT_WINDOW"}, - {8, "NMI_WINDOW"}, - {9, "TASK_SWITCH"}, - {10, "CPUID"}, - {12, "HLT"}, - {13, "INVD"}, - {14, "INVLPG"}, - {15, "RDPMC"}, - {16, "RDTSC"}, - {18, "VMCALL"}, - {19, "VMCLEAR"}, - {20, "VMLAUNCH"}, - {21, "VMPTRLD"}, - {22, "VMPTRST"}, - {23, "VMREAD"}, - {24, "VMRESUME"}, - {25, "VMWRITE"}, - {26, "VMOFF"}, - {27, "VMON"}, - {28, "CR_ACCESS"}, - {29, "DR_ACCESS"}, - {30, "IO_INSTRUCTION"}, - {31, "MSR_READ"}, - {32, "MSR_WRITE"}, - {33, "INVALID_STATE"}, - {34, "MSR_LOAD_FAIL"}, - {36, "MWAIT_INSTRUCTION"}, - {37, "MONITOR_TRAP_FLAG"}, - {39, "MONITOR_INSTRUCTION"}, - {40, "PAUSE_INSTRUCTION"}, - {41, "MCE_DURING_VMENTRY"}, - {43, "TPR_BELOW_THRESHOLD"}, - {44, "APIC_ACCESS"}, - {45, "EOI_INDUCED"}, - {46, "GDTR_IDTR"}, - {47, "LDTR_TR"}, - {48, "EPT_VIOLATION"}, - {49, "EPT_MISCONFIG"}, - {50, "INVEPT"}, - {51, "RDTSCP"}, - {52, "PREEMPTION_TIMER"}, - {53, "INVVPID"}, - {54, "WBINVD"}, - {55, "XSETBV"}, - {56, "APIC_WRITE"}, - {57, "RDRAND"}, - {58, "INVPCID"}, - {59, "VMFUNC"}, - {60, "ENCLS"}, - {61, "RDSEED"}, - {62, "PML_FULL"}, - {63, "XSAVES"}, - {64, "XRSTORS"}, - {67, "UMWAIT"}, - {68, "TPAUSE"}, - {74, "BUS_LOCK"}, - {75, "NOTIFY"}}; +// 创建并打开临时文件 +FILE *create_temp_file(const char *filename) { + const char *directory = "./temp"; + char filepath[256]; + + // 构建文件的完整路径 + snprintf(filepath, sizeof(filepath), "%s/%s", directory, filename); + + // 创建目录,如果不存在 + if (mkdir(directory, 0777) == -1 && errno != EEXIST) { + perror("Failed to create directory"); + return NULL; + } + + // 尝试打开文件 + FILE *output = fopen(filepath, "w"); + if (!output) { + perror("Failed to open output file"); + return NULL; + } + + return output; +} + +const char *getHypercallName(int number) { + struct Hypercall { + int number; + const char *name; + }; + + // 定义超级调用 include\uapi\linux\kvm_para.h + struct Hypercall hypercalls[] = { + {1, "VAPIC_POLL_IRQ"}, {5, "KICK_CPU"}, {9, "CLOCK_PAIRING"}, + {10, "SEND_IPI"}, {11, "SCHED_YIELD"}, {12, "MAP_GPA_RANGE"}}; + + for (int i = 0; i < sizeof(hypercalls) / sizeof(hypercalls[0]); i++) { + if (hypercalls[i].number == number) { + return hypercalls[i].name; + } + } + return "Unknown"; // 如果找不到对应的超级调用号,返回一个默认值 +} const char *getExitReasonName(int number) { + struct ExitReason { + int number; + const char *name; + }; + + // 定义具体的退出原因 arch/x86/include/uapi/asm/vmx.h + struct ExitReason exitReasons[] = {{0, "EXCEPTION_NMI"}, + {1, "EXTERNAL_INTERRUPT"}, + {2, "TRIPLE_FAULT"}, + {3, "INIT_SIGNAL"}, + {4, "SIPI_SIGNAL"}, + {7, "INTERRUPT_WINDOW"}, + {8, "NMI_WINDOW"}, + {9, "TASK_SWITCH"}, + {10, "CPUID"}, + {12, "HLT"}, + {13, "INVD"}, + {14, "INVLPG"}, + {15, "RDPMC"}, + {16, "RDTSC"}, + {18, "VMCALL"}, + {19, "VMCLEAR"}, + {20, "VMLAUNCH"}, + {21, "VMPTRLD"}, + {22, "VMPTRST"}, + {23, "VMREAD"}, + {24, "VMRESUME"}, + {25, "VMWRITE"}, + {26, "VMOFF"}, + {27, "VMON"}, + {28, "CR_ACCESS"}, + {29, "DR_ACCESS"}, + {30, "IO_INSTRUCTION"}, + {31, "MSR_READ"}, + {32, "MSR_WRITE"}, + {33, "INVALID_STATE"}, + {34, "MSR_LOAD_FAIL"}, + {36, "MWAIT_INSTRUCTION"}, + {37, "MONITOR_TRAP_FLAG"}, + {39, "MONITOR_INSTRUCTION"}, + {40, "PAUSE_INSTRUCTION"}, + {41, "MCE_DURING_VMENTRY"}, + {43, "TPR_BELOW_THRESHOLD"}, + {44, "APIC_ACCESS"}, + {45, "EOI_INDUCED"}, + {46, "GDTR_IDTR"}, + {47, "LDTR_TR"}, + {48, "EPT_VIOLATION"}, + {49, "EPT_MISCONFIG"}, + {50, "INVEPT"}, + {51, "RDTSCP"}, + {52, "PREEMPTION_TIMER"}, + {53, "INVVPID"}, + {54, "WBINVD"}, + {55, "XSETBV"}, + {56, "APIC_WRITE"}, + {57, "RDRAND"}, + {58, "INVPCID"}, + {59, "VMFUNC"}, + {60, "ENCLS"}, + {61, "RDSEED"}, + {62, "PML_FULL"}, + {63, "XSAVES"}, + {64, "XRSTORS"}, + {67, "UMWAIT"}, + {68, "TPAUSE"}, + {74, "BUS_LOCK"}, + {75, "NOTIFY"}}; + for (int i = 0; i < sizeof(exitReasons) / sizeof(exitReasons[0]); i++) { if (exitReasons[i].number == number) { return exitReasons[i].name; @@ -228,25 +276,12 @@ int compare(const void *a, const void *b) { // 保存脏页信息到文件 int save_count_dirtypagemap_to_file(struct bpf_map *map) { - const char *directory = "./temp"; - const char *filename = "./temp/dirty_temp"; - - // 创建目录,如果不存在 - if (mkdir(directory, 0777) == -1) { - // 如果目录已经存在,这里的错误是预期的,可以忽略 - // 否则,打印错误信息并返回 - if (errno != EEXIST) { - perror("Failed to create directory"); - return -1; - } - } - - FILE *output = fopen(filename, "w"); + const char *filename = "dirty_temp"; + FILE *output = create_temp_file(filename); if (!output) { - perror("Failed to open output file"); - return -1; + fprintf(stderr, "Failed to create file in directory\n"); + return 1; } - int count_dirty_fd = bpf_map__fd(map); struct dirty_page_info lookup_key = {}; struct dirty_page_info next_key = {}; @@ -311,6 +346,7 @@ static struct env { bool mmio_page_fault; bool execute_irqchip; bool execute_irq_inject; + bool execute_hypercall; int monitoring_time; pid_t vm_pid; enum EventType event_type; @@ -324,6 +360,7 @@ static struct env { .execute_irqchip = false, .execute_irq_inject = false, .mmio_page_fault = false, + .execute_hypercall = false, .monitoring_time = 0, .vm_pid = -1, .event_type = NONE_TYPE, @@ -347,6 +384,7 @@ static const struct argp_option opts[] = { "Monitor the irqchip setting information in KVM VM."}, {"irq_inject(x86)", 'i', NULL, 0, "Monitor the virq injection information in KVM VM "}, + {"hypercall", 'h', NULL, 0, "Monitor the hypercall information in KVM VM "}, {"stat", 's', NULL, 0, "Display statistical data.(The -e option must be specified.)"}, {"mmio", 'm', NULL, 0, @@ -354,13 +392,13 @@ static const struct argp_option opts[] = { "specified.)"}, {"vm_pid", 'p', "PID", 0, "Specify the virtual machine pid to monitor."}, {"monitoring_time", 't', "SEC", 0, "Time for monitoring."}, - {NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help"}, + {NULL, 'H', NULL, OPTION_HIDDEN, "Show the full help"}, {}, }; // 解析命令行参数 static error_t parse_arg(int key, char *arg, struct argp_state *state) { switch (key) { - case 'h': + case 'H': argp_state_help(state, stderr, ARGP_HELP_STD_HELP); break; case 'w': @@ -387,6 +425,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'i': SET_OPTION_AND_CHECK_USAGE(option_selected, env.execute_irq_inject); break; + case 'h': + SET_OPTION_AND_CHECK_USAGE(option_selected, env.execute_hypercall); + break; case 's': if (env.execute_exit) { env.ShowStats = true; @@ -467,6 +508,8 @@ static int determineEventType(struct env *env) { env->event_type = IRQCHIP; } else if (env->execute_irq_inject) { env->event_type = IRQ_INJECT; + } else if (env->execute_hypercall) { + env->event_type = HYPERCALL; } else { env->event_type = NONE_TYPE; // 或者根据需要设置一个默认的事件类型 } @@ -641,6 +684,44 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { e->irq_inject_data.soft ? "Soft/INTn" : "IRQ"); break; } + case HYPERCALL: { + const char *filename = "./temp/hc_temp"; + FILE *output = fopen(filename, "a"); + if (!output) { + perror("Failed to open output file"); + return -1; + } + fprintf(output, "%-18.6f %-15s %-10d %-10d %-10s %-11llu", + timestamp_ms, e->process.comm, e->process.pid, + e->hypercall_data.vcpu_id, + getHypercallName(e->hypercall_data.hc_nr), + e->hypercall_data.hypercalls); + if (e->hypercall_data.hc_nr == 5) { + fprintf(output, "apic_id:%llu\n", e->hypercall_data.a1); + } else if (e->hypercall_data.hc_nr == 9) { + fprintf( + output, "GPA:%#llx CLOCK_TYPE:%s\n", e->hypercall_data.a0, + e->hypercall_data.a1 == 0 ? "KVM_CLOCK_PAIRING_WALLCLOCK" + : ""); + } else if (e->hypercall_data.hc_nr == 10) { + fprintf(output, + "ipi_bitmap_low:%#llx,ipi_bitmap_high:%#llx,min(apic_" + "id):%llu,icr:%#llx\n", + e->hypercall_data.a0, e->hypercall_data.a1, + e->hypercall_data.a2, e->hypercall_data.a3); + } else if (e->hypercall_data.hc_nr == 11) { + fprintf(output, "dest apic_id:%llu\n", e->hypercall_data.a0); + } else if (e->hypercall_data.hc_nr == 12) { + fprintf(output, + "GPA start:%#llx,PAGE_NR(4KB):%llu,Attributes:%#llx\n", + e->hypercall_data.a0, e->hypercall_data.a1, + e->hypercall_data.a2); + } else { + fprintf(output, "\n"); + } + fclose(output); + break; + } default: // 处理未知事件类型 break; @@ -687,6 +768,19 @@ static int print_event_head(struct env *env) { "TIME(ms)", "COMM", "PID", "DELAY", "IRQ_NR", "VCPU_ID", "INJECTIONS", "TYPE"); break; + case HYPERCALL: { + const char *filename = "hc_temp"; + FILE *output = create_temp_file(filename); + if (!output) { + fprintf(stderr, "Failed to create file in directory\n"); + return 1; + } + fprintf(output, "%-18s %-15s %-10s %-10s %-10s %-10s %-10s\n", + "TIME(ms)", "COMM", "PID", "VCPU_ID", "NAME", "HYPERCALLS", + "ARGS"); + fclose(output); + break; + } default: // Handle default case or display an error message break; @@ -732,6 +826,67 @@ static void set_disable_load(struct kvm_watcher_bpf *skel) { env.execute_irq_inject ? true : false); bpf_program__set_autoload(skel->progs.fexit_vmx_inject_irq, env.execute_irq_inject ? true : false); + bpf_program__set_autoload(skel->progs.fentry_emulate_hypercall, + env.execute_hypercall ? true : false); +} + +int print_hc_map(struct kvm_watcher_bpf *skel) { + int fd = bpf_map__fd(skel->maps.hc_map); + int count_fd = bpf_map__fd(skel->maps.hc_count); + int err; + struct hc_key lookup_key = {}; + struct hc_key next_key = {}; + struct hc_value hc_value = {}; + struct tm *tm; + char ts[32]; + time_t t; + time(&t); + tm = localtime(&t); + strftime(ts, sizeof(ts), "%H:%M:%S", tm); + int first_run = 1; + // Iterate over the map + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + if (first_run) { + first_run = 0; + printf( + "--------------------------------------------------------------" + "----------" + "\n"); + printf("TIME:%s\n", ts); + printf("%-12s %-12s %-12s %-12s %-12s\n", "PID", "VCPU_ID", "NAME", + "COUNTS", "HYPERCALLS"); + } + // Print the current entry + err = bpf_map_lookup_elem(fd, &next_key, &hc_value); + if (err < 0) { + fprintf(stderr, "failed to lookup hc_value: %d\n", err); + return -1; + } + printf("%-12d %-12d %-12s %-12d %-12lld\n", next_key.pid, + next_key.vcpu_id, getHypercallName(next_key.nr), hc_value.counts, + hc_value.hypercalls); + // // Move to the next key + lookup_key = next_key; + } + memset(&lookup_key, 0, sizeof(struct hc_key)); + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + err = bpf_map_delete_elem(fd, &next_key); + if (err < 0) { + fprintf(stderr, "failed to cleanup hc_map: %d\n", err); + return -1; + } + lookup_key = next_key; + } + memset(&lookup_key, 0, sizeof(struct hc_key)); + while (!bpf_map_get_next_key(count_fd, &lookup_key, &next_key)) { + err = bpf_map_delete_elem(count_fd, &next_key); + if (err < 0) { + fprintf(stderr, "failed to cleanup hc_count: %d\n", err); + return -1; + } + lookup_key = next_key; + } + return 0; } int main(int argc, char **argv) { @@ -808,6 +963,14 @@ int main(int argc, char **argv) { while (!exiting) { // OUTPUT_INTERVAL(OUTPUT_INTERVAL_SECONDS); // 输出间隔 err = ring_buffer__poll(rb, RING_BUFFER_TIMEOUT_MS /* timeout, ms */); + if (env.execute_hypercall) { + OUTPUT_INTERVAL(OUTPUT_INTERVAL_SECONDS); + print_hc_map(skel); + if (err < 0) { + printf("Error print map: %d\n", err); + break; + } + } /* Ctrl-C will cause -EINTR */ if (err == -EINTR) { err = 0; @@ -826,7 +989,7 @@ int main(int argc, char **argv) { if (err < 0) { printf("Save count dirty page map to file fail: %d\n", err); goto cleanup; - }else{ + } else { printf("\nSave count dirty page map to file success!\n"); goto cleanup; } From 4c7521716bb29eaffd01b497e1b35a84b92e0dcc Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 1 Mar 2024 16:32:26 +0800 Subject: [PATCH 07/17] =?UTF-8?q?=E6=B7=BB=E5=8A=A0hypercall=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=AE=9E=E7=8E=B0=E8=BF=87=E7=A8=8B=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kvm_watcher/docs/Hypercall.md | 369 ++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 eBPF_Supermarket/kvm_watcher/docs/Hypercall.md diff --git a/eBPF_Supermarket/kvm_watcher/docs/Hypercall.md b/eBPF_Supermarket/kvm_watcher/docs/Hypercall.md new file mode 100644 index 000000000..a855470d2 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/docs/Hypercall.md @@ -0,0 +1,369 @@ +> 在Linux中,大家应该对syscall非常的了解和熟悉,其是用户态进入内核态的一种途径或者说是一种方式,完成了两个模式之间的切换;而在虚拟环境中,有没有一种类似于syscall这种方式,能够从no root模式切换到root模式呢?答案是肯定的,KVM提供了Hypercall机制,x86体系架构也有相关的指令支持。 +> +> hypercall:当虚拟机的Guest OS需要执行一些更高权限的操作(如:页表的更新、对物理资源的访问等)时,由于自身在非特权域无法完成这些操作,于是便通过调用Hypercall交给Hypervisor来完成这些操作。 + +## Hypercall的发起 + +KVM代码中提供了五种形式的Hypercall接口: + +``` +file: arch/x86/include/asm/kvm_para.h, line: 34 +static inline long kvm_hypercall0(unsigned int nr); +static inline long kvm_hypercall1(unsigned int nr, unsigned long p1); +static inline long kvm_hypercall2(unsigned int nr, unsigned long p1, unsigned long p2); +static inline long kvm_hypercall3(unsigned int nr, unsigned long p1, unsigned long p2, unsigned long p3) +static inline long kvm_hypercall4(unsigned int nr, unsigned long p1, unsigned long p2, unsigned long p3, unsigned long p4) +``` + +这几个接口的区别在于参数个数的不用,本质是一样的。挑个参数最多的看下: + +``` +static inline long kvm_hypercall4(unsigned int nr, unsigned long p1, + unsigned long p2, unsigned long p3, + unsigned long p4) +{ + long ret; + asm volatile(KVM_HYPERCALL + : "=a"(ret) + : "a"(nr), "b"(p1), "c"(p2), "d"(p3), "S"(p4) + : "memory"); + return ret; +} +``` + +Hypercall内部实现是标准的内嵌汇编,稍作分析: + +### KVM_HYPERCALL + +``` +#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1" +``` + +对于KVM hypercall来说,KVM_HYPERCALL是一个三字节的指令序列,x86体系架构下即是vmcall指令,官方手册解释: + +``` +vmcall: + op code:0F 01 C1 -- VMCALL Call to VM + monitor +by causing VM exit +``` + +言简意赅,vmcall会导致VM exit到VMM。 + +### 返回值 + +: “=a”(ret),表示返回值放在eax寄存器中输出。 + +### 输入 + +: “a”(nr), “b”(p1), “c”(p2), “d”(p3), “S”(p4),表示输入参数放在对应的eax,ebx,ecx,edx,esi中,而nr其实就是可以认为是系统调用号。 + +## hypercall的处理 + +当Guest发起一次hypercall后,VMM会接管到该call导致的VM Exit。 + +``` +static int (*const kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { + ...... + [EXIT_REASON_VMCALL] = kvm_emulate_hypercall, + ...... +} +``` + +进入kvm_emulate_hypercall()处理,过程非常简单: + +``` +int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) +{ + unsigned long nr, a0, a1, a2, a3, ret; + int op_64_bit; + + // 检查是否启用了Xen超级调用,如果是,则调用Xen超级调用处理函数 + if (kvm_xen_hypercall_enabled(vcpu->kvm)) + return kvm_xen_hypercall(vcpu); + + // 检查是否启用了Hypervisor超级调用,如果是,则调用Hypervisor超级调用处理函数 + if (kvm_hv_hypercall_enabled(vcpu)) + return kvm_hv_hypercall(vcpu); + + // 从寄存器中读取超级调用号及参数 + nr = kvm_rax_read(vcpu); + a0 = kvm_rbx_read(vcpu); + a1 = kvm_rcx_read(vcpu); + a2 = kvm_rdx_read(vcpu); + a3 = kvm_rsi_read(vcpu); + + // 记录超级调用的追踪信息 + trace_kvm_hypercall(nr, a0, a1, a2, a3); + + // 检查是否为64位超级调用 + op_64_bit = is_64_bit_hypercall(vcpu); + if (!op_64_bit) { + nr &= 0xFFFFFFFF; + a0 &= 0xFFFFFFFF; + a1 &= 0xFFFFFFFF; + a2 &= 0xFFFFFFFF; + a3 &= 0xFFFFFFFF; + } + + // 检查当前CPU的特权级是否为0 + if (static_call(kvm_x86_get_cpl)(vcpu) != 0) { + ret = -KVM_EPERM; + goto out; + } + + ret = -KVM_ENOSYS; + + // 根据超级调用号执行相应的操作 + switch (nr) { + case KVM_HC_VAPIC_POLL_IRQ: + ret = 0; + break; + case KVM_HC_KICK_CPU: + // 处理CPU唤醒的超级调用 + if (!guest_pv_has(vcpu, KVM_FEATURE_PV_UNHALT)) + break; + + kvm_pv_kick_cpu_op(vcpu->kvm, a1); + kvm_sched_yield(vcpu, a1); + ret = 0; + break; +#ifdef CONFIG_X86_64 + case KVM_HC_CLOCK_PAIRING: + // 处理时钟配对的超级调用 + ret = kvm_pv_clock_pairing(vcpu, a0, a1); + break; +#endif + case KVM_HC_SEND_IPI: + // 处理发送中断请求的超级调用 + if (!guest_pv_has(vcpu, KVM_FEATURE_PV_SEND_IPI)) + break; + + ret = kvm_pv_send_ipi(vcpu->kvm, a0, a1, a2, a3, op_64_bit); + break; + case KVM_HC_SCHED_YIELD: + // 处理调度让出的超级调用 + if (!guest_pv_has(vcpu, KVM_FEATURE_PV_SCHED_YIELD)) + break; + + kvm_sched_yield(vcpu, a0); + ret = 0; + break; + case KVM_HC_MAP_GPA_RANGE: + // 处理GPA范围映射的超级调用 + ret = -KVM_ENOSYS; + if (!(vcpu->kvm->arch.hypercall_exit_enabled & (1 << KVM_HC_MAP_GPA_RANGE))) + break; + + // 设置KVM_EXIT_HYPERCALL退出类型,并填充相关信息 + vcpu->run->exit_reason = KVM_EXIT_HYPERCALL; + vcpu->run->hypercall.nr = KVM_HC_MAP_GPA_RANGE; + vcpu->run->hypercall.args[0] = a0; + vcpu->run->hypercall.args[1] = a1; + vcpu->run->hypercall.args[2] = a2; + vcpu->run->hypercall.longmode = op_64_bit; + vcpu->arch.complete_userspace_io = complete_hypercall_exit; + return 0; + default: + ret = -KVM_ENOSYS; + break; + } + +out: + // 如果不是64位超级调用,则返回值需要截断为32位 + if (!op_64_bit) + ret = (u32)ret; + kvm_rax_write(vcpu, ret); + + // 更新超级调用统计信息,并跳过被模拟的指令 + ++vcpu->stat.hypercalls; + return kvm_skip_emulated_instruction(vcpu); +} +``` + +### Conclusion + +整个过程非常简洁和简单,hypercall机制给了Guest能够主动进入VMM的一种方式。 + +## 调用号 + +``` +#define KVM_HC_VAPIC_POLL_IRQ 1 +#define KVM_HC_MMU_OP 2 +#define KVM_HC_FEATURES 3 +#define KVM_HC_PPC_MAP_MAGIC_PAGE 4 +#define KVM_HC_KICK_CPU 5 +#define KVM_HC_MIPS_GET_CLOCK_FREQ 6 +#define KVM_HC_MIPS_EXIT_VM 7 +#define KVM_HC_MIPS_CONSOLE_OUTPUT 8 +#define KVM_HC_CLOCK_PAIRING 9 +#define KVM_HC_SEND_IPI 10 +#define KVM_HC_SCHED_YIELD 11 +#define KVM_HC_MAP_GPA_RANGE 12 +``` + + +1. ##### KVM_HC_VAPIC_POLL_IRQ + +------------------------ + +Architecture: x86 +Status: active +Purpose: 触发客户机退出,以便在重新进入时主机可以检查待处理的中断。 + +2. ##### KVM_HC_MMU_OP + +---------------- + +Architecture: x86 +Status: deprecated. +Purpose: 支持内存管理单元(MMU)操作,例如写入页表项(PTE)、刷新转换后备缓冲(TLB)以及释放页表(PT)。 + +3. ##### KVM_HC_FEATURES + +------------------ + +Architecture: PPC +Status: active +Purpose: 向客户机公开超级调用的可用性。在 x86 平台上,使用 cpuid 来列举可用的超级调用。在 PPC(PowerPC)上,可以使用基于设备树的查找(也是 EPAPR 规定的方式)或 KVM 特定的列举机制(即这个超级调用)。 + +4. ##### KVM_HC_PPC_MAP_MAGIC_PAGE + +---------------------------- + +Architecture: PPC +Status: active +Purpose:为了实现超级监视器与客户机之间的通信,存在一个共享页面,其中包含了监视器可见寄存器状态的部分。客户机可以通过使用此超级调用将这个共享页面映射,以通过内存访问其监视器寄存器。 + +5. ##### KVM_HC_KICK_CPU + +------------------ + +Architecture: x86 +Status: active +Purpose: 用于唤醒处于 HLT(Halt)状态的vCPU 。 +Usage example: +一个使用了半虚拟化的客户机的虚拟 CPU,在内核模式下忙等待某个事件的发生(例如,自旋锁变为可用)时,如果其忙等待时间超过了一个阈值时间间隔,就可以执行 HLT 指令。执行 HLT 指令将导致 hypervisor 将虚拟 CPU 置于休眠状态,直到发生适当的事件。同一客户机的另一个虚拟 CPU 可以通过发出 KVM_HC_KICK_CPU 超级调用来唤醒正在睡眠的虚拟 CPU,指定要唤醒的虚拟 CPU 的 APIC ID(a1)。另外一个参数(a0)在这个超级调用中用于将来的用途。 + + +6. ##### KVM_HC_CLOCK_PAIRING + +----------------------- + +Architecture: x86 +Status: active +Purpose: 用于同步主机和客户机时钟。 + +Usage: +a0:客户机物理地址,用于存储主机复制的 "struct kvm_clock_offset" 结构。 + +a1:时钟类型,目前只支持 KVM_CLOCK_PAIRING_WALLCLOCK(0)(对应主机的 CLOCK_REALTIME 时钟)。 + +```c +struct kvm_clock_pairing { + __s64 sec; // 从 clock_type 时钟起的秒数。 + __s64 nsec; // 从 clock_type 时钟起的纳秒数。 + __u64 tsc; // 用于计算 sec/nsec 对的客户机 TSC(时间戳计数)值。 + __u32 flags; // 标志,目前未使用(为 0)。 + __u32 pad[9]; // 填充字段,目前未使用。 +}; +``` + +这个超级调用允许客户机在主机和客户机之间计算精确的时间戳。客户机可以使用返回的 TSC(时间戳计数)值来计算其时钟的 CLOCK_REALTIME,即在同一时刻。 + +如果主机不使用 TSC 时钟源,或者时钟类型不同于 KVM_CLOCK_PAIRING_WALLCLOCK,则返回 KVM_EOPNOTSUPP。 + +7. ##### KVM_HC_SEND_IPI + +------------------ + +Architecture: x86 +Status: active +Purpose: 向多个vcpu发生ipi。 + +- `a0`: 目标 APIC ID 位图的低位部分。 +- `a1`: 目标 APIC ID 位图的高位部分。 +- `a2`: 位图中最低的 。 +- `a3`: 中断命令寄存器。 + +这个超级调用允许客户机发送组播中断处理请求(IPIs),每次调用最多可以有 128 个目标(在 64 位模式下)或者 64 个虚拟中央处理单元(vCPU)(在 32 位模式下)。目标由位图表示,位图包含在前两个参数中(a0 和 a1)。a0 的第 0 位对应于第三个参数 a2 中的 APIC ID,a0 的第 1 位对应于 a2+1 的 APIC ID,以此类推。 + +返回成功传递 IPIs 的 CPU 数量。 + +8. ##### KVM_HC_SCHED_YIELD + +--------------------- + +Architecture: x86 +Status: active +Purpose: 用于在目标vCPU被抢占时进行让步。 + +a0: destination APIC ID + +Usage example: 当向多个vCPU发送调用函数中断(call-function IPI)时,如果任何目标 vCPU 被抢占,进行让步。 + +9. ##### KVM_HC_MAP_GPA_RANGE + +------------------------- + +Architecture: x86 +Status: active +Purpose: 请求 KVM 映射一个具有指定属性的 GPA 范围。 + +`a0`: 起始页面的客户机物理地址 +`a1`: (4KB)页面的数量(在 GPA 空间中必须是连续的) +`a2`: 属性 + + 属性: + 位 3:0 - 首选页大小编码,0 = 4KB,1 = 2MB,2 = 1GB,以此类推... + 位 4 - 明文 = 0,加密 = 1 + 位 63:5 - 保留(必须为零) + +**实现注意事项** + +此超级调用通过 KVM_CAP_EXIT_HYPERCALL 能力在用户空间中实现。在向客户机 CPUID 中添加 KVM_FEATURE_HC_MAP_GPA_RANGE 之前,用户空间必须启用该能力。此外,如果客户机支持 KVM_FEATURE_MIGRATION_CONTROL,用户空间还必须设置一个 MSR 过滤器来处理对 MSR_KVM_MIGRATION_CONTROL 的写入。 + +可以通过如下查看发生的hypercall信息: + +``` +root@nans:/sys/kernel/debug/tracing/events/kvm# echo 0 > ../../tracing_on +root@nans:/sys/kernel/debug/tracing/events/kvm# echo 1 > kvm_hypercall/enable +root@nans:/sys/kernel/debug/tracing/events/kvm# echo 1 > ../../tracing_on +root@nans:/sys/kernel/debug/tracing/events/kvm# cat ../../trace_pipe +``` + +输出如下: + +![image-20240110125350965](https://gitee.com/nan-shuaibo/image/raw/master/202401101258714.png) + +使用ebpf技术统计hypercall信息: + +统计两秒内的每个hypercall发生的次数,和自客户机启动以来每个vcpu上发生的hypercall的次数 + +``` +------------------------------------------------------------------------ +TIME:16:22:05 +PID VCPU_ID NAME COUNTS HYPERCALLS +68453 4 KICK_CPU 1 0 +68453 2 KICK_CPU 1 0 +68453 1 SEND_IPI 6 5 +68453 0 SEND_IPI 7 7 +68453 7 KICK_CPU 1 0 +68453 0 KICK_CPU 1 0 +------------------------------------------------------------------------ +TIME:16:22:07 +PID VCPU_ID NAME COUNTS HYPERCALLS +68082 4 KICK_CPU 2 45 +68453 5 SEND_IPI 3 2 +68453 6 SCHED_YIELD 2 66 +68453 6 SEND_IPI 79 80 +68453 3 SEND_IPI 45 44 +68453 1 SEND_IPI 23 28 +68453 0 SEND_IPI 7 14 +68453 4 SEND_IPI 145 145 +``` + +并将详细信息输出至临时文件 + +![image-20240301162527679](https://gitee.com/nan-shuaibo/image/raw/master/202403011629545.png) + From 69b6efc3237eb454ca813a50f3f36fe456edd321 Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 1 Mar 2024 19:26:55 +0800 Subject: [PATCH 08/17] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c index 300540f64..576f1ca23 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c @@ -380,9 +380,9 @@ static const struct argp_option opts[] = { "Monitor virtual machine dirty page information."}, {"kvmmmu_page_fault", 'f', NULL, 0, "Monitoring the data of kvmmmu page fault."}, - {"kvm_irqchip", 'c', NULL, 0, + {"kvm_irqchip(software)", 'c', NULL, 0, "Monitor the irqchip setting information in KVM VM."}, - {"irq_inject(x86)", 'i', NULL, 0, + {"irq_inject(hardware)", 'i', NULL, 0, "Monitor the virq injection information in KVM VM "}, {"hypercall", 'h', NULL, 0, "Monitor the hypercall information in KVM VM "}, {"stat", 's', NULL, 0, From 2db53ea03d51be0ad88d6f149f510743d5834fb0 Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 1 Mar 2024 21:41:47 +0800 Subject: [PATCH 09/17] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=86=B2=E7=AA=81=20?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kvm_watcher/src/kvm_watcher.c | 99 +------------------ 1 file changed, 4 insertions(+), 95 deletions(-) diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c index cafd36491..089c045f5 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c @@ -151,89 +151,6 @@ const char *getExitReasonName(int number) { return "Unknown"; // 如果找不到对应的退出原因,返回一个默认值 } -typedef struct { - int exit_reason; - char info[256]; - unsigned long long total_dur; - unsigned long long avg_dur; -} ExitInfo; - -// 链表节点 -typedef struct Node { - ExitInfo data; - struct Node *next; -} Node; - -Node *exitInfoBuffer = NULL; - -void addExitInfo(Node **head, int exit_reason, const char *info, - unsigned long long dur, int count) { - Node *newNode = (Node *)malloc(sizeof(Node)); - newNode->data.exit_reason = exit_reason; - strncpy(newNode->data.info, info, sizeof(newNode->data.info)); - newNode->next = NULL; - newNode->data.total_dur = dur; - newNode->data.avg_dur = dur / count; - - // 检查是否已经存在相同 exit reason 的信息 - Node *current = *head; - Node *previous = NULL; - while (current != NULL) { - if (current->data.exit_reason == exit_reason) { - // 更新已存在的信息 - strncpy(current->data.info, info, sizeof(current->data.info)); - current->data.total_dur = dur + current->data.total_dur; - current->data.avg_dur = current->data.total_dur / count; - free(newNode); // 释放新节点,因为信息已经更新 - return; - } - previous = current; - current = current->next; - } - // 没有找到相同的 exit reason,将新节点添加到链表 - if (previous != NULL) { - previous->next = newNode; - } else { - *head = newNode; - } -} - -// 查找指定退出原因的信息 -const char *findExitInfo(Node *head, int exit_reason) { - Node *current = head; - while (current != NULL) { - if (current->data.exit_reason == exit_reason) { - return current->data.info; - } - current = current->next; - } - return NULL; -} - -// 释放链表 -void freeExitInfoList(Node *head) { - while (head != NULL) { - Node *temp = head; - head = head->next; - free(temp); - } -} -// 打印退出的信息 -void printExitInfo(Node *head) { - Node *current = head; - CLEAR_SCREEN(); - printf( - "-----------------------------------------------------------------" - "----------\n"); - printf("%-21s %-18s %-8s %-8s %-13s \n", "EXIT_REASON", "COMM", "PID", - "COUNT", "AVG_DURATION(us)"); - while (current != NULL) { - printf("%-2d/%-18s %-33s %-13.4f \n", current->data.exit_reason, - getExitReasonName(current->data.exit_reason), current->data.info, - NS_TO_US_WITH_DECIMAL(current->data.avg_dur)); - current = current->next; - } -} // 检查具有给定 PID 的进程是否存在 int doesVmProcessExist(pid_t pid) { char proc_name[256]; @@ -385,8 +302,6 @@ static const struct argp_option opts[] = { {"irq_inject(hardware)", 'i', NULL, 0, "Monitor the virq injection information in KVM VM "}, {"hypercall", 'h', NULL, 0, "Monitor the hypercall information in KVM VM "}, - {"stat", 's', NULL, 0, - "Display statistical data.(The -e option must be specified.)"}, {"mmio", 'm', NULL, 0, "Monitoring the data of mmio page fault.(The -f option must be " "specified.)"}, @@ -726,9 +641,6 @@ static int print_event_head(struct env *env) { "VAILD?"); break; case EXIT: - // printf("%-18s %-21s %-18s %-15s %-8s %-13s \n", "TIME(ms)", - // "EXIT_REASON", "COMM", "PID/TID", "COUNT", - // "DURATION(us)"); break; case HALT_POLL: printf("%-18s %-15s %-15s %-10s %-7s %-11s %-10s\n", "TIME(ms)", @@ -926,7 +838,7 @@ int print_exit_map(struct kvm_watcher_bpf *skel) { return 0; } -void print_map_and_check_error(int (*print_func)(struct skel *skel), const char *map_name, int err) { +void print_map_and_check_error(int (*print_func)(struct kvm_watcher_bpf *), struct kvm_watcher_bpf *skel, const char *map_name, int err) { OUTPUT_INTERVAL(OUTPUT_INTERVAL_SECONDS); print_func(skel); if (err < 0) { @@ -1009,10 +921,10 @@ int main(int argc, char **argv) { err = ring_buffer__poll(rb, RING_BUFFER_TIMEOUT_MS /* timeout, ms */); if (env.execute_hypercall) { - print_map_and_check_error(print_hc_map, "hypercall", err); + print_map_and_check_error(print_hc_map, skel, "hypercall", err); } if (env.execute_exit) { - print_map_and_check_error(print_exit_map, "exit", err); + print_map_and_check_error(print_exit_map, skel, "exit", err); } /* Ctrl-C will cause -EINTR */ if (err == -EINTR) { @@ -1024,10 +936,7 @@ int main(int argc, char **argv) { break; } } - if (env.ShowStats) { - printExitInfo(exitInfoBuffer); - freeExitInfoList(exitInfoBuffer); - } else if (env.execute_mark_page_dirty) { + if (env.execute_mark_page_dirty) { err = save_count_dirtypagemap_to_file(skel->maps.count_dirty_map); if (err < 0) { printf("Save count dirty page map to file fail: %d\n", err); From 599d808a08bfb285d3f50af9aad3782c9d6caa1a Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 8 Mar 2024 16:42:30 +0800 Subject: [PATCH 10/17] =?UTF-8?q?=E6=A2=B3=E7=90=86kvm=20ept=20page=20faul?= =?UTF-8?q?t=E5=A4=84=E7=90=86=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBPF_Supermarket/kvm_watcher/docs/kvm_mmu.md | 116 ++++++++++++++++++ .../kvm_watcher/include/kvm_mmu.h | 36 +++--- 2 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 eBPF_Supermarket/kvm_watcher/docs/kvm_mmu.md diff --git a/eBPF_Supermarket/kvm_watcher/docs/kvm_mmu.md b/eBPF_Supermarket/kvm_watcher/docs/kvm_mmu.md new file mode 100644 index 000000000..9c23ca138 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/docs/kvm_mmu.md @@ -0,0 +1,116 @@ + + +vm exit(EPT_VIOLATION)处理流程: + +``` +static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { +... + [EXIT_REASON_EPT_VIOLATION] = handle_ept_violation, +... +}; + +``` + +```c +vmx_handle_exit() { + # 处理 VMX(虚拟机扩展)退出的主要函数 + __vmx_handle_exit() { + handle_ept_violation() { + # 处理 EPT(扩展页表)违规的函数 + kvm_mmu_page_fault() { + # 处理 KVM MMU(内存管理单元)页错误 + kvm_tdp_page_fault() { + # 处理 TDP(两级页表)页错误 + kvm_arch_has_noncoherent_dma(); + direct_page_fault() { + # 处理直接页错误 + kvm_vcpu_gfn_to_memslot(); + page_fault_handle_page_track(); + fast_page_fault(); + mmu_topup_memory_caches() { + # 增加内存缓存 + kvm_mmu_topup_memory_cache() { + __kvm_mmu_topup_memory_cache(); + } + kvm_mmu_topup_memory_cache() { + __kvm_mmu_topup_memory_cache(); + } + kvm_mmu_topup_memory_cache() { + __kvm_mmu_topup_memory_cache(); + } + } + kvm_faultin_pfn() { + # 处理 KVM PFN(物理帧号)故障 + __gfn_to_pfn_memslot() { + # 将 GFN(全局帧号)转换为 PFN(物理帧号)并获取内存插槽 + hva_to_pfn() { + # 将 HVA(主机虚拟地址)转换为 PFN(物理帧号) + get_user_pages_fast_only() { + # 快速获取用户页 + internal_get_user_pages_fast() { + # 内部快速获取用户页 + lockless_pages_from_mm() { + # 从内存管理结构中获取无锁页 + gup_pgd_range() { + # 获取页表项(PGD)范围 + pud_huge(); + gup_pmd_range.constprop.0() { + # 获取中间页表项(PMD)范围 + gup_huge_pmd() { + # 获取巨大页面的中间页表项 + try_grab_folio(); + } + } + } + } + } + } + } + } + } + handle_abnormal_pfn(); + _raw_read_lock(); + is_page_fault_stale(); + kvm_tdp_mmu_map() { + # TDP MMU 映射 + kvm_mmu_hugepage_adjust() { + # 调整 KVM MMU 巨大页面 + kvm_mmu_max_mapping_level() { + # 获取 KVM MMU 最大映射级别 + host_pfn_mapping_level(); + } + } + __rcu_read_lock(); + tdp_iter_start() { + # TDP 迭代器开始 + tdp_iter_restart() { + # TDP 迭代器重新启动 + tdp_iter_refresh_sptep(); + } + } + disallowed_hugepage_adjust(); + tdp_iter_next() { + # TDP 迭代器下一个 + tdp_iter_refresh_sptep(); + } + disallowed_hugepage_adjust(); + tdp_iter_next() { + # TDP 迭代器下一个 + tdp_iter_refresh_sptep(); + } + } + tdp_iter_next() { + # TDP 迭代器下一个 + tdp_iter_refresh_sptep(); + } + } + disallowed_hugepage_adjust(); + } + } + } + } +} + +``` + +![kvm-init-mmu](https://gitee.com/nan-shuaibo/image/raw/master/202403081421893.png) \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h b/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h index 580508f45..52d5b4fa0 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h @@ -25,6 +25,8 @@ #include #include +#define PAGE_SHIFT 12 + struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 8192); @@ -56,9 +58,9 @@ static int trace_page_fault(struct page_fault *ctx, pid_t vm_pid) { return 0; } -static int trace_direct_page_fault(struct kvm_vcpu *vcpu, - struct kvm_page_fault *fault, void *rb, - struct common_event *e) { +static int trace_tdp_page_fault(struct kvm_vcpu *vcpu, + struct kvm_page_fault *fault, void *rb, + struct common_event *e) { u64 addr; bpf_probe_read_kernel(&addr, sizeof(u64), &fault->addr); u64 *ts; @@ -81,7 +83,6 @@ static int trace_direct_page_fault(struct kvm_vcpu *vcpu, if (count) { (*count)++; e->page_fault_data.count = *count; - bpf_map_update_elem(&pf_count, &addr, count, BPF_ANY); } else { e->page_fault_data.count = 1; bpf_map_update_elem(&pf_count, &addr, &new_count, BPF_ANY); @@ -104,36 +105,43 @@ static int trace_kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, CHECK_PID(vm_pid); if (error_code & PFERR_RSVD_MASK) { u64 ts = bpf_ktime_get_ns(); - u64 addr = cr2_or_gpa; - bpf_map_update_elem(&pf_delay, &addr, &ts, BPF_ANY); + u64 gfn = cr2_or_gpa >> PAGE_SHIFT; + bpf_map_update_elem(&pf_delay, &gfn, &ts, BPF_ANY); } return 0; } -static int trace_handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr, - bool direct, void *rb, +struct mmio_page_fault { + u64 pad; + u64 addr; + gfn_t gfn; + unsigned access; +}; + +static int trace_handle_mmio_page_fault(struct mmio_page_fault *ctx, void *rb, struct common_event *e) { u64 *ts; - ts = bpf_map_lookup_elem(&pf_delay, &addr); + u64 gfn; + bpf_probe_read_kernel(&gfn, sizeof(u64), &ctx->gfn); + ts = bpf_map_lookup_elem(&pf_delay, &gfn); if (!ts) { return 0; } u32 *count; u32 new_count = 1; u64 delay = bpf_ktime_get_ns() - *ts; - bpf_map_delete_elem(&pf_delay, &addr); + bpf_map_delete_elem(&pf_delay, &gfn); RESERVE_RINGBUF_ENTRY(rb, e); - count = bpf_map_lookup_elem(&pf_count, &addr); + count = bpf_map_lookup_elem(&pf_count, &gfn); if (count) { (*count)++; e->page_fault_data.count = *count; - bpf_map_update_elem(&pf_count, &addr, count, BPF_ANY); } else { e->page_fault_data.count = 1; - bpf_map_update_elem(&pf_count, &addr, &new_count, BPF_ANY); + bpf_map_update_elem(&pf_count, &gfn, &new_count, BPF_ANY); } e->page_fault_data.delay = delay; - e->page_fault_data.addr = addr; + e->page_fault_data.addr = gfn; e->page_fault_data.error_code = PFERR_RSVD_MASK; e->process.pid = bpf_get_current_pid_tgid() >> 32; bpf_get_current_comm(&e->process.comm, sizeof(e->process.comm)); From 9fb6314b9165c346061a5b161c8179278a10c4fb Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 8 Mar 2024 16:47:32 +0800 Subject: [PATCH 11/17] =?UTF-8?q?=E6=9B=B4=E6=94=B9tdp=20page=20fault=20?= =?UTF-8?q?=EF=BC=8C=E5=92=8Cmmio=20page=20fault=20=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E6=89=80=E7=BB=9F=E8=AE=A1=E7=9A=84=E6=8C=87=E6=A0=87?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E6=9C=89=E6=95=88=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?ioctl=E7=9A=84tp=EF=BC=8C=E5=90=8E=E6=9C=9F=E6=96=B9=E4=BE=BF?= =?UTF-8?q?=E6=8F=90=E5=8F=96kvm=20ioctl=E7=9B=B8=E5=85=B3=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kvm_watcher/src/kvm_watcher.bpf.c | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c index 9c32befae..406e5d273 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c @@ -20,12 +20,13 @@ #include #include #include +#include "../include/kvm_watcher.h" #include "../include/kvm_exits.h" #include "../include/kvm_vcpu.h" #include "../include/kvm_mmu.h" #include "../include/kvm_irq.h" #include "../include/kvm_hypercall.h" -#include "../include/kvm_watcher.h" +#include "../include/kvm_ioctl.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; @@ -75,10 +76,10 @@ int tp_page_fault(struct page_fault *ctx) { return trace_page_fault(ctx, vm_pid); } -SEC("fexit/direct_page_fault") -int BPF_PROG(fexit_direct_page_fault, struct kvm_vcpu *vcpu, +SEC("fexit/kvm_tdp_page_fault") +int BPF_PROG(fexit_tdp_page_fault, struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) { - return trace_direct_page_fault(vcpu, fault, &rb, e); + return trace_tdp_page_fault(vcpu, fault, &rb, e); } SEC("fentry/kvm_mmu_page_fault") @@ -87,10 +88,9 @@ int BPF_PROG(fentry_kvm_mmu_page_fault, struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, return trace_kvm_mmu_page_fault(vcpu, cr2_or_gpa, error_code, vm_pid); } -SEC("fexit/handle_mmio_page_fault") -int BPF_PROG(fexit_handle_mmio_page_fault, struct kvm_vcpu *vcpu, u64 addr, - bool direct) { - return trace_handle_mmio_page_fault(vcpu, addr, direct, &rb, e); +SEC("tp/kvmmmu/handle_mmio_page_fault") +int tp_handle_mmio_page_fault(struct mmio_page_fault *ctx) { + return trace_handle_mmio_page_fault(ctx, &rb, e); } SEC("fentry/kvm_pic_set_irq") @@ -145,3 +145,8 @@ SEC("fentry/kvm_emulate_hypercall") int BPF_PROG(fentry_emulate_hypercall, struct kvm_vcpu *vcpu) { return entry_emulate_hypercall(vcpu, &rb, e, vm_pid); } + +SEC("tracepoint/syscalls/sys_enter_ioctl") +int tp_ioctl(struct trace_event_raw_sys_enter *args) { + return trace_kvm_ioctl(args); +} \ No newline at end of file From b17bd776a23c2a19d9c154197352fe7027b31d50 Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 8 Mar 2024 16:50:46 +0800 Subject: [PATCH 12/17] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=86=85=E6=A0=B8?= =?UTF-8?q?=E6=80=81kvm=20ioctl=E6=8F=90=E5=8F=96=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=EF=BC=8C=E7=9B=AE=E5=89=8D=E5=8F=AA=E7=BB=9F=E8=AE=A1=E9=83=A8?= =?UTF-8?q?=E5=88=86ioctl=E4=BF=A1=E6=81=AF=EF=BC=8C=E5=90=8E=E6=9C=9F?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E5=AE=8C=E5=96=84=E7=A8=8B=E5=BA=8F=E5=B9=B6?= =?UTF-8?q?=E5=AF=B9=E6=95=B0=E6=8D=AE=E8=BF=9B=E8=A1=8C=E5=A4=84=E7=90=86?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E5=88=B0=E7=94=A8=E6=88=B7=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kvm_watcher/include/kvm_ioctl.h | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h b/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h new file mode 100644 index 000000000..8c4b73243 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h @@ -0,0 +1,110 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// author: nanshuaibo811@163.com +// +// Kernel space BPF program used for KVM ioctl + +#ifndef __KVM_IOCTL_H +#define __KVM_IOCTL_H + +#include "kvm_watcher.h" +#include "vmlinux.h" +#include +#include +#include +#include + +#define KVMIO 0xAE +#define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */ +#define KVM_CREATE_VCPU _IO(KVMIO, 0x41) +#define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) +#define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) +#define KVM_SET_USER_MEMORY_REGION \ + _IOW(KVMIO, 0x46, struct kvm_userspace_memory_region) +#define KVM_GET_REGS _IOR(KVMIO, 0x81, struct kvm_regs) +#define KVM_SET_REGS _IOW(KVMIO, 0x82, struct kvm_regs) +#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation) +#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt) + +static int trace_kvm_ioctl(struct trace_event_raw_sys_enter *args) { + int fd = (int)args->args[0]; + unsigned int cmd = (unsigned int)args->args[1]; + unsigned long arg = (unsigned long)args->args[2]; + switch (cmd) { + case KVM_CREATE_VM: + bpf_printk("KVM_CREATE_VM: fd=%d\n", fd); + break; + case KVM_CREATE_VCPU: { + int vcpu_id; + bpf_probe_read(&vcpu_id, sizeof(vcpu_id), (void *)arg); + bpf_printk("KVM_CREATE_VCPU: fd=%d, vcpu_id=%d\n", fd, vcpu_id); + break; + } + case KVM_SET_USER_MEMORY_REGION: { + struct kvm_userspace_memory_region region; + bpf_probe_read(®ion, sizeof(region), (void *)arg); + // 打印或处理 region 数据 + bpf_printk( + "KVM_SET_USER_MEMORY_REGION: fd=%d, slot=%u, flags=%u, " + "guest_phys_addr=%llx, memory_size=%lluK,userspace_addr=%llx\n", + fd, region.slot, region.flags, region.guest_phys_addr, + region.memory_size / 1024,region.userspace_addr); + break; + } + case KVM_GET_VCPU_EVENTS: + case KVM_SET_VCPU_EVENTS: { + struct kvm_vcpu_events events; + bpf_probe_read(&events, sizeof(events), (void *)arg); + // 打印或处理 events 数据 + bpf_printk( + "KVM_SET/GET_VCPU_EVENTS: fd=%d, exception=%u, interrupt=%u\n", + fd, events.exception.nr, events.interrupt.nr); + break; + } + case KVM_GET_REGS: + case KVM_SET_REGS: { + struct kvm_regs regs; + bpf_probe_read(®s, sizeof(regs), (void *)arg); + // 此处仅展示部分寄存器值的打印 + bpf_printk( + "KVM_GET/SET_REGS: fd=%d, rax=%llx, rbx=%llx, rcx=%llx, " + "rdx=%llx, rsi=%llx\n", + fd, regs.rax, regs.rbx, regs.rcx, regs.rdx, regs.rsi); + + break; + } + case KVM_TRANSLATE: { + struct kvm_translation tr; + bpf_probe_read(&tr, sizeof(tr), (void *)arg); + bpf_printk( + "KVM_TRANSLATE: fd=%d,linear_address=%llx, " + "physical_address=%llx\n", + fd, tr.linear_address, tr.physical_address); + break; + } + case KVM_INTERRUPT: { + struct kvm_interrupt irq; + bpf_probe_read(&irq, sizeof(irq), (void *)arg); + bpf_printk("KVM_INTERRUPT:fd=%d,interrupt vector:%d\n", fd, + irq.irq); + break; + } + default: + break; + } + return 0; +} + +#endif /* __KVM_IOCTL_H */ \ No newline at end of file From 7b49dc49457962d0aa91ae484933be72b3c1ca12 Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 8 Mar 2024 16:51:02 +0800 Subject: [PATCH 13/17] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBPF_Supermarket/kvm_watcher/include/kvm_exits.h | 2 +- eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h b/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h index 4171c89e8..214bf96cf 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h @@ -58,7 +58,7 @@ static int trace_kvm_exit(struct exit *ctx, pid_t vm_pid) { CHECK_PID(vm_pid); u32 reason; reason = (u32)ctx->exit_reason; - //如果是节能停止退出,就不采集数据 + // 如果是节能停止退出,就不采集数据 if (reason == EXIT_REASON_HLT) { return 0; } diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h b/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h index 3a8a8df61..5eace1a93 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h @@ -28,10 +28,7 @@ #define NS_TO_US_WITH_DECIMAL(ns) ((double)(ns) / NS_TO_US_FACTOR) #define NS_TO_MS_WITH_DECIMAL(ns) ((double)(ns) / NS_TO_MS_FACTOR) -#define MICROSECONDS_IN_SECOND 1000000 -#define OUTPUT_INTERVAL_SECONDS 2 - -#define OUTPUT_INTERVAL(us) usleep((__u32)(us * MICROSECONDS_IN_SECOND)) +#define OUTPUT_INTERVAL(SECONDS) sleep(SECONDS) #define OPTIONS_LIST "-w, -p, -d, -f, -c, -i, ,-h or -e" @@ -147,6 +144,7 @@ enum EventType { IRQCHIP, IRQ_INJECT, HYPERCALL, + IOCTL, } event_type; struct common_event { From 218b21fd6b67a227a634fe8f5c0a591872fb60e2 Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 8 Mar 2024 17:14:26 +0800 Subject: [PATCH 14/17] =?UTF-8?q?Revert=20"=E6=B7=BB=E5=8A=A0=E5=86=85?= =?UTF-8?q?=E6=A0=B8=E6=80=81kvm=20ioctl=E6=8F=90=E5=8F=96=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=EF=BC=8C=E7=9B=AE=E5=89=8D=E5=8F=AA=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E9=83=A8=E5=88=86ioctl=E4=BF=A1=E6=81=AF=EF=BC=8C=E5=90=8E?= =?UTF-8?q?=E6=9C=9F=E8=AE=A1=E5=88=92=E5=AE=8C=E5=96=84=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E5=B9=B6=E5=AF=B9=E6=95=B0=E6=8D=AE=E8=BF=9B=E8=A1=8C=E5=A4=84?= =?UTF-8?q?=E7=90=86=E8=BE=93=E5=87=BA=E5=88=B0=E7=94=A8=E6=88=B7=E6=80=81?= =?UTF-8?q?"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b17bd776a23c2a19d9c154197352fe7027b31d50. --- .../kvm_watcher/include/kvm_ioctl.h | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h b/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h deleted file mode 100644 index 8c4b73243..000000000 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: nanshuaibo811@163.com -// -// Kernel space BPF program used for KVM ioctl - -#ifndef __KVM_IOCTL_H -#define __KVM_IOCTL_H - -#include "kvm_watcher.h" -#include "vmlinux.h" -#include -#include -#include -#include - -#define KVMIO 0xAE -#define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */ -#define KVM_CREATE_VCPU _IO(KVMIO, 0x41) -#define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) -#define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) -#define KVM_SET_USER_MEMORY_REGION \ - _IOW(KVMIO, 0x46, struct kvm_userspace_memory_region) -#define KVM_GET_REGS _IOR(KVMIO, 0x81, struct kvm_regs) -#define KVM_SET_REGS _IOW(KVMIO, 0x82, struct kvm_regs) -#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation) -#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt) - -static int trace_kvm_ioctl(struct trace_event_raw_sys_enter *args) { - int fd = (int)args->args[0]; - unsigned int cmd = (unsigned int)args->args[1]; - unsigned long arg = (unsigned long)args->args[2]; - switch (cmd) { - case KVM_CREATE_VM: - bpf_printk("KVM_CREATE_VM: fd=%d\n", fd); - break; - case KVM_CREATE_VCPU: { - int vcpu_id; - bpf_probe_read(&vcpu_id, sizeof(vcpu_id), (void *)arg); - bpf_printk("KVM_CREATE_VCPU: fd=%d, vcpu_id=%d\n", fd, vcpu_id); - break; - } - case KVM_SET_USER_MEMORY_REGION: { - struct kvm_userspace_memory_region region; - bpf_probe_read(®ion, sizeof(region), (void *)arg); - // 打印或处理 region 数据 - bpf_printk( - "KVM_SET_USER_MEMORY_REGION: fd=%d, slot=%u, flags=%u, " - "guest_phys_addr=%llx, memory_size=%lluK,userspace_addr=%llx\n", - fd, region.slot, region.flags, region.guest_phys_addr, - region.memory_size / 1024,region.userspace_addr); - break; - } - case KVM_GET_VCPU_EVENTS: - case KVM_SET_VCPU_EVENTS: { - struct kvm_vcpu_events events; - bpf_probe_read(&events, sizeof(events), (void *)arg); - // 打印或处理 events 数据 - bpf_printk( - "KVM_SET/GET_VCPU_EVENTS: fd=%d, exception=%u, interrupt=%u\n", - fd, events.exception.nr, events.interrupt.nr); - break; - } - case KVM_GET_REGS: - case KVM_SET_REGS: { - struct kvm_regs regs; - bpf_probe_read(®s, sizeof(regs), (void *)arg); - // 此处仅展示部分寄存器值的打印 - bpf_printk( - "KVM_GET/SET_REGS: fd=%d, rax=%llx, rbx=%llx, rcx=%llx, " - "rdx=%llx, rsi=%llx\n", - fd, regs.rax, regs.rbx, regs.rcx, regs.rdx, regs.rsi); - - break; - } - case KVM_TRANSLATE: { - struct kvm_translation tr; - bpf_probe_read(&tr, sizeof(tr), (void *)arg); - bpf_printk( - "KVM_TRANSLATE: fd=%d,linear_address=%llx, " - "physical_address=%llx\n", - fd, tr.linear_address, tr.physical_address); - break; - } - case KVM_INTERRUPT: { - struct kvm_interrupt irq; - bpf_probe_read(&irq, sizeof(irq), (void *)arg); - bpf_printk("KVM_INTERRUPT:fd=%d,interrupt vector:%d\n", fd, - irq.irq); - break; - } - default: - break; - } - return 0; -} - -#endif /* __KVM_IOCTL_H */ \ No newline at end of file From 4bdcdfc645ebd62ea9f0b1578d7b4b48c4f038bd Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 8 Mar 2024 17:17:59 +0800 Subject: [PATCH 15/17] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=86=85=E6=A0=B8?= =?UTF-8?q?=E6=80=81kvm=20ioctl=E5=A4=84=E7=90=86=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E5=90=8E=E7=BB=AD=E8=AE=A1=E5=88=92=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E7=9B=B8=E5=85=B3ioctl=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kvm_watcher/include/kvm_ioctl.h | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h b/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h new file mode 100644 index 000000000..97d13ba03 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h @@ -0,0 +1,32 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// author: nanshuaibo811@163.com +// +// Kernel space BPF program used for KVM ioctl + +#ifndef __KVM_IOCTL_H +#define __KVM_IOCTL_H + +#include "kvm_watcher.h" +#include "vmlinux.h" +#include +#include +#include + +static int trace_kvm_ioctl(struct trace_event_raw_sys_enter *args) { + return 0; +} + +#endif /* __KVM_IOCTL_H */ \ No newline at end of file From 05788a0157c7c0143e85ae31f445d4f6d927cbf4 Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Fri, 8 Mar 2024 17:23:30 +0800 Subject: [PATCH 16/17] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=80=81kvm=20=20ioctl=E4=BB=A3=E7=A0=81=E6=A1=86=E6=9E=B6=20?= =?UTF-8?q?=EF=BC=8C=E5=87=8F=E5=B0=91=E9=87=8D=E5=A4=8D=E6=80=A7=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=BB=A3=E7=A0=81=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kvm_watcher/src/kvm_watcher.c | 258 +++++++++--------- 1 file changed, 135 insertions(+), 123 deletions(-) diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c index 089c045f5..86e216af6 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c @@ -54,101 +54,102 @@ FILE *create_temp_file(const char *filename) { return output; } -const char *getHypercallName(int number) { - struct Hypercall { +const char *getName(int number, enum EventType type) { + struct NameMapping { int number; const char *name; }; - + // 定义具体的退出原因 arch/x86/include/uapi/asm/vmx.h + struct NameMapping exitReasons[] = {{0, "EXCEPTION_NMI"}, + {1, "EXTERNAL_INTERRUPT"}, + {2, "TRIPLE_FAULT"}, + {3, "INIT_SIGNAL"}, + {4, "SIPI_SIGNAL"}, + {7, "INTERRUPT_WINDOW"}, + {8, "NMI_WINDOW"}, + {9, "TASK_SWITCH"}, + {10, "CPUID"}, + {12, "HLT"}, + {13, "INVD"}, + {14, "INVLPG"}, + {15, "RDPMC"}, + {16, "RDTSC"}, + {18, "VMCALL"}, + {19, "VMCLEAR"}, + {20, "VMLAUNCH"}, + {21, "VMPTRLD"}, + {22, "VMPTRST"}, + {23, "VMREAD"}, + {24, "VMRESUME"}, + {25, "VMWRITE"}, + {26, "VMOFF"}, + {27, "VMON"}, + {28, "CR_ACCESS"}, + {29, "DR_ACCESS"}, + {30, "IO_INSTRUCTION"}, + {31, "MSR_READ"}, + {32, "MSR_WRITE"}, + {33, "INVALID_STATE"}, + {34, "MSR_LOAD_FAIL"}, + {36, "MWAIT_INSTRUCTION"}, + {37, "MONITOR_TRAP_FLAG"}, + {39, "MONITOR_INSTRUCTION"}, + {40, "PAUSE_INSTRUCTION"}, + {41, "MCE_DURING_VMENTRY"}, + {43, "TPR_BELOW_THRESHOLD"}, + {44, "APIC_ACCESS"}, + {45, "EOI_INDUCED"}, + {46, "GDTR_IDTR"}, + {47, "LDTR_TR"}, + {48, "EPT_VIOLATION"}, + {49, "EPT_MISCONFIG"}, + {50, "INVEPT"}, + {51, "RDTSCP"}, + {52, "PREEMPTION_TIMER"}, + {53, "INVVPID"}, + {54, "WBINVD"}, + {55, "XSETBV"}, + {56, "APIC_WRITE"}, + {57, "RDRAND"}, + {58, "INVPCID"}, + {59, "VMFUNC"}, + {60, "ENCLS"}, + {61, "RDSEED"}, + {62, "PML_FULL"}, + {63, "XSAVES"}, + {64, "XRSTORS"}, + {67, "UMWAIT"}, + {68, "TPAUSE"}, + {74, "BUS_LOCK"}, + {75, "NOTIFY"}}; // 定义超级调用 include\uapi\linux\kvm_para.h - struct Hypercall hypercalls[] = { + struct NameMapping hypercalls[] = { {1, "VAPIC_POLL_IRQ"}, {5, "KICK_CPU"}, {9, "CLOCK_PAIRING"}, {10, "SEND_IPI"}, {11, "SCHED_YIELD"}, {12, "MAP_GPA_RANGE"}}; - - for (int i = 0; i < sizeof(hypercalls) / sizeof(hypercalls[0]); i++) { - if (hypercalls[i].number == number) { - return hypercalls[i].name; - } + // 根据枚举类型选择使用哪个结构体数组进行转换 + struct NameMapping *mappings; + int count; + switch (type) { + case EXIT: + mappings = exitReasons; + count = sizeof(exitReasons) / sizeof(exitReasons[0]); + break; + case HYPERCALL: + mappings = hypercalls; + count = sizeof(hypercalls) / sizeof(hypercalls[0]); + break; + default: + return "Unknown"; } - return "Unknown"; // 如果找不到对应的超级调用号,返回一个默认值 -} - -const char *getExitReasonName(int number) { - struct ExitReason { - int number; - const char *name; - }; - // 定义具体的退出原因 arch/x86/include/uapi/asm/vmx.h - struct ExitReason exitReasons[] = {{0, "EXCEPTION_NMI"}, - {1, "EXTERNAL_INTERRUPT"}, - {2, "TRIPLE_FAULT"}, - {3, "INIT_SIGNAL"}, - {4, "SIPI_SIGNAL"}, - {7, "INTERRUPT_WINDOW"}, - {8, "NMI_WINDOW"}, - {9, "TASK_SWITCH"}, - {10, "CPUID"}, - {12, "HLT"}, - {13, "INVD"}, - {14, "INVLPG"}, - {15, "RDPMC"}, - {16, "RDTSC"}, - {18, "VMCALL"}, - {19, "VMCLEAR"}, - {20, "VMLAUNCH"}, - {21, "VMPTRLD"}, - {22, "VMPTRST"}, - {23, "VMREAD"}, - {24, "VMRESUME"}, - {25, "VMWRITE"}, - {26, "VMOFF"}, - {27, "VMON"}, - {28, "CR_ACCESS"}, - {29, "DR_ACCESS"}, - {30, "IO_INSTRUCTION"}, - {31, "MSR_READ"}, - {32, "MSR_WRITE"}, - {33, "INVALID_STATE"}, - {34, "MSR_LOAD_FAIL"}, - {36, "MWAIT_INSTRUCTION"}, - {37, "MONITOR_TRAP_FLAG"}, - {39, "MONITOR_INSTRUCTION"}, - {40, "PAUSE_INSTRUCTION"}, - {41, "MCE_DURING_VMENTRY"}, - {43, "TPR_BELOW_THRESHOLD"}, - {44, "APIC_ACCESS"}, - {45, "EOI_INDUCED"}, - {46, "GDTR_IDTR"}, - {47, "LDTR_TR"}, - {48, "EPT_VIOLATION"}, - {49, "EPT_MISCONFIG"}, - {50, "INVEPT"}, - {51, "RDTSCP"}, - {52, "PREEMPTION_TIMER"}, - {53, "INVVPID"}, - {54, "WBINVD"}, - {55, "XSETBV"}, - {56, "APIC_WRITE"}, - {57, "RDRAND"}, - {58, "INVPCID"}, - {59, "VMFUNC"}, - {60, "ENCLS"}, - {61, "RDSEED"}, - {62, "PML_FULL"}, - {63, "XSAVES"}, - {64, "XRSTORS"}, - {67, "UMWAIT"}, - {68, "TPAUSE"}, - {74, "BUS_LOCK"}, - {75, "NOTIFY"}}; - - for (int i = 0; i < sizeof(exitReasons) / sizeof(exitReasons[0]); i++) { - if (exitReasons[i].number == number) { - return exitReasons[i].name; + // 根据给定的编号在选择的结构体数组中搜索对应的名称 + for (int i = 0; i < count; i++) { + if (mappings[i].number == number) { + return mappings[i].name; } } - return "Unknown"; // 如果找不到对应的退出原因,返回一个默认值 + + return "Unknown"; // 如果找不到对应的条目,返回一个默认值 } // 检查具有给定 PID 的进程是否存在 @@ -256,7 +257,6 @@ int save_count_dirtypagemap_to_file(struct bpf_map *map) { static struct env { bool execute_vcpu_wakeup; bool execute_exit; - bool ShowStats; bool execute_halt_poll_ns; bool execute_mark_page_dirty; bool execute_page_fault; @@ -264,13 +264,13 @@ static struct env { bool execute_irqchip; bool execute_irq_inject; bool execute_hypercall; + bool execute_ioctl; int monitoring_time; pid_t vm_pid; enum EventType event_type; } env = { .execute_vcpu_wakeup = false, .execute_exit = false, - .ShowStats = false, .execute_halt_poll_ns = false, .execute_mark_page_dirty = false, .execute_page_fault = false, @@ -278,6 +278,7 @@ static struct env { .execute_irq_inject = false, .mmio_page_fault = false, .execute_hypercall = false, + .execute_ioctl = false, .monitoring_time = 0, .vm_pid = -1, .event_type = NONE_TYPE, @@ -297,9 +298,9 @@ static const struct argp_option opts[] = { "Monitor virtual machine dirty page information."}, {"kvmmmu_page_fault", 'f', NULL, 0, "Monitoring the data of kvmmmu page fault."}, - {"kvm_irqchip(software)", 'c', NULL, 0, + {"kvm_irqchip", 'c', NULL, 0, "Monitor the irqchip setting information in KVM VM."}, - {"irq_inject(hardware)", 'i', NULL, 0, + {"irq_inject", 'i', NULL, 0, "Monitor the virq injection information in KVM VM "}, {"hypercall", 'h', NULL, 0, "Monitor the hypercall information in KVM VM "}, {"mmio", 'm', NULL, 0, @@ -307,6 +308,7 @@ static const struct argp_option opts[] = { "specified.)"}, {"vm_pid", 'p', "PID", 0, "Specify the virtual machine pid to monitor."}, {"monitoring_time", 't', "SEC", 0, "Time for monitoring."}, + {"kvm_ioctl", 'l', NULL, 0, "Monitoring the KVM IOCTL."}, {NULL, 'H', NULL, OPTION_HIDDEN, "Show the full help"}, {}, }; @@ -343,13 +345,8 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'h': SET_OPTION_AND_CHECK_USAGE(option_selected, env.execute_hypercall); break; - case 's': - if (env.execute_exit) { - env.ShowStats = true; - } else { - fprintf(stderr, "The -e option must be specified.\n"); - argp_state_help(state, stdout, ARGP_HELP_STD_HELP); - } + case 'l': + SET_OPTION_AND_CHECK_USAGE(option_selected, env.execute_ioctl); break; case 'm': if (env.execute_page_fault) { @@ -425,6 +422,8 @@ static int determineEventType(struct env *env) { env->event_type = IRQ_INJECT; } else if (env->execute_hypercall) { env->event_type = HYPERCALL; + } else if (env->execute_ioctl) { + env->event_type = IOCTL; } else { env->event_type = NONE_TYPE; // 或者根据需要设置一个默认的事件类型 } @@ -472,7 +471,7 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { } case PAGE_FAULT: { // 使用 e->page_fault_data 访问 PAGE_FAULT 特有成员 - printf("%-18.6f %-15s %-10u %-12llx %-6u %-10.4f ", timestamp_ms, + printf("%-18.6f %-15s %-10u %-14llx %-6u %-10.4f ", timestamp_ms, e->process.comm, e->process.pid, e->page_fault_data.addr, e->page_fault_data.count, NS_TO_US_WITH_DECIMAL(e->page_fault_data.delay)); @@ -593,7 +592,7 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { fprintf(output, "%-18.6f %-15s %-10d %-10d %-10s %-11llu", timestamp_ms, e->process.comm, e->process.pid, e->hypercall_data.vcpu_id, - getHypercallName(e->hypercall_data.hc_nr), + getName(e->hypercall_data.hc_nr, HYPERCALL), e->hypercall_data.hypercalls); if (e->hypercall_data.hc_nr == 5) { fprintf(output, "apic_id:%llu\n", e->hypercall_data.a1); @@ -621,6 +620,9 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { fclose(output); break; } + case IOCTL: { + break; + } default: // 处理未知事件类型 break; @@ -653,8 +655,8 @@ static int print_event_head(struct env *env) { break; case PAGE_FAULT: printf("%-18s %-15s %-10s %-12s %-6s %-10s %-20s %-17s %-10s %s\n", - "TIME(ms)", "COMM", "PID", "ADDRESS", "COUNT", "DELAY(us)", - "HVA", "PFN", "MEM_SLOTID", "ERROR_TYPE"); + "TIME(ms)", "COMM", "PID", "(f(GPA)m(GFN))", "COUNT", + "DELAY(us)", "HVA", "PFN", "MEM_SLOTID", "ERROR_TYPE"); break; case IRQCHIP: printf("%-18s %-15s %-10s %-10s %-14s %-10s %-10s\n", "TIME(ms)", @@ -678,6 +680,10 @@ static int print_event_head(struct env *env) { fclose(output); break; } + case IOCTL: { + printf("wait....\n"); + break; + } default: // Handle default case or display an error message break; @@ -701,11 +707,11 @@ static void set_disable_load(struct kvm_watcher_bpf *skel) { env.execute_mark_page_dirty ? true : false); bpf_program__set_autoload(skel->progs.tp_page_fault, env.execute_page_fault ? true : false); - bpf_program__set_autoload(skel->progs.fexit_direct_page_fault, + bpf_program__set_autoload(skel->progs.fexit_tdp_page_fault, env.execute_page_fault ? true : false); bpf_program__set_autoload(skel->progs.fentry_kvm_mmu_page_fault, env.mmio_page_fault ? true : false); - bpf_program__set_autoload(skel->progs.fexit_handle_mmio_page_fault, + bpf_program__set_autoload(skel->progs.tp_handle_mmio_page_fault, env.mmio_page_fault ? true : false); bpf_program__set_autoload(skel->progs.fentry_kvm_pic_set_irq, env.execute_irqchip ? true : false); @@ -725,6 +731,23 @@ static void set_disable_load(struct kvm_watcher_bpf *skel) { env.execute_irq_inject ? true : false); bpf_program__set_autoload(skel->progs.fentry_emulate_hypercall, env.execute_hypercall ? true : false); + bpf_program__set_autoload(skel->progs.tp_ioctl, + env.execute_ioctl ? true : false); +} + +// 函数不接受参数,返回一个静态分配的字符串 +const char *getCurrentTimeFormatted() { + static char ts[32]; // 静态分配,每次调用都会覆盖 + time_t t; + struct tm *tm; + + time(&t); + tm = localtime(&t); + + // 格式化时间到静态分配的字符串中 + strftime(ts, sizeof(ts), "%H:%M:%S", tm); + + return ts; // 返回指向静态字符串的指针 } int print_hc_map(struct kvm_watcher_bpf *skel) { @@ -734,12 +757,6 @@ int print_hc_map(struct kvm_watcher_bpf *skel) { struct hc_key lookup_key = {}; struct hc_key next_key = {}; struct hc_value hc_value = {}; - struct tm *tm; - char ts[32]; - time_t t; - time(&t); - tm = localtime(&t); - strftime(ts, sizeof(ts), "%H:%M:%S", tm); int first_run = 1; // Iterate over the map while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { @@ -749,7 +766,7 @@ int print_hc_map(struct kvm_watcher_bpf *skel) { "--------------------------------------------------------------" "----------" "\n"); - printf("TIME:%s\n", ts); + printf("TIME:%s\n", getCurrentTimeFormatted()); printf("%-12s %-12s %-12s %-12s %-12s\n", "PID", "VCPU_ID", "NAME", "COUNTS", "HYPERCALLS"); } @@ -760,8 +777,8 @@ int print_hc_map(struct kvm_watcher_bpf *skel) { return -1; } printf("%-12d %-12d %-12s %-12d %-12lld\n", next_key.pid, - next_key.vcpu_id, getHypercallName(next_key.nr), hc_value.counts, - hc_value.hypercalls); + next_key.vcpu_id, getName(next_key.nr, HYPERCALL), + hc_value.counts, hc_value.hypercalls); // // Move to the next key lookup_key = next_key; } @@ -792,18 +809,12 @@ int print_exit_map(struct kvm_watcher_bpf *skel) { struct exit_key lookup_key = {}; struct exit_key next_key = {}; struct exit_value exit_value; - struct tm *tm; - char ts[32]; - time_t t; - time(&t); - tm = localtime(&t); - strftime(ts, sizeof(ts), "%H:%M:%S", tm); int first_run = 1; // Iterate over the map while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { if (first_run) { first_run = 0; - printf("\nTIME:%s\n", ts); + printf("\nTIME:%s\n", getCurrentTimeFormatted()); printf("%-12s %-12s %-12s %-12s %-12s %-12s\n", "pid", "total_time", "max_time", "min_time", "counts", "reason"); printf( @@ -821,8 +832,7 @@ int print_exit_map(struct kvm_watcher_bpf *skel) { NS_TO_MS_WITH_DECIMAL(exit_value.total_time), NS_TO_MS_WITH_DECIMAL(exit_value.max_time), NS_TO_MS_WITH_DECIMAL(exit_value.min_time), exit_value.count, - getExitReasonName(next_key.reason)); - + getName(next_key.reason, EXIT)); // Move to the next key lookup_key = next_key; } @@ -838,8 +848,10 @@ int print_exit_map(struct kvm_watcher_bpf *skel) { return 0; } -void print_map_and_check_error(int (*print_func)(struct kvm_watcher_bpf *), struct kvm_watcher_bpf *skel, const char *map_name, int err) { - OUTPUT_INTERVAL(OUTPUT_INTERVAL_SECONDS); +void print_map_and_check_error(int (*print_func)(struct kvm_watcher_bpf *), + struct kvm_watcher_bpf *skel, + const char *map_name, int err) { + OUTPUT_INTERVAL(2); print_func(skel); if (err < 0) { printf("Error printing %s map: %d\n", map_name, err); @@ -919,7 +931,7 @@ int main(int argc, char **argv) { } while (!exiting) { err = ring_buffer__poll(rb, RING_BUFFER_TIMEOUT_MS /* timeout, ms */); - + if (env.execute_hypercall) { print_map_and_check_error(print_hc_map, skel, "hypercall", err); } From 660a1c3f166b3b91cab191cdf8e0cd963c16b4ff Mon Sep 17 00:00:00 2001 From: nanshuaibo Date: Sun, 10 Mar 2024 14:57:55 +0800 Subject: [PATCH 17/17] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c index 82e410e68..2746149fe 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c @@ -884,13 +884,13 @@ int print_exit_map(struct kvm_watcher_bpf *skel) { NS_TO_MS_WITH_DECIMAL(values[i].total_time), NS_TO_MS_WITH_DECIMAL(values[i].max_time), NS_TO_MS_WITH_DECIMAL(values[i].min_time), values[i].count, - getName(next_key.reason, EXIT)); + getName(keys[i].reason, EXIT)); } else if (tid == keys[i].tid) { printf("%25s %-12.4f %-12.4f %-12.4f %-12u %-12s\n", "", NS_TO_MS_WITH_DECIMAL(values[i].total_time), NS_TO_MS_WITH_DECIMAL(values[i].max_time), NS_TO_MS_WITH_DECIMAL(values[i].min_time), values[i].count, - getName(next_key.reason, EXIT)); + getName(keys[i].reason, EXIT)); } } // clear the maps