Halaman ini menjelaskan cara menerapkan modul vendor virtual machine berbasis kernel (pKVM) yang dilindungi.
Untuk android16-6.12 dan yang lebih baru, setelah menyelesaikan langkah-langkah ini, Anda akan memiliki pohon direktori yang mirip dengan:
BUILD.bazel
el1.c
hyp/
BUILD.bazel
el2.c
Untuk contoh lengkapnya, lihat Membangun modul pKVM dengan DDK .
Untuk android15-6.6 dan yang lebih lama:
Makefile
el1.c
hyp/
Makefile
el2.c
Tambahkan kode hypervisor EL2 (
el2.c). Setidaknya, kode ini harus mendeklarasikan fungsi init yang menerima referensi ke structpkvm_module_ops:#include <asm/kvm_pkvm_module.h> int pkvm_driver_hyp_init(const struct pkvm_module_ops *ops) { /* Init the EL2 code */ return 0; }API modul vendor pKVM adalah struct yang merangkum callback ke hypervisor pKVM. Struktur ini mengikuti aturan ABI yang sama dengan antarmuka GKI.
Buat
hyp/Makefileuntuk membangun kode hypervisor:hyp-obj-y := el2.o include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.moduleTambahkan kode kernel EL1 (
el1.c). Bagian init kode ini harus berisi panggilan kepkvm_load_el2 moduleuntuk memuat kode hypervisor EL2 dari langkah 1.#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <asm/kvm_pkvm_module.h> int __kvm_nvhe_pkvm_driver_hyp_init(const struct pkvm_module_ops *ops); static int __init pkvm_driver_init(void) { unsigned long token; return pkvm_load_el2_module(__kvm_nvhe_pkvm_driver_hyp_init, &token); } module_init(pkvm_driver_init);Buat aturan build.
Untuk android16-6.12 dan yang lebih baru, lihat Membangun modul pKVM dengan DDK untuk membuat
ddk_library()untuk EL2 danddk_module()untuk EL1.Untuk android15-6.6 dan yang lebih lama, buat file makefile root untuk mengikat kode EL1 dan EL2:
ifneq ($(KERNELRELEASE),) clean-files := hyp/hyp.lds hyp/hyp-reloc.S obj-m := pkvm_module.o pkvm_module-y := el1.o hyp/kvm_nvhe.o $(PWD)/hyp/kvm_nvhe.o: FORCE $(Q)$(MAKE) $(build)=$(obj)/hyp $(obj)/hyp/kvm_nvhe.o else all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean endif
Memuat modul pKVM
Seperti modul vendor GKI, modul vendor pKVM dapat dimuat menggunakan modprobe.
Namun, karena alasan keamanan, pemuatan harus terjadi sebelum penghapusan hak istimewa.
Untuk memuat modul pKVM, Anda harus memastikan modul disertakan dalam
sistem file root (initramfs) dan Anda harus menambahkan kode berikut ke
command line kernel:
kvm-arm.protected_modules=mod1,mod2,mod3,...
Modul vendor pKVM yang disimpan di initramfs mewarisi tanda tangan dan perlindungan initramfs.
Jika salah satu modul vendor pKVM gagal dimuat, sistem dianggap tidak aman dan mesin virtual yang dilindungi tidak dapat dimulai.
Memanggil fungsi EL2 (hypervisor) dari EL1 (modul kernel)
Panggilan hypervisor (HVC) adalah instruksi yang memungkinkan kernel memanggil hypervisor. Dengan diperkenalkannya modul vendor pKVM, HVC dapat digunakan untuk memanggil fungsi agar berjalan di EL2 (dalam modul hypervisor) dari EL1 (modul kernel):
- Dalam kode EL2 (
el2.c), deklarasikan pengendali EL2:
Android 14
void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
{
/* Handle the call */
cpu_reg(ctx, 1) = 0;
}
Android 15 atau yang lebih tinggi
void pkvm_driver_hyp_hvc(struct user_pt_regs *regs)
{
/* Handle the call */
regs->regs[0] = SMCCC_RET_SUCCESS;
regs->regs[1] = 0;
}
Dalam kode EL1 Anda (
el1.c), daftarkan handler EL2 di modul vendor pKVM Anda:int __kvm_nvhe_pkvm_driver_hyp_init(const struct pkvm_module_ops *ops); void __kvm_nvhe_pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx); // Android14 void __kvm_nvhe_pkvm_driver_hyp_hvc(struct user_pt_regs *regs); // Android15 static int hvc_number; static int __init pkvm_driver_init(void) { long token; int ret; ret = pkvm_load_el2_module(__kvm_nvhe_pkvm_driver_hyp_init,token); if (ret) return ret; ret = pkvm_register_el2_mod_call(__kvm_nvhe_pkvm_driver_hyp_hvc, token) if (ret < 0) return ret; hvc_number = ret; return 0; } module_init(pkvm_driver_init);Dalam kode EL1 (
el1.c), panggil HVC:pkvm_el2_mod_call(hvc_number);
Men-debug dan memprofilkan kode EL2
Bagian ini berisi beberapa opsi untuk men-debug kode EL2 modul pKVM.
Memancarkan dan membaca peristiwa rekaman aktivitas hypervisor
Tracefs mendukung hypervisor pKVM. Pengguna root memiliki akses ke antarmuka,
yang berada di /sys/kernel/tracing/hypervisor/:
tracing_on: Mengaktifkan atau menonaktifkan pelacakan.trace: Menulis ke file ini akan mereset rekaman aktivitas.trace_pipe: Membaca file ini akan mencetak peristiwa hypervisor.buffer_size_kb: Ukuran buffer per-CPU yang menyimpan peristiwa. Tingkatkan nilai ini jika peristiwa hilang.
Secara default, peristiwa dinonaktifkan. Untuk mengaktifkan peristiwa, gunakan file /sys/kernel/tracing/hypervisor/events/my_event/enable yang sesuai di Tracefs. Anda juga dapat mengaktifkan peristiwa hypervisor apa pun saat waktu booting dengan command line kernel hyp_event=event1,event2.
Sebelum mendeklarasikan peristiwa, kode EL2 modul harus mendeklarasikan boilerplate berikut, dengan pkvm_ops adalah struct pkvm_module_ops *
yang diteruskan ke fungsi init modul:
#include "events.h"
#define HYP_EVENT_FILE ../../../../relative/path/to/hyp/events.h
#include <nvhe/define_events.h>
#ifdef CONFIG_TRACING
void *tracing_reserve_entry(unsigned long length)
{
return pkvm_ops->tracing_reserve_entry(length);
}
void tracing_commit_entry(void)
{
pkvm_ops->tracing_commit_entry();
}
#endif
Mendeklarasikan acara
Deklarasikan peristiwa dalam file .h-nya sendiri:
$ cat hyp/events.h
#if !defined(__PKVM_DRIVER_HYPEVENTS_H_) || defined(HYP_EVENT_MULTI_READ)
#define __PKVM_DRIVER_HYPEVENTS_H_
#ifdef __KVM_NVHE_HYPERVISOR__
#include <nvhe/trace.h>
#endif
HYP_EVENT(pkvm_driver_event,
HE_PROTO(u64 id),
HE_STRUCT(
he_field(u64, id)
),
HE_ASSIGN(
__entry->id = id;
),
HE_PRINTK("id=0x%08llx", __entry->id)
);
#endif
Memancarkan peristiwa
Anda dapat mencatat peristiwa dalam kode EL2 dengan memanggil fungsi C yang dihasilkan:
trace_pkvm_driver_event(id);
Menambahkan pendaftaran tambahan (Android 15 atau yang lebih rendah)
Untuk Android 15 dan yang lebih rendah, sertakan pendaftaran tambahan selama inisialisasi modul. Hal ini tidak diperlukan di Android 16 dan yang lebih tinggi.
#ifdef CONFIG_TRACING
extern char __hyp_event_ids_start[];
extern char __hyp_event_ids_end[];
#endif
int pkvm_driver_hyp_init(const struct pkvm_module_ops *ops)
{
#ifdef CONFIG_TRACING
ops->register_hyp_event_ids((unsigned long)__hyp_event_ids_start,
(unsigned long)__hyp_event_ids_end);
#endif
/* init module ... */
return 0;
}
Memancarkan peristiwa tanpa deklarasi sebelumnya (Android 16 dan yang lebih tinggi)
Mendeklarasikan peristiwa bisa merepotkan untuk proses debug cepat. trace_hyp_printk()
memungkinkan pemanggil meneruskan hingga empat argumen ke string format tanpa
deklarasi peristiwa:
trace_hyp_printk("This is my debug");
trace_hyp_printk("This is my variable: %d", (int)foo);
trace_hyp_printk("This is my address: 0x%llx", phys);
Boilerplate dalam kode EL2 juga diperlukan. trace_hyp_printk() adalah makro
yang memanggil fungsi trace___hyp_printk():
#include <nvhe/trace.h>
#ifdef CONFIG_TRACING
void trace___hyp_printk(u8 fmt_id, u64 a, u64 b, u64 c, u64 d)
{
pkvm_ops->tracing_mod_hyp_printk(fmt_id, a, b, c, d);
}
#endif
Aktifkan peristiwa __hyp_printk di /sys/kernel/tracing/hypervisor/events/ atau
saat waktu booting dengan command line kernel hyp_event=__hyp_printk.
Mengarahkan peristiwa ke dmesg
Parameter command line kernel hyp_trace_printk=1 membuat antarmuka pelacakan hypervisor meneruskan setiap peristiwa yang dicatat ke dmesg kernel. Hal ini berguna untuk membaca peristiwa saat trace_pipe tidak dapat diakses.
Mengekspor peristiwa selama kernel panik (Android 16 dan yang lebih baru)
Peristiwa hypervisor di-polling. Oleh karena itu, ada jeda antara polling terakhir
dan kernel panic saat peristiwa telah dipancarkan, tetapi tidak di-dump ke konsol.
Opsi konfigurasi kernel CONFIG_PKVM_DUMP_TRACE_ON_PANIC mencoba mencatat peristiwa terbaru di konsol jika hyp_trace_printk telah diaktifkan.
Opsi ini dinonaktifkan secara default untuk GKI.
Menggunakan Ftrace untuk melacak panggilan dan kembalinya fungsi (Android 16 dan yang lebih tinggi)
Ftrace adalah fitur kernel yang memungkinkan Anda melacak setiap panggilan dan kembalinya fungsi.
Dengan cara yang sama, hypervisor pKVM menawarkan dua peristiwa func dan
func_ret.
Anda dapat memilih fungsi yang dilacak dengan command line kernel
hyp_ftrace_filter= atau dengan salah satu file tracefs:
/sys/kernel/tracing/hypervisor/set_ftrace_filter/sys/kernel/tracing/hypervisor/set_ftrace_notrace
Filter menggunakan pencocokan glob gaya shell.
Filter berikut melacak fungsi yang dimulai dengan
pkvm_hyp_driver:
echo "__kvm_nvhe_pkvm_hyp_driver*" > /sys/kernel/tracing/hypervisor/set_ftrace_filter
Acara func dan func_ret hanya tersedia dengan CONFIG_PKVM_FTRACE=y.
Opsi ini dinonaktifkan secara default untuk GKI.