mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-01 07:42:18 +00:00
T3Q is a fucking cunt
* Add KVM feature extensions for NXT core
This commit is contained in:
parent
5b6f2aaaf4
commit
bdc4d96db5
232
virt/kvm/arm/aarch32.c
Normal file
232
virt/kvm/arm/aarch32.c
Normal file
@ -0,0 +1,232 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* (not much of an) Emulation layer for 32bit guests.
|
||||
*
|
||||
* Copyright (C) 2012,2013 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* based on arch/arm/kvm/emulate.c
|
||||
* Copyright (C) 2012 - Virtual Open Systems and Columbia University
|
||||
* Author: Christoffer Dall <c.dall@virtualopensystems.com>
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
#define DFSR_FSC_EXTABT_LPAE 0x10
|
||||
#define DFSR_FSC_EXTABT_nLPAE 0x08
|
||||
#define DFSR_LPAE BIT(9)
|
||||
|
||||
/*
|
||||
* Table taken from ARMv8 ARM DDI0487B-B, table G1-10.
|
||||
*/
|
||||
static const u8 return_offsets[8][2] = {
|
||||
[0] = { 0, 0 }, /* Reset, unused */
|
||||
[1] = { 4, 2 }, /* Undefined */
|
||||
[2] = { 0, 0 }, /* SVC, unused */
|
||||
[3] = { 4, 4 }, /* Prefetch abort */
|
||||
[4] = { 8, 8 }, /* Data abort */
|
||||
[5] = { 0, 0 }, /* HVC, unused */
|
||||
[6] = { 4, 4 }, /* IRQ, unused */
|
||||
[7] = { 4, 4 }, /* FIQ, unused */
|
||||
};
|
||||
|
||||
static bool pre_fault_synchronize(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
preempt_disable();
|
||||
if (kvm_arm_vcpu_loaded(vcpu)) {
|
||||
kvm_arch_vcpu_put(vcpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
preempt_enable();
|
||||
return false;
|
||||
}
|
||||
|
||||
static void post_fault_synchronize(struct kvm_vcpu *vcpu, bool loaded)
|
||||
{
|
||||
if (loaded) {
|
||||
kvm_arch_vcpu_load(vcpu, smp_processor_id());
|
||||
preempt_enable();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When an exception is taken, most CPSR fields are left unchanged in the
|
||||
* handler. However, some are explicitly overridden (e.g. M[4:0]).
|
||||
*
|
||||
* The SPSR/SPSR_ELx layouts differ, and the below is intended to work with
|
||||
* either format. Note: SPSR.J bit doesn't exist in SPSR_ELx, but this bit was
|
||||
* obsoleted by the ARMv7 virtualization extensions and is RES0.
|
||||
*
|
||||
* For the SPSR layout seen from AArch32, see:
|
||||
* - ARM DDI 0406C.d, page B1-1148
|
||||
* - ARM DDI 0487E.a, page G8-6264
|
||||
*
|
||||
* For the SPSR_ELx layout for AArch32 seen from AArch64, see:
|
||||
* - ARM DDI 0487E.a, page C5-426
|
||||
*
|
||||
* Here we manipulate the fields in order of the AArch32 SPSR_ELx layout, from
|
||||
* MSB to LSB.
|
||||
*/
|
||||
static unsigned long get_except32_cpsr(struct kvm_vcpu *vcpu, u32 mode)
|
||||
{
|
||||
u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR);
|
||||
unsigned long old, new;
|
||||
|
||||
old = *vcpu_cpsr(vcpu);
|
||||
new = 0;
|
||||
|
||||
new |= (old & PSR_AA32_N_BIT);
|
||||
new |= (old & PSR_AA32_Z_BIT);
|
||||
new |= (old & PSR_AA32_C_BIT);
|
||||
new |= (old & PSR_AA32_V_BIT);
|
||||
new |= (old & PSR_AA32_Q_BIT);
|
||||
|
||||
// CPSR.IT[7:0] are set to zero upon any exception
|
||||
// See ARM DDI 0487E.a, section G1.12.3
|
||||
// See ARM DDI 0406C.d, section B1.8.3
|
||||
|
||||
new |= (old & PSR_AA32_DIT_BIT);
|
||||
|
||||
// CPSR.SSBS is set to SCTLR.DSSBS upon any exception
|
||||
// See ARM DDI 0487E.a, page G8-6244
|
||||
if (sctlr & BIT(31))
|
||||
new |= PSR_AA32_SSBS_BIT;
|
||||
|
||||
// CPSR.PAN is unchanged unless SCTLR.SPAN == 0b0
|
||||
// SCTLR.SPAN is RES1 when ARMv8.1-PAN is not implemented
|
||||
// See ARM DDI 0487E.a, page G8-6246
|
||||
new |= (old & PSR_AA32_PAN_BIT);
|
||||
if (!(sctlr & BIT(23)))
|
||||
new |= PSR_AA32_PAN_BIT;
|
||||
|
||||
// SS does not exist in AArch32, so ignore
|
||||
|
||||
// CPSR.IL is set to zero upon any exception
|
||||
// See ARM DDI 0487E.a, page G1-5527
|
||||
|
||||
new |= (old & PSR_AA32_GE_MASK);
|
||||
|
||||
// CPSR.IT[7:0] are set to zero upon any exception
|
||||
// See prior comment above
|
||||
|
||||
// CPSR.E is set to SCTLR.EE upon any exception
|
||||
// See ARM DDI 0487E.a, page G8-6245
|
||||
// See ARM DDI 0406C.d, page B4-1701
|
||||
if (sctlr & BIT(25))
|
||||
new |= PSR_AA32_E_BIT;
|
||||
|
||||
// CPSR.A is unchanged upon an exception to Undefined, Supervisor
|
||||
// CPSR.A is set upon an exception to other modes
|
||||
// See ARM DDI 0487E.a, pages G1-5515 to G1-5516
|
||||
// See ARM DDI 0406C.d, page B1-1182
|
||||
new |= (old & PSR_AA32_A_BIT);
|
||||
if (mode != PSR_AA32_MODE_UND && mode != PSR_AA32_MODE_SVC)
|
||||
new |= PSR_AA32_A_BIT;
|
||||
|
||||
// CPSR.I is set upon any exception
|
||||
// See ARM DDI 0487E.a, pages G1-5515 to G1-5516
|
||||
// See ARM DDI 0406C.d, page B1-1182
|
||||
new |= PSR_AA32_I_BIT;
|
||||
|
||||
// CPSR.F is set upon an exception to FIQ
|
||||
// CPSR.F is unchanged upon an exception to other modes
|
||||
// See ARM DDI 0487E.a, pages G1-5515 to G1-5516
|
||||
// See ARM DDI 0406C.d, page B1-1182
|
||||
new |= (old & PSR_AA32_F_BIT);
|
||||
if (mode == PSR_AA32_MODE_FIQ)
|
||||
new |= PSR_AA32_F_BIT;
|
||||
|
||||
// CPSR.T is set to SCTLR.TE upon any exception
|
||||
// See ARM DDI 0487E.a, page G8-5514
|
||||
// See ARM DDI 0406C.d, page B1-1181
|
||||
if (sctlr & BIT(30))
|
||||
new |= PSR_AA32_T_BIT;
|
||||
|
||||
new |= mode;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset)
|
||||
{
|
||||
unsigned long spsr = *vcpu_cpsr(vcpu);
|
||||
bool is_thumb = (spsr & PSR_AA32_T_BIT);
|
||||
u32 return_offset = return_offsets[vect_offset >> 2][is_thumb];
|
||||
u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR);
|
||||
|
||||
*vcpu_cpsr(vcpu) = get_except32_cpsr(vcpu, mode);
|
||||
|
||||
/* Note: These now point to the banked copies */
|
||||
vcpu_write_spsr(vcpu, host_spsr_to_spsr32(spsr));
|
||||
*vcpu_reg32(vcpu, 14) = *vcpu_pc(vcpu) + return_offset;
|
||||
|
||||
/* Branch to exception vector */
|
||||
if (sctlr & (1 << 13))
|
||||
vect_offset += 0xffff0000;
|
||||
else /* always have security exceptions */
|
||||
vect_offset += vcpu_cp15(vcpu, c12_VBAR);
|
||||
|
||||
*vcpu_pc(vcpu) = vect_offset;
|
||||
}
|
||||
|
||||
void kvm_inject_undef32(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
bool loaded = pre_fault_synchronize(vcpu);
|
||||
|
||||
prepare_fault32(vcpu, PSR_AA32_MODE_UND, 4);
|
||||
post_fault_synchronize(vcpu, loaded);
|
||||
}
|
||||
|
||||
/*
|
||||
* Modelled after TakeDataAbortException() and TakePrefetchAbortException
|
||||
* pseudocode.
|
||||
*/
|
||||
static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 vect_offset;
|
||||
u32 *far, *fsr;
|
||||
bool is_lpae;
|
||||
bool loaded;
|
||||
|
||||
loaded = pre_fault_synchronize(vcpu);
|
||||
|
||||
if (is_pabt) {
|
||||
vect_offset = 12;
|
||||
far = &vcpu_cp15(vcpu, c6_IFAR);
|
||||
fsr = &vcpu_cp15(vcpu, c5_IFSR);
|
||||
} else { /* !iabt */
|
||||
vect_offset = 16;
|
||||
far = &vcpu_cp15(vcpu, c6_DFAR);
|
||||
fsr = &vcpu_cp15(vcpu, c5_DFSR);
|
||||
}
|
||||
|
||||
prepare_fault32(vcpu, PSR_AA32_MODE_ABT, vect_offset);
|
||||
|
||||
*far = addr;
|
||||
|
||||
/* Give the guest an IMPLEMENTATION DEFINED exception */
|
||||
is_lpae = (vcpu_cp15(vcpu, c2_TTBCR) >> 31);
|
||||
if (is_lpae) {
|
||||
*fsr = DFSR_LPAE | DFSR_FSC_EXTABT_LPAE;
|
||||
} else {
|
||||
/* no need to shuffle FS[4] into DFSR[10] as its 0 */
|
||||
*fsr = DFSR_FSC_EXTABT_nLPAE;
|
||||
}
|
||||
|
||||
post_fault_synchronize(vcpu, loaded);
|
||||
}
|
||||
|
||||
void kvm_inject_dabt32(struct kvm_vcpu *vcpu, unsigned long addr)
|
||||
{
|
||||
inject_abt32(vcpu, false, addr);
|
||||
}
|
||||
|
||||
void kvm_inject_pabt32(struct kvm_vcpu *vcpu, unsigned long addr)
|
||||
{
|
||||
inject_abt32(vcpu, true, addr);
|
||||
}
|
1180
virt/kvm/arm/arch_timer.c
Normal file
1180
virt/kvm/arm/arch_timer.c
Normal file
File diff suppressed because it is too large
Load Diff
1755
virt/kvm/arm/arm.c
Normal file
1755
virt/kvm/arm/arm.c
Normal file
File diff suppressed because it is too large
Load Diff
140
virt/kvm/arm/hyp/aarch32.c
Normal file
140
virt/kvm/arm/hyp/aarch32.c
Normal file
@ -0,0 +1,140 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Hyp portion of the (not much of an) Emulation layer for 32bit guests.
|
||||
*
|
||||
* Copyright (C) 2012,2013 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* based on arch/arm/kvm/emulate.c
|
||||
* Copyright (C) 2012 - Virtual Open Systems and Columbia University
|
||||
* Author: Christoffer Dall <c.dall@virtualopensystems.com>
|
||||
*/
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
/*
|
||||
* stolen from arch/arm/kernel/opcodes.c
|
||||
*
|
||||
* condition code lookup table
|
||||
* index into the table is test code: EQ, NE, ... LT, GT, AL, NV
|
||||
*
|
||||
* bit position in short is condition code: NZCV
|
||||
*/
|
||||
static const unsigned short cc_map[16] = {
|
||||
0xF0F0, /* EQ == Z set */
|
||||
0x0F0F, /* NE */
|
||||
0xCCCC, /* CS == C set */
|
||||
0x3333, /* CC */
|
||||
0xFF00, /* MI == N set */
|
||||
0x00FF, /* PL */
|
||||
0xAAAA, /* VS == V set */
|
||||
0x5555, /* VC */
|
||||
0x0C0C, /* HI == C set && Z clear */
|
||||
0xF3F3, /* LS == C clear || Z set */
|
||||
0xAA55, /* GE == (N==V) */
|
||||
0x55AA, /* LT == (N!=V) */
|
||||
0x0A05, /* GT == (!Z && (N==V)) */
|
||||
0xF5FA, /* LE == (Z || (N!=V)) */
|
||||
0xFFFF, /* AL always */
|
||||
0 /* NV */
|
||||
};
|
||||
|
||||
/*
|
||||
* Check if a trapped instruction should have been executed or not.
|
||||
*/
|
||||
bool __hyp_text kvm_condition_valid32(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long cpsr;
|
||||
u32 cpsr_cond;
|
||||
int cond;
|
||||
|
||||
/* Top two bits non-zero? Unconditional. */
|
||||
if (kvm_vcpu_get_hsr(vcpu) >> 30)
|
||||
return true;
|
||||
|
||||
/* Is condition field valid? */
|
||||
cond = kvm_vcpu_get_condition(vcpu);
|
||||
if (cond == 0xE)
|
||||
return true;
|
||||
|
||||
cpsr = *vcpu_cpsr(vcpu);
|
||||
|
||||
if (cond < 0) {
|
||||
/* This can happen in Thumb mode: examine IT state. */
|
||||
unsigned long it;
|
||||
|
||||
it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3);
|
||||
|
||||
/* it == 0 => unconditional. */
|
||||
if (it == 0)
|
||||
return true;
|
||||
|
||||
/* The cond for this insn works out as the top 4 bits. */
|
||||
cond = (it >> 4);
|
||||
}
|
||||
|
||||
cpsr_cond = cpsr >> 28;
|
||||
|
||||
if (!((cc_map[cond] >> cpsr_cond) & 1))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* adjust_itstate - adjust ITSTATE when emulating instructions in IT-block
|
||||
* @vcpu: The VCPU pointer
|
||||
*
|
||||
* When exceptions occur while instructions are executed in Thumb IF-THEN
|
||||
* blocks, the ITSTATE field of the CPSR is not advanced (updated), so we have
|
||||
* to do this little bit of work manually. The fields map like this:
|
||||
*
|
||||
* IT[7:0] -> CPSR[26:25],CPSR[15:10]
|
||||
*/
|
||||
static void __hyp_text kvm_adjust_itstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long itbits, cond;
|
||||
unsigned long cpsr = *vcpu_cpsr(vcpu);
|
||||
bool is_arm = !(cpsr & PSR_AA32_T_BIT);
|
||||
|
||||
if (is_arm || !(cpsr & PSR_AA32_IT_MASK))
|
||||
return;
|
||||
|
||||
cond = (cpsr & 0xe000) >> 13;
|
||||
itbits = (cpsr & 0x1c00) >> (10 - 2);
|
||||
itbits |= (cpsr & (0x3 << 25)) >> 25;
|
||||
|
||||
/* Perform ITAdvance (see page A2-52 in ARM DDI 0406C) */
|
||||
if ((itbits & 0x7) == 0)
|
||||
itbits = cond = 0;
|
||||
else
|
||||
itbits = (itbits << 1) & 0x1f;
|
||||
|
||||
cpsr &= ~PSR_AA32_IT_MASK;
|
||||
cpsr |= cond << 13;
|
||||
cpsr |= (itbits & 0x1c) << (10 - 2);
|
||||
cpsr |= (itbits & 0x3) << 25;
|
||||
*vcpu_cpsr(vcpu) = cpsr;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_skip_instr - skip a trapped instruction and proceed to the next
|
||||
* @vcpu: The vcpu pointer
|
||||
*/
|
||||
void __hyp_text kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr)
|
||||
{
|
||||
u32 pc = *vcpu_pc(vcpu);
|
||||
bool is_thumb;
|
||||
|
||||
is_thumb = !!(*vcpu_cpsr(vcpu) & PSR_AA32_T_BIT);
|
||||
if (is_thumb && !is_wide_instr)
|
||||
pc += 2;
|
||||
else
|
||||
pc += 4;
|
||||
|
||||
*vcpu_pc(vcpu) = pc;
|
||||
|
||||
kvm_adjust_itstate(vcpu);
|
||||
}
|
49
virt/kvm/arm/hyp/timer-sr.c
Normal file
49
virt/kvm/arm/hyp/timer-sr.c
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2012-2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <clocksource/arm_arch_timer.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
void __hyp_text __kvm_timer_set_cntvoff(u32 cntvoff_low, u32 cntvoff_high)
|
||||
{
|
||||
u64 cntvoff = (u64)cntvoff_high << 32 | cntvoff_low;
|
||||
write_sysreg(cntvoff, cntvoff_el2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Should only be called on non-VHE systems.
|
||||
* VHE systems use EL2 timers and configure EL1 timers in kvm_timer_init_vhe().
|
||||
*/
|
||||
void __hyp_text __timer_disable_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
/* Allow physical timer/counter access for the host */
|
||||
val = read_sysreg(cnthctl_el2);
|
||||
val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
|
||||
write_sysreg(val, cnthctl_el2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Should only be called on non-VHE systems.
|
||||
* VHE systems use EL2 timers and configure EL1 timers in kvm_timer_init_vhe().
|
||||
*/
|
||||
void __hyp_text __timer_enable_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
/*
|
||||
* Disallow physical timer access for the guest
|
||||
* Physical counter access is allowed
|
||||
*/
|
||||
val = read_sysreg(cnthctl_el2);
|
||||
val &= ~CNTHCTL_EL1PCEN;
|
||||
val |= CNTHCTL_EL1PCTEN;
|
||||
write_sysreg(val, cnthctl_el2);
|
||||
}
|
1130
virt/kvm/arm/hyp/vgic-v3-sr.c
Normal file
1130
virt/kvm/arm/hyp/vgic-v3-sr.c
Normal file
File diff suppressed because it is too large
Load Diff
219
virt/kvm/arm/mmio.c
Normal file
219
virt/kvm/arm/mmio.c
Normal file
@ -0,0 +1,219 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2012 - Virtual Open Systems and Columbia University
|
||||
* Author: Christoffer Dall <c.dall@virtualopensystems.com>
|
||||
*/
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/kvm_mmio.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <trace/events/kvm.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
void kvm_mmio_write_buf(void *buf, unsigned int len, unsigned long data)
|
||||
{
|
||||
void *datap = NULL;
|
||||
union {
|
||||
u8 byte;
|
||||
u16 hword;
|
||||
u32 word;
|
||||
u64 dword;
|
||||
} tmp;
|
||||
|
||||
switch (len) {
|
||||
case 1:
|
||||
tmp.byte = data;
|
||||
datap = &tmp.byte;
|
||||
break;
|
||||
case 2:
|
||||
tmp.hword = data;
|
||||
datap = &tmp.hword;
|
||||
break;
|
||||
case 4:
|
||||
tmp.word = data;
|
||||
datap = &tmp.word;
|
||||
break;
|
||||
case 8:
|
||||
tmp.dword = data;
|
||||
datap = &tmp.dword;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(buf, datap, len);
|
||||
}
|
||||
|
||||
unsigned long kvm_mmio_read_buf(const void *buf, unsigned int len)
|
||||
{
|
||||
unsigned long data = 0;
|
||||
union {
|
||||
u16 hword;
|
||||
u32 word;
|
||||
u64 dword;
|
||||
} tmp;
|
||||
|
||||
switch (len) {
|
||||
case 1:
|
||||
data = *(u8 *)buf;
|
||||
break;
|
||||
case 2:
|
||||
memcpy(&tmp.hword, buf, len);
|
||||
data = tmp.hword;
|
||||
break;
|
||||
case 4:
|
||||
memcpy(&tmp.word, buf, len);
|
||||
data = tmp.word;
|
||||
break;
|
||||
case 8:
|
||||
memcpy(&tmp.dword, buf, len);
|
||||
data = tmp.dword;
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_handle_mmio_return -- Handle MMIO loads after user space emulation
|
||||
* or in-kernel IO emulation
|
||||
*
|
||||
* @vcpu: The VCPU pointer
|
||||
* @run: The VCPU run struct containing the mmio data
|
||||
*/
|
||||
int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||
{
|
||||
unsigned long data;
|
||||
unsigned int len;
|
||||
int mask;
|
||||
|
||||
/* Detect an already handled MMIO return */
|
||||
if (unlikely(!vcpu->mmio_needed))
|
||||
return 0;
|
||||
|
||||
vcpu->mmio_needed = 0;
|
||||
|
||||
if (!run->mmio.is_write) {
|
||||
len = run->mmio.len;
|
||||
if (len > sizeof(unsigned long))
|
||||
return -EINVAL;
|
||||
|
||||
data = kvm_mmio_read_buf(run->mmio.data, len);
|
||||
|
||||
if (vcpu->arch.mmio_decode.sign_extend &&
|
||||
len < sizeof(unsigned long)) {
|
||||
mask = 1U << ((len * 8) - 1);
|
||||
data = (data ^ mask) - mask;
|
||||
}
|
||||
|
||||
if (!vcpu->arch.mmio_decode.sixty_four)
|
||||
data = data & 0xffffffff;
|
||||
|
||||
trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
|
||||
&data);
|
||||
data = vcpu_data_host_to_guest(vcpu, data, len);
|
||||
vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* The MMIO instruction is emulated and should not be re-executed
|
||||
* in the guest.
|
||||
*/
|
||||
kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len)
|
||||
{
|
||||
unsigned long rt;
|
||||
int access_size;
|
||||
bool sign_extend;
|
||||
bool sixty_four;
|
||||
|
||||
if (kvm_vcpu_dabt_iss1tw(vcpu)) {
|
||||
/* page table accesses IO mem: tell guest to fix its TTBR */
|
||||
kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
|
||||
return 1;
|
||||
}
|
||||
|
||||
access_size = kvm_vcpu_dabt_get_as(vcpu);
|
||||
if (unlikely(access_size < 0))
|
||||
return access_size;
|
||||
|
||||
*is_write = kvm_vcpu_dabt_iswrite(vcpu);
|
||||
sign_extend = kvm_vcpu_dabt_issext(vcpu);
|
||||
sixty_four = kvm_vcpu_dabt_issf(vcpu);
|
||||
rt = kvm_vcpu_dabt_get_rd(vcpu);
|
||||
|
||||
*len = access_size;
|
||||
vcpu->arch.mmio_decode.sign_extend = sign_extend;
|
||||
vcpu->arch.mmio_decode.rt = rt;
|
||||
vcpu->arch.mmio_decode.sixty_four = sixty_four;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
||||
phys_addr_t fault_ipa)
|
||||
{
|
||||
unsigned long data;
|
||||
unsigned long rt;
|
||||
int ret;
|
||||
bool is_write;
|
||||
int len;
|
||||
u8 data_buf[8];
|
||||
|
||||
/*
|
||||
* Prepare MMIO operation. First decode the syndrome data we get
|
||||
* from the CPU. Then try if some in-kernel emulation feels
|
||||
* responsible, otherwise let user space do its magic.
|
||||
*/
|
||||
if (kvm_vcpu_dabt_isvalid(vcpu)) {
|
||||
ret = decode_hsr(vcpu, &is_write, &len);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
kvm_err("load/store instruction decoding not implemented\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
rt = vcpu->arch.mmio_decode.rt;
|
||||
|
||||
if (is_write) {
|
||||
data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt),
|
||||
len);
|
||||
|
||||
trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, &data);
|
||||
kvm_mmio_write_buf(data_buf, len, data);
|
||||
|
||||
ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len,
|
||||
data_buf);
|
||||
} else {
|
||||
trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len,
|
||||
fault_ipa, NULL);
|
||||
|
||||
ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len,
|
||||
data_buf);
|
||||
}
|
||||
|
||||
/* Now prepare kvm_run for the potential return to userland. */
|
||||
run->mmio.is_write = is_write;
|
||||
run->mmio.phys_addr = fault_ipa;
|
||||
run->mmio.len = len;
|
||||
vcpu->mmio_needed = 1;
|
||||
|
||||
if (!ret) {
|
||||
/* We handled the access successfully in the kernel. */
|
||||
if (!is_write)
|
||||
memcpy(run->mmio.data, data_buf, len);
|
||||
vcpu->stat.mmio_exit_kernel++;
|
||||
kvm_handle_mmio_return(vcpu, run);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_write)
|
||||
memcpy(run->mmio.data, data_buf, len);
|
||||
vcpu->stat.mmio_exit_user++;
|
||||
run->exit_reason = KVM_EXIT_MMIO;
|
||||
return 0;
|
||||
}
|
2477
virt/kvm/arm/mmu.c
Normal file
2477
virt/kvm/arm/mmu.c
Normal file
File diff suppressed because it is too large
Load Diff
57
virt/kvm/arm/perf.c
Normal file
57
virt/kvm/arm/perf.c
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Based on the x86 implementation.
|
||||
*
|
||||
* Copyright (C) 2012 ARM Ltd.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kvm_emulate.h>
|
||||
|
||||
static int kvm_is_in_guest(void)
|
||||
{
|
||||
return kvm_arm_get_running_vcpu() != NULL;
|
||||
}
|
||||
|
||||
static int kvm_is_user_mode(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
vcpu = kvm_arm_get_running_vcpu();
|
||||
|
||||
if (vcpu)
|
||||
return !vcpu_mode_priv(vcpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long kvm_get_guest_ip(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
vcpu = kvm_arm_get_running_vcpu();
|
||||
|
||||
if (vcpu)
|
||||
return *vcpu_pc(vcpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_guest_info_callbacks kvm_guest_cbs = {
|
||||
.is_in_guest = kvm_is_in_guest,
|
||||
.is_user_mode = kvm_is_user_mode,
|
||||
.get_guest_ip = kvm_get_guest_ip,
|
||||
};
|
||||
|
||||
int kvm_perf_init(void)
|
||||
{
|
||||
return perf_register_guest_info_callbacks(&kvm_guest_cbs);
|
||||
}
|
||||
|
||||
int kvm_perf_teardown(void)
|
||||
{
|
||||
return perf_unregister_guest_info_callbacks(&kvm_guest_cbs);
|
||||
}
|
865
virt/kvm/arm/pmu.c
Normal file
865
virt/kvm/arm/pmu.c
Normal file
@ -0,0 +1,865 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 Linaro Ltd.
|
||||
* Author: Shannon Zhao <shannon.zhao@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/perf/arm_pmu.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <kvm/arm_pmu.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
|
||||
static void kvm_pmu_create_perf_event(struct kvm_vcpu *vcpu, u64 select_idx);
|
||||
|
||||
#define PERF_ATTR_CFG1_KVM_PMU_CHAINED 0x1
|
||||
|
||||
/**
|
||||
* kvm_pmu_idx_is_64bit - determine if select_idx is a 64bit counter
|
||||
* @vcpu: The vcpu pointer
|
||||
* @select_idx: The counter index
|
||||
*/
|
||||
static bool kvm_pmu_idx_is_64bit(struct kvm_vcpu *vcpu, u64 select_idx)
|
||||
{
|
||||
return (select_idx == ARMV8_PMU_CYCLE_IDX &&
|
||||
__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_LC);
|
||||
}
|
||||
|
||||
static struct kvm_vcpu *kvm_pmc_to_vcpu(struct kvm_pmc *pmc)
|
||||
{
|
||||
struct kvm_pmu *pmu;
|
||||
struct kvm_vcpu_arch *vcpu_arch;
|
||||
|
||||
pmc -= pmc->idx;
|
||||
pmu = container_of(pmc, struct kvm_pmu, pmc[0]);
|
||||
vcpu_arch = container_of(pmu, struct kvm_vcpu_arch, pmu);
|
||||
return container_of(vcpu_arch, struct kvm_vcpu, arch);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_pmc_is_chained - determine if the pmc is chained
|
||||
* @pmc: The PMU counter pointer
|
||||
*/
|
||||
static bool kvm_pmu_pmc_is_chained(struct kvm_pmc *pmc)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
|
||||
|
||||
return test_bit(pmc->idx >> 1, vcpu->arch.pmu.chained);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_idx_is_high_counter - determine if select_idx is a high/low counter
|
||||
* @select_idx: The counter index
|
||||
*/
|
||||
static bool kvm_pmu_idx_is_high_counter(u64 select_idx)
|
||||
{
|
||||
return select_idx & 0x1;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_get_canonical_pmc - obtain the canonical pmc
|
||||
* @pmc: The PMU counter pointer
|
||||
*
|
||||
* When a pair of PMCs are chained together we use the low counter (canonical)
|
||||
* to hold the underlying perf event.
|
||||
*/
|
||||
static struct kvm_pmc *kvm_pmu_get_canonical_pmc(struct kvm_pmc *pmc)
|
||||
{
|
||||
if (kvm_pmu_pmc_is_chained(pmc) &&
|
||||
kvm_pmu_idx_is_high_counter(pmc->idx))
|
||||
return pmc - 1;
|
||||
|
||||
return pmc;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_idx_has_chain_evtype - determine if the event type is chain
|
||||
* @vcpu: The vcpu pointer
|
||||
* @select_idx: The counter index
|
||||
*/
|
||||
static bool kvm_pmu_idx_has_chain_evtype(struct kvm_vcpu *vcpu, u64 select_idx)
|
||||
{
|
||||
u64 eventsel, reg;
|
||||
|
||||
select_idx |= 0x1;
|
||||
|
||||
if (select_idx == ARMV8_PMU_CYCLE_IDX)
|
||||
return false;
|
||||
|
||||
reg = PMEVTYPER0_EL0 + select_idx;
|
||||
eventsel = __vcpu_sys_reg(vcpu, reg) & ARMV8_PMU_EVTYPE_EVENT;
|
||||
|
||||
return eventsel == ARMV8_PMUV3_PERFCTR_CHAIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_get_pair_counter_value - get PMU counter value
|
||||
* @vcpu: The vcpu pointer
|
||||
* @pmc: The PMU counter pointer
|
||||
*/
|
||||
static u64 kvm_pmu_get_pair_counter_value(struct kvm_vcpu *vcpu,
|
||||
struct kvm_pmc *pmc)
|
||||
{
|
||||
u64 counter, counter_high, reg, enabled, running;
|
||||
|
||||
if (kvm_pmu_pmc_is_chained(pmc)) {
|
||||
pmc = kvm_pmu_get_canonical_pmc(pmc);
|
||||
reg = PMEVCNTR0_EL0 + pmc->idx;
|
||||
|
||||
counter = __vcpu_sys_reg(vcpu, reg);
|
||||
counter_high = __vcpu_sys_reg(vcpu, reg + 1);
|
||||
|
||||
counter = lower_32_bits(counter) | (counter_high << 32);
|
||||
} else {
|
||||
reg = (pmc->idx == ARMV8_PMU_CYCLE_IDX)
|
||||
? PMCCNTR_EL0 : PMEVCNTR0_EL0 + pmc->idx;
|
||||
counter = __vcpu_sys_reg(vcpu, reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* The real counter value is equal to the value of counter register plus
|
||||
* the value perf event counts.
|
||||
*/
|
||||
if (pmc->perf_event)
|
||||
counter += perf_event_read_value(pmc->perf_event, &enabled,
|
||||
&running);
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_get_counter_value - get PMU counter value
|
||||
* @vcpu: The vcpu pointer
|
||||
* @select_idx: The counter index
|
||||
*/
|
||||
u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx)
|
||||
{
|
||||
u64 counter;
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
struct kvm_pmc *pmc = &pmu->pmc[select_idx];
|
||||
|
||||
counter = kvm_pmu_get_pair_counter_value(vcpu, pmc);
|
||||
|
||||
if (kvm_pmu_pmc_is_chained(pmc) &&
|
||||
kvm_pmu_idx_is_high_counter(select_idx))
|
||||
counter = upper_32_bits(counter);
|
||||
else if (select_idx != ARMV8_PMU_CYCLE_IDX)
|
||||
counter = lower_32_bits(counter);
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_set_counter_value - set PMU counter value
|
||||
* @vcpu: The vcpu pointer
|
||||
* @select_idx: The counter index
|
||||
* @val: The counter value
|
||||
*/
|
||||
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
|
||||
{
|
||||
u64 reg;
|
||||
|
||||
reg = (select_idx == ARMV8_PMU_CYCLE_IDX)
|
||||
? PMCCNTR_EL0 : PMEVCNTR0_EL0 + select_idx;
|
||||
__vcpu_sys_reg(vcpu, reg) += (s64)val - kvm_pmu_get_counter_value(vcpu, select_idx);
|
||||
|
||||
/* Recreate the perf event to reflect the updated sample_period */
|
||||
kvm_pmu_create_perf_event(vcpu, select_idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_release_perf_event - remove the perf event
|
||||
* @pmc: The PMU counter pointer
|
||||
*/
|
||||
static void kvm_pmu_release_perf_event(struct kvm_pmc *pmc)
|
||||
{
|
||||
pmc = kvm_pmu_get_canonical_pmc(pmc);
|
||||
if (pmc->perf_event) {
|
||||
perf_event_disable(pmc->perf_event);
|
||||
perf_event_release_kernel(pmc->perf_event);
|
||||
pmc->perf_event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_stop_counter - stop PMU counter
|
||||
* @pmc: The PMU counter pointer
|
||||
*
|
||||
* If this counter has been configured to monitor some event, release it here.
|
||||
*/
|
||||
static void kvm_pmu_stop_counter(struct kvm_vcpu *vcpu, struct kvm_pmc *pmc)
|
||||
{
|
||||
u64 counter, reg, val;
|
||||
|
||||
pmc = kvm_pmu_get_canonical_pmc(pmc);
|
||||
if (!pmc->perf_event)
|
||||
return;
|
||||
|
||||
counter = kvm_pmu_get_pair_counter_value(vcpu, pmc);
|
||||
|
||||
if (pmc->idx == ARMV8_PMU_CYCLE_IDX) {
|
||||
reg = PMCCNTR_EL0;
|
||||
val = counter;
|
||||
} else {
|
||||
reg = PMEVCNTR0_EL0 + pmc->idx;
|
||||
val = lower_32_bits(counter);
|
||||
}
|
||||
|
||||
__vcpu_sys_reg(vcpu, reg) = val;
|
||||
|
||||
if (kvm_pmu_pmc_is_chained(pmc))
|
||||
__vcpu_sys_reg(vcpu, reg + 1) = upper_32_bits(counter);
|
||||
|
||||
kvm_pmu_release_perf_event(pmc);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_vcpu_init - assign pmu counter idx for cpu
|
||||
* @vcpu: The vcpu pointer
|
||||
*
|
||||
*/
|
||||
void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int i;
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
|
||||
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++)
|
||||
pmu->pmc[i].idx = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_vcpu_reset - reset pmu state for cpu
|
||||
* @vcpu: The vcpu pointer
|
||||
*
|
||||
*/
|
||||
void kvm_pmu_vcpu_reset(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int i;
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
|
||||
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++)
|
||||
kvm_pmu_stop_counter(vcpu, &pmu->pmc[i]);
|
||||
|
||||
bitmap_zero(vcpu->arch.pmu.chained, ARMV8_PMU_MAX_COUNTER_PAIRS);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_vcpu_destroy - free perf event of PMU for cpu
|
||||
* @vcpu: The vcpu pointer
|
||||
*
|
||||
*/
|
||||
void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int i;
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
|
||||
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++)
|
||||
kvm_pmu_release_perf_event(&pmu->pmc[i]);
|
||||
}
|
||||
|
||||
u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val = __vcpu_sys_reg(vcpu, PMCR_EL0) >> ARMV8_PMU_PMCR_N_SHIFT;
|
||||
|
||||
val &= ARMV8_PMU_PMCR_N_MASK;
|
||||
if (val == 0)
|
||||
return BIT(ARMV8_PMU_CYCLE_IDX);
|
||||
else
|
||||
return GENMASK(val - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_enable_counter_mask - enable selected PMU counters
|
||||
* @vcpu: The vcpu pointer
|
||||
* @val: the value guest writes to PMCNTENSET register
|
||||
*
|
||||
* Call perf_event_enable to start counting the perf event
|
||||
*/
|
||||
void kvm_pmu_enable_counter_mask(struct kvm_vcpu *vcpu, u64 val)
|
||||
{
|
||||
int i;
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
struct kvm_pmc *pmc;
|
||||
|
||||
if (!(__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) || !val)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) {
|
||||
if (!(val & BIT(i)))
|
||||
continue;
|
||||
|
||||
pmc = &pmu->pmc[i];
|
||||
|
||||
/*
|
||||
* For high counters of chained events we must recreate the
|
||||
* perf event with the long (64bit) attribute set.
|
||||
*/
|
||||
if (kvm_pmu_pmc_is_chained(pmc) &&
|
||||
kvm_pmu_idx_is_high_counter(i)) {
|
||||
kvm_pmu_create_perf_event(vcpu, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* At this point, pmc must be the canonical */
|
||||
if (pmc->perf_event) {
|
||||
perf_event_enable(pmc->perf_event);
|
||||
if (pmc->perf_event->state != PERF_EVENT_STATE_ACTIVE)
|
||||
kvm_debug("fail to enable perf event\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_disable_counter_mask - disable selected PMU counters
|
||||
* @vcpu: The vcpu pointer
|
||||
* @val: the value guest writes to PMCNTENCLR register
|
||||
*
|
||||
* Call perf_event_disable to stop counting the perf event
|
||||
*/
|
||||
void kvm_pmu_disable_counter_mask(struct kvm_vcpu *vcpu, u64 val)
|
||||
{
|
||||
int i;
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
struct kvm_pmc *pmc;
|
||||
|
||||
if (!val)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) {
|
||||
if (!(val & BIT(i)))
|
||||
continue;
|
||||
|
||||
pmc = &pmu->pmc[i];
|
||||
|
||||
/*
|
||||
* For high counters of chained events we must recreate the
|
||||
* perf event with the long (64bit) attribute unset.
|
||||
*/
|
||||
if (kvm_pmu_pmc_is_chained(pmc) &&
|
||||
kvm_pmu_idx_is_high_counter(i)) {
|
||||
kvm_pmu_create_perf_event(vcpu, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* At this point, pmc must be the canonical */
|
||||
if (pmc->perf_event)
|
||||
perf_event_disable(pmc->perf_event);
|
||||
}
|
||||
}
|
||||
|
||||
static u64 kvm_pmu_overflow_status(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 reg = 0;
|
||||
|
||||
if ((__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E)) {
|
||||
reg = __vcpu_sys_reg(vcpu, PMOVSSET_EL0);
|
||||
reg &= __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
|
||||
reg &= __vcpu_sys_reg(vcpu, PMINTENSET_EL1);
|
||||
reg &= kvm_pmu_valid_counter_mask(vcpu);
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
static void kvm_pmu_update_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
bool overflow;
|
||||
|
||||
if (!kvm_arm_pmu_v3_ready(vcpu))
|
||||
return;
|
||||
|
||||
overflow = !!kvm_pmu_overflow_status(vcpu);
|
||||
if (pmu->irq_level == overflow)
|
||||
return;
|
||||
|
||||
pmu->irq_level = overflow;
|
||||
|
||||
if (likely(irqchip_in_kernel(vcpu->kvm))) {
|
||||
int ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id,
|
||||
pmu->irq_num, overflow, pmu);
|
||||
WARN_ON(ret);
|
||||
}
|
||||
}
|
||||
|
||||
bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
struct kvm_sync_regs *sregs = &vcpu->run->s.regs;
|
||||
bool run_level = sregs->device_irq_level & KVM_ARM_DEV_PMU;
|
||||
|
||||
if (likely(irqchip_in_kernel(vcpu->kvm)))
|
||||
return false;
|
||||
|
||||
return pmu->irq_level != run_level;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reflect the PMU overflow interrupt output level into the kvm_run structure
|
||||
*/
|
||||
void kvm_pmu_update_run(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_sync_regs *regs = &vcpu->run->s.regs;
|
||||
|
||||
/* Populate the timer bitmap for user space */
|
||||
regs->device_irq_level &= ~KVM_ARM_DEV_PMU;
|
||||
if (vcpu->arch.pmu.irq_level)
|
||||
regs->device_irq_level |= KVM_ARM_DEV_PMU;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_flush_hwstate - flush pmu state to cpu
|
||||
* @vcpu: The vcpu pointer
|
||||
*
|
||||
* Check if the PMU has overflowed while we were running in the host, and inject
|
||||
* an interrupt if that was the case.
|
||||
*/
|
||||
void kvm_pmu_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_pmu_update_state(vcpu);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_sync_hwstate - sync pmu state from cpu
|
||||
* @vcpu: The vcpu pointer
|
||||
*
|
||||
* Check if the PMU has overflowed while we were running in the guest, and
|
||||
* inject an interrupt if that was the case.
|
||||
*/
|
||||
void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_pmu_update_state(vcpu);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the perf event overflows, set the overflow status and inform the vcpu.
|
||||
*/
|
||||
static void kvm_pmu_perf_overflow(struct perf_event *perf_event,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kvm_pmc *pmc = perf_event->overflow_handler_context;
|
||||
struct arm_pmu *cpu_pmu = to_arm_pmu(perf_event->pmu);
|
||||
struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
|
||||
int idx = pmc->idx;
|
||||
u64 period;
|
||||
|
||||
cpu_pmu->pmu.stop(perf_event, PERF_EF_UPDATE);
|
||||
|
||||
/*
|
||||
* Reset the sample period to the architectural limit,
|
||||
* i.e. the point where the counter overflows.
|
||||
*/
|
||||
period = -(local64_read(&perf_event->count));
|
||||
|
||||
if (!kvm_pmu_idx_is_64bit(vcpu, pmc->idx))
|
||||
period &= GENMASK(31, 0);
|
||||
|
||||
local64_set(&perf_event->hw.period_left, 0);
|
||||
perf_event->attr.sample_period = period;
|
||||
perf_event->hw.sample_period = period;
|
||||
|
||||
__vcpu_sys_reg(vcpu, PMOVSSET_EL0) |= BIT(idx);
|
||||
|
||||
if (kvm_pmu_overflow_status(vcpu)) {
|
||||
kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu);
|
||||
kvm_vcpu_kick(vcpu);
|
||||
}
|
||||
|
||||
cpu_pmu->pmu.start(perf_event, PERF_EF_RELOAD);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_software_increment - do software increment
|
||||
* @vcpu: The vcpu pointer
|
||||
* @val: the value guest writes to PMSWINC register
|
||||
*/
|
||||
void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val)
|
||||
{
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
int i;
|
||||
|
||||
if (!(__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E))
|
||||
return;
|
||||
|
||||
/* Weed out disabled counters */
|
||||
val &= __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
|
||||
|
||||
for (i = 0; i < ARMV8_PMU_CYCLE_IDX; i++) {
|
||||
u64 type, reg;
|
||||
|
||||
if (!(val & BIT(i)))
|
||||
continue;
|
||||
|
||||
/* PMSWINC only applies to ... SW_INC! */
|
||||
type = __vcpu_sys_reg(vcpu, PMEVTYPER0_EL0 + i);
|
||||
type &= ARMV8_PMU_EVTYPE_EVENT;
|
||||
if (type != ARMV8_PMUV3_PERFCTR_SW_INCR)
|
||||
continue;
|
||||
|
||||
/* increment this even SW_INC counter */
|
||||
reg = __vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i) + 1;
|
||||
reg = lower_32_bits(reg);
|
||||
__vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i) = reg;
|
||||
|
||||
if (reg) /* no overflow on the low part */
|
||||
continue;
|
||||
|
||||
if (kvm_pmu_pmc_is_chained(&pmu->pmc[i])) {
|
||||
/* increment the high counter */
|
||||
reg = __vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i + 1) + 1;
|
||||
reg = lower_32_bits(reg);
|
||||
__vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i + 1) = reg;
|
||||
if (!reg) /* mark overflow on the high counter */
|
||||
__vcpu_sys_reg(vcpu, PMOVSSET_EL0) |= BIT(i + 1);
|
||||
} else {
|
||||
/* mark overflow on low counter */
|
||||
__vcpu_sys_reg(vcpu, PMOVSSET_EL0) |= BIT(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_handle_pmcr - handle PMCR register
|
||||
* @vcpu: The vcpu pointer
|
||||
* @val: the value guest writes to PMCR register
|
||||
*/
|
||||
void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
|
||||
{
|
||||
u64 mask;
|
||||
int i;
|
||||
|
||||
mask = kvm_pmu_valid_counter_mask(vcpu);
|
||||
if (val & ARMV8_PMU_PMCR_E) {
|
||||
kvm_pmu_enable_counter_mask(vcpu,
|
||||
__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & mask);
|
||||
} else {
|
||||
kvm_pmu_disable_counter_mask(vcpu, mask);
|
||||
}
|
||||
|
||||
if (val & ARMV8_PMU_PMCR_C)
|
||||
kvm_pmu_set_counter_value(vcpu, ARMV8_PMU_CYCLE_IDX, 0);
|
||||
|
||||
if (val & ARMV8_PMU_PMCR_P) {
|
||||
for (i = 0; i < ARMV8_PMU_CYCLE_IDX; i++)
|
||||
kvm_pmu_set_counter_value(vcpu, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool kvm_pmu_counter_is_enabled(struct kvm_vcpu *vcpu, u64 select_idx)
|
||||
{
|
||||
return (__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) &&
|
||||
(__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & BIT(select_idx));
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_create_perf_event - create a perf event for a counter
|
||||
* @vcpu: The vcpu pointer
|
||||
* @select_idx: The number of selected counter
|
||||
*/
|
||||
static void kvm_pmu_create_perf_event(struct kvm_vcpu *vcpu, u64 select_idx)
|
||||
{
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
struct kvm_pmc *pmc;
|
||||
struct perf_event *event;
|
||||
struct perf_event_attr attr;
|
||||
u64 eventsel, counter, reg, data;
|
||||
|
||||
/*
|
||||
* For chained counters the event type and filtering attributes are
|
||||
* obtained from the low/even counter. We also use this counter to
|
||||
* determine if the event is enabled/disabled.
|
||||
*/
|
||||
pmc = kvm_pmu_get_canonical_pmc(&pmu->pmc[select_idx]);
|
||||
|
||||
reg = (pmc->idx == ARMV8_PMU_CYCLE_IDX)
|
||||
? PMCCFILTR_EL0 : PMEVTYPER0_EL0 + pmc->idx;
|
||||
data = __vcpu_sys_reg(vcpu, reg);
|
||||
|
||||
kvm_pmu_stop_counter(vcpu, pmc);
|
||||
eventsel = data & ARMV8_PMU_EVTYPE_EVENT;
|
||||
|
||||
/* Software increment event does't need to be backed by a perf event */
|
||||
if (eventsel == ARMV8_PMUV3_PERFCTR_SW_INCR &&
|
||||
pmc->idx != ARMV8_PMU_CYCLE_IDX)
|
||||
return;
|
||||
|
||||
memset(&attr, 0, sizeof(struct perf_event_attr));
|
||||
attr.type = PERF_TYPE_RAW;
|
||||
attr.size = sizeof(attr);
|
||||
attr.pinned = 1;
|
||||
attr.disabled = !kvm_pmu_counter_is_enabled(vcpu, pmc->idx);
|
||||
attr.exclude_user = data & ARMV8_PMU_EXCLUDE_EL0 ? 1 : 0;
|
||||
attr.exclude_kernel = data & ARMV8_PMU_EXCLUDE_EL1 ? 1 : 0;
|
||||
attr.exclude_hv = 1; /* Don't count EL2 events */
|
||||
attr.exclude_host = 1; /* Don't count host events */
|
||||
attr.config = (pmc->idx == ARMV8_PMU_CYCLE_IDX) ?
|
||||
ARMV8_PMUV3_PERFCTR_CPU_CYCLES : eventsel;
|
||||
|
||||
counter = kvm_pmu_get_pair_counter_value(vcpu, pmc);
|
||||
|
||||
if (kvm_pmu_idx_has_chain_evtype(vcpu, pmc->idx)) {
|
||||
/**
|
||||
* The initial sample period (overflow count) of an event. For
|
||||
* chained counters we only support overflow interrupts on the
|
||||
* high counter.
|
||||
*/
|
||||
attr.sample_period = (-counter) & GENMASK(63, 0);
|
||||
if (kvm_pmu_counter_is_enabled(vcpu, pmc->idx + 1))
|
||||
attr.config1 |= PERF_ATTR_CFG1_KVM_PMU_CHAINED;
|
||||
|
||||
event = perf_event_create_kernel_counter(&attr, -1, current,
|
||||
kvm_pmu_perf_overflow,
|
||||
pmc + 1);
|
||||
} else {
|
||||
/* The initial sample period (overflow count) of an event. */
|
||||
if (kvm_pmu_idx_is_64bit(vcpu, pmc->idx))
|
||||
attr.sample_period = (-counter) & GENMASK(63, 0);
|
||||
else
|
||||
attr.sample_period = (-counter) & GENMASK(31, 0);
|
||||
|
||||
event = perf_event_create_kernel_counter(&attr, -1, current,
|
||||
kvm_pmu_perf_overflow, pmc);
|
||||
}
|
||||
|
||||
if (IS_ERR(event)) {
|
||||
pr_err_once("kvm: pmu event creation failed %ld\n",
|
||||
PTR_ERR(event));
|
||||
return;
|
||||
}
|
||||
|
||||
pmc->perf_event = event;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_update_pmc_chained - update chained bitmap
|
||||
* @vcpu: The vcpu pointer
|
||||
* @select_idx: The number of selected counter
|
||||
*
|
||||
* Update the chained bitmap based on the event type written in the
|
||||
* typer register.
|
||||
*/
|
||||
static void kvm_pmu_update_pmc_chained(struct kvm_vcpu *vcpu, u64 select_idx)
|
||||
{
|
||||
struct kvm_pmu *pmu = &vcpu->arch.pmu;
|
||||
struct kvm_pmc *pmc = &pmu->pmc[select_idx];
|
||||
|
||||
if (kvm_pmu_idx_has_chain_evtype(vcpu, pmc->idx)) {
|
||||
/*
|
||||
* During promotion from !chained to chained we must ensure
|
||||
* the adjacent counter is stopped and its event destroyed
|
||||
*/
|
||||
if (!kvm_pmu_pmc_is_chained(pmc))
|
||||
kvm_pmu_stop_counter(vcpu, pmc);
|
||||
|
||||
set_bit(pmc->idx >> 1, vcpu->arch.pmu.chained);
|
||||
} else {
|
||||
clear_bit(pmc->idx >> 1, vcpu->arch.pmu.chained);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pmu_set_counter_event_type - set selected counter to monitor some event
|
||||
* @vcpu: The vcpu pointer
|
||||
* @data: The data guest writes to PMXEVTYPER_EL0
|
||||
* @select_idx: The number of selected counter
|
||||
*
|
||||
* When OS accesses PMXEVTYPER_EL0, that means it wants to set a PMC to count an
|
||||
* event with given hardware event number. Here we call perf_event API to
|
||||
* emulate this action and create a kernel perf event for it.
|
||||
*/
|
||||
void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
|
||||
u64 select_idx)
|
||||
{
|
||||
u64 reg, event_type = data & ARMV8_PMU_EVTYPE_MASK;
|
||||
|
||||
reg = (select_idx == ARMV8_PMU_CYCLE_IDX)
|
||||
? PMCCFILTR_EL0 : PMEVTYPER0_EL0 + select_idx;
|
||||
|
||||
__vcpu_sys_reg(vcpu, reg) = event_type;
|
||||
|
||||
kvm_pmu_update_pmc_chained(vcpu, select_idx);
|
||||
kvm_pmu_create_perf_event(vcpu, select_idx);
|
||||
}
|
||||
|
||||
bool kvm_arm_support_pmu_v3(void)
|
||||
{
|
||||
/*
|
||||
* Check if HW_PERF_EVENTS are supported by checking the number of
|
||||
* hardware performance counters. This could ensure the presence of
|
||||
* a physical PMU and CONFIG_PERF_EVENT is selected.
|
||||
*/
|
||||
return (perf_num_counters() > 0);
|
||||
}
|
||||
|
||||
int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!vcpu->arch.pmu.created)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* A valid interrupt configuration for the PMU is either to have a
|
||||
* properly configured interrupt number and using an in-kernel
|
||||
* irqchip, or to not have an in-kernel GIC and not set an IRQ.
|
||||
*/
|
||||
if (irqchip_in_kernel(vcpu->kvm)) {
|
||||
int irq = vcpu->arch.pmu.irq_num;
|
||||
if (!kvm_arm_pmu_irq_initialized(vcpu))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* If we are using an in-kernel vgic, at this point we know
|
||||
* the vgic will be initialized, so we can check the PMU irq
|
||||
* number against the dimensions of the vgic and make sure
|
||||
* it's valid.
|
||||
*/
|
||||
if (!irq_is_ppi(irq) && !vgic_valid_spi(vcpu->kvm, irq))
|
||||
return -EINVAL;
|
||||
} else if (kvm_arm_pmu_irq_initialized(vcpu)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kvm_pmu_vcpu_reset(vcpu);
|
||||
vcpu->arch.pmu.ready = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!kvm_arm_support_pmu_v3())
|
||||
return -ENODEV;
|
||||
|
||||
if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
|
||||
return -ENXIO;
|
||||
|
||||
if (vcpu->arch.pmu.created)
|
||||
return -EBUSY;
|
||||
|
||||
if (irqchip_in_kernel(vcpu->kvm)) {
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If using the PMU with an in-kernel virtual GIC
|
||||
* implementation, we require the GIC to be already
|
||||
* initialized when initializing the PMU.
|
||||
*/
|
||||
if (!vgic_initialized(vcpu->kvm))
|
||||
return -ENODEV;
|
||||
|
||||
if (!kvm_arm_pmu_irq_initialized(vcpu))
|
||||
return -ENXIO;
|
||||
|
||||
ret = kvm_vgic_set_owner(vcpu, vcpu->arch.pmu.irq_num,
|
||||
&vcpu->arch.pmu);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
vcpu->arch.pmu.created = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For one VM the interrupt type must be same for each vcpu.
|
||||
* As a PPI, the interrupt number is the same for all vcpus,
|
||||
* while as an SPI it must be a separate number per vcpu.
|
||||
*/
|
||||
static bool pmu_irq_is_valid(struct kvm *kvm, int irq)
|
||||
{
|
||||
int i;
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||
if (!kvm_arm_pmu_irq_initialized(vcpu))
|
||||
continue;
|
||||
|
||||
if (irq_is_ppi(irq)) {
|
||||
if (vcpu->arch.pmu.irq_num != irq)
|
||||
return false;
|
||||
} else {
|
||||
if (vcpu->arch.pmu.irq_num == irq)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
|
||||
{
|
||||
switch (attr->attr) {
|
||||
case KVM_ARM_VCPU_PMU_V3_IRQ: {
|
||||
int __user *uaddr = (int __user *)(long)attr->addr;
|
||||
int irq;
|
||||
|
||||
if (!irqchip_in_kernel(vcpu->kvm))
|
||||
return -EINVAL;
|
||||
|
||||
if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
|
||||
return -ENODEV;
|
||||
|
||||
if (get_user(irq, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
/* The PMU overflow interrupt can be a PPI or a valid SPI. */
|
||||
if (!(irq_is_ppi(irq) || irq_is_spi(irq)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!pmu_irq_is_valid(vcpu->kvm, irq))
|
||||
return -EINVAL;
|
||||
|
||||
if (kvm_arm_pmu_irq_initialized(vcpu))
|
||||
return -EBUSY;
|
||||
|
||||
kvm_debug("Set kvm ARM PMU irq: %d\n", irq);
|
||||
vcpu->arch.pmu.irq_num = irq;
|
||||
return 0;
|
||||
}
|
||||
case KVM_ARM_VCPU_PMU_V3_INIT:
|
||||
return kvm_arm_pmu_v3_init(vcpu);
|
||||
}
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
|
||||
{
|
||||
switch (attr->attr) {
|
||||
case KVM_ARM_VCPU_PMU_V3_IRQ: {
|
||||
int __user *uaddr = (int __user *)(long)attr->addr;
|
||||
int irq;
|
||||
|
||||
if (!irqchip_in_kernel(vcpu->kvm))
|
||||
return -EINVAL;
|
||||
|
||||
if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
|
||||
return -ENODEV;
|
||||
|
||||
if (!kvm_arm_pmu_irq_initialized(vcpu))
|
||||
return -ENXIO;
|
||||
|
||||
irq = vcpu->arch.pmu.irq_num;
|
||||
return put_user(irq, uaddr);
|
||||
}
|
||||
}
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
|
||||
{
|
||||
switch (attr->attr) {
|
||||
case KVM_ARM_VCPU_PMU_V3_IRQ:
|
||||
case KVM_ARM_VCPU_PMU_V3_INIT:
|
||||
if (kvm_arm_support_pmu_v3() &&
|
||||
test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENXIO;
|
||||
}
|
605
virt/kvm/arm/psci.c
Normal file
605
virt/kvm/arm/psci.c
Normal file
@ -0,0 +1,605 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2012 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_host.h>
|
||||
|
||||
#include <kvm/arm_psci.h>
|
||||
|
||||
/*
|
||||
* This is an implementation of the Power State Coordination Interface
|
||||
* as described in ARM document number ARM DEN 0022A.
|
||||
*/
|
||||
|
||||
#define AFFINITY_MASK(level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1)
|
||||
|
||||
static u32 smccc_get_function(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu_get_reg(vcpu, 0);
|
||||
}
|
||||
|
||||
static unsigned long smccc_get_arg1(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu_get_reg(vcpu, 1);
|
||||
}
|
||||
|
||||
static unsigned long smccc_get_arg2(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu_get_reg(vcpu, 2);
|
||||
}
|
||||
|
||||
static unsigned long smccc_get_arg3(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu_get_reg(vcpu, 3);
|
||||
}
|
||||
|
||||
static void smccc_set_retval(struct kvm_vcpu *vcpu,
|
||||
unsigned long a0,
|
||||
unsigned long a1,
|
||||
unsigned long a2,
|
||||
unsigned long a3)
|
||||
{
|
||||
vcpu_set_reg(vcpu, 0, a0);
|
||||
vcpu_set_reg(vcpu, 1, a1);
|
||||
vcpu_set_reg(vcpu, 2, a2);
|
||||
vcpu_set_reg(vcpu, 3, a3);
|
||||
}
|
||||
|
||||
static unsigned long psci_affinity_mask(unsigned long affinity_level)
|
||||
{
|
||||
if (affinity_level <= 3)
|
||||
return MPIDR_HWID_BITMASK & AFFINITY_MASK(affinity_level);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long kvm_psci_vcpu_suspend(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* NOTE: For simplicity, we make VCPU suspend emulation to be
|
||||
* same-as WFI (Wait-for-interrupt) emulation.
|
||||
*
|
||||
* This means for KVM the wakeup events are interrupts and
|
||||
* this is consistent with intended use of StateID as described
|
||||
* in section 5.4.1 of PSCI v0.2 specification (ARM DEN 0022A).
|
||||
*
|
||||
* Further, we also treat power-down request to be same as
|
||||
* stand-by request as-per section 5.4.2 clause 3 of PSCI v0.2
|
||||
* specification (ARM DEN 0022A). This means all suspend states
|
||||
* for KVM will preserve the register state.
|
||||
*/
|
||||
kvm_vcpu_block(vcpu);
|
||||
kvm_clear_request(KVM_REQ_UNHALT, vcpu);
|
||||
|
||||
return PSCI_RET_SUCCESS;
|
||||
}
|
||||
|
||||
static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->arch.power_off = true;
|
||||
kvm_make_request(KVM_REQ_SLEEP, vcpu);
|
||||
kvm_vcpu_kick(vcpu);
|
||||
}
|
||||
|
||||
static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
|
||||
{
|
||||
struct vcpu_reset_state *reset_state;
|
||||
struct kvm *kvm = source_vcpu->kvm;
|
||||
struct kvm_vcpu *vcpu = NULL;
|
||||
unsigned long cpu_id;
|
||||
|
||||
cpu_id = smccc_get_arg1(source_vcpu) & MPIDR_HWID_BITMASK;
|
||||
if (vcpu_mode_is_32bit(source_vcpu))
|
||||
cpu_id &= ~((u32) 0);
|
||||
|
||||
vcpu = kvm_mpidr_to_vcpu(kvm, cpu_id);
|
||||
|
||||
/*
|
||||
* Make sure the caller requested a valid CPU and that the CPU is
|
||||
* turned off.
|
||||
*/
|
||||
if (!vcpu)
|
||||
return PSCI_RET_INVALID_PARAMS;
|
||||
if (!vcpu->arch.power_off) {
|
||||
if (kvm_psci_version(source_vcpu, kvm) != KVM_ARM_PSCI_0_1)
|
||||
return PSCI_RET_ALREADY_ON;
|
||||
else
|
||||
return PSCI_RET_INVALID_PARAMS;
|
||||
}
|
||||
|
||||
reset_state = &vcpu->arch.reset_state;
|
||||
|
||||
reset_state->pc = smccc_get_arg2(source_vcpu);
|
||||
|
||||
/* Propagate caller endianness */
|
||||
reset_state->be = kvm_vcpu_is_be(source_vcpu);
|
||||
|
||||
/*
|
||||
* NOTE: We always update r0 (or x0) because for PSCI v0.1
|
||||
* the general puspose registers are undefined upon CPU_ON.
|
||||
*/
|
||||
reset_state->r0 = smccc_get_arg3(source_vcpu);
|
||||
|
||||
WRITE_ONCE(reset_state->reset, true);
|
||||
kvm_make_request(KVM_REQ_VCPU_RESET, vcpu);
|
||||
|
||||
/*
|
||||
* Make sure the reset request is observed if the change to
|
||||
* power_state is observed.
|
||||
*/
|
||||
smp_wmb();
|
||||
|
||||
vcpu->arch.power_off = false;
|
||||
kvm_vcpu_wake_up(vcpu);
|
||||
|
||||
return PSCI_RET_SUCCESS;
|
||||
}
|
||||
|
||||
static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int i, matching_cpus = 0;
|
||||
unsigned long mpidr;
|
||||
unsigned long target_affinity;
|
||||
unsigned long target_affinity_mask;
|
||||
unsigned long lowest_affinity_level;
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
struct kvm_vcpu *tmp;
|
||||
|
||||
target_affinity = smccc_get_arg1(vcpu);
|
||||
lowest_affinity_level = smccc_get_arg2(vcpu);
|
||||
|
||||
/* Determine target affinity mask */
|
||||
target_affinity_mask = psci_affinity_mask(lowest_affinity_level);
|
||||
if (!target_affinity_mask)
|
||||
return PSCI_RET_INVALID_PARAMS;
|
||||
|
||||
/* Ignore other bits of target affinity */
|
||||
target_affinity &= target_affinity_mask;
|
||||
|
||||
/*
|
||||
* If one or more VCPU matching target affinity are running
|
||||
* then ON else OFF
|
||||
*/
|
||||
kvm_for_each_vcpu(i, tmp, kvm) {
|
||||
mpidr = kvm_vcpu_get_mpidr_aff(tmp);
|
||||
if ((mpidr & target_affinity_mask) == target_affinity) {
|
||||
matching_cpus++;
|
||||
if (!tmp->arch.power_off)
|
||||
return PSCI_0_2_AFFINITY_LEVEL_ON;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matching_cpus)
|
||||
return PSCI_RET_INVALID_PARAMS;
|
||||
|
||||
return PSCI_0_2_AFFINITY_LEVEL_OFF;
|
||||
}
|
||||
|
||||
static void kvm_prepare_system_event(struct kvm_vcpu *vcpu, u32 type)
|
||||
{
|
||||
int i;
|
||||
struct kvm_vcpu *tmp;
|
||||
|
||||
/*
|
||||
* The KVM ABI specifies that a system event exit may call KVM_RUN
|
||||
* again and may perform shutdown/reboot at a later time that when the
|
||||
* actual request is made. Since we are implementing PSCI and a
|
||||
* caller of PSCI reboot and shutdown expects that the system shuts
|
||||
* down or reboots immediately, let's make sure that VCPUs are not run
|
||||
* after this call is handled and before the VCPUs have been
|
||||
* re-initialized.
|
||||
*/
|
||||
kvm_for_each_vcpu(i, tmp, vcpu->kvm)
|
||||
tmp->arch.power_off = true;
|
||||
kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);
|
||||
|
||||
memset(&vcpu->run->system_event, 0, sizeof(vcpu->run->system_event));
|
||||
vcpu->run->system_event.type = type;
|
||||
vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
|
||||
}
|
||||
|
||||
static void kvm_psci_system_off(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN);
|
||||
}
|
||||
|
||||
static void kvm_psci_system_reset(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_RESET);
|
||||
}
|
||||
|
||||
static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
u32 psci_fn = smccc_get_function(vcpu);
|
||||
unsigned long val;
|
||||
int ret = 1;
|
||||
|
||||
switch (psci_fn) {
|
||||
case PSCI_0_2_FN_PSCI_VERSION:
|
||||
/*
|
||||
* Bits[31:16] = Major Version = 0
|
||||
* Bits[15:0] = Minor Version = 2
|
||||
*/
|
||||
val = KVM_ARM_PSCI_0_2;
|
||||
break;
|
||||
case PSCI_0_2_FN_CPU_SUSPEND:
|
||||
case PSCI_0_2_FN64_CPU_SUSPEND:
|
||||
val = kvm_psci_vcpu_suspend(vcpu);
|
||||
break;
|
||||
case PSCI_0_2_FN_CPU_OFF:
|
||||
kvm_psci_vcpu_off(vcpu);
|
||||
val = PSCI_RET_SUCCESS;
|
||||
break;
|
||||
case PSCI_0_2_FN_CPU_ON:
|
||||
case PSCI_0_2_FN64_CPU_ON:
|
||||
mutex_lock(&kvm->lock);
|
||||
val = kvm_psci_vcpu_on(vcpu);
|
||||
mutex_unlock(&kvm->lock);
|
||||
break;
|
||||
case PSCI_0_2_FN_AFFINITY_INFO:
|
||||
case PSCI_0_2_FN64_AFFINITY_INFO:
|
||||
val = kvm_psci_vcpu_affinity_info(vcpu);
|
||||
break;
|
||||
case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
|
||||
/*
|
||||
* Trusted OS is MP hence does not require migration
|
||||
* or
|
||||
* Trusted OS is not present
|
||||
*/
|
||||
val = PSCI_0_2_TOS_MP;
|
||||
break;
|
||||
case PSCI_0_2_FN_SYSTEM_OFF:
|
||||
kvm_psci_system_off(vcpu);
|
||||
/*
|
||||
* We should'nt be going back to guest VCPU after
|
||||
* receiving SYSTEM_OFF request.
|
||||
*
|
||||
* If user space accidently/deliberately resumes
|
||||
* guest VCPU after SYSTEM_OFF request then guest
|
||||
* VCPU should see internal failure from PSCI return
|
||||
* value. To achieve this, we preload r0 (or x0) with
|
||||
* PSCI return value INTERNAL_FAILURE.
|
||||
*/
|
||||
val = PSCI_RET_INTERNAL_FAILURE;
|
||||
ret = 0;
|
||||
break;
|
||||
case PSCI_0_2_FN_SYSTEM_RESET:
|
||||
kvm_psci_system_reset(vcpu);
|
||||
/*
|
||||
* Same reason as SYSTEM_OFF for preloading r0 (or x0)
|
||||
* with PSCI return value INTERNAL_FAILURE.
|
||||
*/
|
||||
val = PSCI_RET_INTERNAL_FAILURE;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
val = PSCI_RET_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
smccc_set_retval(vcpu, val, 0, 0, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvm_psci_1_0_call(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 psci_fn = smccc_get_function(vcpu);
|
||||
u32 feature;
|
||||
unsigned long val;
|
||||
int ret = 1;
|
||||
|
||||
switch(psci_fn) {
|
||||
case PSCI_0_2_FN_PSCI_VERSION:
|
||||
val = KVM_ARM_PSCI_1_0;
|
||||
break;
|
||||
case PSCI_1_0_FN_PSCI_FEATURES:
|
||||
feature = smccc_get_arg1(vcpu);
|
||||
switch(feature) {
|
||||
case PSCI_0_2_FN_PSCI_VERSION:
|
||||
case PSCI_0_2_FN_CPU_SUSPEND:
|
||||
case PSCI_0_2_FN64_CPU_SUSPEND:
|
||||
case PSCI_0_2_FN_CPU_OFF:
|
||||
case PSCI_0_2_FN_CPU_ON:
|
||||
case PSCI_0_2_FN64_CPU_ON:
|
||||
case PSCI_0_2_FN_AFFINITY_INFO:
|
||||
case PSCI_0_2_FN64_AFFINITY_INFO:
|
||||
case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
|
||||
case PSCI_0_2_FN_SYSTEM_OFF:
|
||||
case PSCI_0_2_FN_SYSTEM_RESET:
|
||||
case PSCI_1_0_FN_PSCI_FEATURES:
|
||||
case ARM_SMCCC_VERSION_FUNC_ID:
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
val = PSCI_RET_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return kvm_psci_0_2_call(vcpu);
|
||||
}
|
||||
|
||||
smccc_set_retval(vcpu, val, 0, 0, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
u32 psci_fn = smccc_get_function(vcpu);
|
||||
unsigned long val;
|
||||
|
||||
switch (psci_fn) {
|
||||
case KVM_PSCI_FN_CPU_OFF:
|
||||
kvm_psci_vcpu_off(vcpu);
|
||||
val = PSCI_RET_SUCCESS;
|
||||
break;
|
||||
case KVM_PSCI_FN_CPU_ON:
|
||||
mutex_lock(&kvm->lock);
|
||||
val = kvm_psci_vcpu_on(vcpu);
|
||||
mutex_unlock(&kvm->lock);
|
||||
break;
|
||||
default:
|
||||
val = PSCI_RET_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
smccc_set_retval(vcpu, val, 0, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_psci_call - handle PSCI call if r0 value is in range
|
||||
* @vcpu: Pointer to the VCPU struct
|
||||
*
|
||||
* Handle PSCI calls from guests through traps from HVC instructions.
|
||||
* The calling convention is similar to SMC calls to the secure world
|
||||
* where the function number is placed in r0.
|
||||
*
|
||||
* This function returns: > 0 (success), 0 (success but exit to user
|
||||
* space), and < 0 (errors)
|
||||
*
|
||||
* Errors:
|
||||
* -EINVAL: Unrecognized PSCI function
|
||||
*/
|
||||
static int kvm_psci_call(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
switch (kvm_psci_version(vcpu, vcpu->kvm)) {
|
||||
case KVM_ARM_PSCI_1_0:
|
||||
return kvm_psci_1_0_call(vcpu);
|
||||
case KVM_ARM_PSCI_0_2:
|
||||
return kvm_psci_0_2_call(vcpu);
|
||||
case KVM_ARM_PSCI_0_1:
|
||||
return kvm_psci_0_1_call(vcpu);
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
|
||||
int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 func_id = smccc_get_function(vcpu);
|
||||
u32 val = SMCCC_RET_NOT_SUPPORTED;
|
||||
u32 feature;
|
||||
|
||||
switch (func_id) {
|
||||
case ARM_SMCCC_VERSION_FUNC_ID:
|
||||
val = ARM_SMCCC_VERSION_1_1;
|
||||
break;
|
||||
case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
|
||||
feature = smccc_get_arg1(vcpu);
|
||||
switch(feature) {
|
||||
case ARM_SMCCC_ARCH_WORKAROUND_1:
|
||||
switch (kvm_arm_harden_branch_predictor()) {
|
||||
case KVM_BP_HARDEN_UNKNOWN:
|
||||
break;
|
||||
case KVM_BP_HARDEN_WA_NEEDED:
|
||||
val = SMCCC_RET_SUCCESS;
|
||||
break;
|
||||
case KVM_BP_HARDEN_NOT_REQUIRED:
|
||||
val = SMCCC_RET_NOT_REQUIRED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ARM_SMCCC_ARCH_WORKAROUND_2:
|
||||
switch (kvm_arm_have_ssbd()) {
|
||||
case KVM_SSBD_FORCE_DISABLE:
|
||||
case KVM_SSBD_UNKNOWN:
|
||||
break;
|
||||
case KVM_SSBD_KERNEL:
|
||||
val = SMCCC_RET_SUCCESS;
|
||||
break;
|
||||
case KVM_SSBD_FORCE_ENABLE:
|
||||
case KVM_SSBD_MITIGATED:
|
||||
val = SMCCC_RET_NOT_REQUIRED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return kvm_psci_call(vcpu);
|
||||
}
|
||||
|
||||
smccc_set_retval(vcpu, val, 0, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return 3; /* PSCI version and two workaround registers */
|
||||
}
|
||||
|
||||
int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
|
||||
{
|
||||
if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
|
||||
return -EFAULT;
|
||||
|
||||
if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
|
||||
return -EFAULT;
|
||||
|
||||
if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define KVM_REG_FEATURE_LEVEL_WIDTH 4
|
||||
#define KVM_REG_FEATURE_LEVEL_MASK (BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
|
||||
|
||||
/*
|
||||
* Convert the workaround level into an easy-to-compare number, where higher
|
||||
* values mean better protection.
|
||||
*/
|
||||
static int get_kernel_wa_level(u64 regid)
|
||||
{
|
||||
switch (regid) {
|
||||
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
|
||||
switch (kvm_arm_harden_branch_predictor()) {
|
||||
case KVM_BP_HARDEN_UNKNOWN:
|
||||
return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
|
||||
case KVM_BP_HARDEN_WA_NEEDED:
|
||||
return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
|
||||
case KVM_BP_HARDEN_NOT_REQUIRED:
|
||||
return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
|
||||
}
|
||||
return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
|
||||
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
|
||||
switch (kvm_arm_have_ssbd()) {
|
||||
case KVM_SSBD_FORCE_DISABLE:
|
||||
return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
|
||||
case KVM_SSBD_KERNEL:
|
||||
return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL;
|
||||
case KVM_SSBD_FORCE_ENABLE:
|
||||
case KVM_SSBD_MITIGATED:
|
||||
return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
|
||||
case KVM_SSBD_UNKNOWN:
|
||||
default:
|
||||
return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
{
|
||||
void __user *uaddr = (void __user *)(long)reg->addr;
|
||||
u64 val;
|
||||
|
||||
switch (reg->id) {
|
||||
case KVM_REG_ARM_PSCI_VERSION:
|
||||
val = kvm_psci_version(vcpu, vcpu->kvm);
|
||||
break;
|
||||
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
|
||||
val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
|
||||
break;
|
||||
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
|
||||
val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
|
||||
|
||||
if (val == KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL &&
|
||||
kvm_arm_get_vcpu_workaround_2_flag(vcpu))
|
||||
val |= KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED;
|
||||
break;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
{
|
||||
void __user *uaddr = (void __user *)(long)reg->addr;
|
||||
u64 val;
|
||||
int wa_level;
|
||||
|
||||
if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (reg->id) {
|
||||
case KVM_REG_ARM_PSCI_VERSION:
|
||||
{
|
||||
bool wants_02;
|
||||
|
||||
wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
|
||||
|
||||
switch (val) {
|
||||
case KVM_ARM_PSCI_0_1:
|
||||
if (wants_02)
|
||||
return -EINVAL;
|
||||
vcpu->kvm->arch.psci_version = val;
|
||||
return 0;
|
||||
case KVM_ARM_PSCI_0_2:
|
||||
case KVM_ARM_PSCI_1_0:
|
||||
if (!wants_02)
|
||||
return -EINVAL;
|
||||
vcpu->kvm->arch.psci_version = val;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
|
||||
if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
if (get_kernel_wa_level(reg->id) < val)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
||||
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
|
||||
if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
|
||||
KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
|
||||
return -EINVAL;
|
||||
|
||||
wa_level = val & KVM_REG_FEATURE_LEVEL_MASK;
|
||||
|
||||
if (get_kernel_wa_level(reg->id) < wa_level)
|
||||
return -EINVAL;
|
||||
|
||||
/* The enabled bit must not be set unless the level is AVAIL. */
|
||||
if (wa_level != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL &&
|
||||
wa_level != val)
|
||||
return -EINVAL;
|
||||
|
||||
/* Are we finished or do we need to check the enable bit ? */
|
||||
if (kvm_arm_have_ssbd() != KVM_SSBD_KERNEL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If this kernel supports the workaround to be switched on
|
||||
* or off, make sure it matches the requested setting.
|
||||
*/
|
||||
switch (wa_level) {
|
||||
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
|
||||
kvm_arm_set_vcpu_workaround_2_flag(vcpu,
|
||||
val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED);
|
||||
break;
|
||||
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
|
||||
kvm_arm_set_vcpu_workaround_2_flag(vcpu, true);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
378
virt/kvm/arm/trace.h
Normal file
378
virt/kvm/arm/trace.h
Normal file
@ -0,0 +1,378 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#if !defined(_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_KVM_H
|
||||
|
||||
#include <kvm/arm_arch_timer.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM kvm
|
||||
|
||||
/*
|
||||
* Tracepoints for entry/exit to guest
|
||||
*/
|
||||
TRACE_EVENT(kvm_entry,
|
||||
TP_PROTO(unsigned long vcpu_pc),
|
||||
TP_ARGS(vcpu_pc),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_pc )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
),
|
||||
|
||||
TP_printk("PC: 0x%08lx", __entry->vcpu_pc)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_exit,
|
||||
TP_PROTO(int ret, unsigned int esr_ec, unsigned long vcpu_pc),
|
||||
TP_ARGS(ret, esr_ec, vcpu_pc),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( int, ret )
|
||||
__field( unsigned int, esr_ec )
|
||||
__field( unsigned long, vcpu_pc )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->ret = ARM_EXCEPTION_CODE(ret);
|
||||
__entry->esr_ec = ARM_EXCEPTION_IS_TRAP(ret) ? esr_ec : 0;
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
),
|
||||
|
||||
TP_printk("%s: HSR_EC: 0x%04x (%s), PC: 0x%08lx",
|
||||
__print_symbolic(__entry->ret, kvm_arm_exception_type),
|
||||
__entry->esr_ec,
|
||||
__print_symbolic(__entry->esr_ec, kvm_arm_exception_class),
|
||||
__entry->vcpu_pc)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_guest_fault,
|
||||
TP_PROTO(unsigned long vcpu_pc, unsigned long hsr,
|
||||
unsigned long hxfar,
|
||||
unsigned long long ipa),
|
||||
TP_ARGS(vcpu_pc, hsr, hxfar, ipa),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_pc )
|
||||
__field( unsigned long, hsr )
|
||||
__field( unsigned long, hxfar )
|
||||
__field( unsigned long long, ipa )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
__entry->hsr = hsr;
|
||||
__entry->hxfar = hxfar;
|
||||
__entry->ipa = ipa;
|
||||
),
|
||||
|
||||
TP_printk("ipa %#llx, hsr %#08lx, hxfar %#08lx, pc %#08lx",
|
||||
__entry->ipa, __entry->hsr,
|
||||
__entry->hxfar, __entry->vcpu_pc)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_access_fault,
|
||||
TP_PROTO(unsigned long ipa),
|
||||
TP_ARGS(ipa),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, ipa )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->ipa = ipa;
|
||||
),
|
||||
|
||||
TP_printk("IPA: %lx", __entry->ipa)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_irq_line,
|
||||
TP_PROTO(unsigned int type, int vcpu_idx, int irq_num, int level),
|
||||
TP_ARGS(type, vcpu_idx, irq_num, level),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned int, type )
|
||||
__field( int, vcpu_idx )
|
||||
__field( int, irq_num )
|
||||
__field( int, level )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->type = type;
|
||||
__entry->vcpu_idx = vcpu_idx;
|
||||
__entry->irq_num = irq_num;
|
||||
__entry->level = level;
|
||||
),
|
||||
|
||||
TP_printk("Inject %s interrupt (%d), vcpu->idx: %d, num: %d, level: %d",
|
||||
(__entry->type == KVM_ARM_IRQ_TYPE_CPU) ? "CPU" :
|
||||
(__entry->type == KVM_ARM_IRQ_TYPE_PPI) ? "VGIC PPI" :
|
||||
(__entry->type == KVM_ARM_IRQ_TYPE_SPI) ? "VGIC SPI" : "UNKNOWN",
|
||||
__entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_mmio_emulate,
|
||||
TP_PROTO(unsigned long vcpu_pc, unsigned long instr,
|
||||
unsigned long cpsr),
|
||||
TP_ARGS(vcpu_pc, instr, cpsr),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_pc )
|
||||
__field( unsigned long, instr )
|
||||
__field( unsigned long, cpsr )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
__entry->instr = instr;
|
||||
__entry->cpsr = cpsr;
|
||||
),
|
||||
|
||||
TP_printk("Emulate MMIO at: 0x%08lx (instr: %08lx, cpsr: %08lx)",
|
||||
__entry->vcpu_pc, __entry->instr, __entry->cpsr)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_unmap_hva_range,
|
||||
TP_PROTO(unsigned long start, unsigned long end),
|
||||
TP_ARGS(start, end),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, start )
|
||||
__field( unsigned long, end )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->start = start;
|
||||
__entry->end = end;
|
||||
),
|
||||
|
||||
TP_printk("mmu notifier unmap range: %#08lx -- %#08lx",
|
||||
__entry->start, __entry->end)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_set_spte_hva,
|
||||
TP_PROTO(unsigned long hva),
|
||||
TP_ARGS(hva),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, hva )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->hva = hva;
|
||||
),
|
||||
|
||||
TP_printk("mmu notifier set pte hva: %#08lx", __entry->hva)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_age_hva,
|
||||
TP_PROTO(unsigned long start, unsigned long end),
|
||||
TP_ARGS(start, end),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, start )
|
||||
__field( unsigned long, end )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->start = start;
|
||||
__entry->end = end;
|
||||
),
|
||||
|
||||
TP_printk("mmu notifier age hva: %#08lx -- %#08lx",
|
||||
__entry->start, __entry->end)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_test_age_hva,
|
||||
TP_PROTO(unsigned long hva),
|
||||
TP_ARGS(hva),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, hva )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->hva = hva;
|
||||
),
|
||||
|
||||
TP_printk("mmu notifier test age hva: %#08lx", __entry->hva)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_set_way_flush,
|
||||
TP_PROTO(unsigned long vcpu_pc, bool cache),
|
||||
TP_ARGS(vcpu_pc, cache),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_pc )
|
||||
__field( bool, cache )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
__entry->cache = cache;
|
||||
),
|
||||
|
||||
TP_printk("S/W flush at 0x%016lx (cache %s)",
|
||||
__entry->vcpu_pc, __entry->cache ? "on" : "off")
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_toggle_cache,
|
||||
TP_PROTO(unsigned long vcpu_pc, bool was, bool now),
|
||||
TP_ARGS(vcpu_pc, was, now),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_pc )
|
||||
__field( bool, was )
|
||||
__field( bool, now )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
__entry->was = was;
|
||||
__entry->now = now;
|
||||
),
|
||||
|
||||
TP_printk("VM op at 0x%016lx (cache was %s, now %s)",
|
||||
__entry->vcpu_pc, __entry->was ? "on" : "off",
|
||||
__entry->now ? "on" : "off")
|
||||
);
|
||||
|
||||
/*
|
||||
* Tracepoints for arch_timer
|
||||
*/
|
||||
TRACE_EVENT(kvm_timer_update_irq,
|
||||
TP_PROTO(unsigned long vcpu_id, __u32 irq, int level),
|
||||
TP_ARGS(vcpu_id, irq, level),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_id )
|
||||
__field( __u32, irq )
|
||||
__field( int, level )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_id = vcpu_id;
|
||||
__entry->irq = irq;
|
||||
__entry->level = level;
|
||||
),
|
||||
|
||||
TP_printk("VCPU: %ld, IRQ %d, level %d",
|
||||
__entry->vcpu_id, __entry->irq, __entry->level)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_get_timer_map,
|
||||
TP_PROTO(unsigned long vcpu_id, struct timer_map *map),
|
||||
TP_ARGS(vcpu_id, map),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_id )
|
||||
__field( int, direct_vtimer )
|
||||
__field( int, direct_ptimer )
|
||||
__field( int, emul_ptimer )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_id = vcpu_id;
|
||||
__entry->direct_vtimer = arch_timer_ctx_index(map->direct_vtimer);
|
||||
__entry->direct_ptimer =
|
||||
(map->direct_ptimer) ? arch_timer_ctx_index(map->direct_ptimer) : -1;
|
||||
__entry->emul_ptimer =
|
||||
(map->emul_ptimer) ? arch_timer_ctx_index(map->emul_ptimer) : -1;
|
||||
),
|
||||
|
||||
TP_printk("VCPU: %ld, dv: %d, dp: %d, ep: %d",
|
||||
__entry->vcpu_id,
|
||||
__entry->direct_vtimer,
|
||||
__entry->direct_ptimer,
|
||||
__entry->emul_ptimer)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_timer_save_state,
|
||||
TP_PROTO(struct arch_timer_context *ctx),
|
||||
TP_ARGS(ctx),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, ctl )
|
||||
__field( unsigned long long, cval )
|
||||
__field( int, timer_idx )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->ctl = ctx->cnt_ctl;
|
||||
__entry->cval = ctx->cnt_cval;
|
||||
__entry->timer_idx = arch_timer_ctx_index(ctx);
|
||||
),
|
||||
|
||||
TP_printk(" CTL: %#08lx CVAL: %#16llx arch_timer_ctx_index: %d",
|
||||
__entry->ctl,
|
||||
__entry->cval,
|
||||
__entry->timer_idx)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_timer_restore_state,
|
||||
TP_PROTO(struct arch_timer_context *ctx),
|
||||
TP_ARGS(ctx),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, ctl )
|
||||
__field( unsigned long long, cval )
|
||||
__field( int, timer_idx )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->ctl = ctx->cnt_ctl;
|
||||
__entry->cval = ctx->cnt_cval;
|
||||
__entry->timer_idx = arch_timer_ctx_index(ctx);
|
||||
),
|
||||
|
||||
TP_printk("CTL: %#08lx CVAL: %#16llx arch_timer_ctx_index: %d",
|
||||
__entry->ctl,
|
||||
__entry->cval,
|
||||
__entry->timer_idx)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_timer_hrtimer_expire,
|
||||
TP_PROTO(struct arch_timer_context *ctx),
|
||||
TP_ARGS(ctx),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( int, timer_idx )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->timer_idx = arch_timer_ctx_index(ctx);
|
||||
),
|
||||
|
||||
TP_printk("arch_timer_ctx_index: %d", __entry->timer_idx)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_timer_emulate,
|
||||
TP_PROTO(struct arch_timer_context *ctx, bool should_fire),
|
||||
TP_ARGS(ctx, should_fire),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( int, timer_idx )
|
||||
__field( bool, should_fire )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->timer_idx = arch_timer_ctx_index(ctx);
|
||||
__entry->should_fire = should_fire;
|
||||
),
|
||||
|
||||
TP_printk("arch_timer_ctx_index: %d (should_fire: %d)",
|
||||
__entry->timer_idx, __entry->should_fire)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_KVM_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH ../../virt/kvm/arm
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
38
virt/kvm/arm/vgic/trace.h
Normal file
38
virt/kvm/arm/vgic/trace.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#if !defined(_TRACE_VGIC_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_VGIC_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM kvm
|
||||
|
||||
TRACE_EVENT(vgic_update_irq_pending,
|
||||
TP_PROTO(unsigned long vcpu_id, __u32 irq, bool level),
|
||||
TP_ARGS(vcpu_id, irq, level),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_id )
|
||||
__field( __u32, irq )
|
||||
__field( bool, level )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_id = vcpu_id;
|
||||
__entry->irq = irq;
|
||||
__entry->level = level;
|
||||
),
|
||||
|
||||
TP_printk("VCPU: %ld, IRQ %d, level: %d",
|
||||
__entry->vcpu_id, __entry->irq, __entry->level)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_VGIC_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH ../../virt/kvm/arm/vgic
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
288
virt/kvm/arm/vgic/vgic-debug.c
Normal file
288
virt/kvm/arm/vgic/vgic-debug.c
Normal file
@ -0,0 +1,288 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2016 Linaro
|
||||
* Author: Christoffer Dall <christoffer.dall@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include "vgic.h"
|
||||
|
||||
/*
|
||||
* Structure to control looping through the entire vgic state. We start at
|
||||
* zero for each field and move upwards. So, if dist_id is 0 we print the
|
||||
* distributor info. When dist_id is 1, we have already printed it and move
|
||||
* on.
|
||||
*
|
||||
* When vcpu_id < nr_cpus we print the vcpu info until vcpu_id == nr_cpus and
|
||||
* so on.
|
||||
*/
|
||||
struct vgic_state_iter {
|
||||
int nr_cpus;
|
||||
int nr_spis;
|
||||
int nr_lpis;
|
||||
int dist_id;
|
||||
int vcpu_id;
|
||||
int intid;
|
||||
int lpi_idx;
|
||||
u32 *lpi_array;
|
||||
};
|
||||
|
||||
static void iter_next(struct vgic_state_iter *iter)
|
||||
{
|
||||
if (iter->dist_id == 0) {
|
||||
iter->dist_id++;
|
||||
return;
|
||||
}
|
||||
|
||||
iter->intid++;
|
||||
if (iter->intid == VGIC_NR_PRIVATE_IRQS &&
|
||||
++iter->vcpu_id < iter->nr_cpus)
|
||||
iter->intid = 0;
|
||||
|
||||
if (iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS)) {
|
||||
if (iter->lpi_idx < iter->nr_lpis)
|
||||
iter->intid = iter->lpi_array[iter->lpi_idx];
|
||||
iter->lpi_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
static void iter_init(struct kvm *kvm, struct vgic_state_iter *iter,
|
||||
loff_t pos)
|
||||
{
|
||||
int nr_cpus = atomic_read(&kvm->online_vcpus);
|
||||
|
||||
memset(iter, 0, sizeof(*iter));
|
||||
|
||||
iter->nr_cpus = nr_cpus;
|
||||
iter->nr_spis = kvm->arch.vgic.nr_spis;
|
||||
if (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
|
||||
iter->nr_lpis = vgic_copy_lpi_list(kvm, NULL, &iter->lpi_array);
|
||||
if (iter->nr_lpis < 0)
|
||||
iter->nr_lpis = 0;
|
||||
}
|
||||
|
||||
/* Fast forward to the right position if needed */
|
||||
while (pos--)
|
||||
iter_next(iter);
|
||||
}
|
||||
|
||||
static bool end_of_vgic(struct vgic_state_iter *iter)
|
||||
{
|
||||
return iter->dist_id > 0 &&
|
||||
iter->vcpu_id == iter->nr_cpus &&
|
||||
iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS) &&
|
||||
iter->lpi_idx > iter->nr_lpis;
|
||||
}
|
||||
|
||||
static void *vgic_debug_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct kvm *kvm = (struct kvm *)s->private;
|
||||
struct vgic_state_iter *iter;
|
||||
|
||||
mutex_lock(&kvm->lock);
|
||||
iter = kvm->arch.vgic.iter;
|
||||
if (iter) {
|
||||
iter = ERR_PTR(-EBUSY);
|
||||
goto out;
|
||||
}
|
||||
|
||||
iter = kmalloc(sizeof(*iter), GFP_KERNEL);
|
||||
if (!iter) {
|
||||
iter = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
iter_init(kvm, iter, *pos);
|
||||
kvm->arch.vgic.iter = iter;
|
||||
|
||||
if (end_of_vgic(iter))
|
||||
iter = NULL;
|
||||
out:
|
||||
mutex_unlock(&kvm->lock);
|
||||
return iter;
|
||||
}
|
||||
|
||||
static void *vgic_debug_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct kvm *kvm = (struct kvm *)s->private;
|
||||
struct vgic_state_iter *iter = kvm->arch.vgic.iter;
|
||||
|
||||
++*pos;
|
||||
iter_next(iter);
|
||||
if (end_of_vgic(iter))
|
||||
iter = NULL;
|
||||
return iter;
|
||||
}
|
||||
|
||||
static void vgic_debug_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
struct kvm *kvm = (struct kvm *)s->private;
|
||||
struct vgic_state_iter *iter;
|
||||
|
||||
/*
|
||||
* If the seq file wasn't properly opened, there's nothing to clearn
|
||||
* up.
|
||||
*/
|
||||
if (IS_ERR(v))
|
||||
return;
|
||||
|
||||
mutex_lock(&kvm->lock);
|
||||
iter = kvm->arch.vgic.iter;
|
||||
kfree(iter->lpi_array);
|
||||
kfree(iter);
|
||||
kvm->arch.vgic.iter = NULL;
|
||||
mutex_unlock(&kvm->lock);
|
||||
}
|
||||
|
||||
static void print_dist_state(struct seq_file *s, struct vgic_dist *dist)
|
||||
{
|
||||
bool v3 = dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3;
|
||||
|
||||
seq_printf(s, "Distributor\n");
|
||||
seq_printf(s, "===========\n");
|
||||
seq_printf(s, "vgic_model:\t%s\n", v3 ? "GICv3" : "GICv2");
|
||||
seq_printf(s, "nr_spis:\t%d\n", dist->nr_spis);
|
||||
if (v3)
|
||||
seq_printf(s, "nr_lpis:\t%d\n", dist->lpi_list_count);
|
||||
seq_printf(s, "enabled:\t%d\n", dist->enabled);
|
||||
seq_printf(s, "\n");
|
||||
|
||||
seq_printf(s, "P=pending_latch, L=line_level, A=active\n");
|
||||
seq_printf(s, "E=enabled, H=hw, C=config (level=1, edge=0)\n");
|
||||
seq_printf(s, "G=group\n");
|
||||
}
|
||||
|
||||
static void print_header(struct seq_file *s, struct vgic_irq *irq,
|
||||
struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int id = 0;
|
||||
char *hdr = "SPI ";
|
||||
|
||||
if (vcpu) {
|
||||
hdr = "VCPU";
|
||||
id = vcpu->vcpu_id;
|
||||
}
|
||||
|
||||
seq_printf(s, "\n");
|
||||
seq_printf(s, "%s%2d TYP ID TGT_ID PLAEHCG HWID TARGET SRC PRI VCPU_ID\n", hdr, id);
|
||||
seq_printf(s, "----------------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
static void print_irq_state(struct seq_file *s, struct vgic_irq *irq,
|
||||
struct kvm_vcpu *vcpu)
|
||||
{
|
||||
char *type;
|
||||
if (irq->intid < VGIC_NR_SGIS)
|
||||
type = "SGI";
|
||||
else if (irq->intid < VGIC_NR_PRIVATE_IRQS)
|
||||
type = "PPI";
|
||||
else if (irq->intid < VGIC_MAX_SPI)
|
||||
type = "SPI";
|
||||
else
|
||||
type = "LPI";
|
||||
|
||||
if (irq->intid ==0 || irq->intid == VGIC_NR_PRIVATE_IRQS)
|
||||
print_header(s, irq, vcpu);
|
||||
|
||||
seq_printf(s, " %s %4d "
|
||||
" %2d "
|
||||
"%d%d%d%d%d%d%d "
|
||||
"%8d "
|
||||
"%8x "
|
||||
" %2x "
|
||||
"%3d "
|
||||
" %2d "
|
||||
"\n",
|
||||
type, irq->intid,
|
||||
(irq->target_vcpu) ? irq->target_vcpu->vcpu_id : -1,
|
||||
irq->pending_latch,
|
||||
irq->line_level,
|
||||
irq->active,
|
||||
irq->enabled,
|
||||
irq->hw,
|
||||
irq->config == VGIC_CONFIG_LEVEL,
|
||||
irq->group,
|
||||
irq->hwintid,
|
||||
irq->mpidr,
|
||||
irq->source,
|
||||
irq->priority,
|
||||
(irq->vcpu) ? irq->vcpu->vcpu_id : -1);
|
||||
}
|
||||
|
||||
static int vgic_debug_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct kvm *kvm = (struct kvm *)s->private;
|
||||
struct vgic_state_iter *iter = (struct vgic_state_iter *)v;
|
||||
struct vgic_irq *irq;
|
||||
struct kvm_vcpu *vcpu = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
if (iter->dist_id == 0) {
|
||||
print_dist_state(s, &kvm->arch.vgic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!kvm->arch.vgic.initialized)
|
||||
return 0;
|
||||
|
||||
if (iter->vcpu_id < iter->nr_cpus)
|
||||
vcpu = kvm_get_vcpu(kvm, iter->vcpu_id);
|
||||
|
||||
irq = vgic_get_irq(kvm, vcpu, iter->intid);
|
||||
if (!irq) {
|
||||
seq_printf(s, " LPI %4d freed\n", iter->intid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
print_irq_state(s, irq, vcpu);
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
|
||||
vgic_put_irq(kvm, irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations vgic_debug_seq_ops = {
|
||||
.start = vgic_debug_start,
|
||||
.next = vgic_debug_next,
|
||||
.stop = vgic_debug_stop,
|
||||
.show = vgic_debug_show
|
||||
};
|
||||
|
||||
static int debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret;
|
||||
ret = seq_open(file, &vgic_debug_seq_ops);
|
||||
if (!ret) {
|
||||
struct seq_file *seq;
|
||||
/* seq_open will have modified file->private_data */
|
||||
seq = file->private_data;
|
||||
seq->private = inode->i_private;
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static const struct file_operations vgic_debug_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release
|
||||
};
|
||||
|
||||
void vgic_debug_init(struct kvm *kvm)
|
||||
{
|
||||
debugfs_create_file("vgic-state", 0444, kvm->debugfs_dentry, kvm,
|
||||
&vgic_debug_fops);
|
||||
}
|
||||
|
||||
void vgic_debug_destroy(struct kvm *kvm)
|
||||
{
|
||||
}
|
552
virt/kvm/arm/vgic/vgic-init.c
Normal file
552
virt/kvm/arm/vgic/vgic-init.c
Normal file
@ -0,0 +1,552 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015, 2016 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include "vgic.h"
|
||||
|
||||
/*
|
||||
* Initialization rules: there are multiple stages to the vgic
|
||||
* initialization, both for the distributor and the CPU interfaces. The basic
|
||||
* idea is that even though the VGIC is not functional or not requested from
|
||||
* user space, the critical path of the run loop can still call VGIC functions
|
||||
* that just won't do anything, without them having to check additional
|
||||
* initialization flags to ensure they don't look at uninitialized data
|
||||
* structures.
|
||||
*
|
||||
* Distributor:
|
||||
*
|
||||
* - kvm_vgic_early_init(): initialization of static data that doesn't
|
||||
* depend on any sizing information or emulation type. No allocation
|
||||
* is allowed there.
|
||||
*
|
||||
* - vgic_init(): allocation and initialization of the generic data
|
||||
* structures that depend on sizing information (number of CPUs,
|
||||
* number of interrupts). Also initializes the vcpu specific data
|
||||
* structures. Can be executed lazily for GICv2.
|
||||
*
|
||||
* CPU Interface:
|
||||
*
|
||||
* - kvm_vgic_vcpu_init(): initialization of static data that
|
||||
* doesn't depend on any sizing information or emulation type. No
|
||||
* allocation is allowed there.
|
||||
*/
|
||||
|
||||
/* EARLY INIT */
|
||||
|
||||
/**
|
||||
* kvm_vgic_early_init() - Initialize static VGIC VCPU data structures
|
||||
* @kvm: The VM whose VGIC districutor should be initialized
|
||||
*
|
||||
* Only do initialization of static structures that don't require any
|
||||
* allocation or sizing information from userspace. vgic_init() called
|
||||
* kvm_vgic_dist_init() which takes care of the rest.
|
||||
*/
|
||||
void kvm_vgic_early_init(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
|
||||
INIT_LIST_HEAD(&dist->lpi_list_head);
|
||||
INIT_LIST_HEAD(&dist->lpi_translation_cache);
|
||||
raw_spin_lock_init(&dist->lpi_list_lock);
|
||||
}
|
||||
|
||||
/* CREATION */
|
||||
|
||||
/**
|
||||
* kvm_vgic_create: triggered by the instantiation of the VGIC device by
|
||||
* user space, either through the legacy KVM_CREATE_IRQCHIP ioctl (v2 only)
|
||||
* or through the generic KVM_CREATE_DEVICE API ioctl.
|
||||
* irqchip_in_kernel() tells you if this function succeeded or not.
|
||||
* @kvm: kvm struct pointer
|
||||
* @type: KVM_DEV_TYPE_ARM_VGIC_V[23]
|
||||
*/
|
||||
int kvm_vgic_create(struct kvm *kvm, u32 type)
|
||||
{
|
||||
int i, vcpu_lock_idx = -1, ret;
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
if (irqchip_in_kernel(kvm))
|
||||
return -EEXIST;
|
||||
|
||||
/*
|
||||
* This function is also called by the KVM_CREATE_IRQCHIP handler,
|
||||
* which had no chance yet to check the availability of the GICv2
|
||||
* emulation. So check this here again. KVM_CREATE_DEVICE does
|
||||
* the proper checks already.
|
||||
*/
|
||||
if (type == KVM_DEV_TYPE_ARM_VGIC_V2 &&
|
||||
!kvm_vgic_global_state.can_emulate_gicv2)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Any time a vcpu is run, vcpu_load is called which tries to grab the
|
||||
* vcpu->mutex. By grabbing the vcpu->mutex of all VCPUs we ensure
|
||||
* that no other VCPUs are run while we create the vgic.
|
||||
*/
|
||||
ret = -EBUSY;
|
||||
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||
if (!mutex_trylock(&vcpu->mutex))
|
||||
goto out_unlock;
|
||||
vcpu_lock_idx = i;
|
||||
}
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||
if (vcpu->arch.has_run_once)
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
if (type == KVM_DEV_TYPE_ARM_VGIC_V2)
|
||||
kvm->arch.max_vcpus = VGIC_V2_MAX_CPUS;
|
||||
else
|
||||
kvm->arch.max_vcpus = VGIC_V3_MAX_CPUS;
|
||||
|
||||
if (atomic_read(&kvm->online_vcpus) > kvm->arch.max_vcpus) {
|
||||
ret = -E2BIG;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
kvm->arch.vgic.in_kernel = true;
|
||||
kvm->arch.vgic.vgic_model = type;
|
||||
|
||||
kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
|
||||
|
||||
if (type == KVM_DEV_TYPE_ARM_VGIC_V2)
|
||||
kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
|
||||
else
|
||||
INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
|
||||
|
||||
out_unlock:
|
||||
for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
|
||||
vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx);
|
||||
mutex_unlock(&vcpu->mutex);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* INIT/DESTROY */
|
||||
|
||||
/**
|
||||
* kvm_vgic_dist_init: initialize the dist data structures
|
||||
* @kvm: kvm struct pointer
|
||||
* @nr_spis: number of spis, frozen by caller
|
||||
*/
|
||||
static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct kvm_vcpu *vcpu0 = kvm_get_vcpu(kvm, 0);
|
||||
int i;
|
||||
|
||||
dist->spis = kcalloc(nr_spis, sizeof(struct vgic_irq), GFP_KERNEL);
|
||||
if (!dist->spis)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* In the following code we do not take the irq struct lock since
|
||||
* no other action on irq structs can happen while the VGIC is
|
||||
* not initialized yet:
|
||||
* If someone wants to inject an interrupt or does a MMIO access, we
|
||||
* require prior initialization in case of a virtual GICv3 or trigger
|
||||
* initialization when using a virtual GICv2.
|
||||
*/
|
||||
for (i = 0; i < nr_spis; i++) {
|
||||
struct vgic_irq *irq = &dist->spis[i];
|
||||
|
||||
irq->intid = i + VGIC_NR_PRIVATE_IRQS;
|
||||
INIT_LIST_HEAD(&irq->ap_list);
|
||||
raw_spin_lock_init(&irq->irq_lock);
|
||||
irq->vcpu = NULL;
|
||||
irq->target_vcpu = vcpu0;
|
||||
kref_init(&irq->refcount);
|
||||
switch (dist->vgic_model) {
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V2:
|
||||
irq->targets = 0;
|
||||
irq->group = 0;
|
||||
break;
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V3:
|
||||
irq->mpidr = 0;
|
||||
irq->group = 1;
|
||||
break;
|
||||
default:
|
||||
kfree(dist->spis);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vgic_vcpu_init() - Initialize static VGIC VCPU data
|
||||
* structures and register VCPU-specific KVM iodevs
|
||||
*
|
||||
* @vcpu: pointer to the VCPU being created and initialized
|
||||
*
|
||||
* Only do initialization, but do not actually enable the
|
||||
* VGIC CPU interface
|
||||
*/
|
||||
int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
vgic_cpu->rd_iodev.base_addr = VGIC_ADDR_UNDEF;
|
||||
|
||||
INIT_LIST_HEAD(&vgic_cpu->ap_list_head);
|
||||
raw_spin_lock_init(&vgic_cpu->ap_list_lock);
|
||||
|
||||
/*
|
||||
* Enable and configure all SGIs to be edge-triggered and
|
||||
* configure all PPIs as level-triggered.
|
||||
*/
|
||||
for (i = 0; i < VGIC_NR_PRIVATE_IRQS; i++) {
|
||||
struct vgic_irq *irq = &vgic_cpu->private_irqs[i];
|
||||
|
||||
INIT_LIST_HEAD(&irq->ap_list);
|
||||
raw_spin_lock_init(&irq->irq_lock);
|
||||
irq->intid = i;
|
||||
irq->vcpu = NULL;
|
||||
irq->target_vcpu = vcpu;
|
||||
kref_init(&irq->refcount);
|
||||
if (vgic_irq_is_sgi(i)) {
|
||||
/* SGIs */
|
||||
irq->enabled = 1;
|
||||
irq->config = VGIC_CONFIG_EDGE;
|
||||
} else {
|
||||
/* PPIs */
|
||||
irq->config = VGIC_CONFIG_LEVEL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!irqchip_in_kernel(vcpu->kvm))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If we are creating a VCPU with a GICv3 we must also register the
|
||||
* KVM io device for the redistributor that belongs to this VCPU.
|
||||
*/
|
||||
if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
|
||||
mutex_lock(&vcpu->kvm->lock);
|
||||
ret = vgic_register_redist_iodev(vcpu);
|
||||
mutex_unlock(&vcpu->kvm->lock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kvm_vgic_vcpu_enable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (kvm_vgic_global_state.type == VGIC_V2)
|
||||
vgic_v2_enable(vcpu);
|
||||
else
|
||||
vgic_v3_enable(vcpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* vgic_init: allocates and initializes dist and vcpu data structures
|
||||
* depending on two dimensioning parameters:
|
||||
* - the number of spis
|
||||
* - the number of vcpus
|
||||
* The function is generally called when nr_spis has been explicitly set
|
||||
* by the guest through the KVM DEVICE API. If not nr_spis is set to 256.
|
||||
* vgic_initialized() returns true when this function has succeeded.
|
||||
* Must be called with kvm->lock held!
|
||||
*/
|
||||
int vgic_init(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct kvm_vcpu *vcpu;
|
||||
int ret = 0, i, idx;
|
||||
|
||||
if (vgic_initialized(kvm))
|
||||
return 0;
|
||||
|
||||
/* Are we also in the middle of creating a VCPU? */
|
||||
if (kvm->created_vcpus != atomic_read(&kvm->online_vcpus))
|
||||
return -EBUSY;
|
||||
|
||||
/* freeze the number of spis */
|
||||
if (!dist->nr_spis)
|
||||
dist->nr_spis = VGIC_NR_IRQS_LEGACY - VGIC_NR_PRIVATE_IRQS;
|
||||
|
||||
ret = kvm_vgic_dist_init(kvm, dist->nr_spis);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Initialize groups on CPUs created before the VGIC type was known */
|
||||
kvm_for_each_vcpu(idx, vcpu, kvm) {
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
|
||||
for (i = 0; i < VGIC_NR_PRIVATE_IRQS; i++) {
|
||||
struct vgic_irq *irq = &vgic_cpu->private_irqs[i];
|
||||
switch (dist->vgic_model) {
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V3:
|
||||
irq->group = 1;
|
||||
irq->mpidr = kvm_vcpu_get_mpidr_aff(vcpu);
|
||||
break;
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V2:
|
||||
irq->group = 0;
|
||||
irq->targets = 1U << idx;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vgic_has_its(kvm)) {
|
||||
vgic_lpi_translation_cache_init(kvm);
|
||||
ret = vgic_v4_init(kvm);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm)
|
||||
kvm_vgic_vcpu_enable(vcpu);
|
||||
|
||||
ret = kvm_vgic_setup_default_irq_routing(kvm);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
vgic_debug_init(kvm);
|
||||
|
||||
dist->implementation_rev = 2;
|
||||
dist->initialized = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kvm_vgic_dist_destroy(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct vgic_redist_region *rdreg, *next;
|
||||
|
||||
dist->ready = false;
|
||||
dist->initialized = false;
|
||||
|
||||
kfree(dist->spis);
|
||||
dist->spis = NULL;
|
||||
dist->nr_spis = 0;
|
||||
|
||||
if (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
|
||||
list_for_each_entry_safe(rdreg, next, &dist->rd_regions, list) {
|
||||
list_del(&rdreg->list);
|
||||
kfree(rdreg);
|
||||
}
|
||||
INIT_LIST_HEAD(&dist->rd_regions);
|
||||
}
|
||||
|
||||
if (vgic_has_its(kvm))
|
||||
vgic_lpi_translation_cache_destroy(kvm);
|
||||
|
||||
if (vgic_supports_direct_msis(kvm))
|
||||
vgic_v4_teardown(kvm);
|
||||
}
|
||||
|
||||
void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
|
||||
INIT_LIST_HEAD(&vgic_cpu->ap_list_head);
|
||||
}
|
||||
|
||||
/* To be called with kvm->lock held */
|
||||
static void __kvm_vgic_destroy(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
int i;
|
||||
|
||||
vgic_debug_destroy(kvm);
|
||||
|
||||
kvm_vgic_dist_destroy(kvm);
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm)
|
||||
kvm_vgic_vcpu_destroy(vcpu);
|
||||
}
|
||||
|
||||
void kvm_vgic_destroy(struct kvm *kvm)
|
||||
{
|
||||
mutex_lock(&kvm->lock);
|
||||
__kvm_vgic_destroy(kvm);
|
||||
mutex_unlock(&kvm->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_lazy_init: Lazy init is only allowed if the GIC exposed to the guest
|
||||
* is a GICv2. A GICv3 must be explicitly initialized by the guest using the
|
||||
* KVM_DEV_ARM_VGIC_GRP_CTRL KVM_DEVICE group.
|
||||
* @kvm: kvm struct pointer
|
||||
*/
|
||||
int vgic_lazy_init(struct kvm *kvm)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (unlikely(!vgic_initialized(kvm))) {
|
||||
/*
|
||||
* We only provide the automatic initialization of the VGIC
|
||||
* for the legacy case of a GICv2. Any other type must
|
||||
* be explicitly initialized once setup with the respective
|
||||
* KVM device call.
|
||||
*/
|
||||
if (kvm->arch.vgic.vgic_model != KVM_DEV_TYPE_ARM_VGIC_V2)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&kvm->lock);
|
||||
ret = vgic_init(kvm);
|
||||
mutex_unlock(&kvm->lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* RESOURCE MAPPING */
|
||||
|
||||
/**
|
||||
* Map the MMIO regions depending on the VGIC model exposed to the guest
|
||||
* called on the first VCPU run.
|
||||
* Also map the virtual CPU interface into the VM.
|
||||
* v2/v3 derivatives call vgic_init if not already done.
|
||||
* vgic_ready() returns true if this function has succeeded.
|
||||
* @kvm: kvm struct pointer
|
||||
*/
|
||||
int kvm_vgic_map_resources(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&kvm->lock);
|
||||
if (!irqchip_in_kernel(kvm))
|
||||
goto out;
|
||||
|
||||
if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2)
|
||||
ret = vgic_v2_map_resources(kvm);
|
||||
else
|
||||
ret = vgic_v3_map_resources(kvm);
|
||||
|
||||
if (ret)
|
||||
__kvm_vgic_destroy(kvm);
|
||||
|
||||
out:
|
||||
mutex_unlock(&kvm->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* GENERIC PROBE */
|
||||
|
||||
static int vgic_init_cpu_starting(unsigned int cpu)
|
||||
{
|
||||
enable_percpu_irq(kvm_vgic_global_state.maint_irq, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int vgic_init_cpu_dying(unsigned int cpu)
|
||||
{
|
||||
disable_percpu_irq(kvm_vgic_global_state.maint_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t vgic_maintenance_handler(int irq, void *data)
|
||||
{
|
||||
/*
|
||||
* We cannot rely on the vgic maintenance interrupt to be
|
||||
* delivered synchronously. This means we can only use it to
|
||||
* exit the VM, and we perform the handling of EOIed
|
||||
* interrupts on the exit path (see vgic_fold_lr_state).
|
||||
*/
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vgic_init_cpu_hardware - initialize the GIC VE hardware
|
||||
*
|
||||
* For a specific CPU, initialize the GIC VE hardware.
|
||||
*/
|
||||
void kvm_vgic_init_cpu_hardware(void)
|
||||
{
|
||||
BUG_ON(preemptible());
|
||||
|
||||
/*
|
||||
* We want to make sure the list registers start out clear so that we
|
||||
* only have the program the used registers.
|
||||
*/
|
||||
if (kvm_vgic_global_state.type == VGIC_V2)
|
||||
vgic_v2_init_lrs();
|
||||
else
|
||||
kvm_call_hyp(__vgic_v3_init_lrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vgic_hyp_init: populates the kvm_vgic_global_state variable
|
||||
* according to the host GIC model. Accordingly calls either
|
||||
* vgic_v2/v3_probe which registers the KVM_DEVICE that can be
|
||||
* instantiated by a guest later on .
|
||||
*/
|
||||
int kvm_vgic_hyp_init(void)
|
||||
{
|
||||
const struct gic_kvm_info *gic_kvm_info;
|
||||
int ret;
|
||||
|
||||
gic_kvm_info = gic_get_kvm_info();
|
||||
if (!gic_kvm_info)
|
||||
return -ENODEV;
|
||||
|
||||
if (!gic_kvm_info->maint_irq) {
|
||||
kvm_err("No vgic maintenance irq\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
switch (gic_kvm_info->type) {
|
||||
case GIC_V2:
|
||||
ret = vgic_v2_probe(gic_kvm_info);
|
||||
break;
|
||||
case GIC_V3:
|
||||
ret = vgic_v3_probe(gic_kvm_info);
|
||||
if (!ret) {
|
||||
static_branch_enable(&kvm_vgic_global_state.gicv3_cpuif);
|
||||
kvm_info("GIC system register CPU interface enabled\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kvm_vgic_global_state.maint_irq = gic_kvm_info->maint_irq;
|
||||
ret = request_percpu_irq(kvm_vgic_global_state.maint_irq,
|
||||
vgic_maintenance_handler,
|
||||
"vgic", kvm_get_running_vcpus());
|
||||
if (ret) {
|
||||
kvm_err("Cannot register interrupt %d\n",
|
||||
kvm_vgic_global_state.maint_irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cpuhp_setup_state(CPUHP_AP_KVM_ARM_VGIC_INIT_STARTING,
|
||||
"kvm/arm/vgic:starting",
|
||||
vgic_init_cpu_starting, vgic_init_cpu_dying);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register vgic CPU notifier\n");
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
kvm_info("vgic interrupt IRQ%d\n", kvm_vgic_global_state.maint_irq);
|
||||
return 0;
|
||||
|
||||
out_free_irq:
|
||||
free_percpu_irq(kvm_vgic_global_state.maint_irq,
|
||||
kvm_get_running_vcpus());
|
||||
return ret;
|
||||
}
|
141
virt/kvm/arm/vgic/vgic-irqfd.c
Normal file
141
virt/kvm/arm/vgic/vgic-irqfd.c
Normal file
@ -0,0 +1,141 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015, 2016 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <trace/events/kvm.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
#include "vgic.h"
|
||||
|
||||
/**
|
||||
* vgic_irqfd_set_irq: inject the IRQ corresponding to the
|
||||
* irqchip routing entry
|
||||
*
|
||||
* This is the entry point for irqfd IRQ injection
|
||||
*/
|
||||
static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm *kvm, int irq_source_id,
|
||||
int level, bool line_status)
|
||||
{
|
||||
unsigned int spi_id = e->irqchip.pin + VGIC_NR_PRIVATE_IRQS;
|
||||
|
||||
if (!vgic_valid_spi(kvm, spi_id))
|
||||
return -EINVAL;
|
||||
return kvm_vgic_inject_irq(kvm, 0, spi_id, level, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_set_routing_entry: populate a kvm routing entry
|
||||
* from a user routing entry
|
||||
*
|
||||
* @kvm: the VM this entry is applied to
|
||||
* @e: kvm kernel routing entry handle
|
||||
* @ue: user api routing entry handle
|
||||
* return 0 on success, -EINVAL on errors.
|
||||
*/
|
||||
int kvm_set_routing_entry(struct kvm *kvm,
|
||||
struct kvm_kernel_irq_routing_entry *e,
|
||||
const struct kvm_irq_routing_entry *ue)
|
||||
{
|
||||
int r = -EINVAL;
|
||||
|
||||
switch (ue->type) {
|
||||
case KVM_IRQ_ROUTING_IRQCHIP:
|
||||
e->set = vgic_irqfd_set_irq;
|
||||
e->irqchip.irqchip = ue->u.irqchip.irqchip;
|
||||
e->irqchip.pin = ue->u.irqchip.pin;
|
||||
if ((e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) ||
|
||||
(e->irqchip.irqchip >= KVM_NR_IRQCHIPS))
|
||||
goto out;
|
||||
break;
|
||||
case KVM_IRQ_ROUTING_MSI:
|
||||
e->set = kvm_set_msi;
|
||||
e->msi.address_lo = ue->u.msi.address_lo;
|
||||
e->msi.address_hi = ue->u.msi.address_hi;
|
||||
e->msi.data = ue->u.msi.data;
|
||||
e->msi.flags = ue->flags;
|
||||
e->msi.devid = ue->u.msi.devid;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
r = 0;
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void kvm_populate_msi(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm_msi *msi)
|
||||
{
|
||||
msi->address_lo = e->msi.address_lo;
|
||||
msi->address_hi = e->msi.address_hi;
|
||||
msi->data = e->msi.data;
|
||||
msi->flags = e->msi.flags;
|
||||
msi->devid = e->msi.devid;
|
||||
}
|
||||
/**
|
||||
* kvm_set_msi: inject the MSI corresponding to the
|
||||
* MSI routing entry
|
||||
*
|
||||
* This is the entry point for irqfd MSI injection
|
||||
* and userspace MSI injection.
|
||||
*/
|
||||
int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm *kvm, int irq_source_id,
|
||||
int level, bool line_status)
|
||||
{
|
||||
struct kvm_msi msi;
|
||||
|
||||
if (!vgic_has_its(kvm))
|
||||
return -ENODEV;
|
||||
|
||||
if (!level)
|
||||
return -1;
|
||||
|
||||
kvm_populate_msi(e, &msi);
|
||||
return vgic_its_inject_msi(kvm, &msi);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_arch_set_irq_inatomic: fast-path for irqfd injection
|
||||
*
|
||||
* Currently only direct MSI injection is supported.
|
||||
*/
|
||||
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm *kvm, int irq_source_id, int level,
|
||||
bool line_status)
|
||||
{
|
||||
if (e->type == KVM_IRQ_ROUTING_MSI && vgic_has_its(kvm) && level) {
|
||||
struct kvm_msi msi;
|
||||
|
||||
kvm_populate_msi(e, &msi);
|
||||
if (!vgic_its_inject_cached_translation(kvm, &msi))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
int kvm_vgic_setup_default_irq_routing(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_irq_routing_entry *entries;
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
u32 nr = dist->nr_spis;
|
||||
int i, ret;
|
||||
|
||||
entries = kcalloc(nr, sizeof(*entries), GFP_KERNEL);
|
||||
if (!entries)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
entries[i].gsi = i;
|
||||
entries[i].type = KVM_IRQ_ROUTING_IRQCHIP;
|
||||
entries[i].u.irqchip.irqchip = 0;
|
||||
entries[i].u.irqchip.pin = i;
|
||||
}
|
||||
ret = kvm_set_irq_routing(kvm, entries, nr, 0);
|
||||
kfree(entries);
|
||||
return ret;
|
||||
}
|
2774
virt/kvm/arm/vgic/vgic-its.c
Normal file
2774
virt/kvm/arm/vgic/vgic-its.c
Normal file
File diff suppressed because it is too large
Load Diff
741
virt/kvm/arm/vgic/vgic-kvm-device.c
Normal file
741
virt/kvm/arm/vgic/vgic-kvm-device.c
Normal file
@ -0,0 +1,741 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* VGIC: KVM DEVICE API
|
||||
*
|
||||
* Copyright (C) 2015 ARM Ltd.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
#include <linux/kvm_host.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/cputype.h>
|
||||
#include "vgic.h"
|
||||
|
||||
/* common helpers */
|
||||
|
||||
int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
|
||||
phys_addr_t addr, phys_addr_t alignment)
|
||||
{
|
||||
if (addr & ~kvm_phys_mask(kvm))
|
||||
return -E2BIG;
|
||||
|
||||
if (!IS_ALIGNED(addr, alignment))
|
||||
return -EINVAL;
|
||||
|
||||
if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
|
||||
return -EEXIST;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vgic_check_type(struct kvm *kvm, int type_needed)
|
||||
{
|
||||
if (kvm->arch.vgic.vgic_model != type_needed)
|
||||
return -ENODEV;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vgic_addr - set or get vgic VM base addresses
|
||||
* @kvm: pointer to the vm struct
|
||||
* @type: the VGIC addr type, one of KVM_VGIC_V[23]_ADDR_TYPE_XXX
|
||||
* @addr: pointer to address value
|
||||
* @write: if true set the address in the VM address space, if false read the
|
||||
* address
|
||||
*
|
||||
* Set or get the vgic base addresses for the distributor and the virtual CPU
|
||||
* interface in the VM physical address space. These addresses are properties
|
||||
* of the emulated core/SoC and therefore user space initially knows this
|
||||
* information.
|
||||
* Check them for sanity (alignment, double assignment). We can't check for
|
||||
* overlapping regions in case of a virtual GICv3 here, since we don't know
|
||||
* the number of VCPUs yet, so we defer this check to map_resources().
|
||||
*/
|
||||
int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
|
||||
{
|
||||
int r = 0;
|
||||
struct vgic_dist *vgic = &kvm->arch.vgic;
|
||||
phys_addr_t *addr_ptr, alignment;
|
||||
u64 undef_value = VGIC_ADDR_UNDEF;
|
||||
|
||||
mutex_lock(&kvm->lock);
|
||||
switch (type) {
|
||||
case KVM_VGIC_V2_ADDR_TYPE_DIST:
|
||||
r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
addr_ptr = &vgic->vgic_dist_base;
|
||||
alignment = SZ_4K;
|
||||
break;
|
||||
case KVM_VGIC_V2_ADDR_TYPE_CPU:
|
||||
r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
addr_ptr = &vgic->vgic_cpu_base;
|
||||
alignment = SZ_4K;
|
||||
break;
|
||||
case KVM_VGIC_V3_ADDR_TYPE_DIST:
|
||||
r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
addr_ptr = &vgic->vgic_dist_base;
|
||||
alignment = SZ_64K;
|
||||
break;
|
||||
case KVM_VGIC_V3_ADDR_TYPE_REDIST: {
|
||||
struct vgic_redist_region *rdreg;
|
||||
|
||||
r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
if (r)
|
||||
break;
|
||||
if (write) {
|
||||
r = vgic_v3_set_redist_base(kvm, 0, *addr, 0);
|
||||
goto out;
|
||||
}
|
||||
rdreg = list_first_entry(&vgic->rd_regions,
|
||||
struct vgic_redist_region, list);
|
||||
if (!rdreg)
|
||||
addr_ptr = &undef_value;
|
||||
else
|
||||
addr_ptr = &rdreg->base;
|
||||
break;
|
||||
}
|
||||
case KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION:
|
||||
{
|
||||
struct vgic_redist_region *rdreg;
|
||||
u8 index;
|
||||
|
||||
r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
if (r)
|
||||
break;
|
||||
|
||||
index = *addr & KVM_VGIC_V3_RDIST_INDEX_MASK;
|
||||
|
||||
if (write) {
|
||||
gpa_t base = *addr & KVM_VGIC_V3_RDIST_BASE_MASK;
|
||||
u32 count = (*addr & KVM_VGIC_V3_RDIST_COUNT_MASK)
|
||||
>> KVM_VGIC_V3_RDIST_COUNT_SHIFT;
|
||||
u8 flags = (*addr & KVM_VGIC_V3_RDIST_FLAGS_MASK)
|
||||
>> KVM_VGIC_V3_RDIST_FLAGS_SHIFT;
|
||||
|
||||
if (!count || flags)
|
||||
r = -EINVAL;
|
||||
else
|
||||
r = vgic_v3_set_redist_base(kvm, index,
|
||||
base, count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rdreg = vgic_v3_rdist_region_from_index(kvm, index);
|
||||
if (!rdreg) {
|
||||
r = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*addr = index;
|
||||
*addr |= rdreg->base;
|
||||
*addr |= (u64)rdreg->count << KVM_VGIC_V3_RDIST_COUNT_SHIFT;
|
||||
goto out;
|
||||
}
|
||||
default:
|
||||
r = -ENODEV;
|
||||
}
|
||||
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
if (write) {
|
||||
r = vgic_check_ioaddr(kvm, addr_ptr, *addr, alignment);
|
||||
if (!r)
|
||||
*addr_ptr = *addr;
|
||||
} else {
|
||||
*addr = *addr_ptr;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&kvm->lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int vgic_set_common_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
int r;
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_ADDR: {
|
||||
u64 __user *uaddr = (u64 __user *)(long)attr->addr;
|
||||
u64 addr;
|
||||
unsigned long type = (unsigned long)attr->attr;
|
||||
|
||||
if (copy_from_user(&addr, uaddr, sizeof(addr)))
|
||||
return -EFAULT;
|
||||
|
||||
r = kvm_vgic_addr(dev->kvm, type, &addr, true);
|
||||
return (r == -ENODEV) ? -ENXIO : r;
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
u32 val;
|
||||
int ret = 0;
|
||||
|
||||
if (get_user(val, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* We require:
|
||||
* - at least 32 SPIs on top of the 16 SGIs and 16 PPIs
|
||||
* - at most 1024 interrupts
|
||||
* - a multiple of 32 interrupts
|
||||
*/
|
||||
if (val < (VGIC_NR_PRIVATE_IRQS + 32) ||
|
||||
val > VGIC_MAX_RESERVED ||
|
||||
(val & 31))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev->kvm->lock);
|
||||
|
||||
if (vgic_ready(dev->kvm) || dev->kvm->arch.vgic.nr_spis)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
dev->kvm->arch.vgic.nr_spis =
|
||||
val - VGIC_NR_PRIVATE_IRQS;
|
||||
|
||||
mutex_unlock(&dev->kvm->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_CTRL: {
|
||||
switch (attr->attr) {
|
||||
case KVM_DEV_ARM_VGIC_CTRL_INIT:
|
||||
mutex_lock(&dev->kvm->lock);
|
||||
r = vgic_init(dev->kvm);
|
||||
mutex_unlock(&dev->kvm->lock);
|
||||
return r;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int vgic_get_common_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
int r = -ENXIO;
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_ADDR: {
|
||||
u64 __user *uaddr = (u64 __user *)(long)attr->addr;
|
||||
u64 addr;
|
||||
unsigned long type = (unsigned long)attr->attr;
|
||||
|
||||
r = kvm_vgic_addr(dev->kvm, type, &addr, false);
|
||||
if (r)
|
||||
return (r == -ENODEV) ? -ENXIO : r;
|
||||
|
||||
if (copy_to_user(uaddr, &addr, sizeof(addr)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
|
||||
r = put_user(dev->kvm->arch.vgic.nr_spis +
|
||||
VGIC_NR_PRIVATE_IRQS, uaddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int vgic_create(struct kvm_device *dev, u32 type)
|
||||
{
|
||||
return kvm_vgic_create(dev->kvm, type);
|
||||
}
|
||||
|
||||
static void vgic_destroy(struct kvm_device *dev)
|
||||
{
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
int kvm_register_vgic_device(unsigned long type)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
||||
switch (type) {
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V2:
|
||||
ret = kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
break;
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V3:
|
||||
ret = kvm_register_device_ops(&kvm_arm_vgic_v3_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
ret = kvm_vgic_register_its_device();
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vgic_v2_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
|
||||
struct vgic_reg_attr *reg_attr)
|
||||
{
|
||||
int cpuid;
|
||||
|
||||
cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >>
|
||||
KVM_DEV_ARM_VGIC_CPUID_SHIFT;
|
||||
|
||||
if (cpuid >= atomic_read(&dev->kvm->online_vcpus))
|
||||
return -EINVAL;
|
||||
|
||||
reg_attr->vcpu = kvm_get_vcpu(dev->kvm, cpuid);
|
||||
reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unlocks vcpus from @vcpu_lock_idx and smaller */
|
||||
static void unlock_vcpus(struct kvm *kvm, int vcpu_lock_idx)
|
||||
{
|
||||
struct kvm_vcpu *tmp_vcpu;
|
||||
|
||||
for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
|
||||
tmp_vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx);
|
||||
mutex_unlock(&tmp_vcpu->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_all_vcpus(struct kvm *kvm)
|
||||
{
|
||||
unlock_vcpus(kvm, atomic_read(&kvm->online_vcpus) - 1);
|
||||
}
|
||||
|
||||
/* Returns true if all vcpus were locked, false otherwise */
|
||||
bool lock_all_vcpus(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_vcpu *tmp_vcpu;
|
||||
int c;
|
||||
|
||||
/*
|
||||
* Any time a vcpu is run, vcpu_load is called which tries to grab the
|
||||
* vcpu->mutex. By grabbing the vcpu->mutex of all VCPUs we ensure
|
||||
* that no other VCPUs are run and fiddle with the vgic state while we
|
||||
* access it.
|
||||
*/
|
||||
kvm_for_each_vcpu(c, tmp_vcpu, kvm) {
|
||||
if (!mutex_trylock(&tmp_vcpu->mutex)) {
|
||||
unlock_vcpus(kvm, c - 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_v2_attr_regs_access - allows user space to access VGIC v2 state
|
||||
*
|
||||
* @dev: kvm device handle
|
||||
* @attr: kvm device attribute
|
||||
* @reg: address the value is read or written
|
||||
* @is_write: true if userspace is writing a register
|
||||
*/
|
||||
static int vgic_v2_attr_regs_access(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr,
|
||||
u32 *reg, bool is_write)
|
||||
{
|
||||
struct vgic_reg_attr reg_attr;
|
||||
gpa_t addr;
|
||||
struct kvm_vcpu *vcpu;
|
||||
int ret;
|
||||
|
||||
ret = vgic_v2_parse_attr(dev, attr, ®_attr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vcpu = reg_attr.vcpu;
|
||||
addr = reg_attr.addr;
|
||||
|
||||
mutex_lock(&dev->kvm->lock);
|
||||
|
||||
ret = vgic_init(dev->kvm);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!lock_all_vcpus(dev->kvm)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
|
||||
ret = vgic_v2_cpuif_uaccess(vcpu, is_write, addr, reg);
|
||||
break;
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
ret = vgic_v2_dist_uaccess(vcpu, is_write, addr, reg);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
unlock_all_vcpus(dev->kvm);
|
||||
out:
|
||||
mutex_unlock(&dev->kvm->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vgic_v2_set_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = vgic_set_common_attr(dev, attr);
|
||||
if (ret != -ENXIO)
|
||||
return ret;
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
u32 reg;
|
||||
|
||||
if (get_user(reg, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
return vgic_v2_attr_regs_access(dev, attr, ®, true);
|
||||
}
|
||||
}
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int vgic_v2_get_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = vgic_get_common_attr(dev, attr);
|
||||
if (ret != -ENXIO)
|
||||
return ret;
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
u32 reg = 0;
|
||||
|
||||
ret = vgic_v2_attr_regs_access(dev, attr, ®, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
return put_user(reg, uaddr);
|
||||
}
|
||||
}
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int vgic_v2_has_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_ADDR:
|
||||
switch (attr->attr) {
|
||||
case KVM_VGIC_V2_ADDR_TYPE_DIST:
|
||||
case KVM_VGIC_V2_ADDR_TYPE_CPU:
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
|
||||
return vgic_v2_has_attr_regs(dev, attr);
|
||||
case KVM_DEV_ARM_VGIC_GRP_NR_IRQS:
|
||||
return 0;
|
||||
case KVM_DEV_ARM_VGIC_GRP_CTRL:
|
||||
switch (attr->attr) {
|
||||
case KVM_DEV_ARM_VGIC_CTRL_INIT:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
struct kvm_device_ops kvm_arm_vgic_v2_ops = {
|
||||
.name = "kvm-arm-vgic-v2",
|
||||
.create = vgic_create,
|
||||
.destroy = vgic_destroy,
|
||||
.set_attr = vgic_v2_set_attr,
|
||||
.get_attr = vgic_v2_get_attr,
|
||||
.has_attr = vgic_v2_has_attr,
|
||||
};
|
||||
|
||||
int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
|
||||
struct vgic_reg_attr *reg_attr)
|
||||
{
|
||||
unsigned long vgic_mpidr, mpidr_reg;
|
||||
|
||||
/*
|
||||
* For KVM_DEV_ARM_VGIC_GRP_DIST_REGS group,
|
||||
* attr might not hold MPIDR. Hence assume vcpu0.
|
||||
*/
|
||||
if (attr->group != KVM_DEV_ARM_VGIC_GRP_DIST_REGS) {
|
||||
vgic_mpidr = (attr->attr & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) >>
|
||||
KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT;
|
||||
|
||||
mpidr_reg = VGIC_TO_MPIDR(vgic_mpidr);
|
||||
reg_attr->vcpu = kvm_mpidr_to_vcpu(dev->kvm, mpidr_reg);
|
||||
} else {
|
||||
reg_attr->vcpu = kvm_get_vcpu(dev->kvm, 0);
|
||||
}
|
||||
|
||||
if (!reg_attr->vcpu)
|
||||
return -EINVAL;
|
||||
|
||||
reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* vgic_v3_attr_regs_access - allows user space to access VGIC v3 state
|
||||
*
|
||||
* @dev: kvm device handle
|
||||
* @attr: kvm device attribute
|
||||
* @reg: address the value is read or written
|
||||
* @is_write: true if userspace is writing a register
|
||||
*/
|
||||
static int vgic_v3_attr_regs_access(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr,
|
||||
u64 *reg, bool is_write)
|
||||
{
|
||||
struct vgic_reg_attr reg_attr;
|
||||
gpa_t addr;
|
||||
struct kvm_vcpu *vcpu;
|
||||
int ret;
|
||||
u32 tmp32;
|
||||
|
||||
ret = vgic_v3_parse_attr(dev, attr, ®_attr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vcpu = reg_attr.vcpu;
|
||||
addr = reg_attr.addr;
|
||||
|
||||
mutex_lock(&dev->kvm->lock);
|
||||
|
||||
if (unlikely(!vgic_initialized(dev->kvm))) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!lock_all_vcpus(dev->kvm)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
if (is_write)
|
||||
tmp32 = *reg;
|
||||
|
||||
ret = vgic_v3_dist_uaccess(vcpu, is_write, addr, &tmp32);
|
||||
if (!is_write)
|
||||
*reg = tmp32;
|
||||
break;
|
||||
case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
|
||||
if (is_write)
|
||||
tmp32 = *reg;
|
||||
|
||||
ret = vgic_v3_redist_uaccess(vcpu, is_write, addr, &tmp32);
|
||||
if (!is_write)
|
||||
*reg = tmp32;
|
||||
break;
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: {
|
||||
u64 regid;
|
||||
|
||||
regid = (attr->attr & KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK);
|
||||
ret = vgic_v3_cpu_sysregs_uaccess(vcpu, is_write,
|
||||
regid, reg);
|
||||
break;
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
|
||||
unsigned int info, intid;
|
||||
|
||||
info = (attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >>
|
||||
KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT;
|
||||
if (info == VGIC_LEVEL_INFO_LINE_LEVEL) {
|
||||
intid = attr->attr &
|
||||
KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK;
|
||||
ret = vgic_v3_line_level_info_uaccess(vcpu, is_write,
|
||||
intid, reg);
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
unlock_all_vcpus(dev->kvm);
|
||||
out:
|
||||
mutex_unlock(&dev->kvm->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vgic_v3_set_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = vgic_set_common_attr(dev, attr);
|
||||
if (ret != -ENXIO)
|
||||
return ret;
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
u32 tmp32;
|
||||
u64 reg;
|
||||
|
||||
if (get_user(tmp32, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
reg = tmp32;
|
||||
return vgic_v3_attr_regs_access(dev, attr, ®, true);
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: {
|
||||
u64 __user *uaddr = (u64 __user *)(long)attr->addr;
|
||||
u64 reg;
|
||||
|
||||
if (get_user(reg, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
return vgic_v3_attr_regs_access(dev, attr, ®, true);
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
u64 reg;
|
||||
u32 tmp32;
|
||||
|
||||
if (get_user(tmp32, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
reg = tmp32;
|
||||
return vgic_v3_attr_regs_access(dev, attr, ®, true);
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_CTRL: {
|
||||
int ret;
|
||||
|
||||
switch (attr->attr) {
|
||||
case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES:
|
||||
mutex_lock(&dev->kvm->lock);
|
||||
|
||||
if (!lock_all_vcpus(dev->kvm)) {
|
||||
mutex_unlock(&dev->kvm->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = vgic_v3_save_pending_tables(dev->kvm);
|
||||
unlock_all_vcpus(dev->kvm);
|
||||
mutex_unlock(&dev->kvm->lock);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int vgic_v3_get_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = vgic_get_common_attr(dev, attr);
|
||||
if (ret != -ENXIO)
|
||||
return ret;
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
u64 reg;
|
||||
u32 tmp32;
|
||||
|
||||
ret = vgic_v3_attr_regs_access(dev, attr, ®, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
tmp32 = reg;
|
||||
return put_user(tmp32, uaddr);
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: {
|
||||
u64 __user *uaddr = (u64 __user *)(long)attr->addr;
|
||||
u64 reg;
|
||||
|
||||
ret = vgic_v3_attr_regs_access(dev, attr, ®, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
return put_user(reg, uaddr);
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
u64 reg;
|
||||
u32 tmp32;
|
||||
|
||||
ret = vgic_v3_attr_regs_access(dev, attr, ®, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
tmp32 = reg;
|
||||
return put_user(tmp32, uaddr);
|
||||
}
|
||||
}
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int vgic_v3_has_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_ADDR:
|
||||
switch (attr->attr) {
|
||||
case KVM_VGIC_V3_ADDR_TYPE_DIST:
|
||||
case KVM_VGIC_V3_ADDR_TYPE_REDIST:
|
||||
case KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION:
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
|
||||
return vgic_v3_has_attr_regs(dev, attr);
|
||||
case KVM_DEV_ARM_VGIC_GRP_NR_IRQS:
|
||||
return 0;
|
||||
case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
|
||||
if (((attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >>
|
||||
KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT) ==
|
||||
VGIC_LEVEL_INFO_LINE_LEVEL)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_CTRL:
|
||||
switch (attr->attr) {
|
||||
case KVM_DEV_ARM_VGIC_CTRL_INIT:
|
||||
return 0;
|
||||
case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
struct kvm_device_ops kvm_arm_vgic_v3_ops = {
|
||||
.name = "kvm-arm-vgic-v3",
|
||||
.create = vgic_create,
|
||||
.destroy = vgic_destroy,
|
||||
.set_attr = vgic_v3_set_attr,
|
||||
.get_attr = vgic_v3_get_attr,
|
||||
.has_attr = vgic_v3_has_attr,
|
||||
};
|
546
virt/kvm/arm/vgic/vgic-mmio-v2.c
Normal file
546
virt/kvm/arm/vgic/vgic-mmio-v2.c
Normal file
@ -0,0 +1,546 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* VGICv2 MMIO handling functions
|
||||
*/
|
||||
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/nospec.h>
|
||||
|
||||
#include <kvm/iodev.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
|
||||
#include "vgic.h"
|
||||
#include "vgic-mmio.h"
|
||||
|
||||
/*
|
||||
* The Revision field in the IIDR have the following meanings:
|
||||
*
|
||||
* Revision 1: Report GICv2 interrupts as group 0 instead of group 1
|
||||
* Revision 2: Interrupt groups are guest-configurable and signaled using
|
||||
* their configured groups.
|
||||
*/
|
||||
|
||||
static unsigned long vgic_mmio_read_v2_misc(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
struct vgic_dist *vgic = &vcpu->kvm->arch.vgic;
|
||||
u32 value;
|
||||
|
||||
switch (addr & 0x0c) {
|
||||
case GIC_DIST_CTRL:
|
||||
value = vgic->enabled ? GICD_ENABLE : 0;
|
||||
break;
|
||||
case GIC_DIST_CTR:
|
||||
value = vgic->nr_spis + VGIC_NR_PRIVATE_IRQS;
|
||||
value = (value >> 5) - 1;
|
||||
value |= (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5;
|
||||
break;
|
||||
case GIC_DIST_IIDR:
|
||||
value = (PRODUCT_ID_KVM << GICD_IIDR_PRODUCT_ID_SHIFT) |
|
||||
(vgic->implementation_rev << GICD_IIDR_REVISION_SHIFT) |
|
||||
(IMPLEMENTER_ARM << GICD_IIDR_IMPLEMENTER_SHIFT);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_v2_misc(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
bool was_enabled = dist->enabled;
|
||||
|
||||
switch (addr & 0x0c) {
|
||||
case GIC_DIST_CTRL:
|
||||
dist->enabled = val & GICD_ENABLE;
|
||||
if (!was_enabled && dist->enabled)
|
||||
vgic_kick_vcpus(vcpu->kvm);
|
||||
break;
|
||||
case GIC_DIST_CTR:
|
||||
case GIC_DIST_IIDR:
|
||||
/* Nothing to do */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
switch (addr & 0x0c) {
|
||||
case GIC_DIST_IIDR:
|
||||
if (val != vgic_mmio_read_v2_misc(vcpu, addr, len))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* If we observe a write to GICD_IIDR we know that userspace
|
||||
* has been updated and has had a chance to cope with older
|
||||
* kernels (VGICv2 IIDR.Revision == 0) incorrectly reporting
|
||||
* interrupts as group 1, and therefore we now allow groups to
|
||||
* be user writable. Doing this by default would break
|
||||
* migration from old kernels to new kernels with legacy
|
||||
* userspace.
|
||||
*/
|
||||
vcpu->kvm->arch.vgic.v2_groups_user_writable = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
vgic_mmio_write_v2_misc(vcpu, addr, len, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vgic_mmio_uaccess_write_v2_group(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
if (vcpu->kvm->arch.vgic.v2_groups_user_writable)
|
||||
vgic_mmio_write_group(vcpu, addr, len, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
int nr_vcpus = atomic_read(&source_vcpu->kvm->online_vcpus);
|
||||
int intid = val & 0xf;
|
||||
int targets = (val >> 16) & 0xff;
|
||||
int mode = (val >> 24) & 0x03;
|
||||
int c;
|
||||
struct kvm_vcpu *vcpu;
|
||||
unsigned long flags;
|
||||
|
||||
switch (mode) {
|
||||
case 0x0: /* as specified by targets */
|
||||
break;
|
||||
case 0x1:
|
||||
targets = (1U << nr_vcpus) - 1; /* all, ... */
|
||||
targets &= ~(1U << source_vcpu->vcpu_id); /* but self */
|
||||
break;
|
||||
case 0x2: /* this very vCPU only */
|
||||
targets = (1U << source_vcpu->vcpu_id);
|
||||
break;
|
||||
case 0x3: /* reserved */
|
||||
return;
|
||||
}
|
||||
|
||||
kvm_for_each_vcpu(c, vcpu, source_vcpu->kvm) {
|
||||
struct vgic_irq *irq;
|
||||
|
||||
if (!(targets & (1U << c)))
|
||||
continue;
|
||||
|
||||
irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
irq->pending_latch = true;
|
||||
irq->source |= 1U << source_vcpu->vcpu_id;
|
||||
|
||||
vgic_queue_irq_unlock(source_vcpu->kvm, irq, flags);
|
||||
vgic_put_irq(source_vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_target(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
|
||||
int i;
|
||||
u64 val = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
val |= (u64)irq->targets << (i * 8);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
|
||||
u8 cpu_mask = GENMASK(atomic_read(&vcpu->kvm->online_vcpus) - 1, 0);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
/* GICD_ITARGETSR[0-7] are read-only */
|
||||
if (intid < VGIC_NR_PRIVATE_IRQS)
|
||||
return;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i);
|
||||
int target;
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
irq->targets = (val >> (i * 8)) & cpu_mask;
|
||||
target = irq->targets ? __ffs(irq->targets) : 0;
|
||||
irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_sgipend(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = addr & 0x0f;
|
||||
int i;
|
||||
u64 val = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
val |= (u64)irq->source << (i * 8);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = addr & 0x0f;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
irq->source &= ~((val >> (i * 8)) & 0xff);
|
||||
if (!irq->source)
|
||||
irq->pending_latch = false;
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = addr & 0x0f;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
irq->source |= (val >> (i * 8)) & 0xff;
|
||||
|
||||
if (irq->source) {
|
||||
irq->pending_latch = true;
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
} else {
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
}
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
#define GICC_ARCH_VERSION_V2 0x2
|
||||
|
||||
/* These are for userland accesses only, there is no guest-facing emulation. */
|
||||
static unsigned long vgic_mmio_read_vcpuif(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
struct vgic_vmcr vmcr;
|
||||
u32 val;
|
||||
|
||||
vgic_get_vmcr(vcpu, &vmcr);
|
||||
|
||||
switch (addr & 0xff) {
|
||||
case GIC_CPU_CTRL:
|
||||
val = vmcr.grpen0 << GIC_CPU_CTRL_EnableGrp0_SHIFT;
|
||||
val |= vmcr.grpen1 << GIC_CPU_CTRL_EnableGrp1_SHIFT;
|
||||
val |= vmcr.ackctl << GIC_CPU_CTRL_AckCtl_SHIFT;
|
||||
val |= vmcr.fiqen << GIC_CPU_CTRL_FIQEn_SHIFT;
|
||||
val |= vmcr.cbpr << GIC_CPU_CTRL_CBPR_SHIFT;
|
||||
val |= vmcr.eoim << GIC_CPU_CTRL_EOImodeNS_SHIFT;
|
||||
|
||||
break;
|
||||
case GIC_CPU_PRIMASK:
|
||||
/*
|
||||
* Our KVM_DEV_TYPE_ARM_VGIC_V2 device ABI exports the
|
||||
* the PMR field as GICH_VMCR.VMPriMask rather than
|
||||
* GICC_PMR.Priority, so we expose the upper five bits of
|
||||
* priority mask to userspace using the lower bits in the
|
||||
* unsigned long.
|
||||
*/
|
||||
val = (vmcr.pmr & GICV_PMR_PRIORITY_MASK) >>
|
||||
GICV_PMR_PRIORITY_SHIFT;
|
||||
break;
|
||||
case GIC_CPU_BINPOINT:
|
||||
val = vmcr.bpr;
|
||||
break;
|
||||
case GIC_CPU_ALIAS_BINPOINT:
|
||||
val = vmcr.abpr;
|
||||
break;
|
||||
case GIC_CPU_IDENT:
|
||||
val = ((PRODUCT_ID_KVM << 20) |
|
||||
(GICC_ARCH_VERSION_V2 << 16) |
|
||||
IMPLEMENTER_ARM);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_vcpuif(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
struct vgic_vmcr vmcr;
|
||||
|
||||
vgic_get_vmcr(vcpu, &vmcr);
|
||||
|
||||
switch (addr & 0xff) {
|
||||
case GIC_CPU_CTRL:
|
||||
vmcr.grpen0 = !!(val & GIC_CPU_CTRL_EnableGrp0);
|
||||
vmcr.grpen1 = !!(val & GIC_CPU_CTRL_EnableGrp1);
|
||||
vmcr.ackctl = !!(val & GIC_CPU_CTRL_AckCtl);
|
||||
vmcr.fiqen = !!(val & GIC_CPU_CTRL_FIQEn);
|
||||
vmcr.cbpr = !!(val & GIC_CPU_CTRL_CBPR);
|
||||
vmcr.eoim = !!(val & GIC_CPU_CTRL_EOImodeNS);
|
||||
|
||||
break;
|
||||
case GIC_CPU_PRIMASK:
|
||||
/*
|
||||
* Our KVM_DEV_TYPE_ARM_VGIC_V2 device ABI exports the
|
||||
* the PMR field as GICH_VMCR.VMPriMask rather than
|
||||
* GICC_PMR.Priority, so we expose the upper five bits of
|
||||
* priority mask to userspace using the lower bits in the
|
||||
* unsigned long.
|
||||
*/
|
||||
vmcr.pmr = (val << GICV_PMR_PRIORITY_SHIFT) &
|
||||
GICV_PMR_PRIORITY_MASK;
|
||||
break;
|
||||
case GIC_CPU_BINPOINT:
|
||||
vmcr.bpr = val;
|
||||
break;
|
||||
case GIC_CPU_ALIAS_BINPOINT:
|
||||
vmcr.abpr = val;
|
||||
break;
|
||||
}
|
||||
|
||||
vgic_set_vmcr(vcpu, &vmcr);
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_apr(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
int n; /* which APRn is this */
|
||||
|
||||
n = (addr >> 2) & 0x3;
|
||||
|
||||
if (kvm_vgic_global_state.type == VGIC_V2) {
|
||||
/* GICv2 hardware systems support max. 32 groups */
|
||||
if (n != 0)
|
||||
return 0;
|
||||
return vcpu->arch.vgic_cpu.vgic_v2.vgic_apr;
|
||||
} else {
|
||||
struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
if (n > vgic_v3_max_apr_idx(vcpu))
|
||||
return 0;
|
||||
|
||||
n = array_index_nospec(n, 4);
|
||||
|
||||
/* GICv3 only uses ICH_AP1Rn for memory mapped (GICv2) guests */
|
||||
return vgicv3->vgic_ap1r[n];
|
||||
}
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_apr(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
int n; /* which APRn is this */
|
||||
|
||||
n = (addr >> 2) & 0x3;
|
||||
|
||||
if (kvm_vgic_global_state.type == VGIC_V2) {
|
||||
/* GICv2 hardware systems support max. 32 groups */
|
||||
if (n != 0)
|
||||
return;
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_apr = val;
|
||||
} else {
|
||||
struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
if (n > vgic_v3_max_apr_idx(vcpu))
|
||||
return;
|
||||
|
||||
n = array_index_nospec(n, 4);
|
||||
|
||||
/* GICv3 only uses ICH_AP1Rn for memory mapped (GICv2) guests */
|
||||
vgicv3->vgic_ap1r[n] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct vgic_register_region vgic_v2_dist_registers[] = {
|
||||
REGISTER_DESC_WITH_LENGTH_UACCESS(GIC_DIST_CTRL,
|
||||
vgic_mmio_read_v2_misc, vgic_mmio_write_v2_misc,
|
||||
NULL, vgic_mmio_uaccess_write_v2_misc,
|
||||
12, VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_IGROUP,
|
||||
vgic_mmio_read_group, vgic_mmio_write_group,
|
||||
NULL, vgic_mmio_uaccess_write_v2_group, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_SET,
|
||||
vgic_mmio_read_enable, vgic_mmio_write_senable, NULL, NULL, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_CLEAR,
|
||||
vgic_mmio_read_enable, vgic_mmio_write_cenable, NULL, NULL, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_SET,
|
||||
vgic_mmio_read_pending, vgic_mmio_write_spending, NULL, NULL, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_CLEAR,
|
||||
vgic_mmio_read_pending, vgic_mmio_write_cpending, NULL, NULL, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ACTIVE_SET,
|
||||
vgic_mmio_read_active, vgic_mmio_write_sactive,
|
||||
vgic_uaccess_read_active, vgic_mmio_uaccess_write_sactive, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ACTIVE_CLEAR,
|
||||
vgic_mmio_read_active, vgic_mmio_write_cactive,
|
||||
vgic_uaccess_read_active, vgic_mmio_uaccess_write_cactive, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PRI,
|
||||
vgic_mmio_read_priority, vgic_mmio_write_priority, NULL, NULL,
|
||||
8, VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_TARGET,
|
||||
vgic_mmio_read_target, vgic_mmio_write_target, NULL, NULL, 8,
|
||||
VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_CONFIG,
|
||||
vgic_mmio_read_config, vgic_mmio_write_config, NULL, NULL, 2,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GIC_DIST_SOFTINT,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_sgir, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GIC_DIST_SGI_PENDING_CLEAR,
|
||||
vgic_mmio_read_sgipend, vgic_mmio_write_sgipendc, 16,
|
||||
VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GIC_DIST_SGI_PENDING_SET,
|
||||
vgic_mmio_read_sgipend, vgic_mmio_write_sgipends, 16,
|
||||
VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
|
||||
};
|
||||
|
||||
static const struct vgic_register_region vgic_v2_cpu_registers[] = {
|
||||
REGISTER_DESC_WITH_LENGTH(GIC_CPU_CTRL,
|
||||
vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GIC_CPU_PRIMASK,
|
||||
vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GIC_CPU_BINPOINT,
|
||||
vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GIC_CPU_ALIAS_BINPOINT,
|
||||
vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GIC_CPU_ACTIVEPRIO,
|
||||
vgic_mmio_read_apr, vgic_mmio_write_apr, 16,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GIC_CPU_IDENT,
|
||||
vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
};
|
||||
|
||||
unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)
|
||||
{
|
||||
dev->regions = vgic_v2_dist_registers;
|
||||
dev->nr_regions = ARRAY_SIZE(vgic_v2_dist_registers);
|
||||
|
||||
kvm_iodevice_init(&dev->dev, &kvm_io_gic_ops);
|
||||
|
||||
return SZ_4K;
|
||||
}
|
||||
|
||||
int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
|
||||
{
|
||||
const struct vgic_register_region *region;
|
||||
struct vgic_io_device iodev;
|
||||
struct vgic_reg_attr reg_attr;
|
||||
struct kvm_vcpu *vcpu;
|
||||
gpa_t addr;
|
||||
int ret;
|
||||
|
||||
ret = vgic_v2_parse_attr(dev, attr, ®_attr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vcpu = reg_attr.vcpu;
|
||||
addr = reg_attr.addr;
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
iodev.regions = vgic_v2_dist_registers;
|
||||
iodev.nr_regions = ARRAY_SIZE(vgic_v2_dist_registers);
|
||||
iodev.base_addr = 0;
|
||||
break;
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
|
||||
iodev.regions = vgic_v2_cpu_registers;
|
||||
iodev.nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers);
|
||||
iodev.base_addr = 0;
|
||||
break;
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* We only support aligned 32-bit accesses. */
|
||||
if (addr & 3)
|
||||
return -ENXIO;
|
||||
|
||||
region = vgic_get_mmio_region(vcpu, &iodev, addr, sizeof(u32));
|
||||
if (!region)
|
||||
return -ENXIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vgic_v2_cpuif_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
int offset, u32 *val)
|
||||
{
|
||||
struct vgic_io_device dev = {
|
||||
.regions = vgic_v2_cpu_registers,
|
||||
.nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers),
|
||||
.iodev_type = IODEV_CPUIF,
|
||||
};
|
||||
|
||||
return vgic_uaccess(vcpu, &dev, is_write, offset, val);
|
||||
}
|
||||
|
||||
int vgic_v2_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
int offset, u32 *val)
|
||||
{
|
||||
struct vgic_io_device dev = {
|
||||
.regions = vgic_v2_dist_registers,
|
||||
.nr_regions = ARRAY_SIZE(vgic_v2_dist_registers),
|
||||
.iodev_type = IODEV_DIST,
|
||||
};
|
||||
|
||||
return vgic_uaccess(vcpu, &dev, is_write, offset, val);
|
||||
}
|
986
virt/kvm/arm/vgic/vgic-mmio-v3.c
Normal file
986
virt/kvm/arm/vgic/vgic-mmio-v3.c
Normal file
@ -0,0 +1,986 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* VGICv3 MMIO handling functions
|
||||
*/
|
||||
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <kvm/iodev.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_arm.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
|
||||
#include "vgic.h"
|
||||
#include "vgic-mmio.h"
|
||||
|
||||
/* extract @num bytes at @offset bytes offset in data */
|
||||
unsigned long extract_bytes(u64 data, unsigned int offset,
|
||||
unsigned int num)
|
||||
{
|
||||
return (data >> (offset * 8)) & GENMASK_ULL(num * 8 - 1, 0);
|
||||
}
|
||||
|
||||
/* allows updates of any half of a 64-bit register (or the whole thing) */
|
||||
u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
int lower = (offset & 4) * 8;
|
||||
int upper = lower + 8 * len - 1;
|
||||
|
||||
reg &= ~GENMASK_ULL(upper, lower);
|
||||
val &= GENMASK_ULL(len * 8 - 1, 0);
|
||||
|
||||
return reg | ((u64)val << lower);
|
||||
}
|
||||
|
||||
bool vgic_has_its(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
|
||||
if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3)
|
||||
return false;
|
||||
|
||||
return dist->has_its;
|
||||
}
|
||||
|
||||
bool vgic_supports_direct_msis(struct kvm *kvm)
|
||||
{
|
||||
return kvm_vgic_global_state.has_gicv4 && vgic_has_its(kvm);
|
||||
}
|
||||
|
||||
/*
|
||||
* The Revision field in the IIDR have the following meanings:
|
||||
*
|
||||
* Revision 2: Interrupt groups are guest-configurable and signaled using
|
||||
* their configured groups.
|
||||
*/
|
||||
|
||||
static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
struct vgic_dist *vgic = &vcpu->kvm->arch.vgic;
|
||||
u32 value = 0;
|
||||
|
||||
switch (addr & 0x0c) {
|
||||
case GICD_CTLR:
|
||||
if (vgic->enabled)
|
||||
value |= GICD_CTLR_ENABLE_SS_G1;
|
||||
value |= GICD_CTLR_ARE_NS | GICD_CTLR_DS;
|
||||
break;
|
||||
case GICD_TYPER:
|
||||
value = vgic->nr_spis + VGIC_NR_PRIVATE_IRQS;
|
||||
value = (value >> 5) - 1;
|
||||
if (vgic_has_its(vcpu->kvm)) {
|
||||
value |= (INTERRUPT_ID_BITS_ITS - 1) << 19;
|
||||
value |= GICD_TYPER_LPIS;
|
||||
} else {
|
||||
value |= (INTERRUPT_ID_BITS_SPIS - 1) << 19;
|
||||
}
|
||||
break;
|
||||
case GICD_IIDR:
|
||||
value = (PRODUCT_ID_KVM << GICD_IIDR_PRODUCT_ID_SHIFT) |
|
||||
(vgic->implementation_rev << GICD_IIDR_REVISION_SHIFT) |
|
||||
(IMPLEMENTER_ARM << GICD_IIDR_IMPLEMENTER_SHIFT);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
bool was_enabled = dist->enabled;
|
||||
|
||||
switch (addr & 0x0c) {
|
||||
case GICD_CTLR:
|
||||
dist->enabled = val & GICD_CTLR_ENABLE_SS_G1;
|
||||
|
||||
if (!was_enabled && dist->enabled)
|
||||
vgic_kick_vcpus(vcpu->kvm);
|
||||
break;
|
||||
case GICD_TYPER:
|
||||
case GICD_IIDR:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
switch (addr & 0x0c) {
|
||||
case GICD_IIDR:
|
||||
if (val != vgic_mmio_read_v3_misc(vcpu, addr, len))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vgic_mmio_write_v3_misc(vcpu, addr, len, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_irouter(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
int intid = VGIC_ADDR_TO_INTID(addr, 64);
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid);
|
||||
unsigned long ret = 0;
|
||||
|
||||
if (!irq)
|
||||
return 0;
|
||||
|
||||
/* The upper word is RAZ for us. */
|
||||
if (!(addr & 4))
|
||||
ret = extract_bytes(READ_ONCE(irq->mpidr), addr & 7, len);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
int intid = VGIC_ADDR_TO_INTID(addr, 64);
|
||||
struct vgic_irq *irq;
|
||||
unsigned long flags;
|
||||
|
||||
/* The upper word is WI for us since we don't implement Aff3. */
|
||||
if (addr & 4)
|
||||
return;
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, NULL, intid);
|
||||
|
||||
if (!irq)
|
||||
return;
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
/* We only care about and preserve Aff0, Aff1 and Aff2. */
|
||||
irq->mpidr = val & GENMASK(23, 0);
|
||||
irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
|
||||
return vgic_cpu->lpis_enabled ? GICR_CTLR_ENABLE_LPIS : 0;
|
||||
}
|
||||
|
||||
|
||||
static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
bool was_enabled = vgic_cpu->lpis_enabled;
|
||||
|
||||
if (!vgic_has_its(vcpu->kvm))
|
||||
return;
|
||||
|
||||
vgic_cpu->lpis_enabled = val & GICR_CTLR_ENABLE_LPIS;
|
||||
|
||||
if (was_enabled && !vgic_cpu->lpis_enabled) {
|
||||
vgic_flush_pending_lpis(vcpu);
|
||||
vgic_its_invalidate_cache(vcpu->kvm);
|
||||
}
|
||||
|
||||
if (!was_enabled && vgic_cpu->lpis_enabled)
|
||||
vgic_enable_lpis(vcpu);
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
unsigned long mpidr = kvm_vcpu_get_mpidr_aff(vcpu);
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_redist_region *rdreg = vgic_cpu->rdreg;
|
||||
int target_vcpu_id = vcpu->vcpu_id;
|
||||
gpa_t last_rdist_typer = rdreg->base + GICR_TYPER +
|
||||
(rdreg->free_index - 1) * KVM_VGIC_V3_REDIST_SIZE;
|
||||
u64 value;
|
||||
|
||||
value = (u64)(mpidr & GENMASK(23, 0)) << 32;
|
||||
value |= ((target_vcpu_id & 0xffff) << 8);
|
||||
|
||||
if (addr == last_rdist_typer)
|
||||
value |= GICR_TYPER_LAST;
|
||||
if (vgic_has_its(vcpu->kvm))
|
||||
value |= GICR_TYPER_PLPIS;
|
||||
|
||||
return extract_bytes(value, addr & 7, len);
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_v3r_iidr(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
return (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0);
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
switch (addr & 0xffff) {
|
||||
case GICD_PIDR2:
|
||||
/* report a GICv3 compliant implementation */
|
||||
return 0x3b;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long vgic_v3_uaccess_read_pending(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
u32 value = 0;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* pending state of interrupt is latched in pending_latch variable.
|
||||
* Userspace will save and restore pending state and line_level
|
||||
* separately.
|
||||
* Refer to Documentation/virt/kvm/devices/arm-vgic-v3.txt
|
||||
* for handling of ISPENDR and ICPENDR.
|
||||
*/
|
||||
for (i = 0; i < len * 8; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
if (irq->pending_latch)
|
||||
value |= (1U << i);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int vgic_v3_uaccess_write_pending(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < len * 8; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
if (test_bit(i, &val)) {
|
||||
/*
|
||||
* pending_latch is set irrespective of irq type
|
||||
* (level or edge) to avoid dependency that VM should
|
||||
* restore irq config before pending info.
|
||||
*/
|
||||
irq->pending_latch = true;
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
} else {
|
||||
irq->pending_latch = false;
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
}
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We want to avoid outer shareable. */
|
||||
u64 vgic_sanitise_shareability(u64 field)
|
||||
{
|
||||
switch (field) {
|
||||
case GIC_BASER_OuterShareable:
|
||||
return GIC_BASER_InnerShareable;
|
||||
default:
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
/* Avoid any inner non-cacheable mapping. */
|
||||
u64 vgic_sanitise_inner_cacheability(u64 field)
|
||||
{
|
||||
switch (field) {
|
||||
case GIC_BASER_CACHE_nCnB:
|
||||
case GIC_BASER_CACHE_nC:
|
||||
return GIC_BASER_CACHE_RaWb;
|
||||
default:
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
/* Non-cacheable or same-as-inner are OK. */
|
||||
u64 vgic_sanitise_outer_cacheability(u64 field)
|
||||
{
|
||||
switch (field) {
|
||||
case GIC_BASER_CACHE_SameAsInner:
|
||||
case GIC_BASER_CACHE_nC:
|
||||
return field;
|
||||
default:
|
||||
return GIC_BASER_CACHE_nC;
|
||||
}
|
||||
}
|
||||
|
||||
u64 vgic_sanitise_field(u64 reg, u64 field_mask, int field_shift,
|
||||
u64 (*sanitise_fn)(u64))
|
||||
{
|
||||
u64 field = (reg & field_mask) >> field_shift;
|
||||
|
||||
field = sanitise_fn(field) << field_shift;
|
||||
return (reg & ~field_mask) | field;
|
||||
}
|
||||
|
||||
#define PROPBASER_RES0_MASK \
|
||||
(GENMASK_ULL(63, 59) | GENMASK_ULL(55, 52) | GENMASK_ULL(6, 5))
|
||||
#define PENDBASER_RES0_MASK \
|
||||
(BIT_ULL(63) | GENMASK_ULL(61, 59) | GENMASK_ULL(55, 52) | \
|
||||
GENMASK_ULL(15, 12) | GENMASK_ULL(6, 0))
|
||||
|
||||
static u64 vgic_sanitise_pendbaser(u64 reg)
|
||||
{
|
||||
reg = vgic_sanitise_field(reg, GICR_PENDBASER_SHAREABILITY_MASK,
|
||||
GICR_PENDBASER_SHAREABILITY_SHIFT,
|
||||
vgic_sanitise_shareability);
|
||||
reg = vgic_sanitise_field(reg, GICR_PENDBASER_INNER_CACHEABILITY_MASK,
|
||||
GICR_PENDBASER_INNER_CACHEABILITY_SHIFT,
|
||||
vgic_sanitise_inner_cacheability);
|
||||
reg = vgic_sanitise_field(reg, GICR_PENDBASER_OUTER_CACHEABILITY_MASK,
|
||||
GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT,
|
||||
vgic_sanitise_outer_cacheability);
|
||||
|
||||
reg &= ~PENDBASER_RES0_MASK;
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
static u64 vgic_sanitise_propbaser(u64 reg)
|
||||
{
|
||||
reg = vgic_sanitise_field(reg, GICR_PROPBASER_SHAREABILITY_MASK,
|
||||
GICR_PROPBASER_SHAREABILITY_SHIFT,
|
||||
vgic_sanitise_shareability);
|
||||
reg = vgic_sanitise_field(reg, GICR_PROPBASER_INNER_CACHEABILITY_MASK,
|
||||
GICR_PROPBASER_INNER_CACHEABILITY_SHIFT,
|
||||
vgic_sanitise_inner_cacheability);
|
||||
reg = vgic_sanitise_field(reg, GICR_PROPBASER_OUTER_CACHEABILITY_MASK,
|
||||
GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT,
|
||||
vgic_sanitise_outer_cacheability);
|
||||
|
||||
reg &= ~PROPBASER_RES0_MASK;
|
||||
return reg;
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_propbase(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
|
||||
return extract_bytes(dist->propbaser, addr & 7, len);
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_propbase(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
u64 old_propbaser, propbaser;
|
||||
|
||||
/* Storing a value with LPIs already enabled is undefined */
|
||||
if (vgic_cpu->lpis_enabled)
|
||||
return;
|
||||
|
||||
do {
|
||||
old_propbaser = READ_ONCE(dist->propbaser);
|
||||
propbaser = old_propbaser;
|
||||
propbaser = update_64bit_reg(propbaser, addr & 4, len, val);
|
||||
propbaser = vgic_sanitise_propbaser(propbaser);
|
||||
} while (cmpxchg64(&dist->propbaser, old_propbaser,
|
||||
propbaser) != old_propbaser);
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_pendbase(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
|
||||
return extract_bytes(vgic_cpu->pendbaser, addr & 7, len);
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_pendbase(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
u64 old_pendbaser, pendbaser;
|
||||
|
||||
/* Storing a value with LPIs already enabled is undefined */
|
||||
if (vgic_cpu->lpis_enabled)
|
||||
return;
|
||||
|
||||
do {
|
||||
old_pendbaser = READ_ONCE(vgic_cpu->pendbaser);
|
||||
pendbaser = old_pendbaser;
|
||||
pendbaser = update_64bit_reg(pendbaser, addr & 4, len, val);
|
||||
pendbaser = vgic_sanitise_pendbaser(pendbaser);
|
||||
} while (cmpxchg64(&vgic_cpu->pendbaser, old_pendbaser,
|
||||
pendbaser) != old_pendbaser);
|
||||
}
|
||||
|
||||
/*
|
||||
* The GICv3 per-IRQ registers are split to control PPIs and SGIs in the
|
||||
* redistributors, while SPIs are covered by registers in the distributor
|
||||
* block. Trying to set private IRQs in this block gets ignored.
|
||||
* We take some special care here to fix the calculation of the register
|
||||
* offset.
|
||||
*/
|
||||
#define REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(off, rd, wr, ur, uw, bpi, acc) \
|
||||
{ \
|
||||
.reg_offset = off, \
|
||||
.bits_per_irq = bpi, \
|
||||
.len = (bpi * VGIC_NR_PRIVATE_IRQS) / 8, \
|
||||
.access_flags = acc, \
|
||||
.read = vgic_mmio_read_raz, \
|
||||
.write = vgic_mmio_write_wi, \
|
||||
}, { \
|
||||
.reg_offset = off + (bpi * VGIC_NR_PRIVATE_IRQS) / 8, \
|
||||
.bits_per_irq = bpi, \
|
||||
.len = (bpi * (1024 - VGIC_NR_PRIVATE_IRQS)) / 8, \
|
||||
.access_flags = acc, \
|
||||
.read = rd, \
|
||||
.write = wr, \
|
||||
.uaccess_read = ur, \
|
||||
.uaccess_write = uw, \
|
||||
}
|
||||
|
||||
static const struct vgic_register_region vgic_v3_dist_registers[] = {
|
||||
REGISTER_DESC_WITH_LENGTH_UACCESS(GICD_CTLR,
|
||||
vgic_mmio_read_v3_misc, vgic_mmio_write_v3_misc,
|
||||
NULL, vgic_mmio_uaccess_write_v3_misc,
|
||||
16, VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICD_STATUSR,
|
||||
vgic_mmio_read_rao, vgic_mmio_write_wi, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IGROUPR,
|
||||
vgic_mmio_read_group, vgic_mmio_write_group, NULL, NULL, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ISENABLER,
|
||||
vgic_mmio_read_enable, vgic_mmio_write_senable, NULL, NULL, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ICENABLER,
|
||||
vgic_mmio_read_enable, vgic_mmio_write_cenable, NULL, NULL, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ISPENDR,
|
||||
vgic_mmio_read_pending, vgic_mmio_write_spending,
|
||||
vgic_v3_uaccess_read_pending, vgic_v3_uaccess_write_pending, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ICPENDR,
|
||||
vgic_mmio_read_pending, vgic_mmio_write_cpending,
|
||||
vgic_mmio_read_raz, vgic_mmio_uaccess_write_wi, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ISACTIVER,
|
||||
vgic_mmio_read_active, vgic_mmio_write_sactive,
|
||||
vgic_uaccess_read_active, vgic_mmio_uaccess_write_sactive, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ICACTIVER,
|
||||
vgic_mmio_read_active, vgic_mmio_write_cactive,
|
||||
vgic_uaccess_read_active, vgic_mmio_uaccess_write_cactive,
|
||||
1, VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IPRIORITYR,
|
||||
vgic_mmio_read_priority, vgic_mmio_write_priority, NULL, NULL,
|
||||
8, VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ITARGETSR,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_wi, NULL, NULL, 8,
|
||||
VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ICFGR,
|
||||
vgic_mmio_read_config, vgic_mmio_write_config, NULL, NULL, 2,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IGRPMODR,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_wi, NULL, NULL, 1,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IROUTER,
|
||||
vgic_mmio_read_irouter, vgic_mmio_write_irouter, NULL, NULL, 64,
|
||||
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICD_IDREGS,
|
||||
vgic_mmio_read_v3_idregs, vgic_mmio_write_wi, 48,
|
||||
VGIC_ACCESS_32bit),
|
||||
};
|
||||
|
||||
static const struct vgic_register_region vgic_v3_rd_registers[] = {
|
||||
/* RD_base registers */
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_CTLR,
|
||||
vgic_mmio_read_v3r_ctlr, vgic_mmio_write_v3r_ctlr, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_STATUSR,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_wi, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_IIDR,
|
||||
vgic_mmio_read_v3r_iidr, vgic_mmio_write_wi, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_TYPER,
|
||||
vgic_mmio_read_v3r_typer, vgic_mmio_write_wi, 8,
|
||||
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_WAKER,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_wi, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_PROPBASER,
|
||||
vgic_mmio_read_propbase, vgic_mmio_write_propbase, 8,
|
||||
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_PENDBASER,
|
||||
vgic_mmio_read_pendbase, vgic_mmio_write_pendbase, 8,
|
||||
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_IDREGS,
|
||||
vgic_mmio_read_v3_idregs, vgic_mmio_write_wi, 48,
|
||||
VGIC_ACCESS_32bit),
|
||||
/* SGI_base registers */
|
||||
REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_IGROUPR0,
|
||||
vgic_mmio_read_group, vgic_mmio_write_group, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_ISENABLER0,
|
||||
vgic_mmio_read_enable, vgic_mmio_write_senable, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_ICENABLER0,
|
||||
vgic_mmio_read_enable, vgic_mmio_write_cenable, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ISPENDR0,
|
||||
vgic_mmio_read_pending, vgic_mmio_write_spending,
|
||||
vgic_v3_uaccess_read_pending, vgic_v3_uaccess_write_pending, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ICPENDR0,
|
||||
vgic_mmio_read_pending, vgic_mmio_write_cpending,
|
||||
vgic_mmio_read_raz, vgic_mmio_uaccess_write_wi, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ISACTIVER0,
|
||||
vgic_mmio_read_active, vgic_mmio_write_sactive,
|
||||
vgic_uaccess_read_active, vgic_mmio_uaccess_write_sactive, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ICACTIVER0,
|
||||
vgic_mmio_read_active, vgic_mmio_write_cactive,
|
||||
vgic_uaccess_read_active, vgic_mmio_uaccess_write_cactive, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_IPRIORITYR0,
|
||||
vgic_mmio_read_priority, vgic_mmio_write_priority, 32,
|
||||
VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
|
||||
REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_ICFGR0,
|
||||
vgic_mmio_read_config, vgic_mmio_write_config, 8,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_IGRPMODR0,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_wi, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_NSACR,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_wi, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
};
|
||||
|
||||
unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev)
|
||||
{
|
||||
dev->regions = vgic_v3_dist_registers;
|
||||
dev->nr_regions = ARRAY_SIZE(vgic_v3_dist_registers);
|
||||
|
||||
kvm_iodevice_init(&dev->dev, &kvm_io_gic_ops);
|
||||
|
||||
return SZ_64K;
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_register_redist_iodev - register a single redist iodev
|
||||
* @vcpu: The VCPU to which the redistributor belongs
|
||||
*
|
||||
* Register a KVM iodev for this VCPU's redistributor using the address
|
||||
* provided.
|
||||
*
|
||||
* Return 0 on success, -ERRNO otherwise.
|
||||
*/
|
||||
int vgic_register_redist_iodev(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
struct vgic_dist *vgic = &kvm->arch.vgic;
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_io_device *rd_dev = &vcpu->arch.vgic_cpu.rd_iodev;
|
||||
struct vgic_redist_region *rdreg;
|
||||
gpa_t rd_base;
|
||||
int ret;
|
||||
|
||||
if (!IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We may be creating VCPUs before having set the base address for the
|
||||
* redistributor region, in which case we will come back to this
|
||||
* function for all VCPUs when the base address is set. Just return
|
||||
* without doing any work for now.
|
||||
*/
|
||||
rdreg = vgic_v3_rdist_free_slot(&vgic->rd_regions);
|
||||
if (!rdreg)
|
||||
return 0;
|
||||
|
||||
if (!vgic_v3_check_base(kvm))
|
||||
return -EINVAL;
|
||||
|
||||
vgic_cpu->rdreg = rdreg;
|
||||
|
||||
rd_base = rdreg->base + rdreg->free_index * KVM_VGIC_V3_REDIST_SIZE;
|
||||
|
||||
kvm_iodevice_init(&rd_dev->dev, &kvm_io_gic_ops);
|
||||
rd_dev->base_addr = rd_base;
|
||||
rd_dev->iodev_type = IODEV_REDIST;
|
||||
rd_dev->regions = vgic_v3_rd_registers;
|
||||
rd_dev->nr_regions = ARRAY_SIZE(vgic_v3_rd_registers);
|
||||
rd_dev->redist_vcpu = vcpu;
|
||||
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, rd_base,
|
||||
2 * SZ_64K, &rd_dev->dev);
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rdreg->free_index++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_io_device *rd_dev = &vcpu->arch.vgic_cpu.rd_iodev;
|
||||
|
||||
kvm_io_bus_unregister_dev(vcpu->kvm, KVM_MMIO_BUS, &rd_dev->dev);
|
||||
}
|
||||
|
||||
static int vgic_register_all_redist_iodevs(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
int c, ret = 0;
|
||||
|
||||
kvm_for_each_vcpu(c, vcpu, kvm) {
|
||||
ret = vgic_register_redist_iodev(vcpu);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
/* The current c failed, so we start with the previous one. */
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
for (c--; c >= 0; c--) {
|
||||
vcpu = kvm_get_vcpu(kvm, c);
|
||||
vgic_unregister_redist_iodev(vcpu);
|
||||
}
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_v3_insert_redist_region - Insert a new redistributor region
|
||||
*
|
||||
* Performs various checks before inserting the rdist region in the list.
|
||||
* Those tests depend on whether the size of the rdist region is known
|
||||
* (ie. count != 0). The list is sorted by rdist region index.
|
||||
*
|
||||
* @kvm: kvm handle
|
||||
* @index: redist region index
|
||||
* @base: base of the new rdist region
|
||||
* @count: number of redistributors the region is made of (0 in the old style
|
||||
* single region, whose size is induced from the number of vcpus)
|
||||
*
|
||||
* Return 0 on success, < 0 otherwise
|
||||
*/
|
||||
static int vgic_v3_insert_redist_region(struct kvm *kvm, uint32_t index,
|
||||
gpa_t base, uint32_t count)
|
||||
{
|
||||
struct vgic_dist *d = &kvm->arch.vgic;
|
||||
struct vgic_redist_region *rdreg;
|
||||
struct list_head *rd_regions = &d->rd_regions;
|
||||
size_t size = count * KVM_VGIC_V3_REDIST_SIZE;
|
||||
int ret;
|
||||
|
||||
/* single rdist region already set ?*/
|
||||
if (!count && !list_empty(rd_regions))
|
||||
return -EINVAL;
|
||||
|
||||
/* cross the end of memory ? */
|
||||
if (base + size < base)
|
||||
return -EINVAL;
|
||||
|
||||
if (list_empty(rd_regions)) {
|
||||
if (index != 0)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
rdreg = list_last_entry(rd_regions,
|
||||
struct vgic_redist_region, list);
|
||||
if (index != rdreg->index + 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* Cannot add an explicitly sized regions after legacy region */
|
||||
if (!rdreg->count)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* For legacy single-region redistributor regions (!count),
|
||||
* check that the redistributor region does not overlap with the
|
||||
* distributor's address space.
|
||||
*/
|
||||
if (!count && !IS_VGIC_ADDR_UNDEF(d->vgic_dist_base) &&
|
||||
vgic_dist_overlap(kvm, base, size))
|
||||
return -EINVAL;
|
||||
|
||||
/* collision with any other rdist region? */
|
||||
if (vgic_v3_rdist_overlap(kvm, base, size))
|
||||
return -EINVAL;
|
||||
|
||||
rdreg = kzalloc(sizeof(*rdreg), GFP_KERNEL);
|
||||
if (!rdreg)
|
||||
return -ENOMEM;
|
||||
|
||||
rdreg->base = VGIC_ADDR_UNDEF;
|
||||
|
||||
ret = vgic_check_ioaddr(kvm, &rdreg->base, base, SZ_64K);
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
rdreg->base = base;
|
||||
rdreg->count = count;
|
||||
rdreg->free_index = 0;
|
||||
rdreg->index = index;
|
||||
|
||||
list_add_tail(&rdreg->list, rd_regions);
|
||||
return 0;
|
||||
free:
|
||||
kfree(rdreg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = vgic_v3_insert_redist_region(kvm, index, addr, count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Register iodevs for each existing VCPU. Adding more VCPUs
|
||||
* afterwards will register the iodevs when needed.
|
||||
*/
|
||||
ret = vgic_register_all_redist_iodevs(kvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vgic_v3_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
|
||||
{
|
||||
const struct vgic_register_region *region;
|
||||
struct vgic_io_device iodev;
|
||||
struct vgic_reg_attr reg_attr;
|
||||
struct kvm_vcpu *vcpu;
|
||||
gpa_t addr;
|
||||
int ret;
|
||||
|
||||
ret = vgic_v3_parse_attr(dev, attr, ®_attr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vcpu = reg_attr.vcpu;
|
||||
addr = reg_attr.addr;
|
||||
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
|
||||
iodev.regions = vgic_v3_dist_registers;
|
||||
iodev.nr_regions = ARRAY_SIZE(vgic_v3_dist_registers);
|
||||
iodev.base_addr = 0;
|
||||
break;
|
||||
case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:{
|
||||
iodev.regions = vgic_v3_rd_registers;
|
||||
iodev.nr_regions = ARRAY_SIZE(vgic_v3_rd_registers);
|
||||
iodev.base_addr = 0;
|
||||
break;
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: {
|
||||
u64 reg, id;
|
||||
|
||||
id = (attr->attr & KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK);
|
||||
return vgic_v3_has_cpu_sysregs_attr(vcpu, 0, id, ®);
|
||||
}
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* We only support aligned 32-bit accesses. */
|
||||
if (addr & 3)
|
||||
return -ENXIO;
|
||||
|
||||
region = vgic_get_mmio_region(vcpu, &iodev, addr, sizeof(u32));
|
||||
if (!region)
|
||||
return -ENXIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Compare a given affinity (level 1-3 and a level 0 mask, from the SGI
|
||||
* generation register ICC_SGI1R_EL1) with a given VCPU.
|
||||
* If the VCPU's MPIDR matches, return the level0 affinity, otherwise
|
||||
* return -1.
|
||||
*/
|
||||
static int match_mpidr(u64 sgi_aff, u16 sgi_cpu_mask, struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long affinity;
|
||||
int level0;
|
||||
|
||||
/*
|
||||
* Split the current VCPU's MPIDR into affinity level 0 and the
|
||||
* rest as this is what we have to compare against.
|
||||
*/
|
||||
affinity = kvm_vcpu_get_mpidr_aff(vcpu);
|
||||
level0 = MPIDR_AFFINITY_LEVEL(affinity, 0);
|
||||
affinity &= ~MPIDR_LEVEL_MASK;
|
||||
|
||||
/* bail out if the upper three levels don't match */
|
||||
if (sgi_aff != affinity)
|
||||
return -1;
|
||||
|
||||
/* Is this VCPU's bit set in the mask ? */
|
||||
if (!(sgi_cpu_mask & BIT(level0)))
|
||||
return -1;
|
||||
|
||||
return level0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The ICC_SGI* registers encode the affinity differently from the MPIDR,
|
||||
* so provide a wrapper to use the existing defines to isolate a certain
|
||||
* affinity level.
|
||||
*/
|
||||
#define SGI_AFFINITY_LEVEL(reg, level) \
|
||||
((((reg) & ICC_SGI1R_AFFINITY_## level ##_MASK) \
|
||||
>> ICC_SGI1R_AFFINITY_## level ##_SHIFT) << MPIDR_LEVEL_SHIFT(level))
|
||||
|
||||
/**
|
||||
* vgic_v3_dispatch_sgi - handle SGI requests from VCPUs
|
||||
* @vcpu: The VCPU requesting a SGI
|
||||
* @reg: The value written into ICC_{ASGI1,SGI0,SGI1}R by that VCPU
|
||||
* @allow_group1: Does the sysreg access allow generation of G1 SGIs
|
||||
*
|
||||
* With GICv3 (and ARE=1) CPUs trigger SGIs by writing to a system register.
|
||||
* This will trap in sys_regs.c and call this function.
|
||||
* This ICC_SGI1R_EL1 register contains the upper three affinity levels of the
|
||||
* target processors as well as a bitmask of 16 Aff0 CPUs.
|
||||
* If the interrupt routing mode bit is not set, we iterate over all VCPUs to
|
||||
* check for matching ones. If this bit is set, we signal all, but not the
|
||||
* calling VCPU.
|
||||
*/
|
||||
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
struct kvm_vcpu *c_vcpu;
|
||||
u16 target_cpus;
|
||||
u64 mpidr;
|
||||
int sgi, c;
|
||||
int vcpu_id = vcpu->vcpu_id;
|
||||
bool broadcast;
|
||||
unsigned long flags;
|
||||
|
||||
sgi = (reg & ICC_SGI1R_SGI_ID_MASK) >> ICC_SGI1R_SGI_ID_SHIFT;
|
||||
broadcast = reg & BIT_ULL(ICC_SGI1R_IRQ_ROUTING_MODE_BIT);
|
||||
target_cpus = (reg & ICC_SGI1R_TARGET_LIST_MASK) >> ICC_SGI1R_TARGET_LIST_SHIFT;
|
||||
mpidr = SGI_AFFINITY_LEVEL(reg, 3);
|
||||
mpidr |= SGI_AFFINITY_LEVEL(reg, 2);
|
||||
mpidr |= SGI_AFFINITY_LEVEL(reg, 1);
|
||||
|
||||
/*
|
||||
* We iterate over all VCPUs to find the MPIDRs matching the request.
|
||||
* If we have handled one CPU, we clear its bit to detect early
|
||||
* if we are already finished. This avoids iterating through all
|
||||
* VCPUs when most of the times we just signal a single VCPU.
|
||||
*/
|
||||
kvm_for_each_vcpu(c, c_vcpu, kvm) {
|
||||
struct vgic_irq *irq;
|
||||
|
||||
/* Exit early if we have dealt with all requested CPUs */
|
||||
if (!broadcast && target_cpus == 0)
|
||||
break;
|
||||
|
||||
/* Don't signal the calling VCPU */
|
||||
if (broadcast && c == vcpu_id)
|
||||
continue;
|
||||
|
||||
if (!broadcast) {
|
||||
int level0;
|
||||
|
||||
level0 = match_mpidr(mpidr, target_cpus, c_vcpu);
|
||||
if (level0 == -1)
|
||||
continue;
|
||||
|
||||
/* remove this matching VCPU from the mask */
|
||||
target_cpus &= ~BIT(level0);
|
||||
}
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, c_vcpu, sgi);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
/*
|
||||
* An access targetting Group0 SGIs can only generate
|
||||
* those, while an access targetting Group1 SGIs can
|
||||
* generate interrupts of either group.
|
||||
*/
|
||||
if (!irq->group || allow_group1) {
|
||||
irq->pending_latch = true;
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
} else {
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
}
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
int vgic_v3_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
int offset, u32 *val)
|
||||
{
|
||||
struct vgic_io_device dev = {
|
||||
.regions = vgic_v3_dist_registers,
|
||||
.nr_regions = ARRAY_SIZE(vgic_v3_dist_registers),
|
||||
};
|
||||
|
||||
return vgic_uaccess(vcpu, &dev, is_write, offset, val);
|
||||
}
|
||||
|
||||
int vgic_v3_redist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
int offset, u32 *val)
|
||||
{
|
||||
struct vgic_io_device rd_dev = {
|
||||
.regions = vgic_v3_rd_registers,
|
||||
.nr_regions = ARRAY_SIZE(vgic_v3_rd_registers),
|
||||
};
|
||||
|
||||
return vgic_uaccess(vcpu, &rd_dev, is_write, offset, val);
|
||||
}
|
||||
|
||||
int vgic_v3_line_level_info_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
u32 intid, u64 *val)
|
||||
{
|
||||
if (intid % 32)
|
||||
return -EINVAL;
|
||||
|
||||
if (is_write)
|
||||
vgic_write_irq_line_level_info(vcpu, intid, *val);
|
||||
else
|
||||
*val = vgic_read_irq_line_level_info(vcpu, intid);
|
||||
|
||||
return 0;
|
||||
}
|
947
virt/kvm/arm/vgic/vgic-mmio.c
Normal file
947
virt/kvm/arm/vgic/vgic-mmio.c
Normal file
@ -0,0 +1,947 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* VGIC MMIO handling functions
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <kvm/iodev.h>
|
||||
#include <kvm/arm_arch_timer.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
|
||||
#include "vgic.h"
|
||||
#include "vgic-mmio.h"
|
||||
|
||||
unsigned long vgic_mmio_read_raz(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long vgic_mmio_read_rao(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
return -1UL;
|
||||
}
|
||||
|
||||
void vgic_mmio_write_wi(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len, unsigned long val)
|
||||
{
|
||||
/* Ignore */
|
||||
}
|
||||
|
||||
int vgic_mmio_uaccess_write_wi(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len, unsigned long val)
|
||||
{
|
||||
/* Ignore */
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long vgic_mmio_read_group(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
u32 value = 0;
|
||||
int i;
|
||||
|
||||
/* Loop over all IRQs affected by this read */
|
||||
for (i = 0; i < len * 8; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
if (irq->group)
|
||||
value |= BIT(i);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void vgic_mmio_write_group(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len, unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < len * 8; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
irq->group = !!(val & BIT(i));
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read accesses to both GICD_ICENABLER and GICD_ISENABLER return the value
|
||||
* of the enabled bit, so there is only one function for both here.
|
||||
*/
|
||||
unsigned long vgic_mmio_read_enable(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
u32 value = 0;
|
||||
int i;
|
||||
|
||||
/* Loop over all IRQs affected by this read */
|
||||
for (i = 0; i < len * 8; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
if (irq->enabled)
|
||||
value |= (1U << i);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void vgic_mmio_write_senable(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for_each_set_bit(i, &val, len * 8) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
if (vgic_irq_is_mapped_level(irq)) {
|
||||
bool was_high = irq->line_level;
|
||||
|
||||
/*
|
||||
* We need to update the state of the interrupt because
|
||||
* the guest might have changed the state of the device
|
||||
* while the interrupt was disabled at the VGIC level.
|
||||
*/
|
||||
irq->line_level = vgic_get_phys_line_level(irq);
|
||||
/*
|
||||
* Deactivate the physical interrupt so the GIC will let
|
||||
* us know when it is asserted again.
|
||||
*/
|
||||
if (!irq->active && was_high && !irq->line_level)
|
||||
vgic_irq_set_phys_active(irq, false);
|
||||
}
|
||||
irq->enabled = true;
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for_each_set_bit(i, &val, len * 8) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
irq->enabled = false;
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
u32 value = 0;
|
||||
int i;
|
||||
|
||||
/* Loop over all IRQs affected by this read */
|
||||
for (i = 0; i < len * 8; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
if (irq_is_pending(irq))
|
||||
value |= (1U << i);
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will return the VCPU that performed the MMIO access and
|
||||
* trapped from within the VM, and will return NULL if this is a userspace
|
||||
* access.
|
||||
*
|
||||
* We can disable preemption locally around accessing the per-CPU variable,
|
||||
* and use the resolved vcpu pointer after enabling preemption again, because
|
||||
* even if the current thread is migrated to another CPU, reading the per-CPU
|
||||
* value later will give us the same value as we update the per-CPU variable
|
||||
* in the preempt notifier handlers.
|
||||
*/
|
||||
static struct kvm_vcpu *vgic_get_mmio_requester_vcpu(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
preempt_disable();
|
||||
vcpu = kvm_arm_get_running_vcpu();
|
||||
preempt_enable();
|
||||
return vcpu;
|
||||
}
|
||||
|
||||
/* Must be called with irq->irq_lock held */
|
||||
static void vgic_hw_irq_spending(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
|
||||
bool is_uaccess)
|
||||
{
|
||||
if (is_uaccess)
|
||||
return;
|
||||
|
||||
irq->pending_latch = true;
|
||||
vgic_irq_set_phys_active(irq, true);
|
||||
}
|
||||
|
||||
static bool is_vgic_v2_sgi(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
|
||||
{
|
||||
return (vgic_irq_is_sgi(irq->intid) &&
|
||||
vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
}
|
||||
|
||||
void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
bool is_uaccess = !vgic_get_mmio_requester_vcpu();
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for_each_set_bit(i, &val, len * 8) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
/* GICD_ISPENDR0 SGI bits are WI */
|
||||
if (is_vgic_v2_sgi(vcpu, irq)) {
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
continue;
|
||||
}
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
if (irq->hw)
|
||||
vgic_hw_irq_spending(vcpu, irq, is_uaccess);
|
||||
else
|
||||
irq->pending_latch = true;
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
/* Must be called with irq->irq_lock held */
|
||||
static void vgic_hw_irq_cpending(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
|
||||
bool is_uaccess)
|
||||
{
|
||||
if (is_uaccess)
|
||||
return;
|
||||
|
||||
irq->pending_latch = false;
|
||||
|
||||
/*
|
||||
* We don't want the guest to effectively mask the physical
|
||||
* interrupt by doing a write to SPENDR followed by a write to
|
||||
* CPENDR for HW interrupts, so we clear the active state on
|
||||
* the physical side if the virtual interrupt is not active.
|
||||
* This may lead to taking an additional interrupt on the
|
||||
* host, but that should not be a problem as the worst that
|
||||
* can happen is an additional vgic injection. We also clear
|
||||
* the pending state to maintain proper semantics for edge HW
|
||||
* interrupts.
|
||||
*/
|
||||
vgic_irq_set_phys_pending(irq, false);
|
||||
if (!irq->active)
|
||||
vgic_irq_set_phys_active(irq, false);
|
||||
}
|
||||
|
||||
void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
bool is_uaccess = !vgic_get_mmio_requester_vcpu();
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for_each_set_bit(i, &val, len * 8) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
/* GICD_ICPENDR0 SGI bits are WI */
|
||||
if (is_vgic_v2_sgi(vcpu, irq)) {
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
continue;
|
||||
}
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
if (irq->hw)
|
||||
vgic_hw_irq_cpending(vcpu, irq, is_uaccess);
|
||||
else
|
||||
irq->pending_latch = false;
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If we are fiddling with an IRQ's active state, we have to make sure the IRQ
|
||||
* is not queued on some running VCPU's LRs, because then the change to the
|
||||
* active state can be overwritten when the VCPU's state is synced coming back
|
||||
* from the guest.
|
||||
*
|
||||
* For shared interrupts as well as GICv3 private interrupts, we have to
|
||||
* stop all the VCPUs because interrupts can be migrated while we don't hold
|
||||
* the IRQ locks and we don't want to be chasing moving targets.
|
||||
*
|
||||
* For GICv2 private interrupts we don't have to do anything because
|
||||
* userspace accesses to the VGIC state already require all VCPUs to be
|
||||
* stopped, and only the VCPU itself can modify its private interrupts
|
||||
* active state, which guarantees that the VCPU is not running.
|
||||
*/
|
||||
static void vgic_access_active_prepare(struct kvm_vcpu *vcpu, u32 intid)
|
||||
{
|
||||
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3 ||
|
||||
intid >= VGIC_NR_PRIVATE_IRQS)
|
||||
kvm_arm_halt_guest(vcpu->kvm);
|
||||
}
|
||||
|
||||
/* See vgic_access_active_prepare */
|
||||
static void vgic_access_active_finish(struct kvm_vcpu *vcpu, u32 intid)
|
||||
{
|
||||
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3 ||
|
||||
intid >= VGIC_NR_PRIVATE_IRQS)
|
||||
kvm_arm_resume_guest(vcpu->kvm);
|
||||
}
|
||||
|
||||
static unsigned long __vgic_mmio_read_active(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
u32 value = 0;
|
||||
int i;
|
||||
|
||||
/* Loop over all IRQs affected by this read */
|
||||
for (i = 0; i < len * 8; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
/*
|
||||
* Even for HW interrupts, don't evaluate the HW state as
|
||||
* all the guest is interested in is the virtual state.
|
||||
*/
|
||||
if (irq->active)
|
||||
value |= (1U << i);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
u32 val;
|
||||
|
||||
mutex_lock(&vcpu->kvm->lock);
|
||||
vgic_access_active_prepare(vcpu, intid);
|
||||
|
||||
val = __vgic_mmio_read_active(vcpu, addr, len);
|
||||
|
||||
vgic_access_active_finish(vcpu, intid);
|
||||
mutex_unlock(&vcpu->kvm->lock);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned long vgic_uaccess_read_active(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
return __vgic_mmio_read_active(vcpu, addr, len);
|
||||
}
|
||||
|
||||
/* Must be called with irq->irq_lock held */
|
||||
static void vgic_hw_irq_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
|
||||
bool active, bool is_uaccess)
|
||||
{
|
||||
if (is_uaccess)
|
||||
return;
|
||||
|
||||
irq->active = active;
|
||||
vgic_irq_set_phys_active(irq, active);
|
||||
}
|
||||
|
||||
static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
|
||||
bool active)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct kvm_vcpu *requester_vcpu = vgic_get_mmio_requester_vcpu();
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
if (irq->hw) {
|
||||
vgic_hw_irq_change_active(vcpu, irq, active, !requester_vcpu);
|
||||
} else {
|
||||
u32 model = vcpu->kvm->arch.vgic.vgic_model;
|
||||
u8 active_source;
|
||||
|
||||
irq->active = active;
|
||||
|
||||
/*
|
||||
* The GICv2 architecture indicates that the source CPUID for
|
||||
* an SGI should be provided during an EOI which implies that
|
||||
* the active state is stored somewhere, but at the same time
|
||||
* this state is not architecturally exposed anywhere and we
|
||||
* have no way of knowing the right source.
|
||||
*
|
||||
* This may lead to a VCPU not being able to receive
|
||||
* additional instances of a particular SGI after migration
|
||||
* for a GICv2 VM on some GIC implementations. Oh well.
|
||||
*/
|
||||
active_source = (requester_vcpu) ? requester_vcpu->vcpu_id : 0;
|
||||
|
||||
if (model == KVM_DEV_TYPE_ARM_VGIC_V2 &&
|
||||
active && vgic_irq_is_sgi(irq->intid))
|
||||
irq->active_source = active_source;
|
||||
}
|
||||
|
||||
if (irq->active)
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
else
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
}
|
||||
|
||||
static void __vgic_mmio_write_cactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
int i;
|
||||
|
||||
for_each_set_bit(i, &val, len * 8) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
vgic_mmio_change_active(vcpu, irq, false);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
void vgic_mmio_write_cactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
|
||||
mutex_lock(&vcpu->kvm->lock);
|
||||
vgic_access_active_prepare(vcpu, intid);
|
||||
|
||||
__vgic_mmio_write_cactive(vcpu, addr, len, val);
|
||||
|
||||
vgic_access_active_finish(vcpu, intid);
|
||||
mutex_unlock(&vcpu->kvm->lock);
|
||||
}
|
||||
|
||||
int vgic_mmio_uaccess_write_cactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
__vgic_mmio_write_cactive(vcpu, addr, len, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __vgic_mmio_write_sactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
int i;
|
||||
|
||||
for_each_set_bit(i, &val, len * 8) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
vgic_mmio_change_active(vcpu, irq, true);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
void vgic_mmio_write_sactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
|
||||
|
||||
mutex_lock(&vcpu->kvm->lock);
|
||||
vgic_access_active_prepare(vcpu, intid);
|
||||
|
||||
__vgic_mmio_write_sactive(vcpu, addr, len, val);
|
||||
|
||||
vgic_access_active_finish(vcpu, intid);
|
||||
mutex_unlock(&vcpu->kvm->lock);
|
||||
}
|
||||
|
||||
int vgic_mmio_uaccess_write_sactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
__vgic_mmio_write_sactive(vcpu, addr, len, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long vgic_mmio_read_priority(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
|
||||
int i;
|
||||
u64 val = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
val |= (u64)irq->priority << (i * 8);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* We currently don't handle changing the priority of an interrupt that
|
||||
* is already pending on a VCPU. If there is a need for this, we would
|
||||
* need to make this VCPU exit and re-evaluate the priorities, potentially
|
||||
* leading to this interrupt getting presented now to the guest (if it has
|
||||
* been masked by the priority mask before).
|
||||
*/
|
||||
void vgic_mmio_write_priority(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
/* Narrow the priority range to what we actually support */
|
||||
irq->priority = (val >> (i * 8)) & GENMASK(7, 8 - VGIC_PRI_BITS);
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 2);
|
||||
u32 value = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len * 4; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
if (irq->config == VGIC_CONFIG_EDGE)
|
||||
value |= (2U << (i * 2));
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
u32 intid = VGIC_ADDR_TO_INTID(addr, 2);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < len * 4; i++) {
|
||||
struct vgic_irq *irq;
|
||||
|
||||
/*
|
||||
* The configuration cannot be changed for SGIs in general,
|
||||
* for PPIs this is IMPLEMENTATION DEFINED. The arch timer
|
||||
* code relies on PPIs being level triggered, so we also
|
||||
* make them read-only here.
|
||||
*/
|
||||
if (intid + i < VGIC_NR_PRIVATE_IRQS)
|
||||
continue;
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
|
||||
if (test_bit(i * 2 + 1, &val))
|
||||
irq->config = VGIC_CONFIG_EDGE;
|
||||
else
|
||||
irq->config = VGIC_CONFIG_LEVEL;
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
u64 vgic_read_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid)
|
||||
{
|
||||
int i;
|
||||
u64 val = 0;
|
||||
int nr_irqs = vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
struct vgic_irq *irq;
|
||||
|
||||
if ((intid + i) < VGIC_NR_SGIS || (intid + i) >= nr_irqs)
|
||||
continue;
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
if (irq->config == VGIC_CONFIG_LEVEL && irq->line_level)
|
||||
val |= (1U << i);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void vgic_write_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid,
|
||||
const u64 val)
|
||||
{
|
||||
int i;
|
||||
int nr_irqs = vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
struct vgic_irq *irq;
|
||||
bool new_level;
|
||||
|
||||
if ((intid + i) < VGIC_NR_SGIS || (intid + i) >= nr_irqs)
|
||||
continue;
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
/*
|
||||
* Line level is set irrespective of irq type
|
||||
* (level or edge) to avoid dependency that VM should
|
||||
* restore irq config before line level.
|
||||
*/
|
||||
new_level = !!(val & (1U << i));
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
irq->line_level = new_level;
|
||||
if (new_level)
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
else
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
static int match_region(const void *key, const void *elt)
|
||||
{
|
||||
const unsigned int offset = (unsigned long)key;
|
||||
const struct vgic_register_region *region = elt;
|
||||
|
||||
if (offset < region->reg_offset)
|
||||
return -1;
|
||||
|
||||
if (offset >= region->reg_offset + region->len)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct vgic_register_region *
|
||||
vgic_find_mmio_region(const struct vgic_register_region *regions,
|
||||
int nr_regions, unsigned int offset)
|
||||
{
|
||||
return bsearch((void *)(uintptr_t)offset, regions, nr_regions,
|
||||
sizeof(regions[0]), match_region);
|
||||
}
|
||||
|
||||
void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
|
||||
{
|
||||
if (kvm_vgic_global_state.type == VGIC_V2)
|
||||
vgic_v2_set_vmcr(vcpu, vmcr);
|
||||
else
|
||||
vgic_v3_set_vmcr(vcpu, vmcr);
|
||||
}
|
||||
|
||||
void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
|
||||
{
|
||||
if (kvm_vgic_global_state.type == VGIC_V2)
|
||||
vgic_v2_get_vmcr(vcpu, vmcr);
|
||||
else
|
||||
vgic_v3_get_vmcr(vcpu, vmcr);
|
||||
}
|
||||
|
||||
/*
|
||||
* kvm_mmio_read_buf() returns a value in a format where it can be converted
|
||||
* to a byte array and be directly observed as the guest wanted it to appear
|
||||
* in memory if it had done the store itself, which is LE for the GIC, as the
|
||||
* guest knows the GIC is always LE.
|
||||
*
|
||||
* We convert this value to the CPUs native format to deal with it as a data
|
||||
* value.
|
||||
*/
|
||||
unsigned long vgic_data_mmio_bus_to_host(const void *val, unsigned int len)
|
||||
{
|
||||
unsigned long data = kvm_mmio_read_buf(val, len);
|
||||
|
||||
switch (len) {
|
||||
case 1:
|
||||
return data;
|
||||
case 2:
|
||||
return le16_to_cpu(data);
|
||||
case 4:
|
||||
return le32_to_cpu(data);
|
||||
default:
|
||||
return le64_to_cpu(data);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* kvm_mmio_write_buf() expects a value in a format such that if converted to
|
||||
* a byte array it is observed as the guest would see it if it could perform
|
||||
* the load directly. Since the GIC is LE, and the guest knows this, the
|
||||
* guest expects a value in little endian format.
|
||||
*
|
||||
* We convert the data value from the CPUs native format to LE so that the
|
||||
* value is returned in the proper format.
|
||||
*/
|
||||
void vgic_data_host_to_mmio_bus(void *buf, unsigned int len,
|
||||
unsigned long data)
|
||||
{
|
||||
switch (len) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
data = cpu_to_le16(data);
|
||||
break;
|
||||
case 4:
|
||||
data = cpu_to_le32(data);
|
||||
break;
|
||||
default:
|
||||
data = cpu_to_le64(data);
|
||||
}
|
||||
|
||||
kvm_mmio_write_buf(buf, len, data);
|
||||
}
|
||||
|
||||
static
|
||||
struct vgic_io_device *kvm_to_vgic_iodev(const struct kvm_io_device *dev)
|
||||
{
|
||||
return container_of(dev, struct vgic_io_device, dev);
|
||||
}
|
||||
|
||||
static bool check_region(const struct kvm *kvm,
|
||||
const struct vgic_register_region *region,
|
||||
gpa_t addr, int len)
|
||||
{
|
||||
int flags, nr_irqs = kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS;
|
||||
|
||||
switch (len) {
|
||||
case sizeof(u8):
|
||||
flags = VGIC_ACCESS_8bit;
|
||||
break;
|
||||
case sizeof(u32):
|
||||
flags = VGIC_ACCESS_32bit;
|
||||
break;
|
||||
case sizeof(u64):
|
||||
flags = VGIC_ACCESS_64bit;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((region->access_flags & flags) && IS_ALIGNED(addr, len)) {
|
||||
if (!region->bits_per_irq)
|
||||
return true;
|
||||
|
||||
/* Do we access a non-allocated IRQ? */
|
||||
return VGIC_ADDR_TO_INTID(addr, region->bits_per_irq) < nr_irqs;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const struct vgic_register_region *
|
||||
vgic_get_mmio_region(struct kvm_vcpu *vcpu, struct vgic_io_device *iodev,
|
||||
gpa_t addr, int len)
|
||||
{
|
||||
const struct vgic_register_region *region;
|
||||
|
||||
region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions,
|
||||
addr - iodev->base_addr);
|
||||
if (!region || !check_region(vcpu->kvm, region, addr, len))
|
||||
return NULL;
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
static int vgic_uaccess_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
|
||||
gpa_t addr, u32 *val)
|
||||
{
|
||||
struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
|
||||
const struct vgic_register_region *region;
|
||||
struct kvm_vcpu *r_vcpu;
|
||||
|
||||
region = vgic_get_mmio_region(vcpu, iodev, addr, sizeof(u32));
|
||||
if (!region) {
|
||||
*val = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
|
||||
if (region->uaccess_read)
|
||||
*val = region->uaccess_read(r_vcpu, addr, sizeof(u32));
|
||||
else
|
||||
*val = region->read(r_vcpu, addr, sizeof(u32));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vgic_uaccess_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
|
||||
gpa_t addr, const u32 *val)
|
||||
{
|
||||
struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
|
||||
const struct vgic_register_region *region;
|
||||
struct kvm_vcpu *r_vcpu;
|
||||
|
||||
region = vgic_get_mmio_region(vcpu, iodev, addr, sizeof(u32));
|
||||
if (!region)
|
||||
return 0;
|
||||
|
||||
r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
|
||||
if (region->uaccess_write)
|
||||
return region->uaccess_write(r_vcpu, addr, sizeof(u32), *val);
|
||||
|
||||
region->write(r_vcpu, addr, sizeof(u32), *val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Userland access to VGIC registers.
|
||||
*/
|
||||
int vgic_uaccess(struct kvm_vcpu *vcpu, struct vgic_io_device *dev,
|
||||
bool is_write, int offset, u32 *val)
|
||||
{
|
||||
if (is_write)
|
||||
return vgic_uaccess_write(vcpu, &dev->dev, offset, val);
|
||||
else
|
||||
return vgic_uaccess_read(vcpu, &dev->dev, offset, val);
|
||||
}
|
||||
|
||||
static int dispatch_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
|
||||
gpa_t addr, int len, void *val)
|
||||
{
|
||||
struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
|
||||
const struct vgic_register_region *region;
|
||||
unsigned long data = 0;
|
||||
|
||||
region = vgic_get_mmio_region(vcpu, iodev, addr, len);
|
||||
if (!region) {
|
||||
memset(val, 0, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (iodev->iodev_type) {
|
||||
case IODEV_CPUIF:
|
||||
data = region->read(vcpu, addr, len);
|
||||
break;
|
||||
case IODEV_DIST:
|
||||
data = region->read(vcpu, addr, len);
|
||||
break;
|
||||
case IODEV_REDIST:
|
||||
data = region->read(iodev->redist_vcpu, addr, len);
|
||||
break;
|
||||
case IODEV_ITS:
|
||||
data = region->its_read(vcpu->kvm, iodev->its, addr, len);
|
||||
break;
|
||||
}
|
||||
|
||||
vgic_data_host_to_mmio_bus(val, len, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
|
||||
gpa_t addr, int len, const void *val)
|
||||
{
|
||||
struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
|
||||
const struct vgic_register_region *region;
|
||||
unsigned long data = vgic_data_mmio_bus_to_host(val, len);
|
||||
|
||||
region = vgic_get_mmio_region(vcpu, iodev, addr, len);
|
||||
if (!region)
|
||||
return 0;
|
||||
|
||||
switch (iodev->iodev_type) {
|
||||
case IODEV_CPUIF:
|
||||
region->write(vcpu, addr, len, data);
|
||||
break;
|
||||
case IODEV_DIST:
|
||||
region->write(vcpu, addr, len, data);
|
||||
break;
|
||||
case IODEV_REDIST:
|
||||
region->write(iodev->redist_vcpu, addr, len, data);
|
||||
break;
|
||||
case IODEV_ITS:
|
||||
region->its_write(vcpu->kvm, iodev->its, addr, len, data);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct kvm_io_device_ops kvm_io_gic_ops = {
|
||||
.read = dispatch_mmio_read,
|
||||
.write = dispatch_mmio_write,
|
||||
};
|
||||
|
||||
int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
|
||||
enum vgic_type type)
|
||||
{
|
||||
struct vgic_io_device *io_device = &kvm->arch.vgic.dist_iodev;
|
||||
int ret = 0;
|
||||
unsigned int len;
|
||||
|
||||
switch (type) {
|
||||
case VGIC_V2:
|
||||
len = vgic_v2_init_dist_iodev(io_device);
|
||||
break;
|
||||
case VGIC_V3:
|
||||
len = vgic_v3_init_dist_iodev(io_device);
|
||||
break;
|
||||
default:
|
||||
BUG_ON(1);
|
||||
}
|
||||
|
||||
io_device->base_addr = dist_base_address;
|
||||
io_device->iodev_type = IODEV_DIST;
|
||||
io_device->redist_vcpu = NULL;
|
||||
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, dist_base_address,
|
||||
len, &io_device->dev);
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
|
||||
return ret;
|
||||
}
|
216
virt/kvm/arm/vgic/vgic-mmio.h
Normal file
216
virt/kvm/arm/vgic/vgic-mmio.h
Normal file
@ -0,0 +1,216 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2015, 2016 ARM Ltd.
|
||||
*/
|
||||
#ifndef __KVM_ARM_VGIC_MMIO_H__
|
||||
#define __KVM_ARM_VGIC_MMIO_H__
|
||||
|
||||
struct vgic_register_region {
|
||||
unsigned int reg_offset;
|
||||
unsigned int len;
|
||||
unsigned int bits_per_irq;
|
||||
unsigned int access_flags;
|
||||
union {
|
||||
unsigned long (*read)(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len);
|
||||
unsigned long (*its_read)(struct kvm *kvm, struct vgic_its *its,
|
||||
gpa_t addr, unsigned int len);
|
||||
};
|
||||
union {
|
||||
void (*write)(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len, unsigned long val);
|
||||
void (*its_write)(struct kvm *kvm, struct vgic_its *its,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
};
|
||||
unsigned long (*uaccess_read)(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len);
|
||||
union {
|
||||
int (*uaccess_write)(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len, unsigned long val);
|
||||
int (*uaccess_its_write)(struct kvm *kvm, struct vgic_its *its,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
};
|
||||
};
|
||||
|
||||
extern struct kvm_io_device_ops kvm_io_gic_ops;
|
||||
|
||||
#define VGIC_ACCESS_8bit 1
|
||||
#define VGIC_ACCESS_32bit 2
|
||||
#define VGIC_ACCESS_64bit 4
|
||||
|
||||
/*
|
||||
* Generate a mask that covers the number of bytes required to address
|
||||
* up to 1024 interrupts, each represented by <bits> bits. This assumes
|
||||
* that <bits> is a power of two.
|
||||
*/
|
||||
#define VGIC_ADDR_IRQ_MASK(bits) (((bits) * 1024 / 8) - 1)
|
||||
|
||||
/*
|
||||
* (addr & mask) gives us the _byte_ offset for the INT ID.
|
||||
* We multiply this by 8 the get the _bit_ offset, then divide this by
|
||||
* the number of bits to learn the actual INT ID.
|
||||
* But instead of a division (which requires a "long long div" implementation),
|
||||
* we shift by the binary logarithm of <bits>.
|
||||
* This assumes that <bits> is a power of two.
|
||||
*/
|
||||
#define VGIC_ADDR_TO_INTID(addr, bits) (((addr) & VGIC_ADDR_IRQ_MASK(bits)) * \
|
||||
8 >> ilog2(bits))
|
||||
|
||||
/*
|
||||
* Some VGIC registers store per-IRQ information, with a different number
|
||||
* of bits per IRQ. For those registers this macro is used.
|
||||
* The _WITH_LENGTH version instantiates registers with a fixed length
|
||||
* and is mutually exclusive with the _PER_IRQ version.
|
||||
*/
|
||||
#define REGISTER_DESC_WITH_BITS_PER_IRQ(off, rd, wr, ur, uw, bpi, acc) \
|
||||
{ \
|
||||
.reg_offset = off, \
|
||||
.bits_per_irq = bpi, \
|
||||
.len = bpi * 1024 / 8, \
|
||||
.access_flags = acc, \
|
||||
.read = rd, \
|
||||
.write = wr, \
|
||||
.uaccess_read = ur, \
|
||||
.uaccess_write = uw, \
|
||||
}
|
||||
|
||||
#define REGISTER_DESC_WITH_LENGTH(off, rd, wr, length, acc) \
|
||||
{ \
|
||||
.reg_offset = off, \
|
||||
.bits_per_irq = 0, \
|
||||
.len = length, \
|
||||
.access_flags = acc, \
|
||||
.read = rd, \
|
||||
.write = wr, \
|
||||
}
|
||||
|
||||
#define REGISTER_DESC_WITH_LENGTH_UACCESS(off, rd, wr, urd, uwr, length, acc) \
|
||||
{ \
|
||||
.reg_offset = off, \
|
||||
.bits_per_irq = 0, \
|
||||
.len = length, \
|
||||
.access_flags = acc, \
|
||||
.read = rd, \
|
||||
.write = wr, \
|
||||
.uaccess_read = urd, \
|
||||
.uaccess_write = uwr, \
|
||||
}
|
||||
|
||||
int kvm_vgic_register_mmio_region(struct kvm *kvm, struct kvm_vcpu *vcpu,
|
||||
struct vgic_register_region *reg_desc,
|
||||
struct vgic_io_device *region,
|
||||
int nr_irqs, bool offset_private);
|
||||
|
||||
unsigned long vgic_data_mmio_bus_to_host(const void *val, unsigned int len);
|
||||
|
||||
void vgic_data_host_to_mmio_bus(void *buf, unsigned int len,
|
||||
unsigned long data);
|
||||
|
||||
unsigned long extract_bytes(u64 data, unsigned int offset,
|
||||
unsigned int num);
|
||||
|
||||
u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
unsigned long vgic_mmio_read_raz(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len);
|
||||
|
||||
unsigned long vgic_mmio_read_rao(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len);
|
||||
|
||||
void vgic_mmio_write_wi(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len, unsigned long val);
|
||||
|
||||
int vgic_mmio_uaccess_write_wi(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len, unsigned long val);
|
||||
|
||||
unsigned long vgic_mmio_read_group(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len);
|
||||
|
||||
void vgic_mmio_write_group(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len, unsigned long val);
|
||||
|
||||
unsigned long vgic_mmio_read_enable(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len);
|
||||
|
||||
void vgic_mmio_write_senable(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
unsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len);
|
||||
|
||||
void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len);
|
||||
|
||||
unsigned long vgic_uaccess_read_active(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len);
|
||||
|
||||
void vgic_mmio_write_cactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
void vgic_mmio_write_sactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
int vgic_mmio_uaccess_write_cactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
int vgic_mmio_uaccess_write_sactive(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
unsigned long vgic_mmio_read_priority(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len);
|
||||
|
||||
void vgic_mmio_write_priority(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
unsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len);
|
||||
|
||||
void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
int vgic_uaccess(struct kvm_vcpu *vcpu, struct vgic_io_device *dev,
|
||||
bool is_write, int offset, u32 *val);
|
||||
|
||||
u64 vgic_read_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid);
|
||||
|
||||
void vgic_write_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid,
|
||||
const u64 val);
|
||||
|
||||
unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev);
|
||||
|
||||
unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev);
|
||||
|
||||
u64 vgic_sanitise_outer_cacheability(u64 reg);
|
||||
u64 vgic_sanitise_inner_cacheability(u64 reg);
|
||||
u64 vgic_sanitise_shareability(u64 reg);
|
||||
u64 vgic_sanitise_field(u64 reg, u64 field_mask, int field_shift,
|
||||
u64 (*sanitise_fn)(u64));
|
||||
|
||||
/* Find the proper register handler entry given a certain address offset */
|
||||
const struct vgic_register_region *
|
||||
vgic_find_mmio_region(const struct vgic_register_region *regions,
|
||||
int nr_regions, unsigned int offset);
|
||||
|
||||
#endif
|
504
virt/kvm/arm/vgic/vgic-v2.c
Normal file
504
virt/kvm/arm/vgic/vgic-v2.c
Normal file
@ -0,0 +1,504 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015, 2016 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
|
||||
#include "vgic.h"
|
||||
|
||||
static inline void vgic_v2_write_lr(int lr, u32 val)
|
||||
{
|
||||
void __iomem *base = kvm_vgic_global_state.vctrl_base;
|
||||
|
||||
writel_relaxed(val, base + GICH_LR0 + (lr * 4));
|
||||
}
|
||||
|
||||
void vgic_v2_init_lrs(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < kvm_vgic_global_state.nr_lr; i++)
|
||||
vgic_v2_write_lr(i, 0);
|
||||
}
|
||||
|
||||
void vgic_v2_set_underflow(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
|
||||
cpuif->vgic_hcr |= GICH_HCR_UIE;
|
||||
}
|
||||
|
||||
static bool lr_signals_eoi_mi(u32 lr_val)
|
||||
{
|
||||
return !(lr_val & GICH_LR_STATE) && (lr_val & GICH_LR_EOI) &&
|
||||
!(lr_val & GICH_LR_HW);
|
||||
}
|
||||
|
||||
/*
|
||||
* transfer the content of the LRs back into the corresponding ap_list:
|
||||
* - active bit is transferred as is
|
||||
* - pending bit is
|
||||
* - transferred as is in case of edge sensitive IRQs
|
||||
* - set to the line-level (resample time) for level sensitive IRQs
|
||||
*/
|
||||
void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_v2_cpu_if *cpuif = &vgic_cpu->vgic_v2;
|
||||
int lr;
|
||||
|
||||
DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
|
||||
|
||||
cpuif->vgic_hcr &= ~GICH_HCR_UIE;
|
||||
|
||||
for (lr = 0; lr < vgic_cpu->used_lrs; lr++) {
|
||||
u32 val = cpuif->vgic_lr[lr];
|
||||
u32 cpuid, intid = val & GICH_LR_VIRTUALID;
|
||||
struct vgic_irq *irq;
|
||||
|
||||
/* Extract the source vCPU id from the LR */
|
||||
cpuid = val & GICH_LR_PHYSID_CPUID;
|
||||
cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
cpuid &= 7;
|
||||
|
||||
/* Notify fds when the guest EOI'ed a level-triggered SPI */
|
||||
if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
|
||||
kvm_notify_acked_irq(vcpu->kvm, 0,
|
||||
intid - VGIC_NR_PRIVATE_IRQS);
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
|
||||
|
||||
raw_spin_lock(&irq->irq_lock);
|
||||
|
||||
/* Always preserve the active bit */
|
||||
irq->active = !!(val & GICH_LR_ACTIVE_BIT);
|
||||
|
||||
if (irq->active && vgic_irq_is_sgi(intid))
|
||||
irq->active_source = cpuid;
|
||||
|
||||
/* Edge is the only case where we preserve the pending bit */
|
||||
if (irq->config == VGIC_CONFIG_EDGE &&
|
||||
(val & GICH_LR_PENDING_BIT)) {
|
||||
irq->pending_latch = true;
|
||||
|
||||
if (vgic_irq_is_sgi(intid))
|
||||
irq->source |= (1 << cpuid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear soft pending state when level irqs have been acked.
|
||||
*/
|
||||
if (irq->config == VGIC_CONFIG_LEVEL && !(val & GICH_LR_STATE))
|
||||
irq->pending_latch = false;
|
||||
|
||||
/*
|
||||
* Level-triggered mapped IRQs are special because we only
|
||||
* observe rising edges as input to the VGIC.
|
||||
*
|
||||
* If the guest never acked the interrupt we have to sample
|
||||
* the physical line and set the line level, because the
|
||||
* device state could have changed or we simply need to
|
||||
* process the still pending interrupt later.
|
||||
*
|
||||
* If this causes us to lower the level, we have to also clear
|
||||
* the physical active state, since we will otherwise never be
|
||||
* told when the interrupt becomes asserted again.
|
||||
*/
|
||||
if (vgic_irq_is_mapped_level(irq) && (val & GICH_LR_PENDING_BIT)) {
|
||||
irq->line_level = vgic_get_phys_line_level(irq);
|
||||
|
||||
if (!irq->line_level)
|
||||
vgic_irq_set_phys_active(irq, false);
|
||||
}
|
||||
|
||||
raw_spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
vgic_cpu->used_lrs = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Populates the particular LR with the state of a given IRQ:
|
||||
* - for an edge sensitive IRQ the pending state is cleared in struct vgic_irq
|
||||
* - for a level sensitive IRQ the pending state value is unchanged;
|
||||
* it is dictated directly by the input level
|
||||
*
|
||||
* If @irq describes an SGI with multiple sources, we choose the
|
||||
* lowest-numbered source VCPU and clear that bit in the source bitmap.
|
||||
*
|
||||
* The irq_lock must be held by the caller.
|
||||
*/
|
||||
void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
|
||||
{
|
||||
u32 val = irq->intid;
|
||||
bool allow_pending = true;
|
||||
|
||||
if (irq->active) {
|
||||
val |= GICH_LR_ACTIVE_BIT;
|
||||
if (vgic_irq_is_sgi(irq->intid))
|
||||
val |= irq->active_source << GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
if (vgic_irq_is_multi_sgi(irq)) {
|
||||
allow_pending = false;
|
||||
val |= GICH_LR_EOI;
|
||||
}
|
||||
}
|
||||
|
||||
if (irq->group)
|
||||
val |= GICH_LR_GROUP1;
|
||||
|
||||
if (irq->hw) {
|
||||
val |= GICH_LR_HW;
|
||||
val |= irq->hwintid << GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
/*
|
||||
* Never set pending+active on a HW interrupt, as the
|
||||
* pending state is kept at the physical distributor
|
||||
* level.
|
||||
*/
|
||||
if (irq->active)
|
||||
allow_pending = false;
|
||||
} else {
|
||||
if (irq->config == VGIC_CONFIG_LEVEL) {
|
||||
val |= GICH_LR_EOI;
|
||||
|
||||
/*
|
||||
* Software resampling doesn't work very well
|
||||
* if we allow P+A, so let's not do that.
|
||||
*/
|
||||
if (irq->active)
|
||||
allow_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (allow_pending && irq_is_pending(irq)) {
|
||||
val |= GICH_LR_PENDING_BIT;
|
||||
|
||||
if (irq->config == VGIC_CONFIG_EDGE)
|
||||
irq->pending_latch = false;
|
||||
|
||||
if (vgic_irq_is_sgi(irq->intid)) {
|
||||
u32 src = ffs(irq->source);
|
||||
|
||||
if (WARN_RATELIMIT(!src, "No SGI source for INTID %d\n",
|
||||
irq->intid))
|
||||
return;
|
||||
|
||||
val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
irq->source &= ~(1 << (src - 1));
|
||||
if (irq->source) {
|
||||
irq->pending_latch = true;
|
||||
val |= GICH_LR_EOI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Level-triggered mapped IRQs are special because we only observe
|
||||
* rising edges as input to the VGIC. We therefore lower the line
|
||||
* level here, so that we can take new virtual IRQs. See
|
||||
* vgic_v2_fold_lr_state for more info.
|
||||
*/
|
||||
if (vgic_irq_is_mapped_level(irq) && (val & GICH_LR_PENDING_BIT))
|
||||
irq->line_level = false;
|
||||
|
||||
/* The GICv2 LR only holds five bits of priority. */
|
||||
val |= (irq->priority >> 3) << GICH_LR_PRIORITY_SHIFT;
|
||||
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = val;
|
||||
}
|
||||
|
||||
void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr)
|
||||
{
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = 0;
|
||||
}
|
||||
|
||||
void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
u32 vmcr;
|
||||
|
||||
vmcr = (vmcrp->grpen0 << GICH_VMCR_ENABLE_GRP0_SHIFT) &
|
||||
GICH_VMCR_ENABLE_GRP0_MASK;
|
||||
vmcr |= (vmcrp->grpen1 << GICH_VMCR_ENABLE_GRP1_SHIFT) &
|
||||
GICH_VMCR_ENABLE_GRP1_MASK;
|
||||
vmcr |= (vmcrp->ackctl << GICH_VMCR_ACK_CTL_SHIFT) &
|
||||
GICH_VMCR_ACK_CTL_MASK;
|
||||
vmcr |= (vmcrp->fiqen << GICH_VMCR_FIQ_EN_SHIFT) &
|
||||
GICH_VMCR_FIQ_EN_MASK;
|
||||
vmcr |= (vmcrp->cbpr << GICH_VMCR_CBPR_SHIFT) &
|
||||
GICH_VMCR_CBPR_MASK;
|
||||
vmcr |= (vmcrp->eoim << GICH_VMCR_EOI_MODE_SHIFT) &
|
||||
GICH_VMCR_EOI_MODE_MASK;
|
||||
vmcr |= (vmcrp->abpr << GICH_VMCR_ALIAS_BINPOINT_SHIFT) &
|
||||
GICH_VMCR_ALIAS_BINPOINT_MASK;
|
||||
vmcr |= (vmcrp->bpr << GICH_VMCR_BINPOINT_SHIFT) &
|
||||
GICH_VMCR_BINPOINT_MASK;
|
||||
vmcr |= ((vmcrp->pmr >> GICV_PMR_PRIORITY_SHIFT) <<
|
||||
GICH_VMCR_PRIMASK_SHIFT) & GICH_VMCR_PRIMASK_MASK;
|
||||
|
||||
cpu_if->vgic_vmcr = vmcr;
|
||||
}
|
||||
|
||||
void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
u32 vmcr;
|
||||
|
||||
vmcr = cpu_if->vgic_vmcr;
|
||||
|
||||
vmcrp->grpen0 = (vmcr & GICH_VMCR_ENABLE_GRP0_MASK) >>
|
||||
GICH_VMCR_ENABLE_GRP0_SHIFT;
|
||||
vmcrp->grpen1 = (vmcr & GICH_VMCR_ENABLE_GRP1_MASK) >>
|
||||
GICH_VMCR_ENABLE_GRP1_SHIFT;
|
||||
vmcrp->ackctl = (vmcr & GICH_VMCR_ACK_CTL_MASK) >>
|
||||
GICH_VMCR_ACK_CTL_SHIFT;
|
||||
vmcrp->fiqen = (vmcr & GICH_VMCR_FIQ_EN_MASK) >>
|
||||
GICH_VMCR_FIQ_EN_SHIFT;
|
||||
vmcrp->cbpr = (vmcr & GICH_VMCR_CBPR_MASK) >>
|
||||
GICH_VMCR_CBPR_SHIFT;
|
||||
vmcrp->eoim = (vmcr & GICH_VMCR_EOI_MODE_MASK) >>
|
||||
GICH_VMCR_EOI_MODE_SHIFT;
|
||||
|
||||
vmcrp->abpr = (vmcr & GICH_VMCR_ALIAS_BINPOINT_MASK) >>
|
||||
GICH_VMCR_ALIAS_BINPOINT_SHIFT;
|
||||
vmcrp->bpr = (vmcr & GICH_VMCR_BINPOINT_MASK) >>
|
||||
GICH_VMCR_BINPOINT_SHIFT;
|
||||
vmcrp->pmr = ((vmcr & GICH_VMCR_PRIMASK_MASK) >>
|
||||
GICH_VMCR_PRIMASK_SHIFT) << GICV_PMR_PRIORITY_SHIFT;
|
||||
}
|
||||
|
||||
void vgic_v2_enable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* By forcing VMCR to zero, the GIC will restore the binary
|
||||
* points to their reset values. Anything else resets to zero
|
||||
* anyway.
|
||||
*/
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0;
|
||||
|
||||
/* Get the show on the road... */
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN;
|
||||
}
|
||||
|
||||
/* check for overlapping regions and for regions crossing the end of memory */
|
||||
static bool vgic_v2_check_base(gpa_t dist_base, gpa_t cpu_base)
|
||||
{
|
||||
if (dist_base + KVM_VGIC_V2_DIST_SIZE < dist_base)
|
||||
return false;
|
||||
if (cpu_base + KVM_VGIC_V2_CPU_SIZE < cpu_base)
|
||||
return false;
|
||||
|
||||
if (dist_base + KVM_VGIC_V2_DIST_SIZE <= cpu_base)
|
||||
return true;
|
||||
if (cpu_base + KVM_VGIC_V2_CPU_SIZE <= dist_base)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int vgic_v2_map_resources(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
int ret = 0;
|
||||
|
||||
if (vgic_ready(kvm))
|
||||
goto out;
|
||||
|
||||
if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) ||
|
||||
IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) {
|
||||
kvm_err("Need to set vgic cpu and dist addresses first\n");
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!vgic_v2_check_base(dist->vgic_dist_base, dist->vgic_cpu_base)) {
|
||||
kvm_err("VGIC CPU and dist frames overlap\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the vgic if this hasn't already been done on demand by
|
||||
* accessing the vgic state from userspace.
|
||||
*/
|
||||
ret = vgic_init(kvm);
|
||||
if (ret) {
|
||||
kvm_err("Unable to initialize VGIC dynamic data structures\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V2);
|
||||
if (ret) {
|
||||
kvm_err("Unable to register VGIC MMIO regions\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
|
||||
ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
|
||||
kvm_vgic_global_state.vcpu_base,
|
||||
KVM_VGIC_V2_CPU_SIZE, true);
|
||||
if (ret) {
|
||||
kvm_err("Unable to remap VGIC CPU to VCPU\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
dist->ready = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap);
|
||||
|
||||
/**
|
||||
* vgic_v2_probe - probe for a VGICv2 compatible interrupt controller
|
||||
* @info: pointer to the GIC description
|
||||
*
|
||||
* Returns 0 if the VGICv2 has been probed successfully, returns an error code
|
||||
* otherwise
|
||||
*/
|
||||
int vgic_v2_probe(const struct gic_kvm_info *info)
|
||||
{
|
||||
int ret;
|
||||
u32 vtr;
|
||||
|
||||
if (!info->vctrl.start) {
|
||||
kvm_err("GICH not present in the firmware table\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (!PAGE_ALIGNED(info->vcpu.start) ||
|
||||
!PAGE_ALIGNED(resource_size(&info->vcpu))) {
|
||||
kvm_info("GICV region size/alignment is unsafe, using trapping (reduced performance)\n");
|
||||
|
||||
ret = create_hyp_io_mappings(info->vcpu.start,
|
||||
resource_size(&info->vcpu),
|
||||
&kvm_vgic_global_state.vcpu_base_va,
|
||||
&kvm_vgic_global_state.vcpu_hyp_va);
|
||||
if (ret) {
|
||||
kvm_err("Cannot map GICV into hyp\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
static_branch_enable(&vgic_v2_cpuif_trap);
|
||||
}
|
||||
|
||||
ret = create_hyp_io_mappings(info->vctrl.start,
|
||||
resource_size(&info->vctrl),
|
||||
&kvm_vgic_global_state.vctrl_base,
|
||||
&kvm_vgic_global_state.vctrl_hyp);
|
||||
if (ret) {
|
||||
kvm_err("Cannot map VCTRL into hyp\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
vtr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VTR);
|
||||
kvm_vgic_global_state.nr_lr = (vtr & 0x3f) + 1;
|
||||
|
||||
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register GICv2 KVM device\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
kvm_vgic_global_state.can_emulate_gicv2 = true;
|
||||
kvm_vgic_global_state.vcpu_base = info->vcpu.start;
|
||||
kvm_vgic_global_state.type = VGIC_V2;
|
||||
kvm_vgic_global_state.max_gic_vcpus = VGIC_V2_MAX_CPUS;
|
||||
|
||||
kvm_debug("vgic-v2@%llx\n", info->vctrl.start);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
if (kvm_vgic_global_state.vctrl_base)
|
||||
iounmap(kvm_vgic_global_state.vctrl_base);
|
||||
if (kvm_vgic_global_state.vcpu_base_va)
|
||||
iounmap(kvm_vgic_global_state.vcpu_base_va);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
|
||||
u64 elrsr;
|
||||
int i;
|
||||
|
||||
elrsr = readl_relaxed(base + GICH_ELRSR0);
|
||||
if (unlikely(used_lrs > 32))
|
||||
elrsr |= ((u64)readl_relaxed(base + GICH_ELRSR1)) << 32;
|
||||
|
||||
for (i = 0; i < used_lrs; i++) {
|
||||
if (elrsr & (1UL << i))
|
||||
cpu_if->vgic_lr[i] &= ~GICH_LR_STATE;
|
||||
else
|
||||
cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
|
||||
|
||||
writel_relaxed(0, base + GICH_LR0 + (i * 4));
|
||||
}
|
||||
}
|
||||
|
||||
void vgic_v2_save_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
void __iomem *base = kvm_vgic_global_state.vctrl_base;
|
||||
u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
|
||||
|
||||
if (!base)
|
||||
return;
|
||||
|
||||
if (used_lrs) {
|
||||
save_lrs(vcpu, base);
|
||||
writel_relaxed(0, base + GICH_HCR);
|
||||
}
|
||||
}
|
||||
|
||||
void vgic_v2_restore_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
void __iomem *base = kvm_vgic_global_state.vctrl_base;
|
||||
u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
|
||||
int i;
|
||||
|
||||
if (!base)
|
||||
return;
|
||||
|
||||
if (used_lrs) {
|
||||
writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
|
||||
for (i = 0; i < used_lrs; i++) {
|
||||
writel_relaxed(cpu_if->vgic_lr[i],
|
||||
base + GICH_LR0 + (i * 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vgic_v2_load(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
|
||||
writel_relaxed(cpu_if->vgic_vmcr,
|
||||
kvm_vgic_global_state.vctrl_base + GICH_VMCR);
|
||||
writel_relaxed(cpu_if->vgic_apr,
|
||||
kvm_vgic_global_state.vctrl_base + GICH_APR);
|
||||
}
|
||||
|
||||
void vgic_v2_vmcr_sync(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
|
||||
cpu_if->vgic_vmcr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VMCR);
|
||||
}
|
||||
|
||||
void vgic_v2_put(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
|
||||
vgic_v2_vmcr_sync(vcpu);
|
||||
cpu_if->vgic_apr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_APR);
|
||||
}
|
685
virt/kvm/arm/vgic/vgic-v3.c
Normal file
685
virt/kvm/arm/vgic/vgic-v3.c
Normal file
@ -0,0 +1,685 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
|
||||
#include "vgic.h"
|
||||
|
||||
static bool group0_trap;
|
||||
static bool group1_trap;
|
||||
static bool common_trap;
|
||||
static bool gicv4_enable;
|
||||
|
||||
void vgic_v3_set_underflow(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v3_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
cpuif->vgic_hcr |= ICH_HCR_UIE;
|
||||
}
|
||||
|
||||
static bool lr_signals_eoi_mi(u64 lr_val)
|
||||
{
|
||||
return !(lr_val & ICH_LR_STATE) && (lr_val & ICH_LR_EOI) &&
|
||||
!(lr_val & ICH_LR_HW);
|
||||
}
|
||||
|
||||
void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
|
||||
u32 model = vcpu->kvm->arch.vgic.vgic_model;
|
||||
int lr;
|
||||
|
||||
DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
|
||||
|
||||
cpuif->vgic_hcr &= ~ICH_HCR_UIE;
|
||||
|
||||
for (lr = 0; lr < vgic_cpu->used_lrs; lr++) {
|
||||
u64 val = cpuif->vgic_lr[lr];
|
||||
u32 intid, cpuid;
|
||||
struct vgic_irq *irq;
|
||||
bool is_v2_sgi = false;
|
||||
|
||||
cpuid = val & GICH_LR_PHYSID_CPUID;
|
||||
cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
|
||||
if (model == KVM_DEV_TYPE_ARM_VGIC_V3) {
|
||||
intid = val & ICH_LR_VIRTUAL_ID_MASK;
|
||||
} else {
|
||||
intid = val & GICH_LR_VIRTUALID;
|
||||
is_v2_sgi = vgic_irq_is_sgi(intid);
|
||||
}
|
||||
|
||||
/* Notify fds when the guest EOI'ed a level-triggered IRQ */
|
||||
if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
|
||||
kvm_notify_acked_irq(vcpu->kvm, 0,
|
||||
intid - VGIC_NR_PRIVATE_IRQS);
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
|
||||
if (!irq) /* An LPI could have been unmapped. */
|
||||
continue;
|
||||
|
||||
raw_spin_lock(&irq->irq_lock);
|
||||
|
||||
/* Always preserve the active bit */
|
||||
irq->active = !!(val & ICH_LR_ACTIVE_BIT);
|
||||
|
||||
if (irq->active && is_v2_sgi)
|
||||
irq->active_source = cpuid;
|
||||
|
||||
/* Edge is the only case where we preserve the pending bit */
|
||||
if (irq->config == VGIC_CONFIG_EDGE &&
|
||||
(val & ICH_LR_PENDING_BIT)) {
|
||||
irq->pending_latch = true;
|
||||
|
||||
if (is_v2_sgi)
|
||||
irq->source |= (1 << cpuid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear soft pending state when level irqs have been acked.
|
||||
*/
|
||||
if (irq->config == VGIC_CONFIG_LEVEL && !(val & ICH_LR_STATE))
|
||||
irq->pending_latch = false;
|
||||
|
||||
/*
|
||||
* Level-triggered mapped IRQs are special because we only
|
||||
* observe rising edges as input to the VGIC.
|
||||
*
|
||||
* If the guest never acked the interrupt we have to sample
|
||||
* the physical line and set the line level, because the
|
||||
* device state could have changed or we simply need to
|
||||
* process the still pending interrupt later.
|
||||
*
|
||||
* If this causes us to lower the level, we have to also clear
|
||||
* the physical active state, since we will otherwise never be
|
||||
* told when the interrupt becomes asserted again.
|
||||
*/
|
||||
if (vgic_irq_is_mapped_level(irq) && (val & ICH_LR_PENDING_BIT)) {
|
||||
irq->line_level = vgic_get_phys_line_level(irq);
|
||||
|
||||
if (!irq->line_level)
|
||||
vgic_irq_set_phys_active(irq, false);
|
||||
}
|
||||
|
||||
raw_spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
vgic_cpu->used_lrs = 0;
|
||||
}
|
||||
|
||||
/* Requires the irq to be locked already */
|
||||
void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
|
||||
{
|
||||
u32 model = vcpu->kvm->arch.vgic.vgic_model;
|
||||
u64 val = irq->intid;
|
||||
bool allow_pending = true, is_v2_sgi;
|
||||
|
||||
is_v2_sgi = (vgic_irq_is_sgi(irq->intid) &&
|
||||
model == KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
|
||||
if (irq->active) {
|
||||
val |= ICH_LR_ACTIVE_BIT;
|
||||
if (is_v2_sgi)
|
||||
val |= irq->active_source << GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
if (vgic_irq_is_multi_sgi(irq)) {
|
||||
allow_pending = false;
|
||||
val |= ICH_LR_EOI;
|
||||
}
|
||||
}
|
||||
|
||||
if (irq->hw) {
|
||||
val |= ICH_LR_HW;
|
||||
val |= ((u64)irq->hwintid) << ICH_LR_PHYS_ID_SHIFT;
|
||||
/*
|
||||
* Never set pending+active on a HW interrupt, as the
|
||||
* pending state is kept at the physical distributor
|
||||
* level.
|
||||
*/
|
||||
if (irq->active)
|
||||
allow_pending = false;
|
||||
} else {
|
||||
if (irq->config == VGIC_CONFIG_LEVEL) {
|
||||
val |= ICH_LR_EOI;
|
||||
|
||||
/*
|
||||
* Software resampling doesn't work very well
|
||||
* if we allow P+A, so let's not do that.
|
||||
*/
|
||||
if (irq->active)
|
||||
allow_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (allow_pending && irq_is_pending(irq)) {
|
||||
val |= ICH_LR_PENDING_BIT;
|
||||
|
||||
if (irq->config == VGIC_CONFIG_EDGE)
|
||||
irq->pending_latch = false;
|
||||
|
||||
if (vgic_irq_is_sgi(irq->intid) &&
|
||||
model == KVM_DEV_TYPE_ARM_VGIC_V2) {
|
||||
u32 src = ffs(irq->source);
|
||||
|
||||
if (WARN_RATELIMIT(!src, "No SGI source for INTID %d\n",
|
||||
irq->intid))
|
||||
return;
|
||||
|
||||
val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
irq->source &= ~(1 << (src - 1));
|
||||
if (irq->source) {
|
||||
irq->pending_latch = true;
|
||||
val |= ICH_LR_EOI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Level-triggered mapped IRQs are special because we only observe
|
||||
* rising edges as input to the VGIC. We therefore lower the line
|
||||
* level here, so that we can take new virtual IRQs. See
|
||||
* vgic_v3_fold_lr_state for more info.
|
||||
*/
|
||||
if (vgic_irq_is_mapped_level(irq) && (val & ICH_LR_PENDING_BIT))
|
||||
irq->line_level = false;
|
||||
|
||||
if (irq->group)
|
||||
val |= ICH_LR_GROUP;
|
||||
|
||||
val |= (u64)irq->priority << ICH_LR_PRIORITY_SHIFT;
|
||||
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr] = val;
|
||||
}
|
||||
|
||||
void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr)
|
||||
{
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr] = 0;
|
||||
}
|
||||
|
||||
void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||
{
|
||||
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
u32 model = vcpu->kvm->arch.vgic.vgic_model;
|
||||
u32 vmcr;
|
||||
|
||||
if (model == KVM_DEV_TYPE_ARM_VGIC_V2) {
|
||||
vmcr = (vmcrp->ackctl << ICH_VMCR_ACK_CTL_SHIFT) &
|
||||
ICH_VMCR_ACK_CTL_MASK;
|
||||
vmcr |= (vmcrp->fiqen << ICH_VMCR_FIQ_EN_SHIFT) &
|
||||
ICH_VMCR_FIQ_EN_MASK;
|
||||
} else {
|
||||
/*
|
||||
* When emulating GICv3 on GICv3 with SRE=1 on the
|
||||
* VFIQEn bit is RES1 and the VAckCtl bit is RES0.
|
||||
*/
|
||||
vmcr = ICH_VMCR_FIQ_EN_MASK;
|
||||
}
|
||||
|
||||
vmcr |= (vmcrp->cbpr << ICH_VMCR_CBPR_SHIFT) & ICH_VMCR_CBPR_MASK;
|
||||
vmcr |= (vmcrp->eoim << ICH_VMCR_EOIM_SHIFT) & ICH_VMCR_EOIM_MASK;
|
||||
vmcr |= (vmcrp->abpr << ICH_VMCR_BPR1_SHIFT) & ICH_VMCR_BPR1_MASK;
|
||||
vmcr |= (vmcrp->bpr << ICH_VMCR_BPR0_SHIFT) & ICH_VMCR_BPR0_MASK;
|
||||
vmcr |= (vmcrp->pmr << ICH_VMCR_PMR_SHIFT) & ICH_VMCR_PMR_MASK;
|
||||
vmcr |= (vmcrp->grpen0 << ICH_VMCR_ENG0_SHIFT) & ICH_VMCR_ENG0_MASK;
|
||||
vmcr |= (vmcrp->grpen1 << ICH_VMCR_ENG1_SHIFT) & ICH_VMCR_ENG1_MASK;
|
||||
|
||||
cpu_if->vgic_vmcr = vmcr;
|
||||
}
|
||||
|
||||
void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||
{
|
||||
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
u32 model = vcpu->kvm->arch.vgic.vgic_model;
|
||||
u32 vmcr;
|
||||
|
||||
vmcr = cpu_if->vgic_vmcr;
|
||||
|
||||
if (model == KVM_DEV_TYPE_ARM_VGIC_V2) {
|
||||
vmcrp->ackctl = (vmcr & ICH_VMCR_ACK_CTL_MASK) >>
|
||||
ICH_VMCR_ACK_CTL_SHIFT;
|
||||
vmcrp->fiqen = (vmcr & ICH_VMCR_FIQ_EN_MASK) >>
|
||||
ICH_VMCR_FIQ_EN_SHIFT;
|
||||
} else {
|
||||
/*
|
||||
* When emulating GICv3 on GICv3 with SRE=1 on the
|
||||
* VFIQEn bit is RES1 and the VAckCtl bit is RES0.
|
||||
*/
|
||||
vmcrp->fiqen = 1;
|
||||
vmcrp->ackctl = 0;
|
||||
}
|
||||
|
||||
vmcrp->cbpr = (vmcr & ICH_VMCR_CBPR_MASK) >> ICH_VMCR_CBPR_SHIFT;
|
||||
vmcrp->eoim = (vmcr & ICH_VMCR_EOIM_MASK) >> ICH_VMCR_EOIM_SHIFT;
|
||||
vmcrp->abpr = (vmcr & ICH_VMCR_BPR1_MASK) >> ICH_VMCR_BPR1_SHIFT;
|
||||
vmcrp->bpr = (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT;
|
||||
vmcrp->pmr = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT;
|
||||
vmcrp->grpen0 = (vmcr & ICH_VMCR_ENG0_MASK) >> ICH_VMCR_ENG0_SHIFT;
|
||||
vmcrp->grpen1 = (vmcr & ICH_VMCR_ENG1_MASK) >> ICH_VMCR_ENG1_SHIFT;
|
||||
}
|
||||
|
||||
#define INITIAL_PENDBASER_VALUE \
|
||||
(GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWb) | \
|
||||
GIC_BASER_CACHEABILITY(GICR_PENDBASER, OUTER, SameAsInner) | \
|
||||
GIC_BASER_SHAREABILITY(GICR_PENDBASER, InnerShareable))
|
||||
|
||||
void vgic_v3_enable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v3_cpu_if *vgic_v3 = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
/*
|
||||
* By forcing VMCR to zero, the GIC will restore the binary
|
||||
* points to their reset values. Anything else resets to zero
|
||||
* anyway.
|
||||
*/
|
||||
vgic_v3->vgic_vmcr = 0;
|
||||
|
||||
/*
|
||||
* If we are emulating a GICv3, we do it in an non-GICv2-compatible
|
||||
* way, so we force SRE to 1 to demonstrate this to the guest.
|
||||
* Also, we don't support any form of IRQ/FIQ bypass.
|
||||
* This goes with the spec allowing the value to be RAO/WI.
|
||||
*/
|
||||
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
|
||||
vgic_v3->vgic_sre = (ICC_SRE_EL1_DIB |
|
||||
ICC_SRE_EL1_DFB |
|
||||
ICC_SRE_EL1_SRE);
|
||||
vcpu->arch.vgic_cpu.pendbaser = INITIAL_PENDBASER_VALUE;
|
||||
} else {
|
||||
vgic_v3->vgic_sre = 0;
|
||||
}
|
||||
|
||||
vcpu->arch.vgic_cpu.num_id_bits = (kvm_vgic_global_state.ich_vtr_el2 &
|
||||
ICH_VTR_ID_BITS_MASK) >>
|
||||
ICH_VTR_ID_BITS_SHIFT;
|
||||
vcpu->arch.vgic_cpu.num_pri_bits = ((kvm_vgic_global_state.ich_vtr_el2 &
|
||||
ICH_VTR_PRI_BITS_MASK) >>
|
||||
ICH_VTR_PRI_BITS_SHIFT) + 1;
|
||||
|
||||
/* Get the show on the road... */
|
||||
vgic_v3->vgic_hcr = ICH_HCR_EN;
|
||||
if (group0_trap)
|
||||
vgic_v3->vgic_hcr |= ICH_HCR_TALL0;
|
||||
if (group1_trap)
|
||||
vgic_v3->vgic_hcr |= ICH_HCR_TALL1;
|
||||
if (common_trap)
|
||||
vgic_v3->vgic_hcr |= ICH_HCR_TC;
|
||||
}
|
||||
|
||||
int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
int byte_offset, bit_nr;
|
||||
gpa_t pendbase, ptr;
|
||||
bool status;
|
||||
u8 val;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
retry:
|
||||
vcpu = irq->target_vcpu;
|
||||
if (!vcpu)
|
||||
return 0;
|
||||
|
||||
pendbase = GICR_PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
|
||||
|
||||
byte_offset = irq->intid / BITS_PER_BYTE;
|
||||
bit_nr = irq->intid % BITS_PER_BYTE;
|
||||
ptr = pendbase + byte_offset;
|
||||
|
||||
ret = kvm_read_guest_lock(kvm, ptr, &val, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status = val & (1 << bit_nr);
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
if (irq->target_vcpu != vcpu) {
|
||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||
goto retry;
|
||||
}
|
||||
irq->pending_latch = status;
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
|
||||
if (status) {
|
||||
/* clear consumed data */
|
||||
val &= ~(1 << bit_nr);
|
||||
ret = kvm_write_guest_lock(kvm, ptr, &val, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_its_save_pending_tables - Save the pending tables into guest RAM
|
||||
* kvm lock and all vcpu lock must be held
|
||||
*/
|
||||
int vgic_v3_save_pending_tables(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct vgic_irq *irq;
|
||||
gpa_t last_ptr = ~(gpa_t)0;
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
|
||||
int byte_offset, bit_nr;
|
||||
struct kvm_vcpu *vcpu;
|
||||
gpa_t pendbase, ptr;
|
||||
bool stored;
|
||||
|
||||
vcpu = irq->target_vcpu;
|
||||
if (!vcpu)
|
||||
continue;
|
||||
|
||||
pendbase = GICR_PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
|
||||
|
||||
byte_offset = irq->intid / BITS_PER_BYTE;
|
||||
bit_nr = irq->intid % BITS_PER_BYTE;
|
||||
ptr = pendbase + byte_offset;
|
||||
|
||||
if (ptr != last_ptr) {
|
||||
ret = kvm_read_guest_lock(kvm, ptr, &val, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
last_ptr = ptr;
|
||||
}
|
||||
|
||||
stored = val & (1U << bit_nr);
|
||||
if (stored == irq->pending_latch)
|
||||
continue;
|
||||
|
||||
if (irq->pending_latch)
|
||||
val |= 1 << bit_nr;
|
||||
else
|
||||
val &= ~(1 << bit_nr);
|
||||
|
||||
ret = kvm_write_guest_lock(kvm, ptr, &val, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_v3_rdist_overlap - check if a region overlaps with any
|
||||
* existing redistributor region
|
||||
*
|
||||
* @kvm: kvm handle
|
||||
* @base: base of the region
|
||||
* @size: size of region
|
||||
*
|
||||
* Return: true if there is an overlap
|
||||
*/
|
||||
bool vgic_v3_rdist_overlap(struct kvm *kvm, gpa_t base, size_t size)
|
||||
{
|
||||
struct vgic_dist *d = &kvm->arch.vgic;
|
||||
struct vgic_redist_region *rdreg;
|
||||
|
||||
list_for_each_entry(rdreg, &d->rd_regions, list) {
|
||||
if ((base + size > rdreg->base) &&
|
||||
(base < rdreg->base + vgic_v3_rd_region_size(kvm, rdreg)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for overlapping regions and for regions crossing the end of memory
|
||||
* for base addresses which have already been set.
|
||||
*/
|
||||
bool vgic_v3_check_base(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *d = &kvm->arch.vgic;
|
||||
struct vgic_redist_region *rdreg;
|
||||
|
||||
if (!IS_VGIC_ADDR_UNDEF(d->vgic_dist_base) &&
|
||||
d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE < d->vgic_dist_base)
|
||||
return false;
|
||||
|
||||
list_for_each_entry(rdreg, &d->rd_regions, list) {
|
||||
if (rdreg->base + vgic_v3_rd_region_size(kvm, rdreg) <
|
||||
rdreg->base)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IS_VGIC_ADDR_UNDEF(d->vgic_dist_base))
|
||||
return true;
|
||||
|
||||
return !vgic_v3_rdist_overlap(kvm, d->vgic_dist_base,
|
||||
KVM_VGIC_V3_DIST_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_v3_rdist_free_slot - Look up registered rdist regions and identify one
|
||||
* which has free space to put a new rdist region.
|
||||
*
|
||||
* @rd_regions: redistributor region list head
|
||||
*
|
||||
* A redistributor regions maps n redistributors, n = region size / (2 x 64kB).
|
||||
* Stride between redistributors is 0 and regions are filled in the index order.
|
||||
*
|
||||
* Return: the redist region handle, if any, that has space to map a new rdist
|
||||
* region.
|
||||
*/
|
||||
struct vgic_redist_region *vgic_v3_rdist_free_slot(struct list_head *rd_regions)
|
||||
{
|
||||
struct vgic_redist_region *rdreg;
|
||||
|
||||
list_for_each_entry(rdreg, rd_regions, list) {
|
||||
if (!vgic_v3_redist_region_full(rdreg))
|
||||
return rdreg;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct vgic_redist_region *vgic_v3_rdist_region_from_index(struct kvm *kvm,
|
||||
u32 index)
|
||||
{
|
||||
struct list_head *rd_regions = &kvm->arch.vgic.rd_regions;
|
||||
struct vgic_redist_region *rdreg;
|
||||
|
||||
list_for_each_entry(rdreg, rd_regions, list) {
|
||||
if (rdreg->index == index)
|
||||
return rdreg;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int vgic_v3_map_resources(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct kvm_vcpu *vcpu;
|
||||
int ret = 0;
|
||||
int c;
|
||||
|
||||
if (vgic_ready(kvm))
|
||||
goto out;
|
||||
|
||||
kvm_for_each_vcpu(c, vcpu, kvm) {
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
|
||||
if (IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr)) {
|
||||
kvm_debug("vcpu %d redistributor base not set\n", c);
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base)) {
|
||||
kvm_err("Need to set vgic distributor addresses first\n");
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!vgic_v3_check_base(kvm)) {
|
||||
kvm_err("VGIC redist and dist frames overlap\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* For a VGICv3 we require the userland to explicitly initialize
|
||||
* the VGIC before we need to use it.
|
||||
*/
|
||||
if (!vgic_initialized(kvm)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V3);
|
||||
if (ret) {
|
||||
kvm_err("Unable to register VGICv3 dist MMIO regions\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dist->ready = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_STATIC_KEY_FALSE(vgic_v3_cpuif_trap);
|
||||
|
||||
static int __init early_group0_trap_cfg(char *buf)
|
||||
{
|
||||
return strtobool(buf, &group0_trap);
|
||||
}
|
||||
early_param("kvm-arm.vgic_v3_group0_trap", early_group0_trap_cfg);
|
||||
|
||||
static int __init early_group1_trap_cfg(char *buf)
|
||||
{
|
||||
return strtobool(buf, &group1_trap);
|
||||
}
|
||||
early_param("kvm-arm.vgic_v3_group1_trap", early_group1_trap_cfg);
|
||||
|
||||
static int __init early_common_trap_cfg(char *buf)
|
||||
{
|
||||
return strtobool(buf, &common_trap);
|
||||
}
|
||||
early_param("kvm-arm.vgic_v3_common_trap", early_common_trap_cfg);
|
||||
|
||||
static int __init early_gicv4_enable(char *buf)
|
||||
{
|
||||
return strtobool(buf, &gicv4_enable);
|
||||
}
|
||||
early_param("kvm-arm.vgic_v4_enable", early_gicv4_enable);
|
||||
|
||||
/**
|
||||
* vgic_v3_probe - probe for a VGICv3 compatible interrupt controller
|
||||
* @info: pointer to the GIC description
|
||||
*
|
||||
* Returns 0 if the VGICv3 has been probed successfully, returns an error code
|
||||
* otherwise
|
||||
*/
|
||||
int vgic_v3_probe(const struct gic_kvm_info *info)
|
||||
{
|
||||
u32 ich_vtr_el2 = kvm_call_hyp_ret(__vgic_v3_get_ich_vtr_el2);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The ListRegs field is 5 bits, but there is a architectural
|
||||
* maximum of 16 list registers. Just ignore bit 4...
|
||||
*/
|
||||
kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1;
|
||||
kvm_vgic_global_state.can_emulate_gicv2 = false;
|
||||
kvm_vgic_global_state.ich_vtr_el2 = ich_vtr_el2;
|
||||
|
||||
/* GICv4 support? */
|
||||
if (info->has_v4) {
|
||||
kvm_vgic_global_state.has_gicv4 = gicv4_enable;
|
||||
kvm_info("GICv4 support %sabled\n",
|
||||
gicv4_enable ? "en" : "dis");
|
||||
}
|
||||
|
||||
if (!info->vcpu.start) {
|
||||
kvm_info("GICv3: no GICV resource entry\n");
|
||||
kvm_vgic_global_state.vcpu_base = 0;
|
||||
} else if (!PAGE_ALIGNED(info->vcpu.start)) {
|
||||
pr_warn("GICV physical address 0x%llx not page aligned\n",
|
||||
(unsigned long long)info->vcpu.start);
|
||||
kvm_vgic_global_state.vcpu_base = 0;
|
||||
} else {
|
||||
kvm_vgic_global_state.vcpu_base = info->vcpu.start;
|
||||
kvm_vgic_global_state.can_emulate_gicv2 = true;
|
||||
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register GICv2 KVM device.\n");
|
||||
return ret;
|
||||
}
|
||||
kvm_info("vgic-v2@%llx\n", info->vcpu.start);
|
||||
}
|
||||
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register GICv3 KVM device.\n");
|
||||
kvm_unregister_device_ops(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (kvm_vgic_global_state.vcpu_base == 0)
|
||||
kvm_info("disabling GICv2 emulation\n");
|
||||
|
||||
#ifdef CONFIG_ARM64
|
||||
if (cpus_have_const_cap(ARM64_WORKAROUND_CAVIUM_30115)) {
|
||||
group0_trap = true;
|
||||
group1_trap = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (group0_trap || group1_trap || common_trap) {
|
||||
kvm_info("GICv3 sysreg trapping enabled ([%s%s%s], reduced performance)\n",
|
||||
group0_trap ? "G0" : "",
|
||||
group1_trap ? "G1" : "",
|
||||
common_trap ? "C" : "");
|
||||
static_branch_enable(&vgic_v3_cpuif_trap);
|
||||
}
|
||||
|
||||
kvm_vgic_global_state.vctrl_base = NULL;
|
||||
kvm_vgic_global_state.type = VGIC_V3;
|
||||
kvm_vgic_global_state.max_gic_vcpus = VGIC_V3_MAX_CPUS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vgic_v3_load(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
/*
|
||||
* If dealing with a GICv2 emulation on GICv3, VMCR_EL2.VFIQen
|
||||
* is dependent on ICC_SRE_EL1.SRE, and we have to perform the
|
||||
* VMCR_EL2 save/restore in the world switch.
|
||||
*/
|
||||
if (likely(cpu_if->vgic_sre))
|
||||
kvm_call_hyp(__vgic_v3_write_vmcr, cpu_if->vgic_vmcr);
|
||||
|
||||
kvm_call_hyp(__vgic_v3_restore_aprs, vcpu);
|
||||
|
||||
if (has_vhe())
|
||||
__vgic_v3_activate_traps(vcpu);
|
||||
}
|
||||
|
||||
void vgic_v3_vmcr_sync(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
if (likely(cpu_if->vgic_sre))
|
||||
cpu_if->vgic_vmcr = kvm_call_hyp_ret(__vgic_v3_read_vmcr);
|
||||
}
|
||||
|
||||
void vgic_v3_put(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vgic_v3_vmcr_sync(vcpu);
|
||||
|
||||
kvm_call_hyp(__vgic_v3_save_aprs, vcpu);
|
||||
|
||||
if (has_vhe())
|
||||
__vgic_v3_deactivate_traps(vcpu);
|
||||
}
|
355
virt/kvm/arm/vgic/vgic-v4.c
Normal file
355
virt/kvm/arm/vgic/vgic-v4.c
Normal file
@ -0,0 +1,355 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2017 ARM Ltd.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
|
||||
#include "vgic.h"
|
||||
|
||||
/*
|
||||
* How KVM uses GICv4 (insert rude comments here):
|
||||
*
|
||||
* The vgic-v4 layer acts as a bridge between several entities:
|
||||
* - The GICv4 ITS representation offered by the ITS driver
|
||||
* - VFIO, which is in charge of the PCI endpoint
|
||||
* - The virtual ITS, which is the only thing the guest sees
|
||||
*
|
||||
* The configuration of VLPIs is triggered by a callback from VFIO,
|
||||
* instructing KVM that a PCI device has been configured to deliver
|
||||
* MSIs to a vITS.
|
||||
*
|
||||
* kvm_vgic_v4_set_forwarding() is thus called with the routing entry,
|
||||
* and this is used to find the corresponding vITS data structures
|
||||
* (ITS instance, device, event and irq) using a process that is
|
||||
* extremely similar to the injection of an MSI.
|
||||
*
|
||||
* At this stage, we can link the guest's view of an LPI (uniquely
|
||||
* identified by the routing entry) and the host irq, using the GICv4
|
||||
* driver mapping operation. Should the mapping succeed, we've then
|
||||
* successfully upgraded the guest's LPI to a VLPI. We can then start
|
||||
* with updating GICv4's view of the property table and generating an
|
||||
* INValidation in order to kickstart the delivery of this VLPI to the
|
||||
* guest directly, without software intervention. Well, almost.
|
||||
*
|
||||
* When the PCI endpoint is deconfigured, this operation is reversed
|
||||
* with VFIO calling kvm_vgic_v4_unset_forwarding().
|
||||
*
|
||||
* Once the VLPI has been mapped, it needs to follow any change the
|
||||
* guest performs on its LPI through the vITS. For that, a number of
|
||||
* command handlers have hooks to communicate these changes to the HW:
|
||||
* - Any invalidation triggers a call to its_prop_update_vlpi()
|
||||
* - The INT command results in a irq_set_irqchip_state(), which
|
||||
* generates an INT on the corresponding VLPI.
|
||||
* - The CLEAR command results in a irq_set_irqchip_state(), which
|
||||
* generates an CLEAR on the corresponding VLPI.
|
||||
* - DISCARD translates into an unmap, similar to a call to
|
||||
* kvm_vgic_v4_unset_forwarding().
|
||||
* - MOVI is translated by an update of the existing mapping, changing
|
||||
* the target vcpu, resulting in a VMOVI being generated.
|
||||
* - MOVALL is translated by a string of mapping updates (similar to
|
||||
* the handling of MOVI). MOVALL is horrible.
|
||||
*
|
||||
* Note that a DISCARD/MAPTI sequence emitted from the guest without
|
||||
* reprogramming the PCI endpoint after MAPTI does not result in a
|
||||
* VLPI being mapped, as there is no callback from VFIO (the guest
|
||||
* will get the interrupt via the normal SW injection). Fixing this is
|
||||
* not trivial, and requires some horrible messing with the VFIO
|
||||
* internals. Not fun. Don't do that.
|
||||
*
|
||||
* Then there is the scheduling. Each time a vcpu is about to run on a
|
||||
* physical CPU, KVM must tell the corresponding redistributor about
|
||||
* it. And if we've migrated our vcpu from one CPU to another, we must
|
||||
* tell the ITS (so that the messages reach the right redistributor).
|
||||
* This is done in two steps: first issue a irq_set_affinity() on the
|
||||
* irq corresponding to the vcpu, then call its_schedule_vpe(). You
|
||||
* must be in a non-preemptible context. On exit, another call to
|
||||
* its_schedule_vpe() tells the redistributor that we're done with the
|
||||
* vcpu.
|
||||
*
|
||||
* Finally, the doorbell handling: Each vcpu is allocated an interrupt
|
||||
* which will fire each time a VLPI is made pending whilst the vcpu is
|
||||
* not running. Each time the vcpu gets blocked, the doorbell
|
||||
* interrupt gets enabled. When the vcpu is unblocked (for whatever
|
||||
* reason), the doorbell interrupt is disabled.
|
||||
*/
|
||||
|
||||
#define DB_IRQ_FLAGS (IRQ_NOAUTOEN | IRQ_DISABLE_UNLAZY | IRQ_NO_BALANCING)
|
||||
|
||||
static irqreturn_t vgic_v4_doorbell_handler(int irq, void *info)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = info;
|
||||
|
||||
vcpu->arch.vgic_cpu.vgic_v3.its_vpe.pending_last = true;
|
||||
kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu);
|
||||
kvm_vcpu_kick(vcpu);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_v4_init - Initialize the GICv4 data structures
|
||||
* @kvm: Pointer to the VM being initialized
|
||||
*
|
||||
* We may be called each time a vITS is created, or when the
|
||||
* vgic is initialized. This relies on kvm->lock to be
|
||||
* held. In both cases, the number of vcpus should now be
|
||||
* fixed.
|
||||
*/
|
||||
int vgic_v4_init(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct kvm_vcpu *vcpu;
|
||||
int i, nr_vcpus, ret;
|
||||
|
||||
if (!kvm_vgic_global_state.has_gicv4)
|
||||
return 0; /* Nothing to see here... move along. */
|
||||
|
||||
if (dist->its_vm.vpes)
|
||||
return 0;
|
||||
|
||||
nr_vcpus = atomic_read(&kvm->online_vcpus);
|
||||
|
||||
dist->its_vm.vpes = kcalloc(nr_vcpus, sizeof(*dist->its_vm.vpes),
|
||||
GFP_KERNEL);
|
||||
if (!dist->its_vm.vpes)
|
||||
return -ENOMEM;
|
||||
|
||||
dist->its_vm.nr_vpes = nr_vcpus;
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm)
|
||||
dist->its_vm.vpes[i] = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe;
|
||||
|
||||
ret = its_alloc_vcpu_irqs(&dist->its_vm);
|
||||
if (ret < 0) {
|
||||
kvm_err("VPE IRQ allocation failure\n");
|
||||
kfree(dist->its_vm.vpes);
|
||||
dist->its_vm.nr_vpes = 0;
|
||||
dist->its_vm.vpes = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||
int irq = dist->its_vm.vpes[i]->irq;
|
||||
|
||||
/*
|
||||
* Don't automatically enable the doorbell, as we're
|
||||
* flipping it back and forth when the vcpu gets
|
||||
* blocked. Also disable the lazy disabling, as the
|
||||
* doorbell could kick us out of the guest too
|
||||
* early...
|
||||
*/
|
||||
irq_set_status_flags(irq, DB_IRQ_FLAGS);
|
||||
ret = request_irq(irq, vgic_v4_doorbell_handler,
|
||||
0, "vcpu", vcpu);
|
||||
if (ret) {
|
||||
kvm_err("failed to allocate vcpu IRQ%d\n", irq);
|
||||
/*
|
||||
* Trick: adjust the number of vpes so we know
|
||||
* how many to nuke on teardown...
|
||||
*/
|
||||
dist->its_vm.nr_vpes = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
vgic_v4_teardown(kvm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_v4_teardown - Free the GICv4 data structures
|
||||
* @kvm: Pointer to the VM being destroyed
|
||||
*
|
||||
* Relies on kvm->lock to be held.
|
||||
*/
|
||||
void vgic_v4_teardown(struct kvm *kvm)
|
||||
{
|
||||
struct its_vm *its_vm = &kvm->arch.vgic.its_vm;
|
||||
int i;
|
||||
|
||||
if (!its_vm->vpes)
|
||||
return;
|
||||
|
||||
for (i = 0; i < its_vm->nr_vpes; i++) {
|
||||
struct kvm_vcpu *vcpu = kvm_get_vcpu(kvm, i);
|
||||
int irq = its_vm->vpes[i]->irq;
|
||||
|
||||
irq_clear_status_flags(irq, DB_IRQ_FLAGS);
|
||||
free_irq(irq, vcpu);
|
||||
}
|
||||
|
||||
its_free_vcpu_irqs(its_vm);
|
||||
kfree(its_vm->vpes);
|
||||
its_vm->nr_vpes = 0;
|
||||
its_vm->vpes = NULL;
|
||||
}
|
||||
|
||||
int vgic_v4_sync_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!vgic_supports_direct_msis(vcpu->kvm))
|
||||
return 0;
|
||||
|
||||
return its_schedule_vpe(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe, false);
|
||||
}
|
||||
|
||||
int vgic_v4_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int irq = vcpu->arch.vgic_cpu.vgic_v3.its_vpe.irq;
|
||||
int err;
|
||||
|
||||
if (!vgic_supports_direct_msis(vcpu->kvm))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Before making the VPE resident, make sure the redistributor
|
||||
* corresponding to our current CPU expects us here. See the
|
||||
* doc in drivers/irqchip/irq-gic-v4.c to understand how this
|
||||
* turns into a VMOVP command at the ITS level.
|
||||
*/
|
||||
err = irq_set_affinity(irq, cpumask_of(smp_processor_id()));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = its_schedule_vpe(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Now that the VPE is resident, let's get rid of a potential
|
||||
* doorbell interrupt that would still be pending.
|
||||
*/
|
||||
err = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, false);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct vgic_its *vgic_get_its(struct kvm *kvm,
|
||||
struct kvm_kernel_irq_routing_entry *irq_entry)
|
||||
{
|
||||
struct kvm_msi msi = (struct kvm_msi) {
|
||||
.address_lo = irq_entry->msi.address_lo,
|
||||
.address_hi = irq_entry->msi.address_hi,
|
||||
.data = irq_entry->msi.data,
|
||||
.flags = irq_entry->msi.flags,
|
||||
.devid = irq_entry->msi.devid,
|
||||
};
|
||||
|
||||
return vgic_msi_to_its(kvm, &msi);
|
||||
}
|
||||
|
||||
int kvm_vgic_v4_set_forwarding(struct kvm *kvm, int virq,
|
||||
struct kvm_kernel_irq_routing_entry *irq_entry)
|
||||
{
|
||||
struct vgic_its *its;
|
||||
struct vgic_irq *irq;
|
||||
struct its_vlpi_map map;
|
||||
int ret;
|
||||
|
||||
if (!vgic_supports_direct_msis(kvm))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Get the ITS, and escape early on error (not a valid
|
||||
* doorbell for any of our vITSs).
|
||||
*/
|
||||
its = vgic_get_its(kvm, irq_entry);
|
||||
if (IS_ERR(its))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&its->its_lock);
|
||||
|
||||
/* Perform then actual DevID/EventID -> LPI translation. */
|
||||
ret = vgic_its_resolve_lpi(kvm, its, irq_entry->msi.devid,
|
||||
irq_entry->msi.data, &irq);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Emit the mapping request. If it fails, the ITS probably
|
||||
* isn't v4 compatible, so let's silently bail out. Holding
|
||||
* the ITS lock should ensure that nothing can modify the
|
||||
* target vcpu.
|
||||
*/
|
||||
map = (struct its_vlpi_map) {
|
||||
.vm = &kvm->arch.vgic.its_vm,
|
||||
.vpe = &irq->target_vcpu->arch.vgic_cpu.vgic_v3.its_vpe,
|
||||
.vintid = irq->intid,
|
||||
.properties = ((irq->priority & 0xfc) |
|
||||
(irq->enabled ? LPI_PROP_ENABLED : 0) |
|
||||
LPI_PROP_GROUP1),
|
||||
.db_enabled = true,
|
||||
};
|
||||
|
||||
ret = its_map_vlpi(virq, &map);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
irq->hw = true;
|
||||
irq->host_irq = virq;
|
||||
|
||||
out:
|
||||
mutex_unlock(&its->its_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_vgic_v4_unset_forwarding(struct kvm *kvm, int virq,
|
||||
struct kvm_kernel_irq_routing_entry *irq_entry)
|
||||
{
|
||||
struct vgic_its *its;
|
||||
struct vgic_irq *irq;
|
||||
int ret;
|
||||
|
||||
if (!vgic_supports_direct_msis(kvm))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Get the ITS, and escape early on error (not a valid
|
||||
* doorbell for any of our vITSs).
|
||||
*/
|
||||
its = vgic_get_its(kvm, irq_entry);
|
||||
if (IS_ERR(its))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&its->its_lock);
|
||||
|
||||
ret = vgic_its_resolve_lpi(kvm, its, irq_entry->msi.devid,
|
||||
irq_entry->msi.data, &irq);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
WARN_ON(!(irq->hw && irq->host_irq == virq));
|
||||
if (irq->hw) {
|
||||
irq->hw = false;
|
||||
ret = its_unmap_vlpi(virq);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&its->its_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvm_vgic_v4_enable_doorbell(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (vgic_supports_direct_msis(vcpu->kvm)) {
|
||||
int irq = vcpu->arch.vgic_cpu.vgic_v3.its_vpe.irq;
|
||||
if (irq)
|
||||
enable_irq(irq);
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_vgic_v4_disable_doorbell(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (vgic_supports_direct_msis(vcpu->kvm)) {
|
||||
int irq = vcpu->arch.vgic_cpu.vgic_v3.its_vpe.irq;
|
||||
if (irq)
|
||||
disable_irq(irq);
|
||||
}
|
||||
}
|
1015
virt/kvm/arm/vgic/vgic.c
Normal file
1015
virt/kvm/arm/vgic/vgic.c
Normal file
File diff suppressed because it is too large
Load Diff
322
virt/kvm/arm/vgic/vgic.h
Normal file
322
virt/kvm/arm/vgic/vgic.h
Normal file
@ -0,0 +1,322 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2015, 2016 ARM Ltd.
|
||||
*/
|
||||
#ifndef __KVM_ARM_VGIC_NEW_H__
|
||||
#define __KVM_ARM_VGIC_NEW_H__
|
||||
|
||||
#include <linux/irqchip/arm-gic-common.h>
|
||||
|
||||
#define PRODUCT_ID_KVM 0x4b /* ASCII code K */
|
||||
#define IMPLEMENTER_ARM 0x43b
|
||||
|
||||
#define VGIC_ADDR_UNDEF (-1)
|
||||
#define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF)
|
||||
|
||||
#define INTERRUPT_ID_BITS_SPIS 10
|
||||
#define INTERRUPT_ID_BITS_ITS 16
|
||||
#define VGIC_PRI_BITS 5
|
||||
|
||||
#define vgic_irq_is_sgi(intid) ((intid) < VGIC_NR_SGIS)
|
||||
|
||||
#define VGIC_AFFINITY_0_SHIFT 0
|
||||
#define VGIC_AFFINITY_0_MASK (0xffUL << VGIC_AFFINITY_0_SHIFT)
|
||||
#define VGIC_AFFINITY_1_SHIFT 8
|
||||
#define VGIC_AFFINITY_1_MASK (0xffUL << VGIC_AFFINITY_1_SHIFT)
|
||||
#define VGIC_AFFINITY_2_SHIFT 16
|
||||
#define VGIC_AFFINITY_2_MASK (0xffUL << VGIC_AFFINITY_2_SHIFT)
|
||||
#define VGIC_AFFINITY_3_SHIFT 24
|
||||
#define VGIC_AFFINITY_3_MASK (0xffUL << VGIC_AFFINITY_3_SHIFT)
|
||||
|
||||
#define VGIC_AFFINITY_LEVEL(reg, level) \
|
||||
((((reg) & VGIC_AFFINITY_## level ##_MASK) \
|
||||
>> VGIC_AFFINITY_## level ##_SHIFT) << MPIDR_LEVEL_SHIFT(level))
|
||||
|
||||
/*
|
||||
* The Userspace encodes the affinity differently from the MPIDR,
|
||||
* Below macro converts vgic userspace format to MPIDR reg format.
|
||||
*/
|
||||
#define VGIC_TO_MPIDR(val) (VGIC_AFFINITY_LEVEL(val, 0) | \
|
||||
VGIC_AFFINITY_LEVEL(val, 1) | \
|
||||
VGIC_AFFINITY_LEVEL(val, 2) | \
|
||||
VGIC_AFFINITY_LEVEL(val, 3))
|
||||
|
||||
/*
|
||||
* As per Documentation/virt/kvm/devices/arm-vgic-v3.txt,
|
||||
* below macros are defined for CPUREG encoding.
|
||||
*/
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_OP0_MASK 0x000000000000c000
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_OP0_SHIFT 14
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_OP1_MASK 0x0000000000003800
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_OP1_SHIFT 11
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_CRN_MASK 0x0000000000000780
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_CRN_SHIFT 7
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_CRM_MASK 0x0000000000000078
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_CRM_SHIFT 3
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_OP2_MASK 0x0000000000000007
|
||||
#define KVM_REG_ARM_VGIC_SYSREG_OP2_SHIFT 0
|
||||
|
||||
#define KVM_DEV_ARM_VGIC_SYSREG_MASK (KVM_REG_ARM_VGIC_SYSREG_OP0_MASK | \
|
||||
KVM_REG_ARM_VGIC_SYSREG_OP1_MASK | \
|
||||
KVM_REG_ARM_VGIC_SYSREG_CRN_MASK | \
|
||||
KVM_REG_ARM_VGIC_SYSREG_CRM_MASK | \
|
||||
KVM_REG_ARM_VGIC_SYSREG_OP2_MASK)
|
||||
|
||||
/*
|
||||
* As per Documentation/virt/kvm/devices/arm-vgic-its.txt,
|
||||
* below macros are defined for ITS table entry encoding.
|
||||
*/
|
||||
#define KVM_ITS_CTE_VALID_SHIFT 63
|
||||
#define KVM_ITS_CTE_VALID_MASK BIT_ULL(63)
|
||||
#define KVM_ITS_CTE_RDBASE_SHIFT 16
|
||||
#define KVM_ITS_CTE_ICID_MASK GENMASK_ULL(15, 0)
|
||||
#define KVM_ITS_ITE_NEXT_SHIFT 48
|
||||
#define KVM_ITS_ITE_PINTID_SHIFT 16
|
||||
#define KVM_ITS_ITE_PINTID_MASK GENMASK_ULL(47, 16)
|
||||
#define KVM_ITS_ITE_ICID_MASK GENMASK_ULL(15, 0)
|
||||
#define KVM_ITS_DTE_VALID_SHIFT 63
|
||||
#define KVM_ITS_DTE_VALID_MASK BIT_ULL(63)
|
||||
#define KVM_ITS_DTE_NEXT_SHIFT 49
|
||||
#define KVM_ITS_DTE_NEXT_MASK GENMASK_ULL(62, 49)
|
||||
#define KVM_ITS_DTE_ITTADDR_SHIFT 5
|
||||
#define KVM_ITS_DTE_ITTADDR_MASK GENMASK_ULL(48, 5)
|
||||
#define KVM_ITS_DTE_SIZE_MASK GENMASK_ULL(4, 0)
|
||||
#define KVM_ITS_L1E_VALID_MASK BIT_ULL(63)
|
||||
/* we only support 64 kB translation table page size */
|
||||
#define KVM_ITS_L1E_ADDR_MASK GENMASK_ULL(51, 16)
|
||||
|
||||
#define KVM_VGIC_V3_RDIST_INDEX_MASK GENMASK_ULL(11, 0)
|
||||
#define KVM_VGIC_V3_RDIST_FLAGS_MASK GENMASK_ULL(15, 12)
|
||||
#define KVM_VGIC_V3_RDIST_FLAGS_SHIFT 12
|
||||
#define KVM_VGIC_V3_RDIST_BASE_MASK GENMASK_ULL(51, 16)
|
||||
#define KVM_VGIC_V3_RDIST_COUNT_MASK GENMASK_ULL(63, 52)
|
||||
#define KVM_VGIC_V3_RDIST_COUNT_SHIFT 52
|
||||
|
||||
#ifdef CONFIG_DEBUG_SPINLOCK
|
||||
#define DEBUG_SPINLOCK_BUG_ON(p) BUG_ON(p)
|
||||
#else
|
||||
#define DEBUG_SPINLOCK_BUG_ON(p)
|
||||
#endif
|
||||
|
||||
/* Requires the irq_lock to be held by the caller. */
|
||||
static inline bool irq_is_pending(struct vgic_irq *irq)
|
||||
{
|
||||
if (irq->config == VGIC_CONFIG_EDGE)
|
||||
return irq->pending_latch;
|
||||
else
|
||||
return irq->pending_latch || irq->line_level;
|
||||
}
|
||||
|
||||
static inline bool vgic_irq_is_mapped_level(struct vgic_irq *irq)
|
||||
{
|
||||
return irq->config == VGIC_CONFIG_LEVEL && irq->hw;
|
||||
}
|
||||
|
||||
static inline int vgic_irq_get_lr_count(struct vgic_irq *irq)
|
||||
{
|
||||
/* Account for the active state as an interrupt */
|
||||
if (vgic_irq_is_sgi(irq->intid) && irq->source)
|
||||
return hweight8(irq->source) + irq->active;
|
||||
|
||||
return irq_is_pending(irq) || irq->active;
|
||||
}
|
||||
|
||||
static inline bool vgic_irq_is_multi_sgi(struct vgic_irq *irq)
|
||||
{
|
||||
return vgic_irq_get_lr_count(irq) > 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This struct provides an intermediate representation of the fields contained
|
||||
* in the GICH_VMCR and ICH_VMCR registers, such that code exporting the GIC
|
||||
* state to userspace can generate either GICv2 or GICv3 CPU interface
|
||||
* registers regardless of the hardware backed GIC used.
|
||||
*/
|
||||
struct vgic_vmcr {
|
||||
u32 grpen0;
|
||||
u32 grpen1;
|
||||
|
||||
u32 ackctl;
|
||||
u32 fiqen;
|
||||
u32 cbpr;
|
||||
u32 eoim;
|
||||
|
||||
u32 abpr;
|
||||
u32 bpr;
|
||||
u32 pmr; /* Priority mask field in the GICC_PMR and
|
||||
* ICC_PMR_EL1 priority field format */
|
||||
};
|
||||
|
||||
struct vgic_reg_attr {
|
||||
struct kvm_vcpu *vcpu;
|
||||
gpa_t addr;
|
||||
};
|
||||
|
||||
int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
|
||||
struct vgic_reg_attr *reg_attr);
|
||||
int vgic_v2_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
|
||||
struct vgic_reg_attr *reg_attr);
|
||||
const struct vgic_register_region *
|
||||
vgic_get_mmio_region(struct kvm_vcpu *vcpu, struct vgic_io_device *iodev,
|
||||
gpa_t addr, int len);
|
||||
struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
|
||||
u32 intid);
|
||||
void __vgic_put_lpi_locked(struct kvm *kvm, struct vgic_irq *irq);
|
||||
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
|
||||
bool vgic_get_phys_line_level(struct vgic_irq *irq);
|
||||
void vgic_irq_set_phys_pending(struct vgic_irq *irq, bool pending);
|
||||
void vgic_irq_set_phys_active(struct vgic_irq *irq, bool active);
|
||||
bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq,
|
||||
unsigned long flags);
|
||||
void vgic_kick_vcpus(struct kvm *kvm);
|
||||
|
||||
int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
|
||||
phys_addr_t addr, phys_addr_t alignment);
|
||||
|
||||
void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
|
||||
void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
|
||||
void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr);
|
||||
void vgic_v2_set_underflow(struct kvm_vcpu *vcpu);
|
||||
void vgic_v2_set_npie(struct kvm_vcpu *vcpu);
|
||||
int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr);
|
||||
int vgic_v2_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
int offset, u32 *val);
|
||||
int vgic_v2_cpuif_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
int offset, u32 *val);
|
||||
void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
void vgic_v2_enable(struct kvm_vcpu *vcpu);
|
||||
int vgic_v2_probe(const struct gic_kvm_info *info);
|
||||
int vgic_v2_map_resources(struct kvm *kvm);
|
||||
int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
|
||||
enum vgic_type);
|
||||
|
||||
void vgic_v2_init_lrs(void);
|
||||
void vgic_v2_load(struct kvm_vcpu *vcpu);
|
||||
void vgic_v2_put(struct kvm_vcpu *vcpu);
|
||||
void vgic_v2_vmcr_sync(struct kvm_vcpu *vcpu);
|
||||
|
||||
void vgic_v2_save_state(struct kvm_vcpu *vcpu);
|
||||
void vgic_v2_restore_state(struct kvm_vcpu *vcpu);
|
||||
|
||||
static inline void vgic_get_irq_kref(struct vgic_irq *irq)
|
||||
{
|
||||
if (irq->intid < VGIC_MIN_LPI)
|
||||
return;
|
||||
|
||||
kref_get(&irq->refcount);
|
||||
}
|
||||
|
||||
void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu);
|
||||
void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
|
||||
void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr);
|
||||
void vgic_v3_set_underflow(struct kvm_vcpu *vcpu);
|
||||
void vgic_v3_set_npie(struct kvm_vcpu *vcpu);
|
||||
void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
void vgic_v3_enable(struct kvm_vcpu *vcpu);
|
||||
int vgic_v3_probe(const struct gic_kvm_info *info);
|
||||
int vgic_v3_map_resources(struct kvm *kvm);
|
||||
int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq);
|
||||
int vgic_v3_save_pending_tables(struct kvm *kvm);
|
||||
int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count);
|
||||
int vgic_register_redist_iodev(struct kvm_vcpu *vcpu);
|
||||
bool vgic_v3_check_base(struct kvm *kvm);
|
||||
|
||||
void vgic_v3_load(struct kvm_vcpu *vcpu);
|
||||
void vgic_v3_put(struct kvm_vcpu *vcpu);
|
||||
void vgic_v3_vmcr_sync(struct kvm_vcpu *vcpu);
|
||||
|
||||
bool vgic_has_its(struct kvm *kvm);
|
||||
int kvm_vgic_register_its_device(void);
|
||||
void vgic_enable_lpis(struct kvm_vcpu *vcpu);
|
||||
void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu);
|
||||
int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi);
|
||||
int vgic_v3_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr);
|
||||
int vgic_v3_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
int offset, u32 *val);
|
||||
int vgic_v3_redist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
int offset, u32 *val);
|
||||
int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
u64 id, u64 *val);
|
||||
int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id,
|
||||
u64 *reg);
|
||||
int vgic_v3_line_level_info_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
u32 intid, u64 *val);
|
||||
int kvm_register_vgic_device(unsigned long type);
|
||||
void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
int vgic_lazy_init(struct kvm *kvm);
|
||||
int vgic_init(struct kvm *kvm);
|
||||
|
||||
void vgic_debug_init(struct kvm *kvm);
|
||||
void vgic_debug_destroy(struct kvm *kvm);
|
||||
|
||||
bool lock_all_vcpus(struct kvm *kvm);
|
||||
void unlock_all_vcpus(struct kvm *kvm);
|
||||
|
||||
static inline int vgic_v3_max_apr_idx(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_cpu *cpu_if = &vcpu->arch.vgic_cpu;
|
||||
|
||||
/*
|
||||
* num_pri_bits are initialized with HW supported values.
|
||||
* We can rely safely on num_pri_bits even if VM has not
|
||||
* restored ICC_CTLR_EL1 before restoring APnR registers.
|
||||
*/
|
||||
switch (cpu_if->num_pri_bits) {
|
||||
case 7: return 3;
|
||||
case 6: return 1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
vgic_v3_redist_region_full(struct vgic_redist_region *region)
|
||||
{
|
||||
if (!region->count)
|
||||
return false;
|
||||
|
||||
return (region->free_index >= region->count);
|
||||
}
|
||||
|
||||
struct vgic_redist_region *vgic_v3_rdist_free_slot(struct list_head *rdregs);
|
||||
|
||||
static inline size_t
|
||||
vgic_v3_rd_region_size(struct kvm *kvm, struct vgic_redist_region *rdreg)
|
||||
{
|
||||
if (!rdreg->count)
|
||||
return atomic_read(&kvm->online_vcpus) * KVM_VGIC_V3_REDIST_SIZE;
|
||||
else
|
||||
return rdreg->count * KVM_VGIC_V3_REDIST_SIZE;
|
||||
}
|
||||
|
||||
struct vgic_redist_region *vgic_v3_rdist_region_from_index(struct kvm *kvm,
|
||||
u32 index);
|
||||
|
||||
bool vgic_v3_rdist_overlap(struct kvm *kvm, gpa_t base, size_t size);
|
||||
|
||||
static inline bool vgic_dist_overlap(struct kvm *kvm, gpa_t base, size_t size)
|
||||
{
|
||||
struct vgic_dist *d = &kvm->arch.vgic;
|
||||
|
||||
return (base + size > d->vgic_dist_base) &&
|
||||
(base < d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE);
|
||||
}
|
||||
|
||||
int vgic_copy_lpi_list(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 **intid_ptr);
|
||||
int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its,
|
||||
u32 devid, u32 eventid, struct vgic_irq **irq);
|
||||
struct vgic_its *vgic_msi_to_its(struct kvm *kvm, struct kvm_msi *msi);
|
||||
int vgic_its_inject_cached_translation(struct kvm *kvm, struct kvm_msi *msi);
|
||||
void vgic_lpi_translation_cache_init(struct kvm *kvm);
|
||||
void vgic_lpi_translation_cache_destroy(struct kvm *kvm);
|
||||
void vgic_its_invalidate_cache(struct kvm *kvm);
|
||||
|
||||
bool vgic_supports_direct_msis(struct kvm *kvm);
|
||||
int vgic_v4_init(struct kvm *kvm);
|
||||
void vgic_v4_teardown(struct kvm *kvm);
|
||||
int vgic_v4_sync_hwstate(struct kvm_vcpu *vcpu);
|
||||
int vgic_v4_flush_hwstate(struct kvm_vcpu *vcpu);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user