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 Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel