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:
committed by
Carlos Llamas
parent
7a076295e2
commit
9dd94d0919
@@ -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>;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
Reference in New Issue
Block a user