Skip to the content.

似乎关联的源码

sys_insn_descs 也是需要仔细看看的了

sys_reg_descs

首先,这是 sys reg 的标准定义:

#define SYS_PMMIR_EL1			sys_reg(3, 0, 9, 14, 6)
static const struct sys_reg_desc sys_reg_descs[] = {
	// ...
	{ SYS_DESC(SYS_CTR_EL0), access_ctr },
  	// 通过 ioctl 访问,那么就是走默认的,
  	// 因为没有定义 get_user 和 set_user
  	// ...
	{ SYS_DESC(SYS_PMMIR_EL1), trap_raz_wi },
}
struct sys_reg_desc {
	/* Sysreg string for debug */
	const char *name;

	/* MRS/MSR instruction which accesses it. */
	u8	Op0;
	u8	Op1;
	u8	CRn;
	u8	CRm;
	u8	Op2;

	/* Trapped access from guest, if non-NULL. */
	bool (*access)(struct kvm_vcpu *,
		       struct sys_reg_params *,
		       const struct sys_reg_desc *);

	/* Initialization for vcpu. */
	void (*reset)(struct kvm_vcpu *, const struct sys_reg_desc *);

	/* Index into sys_reg[], or 0 if we don't need to save it. */
	int reg;

	/* Value (usually reset value) */
	u64 val;

	/* Custom get/set_user functions, fallback to generic if NULL */
	int (*get_user)(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
			const struct kvm_one_reg *reg, void __user *uaddr);
	int (*set_user)(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
			const struct kvm_one_reg *reg, void __user *uaddr);

	/* Return mask of REG_* runtime visibility overrides */
	unsigned int (*visibility)(const struct kvm_vcpu *vcpu,
				   const struct sys_reg_desc *rd);
};

正如注释显示的那样:

从虚拟机访问 sysreg

static exit_handle_fn arm_exit_handlers[] = {
	[ESR_ELx_EC_CP15_32]	= kvm_handle_cp15_32,
	[ESR_ELx_EC_CP15_64]	= kvm_handle_cp15_64,
	[ESR_ELx_EC_CP14_MR]	= kvm_handle_cp14_32,
	[ESR_ELx_EC_CP14_LS]	= kvm_handle_cp14_load_store,
	[ESR_ELx_EC_CP10_ID]	= kvm_handle_cp10_id,
	[ESR_ELx_EC_CP14_64]	= kvm_handle_cp14_64,

	// ...
	[ESR_ELx_EC_SYS64]	= kvm_handle_sys_reg,

(这里自然有一个问题,为什么 cp 从有一个单独的 exit handler)

除去 sys_reg_descs 之外,还有 cp14_regs cp15_regs cp14_64_regs cp15_64_regs

[!NOTE] 参考 Deepseeek ,有待验证

sys_reg_descs: 這是最通用的AArch64系統暫存器描述表。它包含了標準的64位元系統暫存器定義,這些暫存器可供虛擬機監視器(VMM)進行模擬。 cp14_regs 和 cp15_regs: 這些表格定義了ARM架構中的協處理器14(CP14)和協處理器15(CP15)的32位元暫存器。在ARMv7及更早的架構中,系統控制功能主要透過協處理器來實現。 cp14_64_regs 和 cp15_64_regs: 這兩張表則定義了CP14和CP15的64位元暫存器,主要用於AArch64架構。 sys_insn_descs: 這張表與前述不同,它描述的是系統 指令 而非暫存器。

ARMv7-A/R架構中,CP14和CP15是用於系統控制的協處理器。

隨著ARM架構演進到AArch64,許多原本由CP14和CP15提供的功能被整合到系統暫存器中。 然而,為了相容性和擴展性,部分64位元的CP14和CP15暫存器依然存在。cp14_64_regs和cp15_64_regs這兩個表格就是用來定義這些64位元暫存器的描述,以便在AArch64模式下進行模擬

與其他表格不同,sys_insn_descs是用來描述系統 指令 的,例如TLB(Translation Lookaside Buffer)管理相關的指令。當客戶端執行這些特定的系統指令時,KVM會攔截它們,並在sys_insn_descs中查找相應的描述,以執行模擬的操作。這對於虛擬化記憶體管理至關重要。

一共定义了这些 table

int __init kvm_sys_reg_table_init(void)
{
  // ...
	valid &= check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs), false);
	valid &= check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs), true);
	valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), true);
	valid &= check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), true);
	valid &= check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), true);
	valid &= check_sysreg_table(sys_insn_descs, ARRAY_SIZE(sys_insn_descs), false);
  // ...
}

原来 arm 也是存在 coprocess 的概念:

arm64_ftr_regs

sys_reg_desc::val 和 struct arm64_ftr_reg 中的各个 value 是什么关系?

static void get_ctr_el0(struct kvm_vcpu *v, const struct sys_reg_desc *r)
{
	((struct sys_reg_desc *)r)->val = read_sanitised_ftr_reg(SYS_CTR_EL0);
}
#define ID_DESC_DEFAULT_CALLBACKS		\
	.access	= access_id_reg,		\
	.get_user = get_id_reg,			\
	.set_user = set_id_reg,			\
	.visibility = id_visibility,		\
	.reset = kvm_read_sanitised_id_reg

也就是默认为 kvm_read_sanitised_id_reg

这里的代码:

u64 read_sanitised_ftr_reg(u32 id)
{
	struct arm64_ftr_reg *regp = get_arm64_ftr_reg(id);

	if (!regp)
		return 0;
	return regp->sys_val;
}

read_sanitised_ftr_reg 的如何初始化的

似乎发起修改的地方只有 : init_cpu_ftr_reg

static const struct __ftr_reg_entry {
	u32			sys_id;
	struct arm64_ftr_reg 	*reg;
} arm64_ftr_regs[] = {
struct arm64_ftr_reg arm64_ftr_reg_ctrel0 = {
	.name		= "SYS_CTR_EL0",
	.ftr_bits	= ftr_ctr,
	.override	= &no_override,
};

sys_id 是什么东西?

例如可以看到:

struct arm64_ftr_reg *regp = get_arm64_ftr_reg(sys_id);

从 sys_reg_desc 到 sys_id 的转换:

	const struct sys_reg_desc *rd,
	u32 id = reg_to_encoding(rd);

	#define reg_to_encoding(x)						\
	sys_reg((u32)(x)->Op0, (u32)(x)->Op1,				\
		(u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2)

从一个 SYS_ID 的 macro 到 sys_reg 的转换:

#define SYS_ID_DFR0_EL1                                 sys_reg(3, 0, 0, 1, 2)

access_id_reg 和 access_vm_reg 是什么意思?

(容易,以后再来)

RAZ 的细节考虑

参考: https://developer.arm.com/documentation/aeg0014/g/Glossary

  1. RAZ : read as zero
  2. WI : write ignore
#define REG_HIDDEN		(1 << 0) /* hidden from userspace and guest */
#define REG_RAZ			(1 << 1) /* RAZ from userspace and guest */
#define REG_USER_WI		(1 << 2) /* WI from userspace only */
static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
				  const struct sys_reg_desc *r)
{
	u32 id = reg_to_encoding(r);

	switch (id) {
	case SYS_ID_AA64ZFR0_EL1:
		if (!vcpu_has_sve(vcpu))
			return REG_RAZ;
		break;
	}

	return 0;
}

继续总结一下吧,感觉还不是很清晰

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。