On Sun, Nov 09, 2008 at 09:33:24PM +0100, Martin Mares wrote: > Hello! > > I have released new pciutils. As witnessed by the changelog, it is just > a couple of bug fixes and minor improvements: Thanks for releasing 3.0.3! Last week, somebody calling themselves 'Chaser' joined the linux-pci IRC channel and asked: <Chaser> Is it possible to set maximum memory read request using setpci ? This turns out to be moderately hard since you have to use lspci to find the capability location, then look up the capability offset in the PCI spec, do the arithmetic and so on. One way we could make this easier is to allow symbolic names to refer to offsets from the starts of capabilities. Here's an initial attempt at doing that. It's kind of lame since it doesn't allow you to specify the register as capability.number; you have to use a name. Also, it needs documentation. And there's a lot of names we could add. We might want to put the capability-finding code in lib/access.c so we don't duplicate it between setpci and lspci. There's probably a lot of other things that could be done better too ;-) This doesn't entirely solve the initial problem posed by the user -- to just set the MAX_MEM_READ_RQ we'd want to have a way to specify a bitmask and do a Read-Modify-Write step. First things first, though, is this an interesting feature to add? diff --git a/setpci.c b/setpci.c index 3ebbaee..87a18c5 100644 --- a/setpci.c +++ b/setpci.c @@ -31,6 +31,7 @@ struct value { struct op { struct op *next; struct pci_dev **dev_vector; + signed int cap; /* Capability number */ unsigned int addr; unsigned int width; /* Byte width of the access */ int num_values; /* Number of values to write; <0=read */ @@ -57,6 +58,81 @@ select_devices(struct pci_filter *filt) return b; } +static int find_std_cap(struct pci_dev *dev, int cap) +{ + int where; + byte been_there[256]; + + if (!(pci_read_word(dev, PCI_STATUS) & PCI_STATUS_CAP_LIST)) + return -1; + + memset(been_there, 0, 256); + where = pci_read_byte(dev, PCI_CAPABILITY_LIST) & ~3; + for (;;) { + int id; + + if (where < 0x40 || (been_there[where]++)) + goto fail; + + id = pci_read_byte(dev, where + PCI_CAP_LIST_ID); + if (id == cap) + break; + if (id == 255) { + fprintf(stderr, "Cannot read device offset %d\n", where); + goto fail; + } + + where = pci_read_byte(dev, where + PCI_CAP_LIST_NEXT) & ~3; + } + + return where; + + fail: + return -1; +} + +static int find_ext_cap(struct pci_dev *dev, unsigned cap) +{ + int where; + byte been_there[4096]; + + if (!(pci_read_word(dev, PCI_STATUS) & PCI_STATUS_CAP_LIST)) + return -1; + + memset(been_there, 0, 4096); + where = 256; + for (;;) { + u32 header; + + if (where < 256 || (been_there[where]++)) + goto fail; + + header = pci_read_long(dev, where); + if ((header & 0xffff) == cap) + break; + if (header == 0xffffffff) { + fprintf(stderr, "Cannot read device offset %d\n", where); + goto fail; + } + + where = header >> 20; + } + + return where; + + fail: + return -1; +} + +static int find_cap(struct pci_dev *dev, int cap) +{ + if (cap == -1) + return 0; + if (cap & (1 << 31)) + return find_ext_cap(dev, cap & 0xffff); + return find_std_cap(dev, cap & 0xff); +} + static void exec_op(struct op *op, struct pci_dev *dev) { @@ -66,9 +142,15 @@ exec_op(struct op *op, struct pci_dev *dev) int i, addr; int width = op->width; + addr = find_cap(dev, op->cap); + if (addr == -1) { + fprintf(stderr, "Device %04x:%02x:%02x.%x does not have capability 0x%x\n", + dev->domain, dev->bus, dev->dev, dev->func, op->cap); + return; + } + addr += op->addr; if (verbose) - printf("%02x:%02x.%x:%02x", dev->bus, dev->dev, dev->func, op->addr); - addr = op->addr; + printf("%02x:%02x.%x:%02x", dev->bus, dev->dev, dev->func, addr); if (op->num_values >= 0) { for(i=0; i<op->num_values; i++) @@ -174,77 +256,79 @@ scan_ops(struct op *op) } struct reg_name { + signed int cap; unsigned int offset; unsigned int width; const char *name; }; static const struct reg_name pci_reg_names[] = { - { 0x00, 2, "VENDOR_ID", }, - { 0x02, 2, "DEVICE_ID", }, - { 0x04, 2, "COMMAND", }, - { 0x06, 2, "STATUS", }, - { 0x08, 1, "REVISION", }, - { 0x09, 1, "CLASS_PROG", }, - { 0x0a, 2, "CLASS_DEVICE", }, - { 0x0c, 1, "CACHE_LINE_SIZE", }, - { 0x0d, 1, "LATENCY_TIMER", }, - { 0x0e, 1, "HEADER_TYPE", }, - { 0x0f, 1, "BIST", }, - { 0x10, 4, "BASE_ADDRESS_0", }, - { 0x14, 4, "BASE_ADDRESS_1", }, - { 0x18, 4, "BASE_ADDRESS_2", }, - { 0x1c, 4, "BASE_ADDRESS_3", }, - { 0x20, 4, "BASE_ADDRESS_4", }, - { 0x24, 4, "BASE_ADDRESS_5", }, - { 0x28, 4, "CARDBUS_CIS", }, - { 0x2c, 4, "SUBSYSTEM_VENDOR_ID", }, - { 0x2e, 2, "SUBSYSTEM_ID", }, - { 0x30, 4, "ROM_ADDRESS", }, - { 0x3c, 1, "INTERRUPT_LINE", }, - { 0x3d, 1, "INTERRUPT_PIN", }, - { 0x3e, 1, "MIN_GNT", }, - { 0x3f, 1, "MAX_LAT", }, - { 0x18, 1, "PRIMARY_BUS", }, - { 0x19, 1, "SECONDARY_BUS", }, - { 0x1a, 1, "SUBORDINATE_BUS", }, - { 0x1b, 1, "SEC_LATENCY_TIMER", }, - { 0x1c, 1, "IO_BASE", }, - { 0x1d, 1, "IO_LIMIT", }, - { 0x1e, 2, "SEC_STATUS", }, - { 0x20, 2, "MEMORY_BASE", }, - { 0x22, 2, "MEMORY_LIMIT", }, - { 0x24, 2, "PREF_MEMORY_BASE", }, - { 0x26, 2, "PREF_MEMORY_LIMIT", }, - { 0x28, 4, "PREF_BASE_UPPER32", }, - { 0x2c, 4, "PREF_LIMIT_UPPER32", }, - { 0x30, 2, "IO_BASE_UPPER16", }, - { 0x32, 2, "IO_LIMIT_UPPER16", }, - { 0x38, 4, "BRIDGE_ROM_ADDRESS", }, - { 0x3e, 2, "BRIDGE_CONTROL", }, - { 0x10, 4, "CB_CARDBUS_BASE", }, - { 0x14, 2, "CB_CAPABILITIES", }, - { 0x16, 2, "CB_SEC_STATUS", }, - { 0x18, 1, "CB_BUS_NUMBER", }, - { 0x19, 1, "CB_CARDBUS_NUMBER", }, - { 0x1a, 1, "CB_SUBORDINATE_BUS", }, - { 0x1b, 1, "CB_CARDBUS_LATENCY", }, - { 0x1c, 4, "CB_MEMORY_BASE_0", }, - { 0x20, 4, "CB_MEMORY_LIMIT_0", }, - { 0x24, 4, "CB_MEMORY_BASE_1", }, - { 0x28, 4, "CB_MEMORY_LIMIT_1", }, - { 0x2c, 2, "CB_IO_BASE_0", }, - { 0x2e, 2, "CB_IO_BASE_0_HI", }, - { 0x30, 2, "CB_IO_LIMIT_0", }, - { 0x32, 2, "CB_IO_LIMIT_0_HI", }, - { 0x34, 2, "CB_IO_BASE_1", }, - { 0x36, 2, "CB_IO_BASE_1_HI", }, - { 0x38, 2, "CB_IO_LIMIT_1", }, - { 0x3a, 2, "CB_IO_LIMIT_1_HI", }, - { 0x40, 2, "CB_SUBSYSTEM_VENDOR_ID", }, - { 0x42, 2, "CB_SUBSYSTEM_ID", }, - { 0x44, 4, "CB_LEGACY_MODE_BASE", }, - { 0x00, 0, NULL } + { -1, 0x00, 2, "VENDOR_ID", }, + { -1, 0x02, 2, "DEVICE_ID", }, + { -1, 0x04, 2, "COMMAND", }, + { -1, 0x06, 2, "STATUS", }, + { -1, 0x08, 1, "REVISION", }, + { -1, 0x09, 1, "CLASS_PROG", }, + { -1, 0x0a, 2, "CLASS_DEVICE", }, + { -1, 0x0c, 1, "CACHE_LINE_SIZE", }, + { -1, 0x0d, 1, "LATENCY_TIMER", }, + { -1, 0x0e, 1, "HEADER_TYPE", }, + { -1, 0x0f, 1, "BIST", }, + { -1, 0x10, 4, "BASE_ADDRESS_0", }, + { -1, 0x14, 4, "BASE_ADDRESS_1", }, + { -1, 0x18, 4, "BASE_ADDRESS_2", }, + { -1, 0x1c, 4, "BASE_ADDRESS_3", }, + { -1, 0x20, 4, "BASE_ADDRESS_4", }, + { -1, 0x24, 4, "BASE_ADDRESS_5", }, + { -1, 0x28, 4, "CARDBUS_CIS", }, + { -1, 0x2c, 4, "SUBSYSTEM_VENDOR_ID", }, + { -1, 0x2e, 2, "SUBSYSTEM_ID", }, + { -1, 0x30, 4, "ROM_ADDRESS", }, + { -1, 0x3c, 1, "INTERRUPT_LINE", }, + { -1, 0x3d, 1, "INTERRUPT_PIN", }, + { -1, 0x3e, 1, "MIN_GNT", }, + { -1, 0x3f, 1, "MAX_LAT", }, + { -1, 0x18, 1, "PRIMARY_BUS", }, + { -1, 0x19, 1, "SECONDARY_BUS", }, + { -1, 0x1a, 1, "SUBORDINATE_BUS", }, + { -1, 0x1b, 1, "SEC_LATENCY_TIMER", }, + { -1, 0x1c, 1, "IO_BASE", }, + { -1, 0x1d, 1, "IO_LIMIT", }, + { -1, 0x1e, 2, "SEC_STATUS", }, + { -1, 0x20, 2, "MEMORY_BASE", }, + { -1, 0x22, 2, "MEMORY_LIMIT", }, + { -1, 0x24, 2, "PREF_MEMORY_BASE", }, + { -1, 0x26, 2, "PREF_MEMORY_LIMIT", }, + { -1, 0x28, 4, "PREF_BASE_UPPER32", }, + { -1, 0x2c, 4, "PREF_LIMIT_UPPER32", }, + { -1, 0x30, 2, "IO_BASE_UPPER16", }, + { -1, 0x32, 2, "IO_LIMIT_UPPER16", }, + { -1, 0x38, 4, "BRIDGE_ROM_ADDRESS", }, + { -1, 0x3e, 2, "BRIDGE_CONTROL", }, + { -1, 0x10, 4, "CB_CARDBUS_BASE", }, + { -1, 0x14, 2, "CB_CAPABILITIES", }, + { -1, 0x16, 2, "CB_SEC_STATUS", }, + { -1, 0x18, 1, "CB_BUS_NUMBER", }, + { -1, 0x19, 1, "CB_CARDBUS_NUMBER", }, + { -1, 0x1a, 1, "CB_SUBORDINATE_BUS", }, + { -1, 0x1b, 1, "CB_CARDBUS_LATENCY", }, + { -1, 0x1c, 4, "CB_MEMORY_BASE_0", }, + { -1, 0x20, 4, "CB_MEMORY_LIMIT_0", }, + { -1, 0x24, 4, "CB_MEMORY_BASE_1", }, + { -1, 0x28, 4, "CB_MEMORY_LIMIT_1", }, + { -1, 0x2c, 2, "CB_IO_BASE_0", }, + { -1, 0x2e, 2, "CB_IO_BASE_0_HI", }, + { -1, 0x30, 2, "CB_IO_LIMIT_0", }, + { -1, 0x32, 2, "CB_IO_LIMIT_0_HI", }, + { -1, 0x34, 2, "CB_IO_BASE_1", }, + { -1, 0x36, 2, "CB_IO_BASE_1_HI", }, + { -1, 0x38, 2, "CB_IO_LIMIT_1", }, + { -1, 0x3a, 2, "CB_IO_LIMIT_1_HI", }, + { -1, 0x40, 2, "CB_SUBSYSTEM_VENDOR_ID", }, + { -1, 0x42, 2, "CB_SUBSYSTEM_ID", }, + { -1, 0x44, 4, "CB_LEGACY_MODE_BASE", }, + { 0x10, 0x08, 2, "EXP_DEVCTL", }, + { -1, 0x00, 0, NULL } }; static void NONRET PCI_PRINTF(1,2) @@ -461,6 +545,7 @@ next: if (e && op->width != r->width) usage("Explicit width doesn't correspond with the named register \"%s\"", c); ll = r->offset; + op->cap = r->cap; op->width = r->width; } if (ll > 0x1000 || ll + op->width*((n < 0) ? 1 : n) > 0x1000) -- Matthew Wilcox Intel Open Source Technology Centre "Bill, look, we understand that you're interested in selling us this operating system, but compare it to ours. We can't possibly take such a retrograde step." -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html