On 29.04.20 12:02, [email protected] wrote:
From: Alice Guo <[email protected]>
Support smmu-v2 mmu500, add sid master support, only support stage2
translation.
Signed-off-by: Alice Guo <[email protected]>
---
hypervisor/arch/arm64/Kbuild | 1 +
hypervisor/arch/arm64/arm-smmu-regs.h | 220 ++++++
hypervisor/arch/arm64/smmu.c | 926 ++++++++++++++++++++++++++
include/jailhouse/cell-config.h | 15 +
include/jailhouse/sizes.h | 47 ++
5 files changed, 1209 insertions(+)
create mode 100644 hypervisor/arch/arm64/arm-smmu-regs.h
create mode 100644 hypervisor/arch/arm64/smmu.c
create mode 100644 include/jailhouse/sizes.h
diff --git a/hypervisor/arch/arm64/Kbuild b/hypervisor/arch/arm64/Kbuild
index c34b0f32..e87c6e53 100644
--- a/hypervisor/arch/arm64/Kbuild
+++ b/hypervisor/arch/arm64/Kbuild
@@ -22,3 +22,4 @@ always := lib.a
lib-y := $(common-objs-y)
lib-y += entry.o setup.o control.o mmio.o paging.o caches.o traps.o
lib-y += iommu.o smmu-v3.o ti-pvu.o
+lib-y += smmu.o
diff --git a/hypervisor/arch/arm64/arm-smmu-regs.h
b/hypervisor/arch/arm64/arm-smmu-regs.h
new file mode 100644
index 00000000..a1226e4a
--- /dev/null
+++ b/hypervisor/arch/arm64/arm-smmu-regs.h
@@ -0,0 +1,220 @@
+/*
+ * IOMMU API for ARM architected SMMU implementations.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2013 ARM Limited
+ *
+ * Author: Will Deacon <[email protected]>
Is this file a 1:1 copy of the kernel header? Or is it also adopted?
+ */
+
+#ifndef _ARM_SMMU_REGS_H
+#define _ARM_SMMU_REGS_H
+
+/* Configuration registers */
+#define ARM_SMMU_GR0_sCR0 0x0
+#define sCR0_CLIENTPD (1 << 0)
+#define sCR0_GFRE (1 << 1)
+#define sCR0_GFIE (1 << 2)
+#define sCR0_EXIDENABLE (1 << 3)
+#define sCR0_GCFGFRE (1 << 4)
+#define sCR0_GCFGFIE (1 << 5)
+#define sCR0_USFCFG (1 << 10)
+#define sCR0_VMIDPNE (1 << 11)
+#define sCR0_PTM (1 << 12)
+#define sCR0_FB (1 << 13)
+#define sCR0_VMID16EN (1 << 31)
+#define sCR0_BSU_SHIFT 14
+#define sCR0_BSU_MASK 0x3
+
+/* Auxiliary Configuration register */
+#define ARM_SMMU_GR0_sACR 0x10
+
+/* Identification registers */
+#define ARM_SMMU_GR0_ID0 0x20
+#define ARM_SMMU_GR0_ID1 0x24
+#define ARM_SMMU_GR0_ID2 0x28
+#define ARM_SMMU_GR0_ID3 0x2c
+#define ARM_SMMU_GR0_ID4 0x30
+#define ARM_SMMU_GR0_ID5 0x34
+#define ARM_SMMU_GR0_ID6 0x38
+#define ARM_SMMU_GR0_ID7 0x3c
+#define ARM_SMMU_GR0_sGFSR 0x48
+#define ARM_SMMU_GR0_sGFSYNR0 0x50
+#define ARM_SMMU_GR0_sGFSYNR1 0x54
+#define ARM_SMMU_GR0_sGFSYNR2 0x58
+
+#define ID0_S1TS (1 << 30)
+#define ID0_S2TS (1 << 29)
+#define ID0_NTS (1 << 28)
+#define ID0_SMS (1 << 27)
+#define ID0_ATOSNS (1 << 26)
+#define ID0_PTFS_NO_AARCH32 (1 << 25)
+#define ID0_PTFS_NO_AARCH32S (1 << 24)
+#define ID0_CTTW (1 << 14)
+#define ID0_NUMIRPT_SHIFT 16
+#define ID0_NUMIRPT_MASK 0xff
+#define ID0_NUMSIDB_SHIFT 9
+#define ID0_NUMSIDB_MASK 0xf
+#define ID0_EXIDS (1 << 8)
+#define ID0_NUMSMRG_SHIFT 0
+#define ID0_NUMSMRG_MASK 0xff
+
+#define ID1_PAGESIZE (1 << 31)
+#define ID1_NUMPAGENDXB_SHIFT 28
+#define ID1_NUMPAGENDXB_MASK 7
+#define ID1_NUMS2CB_SHIFT 16
+#define ID1_NUMS2CB_MASK 0xff
+#define ID1_NUMCB_SHIFT 0
+#define ID1_NUMCB_MASK 0xff
+
+#define ID2_OAS_SHIFT 4
+#define ID2_OAS_MASK 0xf
+#define ID2_IAS_SHIFT 0
+#define ID2_IAS_MASK 0xf
+#define ID2_UBS_SHIFT 8
+#define ID2_UBS_MASK 0xf
+#define ID2_PTFS_4K (1 << 12)
+#define ID2_PTFS_16K (1 << 13)
+#define ID2_PTFS_64K (1 << 14)
+#define ID2_VMID16 (1 << 15)
+
+#define ID7_MAJOR_SHIFT 4
+#define ID7_MAJOR_MASK 0xf
+
+/* Global TLB invalidation */
+#define ARM_SMMU_GR0_TLBIVMID 0x64
+#define ARM_SMMU_GR0_TLBIALLNSNH 0x68
+#define ARM_SMMU_GR0_TLBIALLH 0x6c
+#define ARM_SMMU_GR0_sTLBGSYNC 0x70
+#define ARM_SMMU_GR0_sTLBGSTATUS 0x74
+#define sTLBGSTATUS_GSACTIVE (1 << 0)
+
+/* Stream mapping registers */
+#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
+#define SMR_VALID (1 << 31)
+#define SMR_MASK_SHIFT 16
+#define SMR_ID_SHIFT 0
+
+#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
+#define S2CR_CBNDX_SHIFT 0
+#define S2CR_CBNDX_MASK 0xff
+#define S2CR_EXIDVALID (1 << 10)
+#define S2CR_TYPE_SHIFT 16
+#define S2CR_TYPE_MASK 0x3
+enum arm_smmu_s2cr_type {
+ S2CR_TYPE_TRANS,
+ S2CR_TYPE_BYPASS,
+ S2CR_TYPE_FAULT,
+};
What is the advantage of modelling these as enum? And are we anywhere
relying on their exact values for the width if those types? If so, then
better use u* types and constant #defines.
+
+#define S2CR_PRIVCFG_SHIFT 24
+#define S2CR_PRIVCFG_MASK 0x3
+enum arm_smmu_s2cr_privcfg {
+ S2CR_PRIVCFG_DEFAULT,
+ S2CR_PRIVCFG_DIPAN,
+ S2CR_PRIVCFG_UNPRIV,
+ S2CR_PRIVCFG_PRIV,
+};
Same as above.
+
+/* Context bank attribute registers */
+#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
+#define CBAR_VMID_SHIFT 0
+#define CBAR_VMID_MASK 0xff
+#define CBAR_S1_BPSHCFG_SHIFT 8
+#define CBAR_S1_BPSHCFG_MASK 3
+#define CBAR_S1_BPSHCFG_NSH 3
+#define CBAR_S1_MEMATTR_SHIFT 12
+#define CBAR_S1_MEMATTR_MASK 0xf
+#define CBAR_S1_MEMATTR_WB 0xf
+#define CBAR_TYPE_SHIFT 16
+#define CBAR_TYPE_MASK 0x3
+#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT)
+#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT)
+#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT)
+#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT)
+#define CBAR_IRPTNDX_SHIFT 24
+#define CBAR_IRPTNDX_MASK 0xff
+
+#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
+#define CBA2R_RW64_32BIT (0 << 0)
+#define CBA2R_RW64_64BIT (1 << 0)
+#define CBA2R_VMID_SHIFT 16
+#define CBA2R_VMID_MASK 0xffff
+
+#define ARM_SMMU_CB_SCTLR 0x0
+#define ARM_SMMU_CB_ACTLR 0x4
+#define ARM_SMMU_CB_RESUME 0x8
+#define ARM_SMMU_CB_TTBCR2 0x10
+#define ARM_SMMU_CB_TTBR0 0x20
+#define ARM_SMMU_CB_TTBR1 0x28
+#define ARM_SMMU_CB_TTBCR 0x30
+#define ARM_SMMU_CB_CONTEXTIDR 0x34
+#define ARM_SMMU_CB_S1_MAIR0 0x38
+#define ARM_SMMU_CB_S1_MAIR1 0x3c
+#define ARM_SMMU_CB_PAR 0x50
+#define ARM_SMMU_CB_FSR 0x58
+#define ARM_SMMU_CB_FAR 0x60
+#define ARM_SMMU_CB_FSYNR0 0x68
+#define ARM_SMMU_CB_S1_TLBIVA 0x600
+#define ARM_SMMU_CB_S1_TLBIASID 0x610
+#define ARM_SMMU_CB_S1_TLBIVAL 0x620
+#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630
+#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638
+#define ARM_SMMU_CB_TLBSYNC 0x7f0
+#define ARM_SMMU_CB_TLBSTATUS 0x7f4
+#define ARM_SMMU_CB_ATS1PR 0x800
+#define ARM_SMMU_CB_ATSR 0x8f0
+
+#define SCTLR_S1_ASIDPNE (1 << 12)
+#define SCTLR_CFCFG (1 << 7)
+#define SCTLR_CFIE (1 << 6)
+#define SCTLR_CFRE (1 << 5)
+#define SCTLR_E (1 << 4)
+#define SCTLR_AFE (1 << 2)
+#define SCTLR_TRE (1 << 1)
+#define SCTLR_M (1 << 0)
+
+#define CB_PAR_F (1 << 0)
+
+#define ATSR_ACTIVE (1 << 0)
+
+#define RESUME_RETRY (0 << 0)
+#define RESUME_TERMINATE (1 << 0)
+
+#define TTBCR2_SEP_SHIFT 15
+#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
+#define TTBCR2_AS (1 << 4)
+
+#define TTBRn_ASID_SHIFT 48
+
+#define FSR_MULTI (1 << 31)
+#define FSR_SS (1 << 30)
+#define FSR_UUT (1 << 8)
+#define FSR_ASF (1 << 7)
+#define FSR_TLBLKF (1 << 6)
+#define FSR_TLBMCF (1 << 5)
+#define FSR_EF (1 << 4)
+#define FSR_PF (1 << 3)
+#define FSR_AFF (1 << 2)
+#define FSR_TF (1 << 1)
+
+#define FSR_IGN (FSR_AFF | FSR_ASF | \
+ FSR_TLBMCF | FSR_TLBLKF)
+#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \
+ FSR_EF | FSR_PF | FSR_TF | FSR_IGN)
+
+#define FSYNR0_WNR (1 << 4)
+
+#endif /* _ARM_SMMU_REGS_H */
diff --git a/hypervisor/arch/arm64/smmu.c b/hypervisor/arch/arm64/smmu.c
new file mode 100644
index 00000000..ea1b4c1e
--- /dev/null
+++ b/hypervisor/arch/arm64/smmu.c
@@ -0,0 +1,926 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright 2018-2020 NXP
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Modified from Linux smmu.c
+ */
+
+#include <jailhouse/control.h>
+#include <jailhouse/ivshmem.h>
+#include <jailhouse/mmio.h>
+#include <jailhouse/paging.h>
+#include <jailhouse/pci.h>
+#include <jailhouse/printk.h>
+#include <jailhouse/sizes.h>
+#include <jailhouse/string.h>
+#include <jailhouse/unit.h>
+#include <asm/spinlock.h>
+#include <jailhouse/cell-config.h>
+
+#include "arm-smmu-regs.h"
Are those regs common to anyone? SMMUv3? If not, better fold in. If
there is something to share, then move to include/asm/,
+
+#define ARM_32_LPAE_TCR_EAE (1 << 31)
+#define ARM_64_LPAE_S2_TCR_RES1 (1 << 31)
+
+#define ARM_LPAE_TCR_EPD1 (1 << 23)
+
+#define ARM_LPAE_TCR_TG0_4K (0 << 14)
+#define ARM_LPAE_TCR_TG0_64K (1 << 14)
+#define ARM_LPAE_TCR_TG0_16K (2 << 14)
+
+#define ARM_LPAE_TCR_SH0_SHIFT 12
+#define ARM_LPAE_TCR_SH0_MASK 0x3
+#define ARM_LPAE_TCR_SH_NS 0
+#define ARM_LPAE_TCR_SH_OS 2
+#define ARM_LPAE_TCR_SH_IS 3
+
+#define ARM_LPAE_TCR_ORGN0_SHIFT 10
+#define ARM_LPAE_TCR_IRGN0_SHIFT 8
+#define ARM_LPAE_TCR_RGN_MASK 0x3
+#define ARM_LPAE_TCR_RGN_NC 0
+#define ARM_LPAE_TCR_RGN_WBWA 1
+#define ARM_LPAE_TCR_RGN_WT 2
+#define ARM_LPAE_TCR_RGN_WB 3
+
+#define ARM_LPAE_TCR_SL0_SHIFT 6
+#define ARM_LPAE_TCR_SL0_MASK 0x3
+#define ARM_LPAE_TCR_SL0_LVL_2 0
+#define ARM_LPAE_TCR_SL0_LVL_1 1
+
+#define ARM_LPAE_TCR_T0SZ_SHIFT 0
+#define ARM_LPAE_TCR_SZ_MASK 0xf
+
+#define ARM_LPAE_TCR_PS_SHIFT 16
+#define ARM_LPAE_TCR_PS_MASK 0x7
+
+#define ARM_LPAE_TCR_IPS_SHIFT 32
+#define ARM_LPAE_TCR_IPS_MASK 0x7
+
+#define ARM_LPAE_TCR_PS_32_BIT 0x0ULL
+#define ARM_LPAE_TCR_PS_36_BIT 0x1ULL
+#define ARM_LPAE_TCR_PS_40_BIT 0x2ULL
+#define ARM_LPAE_TCR_PS_42_BIT 0x3ULL
+#define ARM_LPAE_TCR_PS_44_BIT 0x4ULL
+#define ARM_LPAE_TCR_PS_48_BIT 0x5ULL
+#define ARM_LPAE_TCR_PS_52_BIT 0x6ULL
+#define ARM_MMU500_ACTLR_CPRE (1 << 1)
+
+#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26)
+#define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10)
+#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8)
+
+#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */
+#define TLB_SPIN_COUNT 10
How do those numbers map to real-time? Are they truly CPU(-frequency)
independent?
+
+/* Maximum number of context banks per SMMU */
+#define ARM_SMMU_MAX_CBS 128
+
+/* SMMU global address space */
+#define ARM_SMMU_GR0(smmu) ((smmu)->base)
+#define ARM_SMMU_GR1(smmu) ((smmu)->base + (1 << (smmu)->pgshift))
+
+/*
+ * SMMU global address space with conditional offset to access secure
+ * aliases of non-secure registers (e.g. nsCR0: 0x400, nsGFSR: 0x448,
+ * nsGFSYNR0: 0x450)
+ */
+#define ARM_SMMU_GR0_NS(smmu) \
+ ((smmu)->base + \
+ ((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS) \
+ ? 0x400 : 0))
+
+/* Translation context bank */
+#define ARM_SMMU_CB(smmu, n) ((smmu)->cb_base + ((n) << (smmu)->pgshift))
+
+#define MSI_IOVA_BASE 0x8000000
+#define MSI_IOVA_LENGTH 0x100000
+
+struct arm_smmu_s2cr {
+ enum arm_smmu_s2cr_type type;
+ enum arm_smmu_s2cr_privcfg privcfg;
+ u8 cbndx;
+};
+
+struct arm_smmu_smr {
+ u16 mask;
+ u16 id;
+ bool valid;
+};
+
+struct arm_smmu_cb {
+ u64 ttbr[2];
+ u32 tcr[2];
+ u32 mair[2];
+ struct arm_smmu_cfg *cfg;
+};
Is any of those data structures shared with the hardware? Or are they
just for internal book-keeping? I'm asking because of aligments, packing
etc. Applies to almost all structs you define.
+
+struct arm_smmu_device {
+ void *base;
+ void *cb_base;
+ u32 num_masters;
+ unsigned long pgshift;
+
+#define ARM_SMMU_FEAT_COHERENT_WALK (1 << 0)
+#define ARM_SMMU_FEAT_STREAM_MATCH (1 << 1)
+#define ARM_SMMU_FEAT_TRANS_S1 (1 << 2)
+#define ARM_SMMU_FEAT_TRANS_S2 (1 << 3)
+#define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4)
+#define ARM_SMMU_FEAT_TRANS_OPS (1 << 5)
+#define ARM_SMMU_FEAT_VMID16 (1 << 6)
+#define ARM_SMMU_FEAT_FMT_AARCH64_4K (1 << 7)
+#define ARM_SMMU_FEAT_FMT_AARCH64_16K (1 << 8)
+#define ARM_SMMU_FEAT_FMT_AARCH64_64K (1 << 9)
+#define ARM_SMMU_FEAT_FMT_AARCH32_L (1 << 10)
+#define ARM_SMMU_FEAT_FMT_AARCH32_S (1 << 11)
+#define ARM_SMMU_FEAT_EXIDS (1 << 12)
I would prefer those defines outside of the struct. Also below.
+ u32 features;
+
+#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
+ u32 options;
+ enum arm_smmu_arch_version version;
+ enum arm_smmu_implementation model;
+
+ u32 num_context_banks;
+ u32 num_s2_context_banks;
+ struct arm_smmu_cb *cbs;
+
+ u32 num_mapping_groups;
+ u16 streamid_mask;
+ u16 smr_mask_mask;
+ struct arm_smmu_smr *smrs;
+ struct arm_smmu_s2cr *s2crs;
+ struct arm_smmu_cfg *cfgs;
+
+ unsigned long va_size;
+ unsigned long ipa_size;
+ unsigned long pa_size;
+ unsigned long pgsize_bitmap;
+
+ u32 num_global_irqs;
+ u32 num_context_irqs;
+ unsigned int *irqs;
+
+ spinlock_t global_sync_lock;
+};
+
+enum arm_smmu_context_fmt {
+ ARM_SMMU_CTX_FMT_NONE,
+ ARM_SMMU_CTX_FMT_AARCH64,
+ ARM_SMMU_CTX_FMT_AARCH32_L,
+ ARM_SMMU_CTX_FMT_AARCH32_S,
+};
+
+struct arm_smmu_cfg {
+ u8 cbndx;
+ u8 irptndx;
+ union {
+ u16 asid;
+ u16 vmid;
+ };
+ u32 cbar;
+ enum arm_smmu_context_fmt fmt;
+};
+#define INVALID_IRPTNDX 0xff
+
+enum arm_smmu_domain_stage {
+ ARM_SMMU_DOMAIN_S1 = 0,
+ ARM_SMMU_DOMAIN_S2,
+ ARM_SMMU_DOMAIN_NESTED,
+ ARM_SMMU_DOMAIN_BYPASS,
+};
This enum is even not uses for typechecking. So, simple #defines suffice.
+
+#define s2cr_init_val (struct arm_smmu_s2cr){ \
+ .type = S2CR_TYPE_BYPASS, \
+}
+
+
+static struct arm_smmu_device smmu_device;
+static unsigned long pgsize_bitmap = -1;
+static u16 arm_sid_mask;
Why a global arm_sid_mask when it is configured per SMMU instance?
+
+static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
+{
+ struct arm_smmu_smr *smr = smmu->smrs + idx;
+ u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT;
+
+ if (!(smmu->features & ARM_SMMU_FEAT_EXIDS) && smr->valid)
+ reg |= SMR_VALID;
+ mmio_write32(ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx), reg);
+}
+
+static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
+{
+ struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
+ u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT |
+ (s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT |
+ (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT;
We already have the GET_FIELD macro. Maybe some SET_FIELD would be
useful. It would allow to encode the fields in-place, without #defines.
+
+ if (smmu->features & ARM_SMMU_FEAT_EXIDS && smmu->smrs &&
+ smmu->smrs[idx].valid)
+ reg |= S2CR_EXIDVALID;
+ mmio_write32(ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx), reg);
+}
+
+static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
+{
+ if (smmu->smrs)
+ arm_smmu_write_smr(smmu, idx);
+
+ arm_smmu_write_s2cr(smmu, idx);
+}
+
+/* Wait for any pending TLB invalidations to complete */
+static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu,
+ void *sync, void *status)
+{
+ unsigned int spin_cnt, delay;
+
+ mmio_write32(sync, 0);
+ for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) {
+ for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) {
+ if (!(mmio_read32(status) & sTLBGSTATUS_GSACTIVE))
+ return;
+ cpu_relax();
+ }
+ }
+ printk("TLB sync timed out -- SMMU may be deadlocked\n");
Hmm, end then? Contiue with eyes shut? Shouldn't this error be
propagated? Seems doable, at least for the cases I checked.
+}
+
+static void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu)
+{
+ void *base = ARM_SMMU_GR0(smmu);
+
+ spin_lock(&smmu->global_sync_lock);
+ __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_GR0_sTLBGSYNC,
+ base + ARM_SMMU_GR0_sTLBGSTATUS);
Just fold that function in - single user.
+ spin_unlock(&smmu->global_sync_lock);
+}
+
+#define ARM_SMMU_CB_VMID(cfg) ((cfg)->cbndx + 1)
Unused.
+static void arm_smmu_init_context_bank(struct arm_smmu_device *smmu,
+ struct arm_smmu_cfg *cfg,
+ struct cell *cell)
+{
+ struct arm_smmu_cb *cb = &smmu->cbs[cfg->cbndx];
+ struct paging_structures *pg_structs;
+ unsigned long cell_table;
+ u32 reg;
+
+ cb->cfg = cfg;
+
+ /* VTCR */
+ reg = ARM_64_LPAE_S2_TCR_RES1 |
+ (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) |
+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) |
+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT);
+
+ reg |= (ARM_LPAE_TCR_SL0_LVL_1 << ARM_LPAE_TCR_SL0_SHIFT);
+
+ switch (PAGE_SIZE) {
+ case SZ_4K:
+ reg |= ARM_LPAE_TCR_TG0_4K;
+ break;
+ case SZ_64K:
+ reg |= ARM_LPAE_TCR_TG0_64K;
+ break;
+ }
+
+ switch (smmu->pa_size) {
+ case 32:
+ reg |= (ARM_LPAE_TCR_PS_32_BIT << ARM_LPAE_TCR_PS_SHIFT);
+ break;
+ case 36:
+ reg |= (ARM_LPAE_TCR_PS_36_BIT << ARM_LPAE_TCR_PS_SHIFT);
+ break;
+ case 40:
+ reg |= (ARM_LPAE_TCR_PS_40_BIT << ARM_LPAE_TCR_PS_SHIFT);
+ break;
+ case 42:
+ reg |= (ARM_LPAE_TCR_PS_42_BIT << ARM_LPAE_TCR_PS_SHIFT);
+ break;
+ case 44:
+ reg |= (ARM_LPAE_TCR_PS_44_BIT << ARM_LPAE_TCR_PS_SHIFT);
+ break;
+ case 48:
+ reg |= (ARM_LPAE_TCR_PS_48_BIT << ARM_LPAE_TCR_PS_SHIFT);
+ break;
+ case 52:
+ reg |= (ARM_LPAE_TCR_PS_52_BIT << ARM_LPAE_TCR_PS_SHIFT);
+ break;
+ default:
+ printk("Not supported\n");
+ break;
+ /* TODO */
+ //goto out_free_data;
If this is possible, please return an error and make the caller fail.
+ }
+
+ reg |= (64ULL - smmu->ipa_size) << ARM_LPAE_TCR_T0SZ_SHIFT;
+
+ cb->tcr[0] = reg;
+
+ pg_structs = &cell->arch.mm;
+ cell_table = paging_hvirt2phys(pg_structs->root_table);
+ u64 vttbr = 0;
+
+ vttbr |= (u64)cell->config->id << VTTBR_VMID_SHIFT;
+ vttbr |= (u64)(cell_table & TTBR_MASK);
+ cb->ttbr[0] = vttbr;
+}
+
+static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)
+{
+ u32 reg;
+ struct arm_smmu_cb *cb = &smmu->cbs[idx];
+ struct arm_smmu_cfg *cfg = cb->cfg;
+ void *cb_base, *gr1_base;
+
+ cb_base = ARM_SMMU_CB(smmu, idx);
+
+ /* Unassigned context banks only need disabling */
+ if (!cfg) {
+ mmio_write32(cb_base + ARM_SMMU_CB_SCTLR, 0);
+ return;
+ }
+
+ gr1_base = ARM_SMMU_GR1(smmu);
+
+ /* CBA2R */
+ if (smmu->version > ARM_SMMU_V1) {
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
+ reg = CBA2R_RW64_64BIT;
+ else
+ reg = CBA2R_RW64_32BIT;
+ /* 16-bit VMIDs live in CBA2R */
+ if (smmu->features & ARM_SMMU_FEAT_VMID16)
+ reg |= cfg->vmid << CBA2R_VMID_SHIFT;
+
+ mmio_write32(gr1_base + ARM_SMMU_GR1_CBA2R(idx), reg);
+ }
+
+ /* CBAR */
+ reg = cfg->cbar;
+ if (smmu->version < ARM_SMMU_V2)
+ reg |= cfg->irptndx << CBAR_IRPTNDX_SHIFT;
+
+ /*
+ * Use the weakest shareability/memory types, so they are
+ * overridden by the ttbcr/pte.
+ */
+ if (!(smmu->features & ARM_SMMU_FEAT_VMID16)) {
+ /* 8-bit VMIDs live in CBAR */
+ reg |= cfg->vmid << CBAR_VMID_SHIFT;
+ }
+ mmio_write32(gr1_base + ARM_SMMU_GR1_CBAR(idx), reg);
+
+ /*
+ * TTBCR
+ * We must write this before the TTBRs, since it determines the
+ * access behaviour of some fields (in particular, ASID[15:8]).
+ */
+ mmio_write32(cb_base + ARM_SMMU_CB_TTBCR, cb->tcr[0]);
+
+ /* TTBRs */
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
+ mmio_write32(cb_base + ARM_SMMU_CB_CONTEXTIDR, cfg->asid);
+ mmio_write32(cb_base + ARM_SMMU_CB_TTBR0, cb->ttbr[0]);
+ mmio_write32(cb_base + ARM_SMMU_CB_TTBR1, cb->ttbr[1]);
+ } else {
+ mmio_write64(cb_base + ARM_SMMU_CB_TTBR0, cb->ttbr[0]);
+ }
+
+ /* SCTLR */
+ reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE |
+ SCTLR_M;
+
+ mmio_write32(cb_base + ARM_SMMU_CB_SCTLR, reg);
+}
+
+static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
+{
+ void *gr0_base = ARM_SMMU_GR0(smmu);
+ int i;
+ u32 reg, major;
+
+ /* clear global FSR */
+ reg = mmio_read32(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
+ mmio_write32(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR, reg);
+
+ /*
+ * Reset stream mapping groups: Initial values mark all SMRn as
+ * invalid and all S2CRn as bypass unless overridden.
+ */
+ for (i = 0; i < smmu->num_mapping_groups; ++i)
+ arm_smmu_write_sme(smmu, i);
+
+ if (smmu->model == ARM_MMU500) {
+ /*
+ * Before clearing ARM_MMU500_ACTLR_CPRE, need to
+ * clear CACHE_LOCK bit of ACR first. And, CACHE_LOCK
+ * bit is only present in MMU-500r2 onwards.
+ */
+ reg = mmio_read32(gr0_base + ARM_SMMU_GR0_ID7);
+ major = (reg >> ID7_MAJOR_SHIFT) & ID7_MAJOR_MASK;
+ reg = mmio_read32(gr0_base + ARM_SMMU_GR0_sACR);
+ if (major >= 2)
+ reg &= ~ARM_MMU500_ACR_CACHE_LOCK;
+ /*
+ * Allow unmatched Stream IDs to allocate bypass
+ * TLB entries for reduced latency.
+ */
+ reg |= ARM_MMU500_ACR_SMTNMB_TLBEN | ARM_MMU500_ACR_S2CRB_TLBEN;
+ mmio_write32(gr0_base + ARM_SMMU_GR0_sACR, reg);
+ }
+
+ /* Make sure all context banks are disabled and clear CB_FSR */
+ for (i = 0; i < smmu->num_context_banks; ++i) {
+ void *cb_base = ARM_SMMU_CB(smmu, i);
+
+ arm_smmu_write_context_bank(smmu, i);
+ mmio_write32(cb_base + ARM_SMMU_CB_FSR, FSR_FAULT);
+ /*
+ * Disable MMU-500's not-particularly-beneficial next-page
+ * prefetcher for the sake of errata #841119 and #826419.
+ */
+ if (smmu->model == ARM_MMU500) {
+ reg = mmio_read32(cb_base + ARM_SMMU_CB_ACTLR);
+ reg &= ~ARM_MMU500_ACTLR_CPRE;
+ mmio_write32(cb_base + ARM_SMMU_CB_ACTLR, reg);
+ }
+ }
+
+ /* Invalidate the TLB, just in case */
+ mmio_write32(gr0_base + ARM_SMMU_GR0_TLBIALLH, 0);
+ mmio_write32(gr0_base + ARM_SMMU_GR0_TLBIALLNSNH, 0);
+
+ reg = mmio_read32(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
+
+ /* Enable fault reporting */
+ reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
+
+ /* Disable TLB broadcasting. */
+ reg |= (sCR0_VMIDPNE | sCR0_PTM);
+
+ /* Enable client access, handling unmatched streams as appropriate */
+ reg &= ~sCR0_CLIENTPD;
+ reg &= ~sCR0_USFCFG;
+
+ /* Disable forced broadcasting */
+ reg &= ~sCR0_FB;
+
+ /* Don't upgrade barriers */
+ reg &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT);
+
+ if (smmu->features & ARM_SMMU_FEAT_VMID16)
+ reg |= sCR0_VMID16EN;
+
+ if (smmu->features & ARM_SMMU_FEAT_EXIDS)
+ reg |= sCR0_EXIDENABLE;
+
+ /* Push the button */
+ arm_smmu_tlb_sync_global(smmu);
+ mmio_write32(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0, reg);
+}
+
+static int arm_smmu_id_size_to_bits(int size)
+{
+ switch (size) {
+ case 0:
+ return 32;
+ case 1:
+ return 36;
+ case 2:
+ return 40;
+ case 3:
+ return 42;
+ case 4:
+ return 44;
+ case 5:
+ default:
+ return 48;
+ }
+}
+
+static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
+{
+ unsigned long size;
+ void *gr0_base = ARM_SMMU_GR0(smmu);
+ u32 id;
+ bool cttw_reg, cttw_fw = smmu->features & ARM_SMMU_FEAT_COHERENT_WALK;
+ int i;
+
+ printk("probing hardware configuration...\n");
+ printk("SMMUv%d with:\n", smmu->version == ARM_SMMU_V2 ? 2 : 1);
+
+ /* ID0 */
+ id = mmio_read32(gr0_base + ARM_SMMU_GR0_ID0);
+
+ /* Restrict stage 2 */
+ id &= ~(ID0_S1TS | ID0_NTS);
+
+ if (id & ID0_S2TS) {
+ smmu->features |= ARM_SMMU_FEAT_TRANS_S2;
+ printk("\tstage 2 translation\n");
+ }
+
+ if (!(smmu->features &
+ (ARM_SMMU_FEAT_TRANS_S1 | ARM_SMMU_FEAT_TRANS_S2))) {
+ printk("\tno translation support!\n");
+ return -ENODEV;
+ }
+
+ /*
+ * In order for DMA API calls to work properly, we must defer to what
+ * the FW says about coherency, regardless of what the hardware claims.
+ * Fortunately, this also opens up a workaround for systems where the
+ * ID register value has ended up configured incorrectly.
+ */
+ cttw_reg = !!(id & ID0_CTTW);
+ if (cttw_fw || cttw_reg)
+ printk("\t%scoherent table walk\n", cttw_fw ? "" : "non-");
+ if (cttw_fw != cttw_reg)
+ printk("\t(IDR0.CTTW overridden by FW configuration)\n");
+
+ /* Max. number of entries we have for stream matching/indexing */
+ if (smmu->version == ARM_SMMU_V2 && id & ID0_EXIDS) {
+ smmu->features |= ARM_SMMU_FEAT_EXIDS;
+ size = 1 << 16;
+ } else {
+ size = 1 << ((id >> ID0_NUMSIDB_SHIFT) & ID0_NUMSIDB_MASK);
+ }
+ smmu->streamid_mask = size - 1;
+
+ if (id & ID0_SMS) {
+ smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH;
+ size = (id >> ID0_NUMSMRG_SHIFT) & ID0_NUMSMRG_MASK;
+ if (size == 0) {
+ printk("stream-matching supported, but no SMRs
present!\n");
+ return -ENODEV;
+ }
+
+ /* Zero-initialised to mark as invalid */
+ smmu->smrs = page_alloc(&mem_pool,
+ PAGES(size * sizeof(*smmu->smrs)));
+ if (!smmu->smrs)
+ return -ENOMEM;
+ memset(smmu->smrs, 0, PAGES(size * sizeof(*smmu->smrs)));
+
+ printk("\tstream matching with %lu register groups", size);
+ }
+ /* s2cr->type == 0 means translation, so initialise explicitly */
+ smmu->s2crs = page_alloc(&mem_pool, PAGES(size * (sizeof(*smmu->s2crs) +
sizeof(*smmu->cfgs))));
+ if (!smmu->s2crs) {
+ page_free(&mem_pool, smmu->smrs,
+ PAGES(size * sizeof(*smmu->smrs)));
If this can happen during cell-create, we should clean up. If this ony
happens during init (jailhouse enable), no need for the rollback, just
return the error.
+ return -ENOMEM;
+ }
+
+ smmu->cfgs = (struct arm_smmu_cfg *)(smmu->s2crs + size);
+
+ /* Configure to bypass */
+ for (i = 0; i < size; i++)
+ smmu->s2crs[i] = s2cr_init_val;
+
+ smmu->num_mapping_groups = size;
+
+ if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {
+ smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L;
+ if (!(id & ID0_PTFS_NO_AARCH32S))
+ smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_S;
+ }
+
+ /* ID1 */
+ id = mmio_read32(gr0_base + ARM_SMMU_GR0_ID1);
+ smmu->pgshift = (id & ID1_PAGESIZE) ? 16 : 12;
+
+ /* Check for size mismatch of SMMU address space from mapped region */
+ size = 1 << (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) +
1);
+ size <<= smmu->pgshift;
+ if (smmu->cb_base != gr0_base + size)
+ printk("SMMU address space size (0x%lx) differs from mapped region
size (0x%tx)!\n",
+ size * 2, (smmu->cb_base - gr0_base) * 2);
...and? Fail?
+
+ smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) &
ID1_NUMS2CB_MASK;
+ smmu->num_context_banks = (id >> ID1_NUMCB_SHIFT) & ID1_NUMCB_MASK;
+ if (smmu->num_s2_context_banks > smmu->num_context_banks) {
+ printk("impossible number of S2 context banks!\n");
+ return -ENODEV;
+ }
+ /* TODO Check More */
HW compatibility? Or what should be checked? Just for my understanding.
+ smmu->num_context_irqs = smmu->num_context_banks;
+
+ printk("\t%u context banks (%u stage-2 only)\n",
+ smmu->num_context_banks, smmu->num_s2_context_banks);
+
+ smmu->cbs = page_alloc(&mem_pool, PAGES(smmu->num_context_banks *
sizeof(*smmu->cbs)));
+ if (!smmu->cbs) {
+ /* TODO: Free smrs s2cr */
See my comment above.
+ return -ENOMEM;
+ }
+
+ /* ID2 */
+ id = mmio_read32(gr0_base + ARM_SMMU_GR0_ID2);
+ size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK);
+ /* Reuse cpu table */
+ smmu->ipa_size = MIN(size, get_cpu_parange());
+
+ /* The output mask is also applied for bypass */
+ size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK);
+ smmu->pa_size = size;
+
+ if (id & ID2_VMID16)
+ smmu->features |= ARM_SMMU_FEAT_VMID16;
+
+ /*
+ * What the page table walker can address actually depends on which
+ * descriptor format is in use, but since a) we don't know that yet,
+ * and b) it can vary per context bank, this will have to do...
+ * TODO: DMA?
???
+ */
+
+ if (smmu->version < ARM_SMMU_V2) {
+ smmu->va_size = smmu->ipa_size;
+ if (smmu->version == ARM_SMMU_V1_64K)
+ smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K;
+ } else {
+ size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
+ smmu->va_size = arm_smmu_id_size_to_bits(size);
+ if (id & ID2_PTFS_4K)
+ smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_4K;
+ if (id & ID2_PTFS_16K)
+ smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_16K;
+ if (id & ID2_PTFS_64K)
+ smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K;
+ }
+
+ /* Now we've corralled the various formats, what'll it do? */
+ if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_S)
+ smmu->pgsize_bitmap |= SZ_4K | SZ_64K | SZ_1M | SZ_16M;
+ if (smmu->features &
+ (ARM_SMMU_FEAT_FMT_AARCH32_L | ARM_SMMU_FEAT_FMT_AARCH64_4K))
+ smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G;
+ if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH64_16K)
+ smmu->pgsize_bitmap |= SZ_16K | SZ_32M;
+ if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH64_64K)
+ smmu->pgsize_bitmap |= SZ_64K | SZ_512M;
+
+ if (pgsize_bitmap == -1UL)
+ pgsize_bitmap = smmu->pgsize_bitmap;
+ else
+ pgsize_bitmap |= smmu->pgsize_bitmap;
+ printk("\tSupported page sizes: 0x%08lx\n", smmu->pgsize_bitmap);
+
+
+ if (smmu->features & ARM_SMMU_FEAT_TRANS_S1)
+ printk("\tStage-1: %lu-bit VA -> %lu-bit IPA\n",
+ smmu->va_size, smmu->ipa_size);
+
+ if (smmu->features & ARM_SMMU_FEAT_TRANS_S2)
+ printk("\tStage-2: %lu-bit IPA -> %lu-bit PA\n",
+ smmu->ipa_size, smmu->pa_size);
+
+ return 0;
+}
+
+static void arm_smmu_test_smr_masks(struct arm_smmu_device *smmu)
+{
+ void *gr0_base = ARM_SMMU_GR0(smmu);
+ u32 smr;
+
+ if (!smmu->smrs)
+ return;
+
+ /*
+ * SMR.ID bits may not be preserved if the corresponding MASK
+ * bits are set, so check each one separately. We can reject
+ * masters later if they try to claim IDs outside these masks.
+ */
+ smr = smmu->streamid_mask << SMR_ID_SHIFT;
+ mmio_write32(gr0_base + ARM_SMMU_GR0_SMR(0), smr);
+ smr = mmio_read32(gr0_base + ARM_SMMU_GR0_SMR(0));
+ smmu->streamid_mask = smr >> SMR_ID_SHIFT;
+
+ smr = smmu->streamid_mask << SMR_MASK_SHIFT;
+ mmio_write32(gr0_base + ARM_SMMU_GR0_SMR(0), smr);
+ smr = mmio_read32(gr0_base + ARM_SMMU_GR0_SMR(0));
+ smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT;
+}
+
+static int arm_smmu_find_sme(u16 id, u16 mask)
+{
+ struct arm_smmu_device *smmu = &smmu_device;
+ struct arm_smmu_smr *smrs = smmu->smrs;
+ int i, free_idx = -EINVAL;
+
+ /* Stream indexing is blissfully easy */
+ if (!smrs)
+ return id;
+
+ /* Validating SMRs is... less so */
+ for (i = 0; i < smmu->num_mapping_groups; ++i) {
+ if (!smrs[i].valid) {
+ /*
+ * Note the first free entry we come across, which
+ * we'll claim in the end if nothing else matches.
+ */
+ if (free_idx < 0)
+ free_idx = i;
+ continue;
+ }
+ /*
+ * If the new entry is _entirely_ matched by an existing entry,
+ * then reuse that, with the guarantee that there also cannot
+ * be any subsequent conflicting entries. In normal use we'd
+ * expect simply identical entries for this case, but there's
+ * no harm in accommodating the generalisation.
+ */
+ if ((mask & smrs[i].mask) == mask &&
+ !((id ^ smrs[i].id) & ~smrs[i].mask)) {
+ return i;
+ }
+ /*
+ * If the new entry has any other overlap with an existing one,
+ * though, then there always exists at least one stream ID
+ * which would cause a conflict, and we can't allow that risk.
+ */
+ if (!((id ^ smrs[i].id) & ~(smrs[i].mask | mask)))
+ return -EINVAL;
+ }
+
+ return free_idx;
+}
+
+static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
+{
+ smmu->s2crs[idx] = s2cr_init_val;
+ if (smmu->smrs) {
+ smmu->smrs[idx].id = 0;
+ smmu->smrs[idx].mask = 0;
+ smmu->smrs[idx].valid = false;
+ }
+
+ return true;
+}
+
+#define for_each_smmu_sid(sid, config, counter)
\
+ for ((sid) = jailhouse_cell_stream_ids(config), (counter) = 0; \
+ (counter) < (config)->num_stream_ids; \
+ (sid)++, (counter)++)
+
+static int arm_smmu_cell_init(struct cell *cell)
+{
+ struct arm_smmu_device *smmu = &smmu_device;
+ enum arm_smmu_s2cr_type type = S2CR_TYPE_TRANS;
+ struct arm_smmu_s2cr *s2cr = smmu->s2crs;
+ struct arm_smmu_cfg *cfg = &smmu->cfgs[cell->config->id];
+ struct arm_smmu_smr *smr;
+ const __u32 *sid;
+ unsigned int n;
+ int ret, idx;
+
+ /* If no sids, ignore */
+ if (!cell->config->num_stream_ids)
+ return 0;
+
+ if (smmu->features & (ARM_SMMU_FEAT_FMT_AARCH64_64K |
+ ARM_SMMU_FEAT_FMT_AARCH64_16K |
+ ARM_SMMU_FEAT_FMT_AARCH64_4K))
+ cfg->fmt = ARM_SMMU_CTX_FMT_AARCH64;
+
+ cfg->cbar = CBAR_TYPE_S2_TRANS;
+
+ /* We use cell->config->id here, one cell use one context */
+ cfg->cbndx = cell->config->id;
+
+ if (smmu->version < ARM_SMMU_V2) {
+ /* TODO */
Error out? Or what is the TODO?
+ } else {
+ cfg->irptndx = cfg->cbndx;
+ }
+
+ cfg->vmid = cfg->cbndx + 1;
+
+ arm_smmu_init_context_bank(smmu, cfg, cell);
+ arm_smmu_write_context_bank(smmu, cfg->cbndx);
+
+ smr = smmu->smrs;
+
+ for_each_smmu_sid(sid, cell->config, n) {
+ ret = arm_smmu_find_sme(*sid, arm_sid_mask);
+ if (ret < 0)
+ printk("arm_smmu_find_sme error %d\n", ret);
return error?
+ idx = ret;
...or use that negative idx? Better not.
+
+ if (type == s2cr[idx].type && cfg->cbndx == s2cr[idx].cbndx)
+ printk("%s error\n", __func__);
return error? Rollback needed? Or impossible?
+
+ s2cr[idx].type = type;
+ s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT;
+ s2cr[idx].cbndx = cfg->cbndx;
+
+ arm_smmu_write_s2cr(smmu, idx);
+
+
+ smr[idx].id = *sid;
+ smr[idx].mask = arm_sid_mask;
+ smr[idx].valid = true;
+
+ arm_smmu_write_smr(smmu, idx);
+ }
+
+ printk("Found %d masters\n", n);
+
+ return 0;
+}
+
+static void arm_smmu_cell_exit(struct cell *cell)
+{
+ const __u32 *sid;
+ struct arm_smmu_device *smmu = &smmu_device;
+ unsigned int n;
+ int ret, idx;
+ int cbndx = cell->config->id;
+
+ if (!cell->config->num_stream_ids)
+ return;
+
+ /* If no sids, ignore */
+ if (cell->config->num_stream_ids) {
+ for_each_smmu_sid(sid, cell->config, n) {
+ ret = arm_smmu_find_sme(*sid, arm_sid_mask);
+ if (ret < 0)
+ printk("arm_smmu_find_sme error %d\n", ret);
+ idx = ret;
Same bug as above.
+
+ if (arm_smmu_free_sme(smmu, idx)) {
+ arm_smmu_write_sme(smmu, idx);
+ }
+
+ smmu->cbs[cbndx].cfg = NULL;
+ arm_smmu_write_context_bank(smmu, cbndx);
+ }
+ }
+}
+
+static int arm_smmu_init(void)
+{
+ int err;
+ __u64 phys_addr;
+ __u32 size;
+ struct arm_smmu_device *smmu = &smmu_device;
+
+ smmu->features &= ~ARM_SMMU_FEAT_COHERENT_WALK;
+
+ phys_addr = system_config->platform_info.arm.iommu_units[0].base;
+ if (!phys_addr) {
+ printk("No SMMU\n");
Won't that also print "No SMMU" on SMMUv3 targets?
+ return 0;
+ }
+
+ size = system_config->platform_info.arm.iommu_units[0].size;
+
+ smmu->version =
+ system_config->platform_info.arm.iommu_units[0].arm_smmu_arch;
+ smmu->model =
+ system_config->platform_info.arm.iommu_units[0].arm_smmu_impl;
+ arm_sid_mask =
+ system_config->platform_info.arm.iommu_units[0].arm_sid_mask;
+ smmu->base = paging_map_device(phys_addr, size);
+ if (!smmu->base)
+ return -ENOMEM;
+
+ smmu->cb_base = smmu->base + size / 2;
+
+ err = arm_smmu_device_cfg_probe(smmu);
+ if (err)
+ return err;
+
+ if (smmu->version == ARM_SMMU_V2 &&
+ smmu->num_context_banks != smmu->num_context_irqs) {
+ printk("found only %d context interrupt(s) but %d required\n",
+ smmu->num_context_irqs, smmu->num_context_banks);
+ /* TODO: page free smr s2cr cbs */
Nope, not needed for hypervisor init.
+ return -ENODEV;
+ }
+
+ /* TODO: request irq */
What will that IRQ do?
+
+ arm_smmu_device_reset(smmu);
+ arm_smmu_test_smr_masks(smmu);
+
+ /*
+ * For ACPI and generic DT bindings, an SMMU will be probed before
+ * any device which might need it, so we want the bus ops in place
+ * ready to handle default domain setup as soon as any SMMU exists.
I suspect that comment comes from Linux and makes no sense here, right?
+ */
+ /* TODO: How handle PCI iommu? */
Valid TODO?
+
+ return arm_smmu_cell_init(&root_cell);
+}
+
+DEFINE_UNIT_MMIO_COUNT_REGIONS_STUB(arm_smmu);
+DEFINE_UNIT_SHUTDOWN_STUB(arm_smmu);
+DEFINE_UNIT(arm_smmu, "ARM SMMU");
diff --git a/include/jailhouse/cell-config.h b/include/jailhouse/cell-config.h
index 30ec5d06..a6a7d8c1 100644
--- a/include/jailhouse/cell-config.h
+++ b/include/jailhouse/cell-config.h
@@ -266,6 +266,10 @@ struct jailhouse_iommu {
__u32 tlb_size;
} __attribute__((packed)) tipvu;
};
+
+ __u32 arm_sid_mask;
+ __u32 arm_smmu_arch;
+ __u32 arm_smmu_impl;
This belongs as one struct into the union - unless there is a system
which also has an AMD IOMMU or a TI PVU... Another advantage: That would
not change the binary layout of the config, and we would not have to
bump its revision (like it is needed now).
Do we have a lot of permutations of arch and impl? Or would multiplying
them out into a single type be reasonable? At least arch should use the
generic type field btw.
} __attribute__((packed));
struct jailhouse_pio {
@@ -290,6 +294,17 @@ struct jailhouse_pio {
#define SYS_FLAGS_VIRTUAL_DEBUG_CONSOLE(flags) \
!!((flags) & JAILHOUSE_SYS_VIRTUAL_DEBUG_CONSOLE)
+enum arm_smmu_arch_version {
+ ARM_SMMU_V1,
+ ARM_SMMU_V1_64K,
+ ARM_SMMU_V2,
JAILHOUSE_IOMMU_SMMUV1...
And please use defines for directly readable values.
+};
+
+enum arm_smmu_implementation {
+ GENERIC_SMMU,
+ ARM_MMU500,
Same here.
+};
+
/**
* General descriptor of the system.
*/
diff --git a/include/jailhouse/sizes.h b/include/jailhouse/sizes.h
new file mode 100644
index 00000000..ce3e8150
--- /dev/null
+++ b/include/jailhouse/sizes.h
@@ -0,0 +1,47 @@
+/*
+ * include/linux/sizes.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LINUX_SIZES_H__
+#define __LINUX_SIZES_H__
+
+#define SZ_1 0x00000001
+#define SZ_2 0x00000002
+#define SZ_4 0x00000004
+#define SZ_8 0x00000008
+#define SZ_16 0x00000010
+#define SZ_32 0x00000020
+#define SZ_64 0x00000040
+#define SZ_128 0x00000080
+#define SZ_256 0x00000100
+#define SZ_512 0x00000200
+
+#define SZ_1K 0x00000400
+#define SZ_2K 0x00000800
+#define SZ_4K 0x00001000
+#define SZ_8K 0x00002000
+#define SZ_16K 0x00004000
+#define SZ_32K 0x00008000
+#define SZ_64K 0x00010000
+#define SZ_128K 0x00020000
+#define SZ_256K 0x00040000
+#define SZ_512K 0x00080000
+
+#define SZ_1M 0x00100000
+#define SZ_2M 0x00200000
+#define SZ_4M 0x00400000
+#define SZ_8M 0x00800000
+#define SZ_16M 0x01000000
+#define SZ_32M 0x02000000
+#define SZ_64M 0x04000000
+#define SZ_128M 0x08000000
+#define SZ_256M 0x10000000
+#define SZ_512M 0x20000000
+
+#define SZ_1G 0x40000000
+#define SZ_2G 0x80000000
+
+#endif /* __LINUX_SIZES_H__ */
That header is worth a separate patch if you see use cases in the config
files or elsewhere. Some small examplary conversion would then be nice.
Otherwise, just take the few defines you use into the smmu module.
Jan
--
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux
--
You received this message because you are subscribed to the Google Groups
"Jailhouse" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/jailhouse-dev/014fcc0a-9754-d785-44de-c6f826a5a478%40siemens.com.