mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 23:52:08 +02:00
Merge branch 'amd/amd-vi' into next
* amd/amd-vi: iommu/amd: Fix geometry.aperture_end for V2 tables iommu/amd: Wrap debugfs ABI testing symbols snippets in literal code blocks iommu/amd: Add documentation for AMD IOMMU debugfs support iommu/amd: Add debugfs support to dump IRT Table iommu/amd: Add debugfs support to dump device table iommu/amd: Add support for device id user input iommu/amd: Add debugfs support to dump IOMMU command buffer iommu/amd: Add debugfs support to dump IOMMU Capability registers iommu/amd: Add debugfs support to dump IOMMU MMIO registers iommu/amd: Refactor AMD IOMMU debugfs initial setup iommu/amd: Enable PASID and ATS capabilities in the correct order iommu/amd: Add efr[HATS] max v1 page table level iommu/amd: Add HATDis feature support
This commit is contained in:
commit
6ae1477fd3
131
Documentation/ABI/testing/debugfs-amd-iommu
Normal file
131
Documentation/ABI/testing/debugfs-amd-iommu
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
What: /sys/kernel/debug/iommu/amd/iommu<x>/mmio
|
||||
Date: January 2025
|
||||
Contact: Dheeraj Kumar Srivastava <dheerajkumar.srivastava@amd.com>
|
||||
Description:
|
||||
This file provides read/write access for user input. Users specify the
|
||||
MMIO register offset for iommu<x>, and the file outputs the corresponding
|
||||
MMIO register value of iommu<x>
|
||||
|
||||
Example::
|
||||
|
||||
$ echo "0x18" > /sys/kernel/debug/iommu/amd/iommu00/mmio
|
||||
$ cat /sys/kernel/debug/iommu/amd/iommu00/mmio
|
||||
|
||||
Output::
|
||||
|
||||
Offset:0x18 Value:0x000c22000003f48d
|
||||
|
||||
What: /sys/kernel/debug/iommu/amd/iommu<x>/capability
|
||||
Date: January 2025
|
||||
Contact: Dheeraj Kumar Srivastava <dheerajkumar.srivastava@amd.com>
|
||||
Description:
|
||||
This file provides read/write access for user input. Users specify the
|
||||
capability register offset for iommu<x>, and the file outputs the
|
||||
corresponding capability register value of iommu<x>.
|
||||
|
||||
Example::
|
||||
|
||||
$ echo "0x10" > /sys/kernel/debug/iommu/amd/iommu00/capability
|
||||
$ cat /sys/kernel/debug/iommu/amd/iommu00/capability
|
||||
|
||||
Output::
|
||||
|
||||
Offset:0x10 Value:0x00203040
|
||||
|
||||
What: /sys/kernel/debug/iommu/amd/iommu<x>/cmdbuf
|
||||
Date: January 2025
|
||||
Contact: Dheeraj Kumar Srivastava <dheerajkumar.srivastava@amd.com>
|
||||
Description:
|
||||
This file is a read-only output file containing iommu<x> command
|
||||
buffer entries.
|
||||
|
||||
Examples::
|
||||
|
||||
$ cat /sys/kernel/debug/iommu/amd/iommu<x>/cmdbuf
|
||||
|
||||
Output::
|
||||
|
||||
CMD Buffer Head Offset:339 Tail Offset:339
|
||||
0: 00835001 10000001 00003c00 00000000
|
||||
1: 00000000 30000005 fffff003 7fffffff
|
||||
2: 00835001 10000001 00003c01 00000000
|
||||
3: 00000000 30000005 fffff003 7fffffff
|
||||
4: 00835001 10000001 00003c02 00000000
|
||||
5: 00000000 30000005 fffff003 7fffffff
|
||||
6: 00835001 10000001 00003c03 00000000
|
||||
7: 00000000 30000005 fffff003 7fffffff
|
||||
8: 00835001 10000001 00003c04 00000000
|
||||
9: 00000000 30000005 fffff003 7fffffff
|
||||
10: 00835001 10000001 00003c05 00000000
|
||||
11: 00000000 30000005 fffff003 7fffffff
|
||||
[...]
|
||||
|
||||
What: /sys/kernel/debug/iommu/amd/devid
|
||||
Date: January 2025
|
||||
Contact: Dheeraj Kumar Srivastava <dheerajkumar.srivastava@amd.com>
|
||||
Description:
|
||||
This file provides read/write access for user input. Users specify the
|
||||
device ID, which can be used to dump IOMMU data structures such as the
|
||||
interrupt remapping table and device table.
|
||||
|
||||
Example:
|
||||
|
||||
1.
|
||||
::
|
||||
|
||||
$ echo 0000:01:00.0 > /sys/kernel/debug/iommu/amd/devid
|
||||
$ cat /sys/kernel/debug/iommu/amd/devid
|
||||
|
||||
Output::
|
||||
|
||||
0000:01:00.0
|
||||
|
||||
2.
|
||||
::
|
||||
|
||||
$ echo 01:00.0 > /sys/kernel/debug/iommu/amd/devid
|
||||
$ cat /sys/kernel/debug/iommu/amd/devid
|
||||
|
||||
Output::
|
||||
|
||||
0000:01:00.0
|
||||
|
||||
What: /sys/kernel/debug/iommu/amd/devtbl
|
||||
Date: January 2025
|
||||
Contact: Dheeraj Kumar Srivastava <dheerajkumar.srivastava@amd.com>
|
||||
Description:
|
||||
This file is a read-only output file containing the device table entry
|
||||
for the device ID provided in /sys/kernel/debug/iommu/amd/devid.
|
||||
|
||||
Example::
|
||||
|
||||
$ cat /sys/kernel/debug/iommu/amd/devtbl
|
||||
|
||||
Output::
|
||||
|
||||
DeviceId QWORD[3] QWORD[2] QWORD[1] QWORD[0] iommu
|
||||
0000:01:00.0 0000000000000000 20000001373b8013 0000000000000038 6000000114d7b603 iommu3
|
||||
|
||||
What: /sys/kernel/debug/iommu/amd/irqtbl
|
||||
Date: January 2025
|
||||
Contact: Dheeraj Kumar Srivastava <dheerajkumar.srivastava@amd.com>
|
||||
Description:
|
||||
This file is a read-only output file containing valid IRT table entries
|
||||
for the device ID provided in /sys/kernel/debug/iommu/amd/devid.
|
||||
|
||||
Example::
|
||||
|
||||
$ cat /sys/kernel/debug/iommu/amd/irqtbl
|
||||
|
||||
Output::
|
||||
|
||||
DeviceId 0000:01:00.0
|
||||
IRT[0000] 0000000000000020 0000000000000241
|
||||
IRT[0001] 0000000000000020 0000000000000841
|
||||
IRT[0002] 0000000000000020 0000000000002041
|
||||
IRT[0003] 0000000000000020 0000000000008041
|
||||
IRT[0004] 0000000000000020 0000000000020041
|
||||
IRT[0005] 0000000000000020 0000000000080041
|
||||
IRT[0006] 0000000000000020 0000000000200041
|
||||
IRT[0007] 0000000000000020 0000000000800041
|
||||
[...]
|
||||
|
|
@ -28,9 +28,9 @@ void *__init iommu_alloc_4k_pages(struct amd_iommu *iommu,
|
|||
gfp_t gfp, size_t size);
|
||||
|
||||
#ifdef CONFIG_AMD_IOMMU_DEBUGFS
|
||||
void amd_iommu_debugfs_setup(struct amd_iommu *iommu);
|
||||
void amd_iommu_debugfs_setup(void);
|
||||
#else
|
||||
static inline void amd_iommu_debugfs_setup(struct amd_iommu *iommu) {}
|
||||
static inline void amd_iommu_debugfs_setup(void) {}
|
||||
#endif
|
||||
|
||||
/* Needed for interrupt remapping */
|
||||
|
|
@ -42,7 +42,9 @@ int amd_iommu_enable_faulting(unsigned int cpu);
|
|||
extern int amd_iommu_guest_ir;
|
||||
extern enum protection_domain_mode amd_iommu_pgtable;
|
||||
extern int amd_iommu_gpt_level;
|
||||
extern u8 amd_iommu_hpt_level;
|
||||
extern unsigned long amd_iommu_pgsize_bitmap;
|
||||
extern bool amd_iommu_hatdis;
|
||||
|
||||
/* Protection domain ops */
|
||||
void amd_iommu_init_identity_domain(void);
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@
|
|||
#define FEATURE_GA BIT_ULL(7)
|
||||
#define FEATURE_HE BIT_ULL(8)
|
||||
#define FEATURE_PC BIT_ULL(9)
|
||||
#define FEATURE_HATS GENMASK_ULL(11, 10)
|
||||
#define FEATURE_GATS GENMASK_ULL(13, 12)
|
||||
#define FEATURE_GLX GENMASK_ULL(15, 14)
|
||||
#define FEATURE_GAM_VAPIC BIT_ULL(21)
|
||||
|
|
@ -460,6 +461,9 @@
|
|||
/* IOMMU Feature Reporting Field (for IVHD type 10h */
|
||||
#define IOMMU_FEAT_GASUP_SHIFT 6
|
||||
|
||||
/* IOMMU HATDIS for IVHD type 11h and 40h */
|
||||
#define IOMMU_IVHD_ATTR_HATDIS_SHIFT 0
|
||||
|
||||
/* IOMMU Extended Feature Register (EFR) */
|
||||
#define IOMMU_EFR_XTSUP_SHIFT 2
|
||||
#define IOMMU_EFR_GASUP_SHIFT 7
|
||||
|
|
@ -558,7 +562,8 @@ struct amd_io_pgtable {
|
|||
};
|
||||
|
||||
enum protection_domain_mode {
|
||||
PD_MODE_V1 = 1,
|
||||
PD_MODE_NONE,
|
||||
PD_MODE_V1,
|
||||
PD_MODE_V2,
|
||||
};
|
||||
|
||||
|
|
@ -790,6 +795,8 @@ struct amd_iommu {
|
|||
#ifdef CONFIG_AMD_IOMMU_DEBUGFS
|
||||
/* DebugFS Info */
|
||||
struct dentry *debugfs;
|
||||
int dbg_mmio_offset;
|
||||
int dbg_cap_offset;
|
||||
#endif
|
||||
|
||||
/* IOPF support */
|
||||
|
|
@ -890,6 +897,13 @@ struct dev_table_entry {
|
|||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure defining one entry in the command buffer
|
||||
*/
|
||||
struct iommu_cmd {
|
||||
u32 data[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure to sture persistent DTE flags from IVHD
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -11,22 +11,382 @@
|
|||
#include <linux/pci.h>
|
||||
|
||||
#include "amd_iommu.h"
|
||||
#include "../irq_remapping.h"
|
||||
|
||||
static struct dentry *amd_iommu_debugfs;
|
||||
static DEFINE_MUTEX(amd_iommu_debugfs_lock);
|
||||
|
||||
#define MAX_NAME_LEN 20
|
||||
#define OFS_IN_SZ 8
|
||||
#define DEVID_IN_SZ 16
|
||||
|
||||
void amd_iommu_debugfs_setup(struct amd_iommu *iommu)
|
||||
static int sbdf = -1;
|
||||
|
||||
static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *m = filp->private_data;
|
||||
struct amd_iommu *iommu = m->private;
|
||||
int ret;
|
||||
|
||||
iommu->dbg_mmio_offset = -1;
|
||||
|
||||
if (cnt > OFS_IN_SZ)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_mmio_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (iommu->dbg_mmio_offset > iommu->mmio_phys_end - 4) {
|
||||
iommu->dbg_mmio_offset = -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int iommu_mmio_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct amd_iommu *iommu = m->private;
|
||||
u64 value;
|
||||
|
||||
if (iommu->dbg_mmio_offset < 0) {
|
||||
seq_puts(m, "Please provide mmio register's offset\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = readq(iommu->mmio_base + iommu->dbg_mmio_offset);
|
||||
seq_printf(m, "Offset:0x%x Value:0x%016llx\n", iommu->dbg_mmio_offset, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_STORE_ATTRIBUTE(iommu_mmio);
|
||||
|
||||
static ssize_t iommu_capability_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *m = filp->private_data;
|
||||
struct amd_iommu *iommu = m->private;
|
||||
int ret;
|
||||
|
||||
iommu->dbg_cap_offset = -1;
|
||||
|
||||
if (cnt > OFS_IN_SZ)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_cap_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Capability register at offset 0x14 is the last IOMMU capability register. */
|
||||
if (iommu->dbg_cap_offset > 0x14) {
|
||||
iommu->dbg_cap_offset = -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int iommu_capability_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct amd_iommu *iommu = m->private;
|
||||
u32 value;
|
||||
int err;
|
||||
|
||||
if (iommu->dbg_cap_offset < 0) {
|
||||
seq_puts(m, "Please provide capability register's offset in the range [0x00 - 0x14]\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = pci_read_config_dword(iommu->dev, iommu->cap_ptr + iommu->dbg_cap_offset, &value);
|
||||
if (err) {
|
||||
seq_printf(m, "Not able to read capability register at 0x%x\n",
|
||||
iommu->dbg_cap_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
seq_printf(m, "Offset:0x%x Value:0x%08x\n", iommu->dbg_cap_offset, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_STORE_ATTRIBUTE(iommu_capability);
|
||||
|
||||
static int iommu_cmdbuf_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct amd_iommu *iommu = m->private;
|
||||
struct iommu_cmd *cmd;
|
||||
unsigned long flag;
|
||||
u32 head, tail;
|
||||
int i;
|
||||
|
||||
raw_spin_lock_irqsave(&iommu->lock, flag);
|
||||
head = readl(iommu->mmio_base + MMIO_CMD_HEAD_OFFSET);
|
||||
tail = readl(iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
|
||||
seq_printf(m, "CMD Buffer Head Offset:%d Tail Offset:%d\n",
|
||||
(head >> 4) & 0x7fff, (tail >> 4) & 0x7fff);
|
||||
for (i = 0; i < CMD_BUFFER_ENTRIES; i++) {
|
||||
cmd = (struct iommu_cmd *)(iommu->cmd_buf + i * sizeof(*cmd));
|
||||
seq_printf(m, "%3d: %08x %08x %08x %08x\n", i, cmd->data[0],
|
||||
cmd->data[1], cmd->data[2], cmd->data[3]);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&iommu->lock, flag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(iommu_cmdbuf);
|
||||
|
||||
static ssize_t devid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct amd_iommu_pci_seg *pci_seg;
|
||||
int seg, bus, slot, func;
|
||||
struct amd_iommu *iommu;
|
||||
char *srcid_ptr;
|
||||
u16 devid;
|
||||
int i;
|
||||
|
||||
sbdf = -1;
|
||||
|
||||
if (cnt >= DEVID_IN_SZ)
|
||||
return -EINVAL;
|
||||
|
||||
srcid_ptr = memdup_user_nul(ubuf, cnt);
|
||||
if (IS_ERR(srcid_ptr))
|
||||
return PTR_ERR(srcid_ptr);
|
||||
|
||||
i = sscanf(srcid_ptr, "%x:%x:%x.%x", &seg, &bus, &slot, &func);
|
||||
if (i != 4) {
|
||||
i = sscanf(srcid_ptr, "%x:%x.%x", &bus, &slot, &func);
|
||||
if (i != 3) {
|
||||
kfree(srcid_ptr);
|
||||
return -EINVAL;
|
||||
}
|
||||
seg = 0;
|
||||
}
|
||||
|
||||
devid = PCI_DEVID(bus, PCI_DEVFN(slot, func));
|
||||
|
||||
/* Check if user device id input is a valid input */
|
||||
for_each_pci_segment(pci_seg) {
|
||||
if (pci_seg->id != seg)
|
||||
continue;
|
||||
if (devid > pci_seg->last_bdf) {
|
||||
kfree(srcid_ptr);
|
||||
return -EINVAL;
|
||||
}
|
||||
iommu = pci_seg->rlookup_table[devid];
|
||||
if (!iommu) {
|
||||
kfree(srcid_ptr);
|
||||
return -ENODEV;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (pci_seg->id != seg) {
|
||||
kfree(srcid_ptr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sbdf = PCI_SEG_DEVID_TO_SBDF(seg, devid);
|
||||
|
||||
kfree(srcid_ptr);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int devid_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
u16 devid;
|
||||
|
||||
if (sbdf >= 0) {
|
||||
devid = PCI_SBDF_TO_DEVID(sbdf);
|
||||
seq_printf(m, "%04x:%02x:%02x.%x\n", PCI_SBDF_TO_SEGID(sbdf),
|
||||
PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid));
|
||||
} else
|
||||
seq_puts(m, "No or Invalid input provided\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_STORE_ATTRIBUTE(devid);
|
||||
|
||||
static void dump_dte(struct seq_file *m, struct amd_iommu_pci_seg *pci_seg, u16 devid)
|
||||
{
|
||||
struct dev_table_entry *dev_table;
|
||||
struct amd_iommu *iommu;
|
||||
|
||||
iommu = pci_seg->rlookup_table[devid];
|
||||
if (!iommu)
|
||||
return;
|
||||
|
||||
dev_table = get_dev_table(iommu);
|
||||
if (!dev_table) {
|
||||
seq_puts(m, "Device table not found");
|
||||
return;
|
||||
}
|
||||
|
||||
seq_printf(m, "%-12s %16s %16s %16s %16s iommu\n", "DeviceId",
|
||||
"QWORD[3]", "QWORD[2]", "QWORD[1]", "QWORD[0]");
|
||||
seq_printf(m, "%04x:%02x:%02x.%x ", pci_seg->id, PCI_BUS_NUM(devid),
|
||||
PCI_SLOT(devid), PCI_FUNC(devid));
|
||||
for (int i = 3; i >= 0; --i)
|
||||
seq_printf(m, "%016llx ", dev_table[devid].data[i]);
|
||||
seq_printf(m, "iommu%d\n", iommu->index);
|
||||
}
|
||||
|
||||
static int iommu_devtbl_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct amd_iommu_pci_seg *pci_seg;
|
||||
u16 seg, devid;
|
||||
|
||||
if (sbdf < 0) {
|
||||
seq_puts(m, "Enter a valid device ID to 'devid' file\n");
|
||||
return 0;
|
||||
}
|
||||
seg = PCI_SBDF_TO_SEGID(sbdf);
|
||||
devid = PCI_SBDF_TO_DEVID(sbdf);
|
||||
|
||||
for_each_pci_segment(pci_seg) {
|
||||
if (pci_seg->id != seg)
|
||||
continue;
|
||||
dump_dte(m, pci_seg, devid);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(iommu_devtbl);
|
||||
|
||||
static void dump_128_irte(struct seq_file *m, struct irq_remap_table *table, u16 int_tab_len)
|
||||
{
|
||||
struct irte_ga *ptr, *irte;
|
||||
int index;
|
||||
|
||||
for (index = 0; index < int_tab_len; index++) {
|
||||
ptr = (struct irte_ga *)table->table;
|
||||
irte = &ptr[index];
|
||||
|
||||
if (AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir) &&
|
||||
!irte->lo.fields_vapic.valid)
|
||||
continue;
|
||||
else if (!irte->lo.fields_remap.valid)
|
||||
continue;
|
||||
seq_printf(m, "IRT[%04d] %016llx %016llx\n", index, irte->hi.val, irte->lo.val);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_32_irte(struct seq_file *m, struct irq_remap_table *table, u16 int_tab_len)
|
||||
{
|
||||
union irte *ptr, *irte;
|
||||
int index;
|
||||
|
||||
for (index = 0; index < int_tab_len; index++) {
|
||||
ptr = (union irte *)table->table;
|
||||
irte = &ptr[index];
|
||||
|
||||
if (!irte->fields.valid)
|
||||
continue;
|
||||
seq_printf(m, "IRT[%04d] %08x\n", index, irte->val);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_irte(struct seq_file *m, u16 devid, struct amd_iommu_pci_seg *pci_seg)
|
||||
{
|
||||
struct dev_table_entry *dev_table;
|
||||
struct irq_remap_table *table;
|
||||
struct amd_iommu *iommu;
|
||||
unsigned long flags;
|
||||
u16 int_tab_len;
|
||||
|
||||
table = pci_seg->irq_lookup_table[devid];
|
||||
if (!table) {
|
||||
seq_printf(m, "IRQ lookup table not set for %04x:%02x:%02x:%x\n",
|
||||
pci_seg->id, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid));
|
||||
return;
|
||||
}
|
||||
|
||||
iommu = pci_seg->rlookup_table[devid];
|
||||
if (!iommu)
|
||||
return;
|
||||
|
||||
dev_table = get_dev_table(iommu);
|
||||
if (!dev_table) {
|
||||
seq_puts(m, "Device table not found");
|
||||
return;
|
||||
}
|
||||
|
||||
int_tab_len = dev_table[devid].data[2] & DTE_INTTABLEN_MASK;
|
||||
if (int_tab_len != DTE_INTTABLEN_512 && int_tab_len != DTE_INTTABLEN_2K) {
|
||||
seq_puts(m, "The device's DTE contains an invalid IRT length value.");
|
||||
return;
|
||||
}
|
||||
|
||||
seq_printf(m, "DeviceId %04x:%02x:%02x.%x\n", pci_seg->id, PCI_BUS_NUM(devid),
|
||||
PCI_SLOT(devid), PCI_FUNC(devid));
|
||||
|
||||
raw_spin_lock_irqsave(&table->lock, flags);
|
||||
if (AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
|
||||
dump_128_irte(m, table, BIT(int_tab_len >> 1));
|
||||
else
|
||||
dump_32_irte(m, table, BIT(int_tab_len >> 1));
|
||||
seq_puts(m, "\n");
|
||||
raw_spin_unlock_irqrestore(&table->lock, flags);
|
||||
}
|
||||
|
||||
static int iommu_irqtbl_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct amd_iommu_pci_seg *pci_seg;
|
||||
u16 devid, seg;
|
||||
|
||||
if (!irq_remapping_enabled) {
|
||||
seq_puts(m, "Interrupt remapping is disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sbdf < 0) {
|
||||
seq_puts(m, "Enter a valid device ID to 'devid' file\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
seg = PCI_SBDF_TO_SEGID(sbdf);
|
||||
devid = PCI_SBDF_TO_DEVID(sbdf);
|
||||
|
||||
for_each_pci_segment(pci_seg) {
|
||||
if (pci_seg->id != seg)
|
||||
continue;
|
||||
dump_irte(m, devid, pci_seg);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(iommu_irqtbl);
|
||||
|
||||
void amd_iommu_debugfs_setup(void)
|
||||
{
|
||||
struct amd_iommu *iommu;
|
||||
char name[MAX_NAME_LEN + 1];
|
||||
|
||||
mutex_lock(&amd_iommu_debugfs_lock);
|
||||
if (!amd_iommu_debugfs)
|
||||
amd_iommu_debugfs = debugfs_create_dir("amd",
|
||||
iommu_debugfs_dir);
|
||||
mutex_unlock(&amd_iommu_debugfs_lock);
|
||||
amd_iommu_debugfs = debugfs_create_dir("amd", iommu_debugfs_dir);
|
||||
|
||||
snprintf(name, MAX_NAME_LEN, "iommu%02d", iommu->index);
|
||||
iommu->debugfs = debugfs_create_dir(name, amd_iommu_debugfs);
|
||||
for_each_iommu(iommu) {
|
||||
iommu->dbg_mmio_offset = -1;
|
||||
iommu->dbg_cap_offset = -1;
|
||||
|
||||
snprintf(name, MAX_NAME_LEN, "iommu%02d", iommu->index);
|
||||
iommu->debugfs = debugfs_create_dir(name, amd_iommu_debugfs);
|
||||
|
||||
debugfs_create_file("mmio", 0644, iommu->debugfs, iommu,
|
||||
&iommu_mmio_fops);
|
||||
debugfs_create_file("capability", 0644, iommu->debugfs, iommu,
|
||||
&iommu_capability_fops);
|
||||
debugfs_create_file("cmdbuf", 0444, iommu->debugfs, iommu,
|
||||
&iommu_cmdbuf_fops);
|
||||
}
|
||||
|
||||
debugfs_create_file("devid", 0644, amd_iommu_debugfs, NULL,
|
||||
&devid_fops);
|
||||
debugfs_create_file("devtbl", 0444, amd_iommu_debugfs, NULL,
|
||||
&iommu_devtbl_fops);
|
||||
debugfs_create_file("irqtbl", 0444, amd_iommu_debugfs, NULL,
|
||||
&iommu_irqtbl_fops);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,6 +152,8 @@ bool amd_iommu_dump;
|
|||
bool amd_iommu_irq_remap __read_mostly;
|
||||
|
||||
enum protection_domain_mode amd_iommu_pgtable = PD_MODE_V1;
|
||||
/* Host page table level */
|
||||
u8 amd_iommu_hpt_level;
|
||||
/* Guest page table level */
|
||||
int amd_iommu_gpt_level = PAGE_MODE_4_LEVEL;
|
||||
|
||||
|
|
@ -168,6 +170,9 @@ static int amd_iommu_target_ivhd_type;
|
|||
u64 amd_iommu_efr;
|
||||
u64 amd_iommu_efr2;
|
||||
|
||||
/* Host (v1) page table is not supported*/
|
||||
bool amd_iommu_hatdis;
|
||||
|
||||
/* SNP is enabled on the system? */
|
||||
bool amd_iommu_snp_en;
|
||||
EXPORT_SYMBOL(amd_iommu_snp_en);
|
||||
|
|
@ -1792,6 +1797,11 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h,
|
|||
if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT))
|
||||
amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE;
|
||||
|
||||
if (h->efr_attr & BIT(IOMMU_IVHD_ATTR_HATDIS_SHIFT)) {
|
||||
pr_warn_once("Host Address Translation is not supported.\n");
|
||||
amd_iommu_hatdis = true;
|
||||
}
|
||||
|
||||
early_iommu_features_init(iommu, h);
|
||||
|
||||
break;
|
||||
|
|
@ -2112,7 +2122,15 @@ static int __init iommu_init_pci(struct amd_iommu *iommu)
|
|||
return ret;
|
||||
}
|
||||
|
||||
iommu_device_register(&iommu->iommu, &amd_iommu_ops, NULL);
|
||||
ret = iommu_device_register(&iommu->iommu, &amd_iommu_ops, NULL);
|
||||
if (ret || amd_iommu_pgtable == PD_MODE_NONE) {
|
||||
/*
|
||||
* Remove sysfs if DMA translation is not supported by the
|
||||
* IOMMU. Do not return an error to enable IRQ remapping
|
||||
* in state_next(), DTE[V, TV] must eventually be set to 0.
|
||||
*/
|
||||
iommu_device_sysfs_remove(&iommu->iommu);
|
||||
}
|
||||
|
||||
return pci_enable_device(iommu->dev);
|
||||
}
|
||||
|
|
@ -2573,7 +2591,7 @@ static void init_device_table_dma(struct amd_iommu_pci_seg *pci_seg)
|
|||
u32 devid;
|
||||
struct dev_table_entry *dev_table = pci_seg->dev_table;
|
||||
|
||||
if (dev_table == NULL)
|
||||
if (!dev_table || amd_iommu_pgtable == PD_MODE_NONE)
|
||||
return;
|
||||
|
||||
for (devid = 0; devid <= pci_seg->last_bdf; ++devid) {
|
||||
|
|
@ -3033,6 +3051,7 @@ static int __init early_amd_iommu_init(void)
|
|||
struct acpi_table_header *ivrs_base;
|
||||
int ret;
|
||||
acpi_status status;
|
||||
u8 efr_hats;
|
||||
|
||||
if (!amd_iommu_detected)
|
||||
return -ENODEV;
|
||||
|
|
@ -3077,6 +3096,19 @@ static int __init early_amd_iommu_init(void)
|
|||
FIELD_GET(FEATURE_GATS, amd_iommu_efr) == GUEST_PGTABLE_5_LEVEL)
|
||||
amd_iommu_gpt_level = PAGE_MODE_5_LEVEL;
|
||||
|
||||
efr_hats = FIELD_GET(FEATURE_HATS, amd_iommu_efr);
|
||||
if (efr_hats != 0x3) {
|
||||
/*
|
||||
* efr[HATS] bits specify the maximum host translation level
|
||||
* supported, with LEVEL 4 being initial max level.
|
||||
*/
|
||||
amd_iommu_hpt_level = efr_hats + PAGE_MODE_4_LEVEL;
|
||||
} else {
|
||||
pr_warn_once(FW_BUG "Disable host address translation due to invalid translation level (%#x).\n",
|
||||
efr_hats);
|
||||
amd_iommu_hatdis = true;
|
||||
}
|
||||
|
||||
if (amd_iommu_pgtable == PD_MODE_V2) {
|
||||
if (!amd_iommu_v2_pgtbl_supported()) {
|
||||
pr_warn("Cannot enable v2 page table for DMA-API. Fallback to v1.\n");
|
||||
|
|
@ -3084,6 +3116,17 @@ static int __init early_amd_iommu_init(void)
|
|||
}
|
||||
}
|
||||
|
||||
if (amd_iommu_hatdis) {
|
||||
/*
|
||||
* Host (v1) page table is not available. Attempt to use
|
||||
* Guest (v2) page table.
|
||||
*/
|
||||
if (amd_iommu_v2_pgtbl_supported())
|
||||
amd_iommu_pgtable = PD_MODE_V2;
|
||||
else
|
||||
amd_iommu_pgtable = PD_MODE_NONE;
|
||||
}
|
||||
|
||||
/* Disable any previously enabled IOMMUs */
|
||||
if (!is_kdump_kernel() || amd_iommu_disabled)
|
||||
disable_iommus();
|
||||
|
|
@ -3376,7 +3419,6 @@ int amd_iommu_enable_faulting(unsigned int cpu)
|
|||
*/
|
||||
static int __init amd_iommu_init(void)
|
||||
{
|
||||
struct amd_iommu *iommu;
|
||||
int ret;
|
||||
|
||||
ret = iommu_go_to_state(IOMMU_INITIALIZED);
|
||||
|
|
@ -3390,8 +3432,8 @@ static int __init amd_iommu_init(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
for_each_iommu(iommu)
|
||||
amd_iommu_debugfs_setup(iommu);
|
||||
if (!ret)
|
||||
amd_iommu_debugfs_setup();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ static bool increase_address_space(struct amd_io_pgtable *pgtable,
|
|||
goto out;
|
||||
|
||||
ret = false;
|
||||
if (WARN_ON_ONCE(pgtable->mode == PAGE_MODE_6_LEVEL))
|
||||
if (WARN_ON_ONCE(pgtable->mode == amd_iommu_hpt_level))
|
||||
goto out;
|
||||
|
||||
*pte = PM_LEVEL_PDE(pgtable->mode, iommu_virt_to_phys(pgtable->root));
|
||||
|
|
@ -526,7 +526,7 @@ static void v1_free_pgtable(struct io_pgtable *iop)
|
|||
|
||||
/* Page-table is not visible to IOMMU anymore, so free it */
|
||||
BUG_ON(pgtable->mode < PAGE_MODE_NONE ||
|
||||
pgtable->mode > PAGE_MODE_6_LEVEL);
|
||||
pgtable->mode > amd_iommu_hpt_level);
|
||||
|
||||
free_sub_pt(pgtable->root, pgtable->mode, &freelist);
|
||||
iommu_put_pages_list(&freelist);
|
||||
|
|
|
|||
|
|
@ -63,13 +63,6 @@ static const struct iommu_dirty_ops amd_dirty_ops;
|
|||
|
||||
int amd_iommu_max_glx_val = -1;
|
||||
|
||||
/*
|
||||
* general struct to manage commands send to an IOMMU
|
||||
*/
|
||||
struct iommu_cmd {
|
||||
u32 data[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* AMD IOMMU allows up to 2^16 different protection domains. This is a bitmap
|
||||
* to know which ones are already in use.
|
||||
|
|
@ -635,8 +628,8 @@ static inline void pdev_disable_cap_pasid(struct pci_dev *pdev)
|
|||
|
||||
static void pdev_enable_caps(struct pci_dev *pdev)
|
||||
{
|
||||
pdev_enable_cap_ats(pdev);
|
||||
pdev_enable_cap_pasid(pdev);
|
||||
pdev_enable_cap_ats(pdev);
|
||||
pdev_enable_cap_pri(pdev);
|
||||
}
|
||||
|
||||
|
|
@ -2425,6 +2418,13 @@ static struct iommu_device *amd_iommu_probe_device(struct device *dev)
|
|||
pci_max_pasids(to_pci_dev(dev)));
|
||||
}
|
||||
|
||||
if (amd_iommu_pgtable == PD_MODE_NONE) {
|
||||
pr_warn_once("%s: DMA translation not supported by iommu.\n",
|
||||
__func__);
|
||||
iommu_dev = ERR_PTR(-ENODEV);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
out_err:
|
||||
|
||||
iommu_completion_wait(iommu);
|
||||
|
|
@ -2512,6 +2512,9 @@ static int pdom_setup_pgtable(struct protection_domain *domain,
|
|||
case PD_MODE_V2:
|
||||
fmt = AMD_IOMMU_V2;
|
||||
break;
|
||||
case PD_MODE_NONE:
|
||||
WARN_ON_ONCE(1);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
domain->iop.pgtbl.cfg.amd.nid = dev_to_node(dev);
|
||||
|
|
@ -2525,14 +2528,30 @@ static int pdom_setup_pgtable(struct protection_domain *domain,
|
|||
static inline u64 dma_max_address(enum protection_domain_mode pgtable)
|
||||
{
|
||||
if (pgtable == PD_MODE_V1)
|
||||
return ~0ULL;
|
||||
return PM_LEVEL_SIZE(amd_iommu_hpt_level);
|
||||
|
||||
/* V2 with 4/5 level page table */
|
||||
return ((1ULL << PM_LEVEL_SHIFT(amd_iommu_gpt_level)) - 1);
|
||||
/*
|
||||
* V2 with 4/5 level page table. Note that "2.2.6.5 AMD64 4-Kbyte Page
|
||||
* Translation" shows that the V2 table sign extends the top of the
|
||||
* address space creating a reserved region in the middle of the
|
||||
* translation, just like the CPU does. Further Vasant says the docs are
|
||||
* incomplete and this only applies to non-zero PASIDs. If the AMDv2
|
||||
* page table is assigned to the 0 PASID then there is no sign extension
|
||||
* check.
|
||||
*
|
||||
* Since the IOMMU must have a fixed geometry, and the core code does
|
||||
* not understand sign extended addressing, we have to chop off the high
|
||||
* bit to get consistent behavior with attachments of the domain to any
|
||||
* PASID.
|
||||
*/
|
||||
return ((1ULL << (PM_LEVEL_SHIFT(amd_iommu_gpt_level) - 1)) - 1);
|
||||
}
|
||||
|
||||
static bool amd_iommu_hd_support(struct amd_iommu *iommu)
|
||||
{
|
||||
if (amd_iommu_hatdis)
|
||||
return false;
|
||||
|
||||
return iommu && (iommu->features & FEATURE_HDSUP);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user