Initial testpci module and command used to query if PCI devices are present.
---
docs/grub.texi | 39 +++++++
grub-core/Makefile.core.def | 7 ++
grub-core/commands/testpci.c | 194 +++++++++++++++++++++++++++++++++++
grub-core/kern/efi/sb.c | 1 +
include/grub/file.h | 2 +
5 files changed, 243 insertions(+)
create mode 100644 grub-core/commands/testpci.c
diff --git a/docs/grub.texi b/docs/grub.texi
index 2b3d536d3..a5712012e 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -4139,6 +4139,7 @@ Modules can be loaded via the @command{insmod}
(@pxref{insmod}) command.
* test_module::
* test_blockarg_module::
* testload_module::
+* testpci_module::
* testspeed_module::
* tftp_module::
* tga_module::
@@ -5728,6 +5729,12 @@ argument function in GRUB internal functions via a test
command
This module is intended for performing a functional test of some file reading /
seeking functions in GRUB internals via a test command @command{testload}.
+@node testpci_module
+@section testpci
+This module provides support for the @command{testpci} command. This
+command can be used to test if specific PCI / PCIe devices are found on
+the system.
+
@node testspeed_module
@section testspeed
This module provides support for the @command{testspeed} command to test and
@@ -8052,6 +8059,38 @@ either @var{expression1} or @var{expression2} is true
@end table
@end deffn
+@node testpci
+@subsection testpci
+
+@deffn Command testpci [@option{--file} device.lst] [@var{device} [...]]
+Test if a PCI device is present.
+
+Gets a device list to check from command line arguments, and/or from the
+@var{device.lst} file.
+
+If any of the devices in the list are present on the machine - the
+command exits with zero status.
+
+If the list is empty; Or non of the devices in the list are present; Or
+any error occurred (bad device format, missing file, memory, etc.) - a
+non-zero exit code is returned.
+
+Each @var{device} should be in the format @code{xxxx:yyyy}, where
+@code{xxxx} is the vendor id, and @code{yyyy} is the device id. All
+lower case as appear in the output of the @command{lspci} command.
+
+If @var{device.lst} file is given, each line should contain a single
+@var{device}. Space padding, comments starting with #, and empty lines
+are ignored.
+
+For example:
+@example
+if testpci --file $prefix/device.lst 10de:233b 1002:74a5; then
+ set somevar=somevalue
+fi
+@end example
+@end deffn
+
@node tpm2_key_protector_init
@subsection tpm2_key_protector_init
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index f70e02e69..f18d2304d 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1032,6 +1032,13 @@ module = {
enable = pci;
};
+module = {
+ name = testpci;
+ common = commands/testpci.c;
+
+ enable = pci;
+};
+
module = {
name = memrw;
common = commands/memrw.c;
diff --git a/grub-core/commands/testpci.c b/grub-core/commands/testpci.c
new file mode 100644
index 000000000..8af2a57a2
--- /dev/null
+++ b/grub-core/commands/testpci.c
@@ -0,0 +1,194 @@
+/* testpci.c - Test if PCI exists by ID. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/mm.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/pci.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static const struct grub_arg_option options[] = {
+ {"file", 'f', 0, "read device list from file", "FILE", ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+};
+
+struct grub_testpci_devlist {
+ /* devices list */
+ char** devices;
+
+ /* size of devices list */
+ int devices_size;
+
+ /* number of allocated devices in the list <= devices_size */
+ int n_devices;
+
+ bool found;
+};
+
+static int
+grub_testpci_iter (grub_pci_device_t dev __attribute__ ((unused)),
+ grub_pci_id_t pciid,
+ void *data) {
+
+ struct grub_testpci_devlist* devlist = (struct grub_testpci_devlist*)data;
+ char device[10];
+
+ grub_snprintf(device, sizeof(device),
+ "%04x:%04x", pciid & 0xFFFF, pciid >> 16);
+ for (int i = 0; i < devlist->n_devices; i++) {
+ if (grub_strcasecmp(device, devlist->devices[i]) == 0) {
+ devlist->found = true;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+testpci_clear_device_list(struct grub_testpci_devlist* devlist)
+{
+ for (int i = 0; i < devlist->n_devices; i++) {
+ grub_free(devlist->devices[i]);
+ }
+ grub_free(devlist->devices);
+ devlist->n_devices = 0;
+ devlist->devices_size = 0;
+}
+
+static grub_err_t
+testpci_add_device_to_list(struct grub_testpci_devlist* devlist,
+ char* device)
+{
+ if (devlist->n_devices == devlist->devices_size) {
+ devlist->devices_size *= 2;
+ char** tmp = grub_realloc(devlist->devices,
+ devlist->devices_size * sizeof(char*));
+ if (!tmp) {
+ return grub_errno;
+ }
+ devlist->devices = tmp;
+ }
+ char* tmp = grub_strdup(device);
+ if (!tmp) {
+ return grub_errno;
+ }
+ devlist->devices[devlist->n_devices++] = tmp;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_testpci (grub_extcmd_context_t ctxt,
+ int argc, char **args)
+{
+
+ struct grub_testpci_devlist devlist;
+
+ devlist.found = false;
+ devlist.n_devices = 0;
+ devlist.devices_size = argc + (ctxt->state[0].set ? 5 : 0);
+ devlist.devices = grub_malloc(devlist.devices_size * sizeof(char*));
+ if (!(devlist.devices)) {
+ return grub_errno;
+ }
+
+ for (int i = 0; i < argc; i++) {
+ grub_err_t err = testpci_add_device_to_list(&devlist, args[i]);
+ if (err) {
+ testpci_clear_device_list(&devlist);
+ return err;
+ }
+ }
+
+ /* device list from file */
+ if (ctxt->state[0].set) {
+
+ grub_file_t listfile = grub_file_open(ctxt->state[0].arg,
GRUB_FILE_TYPE_DEVICE_LIST);
+ if (listfile) {
+
+ char *buf = NULL;
+ while (grub_free (buf), (buf = grub_file_getline (listfile))) {
+
+ /* remove comments */
+ char *p = grub_strchr(buf, '#');
+ if (p) {
+ *p = '\0';
+ }
+
+ /* remove suffix spaces */
+ p = buf + grub_strlen(buf) - 1;
+ while (p >= buf && *p && grub_isspace(*p)) {
+ *p-- = '\0';
+ }
+
+ /* remove prefix spaces */
+ p = buf;
+ while (*p && grub_isspace(*p)) {
+ p++;
+ }
+
+ /* ignore empty */
+ if (*p == '\0')
+ continue;
+
+ grub_err_t err = testpci_add_device_to_list(&devlist, p);
+ if (err) {
+ testpci_clear_device_list(&devlist);
+ return err;
+ }
+ }
+
+ grub_file_close (listfile);
+ } else {
+ return grub_errno;
+ }
+ }
+
+ for (int d = 0 ; d < devlist.n_devices; d++) {
+ if (grub_strlen(devlist.devices[d]) != 9 || devlist.devices[d][4] != ':') {
+ grub_printf("invalid device (%d) \"%s\", expected xxxx:xxxx\n", d,
devlist.devices[d]);
+ testpci_clear_device_list(&devlist);
+ return grub_error(GRUB_ERR_BAD_ARGUMENT, "invalid device");
+ }
+ }
+
+ grub_pci_iterate (grub_testpci_iter, (void*)&devlist);
+
+ testpci_clear_device_list(&devlist);
+
+ return devlist.found ?
+ GRUB_ERR_NONE : grub_error(GRUB_ERR_TEST_FAILURE, "device not found");
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT(testpci)
+{
+ cmd = grub_register_extcmd ("testpci", grub_cmd_testpci, 0,
+ "[<devid> [...]] [--file <filename>]",
+ N_("Check if any of the PCI devices exist."),
options);
+}
+
+GRUB_MOD_FINI(testpci)
+{
+ grub_unregister_extcmd (cmd);
+}
diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c
index 8d3e41360..0c7f45ec5 100644
--- a/grub-core/kern/efi/sb.c
+++ b/grub-core/kern/efi/sb.c
@@ -168,6 +168,7 @@ shim_lock_verifier_init (grub_file_t io __attribute__
((unused)),
case GRUB_FILE_TYPE_THEME:
case GRUB_FILE_TYPE_GETTEXT_CATALOG:
case GRUB_FILE_TYPE_FS_SEARCH:
+ case GRUB_FILE_TYPE_DEVICE_LIST:
case GRUB_FILE_TYPE_LOADENV:
case GRUB_FILE_TYPE_SAVEENV:
case GRUB_FILE_TYPE_VERIFY_SIGNATURE:
diff --git a/include/grub/file.h b/include/grub/file.h
index a5bf3a792..80ebaba90 100644
--- a/include/grub/file.h
+++ b/include/grub/file.h
@@ -126,6 +126,8 @@ enum grub_file_type
GRUB_FILE_TYPE_FS_SEARCH,
GRUB_FILE_TYPE_AUDIO,
GRUB_FILE_TYPE_VBE_DUMP,
+ /* device list for testpci command */
+ GRUB_FILE_TYPE_DEVICE_LIST,
GRUB_FILE_TYPE_LOADENV,
GRUB_FILE_TYPE_SAVEENV,
--
2.39.5
_______________________________________________
Grub-devel mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/grub-devel