blob: 46bb0b3c3d966af699e66fade3dc7450988d8192 [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/gicv2.h>
#include <assert.h>
#include <bits.h>
#include <dev/interrupt/arm_gic_hw_interface.h>
#include <dev/interrupt/arm_gicv2_regs.h>
#include <vm/pmm.h>
static constexpr uint32_t kNumLrs = 64;
// Check that InterruptState corresponds exactly to the LR state bits.
static_assert(static_cast<InterruptState>(BITS_SHIFT(0, GICH_LR_ACTIVE_BIT, GICH_LR_PENDING_BIT)) ==
InterruptState::INACTIVE);
static_assert(static_cast<InterruptState>(BITS_SHIFT(1 << GICH_LR_PENDING_BIT, GICH_LR_ACTIVE_BIT,
GICH_LR_PENDING_BIT)) ==
InterruptState::PENDING);
static_assert(static_cast<InterruptState>(BITS_SHIFT(1 << GICH_LR_ACTIVE_BIT, GICH_LR_ACTIVE_BIT,
GICH_LR_PENDING_BIT)) ==
InterruptState::ACTIVE);
static_assert(static_cast<InterruptState>(BITS_SHIFT(1 << GICH_LR_PENDING_BIT |
1 << GICH_LR_ACTIVE_BIT,
GICH_LR_ACTIVE_BIT, GICH_LR_PENDING_BIT)) ==
InterruptState::PENDING_AND_ACTIVE);
// Representation of GICH registers. For details please refer to ARM Generic Interrupt
// Controller Architecture Specification Version 2, 5.3 GIC virtual interface control
// registers.
typedef struct Gich {
uint32_t hcr;
uint32_t vtr;
uint32_t vmcr;
uint32_t reserved0;
uint32_t misr;
uint32_t reserved1[3];
uint32_t eisr0;
uint32_t eisr1;
uint32_t reserved2[2];
uint32_t elrsr0;
uint32_t elrsr1;
uint32_t reserved3[46];
uint32_t apr;
uint32_t reserved4[3];
uint32_t lr[kNumLrs];
} __attribute__((__packed__)) Gich;
static_assert(__offsetof(Gich, hcr) == 0x00, "");
static_assert(__offsetof(Gich, vtr) == 0x04, "");
static_assert(__offsetof(Gich, vmcr) == 0x08, "");
static_assert(__offsetof(Gich, misr) == 0x10, "");
static_assert(__offsetof(Gich, eisr0) == 0x20, "");
static_assert(__offsetof(Gich, eisr1) == 0x24, "");
static_assert(__offsetof(Gich, elrsr0) == 0x30, "");
static_assert(__offsetof(Gich, elrsr1) == 0x34, "");
static_assert(__offsetof(Gich, apr) == 0xf0, "");
static_assert(__offsetof(Gich, lr) == 0x100, "");
static volatile Gich* gich = NULL;
static zx_status_t gicv2_get_gicv(paddr_t* gicv_paddr) {
// Check for presence of GICv2 virtualisation extensions.
if (GICV_OFFSET == 0) {
return ZX_ERR_NOT_SUPPORTED;
}
*gicv_paddr = vaddr_to_paddr(reinterpret_cast<void*>(GICV_ADDRESS));
return ZX_OK;
}
static void giv2_read_gich_state(IchState* state) {
DEBUG_ASSERT(state->num_aprs == 1);
DEBUG_ASSERT(state->num_lrs <= kNumLrs);
gich->hcr = 0;
state->vmcr = gich->vmcr;
state->misr = gich->misr;
state->elrsr = gich->elrsr0 | (static_cast<uint64_t>(gich->elrsr1) << 32);
state->apr[0][0] = gich->apr;
for (uint8_t i = 0; i < state->num_lrs; i++) {
state->lr[i] = gich->lr[i];
}
}
static void giv2_write_gich_state(IchState* state, uint32_t hcr) {
DEBUG_ASSERT(state->num_aprs == 1);
DEBUG_ASSERT(state->num_lrs <= kNumLrs);
gich->hcr = hcr;
gich->vmcr = state->vmcr;
gich->apr = static_cast<uint32_t>(state->apr[0][0]);
for (uint8_t i = 0; i < state->num_lrs; i++) {
uint32_t lr = static_cast<uint32_t>(state->lr[i]);
if (lr & GICH_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 = GICH_LR_VIRTUAL_ID(lr);
uint32_t reg = vector / 32;
uint32_t mask = 1u << (vector % 32);
GICREG(0, GICD_ISACTIVER(reg)) = mask;
}
gich->lr[i] = lr;
}
}
static uint32_t gicv2_default_gich_vmcr() {
return GICH_VMCR_VPMR | GICH_VMCR_VENG0;
}
static uint64_t gicv2_get_lr_from_vector(bool hw, uint8_t prio, InterruptState state,
uint32_t vector) {
uint64_t lr = (static_cast<uint64_t>(state) << GICH_LR_PENDING_BIT) | GICH_LR_PRIORITY(prio) |
GICH_LR_VIRTUAL_ID(vector);
if (hw) {
lr |= GICH_LR_HARDWARE | GICH_LR_PHYSICAL_ID(vector);
}
return lr;
}
static uint32_t gicv2_get_vector_from_lr(uint64_t lr, InterruptState* state) {
*state = static_cast<InterruptState>(BITS_SHIFT(lr, GICH_LR_ACTIVE_BIT, GICH_LR_PENDING_BIT));
return lr & GICH_LR_VIRTUAL_ID(UINT64_MAX);
}
static uint8_t gicv2_get_num_pres() {
return static_cast<uint8_t>(GICH_VTR_PRES(gich->vtr));
}
static uint8_t gicv2_get_num_lrs() {
return static_cast<uint8_t>(GICH_VTR_LRS(gich->vtr));
}
static const struct arm_gic_hw_interface_ops gic_hw_register_ops = {
.get_gicv = gicv2_get_gicv,
.read_gich_state = giv2_read_gich_state,
.write_gich_state = giv2_write_gich_state,
.default_gich_vmcr = gicv2_default_gich_vmcr,
.get_lr_from_vector = gicv2_get_lr_from_vector,
.get_vector_from_lr = gicv2_get_vector_from_lr,
.get_num_pres = gicv2_get_num_pres,
.get_num_lrs = gicv2_get_num_lrs,
};
void gicv2_hw_interface_register() {
// Populate GICH
gich = reinterpret_cast<volatile Gich*>(GICH_ADDRESS);
arm_gic_hw_interface_register(&gic_hw_register_ops);
}