| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (C) 2022 - Google LLC |
| * Author: David Brazdil <dbrazdil@google.com> |
| */ |
| |
| #include <linux/kvm_host.h> |
| |
| /* Did all IOMMUs register as expected. */ |
| static bool finalised; |
| |
| static unsigned long dev_to_id(struct device *dev) |
| { |
| /* Use the struct device pointer as a unique identifier. */ |
| return (unsigned long)dev; |
| } |
| |
| int pkvm_iommu_driver_init(u64 drv, void *data, size_t size) |
| { |
| return kvm_call_hyp_nvhe(__pkvm_iommu_driver_init, drv, data, size); |
| } |
| EXPORT_SYMBOL(pkvm_iommu_driver_init); |
| |
| int pkvm_iommu_register(struct device *dev, u64 drv, phys_addr_t pa, |
| size_t size, struct device *parent, u8 flags) |
| { |
| void *mem; |
| int ret; |
| |
| /* |
| * Hypcall to register the device. It will return -ENOMEM if it needs |
| * more memory. In that case allocate a page and retry. |
| * We assume that hyp never allocates more than a page per hypcall. |
| */ |
| ret = kvm_call_hyp_nvhe(__pkvm_iommu_register, dev_to_id(dev), |
| drv, pa, size, dev_to_id(parent), flags, NULL); |
| if (ret == -ENOMEM) { |
| mem = (void *)__get_free_page(GFP_KERNEL); |
| if (!mem) |
| return -ENOMEM; |
| |
| ret = kvm_call_hyp_nvhe(__pkvm_iommu_register, dev_to_id(dev), |
| drv, pa, size, dev_to_id(parent), flags, mem); |
| } |
| return ret; |
| } |
| EXPORT_SYMBOL(pkvm_iommu_register); |
| |
| int pkvm_iommu_suspend(struct device *dev) |
| { |
| return kvm_call_hyp_nvhe(__pkvm_iommu_pm_notify, dev_to_id(dev), |
| PKVM_IOMMU_PM_SUSPEND); |
| } |
| EXPORT_SYMBOL(pkvm_iommu_suspend); |
| |
| int pkvm_iommu_resume(struct device *dev) |
| { |
| return kvm_call_hyp_nvhe(__pkvm_iommu_pm_notify, dev_to_id(dev), |
| PKVM_IOMMU_PM_RESUME); |
| } |
| EXPORT_SYMBOL(pkvm_iommu_resume); |
| |
| int pkvm_iommu_finalize(int err) |
| { |
| finalised = !err; |
| return kvm_call_hyp_nvhe(__pkvm_iommu_finalize, 0); |
| } |
| EXPORT_SYMBOL_GPL(pkvm_iommu_finalize); |
| |
| bool pkvm_iommu_finalized(void) |
| { |
| return finalised; |
| } |