Skip to content

Commit fedc612

Browse files
author
Marc Zyngier
committed
KVM: arm64: nv: Handle virtual EL2 registers in vcpu_read/write_sys_reg()
KVM internally uses accessor functions when reading or writing the guest's system registers. This takes care of accessing either the stored copy or using the "live" EL1 system registers when the host uses VHE. With the introduction of virtual EL2 we add a bunch of EL2 system registers, which now must also be taken care of: - If the guest is running in vEL2, and we access an EL1 sysreg, we must revert to the stored version of that, and not use the CPU's copy. - If the guest is running in vEL1, and we access an EL2 sysreg, we must also use the stored version, since the CPU carries the EL1 copy. - Some EL2 system registers are supposed to affect the current execution of the system, so we need to put them into their respective EL1 counterparts. For this we need to define a mapping between the two. - Some EL2 system registers have a different format than their EL1 counterpart, so we need to translate them before writing them to the CPU. This is done using an (optional) translate function in the map. All of these cases are now wrapped into the existing accessor functions, so KVM users wouldn't need to care whether they access EL2 or EL1 registers and also which state the guest is in. Reviewed-by: Ganapatrao Kulkarni <gankulkarni@os.amperecomputing.com> Reviewed-by: Alexandru Elisei <alexandru.elisei@arm.com> Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> Reviewed-by: Oliver Upton <oliver.upton@linux.dev> Co-developed-by: Andre Przywara <andre.przywara@arm.com> Signed-off-by: Andre Przywara <andre.przywara@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org>
1 parent d8bd48e commit fedc612

2 files changed

Lines changed: 126 additions & 5 deletions

File tree

arch/arm64/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,7 @@ static inline bool __vcpu_read_sys_reg_from_cpu(int reg, u64 *val)
907907
case AMAIR_EL1: *val = read_sysreg_s(SYS_AMAIR_EL12); break;
908908
case CNTKCTL_EL1: *val = read_sysreg_s(SYS_CNTKCTL_EL12); break;
909909
case ELR_EL1: *val = read_sysreg_s(SYS_ELR_EL12); break;
910+
case SPSR_EL1: *val = read_sysreg_s(SYS_SPSR_EL12); break;
910911
case PAR_EL1: *val = read_sysreg_par(); break;
911912
case DACR32_EL2: *val = read_sysreg_s(SYS_DACR32_EL2); break;
912913
case IFSR32_EL2: *val = read_sysreg_s(SYS_IFSR32_EL2); break;
@@ -951,6 +952,7 @@ static inline bool __vcpu_write_sys_reg_to_cpu(u64 val, int reg)
951952
case AMAIR_EL1: write_sysreg_s(val, SYS_AMAIR_EL12); break;
952953
case CNTKCTL_EL1: write_sysreg_s(val, SYS_CNTKCTL_EL12); break;
953954
case ELR_EL1: write_sysreg_s(val, SYS_ELR_EL12); break;
955+
case SPSR_EL1: write_sysreg_s(val, SYS_SPSR_EL12); break;
954956
case PAR_EL1: write_sysreg_s(val, SYS_PAR_EL1); break;
955957
case DACR32_EL2: write_sysreg_s(val, SYS_DACR32_EL2); break;
956958
case IFSR32_EL2: write_sysreg_s(val, SYS_IFSR32_EL2); break;

arch/arm64/kvm/sys_regs.c

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,143 @@ static bool write_to_read_only(struct kvm_vcpu *vcpu,
7272
"sys_reg write to read-only register");
7373
}
7474

