From 308eeb0dd627da37a1e0ac2550be4e5a78e8a0f7 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Thu, 17 Jun 2021 20:58:16 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Copy S2MPU configuration to hyp Create variables in hyp that will hold the DT information about S2MPUs to use by hyp at runtime. Copy the information from EL1 to EL2. The EL1 code computes the size of the data and allocates a sufficient number of pages, which hyp will later take ownership of. Test: builds, boots Bug: 190463801 Change-Id: Ic3d4bfa3ec11f7c2e1b4474910e2f57a62139a75 Signed-off-by: David Brazdil (cherry picked from commit bc80f81582975ba3d19162d3c4ee1e725b9e36bd) Signed-off-by: Mostafa Saleh --- arch/arm64/include/asm/kvm_s2mpu.h | 17 ++++- arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c | 3 + arch/arm64/kvm/iommu/s2mpu.c | 89 +++++++++++++++++++++++---- 3 files changed, 96 insertions(+), 13 deletions(-) diff --git a/arch/arm64/include/asm/kvm_s2mpu.h b/arch/arm64/include/asm/kvm_s2mpu.h index d513c56b2c9c..d1ed76c5f066 100644 --- a/arch/arm64/include/asm/kvm_s2mpu.h +++ b/arch/arm64/include/asm/kvm_s2mpu.h @@ -57,9 +57,24 @@ enum s2mpu_version { }; enum s2mpu_power_state { - S2MPU_POWER_ALWAYS_ON, + S2MPU_POWER_ALWAYS_ON = 0, S2MPU_POWER_ON, S2MPU_POWER_OFF, }; +struct s2mpu { + phys_addr_t pa; + void __iomem *va; + u32 version; + enum s2mpu_power_state power_state; + u32 power_domain_id; + u32 context_cfg_valid_vid; +}; + +extern size_t kvm_nvhe_sym(kvm_hyp_nr_s2mpus); +#define kvm_hyp_nr_s2mpus kvm_nvhe_sym(kvm_hyp_nr_s2mpus) + +extern struct s2mpu *kvm_nvhe_sym(kvm_hyp_s2mpus); +#define kvm_hyp_s2mpus kvm_nvhe_sym(kvm_hyp_s2mpus) + #endif /* __ARM64_KVM_S2MPU_H__ */ diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c index 1731300e0bec..43fc1e693aef 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c @@ -8,4 +8,7 @@ #include +size_t __ro_after_init kvm_hyp_nr_s2mpus; +struct s2mpu __ro_after_init *kvm_hyp_s2mpus; + const struct kvm_iommu_ops kvm_s2mpu_ops = (struct kvm_iommu_ops){}; diff --git a/arch/arm64/kvm/iommu/s2mpu.c b/arch/arm64/kvm/iommu/s2mpu.c index 5ac5544a30c9..6d990c47146c 100644 --- a/arch/arm64/kvm/iommu/s2mpu.c +++ b/arch/arm64/kvm/iommu/s2mpu.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -22,6 +23,14 @@ struct s2mpu_irq_info { void __iomem *va; }; +struct s2mpu_list_entry { + struct list_head list; + struct device *dev; + struct s2mpu info; +}; + +static LIST_HEAD(s2mpu_list); + static irqreturn_t s2mpu_irq_handler(int irq, void *data) { struct s2mpu_irq_info *info = data; @@ -109,15 +118,16 @@ static u32 gen_ctx_cfg_valid_vid(struct platform_device *pdev, | CTX_CFG_ENTRY(7, ctx, ctx_vid[7]); } -static int s2mpu_probe_v9(struct platform_device *pdev, void __iomem *kaddr) +static int s2mpu_probe_v9(struct platform_device *pdev, void __iomem *kaddr, + struct s2mpu *info) { unsigned int num_ctx; - u32 ssmt_valid_vid_bmap, ctx_cfg_valid_vid; + u32 ssmt_valid_vid_bmap; ssmt_valid_vid_bmap = ALL_VIDS_BITMAP; num_ctx = readl_relaxed(kaddr + REG_NS_NUM_CONTEXT) & NUM_CONTEXT_MASK; - ctx_cfg_valid_vid = gen_ctx_cfg_valid_vid(pdev, num_ctx, ssmt_valid_vid_bmap); - if (!ctx_cfg_valid_vid) { + 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; } @@ -168,10 +178,16 @@ static int s2mpu_probe(struct platform_device *pdev) struct resource *res; void __iomem *kaddr; size_t res_size; - enum s2mpu_power_state power_state = S2MPU_POWER_ALWAYS_ON; - u32 version, power_domain_id = 0; + struct s2mpu_list_entry *entry; + struct s2mpu *info; int ret; + entry = devm_kzalloc(&pdev->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + entry->dev = &pdev->dev; + info = &entry->info; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "failed to parse 'reg'"); @@ -191,6 +207,7 @@ static int s2mpu_probe(struct platform_device *pdev) res->start); return -EINVAL; } + info->pa = res->start; res_size = resource_size(res); if (res_size != S2MPU_MMIO_SIZE) { @@ -201,9 +218,9 @@ static int s2mpu_probe(struct platform_device *pdev) } ret = of_property_read_u32(pdev->dev.of_node, "power-domain-id", - &power_domain_id); + &info->power_domain_id); if (!ret) { - power_state = S2MPU_POWER_ON; + info->power_state = S2MPU_POWER_ON; } else if (ret != -EINVAL) { dev_err(&pdev->dev, "failed to parse power-domain-id: %d", ret); return ret; @@ -216,20 +233,23 @@ static int s2mpu_probe(struct platform_device *pdev) */ s2mpu_probe_irq(pdev, kaddr); - version = readl_relaxed(kaddr + REG_NS_VERSION); - switch (version & VERSION_CHECK_MASK) { + 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); + ret = s2mpu_probe_v9(pdev, kaddr, info); if (ret) return ret; break; default: - dev_err(&pdev->dev, "unexpected version 0x%08x", version); + 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++; return 0; } @@ -245,16 +265,61 @@ static struct platform_driver of_driver = { }, }; +static struct s2mpu *alloc_s2mpu_array(void) +{ + unsigned int order; + + order = get_order(kvm_hyp_nr_s2mpus * sizeof(struct s2mpu)); + return (struct s2mpu *)__get_free_pages(GFP_KERNEL, order); +} + +static void free_s2mpu_array(struct s2mpu *array) +{ + unsigned int order; + + order = get_order(kvm_hyp_nr_s2mpus * sizeof(struct s2mpu)); + free_pages((unsigned long)array, order); +} + +static int create_s2mpu_array(struct s2mpu **array) +{ + struct s2mpu_list_entry *entry, *tmp; + size_t i; + + *array = alloc_s2mpu_array(); + if (!*array) + return -ENOMEM; + + /* Copy list to hyp array and destroy the list in the process. */ + i = 0; + list_for_each_entry_safe(entry, tmp, &s2mpu_list, list) { + (*array)[i++] = entry->info; + list_del(&entry->list); + devm_kfree(entry->dev, entry); + } + WARN_ON(i != kvm_hyp_nr_s2mpus); + + kvm_hyp_s2mpus = kern_hyp_va(*array); + return 0; +} + int kvm_s2mpu_init(void) { + struct s2mpu *s2mpus = NULL; int ret; ret = platform_driver_probe(&of_driver, s2mpu_probe); if (ret) goto out; + ret = create_s2mpu_array(&s2mpus); + if (ret) + goto out; + kvm_info("S2MPU driver initialized\n"); out: + if (ret) + free_s2mpu_array(s2mpus); return ret; }