Index: sys/amd64/amd64/pmap.c =================================================================== --- sys/amd64/amd64/pmap.c +++ sys/amd64/amd64/pmap.c @@ -1468,6 +1468,13 @@ return (entry); } +boolean_t +pmap_is_valid_memattr(pmap_t pmap, int mode) +{ + + return (mode >= 0 && mode < PAT_INDEX_SIZE && pat_index[mode] >= 0); +} + /* * Determine the appropriate bits to set in a PTE or PDE for a specified * caching mode. @@ -1477,7 +1484,7 @@ { int cache_bits, pat_flag, pat_idx; - if (mode < 0 || mode >= PAT_INDEX_SIZE || pat_index[mode] < 0) + if (!pmap_is_valid_memattr(pmap, mode)) panic("Unknown caching mode %d\n", mode); switch (pmap->pm_type) { Index: sys/dev/pci/pci_user.c =================================================================== --- sys/dev/pci/pci_user.c +++ sys/dev/pci/pci_user.c @@ -31,6 +31,7 @@ #include "opt_bus.h" /* XXX trim includes */ +#include #include #include #include @@ -39,13 +40,19 @@ #include #include #include +#include #include #include -#include +#include +#include #include #include #include +#include +#include +#include +#include #include #include @@ -696,6 +703,75 @@ } } +static int +pci_bar_mmap(device_t pcidev, struct pci_bar_mmap *pbm) +{ + vm_map_t map; + vm_object_t obj; + struct thread *td; + struct sglist *sg; + struct pci_map *pm; + vm_paddr_t pbase; + vm_size_t plen; + vm_offset_t addr; + vm_prot_t prot; + int error, flags; + + td = curthread; + map = &td->td_proc->p_vmspace->vm_map; + if ((pbm->pbm_flags & ~(PCIIO_BAR_MMAP_FIXED | PCIIO_BAR_MMAP_EXCL | + PCIIO_BAR_MMAP_RW | PCIIO_BAR_MMAP_ACTIVATE)) != 0 || + !pmap_is_valid_memattr(map->pmap, pbm->pbm_memattr)) + return (EINVAL); + + /* Fetch the BAR physical base and length. */ + pm = pci_find_bar(pcidev, pbm->pbm_reg); + if (pm == NULL) + return (EINVAL); + if (!pci_bar_enabled(pcidev, pm)) + return (EBUSY); /* XXXKIB enable if _ACTIVATE */ + if (!PCI_BAR_MEM(pm->pm_value)) + return (EIO); + pbase = trunc_page(pm->pm_value); + plen = round_page((pci_addr_t)1 << pm->pm_size); + prot = VM_PROT_READ | (((pbm->pbm_flags & PCIIO_BAR_MMAP_RW) != 0) ? + VM_PROT_WRITE : 0); + + /* Create vm structures and mmap. */ + sg = sglist_alloc(1, M_WAITOK); + error = sglist_append_phys(sg, pbase, plen); + if (error != 0) + goto out; + obj = vm_pager_allocate(OBJT_SG, sg, plen, prot, 0, td->td_ucred); + if (obj == NULL) { + error = EIO; + goto out; + } + obj->memattr = pbm->pbm_memattr; + flags = MAP_SHARED; + addr = 0; + if ((pbm->pbm_flags & PCIIO_BAR_MMAP_FIXED) != 0) { + addr = pbm->pbm_map_base; + flags |= MAP_FIXED; + } + if ((pbm->pbm_flags & PCIIO_BAR_MMAP_EXCL) != 0) + flags |= MAP_CHECK_EXCL; + error = vm_mmap_object(map, &addr, plen, prot, prot, flags, obj, 0, + FALSE, td); + if (error != 0) { + vm_object_deallocate(obj); + goto out; + } + pbm->pbm_map_base = addr; + pbm->pbm_map_length = plen; + pbm->pbm_bar_off = pm->pm_value - pbase; + pbm->pbm_bar_length = (pci_addr_t)1 << pm->pm_size; + +out: + sglist_free(sg); + return (error); +} + static int pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { @@ -709,6 +785,7 @@ struct pci_list_vpd_io *lvio; struct pci_match_conf *pattern_buf; struct pci_map *pm; + struct pci_bar_mmap *pbm; size_t confsz, iolen; int error, ionum, i, num_patterns; union pci_conf_union pcu; @@ -730,6 +807,7 @@ #endif case PCIOCGETBAR: case PCIOCLISTVPD: + case PCIOCBARMMAP: break; default: return (EPERM); @@ -1053,6 +1131,18 @@ } error = pci_list_vpd(pcidev, lvio); break; + + case PCIOCBARMMAP: + pbm = (struct pci_bar_mmap *)data; + if ((flag & FWRITE) == 0 && + (pbm->pbm_flags & PCIIO_BAR_MMAP_RW) != 0) + return (EPERM); + pcidev = pci_find_dbsf(pbm->pbm_sel.pc_domain, + pbm->pbm_sel.pc_bus, pbm->pbm_sel.pc_dev, + pbm->pbm_sel.pc_func); + error = pcidev == NULL ? ENODEV : pci_bar_mmap(pcidev, pbm); + break; + default: error = ENOTTY; break; Index: sys/sys/pciio.h =================================================================== --- sys/sys/pciio.h +++ sys/sys/pciio.h @@ -138,11 +138,30 @@ struct pci_vpd_element *plvi_data; }; +struct pci_bar_mmap { + uint64_t pbm_map_base; /* (sometimes IN)/OUT mmaped base */ + uint64_t pbm_map_length; /* mapped length of the BAR, multiple + of pages */ + uint64_t pbm_bar_length; /* actual length of the BAR */ + int pbm_bar_off; /* offset from the mapped base to the + start of BAR */ + struct pcisel pbm_sel; /* device to operate on */ + int pbm_reg; /* starting address of BAR */ + int pbm_flags; + int pbm_memattr; +}; + +#define PCIIO_BAR_MMAP_FIXED 0x01 +#define PCIIO_BAR_MMAP_EXCL 0x02 +#define PCIIO_BAR_MMAP_RW 0x04 +#define PCIIO_BAR_MMAP_ACTIVATE 0x08 + #define PCIOCGETCONF _IOWR('p', 5, struct pci_conf_io) #define PCIOCREAD _IOWR('p', 2, struct pci_io) #define PCIOCWRITE _IOWR('p', 3, struct pci_io) #define PCIOCATTACHED _IOWR('p', 4, struct pci_io) #define PCIOCGETBAR _IOWR('p', 6, struct pci_bar_io) #define PCIOCLISTVPD _IOWR('p', 7, struct pci_list_vpd_io) +#define PCIOCBARMMAP _IOWR('p', 8, struct pci_bar_mmap) #endif /* !_SYS_PCIIO_H_ */ Index: sys/vm/pmap.h =================================================================== --- sys/vm/pmap.h +++ sys/vm/pmap.h @@ -142,6 +142,7 @@ boolean_t pmap_is_modified(vm_page_t m); boolean_t pmap_is_prefaultable(pmap_t pmap, vm_offset_t va); boolean_t pmap_is_referenced(vm_page_t m); +boolean_t pmap_is_valid_memattr(pmap_t, int); vm_offset_t pmap_map(vm_offset_t *, vm_paddr_t, vm_paddr_t, int); int pmap_mincore(pmap_t pmap, vm_offset_t addr, vm_paddr_t *locked_pa); Index: usr.sbin/pciconf/pciconf.c =================================================================== --- usr.sbin/pciconf/pciconf.c +++ usr.sbin/pciconf/pciconf.c @@ -34,6 +34,13 @@ #include #include +#include +#include +#include + +#include + +#include #include #include @@ -44,10 +51,6 @@ #include #include #include -#include -#include - -#include #include "pathnames.h" #include "pciconf.h" @@ -82,32 +85,35 @@ static void readit(const char *, const char *, int); static void writeit(const char *, const char *, const char *, int); static void chkattached(const char *); +static void dump_bar(const char *name, const char *reg, int width, int verbose); static int exitstatus = 0; static void usage(void) { - fprintf(stderr, "%s\n%s\n%s\n%s\n", - "usage: pciconf -l [-BbcevV] [device]", - " pciconf -a device", - " pciconf -r [-b | -h] device addr[:addr2]", - " pciconf -w [-b | -h] device addr value"); - exit (1); + + fprintf(stderr, "%s", + "usage: pciconf -l [-BbcevV] [device]\n" + " pciconf -a device\n" + " pciconf -r [-b | -h] device addr[:addr2]\n" + " pciconf -w [-b | -h] device addr value\n" + " pciconf -D [-b | -h | -x] device bar\n"); + exit(1); } int main(int argc, char **argv) { - int c; - int listmode, readmode, writemode, attachedmode; + int c, width; + int listmode, readmode, writemode, attachedmode, dumpbarmode; int bars, bridge, caps, errors, verbose, vpd; - int byte, isshort; - listmode = readmode = writemode = attachedmode = 0; - bars = bridge = caps = errors = verbose = vpd = byte = isshort = 0; + listmode = readmode = writemode = attachedmode = dumpbarmode = 0; + bars = bridge = caps = errors = verbose = vpd= 0; + width = 4; - while ((c = getopt(argc, argv, "aBbcehlrwVv")) != -1) { + while ((c = getopt(argc, argv, "aBbcDehlrwVv")) != -1) { switch(c) { case 'a': attachedmode = 1; @@ -119,19 +125,23 @@ case 'b': bars = 1; - byte = 1; + width = 1; break; case 'c': caps = 1; break; + case 'D': + dumpbarmode = 1; + break; + case 'e': errors = 1; break; case 'h': - isshort = 1; + width = 2; break; case 'l': @@ -154,6 +164,10 @@ vpd = 1; break; + case 'x': + width = 8; + break; + default: usage(); } @@ -162,7 +176,9 @@ if ((listmode && optind >= argc + 1) || (writemode && optind + 3 != argc) || (readmode && optind + 2 != argc) - || (attachedmode && optind + 1 != argc)) + || (attachedmode && optind + 1 != argc) + || (dumpbarmode && optind + 2 != argc) + || (width == 8 && !dumpbarmode)) usage(); if (listmode) { @@ -171,16 +187,17 @@ } else if (attachedmode) { chkattached(argv[optind]); } else if (readmode) { - readit(argv[optind], argv[optind + 1], - byte ? 1 : isshort ? 2 : 4); + readit(argv[optind], argv[optind + 1], width); } else if (writemode) { writeit(argv[optind], argv[optind + 1], argv[optind + 2], - byte ? 1 : isshort ? 2 : 4); + width); + } else if (dumpbarmode) { + dump_bar(argv[optind], argv[optind + 1], width, verbose); } else { usage(); } - return exitstatus; + return (exitstatus); } static void @@ -1027,3 +1044,88 @@ printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached"); close(fd); } + +static void +dump_bar(const char *name, const char *reg, int width, int verbose) +{ + struct pci_bar_mmap pbm; + uint32_t *dd; + uint16_t *dh; + uint8_t *db; + uint64_t *dx, a; + size_t res; + int fd; + + pbm.pbm_sel = getsel(name); + pbm.pbm_reg = strtoul(reg, (char **)0, 0); /* XXX error check */ + pbm.pbm_flags = 0; + pbm.pbm_memattr = VM_MEMATTR_UNCACHEABLE; /* XXX */ + + fd = open(_PATH_DEVPCI, O_RDONLY, 0); + if (fd < 0) + err(1, "%s", _PATH_DEVPCI); + + if (ioctl(fd, PCIOCBARMMAP, &pbm) < 0) + err(1, "ioctl(PCIOCBARMMAP)"); + + if (verbose) { + fprintf(stderr, + "Dumping pci%d:%d:%d:%d BAR %x mapped base %#jx " + "off %#x length %#jx\n", + pbm.pbm_sel.pc_domain, pbm.pbm_sel.pc_bus, + pbm.pbm_sel.pc_dev, pbm.pbm_sel.pc_func, + pbm.pbm_reg, pbm.pbm_map_base, pbm.pbm_bar_off, + pbm.pbm_bar_length); + } + switch (width) { + case 1: + for (a = 0; a < pbm.pbm_bar_length; a += width) { + db = (uint8_t *)(pbm.pbm_map_base + pbm.pbm_bar_off + + a); + res = fwrite(db, width, 1, stdout); + if (res != 1) { + errx(1, "error writing to stdout"); + break; + } + } + break; + case 2: + for (a = 0; a < pbm.pbm_bar_length; a += width) { + dh = (uint16_t *)(pbm.pbm_map_base + pbm.pbm_bar_off + + a); + res = fwrite(dh, width, 1, stdout); + if (res != 1) { + errx(1, "error writing to stdout"); + break; + } + } + break; + case 4: + for (a = 0; a < pbm.pbm_bar_length; a += width) { + dd = (uint32_t *)(pbm.pbm_map_base + pbm.pbm_bar_off + + a); + res = fwrite(dd, width, 1, stdout); + if (res != 1) { + errx(1, "error writing to stdout"); + break; + } + } + break; + case 8: + for (a = 0; a < pbm.pbm_bar_length; a += width) { + dx = (uint64_t *)(pbm.pbm_map_base + pbm.pbm_bar_off + + a); + res = fwrite(dx, width, 1, stdout); + if (res != 1) { + errx(1, "error writing to stdout"); + break; + } + } + break; + default: + errx(1, "invalid access width"); + } + + munmap((void *)pbm.pbm_map_base, pbm.pbm_map_length); + close(fd); +}