Initial testpci module and command used to query if PCI devices are
present.

---
 docs/grub.texi               |   8 ++
 grub-core/Makefile.core.def  |   7 ++
 grub-core/commands/testpci.c | 167 +++++++++++++++++++++++++++++++++++
 3 files changed, 182 insertions(+)
 create mode 100644 grub-core/commands/testpci.c

diff --git a/docs/grub.texi b/docs/grub.texi
index 2b3d536d3..296c60b72 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
@@ -8498,6 +8505,7 @@ GRUB shell may provide more information on parameters and 
usage.
 @item @command{syslinux_source} - Execute syslinux config in same context
 @item @command{test_blockarg} - Print and execute block argument., 0
 @item @command{testload} - Load the same file in multiple ways.
+@item @command{testpci} - Test if a PCI device is present.
 @item @command{testspeed} - Test file read speed.
 @item @command{tgatest} - Tests loading of TGA bitmap.
 @item @command{time} - Measure time used by COMMAND
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..aaeb45e55
--- /dev/null
+++ b/grub-core/commands/testpci.c
@@ -0,0 +1,167 @@
+/* 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", 0, 0, "read device list from file", "FILE", ARG_TYPE_STRING},
+  {0, 0, 0, 0, 0, 0}
+};
+
+struct grub_testpci_devlist {
+  char** devices;
+  int n_devices;
+  int s_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 = grub_xasprintf ("%04x:%04x", pciid & 0xFFFF, pciid >> 16);
+  for (int i = 0; i < devlist->n_devices; i++) {
+    if (grub_strcasecmp(device, devlist->devices[i]) == 0) {
+      grub_free(device);
+      devlist->found = true;
+      return 1;
+    }
+  }
+
+  grub_free(device);
+  return 0;
+}
+
+static void
+testpci_add_device_to_list(struct grub_testpci_devlist* devlist,
+                           char* device)
+{
+  if (devlist->n_devices == devlist->s_devices) {
+    devlist->s_devices *= 2;
+    devlist->devices = grub_realloc(devlist->devices,
+                                    devlist->s_devices * sizeof(char*));
+    if (!(devlist->devices)) {
+      return;
+    }
+  }
+  devlist->devices[devlist->n_devices++] = grub_strdup(device);
+}
+
+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.s_devices = argc + (ctxt->state[0].set ? 5 : 0);
+  devlist.devices = grub_malloc(devlist.s_devices * sizeof(char*));
+  if (!(devlist.devices)) {
+    return GRUB_ERR_OUT_OF_MEMORY;
+  }
+
+  for (int i = 0; i < argc; i++) {
+    testpci_add_device_to_list(&devlist, args[i]);
+    if (!(devlist.devices)) {
+      return GRUB_ERR_OUT_OF_MEMORY;
+    }
+  }
+
+  /* device list from file */
+  if (ctxt->state[0].set) {
+
+    grub_file_t listfile = grub_file_open(ctxt->state[0].arg, 
GRUB_FILE_TYPE_NONE);
+    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;
+
+        testpci_add_device_to_list(&devlist, p);
+        if (!(devlist.devices)) {
+          return GRUB_ERR_OUT_OF_MEMORY;
+        }
+
+      }
+
+      grub_file_close (listfile);
+    }
+  }
+
+  for (int d = 0 ; d < devlist.n_devices; d++) {
+    if (grub_strlen(devlist.devices[d]) != 9 || devlist.devices[d][4] != ':') {
+      grub_printf("bad input device (%d) \"%s\", expected xxxx:xxxx\n", d, 
devlist.devices[d]);
+    }
+  }
+
+  grub_pci_iterate (grub_testpci_iter, (void*)&devlist);
+
+  for (int i = 0; i < devlist.n_devices; i++) {
+    grub_free(devlist.devices[i]);
+  }
+  grub_free(devlist.devices);
+  return devlist.found ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE;
+}
+
+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);
+}
-- 
2.39.5


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to