public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
To: <linux-kernel@vger.kernel.org>, <iommu@lists.linux.dev>
Cc: <joro@8bytes.org>, <jon.grimm@amd.com>, <vasant.hegde@amd.com>,
	<frank.gorishek@amd.com>, <thomas.lendacky@amd.com>,
	<santosh.shukla@amd.com>,
	Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Subject: [PATCH 4/4] iommu/amd: Introduce boot option ivmd=seg:bus:dev.fun,start,size,flags
Date: Mon, 20 Apr 2026 17:00:33 +0000	[thread overview]
Message-ID: <20260420170033.6780-5-suravee.suthikulpanit@amd.com> (raw)
In-Reply-To: <20260420170033.6780-1-suravee.suthikulpanit@amd.com>

IVRS table contains IVMD blocks, which allow firmware to specify memory
usage requirements to communicate to system software based on its needs
or on hardware characteristics. Each IVMD entry may be per-device, range
of devices.

Some BIOS specify incorrect or missing IVMD entry. Introduce a new ivmd
boot option to allow user to specify up-to 4 per-device IVMD entries at
boot time. The entries are stored during driver initialization, and will
be added to the per-segment ivmd_entry_map so that they can be included
during struct iommu_ops.get_resv_regions.

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
 .../admin-guide/kernel-parameters.txt         |  16 +++
 drivers/iommu/amd/amd_iommu_types.h           |  14 +++
 drivers/iommu/amd/init.c                      | 104 +++++++++++++++++-
 3 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 03a550630644..e680a258008f 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2897,6 +2897,22 @@ Kernel parameters
 			  PCI device ID 00:14.5, write the parameter as:
 				ivrs_acpihid[0001:00:14.5]=AMD0020:0
 
+	ivmd		[HW,X86-64]
+			Supplement IVMD unity mapping or exclusion ranges from
+			the kernel command line (in addition to the IVRS ACPI
+			table). May be specified multiple times.
+
+			Form:
+				ivmd=segment:bus:dev.fn,start,length,flags
+
+			start and length are byte counts (decimal or 0x hex).
+			flags is the IVMD flags byte (UNITY, IR, IW, EXCL bits
+			per the AMD IOMMU specification). Use segment 0 for
+			PCI segment zero.
+
+			Example:
+				ivmd=0000:00:01.0,0xe0000000,0x100000,0xb
+
 	js=		[HW,JOY] Analog joystick
 			See Documentation/input/joydev/joystick.rst.
 
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index e93b4b857dde..25a5bbd8ec03 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -893,6 +893,20 @@ struct ivmd_entry {
 	u8 flags;
 };
 
