LoongArch: add iommu support

Upstream: no

Added iommu support for loongarch

Signed-off-by: Xianglai Li <lixianglai@loongson.cn>
This commit is contained in:
Xianglai Li 2024-08-08 17:13:07 +08:00
parent 013ea94d74
commit 364b05f0da
10 changed files with 1974 additions and 4 deletions

View File

@ -2200,3 +2200,6 @@ CONFIG_RCU_CPU_STALL_TIMEOUT=60
# CONFIG_RCU_TRACE is not set
# CONFIG_STRICT_DEVMEM is not set
# CONFIG_RUNTIME_TESTING_MENU is not set
CONFIG_LOONGARCH_IOMMU=m
CONFIG_CMDLINE_EXTEND=y
CONFIG_CMDLINE="vfio_iommu_type1.allow_unsafe_interrupts=1 nokaslr"

View File

@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Arch specific extensions to struct device
*
* This file is released under the GPLv2
* Copyright (C) 2020 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_DEVICE_H
#define _ASM_LOONGARCH_DEVICE_H
struct dev_archdata {
/* hook for IOMMU specific extension */
void *iommu;
struct bus_dma_region *dma_range_map;
/*
* On some old 7A chipset, dma address is different from physical
* address, the main difference is that node id. For dma address
* node id starts from bit 36, physical node id starts from
* bit 44. The remaining address below node id is the same.
*/
unsigned long dma_node_mask;
unsigned int dma_node_off;
};
struct pdev_archdata {
};
struct dma_domain {
struct list_head node;
const struct dma_map_ops *dma_ops;
int domain_nr;
};
void add_dma_domain(struct dma_domain *domain);
void del_dma_domain(struct dma_domain *domain);
#endif /* _ASM_LOONGARCH_DEVICE_H*/

View File

@ -35,6 +35,7 @@ config KVM
select SCHED_INFO
select MMU_NOTIFIER
select PREEMPT_NOTIFIERS
select KVM_VFIO
help
Support hosting virtualized guest machines using
hardware virtualization extensions. You will need

View File

@ -150,7 +150,7 @@ config OF_IOMMU
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
def_bool ARM64 || IA64 || X86
def_bool ARM64 || IA64 || X86 || LOONGARCH
select DMA_OPS
select IOMMU_API
select IOMMU_IOVA
@ -498,4 +498,17 @@ config SPRD_IOMMU
Say Y here if you want to use the multimedia devices listed above.
# LOONGARCH IOMMU support
config LOONGARCH_IOMMU
tristate "LOONGARCH IOMMU support"
select IOMMU_API
select IOMMU_DEFAULT_PASSTHROUGH
depends on LOONGARCH
help
With this option you can enable support for LOONGARCH IOMMU hardware in
your system. An IOMMU is a hardware component which provides
remapping of DMA memory accesses from devices. With an LOONGARCH IOMMU you
can isolate the DMA memory of different devices and protect the
system from misbehaving device drivers or hardware.
endif # IOMMU_SUPPORT

View File

@ -30,3 +30,4 @@ obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o io-pgfault.o
obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
obj-$(CONFIG_APPLE_DART) += apple-dart.o
obj-$(CONFIG_LOONGARCH_IOMMU) += loongarch_iommu.o

View File

