From adc89895e05a4cdd5cd69bfc313fd09633f900f9 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Tue, 22 Dec 2009 16:42:45 +0100 Subject: [PATCH 1/3] setpci implementation --- commands/setpci.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++ conf/i386.rmk | 6 + 2 files changed, 346 insertions(+) create mode 100644 commands/setpci.c diff --git a/commands/setpci.c b/commands/setpci.c new file mode 100644 index 000000000..aa5e51de9 --- /dev/null +++ b/commands/setpci.c @@ -0,0 +1,340 @@ +/* lspci.c - List PCI devices. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008, 2009 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 . + */ + +#include +#include +#include +#include +#include +#include + +struct pci_register +{ + const char *name; + grub_uint16_t addr; + unsigned size; +}; + +struct pci_register pci_registers[] = + { + {"VENDOR_ID", GRUB_PCI_REG_VENDOR , 2}, + {"DEVICE_ID", GRUB_PCI_REG_DEVICE , 2}, + {"COMMAND", GRUB_PCI_REG_COMMAND , 2}, + {"STATUS", GRUB_PCI_REG_STATUS , 2}, + {"REVISION", GRUB_PCI_REG_REVISION , 1}, + {"CLASS_PROG", GRUB_PCI_REG_CLASS + 1 , 1}, + {"CLASS_DEVICE", GRUB_PCI_REG_CLASS + 2 , 2}, + {"CACHE_LINE_SIZE", GRUB_PCI_REG_CACHELINE , 1}, + {"LATENCY_TIMER", GRUB_PCI_REG_LAT_TIMER , 1}, + {"HEADER_TYPE", GRUB_PCI_REG_HEADER_TYPE , 1}, + {"BIST", GRUB_PCI_REG_BIST , 1}, + {"BASE_ADDRESS_0", GRUB_PCI_REG_ADDRESS_REG0, 4}, + {"BASE_ADDRESS_1", GRUB_PCI_REG_ADDRESS_REG1, 4}, + {"BASE_ADDRESS_2", GRUB_PCI_REG_ADDRESS_REG2, 4}, + {"BASE_ADDRESS_3", GRUB_PCI_REG_ADDRESS_REG3, 4}, + {"BASE_ADDRESS_4", GRUB_PCI_REG_ADDRESS_REG4, 4}, + {"BASE_ADDRESS_5", GRUB_PCI_REG_ADDRESS_REG5, 4}, + {"CARDBUS_CIS", GRUB_PCI_REG_CIS_POINTER , 4}, + {"SUBVENDOR_ID", GRUB_PCI_REG_SUBVENDOR , 2}, + {"SUBSYSTEM_ID", GRUB_PCI_REG_SUBSYSTEM , 2}, + {"ROM_ADDRESS", GRUB_PCI_REG_ROM_ADDRESS , 4}, + {"CAP_POINTER", GRUB_PCI_REG_CAP_POINTER , 1}, + {"INTERRUPT_LINE", GRUB_PCI_REG_IRQ_LINE , 1}, + {"INTERRUPT_PIN", GRUB_PCI_REG_IRQ_PIN , 1}, + {"MIN_GNT", GRUB_PCI_REG_MIN_GNT , 1}, + {"MAX_LAT", GRUB_PCI_REG_MIN_GNT , 1}, + }; + +static const struct grub_arg_option options[] = + { + {0, 'd', 0, "Select device by vendor and device IDs.", + "[vendor]:[device]", ARG_TYPE_STRING}, + {0, 's', 0, "Select device by its position on the bus.", + "[bus]:[slot][.func]", ARG_TYPE_STRING}, + {0, 'v', 0, "Save read value into variable VARNAME.", + "VARNAME", ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + +static grub_uint32_t pciid_check_mask, pciid_check_value; +static int bus, device, function; +static int check_bus, check_device, check_function; +static grub_uint32_t write_mask, regwrite; +static int regsize; +static grub_uint16_t regaddr; +static const char *varname; + +static int NESTED_FUNC_ATTR +grub_setpci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) +{ + grub_uint32_t regval = 0; + grub_pci_address_t addr; + + if ((pciid & pciid_check_mask) != pciid_check_value) + return 0; + + if (check_bus && grub_pci_get_bus (dev) != bus) + return 0; + + if (check_device && grub_pci_get_device (dev) != device) + return 0; + + if (check_function && grub_pci_get_function (dev) != device) + return 0; + + addr = grub_pci_make_address (dev, regaddr); + + switch (regsize) + { + case 1: + regval = grub_pci_read_byte (addr); + break; + + case 2: + regval = grub_pci_read_word (addr); + break; + + case 4: + regval = grub_pci_read (addr); + break; + } + + if (varname) + { + char buf[sizeof ("XXXXXXXX")]; + grub_sprintf (buf, "%x", regval); + grub_env_set (varname, buf); + return 1; + } + + if (!write_mask) + { + grub_printf ("Register %x of %d:%d.%d is %x\n", regaddr, + grub_pci_get_bus (dev), + grub_pci_get_device (dev), + grub_pci_get_function (dev), + regval); + return 0; + } + + regval = (regval & ~write_mask) | regwrite; + + switch (regsize) + { + case 1: + grub_pci_write_byte (addr, regval); + break; + + case 2: + grub_pci_write_word (addr, regval); + break; + + case 4: + grub_pci_write (addr, regval); + break; + } + + return 0; +} + +static grub_err_t +grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv) +{ + const char *ptr; + unsigned i; + + pciid_check_value = 0; + pciid_check_mask = 0; + + if (cmd->state[0].set) + { + ptr = cmd->state[0].arg; + pciid_check_value |= (grub_strtoul (ptr, (char **) &ptr, 16) & 0xffff); + if (grub_errno == GRUB_ERR_BAD_NUMBER) + { + grub_errno = GRUB_ERR_NONE; + ptr = cmd->state[0].arg; + } + else + pciid_check_mask |= 0xffff; + if (grub_errno) + return grub_errno; + if (*ptr != ':') + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Colon expected."); + ptr++; + pciid_check_value |= (grub_strtoul (ptr, (char **) &ptr, 16) & 0xffff) + << 16; + if (grub_errno == GRUB_ERR_BAD_NUMBER) + grub_errno = GRUB_ERR_NONE; + else + pciid_check_mask |= 0xffff0000; + } + + pciid_check_value &= pciid_check_mask; + + check_bus = check_device = check_function = 0; + + if (cmd->state[1].set) + { + const char *optr; + + ptr = cmd->state[1].arg; + optr = ptr; + bus = grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno == GRUB_ERR_BAD_NUMBER) + { + grub_errno = GRUB_ERR_NONE; + ptr = optr; + } + else + check_bus = 1; + if (grub_errno) + return grub_errno; + if (*ptr != ':') + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Colon expected."); + ptr++; + optr = ptr; + device = grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno == GRUB_ERR_BAD_NUMBER) + { + grub_errno = GRUB_ERR_NONE; + ptr = optr; + } + else + check_device = 1; + if (*ptr == '.') + { + ptr++; + function = grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno) + return grub_errno; + check_function = 1; + } + } + + if (cmd->state[2].set) + varname = cmd->state[2].arg; + else + varname = NULL; + + write_mask = 0; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Command expected."); + + if (argc > 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Only one command is supported."); + + ptr = argv[0]; + + for (i = 0; i < ARRAY_SIZE (pci_registers); i++) + { + if (grub_strncmp (ptr, pci_registers[i].name, + grub_strlen (pci_registers[i].name)) == 0) + break; + } + if (i == ARRAY_SIZE (pci_registers)) + { + regsize = 0; + regaddr = grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown register"); + } + else + { + regaddr = pci_registers[i].addr; + regsize = pci_registers[i].size; + ptr += grub_strlen (pci_registers[i].name); + } + + if (grub_errno) + return grub_errno; + + if (*ptr == '+') + { + ptr++; + regaddr += grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno) + return grub_errno; + } + + if (grub_memcmp (ptr, ".L", sizeof (".L") - 1) == 0 + || grub_memcmp (ptr, ".l", sizeof (".l") - 1) == 0) + { + regsize = 4; + ptr += sizeof (".l") - 1; + } + else if (grub_memcmp (ptr, ".W", sizeof (".W") - 1) == 0 + || grub_memcmp (ptr, ".w", sizeof (".w") - 1) == 0) + { + regsize = 2; + ptr += sizeof (".w") - 1; + } + else if (grub_memcmp (ptr, ".B", sizeof (".B") - 1) == 0 + || grub_memcmp (ptr, ".b", sizeof (".b") - 1) == 0) + { + regsize = 1; + ptr += sizeof (".b") - 1; + } + + if (!regsize) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "Unknown register size."); + + write_mask = 0; + if (*ptr == '=') + { + ptr++; + regwrite = grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno) + return grub_errno; + write_mask = 0xffffffff; + if (*ptr == ':') + { + ptr++; + write_mask = grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno) + return grub_errno; + write_mask = 0xffffffff; + } + regwrite &= write_mask; + } + + if (write_mask && varname) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "Option -v isn't valid for writes."); + + grub_pci_iterate (grub_setpci_iter); + return GRUB_ERR_NONE; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(setpci) +{ + cmd = grub_register_extcmd ("setpci", grub_cmd_setpci, GRUB_COMMAND_FLAG_BOTH, + "setpci [-s POSITION] [-d DEVICE] [-v VAR] " + "[REGISTER][=VALUE[:MASK]]", + "Manipulate PCI devices.", options); +} + +GRUB_MOD_FINI(setpci) +{ + grub_unregister_extcmd (cmd); +} diff --git a/conf/i386.rmk b/conf/i386.rmk index c3f036d0f..2c1edf62f 100644 --- a/conf/i386.rmk +++ b/conf/i386.rmk @@ -25,3 +25,9 @@ pkglib_MODULES += ata.mod ata_mod_SOURCES = disk/ata.c ata_mod_CFLAGS = $(COMMON_CFLAGS) ata_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For setpci.mod +pkglib_MODULES += setpci.mod +setpci_mod_SOURCES = commands/setpci.c +setpci_mod_CFLAGS = $(COMMON_CFLAGS) +setpci_mod_LDFLAGS = $(COMMON_LDFLAGS) From 281d82347e4c6fdfe31e5c3a18196f373651af4d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 27 Dec 2009 17:02:03 +0100 Subject: [PATCH 2/3] ChangeLog --- ChangeLog.setpci | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 ChangeLog.setpci diff --git a/ChangeLog.setpci b/ChangeLog.setpci new file mode 100644 index 000000000..e488379ce --- /dev/null +++ b/ChangeLog.setpci @@ -0,0 +1,9 @@ +2009-12-27 Vladimir Serbinenko + + setpci support. + + * commands/setpci.c: New file. + * conf/i386.rmk (pkglib_MODULES): Add setpci.mod. + (setpci_mod_SOURCES): New variable. + (setpci_mod_CFLAGS): Likewise. + (setpci_mod_LDFLAGS): Likewise. From baccdb23a716413b74faaae556e7fbad5eb2c951 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 20 Jan 2010 20:54:58 +0100 Subject: [PATCH 3/3] Use grub_snprintf in commands/setpci.c --- commands/setpci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/setpci.c b/commands/setpci.c index aa5e51de9..f780547a2 100644 --- a/commands/setpci.c +++ b/commands/setpci.c @@ -118,7 +118,7 @@ grub_setpci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) if (varname) { char buf[sizeof ("XXXXXXXX")]; - grub_sprintf (buf, "%x", regval); + grub_snprintf (buf, sizeof (buf), "%x", regval); grub_env_set (varname, buf); return 1; }