blob: c47ed8bc8ef6bdde74a8ba5788a95fe5e5fba425 [file] [log] [blame] [edit]
// SPDX-License-Identifier: GPL-2.0
/*
* pKVM host driver for the Arm SMMUv3
*
* Copyright (C) 2022 Linaro Ltd.
*/
#include <asm/kvm_mmu.h>
#include <linux/local_lock.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <kvm/arm_smmu_v3.h>
#include <linux/kvm_host.h>
#include "arm-smmu-v3.h"
struct host_arm_smmu_device {
struct arm_smmu_device smmu;
pkvm_handle_t id;
u32 boot_gbpa;
unsigned int pgd_order_s1;
unsigned int pgd_order_s2;
atomic_t initialized;
};
#define smmu_to_host(_smmu) \
container_of(_smmu, struct host_arm_smmu_device, smmu);
struct kvm_arm_smmu_master {
struct arm_smmu_device *smmu;
struct device *dev;
struct kvm_arm_smmu_domain *domain;
u32 ssid_bits;
};
struct kvm_arm_smmu_domain {
struct iommu_domain domain;
struct arm_smmu_device *smmu;
struct mutex init_mutex;
unsigned long pgd;
pkvm_handle_t id;
};
#define to_kvm_smmu_domain(_domain) \
container_of(_domain, struct kvm_arm_smmu_domain, domain)
static size_t kvm_arm_smmu_cur;
static ssize_t kvm_arm_smmu_count;
static struct hyp_arm_smmu_v3_device *kvm_arm_smmu_array;
static struct kvm_hyp_iommu_memcache *kvm_arm_smmu_memcache;
static DEFINE_IDA(kvm_arm_smmu_domain_ida);
static DEFINE_PER_CPU(local_lock_t, memcache_lock) =
INIT_LOCAL_LOCK(memcache_lock);
extern struct kvm_iommu_ops kvm_nvhe_sym(smmu_ops);
static void *kvm_arm_smmu_alloc_page(void *opaque)
{
struct arm_smmu_device *smmu = opaque;
struct page *p;
p = alloc_pages_node(dev_to_node(smmu->dev), GFP_ATOMIC, 0);
if (!p)
return NULL;
return page_address(p);
}
static void kvm_arm_smmu_free_page(void *va, void *opaque)
{
free_page((unsigned long)va);
}
static phys_addr_t kvm_arm_smmu_host_pa(void *va)
{
return __pa(va);
}
static void *kvm_arm_smmu_host_va(phys_addr_t pa)
{
return __va(pa);
}
static int kvm_arm_smmu_topup_memcache(struct arm_smmu_device *smmu)
{
struct kvm_hyp_memcache *mc;
int cpu = raw_smp_processor_id();
lockdep_assert_held(this_cpu_ptr(&memcache_lock));
mc = &kvm_arm_smmu_memcache[cpu].pages;
if (!kvm_arm_smmu_memcache[cpu].needs_page)
return -EBADE;
kvm_arm_smmu_memcache[cpu].needs_page = false;
return __topup_hyp_memcache(mc, 1, kvm_arm_smmu_alloc_page,
kvm_arm_smmu_host_pa, smmu);
}
static pkvm_handle_t kvm_arm_smmu_v3_id(struct device *dev)
{
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
struct host_arm_smmu_device *host_smmu = smmu_to_host(smmu);
return host_smmu->id;
}
static void kvm_arm_smmu_reclaim_memcache(void)
{
struct kvm_hyp_memcache *mc;
int cpu = raw_smp_processor_id();
lockdep_assert_held(this_cpu_ptr(&memcache_lock));
mc = &kvm_arm_smmu_memcache[cpu].pages;
__free_hyp_memcache(mc, kvm_arm_smmu_free_page,
kvm_arm_smmu_host_va, NULL);
}
/*
* Issue hypercall, and retry after filling the memcache if necessary.
* After the call, reclaim pages pushed in the memcache by the hypervisor.
*/
#define kvm_call_hyp_nvhe_mc(smmu, ...) \
({ \
int __ret; \
do { \
__ret = kvm_call_hyp_nvhe(__VA_ARGS__); \
} while (__ret && !kvm_arm_smmu_topup_memcache(smmu)); \
kvm_arm_smmu_reclaim_memcache(); \
__ret; \
})
static struct platform_driver kvm_arm_smmu_driver;
static struct arm_smmu_device *
kvm_arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
{
struct device *dev;
dev = driver_find_device_by_fwnode(&kvm_arm_smmu_driver.driver, fwnode);
put_device(dev);
return dev ? dev_get_drvdata(dev) : NULL;
}
static struct iommu_ops kvm_arm_smmu_ops;
static struct iommu_device *kvm_arm_smmu_probe_device(struct device *dev)
{
int ret;
struct arm_smmu_device *smmu;
struct kvm_arm_smmu_master *master;
struct host_arm_smmu_device *host_smmu;
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
if (!fwspec || fwspec->ops != &kvm_arm_smmu_ops)
return ERR_PTR(-ENODEV);
if (WARN_ON_ONCE(dev_iommu_priv_get(dev)))
return ERR_PTR(-EBUSY);
smmu = kvm_arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
if (!smmu)
return ERR_PTR(-ENODEV);
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return ERR_PTR(-ENOMEM);
master->dev = dev;
master->smmu = smmu;
device_property_read_u32(dev, "pasid-num-bits", &master->ssid_bits);
master->ssid_bits = min(smmu->ssid_bits, master->ssid_bits);
dev_iommu_priv_set(dev, master);
if (!device_link_add(dev, smmu->dev,
DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE |
DL_FLAG_AUTOREMOVE_SUPPLIER)) {
ret = -ENOLINK;
goto err_free;
}
/*
* If the SMMU has just been initialized by the hypervisor, release the
* extra PM reference taken by kvm_arm_smmu_probe(). Not sure yet how
* to improve this. Maybe have KVM call us back when it finished
* initializing?
*/
host_smmu = smmu_to_host(smmu);
if (atomic_add_unless(&host_smmu->initialized, 1, 1))
pm_runtime_put_noidle(smmu->dev);
return &smmu->iommu;
err_free:
kfree(master);
return ERR_PTR(ret);
}
static void kvm_arm_smmu_release_device(struct device *dev)
{
struct kvm_arm_smmu_master *master = dev_iommu_priv_get(dev);
kfree(master);
iommu_fwspec_free(dev);
}
static struct iommu_domain *kvm_arm_smmu_domain_alloc(unsigned type)
{
struct kvm_arm_smmu_domain *kvm_smmu_domain;
/*
* We don't support
* - IOMMU_DOMAIN_IDENTITY because we rely on the host telling the
* hypervisor which pages are used for DMA.
* - IOMMU_DOMAIN_DMA_FQ because lazy unmap would clash with memory
* donation to guests.
*/
if (type != IOMMU_DOMAIN_DMA &&
type != IOMMU_DOMAIN_UNMANAGED)
return NULL;
kvm_smmu_domain = kzalloc(sizeof(*kvm_smmu_domain), GFP_KERNEL);
if (!kvm_smmu_domain)
return NULL;
mutex_init(&kvm_smmu_domain->init_mutex);
return &kvm_smmu_domain->domain;
}
static int kvm_arm_smmu_domain_finalize(struct kvm_arm_smmu_domain *kvm_smmu_domain,
struct kvm_arm_smmu_master *master)
{
int ret = 0;
struct page *p;
unsigned long pgd;
struct arm_smmu_device *smmu = master->smmu;
struct host_arm_smmu_device *host_smmu = smmu_to_host(smmu);
if (kvm_smmu_domain->smmu) {
if (kvm_smmu_domain->smmu != smmu)
return -EINVAL;
return 0;
}
ret = ida_alloc_range(&kvm_arm_smmu_domain_ida, 0, 1 << (smmu->vmid_bits - 1),
GFP_KERNEL);
if (ret < 0)
return ret;
kvm_smmu_domain->id = ret;
/*
* PGD allocation does not use the memcache because it may be of higher
* order when concatenated.
*/
p = alloc_pages_node(dev_to_node(smmu->dev), GFP_KERNEL | __GFP_ZERO,
host_smmu->pgd_order_s1);
if (!p)
return -ENOMEM;
pgd = (unsigned long)page_to_virt(p);
/* TODO: choose s1 or s2 based on master->pasid_bits? */
local_lock_irq(&memcache_lock);
ret = kvm_call_hyp_nvhe_mc(smmu, __pkvm_host_iommu_alloc_domain,
host_smmu->id, kvm_smmu_domain->id, pgd, ARM_64_LPAE_S1);
local_unlock_irq(&memcache_lock);
if (ret)
goto err_free;
kvm_smmu_domain->domain.pgsize_bitmap = smmu->pgsize_bitmap;
kvm_smmu_domain->domain.geometry.aperture_end = (1UL << smmu->ias) - 1;
kvm_smmu_domain->domain.geometry.force_aperture = true;
kvm_smmu_domain->smmu = smmu;
kvm_smmu_domain->pgd = pgd;
return 0;
err_free:
free_pages(pgd, host_smmu->pgd_order_s1);
ida_free(&kvm_arm_smmu_domain_ida, kvm_smmu_domain->id);
return ret;
}
static void kvm_arm_smmu_domain_free(struct iommu_domain *domain)
{
int ret;
struct kvm_arm_smmu_domain *kvm_smmu_domain = to_kvm_smmu_domain(domain);
struct arm_smmu_device *smmu = kvm_smmu_domain->smmu;
if (smmu) {
struct host_arm_smmu_device *host_smmu = smmu_to_host(smmu);
ret = kvm_call_hyp_nvhe(__pkvm_host_iommu_free_domain,
host_smmu->id, kvm_smmu_domain->id);
/*
* On failure, leak the pgd because it probably hasn't been
* reclaimed by the host.
*/
if (!WARN_ON(ret))
free_pages(kvm_smmu_domain->pgd, host_smmu->pgd_order_s1);
ida_free(&kvm_arm_smmu_domain_ida, kvm_smmu_domain->id);
}
kfree(kvm_smmu_domain);
}
static int kvm_arm_smmu_detach_dev(struct host_arm_smmu_device *host_smmu,
struct kvm_arm_smmu_master *master)
{
int i, ret;
struct arm_smmu_device *smmu = &host_smmu->smmu;
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
if (!master->domain)
return 0;
for (i = 0; i < fwspec->num_ids; i++) {
int sid = fwspec->ids[i];
ret = kvm_call_hyp_nvhe(__pkvm_host_iommu_detach_dev,
host_smmu->id, master->domain->id, sid);
if (ret) {
dev_err(smmu->dev, "cannot detach device %s (0x%x): %d\n",
dev_name(master->dev), sid, ret);
break;
}
}
master->domain = NULL;
return ret;
}
static int kvm_arm_smmu_attach_dev(struct iommu_domain *domain,
struct device *dev)
{
int i, ret;
struct arm_smmu_device *smmu;
struct host_arm_smmu_device *host_smmu;
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct kvm_arm_smmu_master *master = dev_iommu_priv_get(dev);
struct kvm_arm_smmu_domain *kvm_smmu_domain = to_kvm_smmu_domain(domain);
if (!master)
return -ENODEV;
smmu = master->smmu;
host_smmu = smmu_to_host(smmu);
ret = kvm_arm_smmu_detach_dev(host_smmu, master);
if (ret)
return ret;
mutex_lock(&kvm_smmu_domain->init_mutex);
ret = kvm_arm_smmu_domain_finalize(kvm_smmu_domain, master);
mutex_unlock(&kvm_smmu_domain->init_mutex);
if (ret)
return ret;
local_lock_irq(&memcache_lock);
for (i = 0; i < fwspec->num_ids; i++) {
int sid = fwspec->ids[i];
ret = kvm_call_hyp_nvhe_mc(smmu, __pkvm_host_iommu_attach_dev,
host_smmu->id, kvm_smmu_domain->id,
sid, 0, master->ssid_bits);
if (ret) {
dev_err(smmu->dev, "cannot attach device %s (0x%x): %d\n",
dev_name(dev), sid, ret);
goto out_unlock;
}
}
master->domain = kvm_smmu_domain;
out_unlock:
if (ret)
kvm_arm_smmu_detach_dev(host_smmu, master);
local_unlock_irq(&memcache_lock);
return ret;
}
static int kvm_arm_smmu_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)
{
int ret;
unsigned long irqflags;
struct kvm_arm_smmu_domain *kvm_smmu_domain = to_kvm_smmu_domain(domain);
struct arm_smmu_device *smmu = kvm_smmu_domain->smmu;
struct host_arm_smmu_device *host_smmu = smmu_to_host(smmu);
local_lock_irqsave(&memcache_lock, irqflags);
ret = kvm_call_hyp_nvhe_mc(smmu, __pkvm_host_iommu_map_pages,
host_smmu->id, kvm_smmu_domain->id, iova,
paddr, pgsize, pgcount, prot);
local_unlock_irqrestore(&memcache_lock, irqflags);
if (ret)
return ret;
*mapped = pgsize * pgcount;
return 0;
}
static size_t kvm_arm_smmu_unmap_pages(struct iommu_domain *domain,
unsigned long iova, size_t pgsize,
size_t pgcount,
struct iommu_iotlb_gather *iotlb_gather)
{
int ret;
unsigned long irqflags;
struct kvm_arm_smmu_domain *kvm_smmu_domain = to_kvm_smmu_domain(domain);
struct arm_smmu_device *smmu = kvm_smmu_domain->smmu;
struct host_arm_smmu_device *host_smmu = smmu_to_host(smmu);
local_lock_irqsave(&memcache_lock, irqflags);
ret = kvm_call_hyp_nvhe_mc(smmu, __pkvm_host_iommu_unmap_pages,
host_smmu->id, kvm_smmu_domain->id, iova,
pgsize, pgcount);
local_unlock_irqrestore(&memcache_lock, irqflags);
return ret ? 0 : pgsize * pgcount;
}
static phys_addr_t kvm_arm_smmu_iova_to_phys(struct iommu_domain *domain,
dma_addr_t iova)
{
struct kvm_arm_smmu_domain *kvm_smmu_domain = to_kvm_smmu_domain(domain);
struct host_arm_smmu_device *host_smmu = smmu_to_host(kvm_smmu_domain->smmu);
return kvm_call_hyp_nvhe(__pkvm_host_iommu_iova_to_phys, host_smmu->id,
kvm_smmu_domain->id, iova);
}
static struct iommu_ops kvm_arm_smmu_ops = {
.capable = arm_smmu_capable,
.device_group = arm_smmu_device_group,
.of_xlate = arm_smmu_of_xlate,
.probe_device = kvm_arm_smmu_probe_device,
.release_device = kvm_arm_smmu_release_device,
.domain_alloc = kvm_arm_smmu_domain_alloc,
.owner = THIS_MODULE,
.default_domain_ops = &(const struct iommu_domain_ops) {
.attach_dev = kvm_arm_smmu_attach_dev,
.free = kvm_arm_smmu_domain_free,
.map_pages = kvm_arm_smmu_map_pages,
.unmap_pages = kvm_arm_smmu_unmap_pages,
.iova_to_phys = kvm_arm_smmu_iova_to_phys,
}
};
static bool kvm_arm_smmu_validate_features(struct arm_smmu_device *smmu)
{
unsigned long oas;
unsigned int required_features =
ARM_SMMU_FEAT_TT_LE;
unsigned int forbidden_features =
ARM_SMMU_FEAT_STALL_FORCE;
unsigned int keep_features =
ARM_SMMU_FEAT_2_LVL_STRTAB |
ARM_SMMU_FEAT_2_LVL_CDTAB |
ARM_SMMU_FEAT_TT_LE |
ARM_SMMU_FEAT_SEV |
ARM_SMMU_FEAT_COHERENCY |
ARM_SMMU_FEAT_TRANS_S1 |
ARM_SMMU_FEAT_TRANS_S2 |
ARM_SMMU_FEAT_VAX |
ARM_SMMU_FEAT_RANGE_INV;
if (smmu->options & ARM_SMMU_OPT_PAGE0_REGS_ONLY) {
dev_err(smmu->dev, "unsupported layout\n");
return false;
}
if ((smmu->features & required_features) != required_features) {
dev_err(smmu->dev, "missing features 0x%x\n",
required_features & ~smmu->features);
return false;
}
if (smmu->features & forbidden_features) {
dev_err(smmu->dev, "features 0x%x forbidden\n",
smmu->features & forbidden_features);
return false;
}
smmu->features &= keep_features;
/*
* This can be relaxed (although the spec says that OAS "must match
* the system physical address size."), but requires some changes. All
* table and queue allocations must use GFP_DMA* to ensure the SMMU can
* access them.
*/
oas = get_kvm_ipa_limit();
if (smmu->oas < oas) {
dev_err(smmu->dev, "incompatible address size\n");
return false;
}
return true;
}
static int kvm_arm_smmu_device_reset(struct host_arm_smmu_device *host_smmu)
{
int ret;
u32 reg;
struct arm_smmu_device *smmu = &host_smmu->smmu;
reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
if (reg & CR0_SMMUEN)
dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
/* Disable bypass */
host_smmu->boot_gbpa = readl_relaxed(smmu->base + ARM_SMMU_GBPA);
ret = arm_smmu_update_gbpa(smmu, GBPA_ABORT, 0);
if (ret)
return ret;
ret = arm_smmu_device_disable(smmu);
if (ret)
return ret;
/* Stream table */
writeq_relaxed(smmu->strtab_cfg.strtab_base,
smmu->base + ARM_SMMU_STRTAB_BASE);
writel_relaxed(smmu->strtab_cfg.strtab_base_cfg,
smmu->base + ARM_SMMU_STRTAB_BASE_CFG);
/* Command queue */
writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE);
return 0;
}
static int kvm_arm_probe_scmi_pd(struct device_node *scmi_node,
struct kvm_power_domain *pd)
{
int ret;
struct resource res;
struct of_phandle_args args;
pd->type = KVM_POWER_DOMAIN_ARM_SCMI;
ret = of_parse_phandle_with_args(scmi_node, "shmem", NULL, 0, &args);
if (ret)
return ret;
ret = of_address_to_resource(args.np, 0, &res);
if (ret)
goto out_put_nodes;
ret = of_property_read_u32(scmi_node, "arm,smc-id",
&pd->arm_scmi.smc_id);
if (ret)
goto out_put_nodes;
/*
* The shared buffer is unmapped from the host while a request is in
* flight, so it has to be on its own page.
*/
if (!IS_ALIGNED(res.start, SZ_64K) || resource_size(&res) < SZ_64K) {
ret = -EINVAL;
goto out_put_nodes;
}
pd->arm_scmi.shmem_base = res.start;
pd->arm_scmi.shmem_size = resource_size(&res);
out_put_nodes:
of_node_put(args.np);
return ret;
}
/* TODO: Move this. None of it is specific to SMMU */
static int kvm_arm_probe_power_domain(struct device *dev,
struct kvm_power_domain *pd)
{
int ret;
struct device_node *parent;
struct of_phandle_args args;
if (!of_get_property(dev->of_node, "power-domains", NULL))
return 0;
ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells", 0, &args);
if (ret)
return ret;
parent = of_get_parent(args.np);
if (parent && of_device_is_compatible(parent, "arm,scmi-smc") &&
args.args_count > 0) {
pd->arm_scmi.domain_id = args.args[0];
ret = kvm_arm_probe_scmi_pd(parent, pd);
} else {
dev_err(dev, "Unsupported PM method for %pOF\n", args.np);
ret = -EINVAL;
}
of_node_put(parent);
of_node_put(args.np);
return ret;
}
static void *kvm_arm_smmu_alloc_domains(struct arm_smmu_device *smmu)
{
return (void *)devm_get_free_pages(smmu->dev, GFP_KERNEL | __GFP_ZERO,
get_order(KVM_IOMMU_DOMAINS_ROOT_SIZE));
}
static int kvm_arm_smmu_probe(struct platform_device *pdev)
{
int ret;
bool bypass;
struct resource *res;
phys_addr_t mmio_addr;
struct io_pgtable_cfg cfg_s1, cfg_s2;
size_t mmio_size, pgd_size;
struct arm_smmu_device *smmu;
struct device *dev = &pdev->dev;
struct host_arm_smmu_device *host_smmu;
struct hyp_arm_smmu_v3_device *hyp_smmu;
struct kvm_power_domain power_domain = {};
unsigned long ias;
if (kvm_arm_smmu_cur >= kvm_arm_smmu_count)
return -ENOSPC;
hyp_smmu = &kvm_arm_smmu_array[kvm_arm_smmu_cur];
host_smmu = devm_kzalloc(dev, sizeof(*host_smmu), GFP_KERNEL);
if (!host_smmu)
return -ENOMEM;
smmu = &host_smmu->smmu;
smmu->dev = dev;
ret = arm_smmu_fw_probe(pdev, smmu, &bypass);
if (ret || bypass)
return ret ?: -EINVAL;
ret = kvm_arm_probe_power_domain(dev, &power_domain);
if (ret)
return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mmio_size = resource_size(res);
if (mmio_size < SZ_128K) {
dev_err(dev, "unsupported MMIO region size (%pr)\n", res);
return -EINVAL;
}
mmio_addr = res->start;
host_smmu->id = kvm_arm_smmu_cur;
smmu->base = devm_ioremap_resource(dev, res);
if (IS_ERR(smmu->base))
return PTR_ERR(smmu->base);
ret = arm_smmu_device_hw_probe(smmu);
if (ret)
return ret;
if (!kvm_arm_smmu_validate_features(smmu))
return -ENODEV;
ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48;
/*
* SMMU will hold possible configuration for both S1 and S2 as any of them can be chosen
* when a device is attached.
*/
cfg_s1 = (struct io_pgtable_cfg) {
.fmt = ARM_64_LPAE_S1,
.pgsize_bitmap = smmu->pgsize_bitmap,
.ias = min_t(unsigned long, ias, VA_BITS),
.oas = smmu->ias,
.coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENCY,
};
cfg_s2 = (struct io_pgtable_cfg) {
.fmt = ARM_64_LPAE_S2,
.pgsize_bitmap = smmu->pgsize_bitmap,
.ias = smmu->ias,
.oas = smmu->oas,
.coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENCY,
};
/*
* Choose the page and address size. Compute the PGD size as well, so we
* know how much memory to pre-allocate.
*/
ret = io_pgtable_configure(&cfg_s1, &pgd_size);
if (ret)
return ret;
host_smmu->pgd_order_s1 = get_order(pgd_size);
ret = io_pgtable_configure(&cfg_s2, &pgd_size);
if (ret)
return ret;
host_smmu->pgd_order_s2 = get_order(pgd_size);
smmu->pgsize_bitmap = cfg_s1.pgsize_bitmap;
ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, smmu->base,
ARM_SMMU_CMDQ_PROD, ARM_SMMU_CMDQ_CONS,
CMDQ_ENT_DWORDS, "cmdq");
if (ret)
return ret;
ret = arm_smmu_init_strtab(smmu);
if (ret)
return ret;
ret = kvm_arm_smmu_device_reset(host_smmu);
if (ret)
return ret;
hyp_smmu->iommu.domains = kvm_arm_smmu_alloc_domains(smmu);
if (!hyp_smmu->iommu.domains)
return -ENOMEM;
hyp_smmu->iommu.nr_domains = 1 << smmu->vmid_bits;
ret = arm_smmu_register_iommu(smmu, &kvm_arm_smmu_ops, mmio_addr);
if (ret)
return ret;
platform_set_drvdata(pdev, host_smmu);
/* Hypervisor parameters */
hyp_smmu->mmio_addr = mmio_addr;
hyp_smmu->mmio_size = mmio_size;
hyp_smmu->features = smmu->features;
hyp_smmu->iommu.power_domain = power_domain;
hyp_smmu->pgsize_bitmap = smmu->pgsize_bitmap;
hyp_smmu->ias = smmu->ias;
hyp_smmu->oas = smmu->oas;
hyp_smmu->ssid_bits = smmu->ssid_bits;
kvm_arm_smmu_cur++;
/*
* The state of endpoints dictates when the SMMU is powered off. To turn
* the SMMU on and off, a genpd driver uses SCMI over the SMC transport,
* or some other platform-specific SMC. Those power requests are caught
* by the hypervisor, so that the hyp driver doesn't touch the hardware
* state while it is off.
*
* We are making a big assumption here, that TLBs and caches are invalid
* on power on, and therefore we don't need to wake the SMMU when
* modifying page tables, stream tables and context tables. If this
* assumption does not hold on some systems, then we'll need to grab RPM
* reference in map(), attach(), etc, so the hyp driver can send
* invalidations.
*/
hyp_smmu->caches_clean_on_power_on = true;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
/*
* Take a reference to keep the SMMU powered on while the hypervisor
* initializes it.
*/
pm_runtime_resume_and_get(dev);
return 0;
}
static int kvm_arm_smmu_remove(struct platform_device *pdev)
{
struct host_arm_smmu_device *host_smmu = platform_get_drvdata(pdev);
struct arm_smmu_device *smmu = &host_smmu->smmu;
/*
* There was an error during hypervisor setup. The hyp driver may
* have already enabled the device, so disable it.
*/
if (!atomic_read(&host_smmu->initialized))
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
arm_smmu_unregister_iommu(smmu);
arm_smmu_device_disable(smmu);
arm_smmu_update_gbpa(smmu, host_smmu->boot_gbpa, GBPA_ABORT);
return 0;
}
static const struct of_device_id arm_smmu_of_match[] = {
{ .compatible = "arm,smmu-v3", },
{ },
};
static const struct platform_device_id kvm_arm_smmu_ids[] = {
{ .name = "arm-smmu-v3" },
{ },
};
static struct platform_driver kvm_arm_smmu_driver = {
.driver = {
.name = "kvm-arm-smmu-v3",
.of_match_table = arm_smmu_of_match,
},
/*
* Use the id_table method for matching this driver with ACPI. It
* normally relies on the driver name, but that would conflict with the
* original driver.
*/
.id_table = kvm_arm_smmu_ids,
.remove = kvm_arm_smmu_remove,
};
int acpi_iort_count_smmuv3(void);
static int kvm_arm_smmu_array_alloc(void)
{
int smmu_order, mc_order;
struct device_node *np;
kvm_arm_smmu_count = 0;
for_each_compatible_node(np, NULL, "arm,smmu-v3")
kvm_arm_smmu_count++;
if (!kvm_arm_smmu_count)
kvm_arm_smmu_count = acpi_iort_count_smmuv3();
if (kvm_arm_smmu_count <= 0)
return 0;
/* Allocate the parameter list shared with the hypervisor */
smmu_order = get_order(kvm_arm_smmu_count * sizeof(*kvm_arm_smmu_array));
kvm_arm_smmu_array = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
smmu_order);
if (!kvm_arm_smmu_array)
return -ENOMEM;
mc_order = get_order(NR_CPUS * sizeof(*kvm_arm_smmu_memcache));
kvm_arm_smmu_memcache = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
mc_order);
if (!kvm_arm_smmu_memcache)
goto err_free_array;
return 0;
err_free_array:
free_pages((unsigned long)kvm_arm_smmu_array, smmu_order);
return -ENOMEM;
}
static void kvm_arm_smmu_array_free(void)
{
int order;
order = get_order(kvm_arm_smmu_count * sizeof(*kvm_arm_smmu_array));
free_pages((unsigned long)kvm_arm_smmu_array, order);
order = get_order(NR_CPUS * sizeof(*kvm_arm_smmu_memcache));
free_pages((unsigned long)kvm_arm_smmu_memcache, order);
}
/**
* kvm_arm_smmu_v3_init() - Reserve the SMMUv3 for KVM
* Return 0 if all present SMMUv3 were probed successfully, or an error.
* If no SMMU was found, return 0, with a count of 0.
*/
static int kvm_arm_smmu_v3_init(void)
{
int ret;
/*
* Check whether any device owned by the host is behind an SMMU.
*/
ret = kvm_arm_smmu_array_alloc();
if (ret || !kvm_arm_smmu_count)
return ret;
ret = platform_driver_probe(&kvm_arm_smmu_driver, kvm_arm_smmu_probe);
if (ret)
goto err_free;
if (kvm_arm_smmu_cur != kvm_arm_smmu_count) {
/* A device exists but failed to probe */
ret = -EUNATCH;
goto err_free;
}
/*
* These variables are stored in the nVHE image, and won't be accessible
* after KVM initialization. Ownership of kvm_arm_smmu_array will be
* transferred to the hypervisor as well.
*
* kvm_arm_smmu_memcache is shared between hypervisor and host.
*/
kvm_hyp_arm_smmu_v3_smmus = kern_hyp_va(kvm_arm_smmu_array);
kvm_hyp_arm_smmu_v3_count = kvm_arm_smmu_count;
kvm_hyp_iommu_memcaches = kern_hyp_va(kvm_arm_smmu_memcache);
return 0;
err_free:
kvm_arm_smmu_array_free();
return ret;
}
static void kvm_arm_smmu_v3_remove(void)
{
platform_driver_unregister(&kvm_arm_smmu_driver);
}
struct kvm_iommu_driver kvm_smmu_v3_ops = {
.get_iommu_id = kvm_arm_smmu_v3_id,
.init_driver = kvm_arm_smmu_v3_init,
.remove_driver = kvm_arm_smmu_v3_remove
};
static int kvm_arm_smmu_v3_register(void)
{
set_pkvm_iommu_ops(&kvm_smmu_v3_ops, lm_alias(&kvm_nvhe_sym(smmu_ops)));
return 0;
}
core_initcall(kvm_arm_smmu_v3_register);