blob: ec72e19c4fb7e3e9ee07fa49eec0058edca4b4bc [file] [log] [blame] [edit]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023 Google LLC
* Author: Mostafa Saleh <smostafa@google.com>
*/
#include <asm/hyp_alloc.h>
#include <asm/kvm_mmu.h>
#include <kvm/iommu.h>
#include <linux/dma-map-ops.h>
#include <linux/kvm_host.h>
struct kvm_iommu_driver *iommu_driver;
extern struct kvm_iommu_ops *kvm_nvhe_sym(kvm_iommu_ops);
int kvm_iommu_register_driver(struct kvm_iommu_driver *kern_ops)
{
BUG_ON(!kern_ops);
iommu_driver = kern_ops;
return 0;
}
EXPORT_SYMBOL(kvm_iommu_register_driver);
int kvm_iommu_init_hyp(struct kvm_iommu_ops *hyp_ops,
struct kvm_hyp_memcache *mc,
unsigned long init_arg)
{
int ret = 0;
BUG_ON(!hyp_ops);
ret = kvm_call_hyp_nvhe(__pkvm_iommu_init, hyp_ops, mc->head, mc->nr_pages, init_arg);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL(kvm_iommu_init_hyp);
int __init kvm_iommu_init_driver(void)
{
if (WARN_ON(!iommu_driver))
return -ENODEV;
/*
* init_driver is optional as the driver already registered it self.
* This call mainly notify the driver we are about to drop privilege.
*/
if (!iommu_driver->init_driver)
return 0;
kvm_hyp_iommu_domains = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
get_order(KVM_IOMMU_DOMAINS_ROOT_SIZE));
kvm_hyp_iommu_domains = kern_hyp_va(kvm_hyp_iommu_domains);
if (!kvm_hyp_iommu_domains) {
kvm_err("No enough mem for IOMMU domains");
return -ENOMEM;
}
return iommu_driver->init_driver();
}
void kvm_iommu_remove_driver(void)
{
if (iommu_driver)
iommu_driver->remove_driver();
}
pkvm_handle_t kvm_get_iommu_id(struct device *dev)
{
return iommu_driver->get_iommu_id(dev);
}
pkvm_handle_t kvm_get_iommu_id_by_of(struct device_node *np)
{
return iommu_driver->get_iommu_id_by_of(np);
}
int pkvm_iommu_suspend(struct device *dev)
{
int device_id = kvm_get_iommu_id(dev);
return kvm_call_hyp_nvhe(__pkvm_host_hvc_pd, device_id, 0);
}
EXPORT_SYMBOL_GPL(pkvm_iommu_suspend);
int pkvm_iommu_resume(struct device *dev)
{
int device_id = kvm_get_iommu_id(dev);
return kvm_call_hyp_nvhe(__pkvm_host_hvc_pd, device_id, 1);
}
EXPORT_SYMBOL_GPL(pkvm_iommu_resume);