From 529b4abfdbade393a36617c0b3e51489cf9b00cd Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 7 Jul 2022 15:49:03 +0100 Subject: [PATCH] Revert "ANDROID: KVM: arm64: s2mpu: Move SFR init to EL2" This reverts commit 880d95ffa3a64937f3802bfd8174fc2b2e807684. Bug: 233587962 Signed-off-by: Will Deacon Change-Id: Ide4de67f0e6e1d6174460e373f23b2411fdadf61 --- arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c | 117 +++++--------------------- arch/arm64/kvm/iommu/s2mpu.c | 71 ++++++++++++++++ 2 files changed, 93 insertions(+), 95 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c index e0783369a41b..ad5716cd51ee 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c @@ -30,10 +30,6 @@ #define for_each_powered_s2mpu(i) \ for_each_s2mpu((i)) if (is_powered_on((i))) -#define CTX_CFG_ENTRY(ctxid, nr_ctx, vid) \ - (CONTEXT_CFG_VALID_VID_CTX_VID(ctxid, vid) \ - | (((ctxid) < (nr_ctx)) ? CONTEXT_CFG_VALID_VID_CTX_VALID(ctxid) : 0)) - size_t __ro_after_init kvm_hyp_nr_s2mpus; struct s2mpu __ro_after_init *kvm_hyp_s2mpus; struct mpt kvm_hyp_host_mpt; @@ -71,75 +67,18 @@ static bool is_in_power_domain(struct s2mpu *dev, u64 power_domain_id) } } -static u32 __context_cfg_valid_vid(struct s2mpu *dev, u32 vid_bmap) +/* + * Write CONTEXT_CFG_VALID_VID configuration before touching L1ENTRY* registers. + * Writes to those registers are ignored unless there is a context ID allocated + * to the corresponding VID (v9 only). + */ +static void __set_context_ids(struct s2mpu *dev) { - u8 ctx_vid[NR_CTX_IDS] = { 0 }; - unsigned int vid, ctx = 0; - unsigned int num_ctx; - u32 res; + if (!is_version(dev, S2MPU_VERSION_9)) + return; - /* Only initialize once. */ - if (dev->context_cfg_valid_vid) - return dev->context_cfg_valid_vid; - - num_ctx = readl_relaxed(dev->va + REG_NS_NUM_CONTEXT) & NUM_CONTEXT_MASK; - while (vid_bmap) { - /* Break if we cannot allocate more. */ - if (ctx >= num_ctx) - break; - - vid = __ffs(vid_bmap); - vid_bmap &= ~BIT(vid); - ctx_vid[ctx++] = vid; - } - - /* The following loop was unrolled so bitmasks are constant. */ - BUILD_BUG_ON(NR_CTX_IDS != 8); - res = CTX_CFG_ENTRY(0, ctx, ctx_vid[0]) - | CTX_CFG_ENTRY(1, ctx, ctx_vid[1]) - | CTX_CFG_ENTRY(2, ctx, ctx_vid[2]) - | CTX_CFG_ENTRY(3, ctx, ctx_vid[3]) - | CTX_CFG_ENTRY(4, ctx, ctx_vid[4]) - | CTX_CFG_ENTRY(5, ctx, ctx_vid[5]) - | CTX_CFG_ENTRY(6, ctx, ctx_vid[6]) - | CTX_CFG_ENTRY(7, ctx, ctx_vid[7]); - - dev->context_cfg_valid_vid = res; - return res; -} - -static int __initialize_v9(struct s2mpu *dev) -{ - u32 ssmt_valid_vid_bmap, ctx_cfg; - - /* Assume all VIDs may be generated by the connected SSMTs for now. */ - ssmt_valid_vid_bmap = ALL_VIDS_BITMAP; - ctx_cfg = __context_cfg_valid_vid(dev, ssmt_valid_vid_bmap); - if (!ctx_cfg) - return -EINVAL; - - /* - * Write CONTEXT_CFG_VALID_VID configuration before touching L1ENTRY* - * registers. Writes to those registers are ignored unless there is - * a context ID allocated to the corresponding VID (v9 only). - */ - writel_relaxed(ctx_cfg, dev->va + REG_NS_CONTEXT_CFG_VALID_VID); - return 0; -} - -static int __initialize(struct s2mpu *dev) -{ - if (!dev->version) - dev->version = readl_relaxed(dev->va + REG_NS_VERSION); - - switch (dev->version & VERSION_CHECK_MASK) { - case S2MPU_VERSION_8: - return 0; - case S2MPU_VERSION_9: - return __initialize_v9(dev); - default: - return -EINVAL; - } + writel_relaxed(dev->context_cfg_valid_vid, + dev->va + REG_NS_CONTEXT_CFG_VALID_VID); } static void __set_control_regs(struct s2mpu *dev) @@ -240,14 +179,12 @@ static void __set_l1entry_l2table_addr(struct s2mpu *dev, unsigned int gb, * Initialize S2MPU device and set all GB regions to 1G granularity with * given protection bits. */ -static int initialize_with_prot(struct s2mpu *dev, enum mpt_prot prot) +static void initialize_with_prot(struct s2mpu *dev, enum mpt_prot prot) { unsigned int gb, vid; - int ret; - ret = __initialize(dev); - if (ret) - return ret; + /* Must write CONTEXT_CFG_VALID_VID before setting L1ENTRY registers. */ + __set_context_ids(dev); for_each_gb_and_vid(gb, vid) __set_l1entry_attr_with_prot(dev, gb, vid, prot); @@ -255,22 +192,19 @@ static int initialize_with_prot(struct s2mpu *dev, enum mpt_prot prot) /* Set control registers, enable the S2MPU. */ __set_control_regs(dev); - return 0; } /* * Initialize S2MPU device, set L2 table addresses and configure L1TABLE_ATTR * registers according to the given MPT struct. */ -static int initialize_with_mpt(struct s2mpu *dev, struct mpt *mpt) +static void initialize_with_mpt(struct s2mpu *dev, struct mpt *mpt) { unsigned int gb, vid; struct fmpt *fmpt; - int ret; - ret = __initialize(dev); - if (ret) - return ret; + /* Must write CONTEXT_CFG_VALID_VID before setting L1ENTRY registers. */ + __set_context_ids(dev); for_each_gb_and_vid(gb, vid) { fmpt = &mpt->fmpt[gb]; @@ -281,7 +215,6 @@ static int initialize_with_mpt(struct s2mpu *dev, struct mpt *mpt) /* Set control registers, enable the S2MPU. */ __set_control_regs(dev); - return 0; } /* @@ -395,7 +328,6 @@ static bool s2mpu_host_smc_handler(struct kvm_cpu_context *host_ctxt) struct arm_smccc_res res; struct s2mpu *dev; - int ret; if (fn != SMC_CMD_PREPARE_PD_ONOFF) return false; /* SMC not handled */ @@ -420,25 +352,23 @@ static bool s2mpu_host_smc_handler(struct kvm_cpu_context *host_ctxt) hyp_spin_lock(&s2mpu_lock); arm_smccc_1_1_smc(fn, mode, domain_id, group, &res); - ret = res.a0; - - if (ret == SMCCC_RET_SUCCESS) { + if (res.a0 == SMCCC_RET_SUCCESS) { for_each_s2mpu(dev) { if (!is_in_power_domain(dev, domain_id)) continue; if (mode == SMC_MODE_POWER_UP) { dev->power_state = S2MPU_POWER_ON; - ret = initialize_with_mpt(dev, &kvm_hyp_host_mpt); + initialize_with_mpt(dev, &kvm_hyp_host_mpt); } else { - ret = initialize_with_prot(dev, MPT_PROT_NONE); + initialize_with_prot(dev, MPT_PROT_NONE); dev->power_state = S2MPU_POWER_OFF; } } } hyp_spin_unlock(&s2mpu_lock); - cpu_reg(host_ctxt, 0) = ret; + cpu_reg(host_ctxt, 0) = res.a0; return true; /* SMC handled */ } @@ -541,11 +471,8 @@ static int s2mpu_init(void) * Program all S2MPUs powered on at boot. Note that they may not be in * the blocking reset state as the bootloader may have programmed them. */ - for_each_powered_s2mpu(dev) { - ret = initialize_with_mpt(dev, &kvm_hyp_host_mpt); - if (ret) - return ret; - } + for_each_powered_s2mpu(dev) + initialize_with_mpt(dev, &kvm_hyp_host_mpt); return 0; } diff --git a/arch/arm64/kvm/iommu/s2mpu.c b/arch/arm64/kvm/iommu/s2mpu.c index 79c33815267b..00111f237de3 100644 --- a/arch/arm64/kvm/iommu/s2mpu.c +++ b/arch/arm64/kvm/iommu/s2mpu.c @@ -13,6 +13,10 @@ #include #include +#define CTX_CFG_ENTRY(ctxid, nr_ctx, vid) \ + (CONTEXT_CFG_VALID_VID_CTX_VID(ctxid, vid) \ + | (((ctxid) < (nr_ctx)) ? CONTEXT_CFG_VALID_VID_CTX_VALID(ctxid) : 0)) + struct s2mpu_irq_info { struct device *dev; void __iomem *va; @@ -77,6 +81,59 @@ static irqreturn_t s2mpu_irq_handler(int irq, void *data) return ret; } +static u32 gen_ctx_cfg_valid_vid(struct platform_device *pdev, + unsigned int num_ctx, u32 vid_bmap) +{ + u8 ctx_vid[NR_CTX_IDS] = { 0 }; + unsigned int vid, ctx = 0; + + /* Check NUM_CONTEXT value is within bounds. This should not happen. */ + if (WARN_ON(num_ctx > NR_CTX_IDS)) + num_ctx = NR_CTX_IDS; + + while (vid_bmap) { + /* Break if we cannot allocate more. */ + if (ctx >= num_ctx) { + dev_warn(&pdev->dev, + "could not allocate all context IDs, DMA may be blocked (VID bitmap: 0x%x)", + vid_bmap); + break; + } + + vid = __ffs(vid_bmap); + vid_bmap &= ~BIT(vid); + ctx_vid[ctx++] = vid; + } + + /* The following loop was unrolled so bitmasks are constant. */ + BUILD_BUG_ON(NR_CTX_IDS != 8); + return CTX_CFG_ENTRY(0, ctx, ctx_vid[0]) + | CTX_CFG_ENTRY(1, ctx, ctx_vid[1]) + | CTX_CFG_ENTRY(2, ctx, ctx_vid[2]) + | CTX_CFG_ENTRY(3, ctx, ctx_vid[3]) + | CTX_CFG_ENTRY(4, ctx, ctx_vid[4]) + | CTX_CFG_ENTRY(5, ctx, ctx_vid[5]) + | CTX_CFG_ENTRY(6, ctx, ctx_vid[6]) + | CTX_CFG_ENTRY(7, ctx, ctx_vid[7]); +} + +static int s2mpu_probe_v9(struct platform_device *pdev, void __iomem *kaddr, + struct s2mpu *info) +{ + unsigned int num_ctx; + u32 ssmt_valid_vid_bmap; + + ssmt_valid_vid_bmap = ALL_VIDS_BITMAP; + num_ctx = readl_relaxed(kaddr + REG_NS_NUM_CONTEXT) & NUM_CONTEXT_MASK; + info->context_cfg_valid_vid = gen_ctx_cfg_valid_vid(pdev, num_ctx, ssmt_valid_vid_bmap); + if (!info->context_cfg_valid_vid) { + dev_err(&pdev->dev, "failed to allocate context IDs"); + return -EINVAL; + } + + return 0; +} + /* * Parse interrupt information from DT and if found, register IRQ handler. * This is considered optional and will not fail even if the initialization is @@ -175,6 +232,20 @@ static int s2mpu_probe(struct platform_device *pdev) */ s2mpu_probe_irq(pdev, kaddr); + info->version = readl_relaxed(kaddr + REG_NS_VERSION); + switch (info->version & VERSION_CHECK_MASK) { + case S2MPU_VERSION_8: + break; + case S2MPU_VERSION_9: + ret = s2mpu_probe_v9(pdev, kaddr, info); + if (ret) + return ret; + break; + default: + dev_err(&pdev->dev, "unexpected version 0x%08x", info->version); + return -EINVAL; + } + /* Insert successfully parsed devices to a list later copied to hyp. */ list_add_tail(&entry->list, &s2mpu_list); kvm_hyp_nr_s2mpus++;