// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2021 - Google LLC * Author: David Brazdil */ #include #include static int init_s2mpu_driver(void) { static DEFINE_MUTEX(lock); static bool init_done; struct mpt *mpt; unsigned int gb; unsigned long addr; u64 pfn; int ret = 0; mutex_lock(&lock); if (init_done) goto out; /* Allocate a page for driver data. Must fit MPT descriptor. */ BUILD_BUG_ON(sizeof(*mpt) > PAGE_SIZE); addr = __get_free_page(GFP_KERNEL); if (!addr) { ret = -ENOMEM; goto out; } mpt = (struct mpt *)addr; /* Allocate SMPT buffers. */ for_each_gb(gb) { addr = __get_free_pages(GFP_KERNEL, SMPT_ORDER); if (!addr) { ret = -ENOMEM; goto out_free; } mpt->fmpt[gb].smpt = (u32 *)addr; } /* Share MPT descriptor with hyp. */ pfn = __pa(mpt) >> PAGE_SHIFT; ret = kvm_call_hyp_nvhe(__pkvm_host_share_hyp, pfn); if (ret) goto out_free; /* Hypercall to initialize EL2 driver. */ ret = pkvm_iommu_driver_init(PKVM_IOMMU_DRIVER_S2MPU, mpt, sizeof(*mpt)); if (ret) goto out_unshare; init_done = true; out_unshare: WARN_ON(kvm_call_hyp_nvhe(__pkvm_host_unshare_hyp, pfn)); out_free: /* TODO - will driver return the memory? */ if (ret) { for_each_gb(gb) free_pages((unsigned long)mpt->fmpt[gb].smpt, SMPT_ORDER); free_page((unsigned long)mpt); } out: mutex_unlock(&lock); return ret; } int pkvm_iommu_s2mpu_register(struct device *dev, phys_addr_t addr) { int ret; if (!is_protected_kvm_enabled()) return -ENODEV; ret = init_s2mpu_driver(); if (ret) return ret; return pkvm_iommu_register(dev, PKVM_IOMMU_DRIVER_S2MPU, addr, S2MPU_MMIO_SIZE, NULL); } EXPORT_SYMBOL_GPL(pkvm_iommu_s2mpu_register); static int init_sysmmu_sync_driver(void) { static DEFINE_MUTEX(lock); static bool init_done; int ret = 0; mutex_lock(&lock); if (!init_done) { ret = pkvm_iommu_driver_init(PKVM_IOMMU_DRIVER_SYSMMU_SYNC, NULL, 0); init_done = !ret; } mutex_unlock(&lock); return ret; } int pkvm_iommu_sysmmu_sync_register(struct device *dev, phys_addr_t addr, struct device *parent) { int ret; if (!is_protected_kvm_enabled()) return -ENODEV; ret = init_sysmmu_sync_driver(); if (ret) return ret; return pkvm_iommu_register(dev, PKVM_IOMMU_DRIVER_SYSMMU_SYNC, addr + SYSMMU_SYNC_S2_OFFSET, SYSMMU_SYNC_S2_MMIO_SIZE, parent); } EXPORT_SYMBOL_GPL(pkvm_iommu_sysmmu_sync_register);