/* * Used for MAP_ANON - fast way to get anonymous pages */ static int zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags, offset_t pos) { struct segvn_crargs vn_a; int error; if (((PROT_ALL & uprot) != uprot)) return (EACCES); if ((flags & MAP_FIXED) != 0) { caddr_t userlimit; /* * Use the user address. First verify that * the address to be used is page aligned. * Then make some simple bounds checks. */ if (((uintptr_t)*addrp & PAGEOFFSET) != 0) return (EINVAL); userlimit = flags & _MAP_LOW32 ? (caddr_t)USERLIMIT32 : as->a_userlimit; switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) { case RANGE_OKAY: break; case RANGE_BADPROT: return (ENOTSUP); case RANGE_BADADDR: default: return (ENOMEM); } } /* * No need to worry about vac alignment for anonymous * pages since this is a "clone" object that doesn't * yet exist. */ error = choose_addr(as, addrp, len, pos, ADDR_NOVACALIGN, flags); if (error != 0) { return (error); } /* * Use the seg_vn segment driver; passing in the NULL amp * gives the desired "cloning" effect. */ vn_a.vp = NULL; vn_a.offset = 0; vn_a.type = flags & MAP_TYPE; vn_a.prot = uprot; vn_a.maxprot = PROT_ALL; vn_a.flags = flags & ~MAP_TYPE; vn_a.cred = CRED(); vn_a.amp = NULL; vn_a.szc = 0; vn_a.lgrp_mem_policy_flags = 0; return (as_map(as, *addrp, len, segvn_create, &vn_a)); }
/*ARGSUSED*/ static int bootfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, caller_context_t *ct) { int ret; segvn_crargs_t vn_a; #ifdef _ILP32 if (len > MAXOFF_T) return (ENOMEM); #endif if (vp->v_flag & VNOMAP) return (ENOSYS); if (off < 0 || off > MAXOFFSET_T - off) return (ENXIO); if (vp->v_type != VREG) return (ENODEV); if (prot & PROT_WRITE) return (ENOTSUP); as_rangelock(as); ret = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags); if (ret != 0) { as_rangeunlock(as); return (ret); } vn_a.vp = vp; vn_a.offset = (u_offset_t)off; vn_a.type = flags & MAP_TYPE; vn_a.prot = prot; vn_a.maxprot = maxprot; vn_a.cred = cr; vn_a.amp = NULL; vn_a.flags = flags & ~MAP_TYPE; vn_a.szc = 0; vn_a.lgrp_mem_policy_flags = 0; ret = as_map(as, *addrp, len, segvn_create, &vn_a); as_rangeunlock(as); return (ret); }
/* * This function is called when a memory device is mmap'ed. * Set up the mapping to the correct device driver. */ static int mmsegmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, off_t len, uint_t prot, uint_t maxprot, uint_t flags, struct cred *cred) { struct segvn_crargs vn_a; struct segdev_crargs dev_a; int error; minor_t minor; off_t i; minor = getminor(dev); as_rangelock(as); /* * No need to worry about vac alignment on /dev/zero * since this is a "clone" object that doesn't yet exist. */ error = choose_addr(as, addrp, len, off, (minor == M_MEM) || (minor == M_KMEM), flags); if (error != 0) { as_rangeunlock(as); return (error); } switch (minor) { case M_MEM: /* /dev/mem cannot be mmap'ed with MAP_PRIVATE */ if ((flags & MAP_TYPE) != MAP_SHARED) { as_rangeunlock(as); return (EINVAL); } /* * Check to ensure that the entire range is * legal and we are not trying to map in * more than the device will let us. */ for (i = 0; i < len; i += PAGESIZE) { if (mmmmap(dev, off + i, maxprot) == -1) { as_rangeunlock(as); return (ENXIO); } } /* * Use seg_dev segment driver for /dev/mem mapping. */ dev_a.mapfunc = mmmmap; dev_a.dev = dev; dev_a.offset = off; dev_a.type = (flags & MAP_TYPE); dev_a.prot = (uchar_t)prot; dev_a.maxprot = (uchar_t)maxprot; dev_a.hat_attr = 0; /* * Make /dev/mem mappings non-consistent since we can't * alias pages that don't have page structs behind them, * such as kernel stack pages. If someone mmap()s a kernel * stack page and if we give him a tte with cv, a line from * that page can get into both pages of the spitfire d$. * But snoop from another processor will only invalidate * the first page. This later caused kernel (xc_attention) * to go into an infinite loop at pil 13 and no interrupts * could come in. See 1203630. * */ dev_a.hat_flags = HAT_LOAD_NOCONSIST; dev_a.devmap_data = NULL; error = as_map(as, *addrp, len, segdev_create, &dev_a); break; case M_ZERO: /* * Use seg_vn segment driver for /dev/zero mapping. * Passing in a NULL amp gives us the "cloning" effect. */ vn_a.vp = NULL; vn_a.offset = 0; vn_a.type = (flags & MAP_TYPE); vn_a.prot = prot; vn_a.maxprot = maxprot; vn_a.flags = flags & ~MAP_TYPE; vn_a.cred = cred; vn_a.amp = NULL; vn_a.szc = 0; vn_a.lgrp_mem_policy_flags = 0; error = as_map(as, *addrp, len, segvn_create, &vn_a); break; case M_KMEM: case M_ALLKMEM: /* No longer supported with KPR. */ error = ENXIO; break; case M_NULL: /* * Use seg_dev segment driver for /dev/null mapping. */ dev_a.mapfunc = mmmmap; dev_a.dev = dev; dev_a.offset = off; dev_a.type = 0; /* neither PRIVATE nor SHARED */ dev_a.prot = dev_a.maxprot = (uchar_t)PROT_NONE; dev_a.hat_attr = 0; dev_a.hat_flags = 0; error = as_map(as, *addrp, len, segdev_create, &dev_a); break; default: error = ENXIO; } as_rangeunlock(as); return (error); }
/* ARGSUSED */ int gfs_vop_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cred, caller_context_t *ct) { int rv; ssize_t resid = len; /* * Check for bad parameters */ #ifdef _ILP32 if (len > MAXOFF_T) return (ENOMEM); #endif if (vp->v_flag & VNOMAP) return (ENOTSUP); if (off > MAXOFF_T) return (EFBIG); if ((long)off < 0 || (long)(off + len) < 0) return (EINVAL); if (vp->v_type != VREG) return (ENODEV); if ((prot & (PROT_EXEC | PROT_WRITE)) != 0) return (EACCES); /* * Find appropriate address if needed, otherwise clear address range. */ as_rangelock(as); rv = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags); if (rv != 0) { as_rangeunlock(as); return (rv); } /* * Create mapping */ rv = as_map(as, *addrp, len, segvn_create, zfod_argsp); as_rangeunlock(as); if (rv != 0) return (rv); /* * Fill with data from read() */ rv = vn_rdwr(UIO_READ, vp, *addrp, len, off, UIO_USERSPACE, 0, (rlim64_t)0, cred, &resid); if (rv == 0 && resid != 0) rv = ENXIO; if (rv != 0) { as_rangelock(as); (void) as_unmap(as, *addrp, len); as_rangeunlock(as); } return (rv); }