blob: 1ec2728a490063c10a69f0e4100e7d7c8d978a19 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/bin/guest/vmm/arch/arm64/gic_distributor.h"
#include <endian.h>
#include <fcntl.h>
#include <fbl/unique_fd.h>
#include <fuchsia/sysinfo/c/fidl.h>
#include <lib/fdio/util.h>
#include <lib/zx/channel.h>
#include <libzbi/zbi.h>
#include <zircon/boot/driver-config.h>
#include "garnet/bin/guest/vmm/bits.h"
#include "garnet/bin/guest/vmm/guest.h"
#include "garnet/bin/guest/vmm/sysinfo.h"
#include "garnet/bin/guest/vmm/vcpu.h"
#include "lib/fxl/logging.h"
__BEGIN_CDECLS;
#include <libfdt.h>
__END_CDECLS;
static constexpr uint32_t kGicv2Revision = 2;
static constexpr uint32_t kGicv3Revision = 3;
static constexpr uint32_t kGicdCtlr = 0x7;
static constexpr uint32_t kGicdCtlrARENSMask = 1u << 5;
static constexpr uint32_t kGicdIrouteIRMMask = 1u << 31;
// clang-format off
// For arm64, memory addresses must be in a 36-bit range. This is due to limits
// placed within the MMU code based on the limits of a Cortex-A53.
//
// See ARM DDI 0487B.b, Table D4-25 for the maximum IPA range that can be used.
// GIC v2 distributor memory range.
static constexpr uint64_t kGicv2DistributorPhysBase = 0x800000000;
static constexpr uint64_t kGicv2DistributorSize = 0x1000;
// GIC v3 distributor memory range.
static constexpr uint64_t kGicv3DistributorPhysBase = 0x800000000;
static constexpr uint64_t kGicv3DistributorSize = 0x10000;
// GIC v3 Redistributor memory range.
//
// See GIC v3.0/v4.0 Architecture Spec 8.10.
static constexpr uint64_t kGicv3RedistributorPhysBase = 0x800010000; // GICR_RD_BASE
static constexpr uint64_t kGicv3RedistributorSize = 0x10000;
static constexpr uint64_t kGicv3RedistributorSgiPhysBase = 0x800020000; // GICR_SGI_BASE
static constexpr uint64_t kGicv3RedistributorSgiSize = 0x10000;
static constexpr uint64_t kGicv3RedistributorStride = 0x20000;
static_assert(kGicv3RedistributorPhysBase + kGicv3RedistributorSize == kGicv3RedistributorSgiPhysBase,
"GICv3 Redistributor base and SGI base must be continguous");
static_assert(kGicv3RedistributorStride >= kGicv3RedistributorSize + kGicv3RedistributorSgiSize,
"GICv3 Redistributor stride must be >= the size of a single mapping");
// GIC Distributor registers.
enum class GicdRegister : uint64_t {
CTL = 0x000,
TYPE = 0x004,
IGROUP0 = 0x080,
IGROUP31 = 0x0FC,
ISENABLE0 = 0x100,
ISENABLE1 = 0x104,
ISENABLE31 = 0x11c,
ICENABLE0 = 0x180,
ICENABLE1 = 0x184,
ICENABLE31 = 0x19c,
ICPEND0 = 0x280,
ICPEND31 = 0x2bc,
ICFG0 = 0xc00,
ICFG1 = 0xc04,
ICFG31 = 0xc7c,
ICACTIVE0 = 0x380,
ICACTIVE15 = 0x3bc,
IPRIORITY0 = 0x400,
IPRIORITY255 = 0x4fc,
ITARGETS0 = 0x800,
ITARGETS7 = 0x81c,
ITARGETS8 = 0x820,
ITARGETS63 = 0x8fc,
IGRPMOD0 = 0xd00,
IGRPMOD31 = 0xd7c,
SGI = 0xf00,
PID2_V2 = 0xfe8,
// This is the offset of PID2 register when are running GICv3,
// since the offset mappings of GICD & GICR are 0x1000 apart
PID2_V2_V3 = 0x1fe8,
PID2_V3 = 0xffe8,
IROUTE32 = 0x6100,
IROUTE1019 = 0x7fd8,
};
// GIC Redistributor registers.
enum class GicrRegister : uint64_t {
// Offset from RD_BASE
CTL = 0x000,
TYPE = 0x008,
WAKE = 0x014,
PID2_V3 = 0xffe8,
// Offset from SGI_BASE
IGROUP0 = 0x10080,
ISENABLE0 = 0x10100,
ICENABLE0 = 0x10180,
ICPEND0 = 0x10280,
ICACTIVE0 = 0x10380,
IPRIORITY0 = 0x10400,
IPRIORITY255 = 0x104fc,
ICFG0 = 0x10c00,
ICFG1 = 0x10c04,
};
// Target CPU for the software-generated interrupt.
enum class InterruptTarget {
MASK = 0b00,
ALL_BUT_LOCAL = 0b01,
LOCAL = 0b10,
};
// clang-format on
// Software-generated interrupt received by the GIC distributor.
struct SoftwareGeneratedInterrupt {
InterruptTarget target;
uint8_t cpu_mask;
uint8_t vector;
SoftwareGeneratedInterrupt(uint32_t sgi) {
target = static_cast<InterruptTarget>(bits_shift(sgi, 25, 24));
cpu_mask = static_cast<uint8_t>(bits_shift(sgi, 23, 16));
vector = static_cast<uint8_t>(bits_shift(sgi, 3, 0));
}
};
static size_t gicd_register_size(uint64_t addr) {
if (addr >= static_cast<uint64_t>(GicdRegister::IROUTE32) &&
addr <= static_cast<uint64_t>(GicdRegister::IROUTE1019)) {
return 8;
} else {
return 4;
}
}
static uint32_t typer(uint32_t num_interrupts, uint8_t num_cpus,
fuchsia::sysinfo::InterruptControllerType type) {
uint32_t typer = set_bits((num_interrupts >> 5) - 1, 4, 0);
typer |= set_bits(num_cpus - 1, 7, 5);
if (type == fuchsia::sysinfo::InterruptControllerType::GIC_V3) {
// Take log2 of num_interrupts
uint8_t num_bits =
(sizeof(num_interrupts) * CHAR_BIT) - __builtin_clz(num_interrupts - 1);
typer |= set_bits(num_bits - 1, 23, 19);
}
return typer;
}
static uint32_t pidr2_arch_rev(uint32_t revision) {
return set_bits(revision, 7, 4);
}
static zx_status_t get_interrupt_controller_info(
const fuchsia::sysinfo::DeviceSyncPtr& sysinfo,
fuchsia::sysinfo::InterruptControllerInfoPtr* info) {
zx_status_t fidl_status;
zx_status_t status = sysinfo->GetInterruptControllerInfo(&fidl_status, info);
if (status != ZX_OK) {
return status;
}
return fidl_status;
}
GicDistributor::GicDistributor(Guest* guest) : guest_(guest) {}
zx_status_t GicDistributor::Init(uint8_t num_cpus,
const std::vector<InterruptSpec>& interrupts) {
// Fetch the interrupt controller type.
fuchsia::sysinfo::DeviceSyncPtr sysinfo = get_sysinfo();
fuchsia::sysinfo::InterruptControllerInfoPtr info;
zx_status_t status = get_interrupt_controller_info(sysinfo, &info);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to get GIC version " << status;
return status;
}
if (info->type != fuchsia::sysinfo::InterruptControllerType::GIC_V2 &&
info->type != fuchsia::sysinfo::InterruptControllerType::GIC_V3) {
FXL_LOG(ERROR) << "Unsupported interrupt controller type "
<< static_cast<size_t>(info->type);
return ZX_ERR_NOT_SUPPORTED;
}
type_ = info->type;
// Create physical interrupts, so that we can bind them to VCPUs.
if (!interrupts.empty()) {
zx::resource resource;
zx_status_t status = get_root_resource(sysinfo, &resource);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to get root resource " << status;
return status;
}
for (const InterruptSpec& spec : interrupts) {
if (spec.vector < kSpiBase || spec.vector >= kNumInterrupts) {
FXL_LOG(ERROR) << "Invalid interrupt " << spec.vector;
return ZX_ERR_OUT_OF_RANGE;
}
zx::interrupt interrupt;
status = zx::interrupt::create(resource, spec.vector, spec.options,
&interrupt);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to create interrupt " << spec.vector
<< " with options " << spec.options << " " << status;
return status;
}
interrupts_.try_emplace(spec.vector, std::move(interrupt));
}
}
// Always allocate redistributors to use them for banked registers.
redistributors_.reserve(num_cpus);
for (uint8_t id = 0; id != num_cpus; id++) {
redistributors_.emplace_back(id, id == num_cpus - 1);
}
// Map the GICv2 distributor.
if (type_ == fuchsia::sysinfo::InterruptControllerType::GIC_V2) {
return guest_->CreateMapping(TrapType::MMIO_SYNC, kGicv2DistributorPhysBase,
kGicv2DistributorSize, 0, this);
}
// Map the GICv3 distributor.
status = guest_->CreateMapping(TrapType::MMIO_SYNC, kGicv3DistributorPhysBase,
kGicv3DistributorSize, 0, this);
if (status != ZX_OK) {
return status;
}
// Map the GICv3 redistributors, map both RD_BASE and SGI_BASE as one since
// they are contiguous. See GIC v3.0/v4.0 Architecture Spec 8.10.
for (uint8_t id = 0; id != num_cpus; id++) {
status = guest_->CreateMapping(
TrapType::MMIO_SYNC,
kGicv3RedistributorPhysBase + (id * kGicv3RedistributorStride),
kGicv3RedistributorSize + kGicv3RedistributorSgiSize, 0,
&redistributors_[id]);
if (status != ZX_OK) {
return status;
}
}
return status;
}
zx_status_t GicDistributor::Interrupt(uint32_t vector) {
uint8_t cpu_mask;
if (vector < kSpiBase || vector >= kNumInterrupts) {
return ZX_ERR_OUT_OF_RANGE;
} else {
std::lock_guard<std::mutex> lock(mutex_);
cpu_mask = cpu_masks_[vector - kSpiBase];
}
return TargetInterrupt(vector, cpu_mask);
}
zx_status_t GicDistributor::TargetInterrupt(uint32_t vector, uint8_t cpu_mask) {
if (vector >= kNumInterrupts) {
return ZX_ERR_OUT_OF_RANGE;
} else if (vector >= kSpiBase) {
std::lock_guard<std::mutex> lock(mutex_);
uint32_t spi = vector - kSpiBase;
bool is_enabled = enabled_[spi / CHAR_BIT] & (1u << (spi % CHAR_BIT));
if (!is_enabled) {
return ZX_OK;
}
} else {
std::lock_guard<std::mutex> lock(mutex_);
for (size_t i = 0; i < redistributors_.size(); i++) {
if (!redistributors_[i].IsEnabled(vector)) {
cpu_mask &= ~(1u << i);
}
}
}
return guest_->Interrupt(cpu_mask, vector);
}
zx_status_t GicDistributor::BindVcpus(uint32_t vector, uint8_t cpu_mask) {
auto it = interrupts_.find(vector);
if (it == interrupts_.end()) {
return ZX_OK;
}
const Guest::VcpuArray& vcpus = guest_->vcpus();
for (size_t i = 0; i < vcpus.size(); i++, cpu_mask >>= 1) {
if (!(cpu_mask & 1)) {
continue;
}
zx_status_t status = it->second.bind_vcpu(vcpus[i]->object(), 0);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to bind VCPU " << status;
return status;
}
}
return ZX_OK;
}
zx_status_t GicDistributor::Read(uint64_t addr, IoValue* value) const {
if (addr % 4 != 0 || value->access_size != gicd_register_size(addr)) {
return ZX_ERR_IO_DATA_INTEGRITY;
}
switch (static_cast<GicdRegister>(addr)) {
case GicdRegister::TYPE: {
std::lock_guard<std::mutex> lock(mutex_);
value->u32 = typer(kNumInterrupts, redistributors_.size(), type_);
return ZX_OK;
}
case GicdRegister::ICFG0:
// SGIs are RAO/WI.
value->u32 = UINT32_MAX;
return ZX_OK;
case GicdRegister::ICFG1... GicdRegister::ICFG31:
value->u32 = 0;
return ZX_OK;
case GicdRegister::ISENABLE0: {
uint64_t id = Vcpu::GetCurrent()->id();
std::lock_guard<std::mutex> lock(mutex_);
return redistributors_[id].Read(
static_cast<uint64_t>(GicrRegister::ISENABLE0), value);
}
case GicdRegister::ISENABLE1... GicdRegister::ISENABLE31: {
std::lock_guard<std::mutex> lock(mutex_);
const uint8_t* enable =
&enabled_[addr - static_cast<uint64_t>(GicdRegister::ISENABLE1)];
value->u32 = *reinterpret_cast<const uint32_t*>(enable);
return ZX_OK;
}
case GicdRegister::ITARGETS0... GicdRegister::ITARGETS7: {
// GIC Architecture Spec 4.3.12: Each field of ITARGETS0 to ITARGETS7
// returns a mask that corresponds only to the current processor.
uint8_t mask = 1u << Vcpu::GetCurrent()->id();
value->u32 = mask | mask << 8 | mask << 16 | mask << 24;
return ZX_OK;
}
case GicdRegister::ITARGETS8... GicdRegister::ITARGETS63: {
std::lock_guard<std::mutex> lock(mutex_);
if (affinity_routing_) {
value->u32 = 0;
return ZX_OK;
}
const uint8_t* masks =
&cpu_masks_[addr - static_cast<uint64_t>(GicdRegister::ITARGETS8)];
// Target registers are read from 4 at a time.
value->u32 = *reinterpret_cast<const uint32_t*>(masks);
return ZX_OK;
}
case GicdRegister::IROUTE32... GicdRegister::IROUTE1019: {
std::lock_guard<std::mutex> lock(mutex_);
if (!affinity_routing_) {
value->u32 = 0;
return ZX_OK;
}
uint32_t vector = (addr - static_cast<uint64_t>(GicdRegister::IROUTE32)) /
value->access_size;
value->u64 = cpu_masks_[vector - kSpiBase];
if (value->u64 == UINT8_MAX) {
value->u64 |= kGicdIrouteIRMMask;
}
return ZX_OK;
}
case GicdRegister::PID2_V2_V3:
value->u32 = pidr2_arch_rev(kGicv3Revision);
return ZX_OK;
case GicdRegister::PID2_V2:
value->u32 = pidr2_arch_rev(kGicv2Revision);
return ZX_OK;
case GicdRegister::PID2_V3:
value->u32 = pidr2_arch_rev(kGicv3Revision);
return ZX_OK;
case GicdRegister::CTL: {
std::lock_guard<std::mutex> lock(mutex_);
value->u32 = kGicdCtlr;
if (type_ == fuchsia::sysinfo::InterruptControllerType::GIC_V3 &&
affinity_routing_) {
value->u32 |= kGicdCtlrARENSMask;
}
return ZX_OK;
}
default:
FXL_LOG(ERROR) << "Unhandled GIC distributor address read 0x" << std::hex
<< addr;
return ZX_ERR_NOT_SUPPORTED;
}
}
zx_status_t GicDistributor::Write(uint64_t addr, const IoValue& value) {
if (addr % 4 != 0 || value.access_size != gicd_register_size(addr)) {
return ZX_ERR_IO_DATA_INTEGRITY;
}
switch (static_cast<GicdRegister>(addr)) {
case GicdRegister::ITARGETS0... GicdRegister::ITARGETS7: {
// GIC Architecture Spec 4.3.12: ITARGETS0 to ITARGETS7 are read only.
FXL_LOG(ERROR) << "Write to read-only GIC distributor address 0x"
<< std::hex << addr;
return ZX_ERR_INVALID_ARGS;
}
case GicdRegister::ITARGETS8... GicdRegister::ITARGETS63: {
std::lock_guard<std::mutex> lock(mutex_);
if (affinity_routing_) {
return ZX_OK;
}
uint32_t spi = addr - static_cast<uint64_t>(GicdRegister::ITARGETS8);
uint8_t* masks = &cpu_masks_[spi];
*reinterpret_cast<uint32_t*>(masks) = value.u32;
for (uint32_t i = 0; i < 4; i++) {
uint8_t cpu_mask = masks[i];
if (cpu_mask == 0) {
continue;
}
zx_status_t status = BindVcpus(kSpiBase + spi + i, cpu_mask);
if (status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
case GicdRegister::SGI: {
SoftwareGeneratedInterrupt sgi(value.u32);
uint8_t cpu_mask;
switch (sgi.target) {
case InterruptTarget::MASK:
cpu_mask = sgi.cpu_mask;
break;
case InterruptTarget::ALL_BUT_LOCAL:
cpu_mask = ~(1u << Vcpu::GetCurrent()->id());
break;
case InterruptTarget::LOCAL:
cpu_mask = 1u << Vcpu::GetCurrent()->id();
break;
default:
return ZX_ERR_NOT_SUPPORTED;
}
return TargetInterrupt(sgi.vector, cpu_mask);
}
case GicdRegister::ISENABLE0: {
uint64_t id = Vcpu::GetCurrent()->id();
std::lock_guard<std::mutex> lock(mutex_);
return redistributors_[id].Write(
static_cast<uint64_t>(GicrRegister::ISENABLE0), value);
}
case GicdRegister::ISENABLE1... GicdRegister::ISENABLE31: {
std::lock_guard<std::mutex> lock(mutex_);
uint8_t* enable =
&enabled_[addr - static_cast<uint64_t>(GicdRegister::ISENABLE1)];
*reinterpret_cast<uint32_t*>(enable) |= value.u32;
return ZX_OK;
}
case GicdRegister::ICENABLE0: {
uint64_t id = Vcpu::GetCurrent()->id();
std::lock_guard<std::mutex> lock(mutex_);
return redistributors_[id].Write(
static_cast<uint64_t>(GicrRegister::ICENABLE0), value);
}
case GicdRegister::ICENABLE1... GicdRegister::ICENABLE31: {
std::lock_guard<std::mutex> lock(mutex_);
uint8_t* enable =
&enabled_[addr - static_cast<uint64_t>(GicdRegister::ICENABLE1)];
*reinterpret_cast<uint32_t*>(enable) &= ~value.u32;
return ZX_OK;
}
case GicdRegister::CTL: {
std::lock_guard<std::mutex> lock(mutex_);
affinity_routing_ =
type_ == fuchsia::sysinfo::InterruptControllerType::GIC_V3 &&
(value.u32 & kGicdCtlrARENSMask);
memset(cpu_masks_, UINT8_MAX, sizeof(cpu_masks_));
return ZX_OK;
}
case GicdRegister::IROUTE32... GicdRegister::IROUTE1019: {
std::lock_guard<std::mutex> lock(mutex_);
if (!affinity_routing_) {
return ZX_OK;
}
uint32_t vector = (addr - static_cast<uint64_t>(GicdRegister::IROUTE32)) /
value.access_size;
uint8_t cpu_mask = UINT8_MAX;
if (!(value.u64 & kGicdIrouteIRMMask)) {
cpu_mask &= value.u64;
}
cpu_masks_[vector - kSpiBase] = cpu_mask;
return BindVcpus(vector, cpu_mask);
}
case GicdRegister::ICACTIVE0... GicdRegister::ICACTIVE15:
case GicdRegister::ICFG0... GicdRegister::ICFG31:
case GicdRegister::ICPEND0... GicdRegister::ICPEND31:
case GicdRegister::IPRIORITY0... GicdRegister::IPRIORITY255:
case GicdRegister::IGROUP0... GicdRegister::IGROUP31:
case GicdRegister::IGRPMOD0... GicdRegister::IGRPMOD31:
return ZX_OK;
default:
FXL_LOG(ERROR) << "Unhandled GIC distributor address write 0x" << std::hex
<< addr;
return ZX_ERR_NOT_SUPPORTED;
}
}
zx_status_t GicDistributor::ConfigureZbi(void* zbi_base, size_t zbi_max) const {
const dcfg_arm_gicv2_driver_t gic_v2 = {
.mmio_phys = kGicv2DistributorPhysBase,
.gicd_offset = 0x0000,
.gicc_offset = kGicv2DistributorSize,
.ipi_base = 12,
.optional = true,
.use_msi = true,
};
const dcfg_arm_gicv3_driver_t gic_v3 = {
.mmio_phys = kGicv3DistributorPhysBase,
.gicd_offset = 0x00000,
.gicr_offset = kGicv3RedistributorSize,
.gicr_stride = kGicv3RedistributorStride,
.ipi_base = 12,
.optional = true,
};
zbi_result_t res;
if (type_ == fuchsia::sysinfo::InterruptControllerType::GIC_V2) {
res =
zbi_append_section(zbi_base, zbi_max, sizeof(gic_v2),
ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V2, 0, &gic_v2);
} else {
// GICv3 driver.
res =
zbi_append_section(zbi_base, zbi_max, sizeof(gic_v3),
ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V3, 0, &gic_v3);
}
return res == ZBI_RESULT_OK ? ZX_OK : ZX_ERR_INTERNAL;
}
static inline void gic_dtb_error(const char* reg) {
FXL_LOG(ERROR) << "Failed to add GiC property \"" << reg << "\" to device "
<< "tree, space must be reserved in the device tree";
}
zx_status_t GicDistributor::ConfigureDtb(void* dtb) const {
int gic_off = fdt_path_offset(dtb, "/interrupt-controller");
if (gic_off < 0) {
FXL_LOG(ERROR) << "Failed to find \"/interrupt-controller\" in device tree";
return ZX_ERR_BAD_STATE;
}
const char* compatible;
uint64_t reg_prop[4];
if (type_ == fuchsia::sysinfo::InterruptControllerType::GIC_V2) {
compatible = "arm,gic-400";
// GICD memory map
reg_prop[0] = kGicv2DistributorPhysBase;
reg_prop[1] = kGicv2DistributorSize;
// GICC memory map
reg_prop[2] = kGicv2DistributorPhysBase + kGicv2DistributorSize;
reg_prop[3] = 0x2000;
} else {
// Set V3 only properties
int ret = fdt_setprop_u32(dtb, gic_off, "#redistributor-regions", 1);
if (ret != 0) {
gic_dtb_error("#redistributor-regions");
return ZX_ERR_BAD_STATE;
}
compatible = "arm,gic-v3";
// GICD memory map
reg_prop[0] = kGicv3DistributorPhysBase;
reg_prop[1] = kGicv3DistributorSize;
// GICR memory map
reg_prop[2] = kGicv3RedistributorPhysBase;
std::lock_guard<std::mutex> lock(mutex_);
reg_prop[3] = kGicv3RedistributorStride * redistributors_.size();
}
int ret = fdt_setprop_string(dtb, gic_off, "compatible", compatible);
if (ret != 0) {
gic_dtb_error("compatible");
return ZX_ERR_BAD_STATE;
}
for (auto& prop : reg_prop) {
prop = htobe64(prop);
}
ret = fdt_setprop(dtb, gic_off, "reg", reg_prop, sizeof(reg_prop));
if (ret != 0) {
gic_dtb_error("reg");
return ZX_ERR_BAD_STATE;
}
return ZX_OK;
}
zx_status_t GicRedistributor::Read(uint64_t addr, IoValue* value) const {
if (addr % 4 != 0) {
return ZX_ERR_IO_DATA_INTEGRITY;
}
switch (static_cast<GicrRegister>(addr)) {
case GicrRegister::ISENABLE0:
value->u32 = enabled_;
return ZX_OK;
case GicrRegister::CTL:
case GicrRegister::WAKE:
case GicrRegister::ICFG0:
case GicrRegister::ICFG1:
if (value->access_size != 4) {
return ZX_ERR_IO_DATA_INTEGRITY;
}
value->u32 = 0;
return ZX_OK;
case GicrRegister::TYPE:
if (value->access_size != 8) {
return ZX_ERR_IO_DATA_INTEGRITY;
}
// Set both Processor_Number and Affinity_Value to id_.
value->u64 = set_bits(static_cast<uint64_t>(id_), 23, 8) |
set_bits(static_cast<uint64_t>(id_), 39, 32);
if (last_) {
value->u64 |= 1u << 4;
}
return ZX_OK;
case GicrRegister::PID2_V3:
if (value->access_size != 4) {
return ZX_ERR_IO_DATA_INTEGRITY;
}
value->u32 = pidr2_arch_rev(kGicv3Revision);
return ZX_OK;
default:
FXL_LOG(ERROR) << "Unhandled GIC redistributor address read 0x"
<< std::hex << addr;
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t GicRedistributor::Write(uint64_t addr, const IoValue& value) {
if (addr % 4 != 0 || value.access_size != 4) {
return ZX_ERR_IO_DATA_INTEGRITY;
}
switch (static_cast<GicrRegister>(addr)) {
case GicrRegister::ISENABLE0:
enabled_ |= value.u32;
return ZX_OK;
case GicrRegister::ICENABLE0:
enabled_ &= ~value.u32;
return ZX_OK;
case GicrRegister::WAKE:
case GicrRegister::IGROUP0:
case GicrRegister::ICPEND0:
case GicrRegister::ICACTIVE0:
case GicrRegister::IPRIORITY0... GicrRegister::IPRIORITY255:
case GicrRegister::ICFG0:
case GicrRegister::ICFG1:
return ZX_OK;
default:
FXL_LOG(ERROR) << "Unhandled GIC redistributor address write 0x"
<< std::hex << addr;
return ZX_ERR_NOT_SUPPORTED;
}
}
bool GicRedistributor::IsEnabled(uint32_t vector) const {
return enabled_ & (1u << vector);
}