From: Stephen Bancroft <stevereaver@gmail.com>
To: linux-mtd@lists.infradead.org
Cc: Miquel Raynal <miquel.raynal@bootlin.com>,
Richard Weinberger <richard@nod.at>,
Vignesh Raghavendra <vigneshr@ti.com>,
linux-kernel@vger.kernel.org,
Stephen Bancroft <stevereaver@gmail.com>
Subject: [PATCH v3] mtd: maps: add INT0800 firmware-flash map driver
Date: Mon, 21 Sep 2026 12:14:47 +1000 [thread overview]
Message-ID: <20260921021459.283023-1-stevereaver@gmail.com> (raw)
In-Reply-To: <V2-MESSAGE-ID>
Add a read-only mapping driver that binds the ACPI INT0800 "Intel
82802 firmware hub" device and exposes the system firmware flash as an
MTD ROM device.
On x86 machines the boot flash is decoded into the physical address
space below 4 GB, so a plain ioremap() of the declared resource window
is sufficient for reads - no SPI or LPC controller access is needed.
The window may be larger than the real flash; undecoded holes read as
0xff.
This gives userspace a clean, safe way to read firmware flash contents
without flashrom or relaxed /dev/mem access - for firmware analysis,
and for extracting option ROMs stored inside EFI firmware volumes
(e.g. the NVIDIA VBIOS on EFI-booted Apple machines, which can then be
fed to nouveau via nouveau.config=NvBios=). There is deliberately no
write or erase support.
Tested on a MacBookPro4,1 (ICH8M): /dev/mtd0 reads are byte-identical
to a flashrom dump of the 2 MiB SST25VF016B, except for live NVRAM
variable-store regions.
Signed-off-by: Stephen Bancroft <stevereaver@gmail.com>
---
Changes in v3:
- MODULE_AUTHOR: use full author identity (per Miquèl Raynal's review)
Changes in v2:
- Per-device state via devm_kzalloc instead of globals (fixes shared
state corruption with multiple INT0800 devices)
- Clean up map probe on mtd_device_register() failure (fixes
memory/mapping leak)
- Use %pa for resource_size_t in dev_info (fixes format string on
32-bit)
- Add missing Signed-off-by
drivers/mtd/maps/Kconfig | 17 ++++++
drivers/mtd/maps/Makefile | 1 +
drivers/mtd/maps/int0800.c | 108 +++++++++++++++++++++++++++++++++++++
3 files changed, 126 insertions(+)
create mode 100644 drivers/mtd/maps/int0800.c
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 1bb3dba2631d..649fd145e15d 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -161,6 +161,23 @@ config MTD_AMD76XROM
BE VERY CAREFUL.
+config MTD_INT0800
+ tristate "Read-only BIOS/firmware flash via ACPI INT0800"
+ depends on X86 && ACPI
+ select MTD_ROM
+ help
+ Support for reading the system firmware (BIOS/EFI) flash chip
+ through the memory window described by the ACPI INT0800
+ "82802 firmware hub" device, present on most x86 machines.
+
+ The flash is exposed read-only via the ROM chip driver, e.g.
+ for firmware analysis or extracting option ROMs (such as
+ video BIOS images embedded in EFI firmware volumes). There is
+ no write or erase support.
+
+ To compile this driver as a module, choose M here: the
+ module will be called int0800.
+
config MTD_ICHXROM
tristate "BIOS flash chip on Intel Controller Hub 2/3/4/5"
depends on X86 && MTD_JEDECPROBE
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 01745eca1f73..2f1a7375e13c 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_L440GX) += l440gx.o
obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o
obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
+obj-$(CONFIG_MTD_INT0800) += int0800.o
obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
diff --git a/drivers/mtd/maps/int0800.c b/drivers/mtd/maps/int0800.c
new file mode 100644
index 000000000000..0188d58b168d
--- /dev/null
+++ b/drivers/mtd/maps/int0800.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Read-only MTD access to the system firmware flash behind the ACPI
+ * INT0800 "Intel 82802 firmware hub" device.
+ *
+ * On x86 systems the boot flash is decoded into the physical address
+ * space below 4 GB, so a plain ioremap() is sufficient to read it -
+ * no SPI or LPC controller access is required. The declared _CRS
+ * window may be larger than the real flash (the whole top-16MiB
+ * decode range is commonly claimed); undecoded holes read as 0xff.
+ *
+ * The device is exposed read-only via the ROM chip driver; there is
+ * deliberately no write or erase support.
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* top-of-4GB firmware decode, used when _CRS reports no window */
+#define INT0800_DEFAULT_PHYS 0xffe00000UL
+#define INT0800_DEFAULT_SIZE SZ_2M
+
+struct int0800 {
+ struct map_info map;
+ struct mtd_info *mtd;
+};
+
+static int int0800_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct int0800 *fw;
+ resource_size_t end;
+ int ret;
+
+ fw = devm_kzalloc(&pdev->dev, sizeof(*fw), GFP_KERNEL);
+ if (!fw)
+ return -ENOMEM;
+
+ fw->map.name = dev_name(&pdev->dev);
+ fw->map.bankwidth = 1;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ fw->map.phys = res->start;
+ fw->map.size = resource_size(res);
+ } else {
+ fw->map.phys = INT0800_DEFAULT_PHYS;
+ fw->map.size = INT0800_DEFAULT_SIZE;
+ }
+
+ /*
+ * Plain ioremap on purpose: the window is already claimed by the
+ * ACPI/pnp resource reservation, so devm_ioremap_resource() would
+ * fail with -EBUSY.
+ */
+ fw->map.virt = devm_ioremap(&pdev->dev, fw->map.phys, fw->map.size);
+ if (!fw->map.virt)
+ return -ENOMEM;
+
+ simple_map_init(&fw->map);
+ fw->mtd = do_map_probe("map_rom", &fw->map);
+ if (!fw->mtd)
+ return -ENODEV;
+ fw->mtd->dev.parent = &pdev->dev;
+ platform_set_drvdata(pdev, fw);
+
+ end = fw->map.phys + fw->map.size - 1;
+ dev_info(&pdev->dev, "mapped firmware window %pa-%pa\n",
+ &fw->map.phys, &end);
+
+ ret = mtd_device_register(fw->mtd, NULL, 0);
+ if (ret)
+ map_destroy(fw->mtd);
+ return ret;
+}
+
+static void int0800_remove(struct platform_device *pdev)
+{
+ struct int0800 *fw = platform_get_drvdata(pdev);
+
+ mtd_device_unregister(fw->mtd);
+ map_destroy(fw->mtd);
+}
+
+static const struct acpi_device_id int0800_ids[] = {
+ { "INT0800", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, int0800_ids);
+
+static struct platform_driver int0800_driver = {
+ .probe = int0800_probe,
+ .remove = int0800_remove,
+ .driver = {
+ .name = "int0800",
+ .acpi_match_table = int0800_ids,
+ },
+};
+module_platform_driver(int0800_driver);
+
+MODULE_AUTHOR("Stephen Bancroft <stevereaver@gmail.com>");
+MODULE_DESCRIPTION("Read-only MTD map over the INT0800 firmware flash window");
+MODULE_LICENSE("GPL");
--
2.43.0
next parent reply other threads:[~2026-09-21 2:15 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <V2-MESSAGE-ID>
2026-09-21 2:14 ` Stephen Bancroft [this message]
2026-09-21 2:30 ` sashiko-bot
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=20260921021459.283023-1-stevereaver@gmail.com \
--to=stevereaver@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=miquel.raynal@bootlin.com \
--cc=richard@nod.at \
--cc=vigneshr@ti.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
all inboxes | Powered by JetHome®