Index: lib/libkvm/kvm_minidump_mips.c =================================================================== --- lib/libkvm/kvm_minidump_mips.c +++ lib/libkvm/kvm_minidump_mips.c @@ -45,6 +45,8 @@ #include #include +#include "../../sys/mips/include/cca.h" +#define _KVM_MINIDUMP #include "../../sys/mips/include/cpuregs.h" #include "../../sys/mips/include/minidump.h" Index: share/man/man4/pci.4 =================================================================== --- share/man/man4/pci.4 +++ share/man/man4/pci.4 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 20, 2017 +.Dd June 14, 2018 .Dt PCI 4 .Os .Sh NAME @@ -333,6 +333,72 @@ reading registers, above, also apply to writing .Tn PCI configuration registers. +.It PCIOCBARMMAP +This +.Xr ioctl 2 +command allows userspace processes to +.Xr mmap 2 +the memory-mapped PCI BAR into its address space. +The input parameters and results are passed in the +.Va pci_bar_mmap +structure, which has the following fields: +.Bl -tag -width Vt struct pcise pbm_sel +.It Vt uint64_t pbm_map_base +Reports the established mapping base to the caller. +If +.Va PCIIO_BAR_MMAP_FIXED +flag was specified, then this field must be filled before the call +with the desired address for the mapping. +.It Vt uint64_t pbm_map_length +Reports the mapped length of the BAR, in bytes. +Its .Vt uint64_t value is always multiple of machine pages. +.It Vt int64_t pbm_bar_length +Reports length of the bar as exposed by the device. +.It Vt int pbm_bar_off +Reports offset from the mapped base to the start of the +first register in the bar. +.It Vt struct pcisel pbm_sel +Should be filled before the call. +Describes the device to operate on. +.It Vt int pbm_reg +The BAR index to mmap. +.It Vt int pbm_flags +Flags which augments the operation. +See below. +.It Vt int pbm_memattr +The caching attribute for the mapping. +Typical values are +.Dv VM_MEMATTR_UNCACHEABLE +for control registers BARs, and +.Dv VM_MEMATTR_WRITE_COMBINING +for frame buffers. +Regular memory-like BAR should be mapped with +.Dv VM_MEMATTR_DEFAULT +attribute. +.El +.Pp +Currently defined flags are: +.Bl -tag -width PCIIO_BAR_MMAP_ACTIVATE +.It PCIIO_BAR_MMAP_FIXED +The resulted mappings should be established at the address +specified by the +.Va pbm_map_base +member, otherwise fail. +.It PCIIO_BAR_MMAP_EXCL +Must be used together with +.Vd PCIIO_BAR_MMAP_FIXED +If the specified base contains already established mappings, the +operation fails instead of implicitly unmapping them. +.It PCIIO_BAR_MMAP_RW +The requested mapping allows both reading and writing. +Without the flag, read-only mapping is established. +Note that it is common for the device registers to have side-effects +even on reads. +.It PCIIO_BAR_MMAP_ACTIVATE +(Unimplemented) If the BAR is not activated, activate it in the course +of mapping. +Currently attempt to mmap an inactive BAR results in error. +.El .El .Sh LOADER TUNABLES Tunables can be set at the Index: sys/amd64/amd64/pmap.c =================================================================== --- sys/amd64/amd64/pmap.c +++ sys/amd64/amd64/pmap.c @@ -1468,6 +1468,14 @@ return (entry); } +boolean_t +pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) +{ + + return (mode >= 0 && mode < PAT_INDEX_SIZE && + pat_index[(int)mode] >= 0); +} + /* * Determine the appropriate bits to set in a PTE or PDE for a specified * caching mode. @@ -1477,7 +1485,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/arm/arm/pmap-v4.c =================================================================== --- sys/arm/arm/pmap-v4.c +++ sys/arm/arm/pmap-v4.c @@ -4859,4 +4859,9 @@ panic("Can't change memattr on page with existing mappings"); } +boolean_t +pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) +{ + return (mode == VM_MEMATTR_DEFAULT || mode == VM_MEMATTR_UNCACHEABLE); +} Index: sys/arm/arm/pmap-v6.c =================================================================== --- sys/arm/arm/pmap-v6.c +++ sys/arm/arm/pmap-v6.c @@ -393,12 +393,21 @@ CTASSERT(VM_MEMATTR_DEVICE == 2); CTASSERT(VM_MEMATTR_SO == 3); CTASSERT(VM_MEMATTR_WRITE_THROUGH == 4); +#define VM_MEMATTR_END (VM_MEMATTR_WRITE_THROUGH + 1) + +boolean_t +pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) +{ + + return (mode >= 0 && mode < VM_MEMATTR_END); +} static inline uint32_t vm_memattr_to_pte2(vm_memattr_t ma) { - KASSERT((u_int)ma < 5, ("%s: bad vm_memattr_t %d", __func__, ma)); + KASSERT((u_int)ma < VM_MEMATTR_END, + ("%s: bad vm_memattr_t %d", __func__, ma)); return (pte2_attr_tab[(u_int)ma]); } Index: sys/arm64/arm64/pmap.c =================================================================== --- sys/arm64/arm64/pmap.c +++ sys/arm64/arm64/pmap.c @@ -5102,3 +5102,10 @@ } } } + +boolean_t +pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) +{ + + return (mode >= VM_MEMATTR_DEVICE && mode <= VM_MEMATTR_WRITE_THROUGH); +} Index: sys/compat/freebsd32/freebsd32_ioctl.h =================================================================== --- sys/compat/freebsd32/freebsd32_ioctl.h +++ sys/compat/freebsd32/freebsd32_ioctl.h @@ -95,11 +95,23 @@ u_int32_t status; /* request status */ }; +struct pci_bar_mmap32 { + uint32_t pbm_map_base; + uint32_t pbm_map_length; + uint32_t pbm_bar_length1, pbm_bar_length2; + int pbm_bar_off; + struct pcisel pbm_sel; + int pbm_reg; + int pbm_flags; + int pbm_memattr; +}; + #define CDIOREADTOCENTRYS_32 _IOWR('c', 5, struct ioc_read_toc_entry32) #define FIODGNAME_32 _IOW('f', 120, struct fiodgname_arg32) #define MEMRANGE_GET32 _IOWR('m', 50, struct mem_range_op32) #define MEMRANGE_SET32 _IOW('m', 51, struct mem_range_op32) #define PCIOCGETCONF_32 _IOWR('p', 5, struct pci_conf_io32) #define SG_IO_32 _IOWR(SGIOC, 0x85, struct sg_io_hdr32) +#define PCIOCBARMMAP_32 _IOWR('p', 8, struct pci_bar_mmap32) #endif /* _COMPAT_FREEBSD32_IOCTL_H_ */ Index: sys/compat/freebsd32/freebsd32_ioctl.c =================================================================== --- sys/compat/freebsd32/freebsd32_ioctl.c +++ sys/compat/freebsd32/freebsd32_ioctl.c @@ -53,6 +53,7 @@ #include #include +#include #include CTASSERT(sizeof(struct ioc_read_toc_entry32) == 8); @@ -248,6 +249,40 @@ return (error); } +static int +freebsd32_ioctl_barmmap(struct thread *td, + struct freebsd32_ioctl_args *uap, struct file *fp) +{ + struct pci_bar_mmap32 pbm32; + struct pci_bar_mmap pbm; + int error; + + error = copyin(uap->data, &pbm32, sizeof(pbm32)); + if (error != 0) + return (error); + PTRIN_CP(pbm32, pbm, pbm_map_base); + CP(pbm32, pbm, pbm_sel); + CP(pbm32, pbm, pbm_reg); + CP(pbm32, pbm, pbm_flags); + CP(pbm32, pbm, pbm_memattr); + pbm.pbm_bar_length = PAIR32TO64(uint64_t, pbm32.pbm_bar_length); + error = fo_ioctl(fp, PCIOCBARMMAP, (caddr_t)&pbm, td->td_ucred, td); + if (error == 0) { + PTROUT_CP(pbm, pbm32, pbm_map_base); + CP(pbm, pbm32, pbm_map_length); +#if BYTE_ORDER == LITTLE_ENDIAN + pbm32.pbm_bar_length1 = pbm.pbm_bar_length; + pbm32.pbm_bar_length2 = pbm.pbm_bar_length >> 32; +#else + pbm32.pbm_bar_length1 = pbm.pbm_bar_length >> 32; + pbm32.pbm_bar_length2 = pbm.pbm_bar_length; +#endif + CP(pbm, pbm32, pbm_bar_off); + error = copyout(&pbm32, uap->data, sizeof(pbm32)); + } + return (error); +} + static int freebsd32_ioctl_sg(struct thread *td, struct freebsd32_ioctl_args *uap, struct file *fp) @@ -355,6 +390,10 @@ error = freebsd32_ioctl_sg(td, uap, fp); break; + case PCIOCBARMMAP_32: + error = freebsd32_ioctl_barmmap(td, uap, fp); + break; + default: fdrop(fp, td); ap.fd = uap->fd; @@ -364,5 +403,5 @@ } fdrop(fp, td); - return error; + return (error); } 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,77 @@ } } +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 || + pbm->pbm_memattr != (vm_memattr_t)pbm->pbm_memattr || + !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(pm->pm_value + ((pci_addr_t)1 << pm->pm_size)) - + pbase; + 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 = (uintptr_t)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 = (void *)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 +787,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 +809,7 @@ #endif case PCIOCGETBAR: case PCIOCLISTVPD: + case PCIOCBARMMAP: break; default: return (EPERM); @@ -1053,6 +1133,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/i386/i386/copyout.c =================================================================== --- sys/i386/i386/copyout.c +++ sys/i386/i386/copyout.c @@ -130,7 +130,8 @@ } for (i = 0, pte = vtopte(kaddr); i < plen; i++, pte++) { *pte = PG_V | PG_RW | PG_A | PG_M | VM_PAGE_TO_PHYS(m[i]) | - pmap_cache_bits(pmap_page_get_memattr(m[i]), FALSE); + pmap_cache_bits(kernel_pmap, pmap_page_get_memattr(m[i]), + FALSE); invlpg(kaddr + ptoa(i)); } kaddr += uva - trunc_page(uva); Index: sys/i386/i386/pmap.c =================================================================== --- sys/i386/i386/pmap.c +++ sys/i386/i386/pmap.c @@ -1039,16 +1039,24 @@ * Low level helper routines..... ***************************************************/ +boolean_t +pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) +{ + + return (mode >= 0 && mode < PAT_INDEX_SIZE && + pat_index[(int)mode] >= 0); +} + /* * Determine the appropriate bits to set in a PTE or PDE for a specified * caching mode. */ int -pmap_cache_bits(int mode, boolean_t is_pde) +pmap_cache_bits(pmap_t pmap, int mode, boolean_t is_pde) { 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); /* The PAT bit is different for PTE's and PDE's. */ @@ -1718,7 +1726,8 @@ pt_entry_t *pte; pte = vtopte(va); - pte_store(pte, pa | PG_RW | PG_V | pmap_cache_bits(mode, 0)); + pte_store(pte, pa | PG_RW | PG_V | pmap_cache_bits(kernel_pmap, + mode, 0)); } /* @@ -1813,7 +1822,8 @@ endpte = pte + count; while (pte < endpte) { m = *ma++; - pa = VM_PAGE_TO_PHYS(m) | pmap_cache_bits(m->md.pat_mode, 0); + pa = VM_PAGE_TO_PHYS(m) | pmap_cache_bits(kernel_pmap, + m->md.pat_mode, 0); if ((*pte & (PG_FRAME | PG_PTE_CACHE)) != pa) { oldpte |= *pte; #if defined(PAE) || defined(PAE_TABLES) @@ -3716,7 +3726,8 @@ /* * Now validate mapping with desired protection/wiring. */ - newpte = (pt_entry_t)(pa | pmap_cache_bits(m->md.pat_mode, 0) | PG_V); + newpte = (pt_entry_t)(pa | pmap_cache_bits(pmap, m->md.pat_mode, 0) | + PG_V); if ((prot & VM_PROT_WRITE) != 0) { newpte |= PG_RW; if ((newpte & PG_MANAGED) != 0) @@ -3806,7 +3817,7 @@ " in pmap %p", va, pmap); return (FALSE); } - newpde = VM_PAGE_TO_PHYS(m) | pmap_cache_bits(m->md.pat_mode, 1) | + newpde = VM_PAGE_TO_PHYS(m) | pmap_cache_bits(pmap, m->md.pat_mode, 1) | PG_PS | PG_V; if ((m->oflags & VPO_UNMANAGED) == 0) { newpde |= PG_MANAGED; @@ -3994,7 +4005,7 @@ */ pmap->pm_stats.resident_count++; - pa = VM_PAGE_TO_PHYS(m) | pmap_cache_bits(m->md.pat_mode, 0); + pa = VM_PAGE_TO_PHYS(m) | pmap_cache_bits(pmap, m->md.pat_mode, 0); #if defined(PAE) || defined(PAE_TABLES) if ((prot & VM_PROT_EXECUTE) == 0) pa |= pg_nx; @@ -4082,8 +4093,8 @@ * "pa" will not affect the termination of this loop. */ PMAP_LOCK(pmap); - for (pa = ptepa | pmap_cache_bits(pat_mode, 1); pa < ptepa + - size; pa += NBPDR) { + for (pa = ptepa | pmap_cache_bits(pmap, pat_mode, 1); + pa < ptepa + size; pa += NBPDR) { pde = pmap_pde(pmap, addr); if (*pde == 0) { pde_store(pde, pa | PG_PS | PG_M | PG_A | @@ -4348,7 +4359,7 @@ if (*cmap_pte2) panic("pmap_zero_page: CMAP2 busy"); *cmap_pte2 = PG_V | PG_RW | VM_PAGE_TO_PHYS(m) | PG_A | PG_M | - pmap_cache_bits(m->md.pat_mode, 0); + pmap_cache_bits(kernel_pmap, m->md.pat_mode, 0); invlcaddr(pc->pc_cmap_addr2); pagezero(pc->pc_cmap_addr2); *cmap_pte2 = 0; @@ -4379,7 +4390,7 @@ if (*cmap_pte2) panic("pmap_zero_page_area: CMAP2 busy"); *cmap_pte2 = PG_V | PG_RW | VM_PAGE_TO_PHYS(m) | PG_A | PG_M | - pmap_cache_bits(m->md.pat_mode, 0); + pmap_cache_bits(kernel_pmap, m->md.pat_mode, 0); invlcaddr(pc->pc_cmap_addr2); if (off == 0 && size == PAGE_SIZE) pagezero(pc->pc_cmap_addr2); @@ -4409,10 +4420,10 @@ if (*cmap_pte2) panic("pmap_copy_page: CMAP2 busy"); *cmap_pte1 = PG_V | VM_PAGE_TO_PHYS(src) | PG_A | - pmap_cache_bits(src->md.pat_mode, 0); + pmap_cache_bits(kernel_pmap, src->md.pat_mode, 0); invlcaddr(pc->pc_cmap_addr1); *cmap_pte2 = PG_V | PG_RW | VM_PAGE_TO_PHYS(dst) | PG_A | PG_M | - pmap_cache_bits(dst->md.pat_mode, 0); + pmap_cache_bits(kernel_pmap, dst->md.pat_mode, 0); invlcaddr(pc->pc_cmap_addr2); bcopy(pc->pc_cmap_addr1, pc->pc_cmap_addr2, PAGE_SIZE); *cmap_pte1 = 0; @@ -4451,10 +4462,10 @@ b_pg_offset = b_offset & PAGE_MASK; cnt = min(cnt, PAGE_SIZE - b_pg_offset); *cmap_pte1 = PG_V | VM_PAGE_TO_PHYS(a_pg) | PG_A | - pmap_cache_bits(a_pg->md.pat_mode, 0); + pmap_cache_bits(kernel_pmap, a_pg->md.pat_mode, 0); invlcaddr(pc->pc_cmap_addr1); *cmap_pte2 = PG_V | PG_RW | VM_PAGE_TO_PHYS(b_pg) | PG_A | - PG_M | pmap_cache_bits(b_pg->md.pat_mode, 0); + PG_M | pmap_cache_bits(kernel_pmap, b_pg->md.pat_mode, 0); invlcaddr(pc->pc_cmap_addr2); a_cp = pc->pc_cmap_addr1 + a_pg_offset; b_cp = pc->pc_cmap_addr2 + b_pg_offset; @@ -5426,7 +5437,8 @@ if (*cmap_pte2) panic("pmap_flush_page: CMAP2 busy"); *cmap_pte2 = PG_V | PG_RW | VM_PAGE_TO_PHYS(m) | - PG_A | PG_M | pmap_cache_bits(m->md.pat_mode, 0); + PG_A | PG_M | pmap_cache_bits(kernel_pmap, m->md.pat_mode, + 0); invlcaddr(pc->pc_cmap_addr2); sva = (vm_offset_t)pc->pc_cmap_addr2; eva = sva + PAGE_SIZE; @@ -5487,8 +5499,8 @@ if (base < VM_MIN_KERNEL_ADDRESS) return (EINVAL); - cache_bits_pde = pmap_cache_bits(mode, 1); - cache_bits_pte = pmap_cache_bits(mode, 0); + cache_bits_pde = pmap_cache_bits(kernel_pmap, mode, 1); + cache_bits_pte = pmap_cache_bits(kernel_pmap, mode, 0); changed = FALSE; /* @@ -5694,7 +5706,7 @@ KASSERT(*pte == 0, ("pmap_quick_enter_page: PTE busy")); *pte = PG_V | PG_RW | VM_PAGE_TO_PHYS(m) | PG_A | PG_M | - pmap_cache_bits(pmap_page_get_memattr(m), 0); + pmap_cache_bits(kernel_pmap, pmap_page_get_memattr(m), 0); invlpg(qaddr); return (qaddr); @@ -5745,7 +5757,7 @@ VM_ALLOC_NORMAL | VM_ALLOC_WIRED | VM_ALLOC_WAITOK); pte_store(&trm_pte[atop(af - prev_addr)], VM_PAGE_TO_PHYS(m) | PG_M | PG_A | PG_RW | PG_V | pgeflag | - pmap_cache_bits(VM_MEMATTR_DEFAULT, FALSE)); + pmap_cache_bits(kernel_pmap, VM_MEMATTR_DEFAULT, FALSE)); } *addrp = prev_addr; return (0); @@ -5766,7 +5778,7 @@ if ((pd_m->flags & PG_ZERO) == 0) pmap_zero_page(pd_m); PTD[TRPTDI] = VM_PAGE_TO_PHYS(pd_m) | PG_M | PG_A | PG_RW | PG_V | - pmap_cache_bits(VM_MEMATTR_DEFAULT, TRUE); + pmap_cache_bits(kernel_pmap, VM_MEMATTR_DEFAULT, TRUE); } void * Index: sys/i386/i386/vm_machdep.c =================================================================== --- sys/i386/i386/vm_machdep.c +++ sys/i386/i386/vm_machdep.c @@ -584,7 +584,7 @@ ptep = vtopte(sf->kva); opte = *ptep; *ptep = VM_PAGE_TO_PHYS(sf->m) | PG_RW | PG_V | - pmap_cache_bits(sf->m->md.pat_mode, 0); + pmap_cache_bits(kernel_pmap, sf->m->md.pat_mode, 0); /* * Avoid unnecessary TLB invalidations: If the sf_buf's old Index: sys/i386/include/pmap.h =================================================================== --- sys/i386/include/pmap.h +++ sys/i386/include/pmap.h @@ -376,7 +376,7 @@ * vtopte(). */ void pmap_bootstrap(vm_paddr_t); -int pmap_cache_bits(int mode, boolean_t is_pde); +int pmap_cache_bits(pmap_t, int mode, boolean_t is_pde); int pmap_change_attr(vm_offset_t, vm_size_t, int); void pmap_init_pat(void); void pmap_kenter(vm_offset_t va, vm_paddr_t pa); Index: sys/mips/include/cca.h =================================================================== --- /dev/null +++ sys/mips/include/cca.h @@ -0,0 +1,153 @@ +/* $NetBSD: cpuregs.h,v 1.70 2006/05/15 02:26:54 simonb Exp $ */ + +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)machConst.h 8.1 (Berkeley) 6/10/93 + * + * machConst.h -- + * + * Machine dependent constants. + * + * Copyright (C) 1989 Digital Equipment Corporation. + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appears in all copies. + * Digital Equipment Corporation makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * from: Header: /sprite/src/kernel/mach/ds3100.md/RCS/machConst.h, + * v 9.2 89/10/21 15:55:22 jhh Exp SPRITE (DECWRL) + * from: Header: /sprite/src/kernel/mach/ds3100.md/RCS/machAddrs.h, + * v 1.2 89/08/15 18:28:21 rab Exp SPRITE (DECWRL) + * from: Header: /sprite/src/kernel/vm/ds3100.md/RCS/vmPmaxConst.h, + * v 9.1 89/09/18 17:33:00 shirriff Exp SPRITE (DECWRL) + * + * $FreeBSD$ + */ + +#ifndef _MIPS_CCA_H_ +#define _MIPS_CCA_H_ + +/* + * Cache Coherency Attributes: + * UC: Uncached. + * UA: Uncached accelerated. + * C: Cacheable, coherency unspecified. + * CNC: Cacheable non-coherent. + * CC: Cacheable coherent. + * CCS: Cacheable coherent, shared read. + * CCE: Cacheable coherent, exclusive read. + * CCEW: Cacheable coherent, exclusive write. + * CCUOW: Cacheable coherent, update on write. + * + * Note that some bits vary in meaning across implementations (and that the + * listing here is no doubt incomplete) and that the optimal cached mode varies + * between implementations. 0x02 is required to be UC and 0x03 is required to + * be a least C. + * + * We define the following logical bits: + * UNCACHED: + * The optimal uncached mode for the target CPU type. This must + * be suitable for use in accessing memory-mapped devices. + * CACHED: The optional cached mode for the target CPU type. + */ + +#define MIPS_CCA_UC 0x02 /* Uncached. */ +#define MIPS_CCA_C 0x03 /* Cacheable, coherency unspecified. */ + +#if defined(CPU_R4000) || defined(CPU_R10000) +#define MIPS_CCA_CNC 0x03 +#define MIPS_CCA_CCE 0x04 +#define MIPS_CCA_CCEW 0x05 + +#ifdef CPU_R4000 +#define MIPS_CCA_CCUOW 0x06 +#endif + +#ifdef CPU_R10000 +#define MIPS_CCA_UA 0x07 +#endif + +#define MIPS_CCA_CACHED MIPS_CCA_CCEW +#endif /* defined(CPU_R4000) || defined(CPU_R10000) */ + +#if defined(CPU_SB1) +#define MIPS_CCA_CC 0x05 /* Cacheable Coherent. */ +#endif + +#if defined(CPU_MIPS74K) +#define MIPS_CCA_UNCACHED 0x02 +#define MIPS_CCA_CACHED 0x03 +#endif + +/* + * 1004K and 1074K cores, as well as interAptiv and proAptiv cores, support + * Cacheable Coherent CCAs 0x04 and 0x05, as well as Cacheable non-Coherent + * CCA 0x03 and Uncached Accelerated CCA 0x07 + */ +#if defined(CPU_MIPS1004K) || defined(CPU_MIPS1074K) || \ + defined(CPU_INTERAPTIV) || defined(CPU_PROAPTIV) +#define MIPS_CCA_CNC 0x03 +#define MIPS_CCA_CCE 0x04 +#define MIPS_CCA_CCS 0x05 +#define MIPS_CCA_UA 0x07 + +/* We use shared read CCA for CACHED CCA */ +#define MIPS_CCA_CACHED MIPS_CCA_CCS +#endif + +#if defined(CPU_XBURST) +#define MIPS_CCA_UA 0x01 +#define MIPS_CCA_WC MIPS_CCA_UA +#endif + +#ifndef MIPS_CCA_UNCACHED +#define MIPS_CCA_UNCACHED MIPS_CCA_UC +#endif + +/* + * If we don't know which cached mode to use and there is a cache coherent + * mode, use it. If there is not a cache coherent mode, use the required + * cacheable mode. + */ +#ifndef MIPS_CCA_CACHED +#ifdef MIPS_CCA_CC +#define MIPS_CCA_CACHED MIPS_CCA_CC +#else +#define MIPS_CCA_CACHED MIPS_CCA_C +#endif +#endif + +#endif Index: sys/mips/include/cpuregs.h =================================================================== --- sys/mips/include/cpuregs.h +++ sys/mips/include/cpuregs.h @@ -60,6 +60,10 @@ #ifndef _MIPS_CPUREGS_H_ #define _MIPS_CPUREGS_H_ +#ifndef _KVM_MINIDUMP +#include +#endif + /* * Address space. * 32-bit mips CPUS partition their 32-bit address space into four segments: @@ -105,96 +109,6 @@ #define MIPS_IS_VALID_PTR(x) (MIPS_IS_KSEG0_ADDR(x) || \ MIPS_IS_KSEG1_ADDR(x)) -/* - * Cache Coherency Attributes: - * UC: Uncached. - * UA: Uncached accelerated. - * C: Cacheable, coherency unspecified. - * CNC: Cacheable non-coherent. - * CC: Cacheable coherent. - * CCS: Cacheable coherent, shared read. - * CCE: Cacheable coherent, exclusive read. - * CCEW: Cacheable coherent, exclusive write. - * CCUOW: Cacheable coherent, update on write. - * - * Note that some bits vary in meaning across implementations (and that the - * listing here is no doubt incomplete) and that the optimal cached mode varies - * between implementations. 0x02 is required to be UC and 0x03 is required to - * be a least C. - * - * We define the following logical bits: - * UNCACHED: - * The optimal uncached mode for the target CPU type. This must - * be suitable for use in accessing memory-mapped devices. - * CACHED: The optional cached mode for the target CPU type. - */ - -#define MIPS_CCA_UC 0x02 /* Uncached. */ -#define MIPS_CCA_C 0x03 /* Cacheable, coherency unspecified. */ - -#if defined(CPU_R4000) || defined(CPU_R10000) -#define MIPS_CCA_CNC 0x03 -#define MIPS_CCA_CCE 0x04 -#define MIPS_CCA_CCEW 0x05 - -#ifdef CPU_R4000 -#define MIPS_CCA_CCUOW 0x06 -#endif - -#ifdef CPU_R10000 -#define MIPS_CCA_UA 0x07 -#endif - -#define MIPS_CCA_CACHED MIPS_CCA_CCEW -#endif /* defined(CPU_R4000) || defined(CPU_R10000) */ - -#if defined(CPU_SB1) -#define MIPS_CCA_CC 0x05 /* Cacheable Coherent. */ -#endif - -#if defined(CPU_MIPS74K) -#define MIPS_CCA_UNCACHED 0x02 -#define MIPS_CCA_CACHED 0x03 -#endif - -/* - * 1004K and 1074K cores, as well as interAptiv and proAptiv cores, support - * Cacheable Coherent CCAs 0x04 and 0x05, as well as Cacheable non-Coherent - * CCA 0x03 and Uncached Accelerated CCA 0x07 - */ -#if defined(CPU_MIPS1004K) || defined(CPU_MIPS1074K) || \ - defined(CPU_INTERAPTIV) || defined(CPU_PROAPTIV) -#define MIPS_CCA_CNC 0x03 -#define MIPS_CCA_CCE 0x04 -#define MIPS_CCA_CCS 0x05 -#define MIPS_CCA_UA 0x07 - -/* We use shared read CCA for CACHED CCA */ -#define MIPS_CCA_CACHED MIPS_CCA_CCS -#endif - -#if defined(CPU_XBURST) -#define MIPS_CCA_UA 0x01 -#define MIPS_CCA_WC MIPS_CCA_UA -#endif - -#ifndef MIPS_CCA_UNCACHED -#define MIPS_CCA_UNCACHED MIPS_CCA_UC -#endif - -/* - * If we don't know which cached mode to use and there is a cache coherent - * mode, use it. If there is not a cache coherent mode, use the required - * cacheable mode. - */ -#ifndef MIPS_CCA_CACHED -#ifdef MIPS_CCA_CC -#define MIPS_CCA_CACHED MIPS_CCA_CC -#else -#define MIPS_CCA_CACHED MIPS_CCA_C -#endif -#endif - #define MIPS_PHYS_TO_XKPHYS(cca,x) \ ((0x2ULL << 62) | ((unsigned long long)(cca) << 59) | (x)) #define MIPS_PHYS_TO_XKPHYS_CACHED(x) \ Index: sys/mips/include/vm.h =================================================================== --- sys/mips/include/vm.h +++ sys/mips/include/vm.h @@ -32,6 +32,7 @@ #define _MACHINE_VM_H_ #include +#include /* Memory attributes. */ #define VM_MEMATTR_UNCACHEABLE ((vm_memattr_t)MIPS_CCA_UNCACHED) Index: sys/mips/mips/pmap.c =================================================================== --- sys/mips/mips/pmap.c +++ sys/mips/mips/pmap.c @@ -3641,3 +3641,19 @@ mips_dcache_wbinv_range(ova, size); return 0; } + +boolean_t +pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) +{ + + switch (mode) { + case VM_MEMATTR_UNCACHEABLE: + case VM_MEMATTR_WRITE_BACK: +#ifdef MIPS_CCA_WC + case VM_MEMATTR_WRITE_COMBINING: +#endif + return (TRUE); + default: + return (FALSE); + } +} Index: sys/powerpc/powerpc/pmap_dispatch.c =================================================================== --- sys/powerpc/powerpc/pmap_dispatch.c +++ sys/powerpc/powerpc/pmap_dispatch.c @@ -620,3 +620,21 @@ } int unmapped_buf_allowed; + +boolean_t +pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) +{ + + switch (mode) { + case VM_MEMATTR_DEFAULT: + case VM_MEMATTR_UNCACHEABLE: + case VM_MEMATTR_CACHEABLE: + case VM_MEMATTR_WRITE_COMBINING: + case VM_MEMATTR_WRITE_BACK: + case VM_MEMATTR_WRITE_THROUGH: + case VM_MEMATTR_PREFETCHABLE: + return (TRUE); + default: + return (FALSE); + } +} Index: sys/riscv/riscv/pmap.c =================================================================== --- sys/riscv/riscv/pmap.c +++ sys/riscv/riscv/pmap.c @@ -3263,3 +3263,10 @@ } } } + +boolean_t +pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) +{ + + return (mode >= VM_MEMATTR_DEVICE && mode <= VM_MEMATTR_WRITE_BACK); +} Index: sys/sparc64/sparc64/pmap.c =================================================================== --- sys/sparc64/sparc64/pmap.c +++ sys/sparc64/sparc64/pmap.c @@ -2318,3 +2318,10 @@ { } + +boolean_t +pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) +{ + + return (mode == VM_MEMATTR_DEFAULT); +} 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 { + void *pbm_map_base; /* (sometimes IN)/OUT mmaped base */ + size_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, vm_memattr_t); 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.8 =================================================================== --- usr.sbin/pciconf/pciconf.8 +++ usr.sbin/pciconf/pciconf.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 23, 2015 +.Dd June 14, 2018 .Dt PCICONF 8 .Os .Sh NAME @@ -40,6 +40,8 @@ .Fl r Oo Fl b | h Oc Ar device addr Ns Op : Ns Ar addr2 .Nm .Fl w Oo Fl b | h Oc Ar device addr value +.Nm +.Fl D Oo Fl b | h | x Oc Ar device addr Op start Ns Op : Ns Ar count .Sh DESCRIPTION The .Nm @@ -305,17 +307,38 @@ .Ar addr of device .Ar selector . -For both operations, the flags -.Fl b +.Pp +The +.Fl D +option request a dump of the specified BAR. +Dump is performed to the standard output, raw register values +are written. +Use +.Xr hexdump 1 +to convert them to human-readable dump, +or redirect into a file to save the snapshot of the device state. +Optionally, the +.Ar start and -.Fl h +.Ar count +of the registers dumped can be specified, in multiple of the operation width, +see next paragraph. +.Pp +For read, write, and dump operations, the flags +.Fl b , +.Fl h , +and +.Fl x select the width of the operation; .Fl b indicates a byte operation, and .Fl h indicates a halfword (two-byte) operation. +.Fl x +indicates a quadword (four-byte) operation. The default is to read or write a longword (four bytes). +The quadword mode is only valid for BAR dump. .Sh ENVIRONMENT PCI vendor and device information is read from .Pa /usr/local/share/pciids/pci.ids . @@ -368,3 +391,11 @@ registers may cause a failure in badly designed .Tn PCI chips. +.Pp +There is currently no way to specify the caching mode for the mapping +established by the +.Fl D +option, +.Nm +always uses uncached access. +This is fine for control register BARs. 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,37 @@ 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, const char *bar_start, + const char *bar_count, 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 [start [count]]" + "\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 +127,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 +166,10 @@ vpd = 1; break; + case 'x': + width = 8; + break; + default: usage(); } @@ -162,7 +178,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 || optind + 4 < argc)) + || (width == 8 && !dumpbarmode)) usage(); if (listmode) { @@ -171,16 +189,20 @@ } 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], + optind + 2 < argc ? argv[optind + 2] : NULL, + optind + 3 < argc ? argv[optind + 3] : NULL, + width, verbose); } else { usage(); } - return exitstatus; + return (exitstatus); } static void @@ -1027,3 +1049,117 @@ printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached"); close(fd); } + +static void +dump_bar(const char *name, const char *reg, const char *bar_start, + const char *bar_count, int width, int verbose) +{ + struct pci_bar_mmap pbm; + uint32_t *dd; + uint16_t *dh; + uint8_t *db; + uint64_t *dx, a, start, count; + char *el; + size_t res; + int fd; + + start = 0; + if (bar_start != NULL) { + start = strtoul(bar_start, &el, 0); + if (*el != '\0') + errx(1, "Invalid bar start specification %s", + bar_start); + } + count = 0; + if (bar_count != NULL) { + count = strtoul(bar_count, &el, 0); + if (*el != '\0') + errx(1, "Invalid count specification %s", + bar_count); + } + + pbm.pbm_sel = getsel(name); + pbm.pbm_reg = strtoul(reg, &el, 0); + if (*reg == '\0' || *el != '\0') + errx(1, "Invalid bar specification %s", reg); + 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 (count == 0) + count = pbm.pbm_bar_length / width; + if (start + count < start || (start + count) * width < (uint64_t)width) + errx(1, "(start + count) x width overflow"); + if ((start + count) * width > pbm.pbm_bar_length) { + if (start * width > pbm.pbm_bar_length) + count = 0; + else + count = (pbm.pbm_bar_length - start * width) / width; + } + if (verbose) { + fprintf(stderr, + "Dumping pci%d:%d:%d:%d BAR %x mapped base %p " + "off %#x length %#jx from %#jx count %#jx in %d-bytes\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, start, count, width); + } + switch (width) { + case 1: + db = (uint8_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + + pbm.pbm_bar_off + start * width); + for (a = 0; a < count; a += width, db++) { + res = fwrite(db, width, 1, stdout); + if (res != 1) { + errx(1, "error writing to stdout"); + break; + } + } + break; + case 2: + dh = (uint16_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + + pbm.pbm_bar_off + start * width); + for (a = 0; a < count; a += width, dh++) { + res = fwrite(dh, width, 1, stdout); + if (res != 1) { + errx(1, "error writing to stdout"); + break; + } + } + break; + case 4: + dd = (uint32_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + + pbm.pbm_bar_off + start * width); + for (a = 0; a < count; a += width) { + res = fwrite(dd, width, 1, stdout); + if (res != 1) { + errx(1, "error writing to stdout"); + break; + } + } + break; + case 8: + dx = (uint64_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + + pbm.pbm_bar_off + start * width); + for (a = 0; a < count; a += width, dx++) { + 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); +}