@ -1743,7 +1743,7 @@ static size_t iommu_dma_max_mapping_size(struct device *dev)
return SIZE_MAX;
}
static const struct dma_map_ops iommu_dma_ops = {
static const struct dma_map_ops iommu_dmafops = {
.flags = DMA_F_PCI_P2PDMA_SUPPORTED,
.alloc = iommu_dma_alloc,
.free = iommu_dma_free,
@ -1786,7 +1786,7 @@ void iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 dma_limit)
if (iommu_is_dma_domain(domain)) {
if (iommu_dma_init_domain(domain, dma_base, dma_limit, dev))
goto out_err;
dev->dma_ops = &iommu_dma_ops;
dev->dma_ops = &iommu_dmafops;
}
return;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,184 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Loongson IOMMU Driver
*
* Copyright (C) 2020-2021 Loongson Technology Ltd.
* Author: Lv Chen <lvchen@loongson.cn>
* Wang Yang <wangyang@loongson.cn>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef LOONGARCH_IOMMU_H
#define LOONGARCH_IOMMU_H
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/iommu.h>
#include <linux/sizes.h>
#include <asm/addrspace.h>
#include <linux/io.h>
#define IOVA_WIDTH 47
/* Bit value definition for I/O PTE fields */
#define IOMMU_PTE_PR (1ULL << 0) /* Present */
#define IOMMU_PTE_HP (1ULL << 1) /* HugePage */
#define IOMMU_PTE_IR (1ULL << 2) /* Readable */
#define IOMMU_PTE_IW (1ULL << 3) /* Writeable */
#define IOMMU_PTE_RW (IOMMU_PTE_PR | IOMMU_PTE_IR | IOMMU_PTE_IW)
#define iommu_pte_present(ptep) ((*ptep != 0))
#define iommu_pte_huge(ptep) ((*ptep) & IOMMU_PTE_HP)
#define LA_IOMMU_PGSIZE (SZ_16K | SZ_32M)
#define IOMMU_PT_LEVEL0 0x00
#define IOMMU_PT_LEVEL1 0x01
/* IOMMU page table */
#define IOMMU_PAGE_SHIFT PAGE_SHIFT
#define IOMMU_PAGE_SIZE (_AC(1, UL) << IOMMU_PAGE_SHIFT)
#define IOMMU_LEVEL_STRIDE (IOMMU_PAGE_SHIFT - 3)
#define IOMMU_PTRS_PER_LEVEL (IOMMU_PAGE_SIZE >> 3)
#define IOMMU_LEVEL_SHIFT(n) (((n) * IOMMU_LEVEL_STRIDE) + IOMMU_PAGE_SHIFT)
#define IOMMU_LEVEL_SIZE(n) (_AC(1, UL) << (((n) * IOMMU_LEVEL_STRIDE) + IOMMU_PAGE_SHIFT))
#define IOMMU_LEVEL_MASK(n) (~(IOMMU_LEVEL_SIZE(n) - 1))
#define IOMMU_LEVEL_MAX DIV_ROUND_UP((IOVA_WIDTH - IOMMU_PAGE_SHIFT), IOMMU_LEVEL_STRIDE)
#define IOMMU_PAGE_MASK (~(IOMMU_PAGE_SIZE - 1))
#define IOMMU_HPAGE_SIZE (1UL << IOMMU_LEVEL_SHIFT(IOMMU_PT_LEVEL1))
#define IOMMU_HPAGE_MASK (~(IOMMU_HPAGE_SIZE - 1))
/* wired | index | domain | shift */
#define LA_IOMMU_WIDS 0x10
/* valid | busy | tlbar/aw | cmd */
#define LA_IOMMU_VBTC 0x14
#define IOMMU_PGTABLE_BUSY (1 << 16)
/* enable |index | valid | domain | bdf */
#define LA_IOMMU_EIVDB 0x18
/* enable | valid | cmd */
#define LA_IOMMU_CMD 0x1C
#define LA_IOMMU_PGD0_LO 0x20
#define LA_IOMMU_PGD0_HI 0x24
#define STEP_PGD 0x8
#define STEP_PGD_SHIFT 3
#define LA_IOMMU_PGD_LO(domain_id) \
(LA_IOMMU_PGD0_LO + ((domain_id) << STEP_PGD_SHIFT))
#define LA_IOMMU_PGD_HI(domain_id) \
(LA_IOMMU_PGD0_HI + ((domain_id) << STEP_PGD_SHIFT))
#define LA_IOMMU_DIR_CTRL0 0xA0
#define LA_IOMMU_DIR_CTRL1 0xA4
#define LA_IOMMU_DIR_CTRL(x) (LA_IOMMU_DIR_CTRL0 + ((x) << 2))
#define LA_IOMMU_SAFE_BASE_HI 0xE0
#define LA_IOMMU_SAFE_BASE_LO 0xE4
#define LA_IOMMU_EX_ADDR_LO 0xE8
#define LA_IOMMU_EX_ADDR_HI 0xEC
#define LA_IOMMU_PFM_CNT_EN 0x100
#define LA_IOMMU_RD_HIT_CNT_0 0x110
#define LA_IOMMU_RD_MISS_CNT_O 0x114
#define LA_IOMMU_WR_HIT_CNT_0 0x118
#define LA_IOMMU_WR_MISS_CNT_0 0x11C
#define LA_IOMMU_RD_HIT_CNT_1 0x120
#define LA_IOMMU_RD_MISS_CNT_1 0x124
#define LA_IOMMU_WR_HIT_CNT_1 0x128
#define LA_IOMMU_WR_MISS_CNT_1 0x12C
#define LA_IOMMU_RD_HIT_CNT_2 0x130
#define LA_IOMMU_RD_MISS_CNT_2 0x134
#define LA_IOMMU_WR_HIT_CNT_2 0x138
#define LA_IOMMU_WR_MISS_CNT_2 0x13C
#define MAX_DOMAIN_ID 16
#define MAX_ATTACHED_DEV_ID 16
#define iommu_ptable_end(addr, end, level) \
({ unsigned long __boundary = ((addr) + IOMMU_LEVEL_SIZE(level)) & \
IOMMU_LEVEL_MASK(level); \
(__boundary - 1 < (end) - 1) ? __boundary : (end); \
})
/* To find an entry in an iommu page table directory */
#define iommu_page_index(addr, level) \
(((addr) >> ((level * IOMMU_LEVEL_STRIDE) + IOMMU_PAGE_SHIFT)) \
& (IOMMU_PTRS_PER_LEVEL - 1))
struct loongarch_iommu {
struct list_head list; /* for la_iommu_list */
spinlock_t domain_bitmap_lock; /* Lock for domain allocing */
spinlock_t dom_info_lock; /* Lock for dom_list */
void *domain_bitmap; /* Bitmap of global domains */
void *devtable_bitmap; /* Bitmap of devtable */
struct list_head dom_list; /* List of all domain privates */
/* PCI device id of the IOMMU device */
u16 devid;
int segment; /* PCI segment# */
/* iommu configures the register space base address */
void *confbase;
/* iommu configures the register space physical base address */
resource_size_t confbase_phy;
/* iommu configures the register space size */
resource_size_t conf_size;
struct pci_dev *pdev;
/* Handle for IOMMU core code */
struct iommu_device iommu_dev;
} loongarch_iommu;
struct iommu_rlookup_entry {
struct list_head list;
struct loongarch_iommu **rlookup_table;
int pcisegment;
};
struct iommu_info {
struct list_head list; /* for dom_info->iommu_devlist */
struct loongarch_iommu *iommu;
spinlock_t devlock; /* priv dev list lock */
struct list_head dev_list; /* List of all devices in this domain iommu */
unsigned int dev_cnt; /* devices assigned to this domain iommu */
short id;
} iommu_info;
/* One vm is equal to a domain,one domain has a priv */
struct dom_info {
struct list_head iommu_devlist;
struct iommu_domain domain;
struct mutex ptl_lock; /* Lock for page table */
void *pgd;
spinlock_t lock; /* Lock for dom_info->iommu_devlist */
} dom_info;
struct dom_entry {
struct list_head list; /* for loongarch_iommu->dom_list */
struct dom_info *domain_info;
} dom_entry;
/* A device for passthrough */
struct la_iommu_dev_data {
struct list_head list; /* for iommu_entry->dev_list */
struct loongarch_iommu *iommu;
struct iommu_info *iommu_entry;
struct iommu_domain *domain;
struct device *dev;
unsigned short bdf;
int count;
int index; /* index in device table */
};
static inline unsigned long *iommu_pte_offset(unsigned long *ptep, unsigned long addr, int level)
{
return ptep + iommu_page_index(addr, level);
}
#endif /* LOONGARCH_IOMMU_H */

View File

@ -4457,6 +4457,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9000,
quirk_bridge_cavm_thrx2_pcie_root);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9084,
quirk_bridge_cavm_thrx2_pcie_root);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c09,
quirk_bridge_cavm_thrx2_pcie_root);
/*
* Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero)
@ -5177,6 +5179,8 @@ static const struct pci_dev_acs_enabled {
{ PCI_VENDOR_ID_ZHAOXIN, PCI_ANY_ID, pci_quirk_zhaoxin_pcie_ports_acs },
/* Wangxun nics */
{ PCI_VENDOR_ID_WANGXUN, PCI_ANY_ID, pci_quirk_wangxun_nic_acs },
{ PCI_VENDOR_ID_LOONGSON, 0x3c09, pci_quirk_xgene_acs},
{ PCI_VENDOR_ID_LOONGSON, 0x3c19, pci_quirk_xgene_acs},
{ 0 }
};

View File

@ -39,7 +39,7 @@ config VFIO_GROUP
config VFIO_CONTAINER
bool "Support for the VFIO container /dev/vfio/vfio"
select VFIO_IOMMU_TYPE1 if MMU && (X86 || S390 || ARM || ARM64)
select VFIO_IOMMU_TYPE1 if MMU && (X86 || S390 || ARM || ARM64 || LOONGARCH)
depends on VFIO_GROUP
default y
help