75+
#define PURE_EL2_SYSREG(el2) \
76+
case el2: { \
77+
*el1r = el2; \
78+
return true; \
79+
}
80+
81+
#define MAPPED_EL2_SYSREG(el2, el1, fn) \
82+
case el2: { \
83+
*xlate = fn; \
84+
*el1r = el1; \
85+
return true; \
86+
}
87+
88+
static bool get_el2_to_el1_mapping(unsigned int reg,
89+
unsigned int *el1r, u64 (**xlate)(u64))
90+
{
91+
switch (reg) {
92+
PURE_EL2_SYSREG( VPIDR_EL2 );
93+
PURE_EL2_SYSREG( VMPIDR_EL2 );
94+
PURE_EL2_SYSREG( ACTLR_EL2 );
95+
PURE_EL2_SYSREG( HCR_EL2 );
96+
PURE_EL2_SYSREG( MDCR_EL2 );
97+
PURE_EL2_SYSREG( HSTR_EL2 );
98+
PURE_EL2_SYSREG( HACR_EL2 );
99+
PURE_EL2_SYSREG( VTTBR_EL2 );
100+
PURE_EL2_SYSREG( VTCR_EL2 );
101+
PURE_EL2_SYSREG( RVBAR_EL2 );
102+
PURE_EL2_SYSREG( TPIDR_EL2 );
103+
PURE_EL2_SYSREG( HPFAR_EL2 );
104+
PURE_EL2_SYSREG( CNTHCTL_EL2 );
105+
MAPPED_EL2_SYSREG(SCTLR_EL2, SCTLR_EL1,
106+
translate_sctlr_el2_to_sctlr_el1 );
107+
MAPPED_EL2_SYSREG(CPTR_EL2, CPACR_EL1,
108+
translate_cptr_el2_to_cpacr_el1 );
109+
MAPPED_EL2_SYSREG(TTBR0_EL2, TTBR0_EL1,
110+
translate_ttbr0_el2_to_ttbr0_el1 );
111+
MAPPED_EL2_SYSREG(TTBR1_EL2, TTBR1_EL1, NULL );
112+
MAPPED_EL2_SYSREG(TCR_EL2, TCR_EL1,
113+
translate_tcr_el2_to_tcr_el1 );
114+
MAPPED_EL2_SYSREG(VBAR_EL2, VBAR_EL1, NULL );
115+
MAPPED_EL2_SYSREG(AFSR0_EL2, AFSR0_EL1, NULL );
116+
MAPPED_EL2_SYSREG(AFSR1_EL2, AFSR1_EL1, NULL );
117+
MAPPED_EL2_SYSREG(ESR_EL2, ESR_EL1, NULL );
118+
MAPPED_EL2_SYSREG(FAR_EL2, FAR_EL1, NULL );
119+
MAPPED_EL2_SYSREG(MAIR_EL2, MAIR_EL1, NULL );
120+
MAPPED_EL2_SYSREG(AMAIR_EL2, AMAIR_EL1, NULL );
121+
MAPPED_EL2_SYSREG(ELR_EL2, ELR_EL1, NULL );
122+
MAPPED_EL2_SYSREG(SPSR_EL2, SPSR_EL1, NULL );
123+
default:
124+
return false;
125+
}
126+
}
127+
75128
u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg)
76129
{
77130
u64 val = 0x8badf00d8badf00d;
131+
u64 (*xlate)(u64) = NULL;
132+
unsigned int el1r;
133+
134+
if (!vcpu_get_flag(vcpu, SYSREGS_ON_CPU))
135+
goto memory_read;
78136

79-
if (vcpu_get_flag(vcpu, SYSREGS_ON_CPU) &&
80-
__vcpu_read_sys_reg_from_cpu(reg, &val))
137+
if (unlikely(get_el2_to_el1_mapping(reg, &el1r, &xlate))) {
138+
if (!is_hyp_ctxt(vcpu))
139+
goto memory_read;
140+
141+
/*
142+
* If this register does not have an EL1 counterpart,
143+
* then read the stored EL2 version.
144+
*/
145+
if (reg == el1r)
146+
goto memory_read;
147+
148+
/*
149+
* If we have a non-VHE guest and that the sysreg
150+
* requires translation to be used at EL1, use the
151+
* in-memory copy instead.
152+
*/
153+
if (!vcpu_el2_e2h_is_set(vcpu) && xlate)
154+
goto memory_read;
155+
156+
/* Get the current version of the EL1 counterpart. */
157+
WARN_ON(!__vcpu_read_sys_reg_from_cpu(el1r, &val));
158+
return val;
159+
}
160+
161+
/* EL1 register can't be on the CPU if the guest is in vEL2. */
162+
if (unlikely(is_hyp_ctxt(vcpu)))
163+
goto memory_read;
164+
165+
if (__vcpu_read_sys_reg_from_cpu(reg, &val))
81166
return val;
82167

168+
memory_read:
83169
return __vcpu_sys_reg(vcpu, reg);
84170
}
85171

86172
void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg)
87173
{
88-
if (vcpu_get_flag(vcpu, SYSREGS_ON_CPU) &&
89-
__vcpu_write_sys_reg_to_cpu(val, reg))
174+
u64 (*xlate)(u64) = NULL;
175+
unsigned int el1r;
176+
177+
if (!vcpu_get_flag(vcpu, SYSREGS_ON_CPU))
178+
goto memory_write;
179+
180+
if (unlikely(get_el2_to_el1_mapping(reg, &el1r, &xlate))) {
181+
if (!is_hyp_ctxt(vcpu))
182+
goto memory_write;
183+
184+
/*
185+
* Always store a copy of the write to memory to avoid having
186+
* to reverse-translate virtual EL2 system registers for a
187+
* non-VHE guest hypervisor.
188+
*/
189+
__vcpu_sys_reg(vcpu, reg) = val;
190+
191+
/* No EL1 counterpart? We're done here.? */
192+
if (reg == el1r)
193+
return;
194+
195+
if (!vcpu_el2_e2h_is_set(vcpu) && xlate)
196+
val = xlate(val);
197+
198+
/* Redirect this to the EL1 version of the register. */
199+
WARN_ON(!__vcpu_write_sys_reg_to_cpu(val, el1r));
200+
return;
201+
}
202+
203+
/* EL1 register can't be on the CPU if the guest is in vEL2. */
204+
if (unlikely(is_hyp_ctxt(vcpu)))
205+
goto memory_write;
206+
207+
if (__vcpu_write_sys_reg_to_cpu(val, reg))
90208
return;
91209

92-
__vcpu_sys_reg(vcpu, reg) = val;
210+
memory_write:
211+
__vcpu_sys_reg(vcpu, reg) = val;
93212
}
94213

95214
/* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */

0 commit comments

Comments
 (0)