+/*
+ * IVMD-style ranges from ivrs_ivmd=... ; applied in
+ * init_memory_definitions() once per-PCI-segment
+ * ivmd_entry_map exists.
+ */
+struct ivmd_cmdline {
+	struct list_head list;
+	u16 pci_seg;
+	u16 devid;
+	u64 range_start;
+	u64 range_length;
+	u8 flags;
+};
+
 /*
  * Data structures for device handling
  */
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index 4b62bb89a12c..7d60143d4711 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -208,10 +208,12 @@ enum iommu_init_state {
 static struct devid_map __initdata early_ioapic_map[EARLY_MAP_SIZE];
 static struct devid_map __initdata early_hpet_map[EARLY_MAP_SIZE];
 static struct acpihid_map_entry __initdata early_acpihid_map[EARLY_MAP_SIZE];
+static struct ivmd_cmdline early_ivmd_cmdline_map[EARLY_MAP_SIZE] __initdata;
 
 static int __initdata early_ioapic_map_size;
 static int __initdata early_hpet_map_size;
 static int __initdata early_acpihid_map_size;
+static int early_ivmd_cmdline_map_size __initdata;
 
 static bool __initdata cmdline_maps;
 
@@ -2653,6 +2655,47 @@ static int __init init_ivmd_map_range(struct ivmd_header *m,
 	return 0;
 }
 
+static int __init apply_ivmd_cmdline_entries(struct acpi_table_header *ivrs_base)
+{
+	int i;
+	struct ivmd_entry *e;
+	struct amd_iommu_pci_seg *pci_seg;
+
+	for (i = 0; i < early_ivmd_cmdline_map_size; i++) {
+		struct ivmd_cmdline *cmd = &early_ivmd_cmdline_map[i];
+
+		pci_seg = get_pci_segment(cmd->pci_seg, ivrs_base);
+		if (!pci_seg) {
+			pr_err("ivmd: PCI segment %#x unavailable\n",
+			       cmd->pci_seg);
+			continue;
+		}
+
+		if (cmd->devid > pci_seg->last_bdf) {
+			pr_err("%s: requestor %#x:%#02x:%#02x.%#02x exceeds segment %#x last BDF %#x\n",
+			       __func__, cmd->pci_seg, PCI_BUS_NUM(cmd->devid),
+			       PCI_SLOT(cmd->devid), PCI_FUNC(cmd->devid),
+			       pci_seg->id, pci_seg->last_bdf);
+			continue;
+		}
+
+		e = kzalloc(sizeof(*e), GFP_KERNEL);
+		if (!e) {
+			kfree(cmd);
+			return -ENOMEM;
+		}
+
+		e->devid_start = cmd->devid;
+		e->devid_end = cmd->devid;
+		e->address_start = PAGE_ALIGN(cmd->range_start);
+		e->address_end = e->address_start + PAGE_ALIGN(cmd->range_length);
+		e->flags = cmd->flags;
+		list_add_tail(&e->list, &pci_seg->ivmd_entry_map);
+	}
+
+	return 0;
+}
+
 /* iterates over all memory definitions we find in the ACPI table */
 static int __init init_memory_definitions(struct acpi_table_header *table)
 {
@@ -2670,7 +2713,7 @@ static int __init init_memory_definitions(struct acpi_table_header *table)
 		p += m->length;
 	}
 
-	return 0;
+	return apply_ivmd_cmdline_entries(table);
 }
 
 /*
@@ -3812,12 +3855,71 @@ static int __init parse_ivrs_acpihid(char *str)
 	return 1;
 }
 
+/*
+ * ivmd=seg:bus:dev.fn,start,length,flags
+ *
+ * start and length are in bytes (decimal or 0x hex). flags is the IVMD flags
+ * byte (IVMD_FLAG_UNITY, IVMD_FLAG_IR, IVMD_FLAG_IW, IVMD_FLAG_EXCL). May be
+ * repeated for multiple ranges.
+ */
+static int __init parse_ivmd(char *str)
+{
+	u32 seg, bus, dev, fn;
+	unsigned long long range_start, range_len;
+	unsigned int flags;
+	struct ivmd_cmdline *cmd;
+
+	if (!str)
+		goto invalid;
+
+	if (sscanf(str, "%x:%x:%x.%x,%llu,%llu,%x",
+		   &seg, &bus, &dev, &fn, &range_start,
+		   &range_len, &flags) != 7)
+		goto invalid;
+
+	if (flags & ~IVMD_FLAG_MASK) {
+		pr_err("%s: flags %#x contains unknown flags\n", __func__, flags);
+		return 1;
+	}
+
+	if (!(flags & (IVMD_FLAG_UNITY | IVMD_FLAG_EXCL)))
+		pr_warn("%s: flags %#02x omit UNITY/EXCL; entry may have no effect\n",
+			__func__, (u8)flags);
+
+	if (early_ivmd_cmdline_map_size == EARLY_MAP_SIZE) {
+		pr_err("%s: Early IVMD command line map overflow - ignoring ivmd=%s\n",
+			__func__, str);
+		return 1;
+	}
+
+	cmd = &early_ivmd_cmdline_map[early_ivmd_cmdline_map_size];
+	early_ivmd_cmdline_map_size++;
+
+	cmd->pci_seg = (u16)seg;
+	cmd->devid = PCI_DEVID(bus & 0xff, PCI_DEVFN(dev & 0x1f, fn & 0x7));
+	cmd->range_start = range_start;
+	cmd->range_length = range_len;
+	cmd->flags = (u8)flags;
+
+	pr_info("%s: segment=%04x dev=%02x:%02x.%x start=%#llx len=%#llx flags=%#02x\n",
+		__func__, cmd->pci_seg,
+		PCI_BUS_NUM(cmd->devid), PCI_SLOT(cmd->devid),
+		PCI_FUNC(cmd->devid), range_start, range_len, cmd->flags);
+
+	return 1;
+
+invalid:
+	pr_err("Invalid command line: ivmd=%s\n", str ? str : "");
+	return 1;
+}
+
 __setup("amd_iommu_dump",	parse_amd_iommu_dump);
 __setup("amd_iommu=",		parse_amd_iommu_options);
 __setup("amd_iommu_intr=",	parse_amd_iommu_intr);
 __setup("ivrs_ioapic",		parse_ivrs_ioapic);
 __setup("ivrs_hpet",		parse_ivrs_hpet);
 __setup("ivrs_acpihid",		parse_ivrs_acpihid);
+__setup("ivmd=",		parse_ivmd);
 
 bool amd_iommu_pasid_supported(void)
 {
-- 
2.34.1


      parent reply	other threads:[~2026-04-20 17:01 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-20 17:00 [PATCH 0/4] iommu/amd: IVMD handling cleanup and ivmd= boot override Suravee Suthikulpanit
2026-04-20 17:00 ` [PATCH 1/4] iommu/amd: Drop unused global exclusion range fields and init Suravee Suthikulpanit
2026-04-20 17:00 ` [PATCH 2/4] iommu/amd: Do not convert IVMD exclusion range to unity range Suravee Suthikulpanit
2026-04-20 17:00 ` [PATCH 3/4] iommu/amd: Clean up and simplify IVMD entry handling Suravee Suthikulpanit
2026-04-20 17:00 ` Suravee Suthikulpanit [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260420170033.6780-5-suravee.suthikulpanit@amd.com \
    --to=suravee.suthikulpanit@amd.com \
    --cc=frank.gorishek@amd.com \
    --cc=iommu@lists.linux.dev \
    --cc=jon.grimm@amd.com \
    --cc=joro@8bytes.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=santosh.shukla@amd.com \
    --cc=thomas.lendacky@amd.com \
    --cc=vasant.hegde@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox