এই পৃষ্ঠায় সুরক্ষিত কার্নেল-ভিত্তিক ভার্চুয়াল মেশিন (pKVM) ভেন্ডর মডিউল কীভাবে প্রয়োগ করতে হয় তা ব্যাখ্যা করা হয়েছে।
android16-6.12 এবং তার পরের যেকোনও ভার্সনের ক্ষেত্রে, এইসব ধাপ সম্পূর্ণ করার পরে, আপনার কাছে এই ধরনের ডিরেক্টরি ট্রি থাকা উচিত:
BUILD.bazel
el1.c
hyp/
BUILD.bazel
el2.c
সম্পূর্ণ উদাহরণের জন্য, DDK-এর মাধ্যমে pKVM মডিউল তৈরি করুন লিঙ্ক দেখুন।
android15-6.6 এবং এর আগের যেকোনও ভার্সনের জন্য:
Makefile
el1.c
hyp/
Makefile
el2.c
EL2 হাইপারভাইজার কোড (
el2.c) যোগ করুন। ন্যূনতম, এই কোডটিকেpkvm_module_opsstruct-এর রেফারেন্স গ্রহণকারী একটি init ফাংশন ঘোষণা করতে হবে:#include <asm/kvm_pkvm_module.h> int pkvm_driver_hyp_init(const struct pkvm_module_ops *ops) { /* Init the EL2 code */ return 0; }pKVM ভেন্ডর মডিউল API হল pKVM হাইপারভাইজরে কলব্যাক এনক্যাপসুলেট করা একটি স্ট্রাক্ট। এই স্ট্রাকচার GKI ইন্টারফেসের মতো একই ABI নিয়ম মেনে চলে।
হাইপারভাইজার কোড তৈরি করতে
hyp/Makefileতৈরি করুন:hyp-obj-y := el2.o include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.moduleEL1 কার্নেল কোড (
el1.c) যোগ করুন। এই কোডের ইনিট বিভাগে অবশ্যইpkvm_load_el2 module-এ কল থাকতে হবে, যাতে ধাপ ১ থেকে EL2 হাইপারভাইজার কোড লোড করা যায়।#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);বিল্ড সংক্রান্ত নিয়ম তৈরি করুন।
android16-6.12 এবং এর পরবর্তী যেকোনও ভার্সনের জন্য, DDK-এর সাহায্যে pKVM মডিউল তৈরি করুন লিঙ্ক দেখুন, যাতে EL2-এর জন্য
ddk_library()এবং EL1-এর জন্যddk_module()তৈরি করা যায়।android15-6.6 এবং এর আগের ভার্সনের জন্য, EL1 এবং 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
pKVM মডিউল লোড করুন
GKI ভেন্ডর মডিউলের মতো, pKVM ভেন্ডর মডিউলও modprobe ব্যবহার করে লোড করা যেতে পারে।
তবে, নিরাপত্তার কারণে, বিশেষ সুবিধা সরিয়ে নেওয়ার আগে লোডিং অবশ্যই হতে হবে।
pKVM মডিউল লোড করতে, আপনাকে অবশ্যই নিশ্চিত করতে হবে যে আপনার মডিউলগুলি
রুট ফাইল সিস্টেমে (initramfs) অন্তর্ভুক্ত করা হয়েছে এবং আপনাকে অবশ্যই নিম্নলিখিতগুলি আপনার
কার্নেল কমান্ড-লাইনে যোগ করতে হবে:
kvm-arm.protected_modules=mod1,mod2,mod3,...
initramfs-এ স্টোর করা pKVM ভেন্ডর মডিউল initramfs-এর সিগনেচার ও সুরক্ষা ইনহেরিট করে।
pKVM ভেন্ডর মডিউলগুলির মধ্যে একটি লোড করা না গেলে, সিস্টেমটি সুরক্ষিত নয় বলে মনে করা হয় এবং সুরক্ষিত ভার্চুয়াল মেশিন চালু করা সম্ভব হবে না।
EL1 (কার্নেল মডিউল) থেকে EL2 (হাইপারভাইজার) ফাংশন কল করা
হাইপারভাইজার কল (HVC) হল এমন একটি নির্দেশ যা কার্নেলকে হাইপারভাইজার কল করার অনুমতি দেয়। pKVM ভেন্ডর মডিউল চালু করার ফলে, HVC-কে EL1 (কার্নেল মডিউল) থেকে EL2 (হাইপারভাইজার মডিউল)-এ ফাংশন চালানোর জন্য কল করতে ব্যবহার করা যেতে পারে:
- EL2 কোডে (
el2.c), EL2 হ্যান্ডলার ঘোষণা করুন:
Android 14
void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
{
/* Handle the call */
cpu_reg(ctx, 1) = 0;
}
Android 15 বা তার পরবর্তী যেকোনও ভার্সন
void pkvm_driver_hyp_hvc(struct user_pt_regs *regs)
{
/* Handle the call */
regs->regs[0] = SMCCC_RET_SUCCESS;
regs->regs[1] = 0;
}
আপনার EL1 কোডে (
el1.c), pKVM ভেন্ডর মডিউলে EL2 হ্যান্ডলার রেজিস্টার করুন: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);আপনার EL1 কোডে (
el1.c), HVC-কে কল করুন:pkvm_el2_mod_call(hvc_number);
EL2 কোড ডিবাগ ও প্রোফাইল করা
এই বিভাগে pKVM মডিউল EL2 কোড ডিবাগ করার জন্য একাধিক বিকল্প রয়েছে।
হাইপারভাইজার ট্রেস ইভেন্ট এমিট ও পড়া
Tracefs-এ pKVM হাইপারভাইজর কাজ করে। ইন্টারফেসের অ্যাক্সেস রুট ব্যবহারকারীর কাছে থাকে,
যা /sys/kernel/tracing/hypervisor/-এ থাকে:
tracing_on: ট্রেসিং চালু বা বন্ধ করে।trace: এই ফাইলে লিখলে ট্রেস রিসেট হয়ে যায়।trace_pipe: এই ফাইলটি পড়লে হাইপারভাইজার ইভেন্ট প্রিন্ট হয়।buffer_size_kb: ইভেন্ট হোল্ড করা প্রতি-সিপিইউ বাফারের সাইজ। ইভেন্ট মিস হয়ে গেলে এই ভ্যালু বাড়ান।
সাধারণত, ইভেন্ট বন্ধ করা থাকে। ইভেন্ট চালু করতে, Tracefs-এ সংশ্লিষ্ট
/sys/kernel/tracing/hypervisor/events/my_event/enable ফাইল ব্যবহার করুন। এছাড়াও, আপনি
hyp_event=event1,event2-এর কার্নেল কমান্ড-লাইন ব্যবহার করে বুট করার সময় যেকোনও হাইপারভাইজার ইভেন্ট চালু করতে
পারেন।
কোনও ইভেন্ট ঘোষণা করার আগে, মডিউলের EL2 কোডকে অবশ্যই নিম্নলিখিত বয়লারপ্লেট ঘোষণা করতে হবে, যেখানে pkvm_ops হল struct pkvm_module_ops *
যা মডিউল init ফাংশনে পাস করা হয়েছে:
#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
ইভেন্ট ঘোষণা করা
নিজস্ব .h ফাইলে ইভেন্ট ঘোষণা করুন:
$ 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
ইভেন্ট এমিট করা
জেনারেট করা C ফাংশনকে কল করে আপনি EL2 কোডে ইভেন্ট লগ করতে পারবেন:
trace_pkvm_driver_event(id);
অতিরিক্ত রেজিস্ট্রেশন যোগ করা (Android 15 বা তার আগের যেকোনও ভার্সন)
Android 15 ও এর আগের যেকোনও ভার্সনের জন্য, মডিউল ইনিশিয়ালাইজেশনের সময় অতিরিক্ত রেজিস্ট্রেশন অন্তর্ভুক্ত করুন। Android 16 ও তার পরবর্তী যেকোনও ভার্সনে এটি প্রয়োজন হয় না।
#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;
}
আগে ঘোষণা না করেই ইভেন্ট এমিট করা (Android 16 ও তার পরবর্তী যেকোনও ভার্সন)
দ্রুত ডিবাগিংয়ের জন্য ইভেন্ট ঘোষণা করা কষ্টকর হতে পারে। trace_hyp_printk()
কলারকে কোনও
ইভেন্ট ঘোষণা ছাড়াই ফর্ম্যাট স্ট্রিংয়ে সর্বাধিক চারটি আর্গুমেন্ট পাস করতে দেয়:
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);
এছাড়াও, EL2 কোডে একটি বয়লারপ্লেট প্রয়োজন। trace_hyp_printk() হল একটি ম্যাক্রো
যা 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
/sys/kernel/tracing/hypervisor/events/-এ __hyp_printk ইভেন্ট চালু করুন অথবা
কার্নেল কমান্ড-লাইন hyp_event=__hyp_printk-এর মাধ্যমে বুট করার সময় চালু করুন।
dmesg-এ ইভেন্ট রিডাইরেক্ট করা
hyp_trace_printk=1 কার্নেল কমান্ড-লাইন প্যারামিটার হাইপারভাইজারের
ট্রেসিং ইন্টারফেসকে প্রতিটি লগ করা ইভেন্ট কার্নেলের dmesg-এ ফরওয়ার্ড করে। trace_pipe অ্যাক্সেস করা না গেলে ইভেন্ট পড়ার জন্য এটি
উপযোগী।
কার্নেল প্যানিক চলাকালীন ডাম্প ইভেন্ট (Android 16 ও তার পরবর্তী যেকোনও ভার্সন)
হাইপারভাইজার ইভেন্ট পোল করা হয়। তাই, শেষ পোল
এবং কার্নেল প্যানিকের মধ্যে একটি উইন্ডো আছে যেখানে ইভেন্ট নির্গমন করা হয়েছে কিন্তু কনসোলে ডাম্প করা হয়নি।
hyp_trace_printk চালু করা থাকলে, কার্নেল কনফিগারেশন বিকল্প CONFIG_PKVM_DUMP_TRACE_ON_PANIC কনসোলে
সবচেয়ে সাম্প্রতিক ইভেন্ট ডাম্প করার চেষ্টা করে।
GKI-এর জন্য এই বিকল্প ডিফল্ট হিসেবে বন্ধ করা থাকে।
ফাংশন কল ও রিটার্ন ট্রেস করতে Ftrace ব্যবহার করা (Android 16 ও তার পরের যেকোনও ভার্সন)
Ftrace হল একটি কার্নেল ফিচার যা আপনাকে প্রতিটি ফাংশন কল এবং রিটার্ন ট্রেস করতে দেয়।
একইভাবে, pKVM হাইপারভাইজার দুটি ইভেন্ট অফার করে func এবং
func_ret।
আপনি কার্নেল কমান্ড-লাইন
hyp_ftrace_filter= বা tracefs ফাইলের মধ্যে একটির সাহায্যে ট্রেস করা ফাংশন বেছে নিতে পারবেন:
/sys/kernel/tracing/hypervisor/set_ftrace_filter/sys/kernel/tracing/hypervisor/set_ftrace_notrace
ফিল্টার শেল-স্টাইল গ্লোব ম্যাচিং ব্যবহার করে।
নিম্নলিখিত ফিল্টারটি pkvm_hyp_driver দিয়ে শুরু হওয়া ফাংশনগুলি ট্রেস করে:
echo "__kvm_nvhe_pkvm_hyp_driver*" > /sys/kernel/tracing/hypervisor/set_ftrace_filter
func ও func_ret ইভেন্ট শুধুমাত্র CONFIG_PKVM_FTRACE=y-এর সাথে উপলভ্য।
GKI-এর জন্য এই বিকল্প ডিফল্ট হিসেবে বন্ধ করা থাকে।