blob: da99cce147cb4ab331b6fd7e9179a7b1d3f65bf0 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <arch/arm64/hypervisor/el2_state.h>
#include <arch/arm64/hypervisor/gic/el2.h>
#include <arch/arm64/hypervisor/gic/gicv3.h>
#include <arch/ops.h>
#include <dev/interrupt/arm_gic_hw_interface.h>
#include <dev/interrupt/arm_gicv3_regs.h>
#include <vm/physmap.h>
static constexpr uint32_t kNumAprs = 4;
static constexpr uint32_t kNumLrs = 16;
static zx_status_t gicv3_get_gicv(paddr_t* gicv_paddr) {
// Check for presence of GICv3 virtualisation extensions.
// We return ZX_ERR_NOT_FOUND since this API is used to get
// address of GICV base to map it to guest
// On GICv3 we do not need to map this region, since we use system registers
return ZX_ERR_NOT_FOUND;
}
static uint32_t gicv3_default_gich_vmcr() {
// From ARM GIC v3/v4, Section 8.4.8: VFIQEn - In implementations where the
// Non-secure copy of ICC_SRE_EL1.SRE is always 1, this bit is RES 1.
return ICH_VMCR_VPMR | ICH_VMCR_VFIQEN | ICH_VMCR_VENG1;
}
static void giv3_read_gich_state(IchState* state) {
DEBUG_ASSERT(state->num_aprs <= kNumAprs);
DEBUG_ASSERT(state->num_lrs <= kNumLrs);
arm64_el2_gicv3_read_gich_state(physmap_to_paddr(state));
}
static void giv3_write_gich_state(IchState* state, uint32_t hcr) {
DEBUG_ASSERT(state->num_aprs <= kNumAprs);
DEBUG_ASSERT(state->num_lrs <= kNumLrs);
cpu_num_t cpu_num = arch_curr_cpu_num();
for (uint8_t i = 0; i < state->num_lrs; i++) {
uint64_t lr = state->lr[i];
if (lr & ICH_LR_HARDWARE) {
// We are adding a physical interrupt to a list register, therefore we
// mark the physical interrupt as active on the physical distributor so
// that the guest can deactivate it directly.
uint32_t vector = ICH_LR_VIRTUAL_ID(lr);
uint32_t reg = vector / 32;
uint32_t mask = 1u << (vector % 32);
// Since we use affinity routing, if this vector is associated with an
// SGI or PPI, we should talk to the redistributor for the current CPU.
if (vector < 32) {
GICREG(0, GICR_ISACTIVER0(cpu_num)) = mask;
} else {
GICREG(0, GICD_ISACTIVER(reg)) = mask;
}
}
}
arm64_el2_gicv3_write_gich_state(physmap_to_paddr(state), hcr);
}
static uint64_t gicv3_get_lr_from_vector(bool hw, uint8_t prio, uint32_t vector) {
uint64_t lr = ICH_LR_PENDING | ICH_LR_GROUP1 | ICH_LR_PRIORITY(prio) |
ICH_LR_VIRTUAL_ID(vector);
if (hw) {
lr |= ICH_LR_HARDWARE | ICH_LR_PHYSICAL_ID(vector);
}
return lr;
}
static uint32_t gicv3_get_vector_from_lr(uint64_t lr) {
return lr & ICH_LR_VIRTUAL_ID(UINT64_MAX);
}
static uint8_t gicv3_get_num_pres() {
return static_cast<uint8_t>(ICH_VTR_PRES(arm64_el2_gicv3_read_gich_vtr()));
}
static uint8_t gicv3_get_num_lrs() {
return static_cast<uint8_t>(ICH_VTR_LRS(arm64_el2_gicv3_read_gich_vtr()));
}
static const struct arm_gic_hw_interface_ops gic_hw_register_ops = {
.get_gicv = gicv3_get_gicv,
.read_gich_state = giv3_read_gich_state,
.write_gich_state = giv3_write_gich_state,
.default_gich_vmcr = gicv3_default_gich_vmcr,
.get_lr_from_vector = gicv3_get_lr_from_vector,
.get_vector_from_lr = gicv3_get_vector_from_lr,
.get_num_pres = gicv3_get_num_pres,
.get_num_lrs = gicv3_get_num_lrs,
};
void gicv3_hw_interface_register() {
arm_gic_hw_interface_register(&gic_hw_register_ops);
}