ANDROID: drivers: iommu: pviommu: Add basic driver structure

pviommu is an IOMMU driver that interacts with pKVM paravirtualized
IOMMU through hypercalls(HVC).

The device tree binding for the driver is:
iommu {
        compatible = “pkvm,pv-iommu”;
        handle=<id>;
};

The device tree binding is typically generated from VMM with a
virtual IOMMU ID that the hypervisor translates to an IOMMU.

Where the id is a unique ID for this IOMMU used when talking with the
hypervisor.

This patch adds IOMMU ops handlers with implementation of trivial
functions only and Kconfig for the driver PKVM_PVIOMMU.

Bug: 357781595
Bug: 348382247
Bug: 236685427
Change-Id: Id7fcd2106d7f58fc2822996e9adede419b82b84f
Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
Mostafa Saleh
2023-04-18 13:12:10 +00:00
committed by Carlos Llamas
parent 7a076295e2
commit 9dd94d0919
4 changed files with 231 additions and 0 deletions
@@ -0,0 +1,44 @@
# SPDX-License-Identifier: GPL-2.0-only
%YAML 1.2
---
title: pKVM pvIOMMU
maintainers:
- Mostafa Saleh <smostafa@google.org>
description: |+
pvIOMMU is a paravirtual IOMMU implemented through arm64 hypercalls (HVC).
What distinguishes it from other paravirtual IOMMUs (virtio-iommu)
is that it has very simple transport which makes it easy for
hypervisors with a small TCB as in confidential computing to implement
it without complexity and with lower performance overhead.
pvIOMMU was initially developed for protected KVM (pKVM), but it has no
KVM specific interface, so it can be implemented by other hypervisors
also.
properties:
$nodename:
pattern: "^iommu@[0-9a-f]*"
compatible:
const: pkvm,pviommu
id:
maxItems: 1
'#iommu-cells':
const: 1
required:
- compatible
- id
- '#iommu-cells'
additionalProperties: false
examples:
- |+
iommu@55 {
compatible = "pkvm,pviommu";
id = <0x55>;
#iommu-cells = <1>;
};
+9
View File
@@ -528,4 +528,13 @@ config SPRD_IOMMU
Say Y here if you want to use the multimedia devices listed above.
config PKVM_PVIOMMU
tristate "pKVM pvIOMMU driver"
depends on ARM64
select IOMMU_API
help
Para-virtualised IOMMU driver with pKVM.
Say Y here if you intend to run this kernel as a guest.
endif # IOMMU_SUPPORT
+1
View File
@@ -30,3 +30,4 @@ obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o
obj-$(CONFIG_IOMMU_IOPF) += io-pgfault.o
obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
obj-$(CONFIG_APPLE_DART) += apple-dart.o
obj-$(CONFIG_PKVM_PVIOMMU) += pkvm-pviommu.o
+177
View File
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 Google LLC
* Author: Mostafa Saleh <smostafa@google.com>
*/
#include <linux/of_platform.h>
#include <linux/arm-smccc.h>
#include <linux/iommu.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
struct pviommu {
struct iommu_device iommu;
u32 id;
};
struct pviommu_master {
struct device *dev;
struct pviommu *iommu;
u32 ssid_bits;
};
static int pviommu_map_pages(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t pgsize, size_t pgcount,
int prot, gfp_t gfp, size_t *mapped)
{
return 0;
}
static size_t pviommu_unmap_pages(struct iommu_domain *domain, unsigned long iova,
size_t pgsize, size_t pgcount,
struct iommu_iotlb_gather *gather)
{
return 0;
}
static phys_addr_t pviommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
{
return 0;
}
static void pviommu_domain_free(struct iommu_domain *domain)
{
}
static int pviommu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
return -ENODEV;
}
static struct iommu_domain *pviommu_domain_alloc(unsigned int type)
{
return ERR_PTR(-ENODEV);
}
static struct platform_driver pkvm_pviommu_driver;
static struct pviommu *pviommu_get_by_fwnode(struct fwnode_handle *fwnode)
{
struct device *dev = driver_find_device_by_fwnode(&pkvm_pviommu_driver.driver, fwnode);
put_device(dev);
return dev ? dev_get_drvdata(dev) : NULL;
}
static struct iommu_ops pviommu_ops;
static struct iommu_device *pviommu_probe_device(struct device *dev)
{
struct pviommu_master *master;
struct pviommu *pv = NULL;
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
if (!fwspec)
return ERR_PTR(-ENODEV);
pv = pviommu_get_by_fwnode(fwspec->iommu_fwnode);
if (!pv)
return ERR_PTR(-ENODEV);
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return ERR_PTR(-ENOMEM);
master->dev = dev;
master->iommu = pv;
device_property_read_u32(dev, "pasid-num-bits", &master->ssid_bits);
dev_iommu_priv_set(dev, master);
return &pv->iommu;
}
static void pviommu_release_device(struct device *dev)
{
}
static int pviommu_of_xlate(struct device *dev, const struct of_phandle_args *args)
{
return iommu_fwspec_add_ids(dev, args->args, 1);
}
static struct iommu_group *pviommu_device_group(struct device *dev)
{
if (dev_is_pci(dev))
return pci_device_group(dev);
else
return generic_device_group(dev);
}
static struct iommu_ops pviommu_ops = {
.device_group = pviommu_device_group,
.of_xlate = pviommu_of_xlate,
.probe_device = pviommu_probe_device,
.release_device = pviommu_release_device,
.domain_alloc = pviommu_domain_alloc,
.owner = THIS_MODULE,
.default_domain_ops = &(const struct iommu_domain_ops) {
.attach_dev = pviommu_attach_dev,
.map_pages = pviommu_map_pages,
.unmap_pages = pviommu_unmap_pages,
.iova_to_phys = pviommu_iova_to_phys,
.free = pviommu_domain_free,
}
};
static int pviommu_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pviommu *pv = devm_kmalloc(dev, sizeof(*pv), GFP_KERNEL);
struct device_node *np = pdev->dev.of_node;
int ret;
struct arm_smccc_res res;
ret = of_property_read_u32_index(np, "id", 0, &pv->id);
if (ret) {
dev_err(dev, "Failed to read id from device tree node %d\n", ret);
return ret;
}
arm_smccc_1_1_hvc(ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID, 0, 0, 0, &res);
if (res.a0 < 0)
return -ENODEV;
pviommu_ops.pgsize_bitmap = res.a0;
ret = iommu_device_sysfs_add(&pv->iommu, dev, NULL,
"pviommu.%pa", &pv->id);
ret = iommu_device_register(&pv->iommu, &pviommu_ops, dev);
if (ret) {
dev_err(dev, "Couldn't register %d\n", ret);
iommu_device_sysfs_remove(&pv->iommu);
}
platform_set_drvdata(pdev, pv);
return ret;
}
static const struct of_device_id pviommu_of_match[] = {
{ .compatible = "pkvm,pviommu", },
{ },
};
static struct platform_driver pkvm_pviommu_driver = {
.probe = pviommu_probe,
.driver = {
.name = "pkvm-pviommu",
.of_match_table = pviommu_of_match,
},
};
module_platform_driver(pkvm_pviommu_driver);
MODULE_DESCRIPTION("IOMMU API for pKVM paravirtualized IOMMU");
MODULE_AUTHOR("Mostafa Saleh <smostafa@google.com>");
MODULE_LICENSE("GPL");