From patchwork Thu Sep 26 21:03:06 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoffer Dall X-Patchwork-Id: 20654 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ve0-f199.google.com (mail-ve0-f199.google.com [209.85.128.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 8CD7C23A4E for ; Thu, 26 Sep 2013 21:03:52 +0000 (UTC) Received: by mail-ve0-f199.google.com with SMTP id db12sf1607768veb.2 for ; Thu, 26 Sep 2013 14:03:52 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=lunjp/4IR5fS4FSI6wr9n5AQ6HDU+rO6L6L48TwmIos=; b=LhPbjF5+HD3nGu8FJcCHl+Or+j4Hd9fd+A1JE/+vExgD3rPBrBkuDkiUzRN3X6cIf5 r+dnvyRiPu64km1G9KMVanT5BNZXHrl3DVnIxTJil1xHlljnU2zWQ8Qko+qsLHcVjtpU Lx4tYGDjPm9j18WzoLXXND1XBr+mHS9dMi0u17NR24YyShj/baNwKi/lJc3CRt2DAC/1 pDOFEVteHmBFQj5mnWuSe2rpEkIsfVx5DJCoN4aGN3kYsq3j/dXKD5H7K/F+XpCaz20K OeHcGihzzG900/d9vPfH1VP80Cl/vf3ev3lodHwz3/PxxYIszgSn/kS1eOzYMH/IzLqt hKiQ== X-Gm-Message-State: ALoCoQk47vHBJy464V6/Ox7264Q7Mqr4Ogh3FIlrtbW74C72e7HTjFJOdletET5yUGnS3gbS+8TB X-Received: by 10.236.92.107 with SMTP id i71mr624906yhf.1.1380229432403; Thu, 26 Sep 2013 14:03:52 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.49.99.106 with SMTP id ep10ls1069427qeb.59.gmail; Thu, 26 Sep 2013 14:03:52 -0700 (PDT) X-Received: by 10.58.67.9 with SMTP id j9mr2500854vet.3.1380229432318; Thu, 26 Sep 2013 14:03:52 -0700 (PDT) Received: from mail-vb0-f41.google.com (mail-vb0-f41.google.com [209.85.212.41]) by mx.google.com with ESMTPS id oh2si889124vcb.85.1969.12.31.16.00.00 (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 26 Sep 2013 14:03:52 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.212.41 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.212.41; Received: by mail-vb0-f41.google.com with SMTP id g17so1295455vbg.0 for ; Thu, 26 Sep 2013 14:03:22 -0700 (PDT) X-Received: by 10.221.64.17 with SMTP id xg17mr2478939vcb.5.1380229402149; Thu, 26 Sep 2013 14:03:22 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.220.174.196 with SMTP id u4csp388824vcz; Thu, 26 Sep 2013 14:03:21 -0700 (PDT) X-Received: by 10.68.217.196 with SMTP id pa4mr3315213pbc.117.1380229401013; Thu, 26 Sep 2013 14:03:21 -0700 (PDT) Received: from mail-pb0-f41.google.com (mail-pb0-f41.google.com [209.85.160.41]) by mx.google.com with ESMTPS id pi7si2994660pbc.51.1969.12.31.16.00.00 (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 26 Sep 2013 14:03:21 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.160.41 is neither permitted nor denied by best guess record for domain of christoffer.dall@linaro.org) client-ip=209.85.160.41; Received: by mail-pb0-f41.google.com with SMTP id rp2so1660633pbb.14 for ; Thu, 26 Sep 2013 14:03:20 -0700 (PDT) X-Received: by 10.66.180.200 with SMTP id dq8mr7754681pac.104.1380229400570; Thu, 26 Sep 2013 14:03:20 -0700 (PDT) Received: from localhost.localdomain (c-67-169-181-221.hsd1.ca.comcast.net. [67.169.181.221]) by mx.google.com with ESMTPSA id 7sm7385572paf.22.1969.12.31.16.00.00 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 26 Sep 2013 14:03:19 -0700 (PDT) From: Christoffer Dall To: qemu-devel@nongnu.org Cc: kvmarm@lists.cs.columbia.edu, patches@linaro.org, Christoffer Dall Subject: [RFC PATCH v2 6/6] hw: arm_gic_kvm: Add KVM VGIC save/restore logic Date: Thu, 26 Sep 2013 14:03:06 -0700 Message-Id: <1380229386-24166-7-git-send-email-christoffer.dall@linaro.org> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1380229386-24166-1-git-send-email-christoffer.dall@linaro.org> References: <1380229386-24166-1-git-send-email-christoffer.dall@linaro.org> X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: christoffer.dall@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.212.41 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Save and restore the ARM KVM VGIC state from the kernel. We rely on QEMU to marshal the GICState data structure and therefore simply synchronize the kernel state with the QEMU emulated state in both directions. We take some care on the restore path to check the VGIC has been configured with enough IRQs and CPU interfaces that we can properly restore the state, and for separate set/clear registers we first fully clear the registers and then set the required bits. Signed-off-by: Christoffer Dall Changelog [v2]: - Remove num_irq from GIC VMstate structure - Increment GIC VMstate version number - Use extract32/deposit32 for bit-field modifications - Address other smaller review comments - Renames kvm_arm_gic_dist_[readr/writer] functions to kvm_dist_[get/put] and shortened other function names - Use concrete format for APRn --- hw/intc/arm_gic_common.c | 5 +- hw/intc/arm_gic_kvm.c | 424 +++++++++++++++++++++++++++++++++++++++++++++- hw/intc/gic_internal.h | 8 + 3 files changed, 433 insertions(+), 4 deletions(-) diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 5449d77..1d3b738 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -58,8 +58,8 @@ static const VMStateDescription vmstate_gic_irq_state = { static const VMStateDescription vmstate_gic = { .name = "arm_gic", - .version_id = 6, - .minimum_version_id = 6, + .version_id = 7, + .minimum_version_id = 7, .pre_save = gic_pre_save, .post_load = gic_post_load, .fields = (VMStateField[]) { @@ -78,6 +78,7 @@ static const VMStateDescription vmstate_gic = { VMSTATE_UINT16_ARRAY(current_pending, GICState, NCPU), VMSTATE_UINT8_ARRAY(bpr, GICState, NCPU), VMSTATE_UINT8_ARRAY(abpr, GICState, NCPU), + VMSTATE_UINT32_2DARRAY(apr, GICState, 4, NCPU), VMSTATE_END_OF_LIST() } }; diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 158f047..1510c4d 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -3,6 +3,7 @@ * * Copyright (c) 2012 Linaro Limited * Written by Peter Maydell + * Save/Restore logic added by Christoffer Dall. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +24,20 @@ #include "kvm_arm.h" #include "gic_internal.h" +//#define DEBUG_GIC_KVM + +#ifdef DEBUG_GIC_KVM +static const int debug_gic_kvm = 1; +#else +static const int debug_gic_kvm = 0; +#endif + +#define DPRINTF(fmt, ...) do { \ + if (debug_gic_kvm) { \ + printf("arm_gic: " fmt , ## __VA_ARGS__); \ + } \ + } while (0) + #define TYPE_KVM_ARM_GIC "kvm-arm-gic" #define KVM_ARM_GIC(obj) \ OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC) @@ -72,14 +87,419 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level) kvm_set_irq(kvm_state, kvm_irq, !!level); } +static bool kvm_arm_gic_can_save_restore(GICState *s) +{ + return s->dev_fd >= 0; +} + +static void kvm_gic_access(GICState *s, int group, int offset, + int cpu, uint32_t *val, bool write) +{ + struct kvm_device_attr attr; + int type; + int err; + + cpu = cpu & 0xff; + + attr.flags = 0; + attr.group = group; + attr.attr = (((uint64_t)cpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & + KVM_DEV_ARM_VGIC_CPUID_MASK) | + (((uint64_t)offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & + KVM_DEV_ARM_VGIC_OFFSET_MASK); + attr.addr = (uintptr_t)val; + + if (write) { + type = KVM_SET_DEVICE_ATTR; + } else { + type = KVM_GET_DEVICE_ATTR; + } + + err = kvm_device_ioctl(s->dev_fd, type, &attr); + if (err < 0) { + fprintf(stderr, "KVM_{SET/GET}_DEVICE_ATTR failed: %s\n", + strerror(-err)); + abort(); + } +} + +static void kvm_gicd_access(GICState *s, int offset, int cpu, + uint32_t *val, bool write) +{ + kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + offset, cpu, val, write); +} + +static void kvm_gicc_access(GICState *s, int offset, int cpu, + uint32_t *val, bool write) +{ + kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CPU_REGS, + offset, cpu, val, write); +} + +#define for_each_irq_reg(_ctr, _max_irq, _field_width) \ + for (_ctr = 0; _ctr < ((_max_irq) / (32 / (_field_width))); _ctr++) + +/* + * Translate from the in-kernel field for an IRQ value to/from the qemu + * representation. + */ +typedef void (*vgic_translate_fn)(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel); + +/* synthetic translate function used for clear/set registers to completely + * clear a setting using a clear-register before setting the remaing bits + * using a set-register */ +static void translate_clear(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = ~0; + } else { + /* does not make sense: qemu model doesn't use set/clear regs */ + abort(); + } +} + +static void translate_enabled(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; + + if (to_kernel) { + *field = GIC_TEST_ENABLED(irq, cm); + } else { + if (*field & 1) { + GIC_SET_ENABLED(irq, cm); + } + } +} + +static void translate_pending(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; + + if (to_kernel) { + *field = GIC_TEST_PENDING(irq, cm); + } else { + if (*field & 1) { + GIC_SET_PENDING(irq, cm); + /* TODO: Capture is level-line is held high in the kernel */ + } + } +} + +static void translate_active(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; + + if (to_kernel) { + *field = GIC_TEST_ACTIVE(irq, cm); + } else { + if (*field & 1) { + GIC_SET_ACTIVE(irq, cm); + } + } +} + +static void translate_trigger(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = (GIC_TEST_TRIGGER(irq)) ? 0x2 : 0x0; + } else { + if (*field & 0x2) { + GIC_SET_TRIGGER(irq); + } + } +} + +static void translate_priority(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = GIC_GET_PRIORITY(irq, cpu) & 0xff; + } else { + gic_set_priority(s, cpu, irq, *field & 0xff); + } +} + +static void translate_targets(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = s->irq_target[irq] & 0xff; + } else { + s->irq_target[irq] = *field & 0xff; + } +} + +static void translate_sgisource(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = s->sgi_source[irq][cpu] & 0xff; + } else { + s->sgi_source[irq][cpu] = *field & 0xff; + } +} + +/* Read a register group from the kernel VGIC */ +static void kvm_dist_get(GICState *s, uint32_t offset, int width, + int maxirq, vgic_translate_fn translate_fn) +{ + uint32_t reg; + int i; + int j; + int irq; + int cpu; + int regsz = 32 / width; /* irqs per kernel register */ + uint32_t field; + + for_each_irq_reg(i, maxirq, width) { + irq = i * regsz; + cpu = 0; + while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) { + kvm_gicd_access(s, offset, cpu, ®, false); + for (j = 0; j < regsz; j++) { + field = extract32(reg, j * width, width); + translate_fn(s, irq + j, cpu, &field, false); + } + + cpu++; + } + offset += 4; + } +} + +/* Write a register group to the kernel VGIC */ +static void kvm_dist_put(GICState *s, uint32_t offset, int width, + int maxirq, vgic_translate_fn translate_fn) +{ + uint32_t reg; + int i; + int j; + int irq; + int cpu; + int regsz = 32 / width; /* irqs per kernel register */ + uint32_t field; + + for_each_irq_reg(i, maxirq, width) { + irq = i * regsz; + cpu = 0; + while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) { + reg = 0; + for (j = 0; j < regsz; j++) { + translate_fn(s, irq + j, cpu, &field, true); + reg = deposit32(reg, j * width, width, field); + } + kvm_gicd_access(s, offset, cpu, ®, true); + + cpu++; + } + offset += 4; + } +} + static void kvm_arm_gic_put(GICState *s) { - /* TODO: there isn't currently a kernel interface to set the GIC state */ + uint32_t reg; + int i; + int cpu; + int num_cpu; + int num_irq; + + if (!kvm_arm_gic_can_save_restore(s)) { + DPRINTF("Cannot put kernel gic state, no kernel interface"); + return; + } + + /* Note: We do the restore in a slightly different order than the save + * (where the order doesn't matter and is simply ordered according to the + * register offset values */ + + /***************************************************************** + * Distributor State + */ + + /* s->enabled -> GICD_CTLR */ + reg = s->enabled; + kvm_gicd_access(s, 0x0, 0, ®, true); + + /* Sanity checking on GICD_TYPER and s->num_irq, s->num_cpu */ + kvm_gicd_access(s, 0x4, 0, ®, false); + num_irq = ((reg & 0x1f) + 1) * 32; + num_cpu = ((reg & 0xe0) >> 5) + 1; + + if (num_irq < s->num_irq) { + fprintf(stderr, "Restoring %u IRQs, but kernel supports max %d\n", + s->num_irq, num_irq); + abort(); + } else if (num_cpu != s->num_cpu ) { + fprintf(stderr, "Restoring %u CPU interfaces, kernel only has %d\n", + s->num_cpu, num_cpu); + /* Did we not create the VCPUs in the kernel yet? */ + abort(); + } + + /* TODO: Consider checking compatibility with the IIDR ? */ + + /* irq_state[n].enabled -> GICD_ISENABLERn */ + kvm_dist_put(s, 0x180, 1, s->num_irq, translate_clear); + kvm_dist_put(s, 0x100, 1, s->num_irq, translate_enabled); + + /* s->irq_target[irq] -> GICD_ITARGETSRn + * (restore targets before pending to ensure the pending state is set on + * the appropriate CPU interfaces in the kernel) */ + kvm_dist_put(s, 0x800, 8, s->num_irq, translate_targets); + + /* irq_state[n].pending + irq_state[n].level -> GICD_ISPENDRn */ + kvm_dist_put(s, 0x280, 1, s->num_irq, translate_clear); + kvm_dist_put(s, 0x200, 1, s->num_irq, translate_pending); + + /* irq_state[n].active -> GICD_ISACTIVERn */ + kvm_dist_put(s, 0x380, 1, s->num_irq, translate_clear); + kvm_dist_put(s, 0x300, 1, s->num_irq, translate_active); + + /* irq_state[n].trigger -> GICD_ICFRn */ + kvm_dist_put(s, 0xc00, 2, s->num_irq, translate_trigger); + + /* s->priorityX[irq] -> ICD_IPRIORITYRn */ + kvm_dist_put(s, 0x400, 8, s->num_irq, translate_priority); + + /* s->sgi_source -> ICD_CPENDSGIRn */ + kvm_dist_put(s, 0xf10, 8, GIC_NR_SGIS, translate_clear); + kvm_dist_put(s, 0xf20, 8, GIC_NR_SGIS, translate_sgisource); + + + /***************************************************************** + * CPU Interface(s) State + */ + + for (cpu = 0; cpu < s->num_cpu; cpu++) { + /* s->cpu_enabled[cpu] -> GICC_CTLR */ + reg = s->cpu_enabled[cpu]; + kvm_gicc_access(s, 0x00, cpu, ®, true); + + /* s->priority_mask[cpu] -> GICC_PMR */ + reg = (s->priority_mask[cpu] & 0xff); + kvm_gicc_access(s, 0x04, cpu, ®, true); + + /* s->bpr[cpu] -> GICC_BPR */ + reg = (s->bpr[cpu] & 0x7); + kvm_gicc_access(s, 0x08, cpu, ®, true); + + /* s->abpr[cpu] -> GICC_ABPR */ + reg = (s->abpr[cpu] & 0x7); + kvm_gicc_access(s, 0x1c, cpu, ®, true); + + /* s->apr[n][cpu] -> GICC_APRn */ + for (i = 0; i < 4; i++) { + reg = s->apr[i][cpu]; + kvm_gicc_access(s, 0xd0 + i * 4, cpu, ®, true); + } + } } static void kvm_arm_gic_get(GICState *s) { - /* TODO: there isn't currently a kernel interface to get the GIC state */ + uint32_t reg; + int i; + int cpu; + + if (!kvm_arm_gic_can_save_restore(s)) { + DPRINTF("Cannot get kernel gic state, no kernel interface"); + return; + } + + /***************************************************************** + * Distributor State + */ + + /* GICD_CTLR -> s->enabled */ + kvm_gicd_access(s, 0x0, 0, ®, false); + s->enabled = reg & 1; + + /* Sanity checking on GICD_TYPER -> s->num_irq, s->num_cpu */ + kvm_gicd_access(s, 0x4, 0, ®, false); + s->num_irq = ((reg & 0x1f) + 1) * 32; + s->num_cpu = ((reg & 0xe0) >> 5) + 1; + + if (s->num_irq > GIC_MAXIRQ) { + fprintf(stderr, "Too many IRQs reported from the kernel: %d\n", + s->num_irq); + abort(); + } + + /* GICD_IIDR -> ? */ + kvm_gicd_access(s, 0x8, 0, ®, false); + + /* Verify no GROUP 1 interrupts configured in the kernel */ + for_each_irq_reg(i, s->num_irq, 1) { + kvm_gicd_access(s, 0x80 + (i * 4), 0, ®, false); + if (reg != 0) { + fprintf(stderr, "Unsupported GICD_IGROUPRn value: %08x\n", + reg); + abort(); + } + } + + /* Clear all the IRQ settings */ + for (i = 0; i < s->num_irq; i++) { + memset(&s->irq_state[i], 0, sizeof(s->irq_state[0])); + } + + /* GICD_ISENABLERn -> irq_state[n].enabled */ + kvm_dist_get(s, 0x100, 1, s->num_irq, translate_enabled); + + /* GICD_ISPENDRn -> irq_state[n].pending + irq_state[n].level */ + kvm_dist_get(s, 0x200, 1, s->num_irq, translate_pending); + + /* GICD_ISACTIVERn -> irq_state[n].active */ + kvm_dist_get(s, 0x300, 1, s->num_irq, translate_active); + + /* GICD_ICFRn -> irq_state[n].trigger */ + kvm_dist_get(s, 0xc00, 2, s->num_irq, translate_trigger); + + /* GICD_IPRIORITYRn -> s->priorityX[irq] */ + kvm_dist_get(s, 0x400, 8, s->num_irq, translate_priority); + + /* GICD_ITARGETSRn -> s->irq_target[irq] */ + kvm_dist_get(s, 0x800, 8, s->num_irq, translate_targets); + + /* GICD_CPENDSGIRn -> s->sgi_source */ + kvm_dist_get(s, 0xf10, 8, GIC_NR_SGIS, translate_sgisource); + + + /***************************************************************** + * CPU Interface(s) State + */ + + for (cpu = 0; cpu < s->num_cpu; cpu++) { + /* GICC_CTLR -> s->cpu_enabled[cpu] */ + kvm_gicc_access(s, 0x00, cpu, ®, false); + s->cpu_enabled[cpu] = (reg & 1); + + /* GICC_PMR -> s->priority_mask[cpu] */ + kvm_gicc_access(s, 0x04, cpu, ®, false); + s->priority_mask[cpu] = (reg & 0xff); + + /* GICC_BPR -> s->bpr[cpu] */ + kvm_gicc_access(s, 0x08, cpu, ®, false); + s->bpr[cpu] = (reg & 0x7); + + /* GICC_ABPR -> s->abpr[cpu] */ + kvm_gicc_access(s, 0x1c, cpu, ®, false); + s->abpr[cpu] = (reg & 0x7); + + /* GICC_APRn -> s->apr[n][cpu] */ + for (i = 0; i < 4; i++) { + kvm_gicc_access(s, 0xd0 + i * 4, cpu, ®, false); + s->apr[i][cpu] = reg; + } + } } static void kvm_arm_gic_reset(DeviceState *dev) diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 758b85a..f9133b9 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -99,6 +99,14 @@ typedef struct GICState { uint8_t bpr[NCPU]; uint8_t abpr[NCPU]; + /* The APR is implementation defined, so we choose a layout identical to + * the KVM ABI layout for QEMU's implementation of the gic: + * If one or more interrupts for preemption level X are active, then + * APRn[X mod 32] == 0b1, where n = X / 32 + * otherwise the bit is clear. + */ + uint32_t apr[4][NCPU]; + uint32_t num_cpu; MemoryRegion iomem; /* Distributor */