141490Smckusick /* 241490Smckusick * Copyright (c) 1988 University of Utah. 363178Sbostic * Copyright (c) 1990, 1993 463178Sbostic * The Regents of the University of California. All rights reserved. 5*65771Sbostic * (c) UNIX System Laboratories, Inc. 6*65771Sbostic * All or some portions of this file are derived from material licensed 7*65771Sbostic * to the University of California by American Telephone and Telegraph 8*65771Sbostic * Co. or Unix System Laboratories, Inc. and are reproduced herein with 9*65771Sbostic * the permission of UNIX System Laboratories, Inc. 1041490Smckusick * 1141490Smckusick * This code is derived from software contributed to Berkeley by 1241490Smckusick * the Systems Programming Group of the University of Utah Computer 13*65771Sbostic * Science Department. Originally from the University of Wisconsin. 1441490Smckusick * 1564553Sbostic * %sccs.include.proprietary.c% 1641490Smckusick * 1754856Shibler * from: Utah $Hdr: uipc_shm.c 1.11 92/04/23$ 1841490Smckusick * 19*65771Sbostic * @(#)sysv_shm.c 8.6 (Berkeley) 01/21/94 2041490Smckusick */ 2141490Smckusick 2241490Smckusick /* 2341490Smckusick * System V shared memory routines. 2443630Skarels * TEMPORARY, until mmap is in place; 2543630Skarels * needed now for HP-UX compatibility and X server (yech!). 2641490Smckusick */ 2741490Smckusick 2841490Smckusick #ifdef SYSVSHM 2941490Smckusick 3056517Sbostic #include <sys/param.h> 3156517Sbostic #include <sys/systm.h> 3256517Sbostic #include <sys/kernel.h> 3356517Sbostic #include <sys/proc.h> 3456517Sbostic #include <sys/shm.h> 3556517Sbostic #include <sys/malloc.h> 3656517Sbostic #include <sys/mman.h> 3764399Smckusick #include <sys/stat.h> 3841490Smckusick 3956517Sbostic #include <vm/vm.h> 4056517Sbostic #include <vm/vm_kern.h> 4156517Sbostic #include <vm/vm_inherit.h> 4256517Sbostic #include <vm/vm_pager.h> 4356517Sbostic 4441490Smckusick int shmat(), shmctl(), shmdt(), shmget(); 4541490Smckusick int (*shmcalls[])() = { shmat, shmctl, shmdt, shmget }; 4641490Smckusick int shmtot = 0; 4741490Smckusick 4845737Smckusick /* 4945737Smckusick * Per process internal structure for managing segments. 5045737Smckusick * Each process using shm will have an array of ``shmseg'' of these. 5145737Smckusick */ 5245737Smckusick struct shmdesc { 5345737Smckusick vm_offset_t shmd_uva; 5445737Smckusick int shmd_id; 5545737Smckusick }; 5641490Smckusick 5745737Smckusick /* 5845737Smckusick * Per segment internal structure (shm_handle). 5945737Smckusick */ 6045737Smckusick struct shmhandle { 6145737Smckusick vm_offset_t shmh_kva; 6245737Smckusick caddr_t shmh_id; 6345737Smckusick }; 6445737Smckusick 6545737Smckusick vm_map_t shm_map; /* address space for shared memory segments */ 6645737Smckusick 6741490Smckusick shminit() 6841490Smckusick { 6941490Smckusick register int i; 7045737Smckusick vm_offset_t whocares1, whocares2; 7141490Smckusick 7245737Smckusick shm_map = kmem_suballoc(kernel_map, &whocares1, &whocares2, 7365668Shibler shminfo.shmall * NBPG, TRUE); 7441490Smckusick if (shminfo.shmmni > SHMMMNI) 7541490Smckusick shminfo.shmmni = SHMMMNI; 7641490Smckusick for (i = 0; i < shminfo.shmmni; i++) { 7741490Smckusick shmsegs[i].shm_perm.mode = 0; 7841490Smckusick shmsegs[i].shm_perm.seq = 0; 7941490Smckusick } 8041490Smckusick } 8141490Smckusick 8242961Smckusick /* 8342961Smckusick * Entry point for all SHM calls 8442961Smckusick */ 8554932Storek struct shmsys_args { 8654932Storek u_int which; 8754932Storek }; 8842961Smckusick shmsys(p, uap, retval) 8942961Smckusick struct proc *p; 9054932Storek struct shmsys_args *uap; 9142961Smckusick int *retval; 9241490Smckusick { 9341490Smckusick 9442961Smckusick if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 9544405Skarels return (EINVAL); 9644405Skarels return ((*shmcalls[uap->which])(p, &uap[1], retval)); 9741490Smckusick } 9841490Smckusick 9942961Smckusick /* 10042961Smckusick * Get a shared memory segment 10142961Smckusick */ 10254932Storek struct shmget_args { 10354932Storek key_t key; 10454932Storek int size; 10554932Storek int shmflg; 10654932Storek }; 10742961Smckusick shmget(p, uap, retval) 10842961Smckusick struct proc *p; 10954932Storek register struct shmget_args *uap; 11042961Smckusick int *retval; 11142961Smckusick { 11241490Smckusick register struct shmid_ds *shp; 11347540Skarels register struct ucred *cred = p->p_ucred; 11441490Smckusick register int i; 11542961Smckusick int error, size, rval = 0; 11645737Smckusick register struct shmhandle *shmh; 11741490Smckusick 11841490Smckusick /* look up the specified shm_id */ 11941490Smckusick if (uap->key != IPC_PRIVATE) { 12041490Smckusick for (i = 0; i < shminfo.shmmni; i++) 12141490Smckusick if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) && 12241490Smckusick shmsegs[i].shm_perm.key == uap->key) { 12341490Smckusick rval = i; 12441490Smckusick break; 12541490Smckusick } 12641490Smckusick } else 12741490Smckusick i = shminfo.shmmni; 12841490Smckusick 12941490Smckusick /* create a new shared segment if necessary */ 13041490Smckusick if (i == shminfo.shmmni) { 13142961Smckusick if ((uap->shmflg & IPC_CREAT) == 0) 13242961Smckusick return (ENOENT); 13342961Smckusick if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 13442961Smckusick return (EINVAL); 13541490Smckusick for (i = 0; i < shminfo.shmmni; i++) 13641490Smckusick if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) == 0) { 13741490Smckusick rval = i; 13841490Smckusick break; 13941490Smckusick } 14042961Smckusick if (i == shminfo.shmmni) 14142961Smckusick return (ENOSPC); 14241490Smckusick size = clrnd(btoc(uap->size)); 14342961Smckusick if (shmtot + size > shminfo.shmall) 14442961Smckusick return (ENOMEM); 14541490Smckusick shp = &shmsegs[rval]; 14641490Smckusick /* 14741490Smckusick * We need to do a couple of things to ensure consistency 14841490Smckusick * in case we sleep in malloc(). We mark segment as 14941490Smckusick * allocated so that other shmgets() will not allocate it. 15041490Smckusick * We mark it as "destroyed" to insure that shmvalid() is 15141490Smckusick * false making most operations fail (XXX). We set the key, 15241490Smckusick * so that other shmget()s will fail. 15341490Smckusick */ 15441490Smckusick shp->shm_perm.mode = SHM_ALLOC | SHM_DEST; 15541490Smckusick shp->shm_perm.key = uap->key; 15645737Smckusick shmh = (struct shmhandle *) 15745737Smckusick malloc(sizeof(struct shmhandle), M_SHM, M_WAITOK); 15845737Smckusick shmh->shmh_kva = 0; 15945737Smckusick shmh->shmh_id = (caddr_t)(0xc0000000|rval); /* XXX */ 16045737Smckusick error = vm_mmap(shm_map, &shmh->shmh_kva, ctob(size), 16158595Shibler VM_PROT_ALL, VM_PROT_ALL, 16258595Shibler MAP_ANON, shmh->shmh_id, 0); 16345737Smckusick if (error) { 16445737Smckusick free((caddr_t)shmh, M_SHM); 16541490Smckusick shp->shm_perm.mode = 0; 16645737Smckusick return(ENOMEM); 16741490Smckusick } 16845737Smckusick shp->shm_handle = (void *) shmh; 16941490Smckusick shmtot += size; 17042961Smckusick shp->shm_perm.cuid = shp->shm_perm.uid = cred->cr_uid; 17142961Smckusick shp->shm_perm.cgid = shp->shm_perm.gid = cred->cr_gid; 17264399Smckusick shp->shm_perm.mode = SHM_ALLOC | (uap->shmflg & ACCESSPERMS); 17341490Smckusick shp->shm_segsz = uap->size; 17442922Smckusick shp->shm_cpid = p->p_pid; 17541490Smckusick shp->shm_lpid = shp->shm_nattch = 0; 17641490Smckusick shp->shm_atime = shp->shm_dtime = 0; 17741490Smckusick shp->shm_ctime = time.tv_sec; 17841490Smckusick } else { 17941490Smckusick shp = &shmsegs[rval]; 18041490Smckusick /* XXX: probably not the right thing to do */ 18142961Smckusick if (shp->shm_perm.mode & SHM_DEST) 18242961Smckusick return (EBUSY); 18364399Smckusick if (error = ipcaccess(&shp->shm_perm, uap->shmflg & ACCESSPERMS, 18464399Smckusick cred)) 18542961Smckusick return (error); 18642961Smckusick if (uap->size && uap->size > shp->shm_segsz) 18742961Smckusick return (EINVAL); 18842961Smckusick if ((uap->shmflg&IPC_CREAT) && (uap->shmflg&IPC_EXCL)) 18942961Smckusick return (EEXIST); 19041490Smckusick } 19142961Smckusick *retval = shp->shm_perm.seq * SHMMMNI + rval; 19243408Shibler return (0); 19341490Smckusick } 19441490Smckusick 19542961Smckusick /* 19642961Smckusick * Shared memory control 19742961Smckusick */ 19854932Storek struct shmctl_args { 19954932Storek int shmid; 20054932Storek int cmd; 20154932Storek caddr_t buf; 20254932Storek }; 20342961Smckusick /* ARGSUSED */ 20442961Smckusick shmctl(p, uap, retval) 20542961Smckusick struct proc *p; 20654932Storek register struct shmctl_args *uap; 20742961Smckusick int *retval; 20842961Smckusick { 20941490Smckusick register struct shmid_ds *shp; 21047540Skarels register struct ucred *cred = p->p_ucred; 21141490Smckusick struct shmid_ds sbuf; 21242961Smckusick int error; 21341490Smckusick 21442961Smckusick if (error = shmvalid(uap->shmid)) 21542961Smckusick return (error); 21641490Smckusick shp = &shmsegs[uap->shmid % SHMMMNI]; 21741490Smckusick switch (uap->cmd) { 21841490Smckusick case IPC_STAT: 21943408Shibler if (error = ipcaccess(&shp->shm_perm, IPC_R, cred)) 22042961Smckusick return (error); 22142961Smckusick return (copyout((caddr_t)shp, uap->buf, sizeof(*shp))); 22241490Smckusick 22341490Smckusick case IPC_SET: 22442961Smckusick if (cred->cr_uid && cred->cr_uid != shp->shm_perm.uid && 22542961Smckusick cred->cr_uid != shp->shm_perm.cuid) 22642961Smckusick return (EPERM); 22742961Smckusick if (error = copyin(uap->buf, (caddr_t)&sbuf, sizeof sbuf)) 22842961Smckusick return (error); 22942961Smckusick shp->shm_perm.uid = sbuf.shm_perm.uid; 23042961Smckusick shp->shm_perm.gid = sbuf.shm_perm.gid; 23164399Smckusick shp->shm_perm.mode = (shp->shm_perm.mode & ~ACCESSPERMS) 23264399Smckusick | (sbuf.shm_perm.mode & ACCESSPERMS); 23342961Smckusick shp->shm_ctime = time.tv_sec; 23441490Smckusick break; 23541490Smckusick 23641490Smckusick case IPC_RMID: 23742961Smckusick if (cred->cr_uid && cred->cr_uid != shp->shm_perm.uid && 23842961Smckusick cred->cr_uid != shp->shm_perm.cuid) 23942961Smckusick return (EPERM); 24041490Smckusick /* set ctime? */ 24141490Smckusick shp->shm_perm.key = IPC_PRIVATE; 24241490Smckusick shp->shm_perm.mode |= SHM_DEST; 24341490Smckusick if (shp->shm_nattch <= 0) 24441490Smckusick shmfree(shp); 24541490Smckusick break; 24641490Smckusick 24741490Smckusick default: 24842961Smckusick return (EINVAL); 24941490Smckusick } 25042961Smckusick return (0); 25141490Smckusick } 25241490Smckusick 25342961Smckusick /* 25442961Smckusick * Attach to shared memory segment. 25542961Smckusick */ 25654932Storek struct shmat_args { 25754932Storek int shmid; 25854932Storek caddr_t shmaddr; 25954932Storek int shmflg; 26054932Storek }; 26142961Smckusick shmat(p, uap, retval) 26242961Smckusick struct proc *p; 26354932Storek register struct shmat_args *uap; 26442961Smckusick int *retval; 26542961Smckusick { 26641490Smckusick register struct shmid_ds *shp; 26741490Smckusick register int size; 26841490Smckusick caddr_t uva; 26945737Smckusick int error; 27045737Smckusick int flags; 27145737Smckusick vm_prot_t prot; 27245737Smckusick struct shmdesc *shmd; 27341490Smckusick 27445737Smckusick /* 27545737Smckusick * Allocate descriptors now (before validity check) 27645737Smckusick * in case malloc() blocks. 27745737Smckusick */ 27847540Skarels shmd = (struct shmdesc *)p->p_vmspace->vm_shm; 27945737Smckusick size = shminfo.shmseg * sizeof(struct shmdesc); 28045737Smckusick if (shmd == NULL) { 28145737Smckusick shmd = (struct shmdesc *)malloc(size, M_SHM, M_WAITOK); 28245737Smckusick bzero((caddr_t)shmd, size); 28347540Skarels p->p_vmspace->vm_shm = (caddr_t)shmd; 28445737Smckusick } 28542961Smckusick if (error = shmvalid(uap->shmid)) 28642961Smckusick return (error); 28741490Smckusick shp = &shmsegs[uap->shmid % SHMMMNI]; 28841490Smckusick if (shp->shm_handle == NULL) 28942349Smckusick panic("shmat NULL handle"); 29043408Shibler if (error = ipcaccess(&shp->shm_perm, 29147540Skarels (uap->shmflg&SHM_RDONLY) ? IPC_R : IPC_R|IPC_W, p->p_ucred)) 29242961Smckusick return (error); 29341490Smckusick uva = uap->shmaddr; 29441490Smckusick if (uva && ((int)uva & (SHMLBA-1))) { 29541490Smckusick if (uap->shmflg & SHM_RND) 29641490Smckusick uva = (caddr_t) ((int)uva & ~(SHMLBA-1)); 29742961Smckusick else 29842961Smckusick return (EINVAL); 29941490Smckusick } 30041490Smckusick /* 30141490Smckusick * Make sure user doesn't use more than their fair share 30241490Smckusick */ 30345737Smckusick for (size = 0; size < shminfo.shmseg; size++) { 30445737Smckusick if (shmd->shmd_uva == 0) 30545737Smckusick break; 30645737Smckusick shmd++; 30745737Smckusick } 30842961Smckusick if (size >= shminfo.shmseg) 30942961Smckusick return (EMFILE); 31041490Smckusick size = ctob(clrnd(btoc(shp->shm_segsz))); 31145737Smckusick prot = VM_PROT_READ; 31245737Smckusick if ((uap->shmflg & SHM_RDONLY) == 0) 31345737Smckusick prot |= VM_PROT_WRITE; 31445737Smckusick flags = MAP_ANON|MAP_SHARED; 31545737Smckusick if (uva) 31645737Smckusick flags |= MAP_FIXED; 31745737Smckusick else 31845737Smckusick uva = (caddr_t)0x1000000; /* XXX */ 31953313Smckusick error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)&uva, 32058595Shibler (vm_size_t)size, prot, VM_PROT_ALL, flags, 32158595Shibler ((struct shmhandle *)shp->shm_handle)->shmh_id, 0); 32242961Smckusick if (error) 32345737Smckusick return(error); 32445737Smckusick shmd->shmd_uva = (vm_offset_t)uva; 32545737Smckusick shmd->shmd_id = uap->shmid; 32641490Smckusick /* 32741490Smckusick * Fill in the remaining fields 32841490Smckusick */ 32942922Smckusick shp->shm_lpid = p->p_pid; 33041490Smckusick shp->shm_atime = time.tv_sec; 33141490Smckusick shp->shm_nattch++; 33242961Smckusick *retval = (int) uva; 33343408Shibler return (0); 33441490Smckusick } 33541490Smckusick 33642961Smckusick /* 33742961Smckusick * Detach from shared memory segment. 33842961Smckusick */ 33954932Storek struct shmdt_args { 34054932Storek caddr_t shmaddr; 34154932Storek }; 34242961Smckusick /* ARGSUSED */ 34342961Smckusick shmdt(p, uap, retval) 34442961Smckusick struct proc *p; 34554932Storek struct shmdt_args *uap; 34642961Smckusick int *retval; 34741490Smckusick { 34845737Smckusick register struct shmdesc *shmd; 34945737Smckusick register int i; 35041490Smckusick 35147540Skarels shmd = (struct shmdesc *)p->p_vmspace->vm_shm; 35245737Smckusick for (i = 0; i < shminfo.shmseg; i++, shmd++) 35345737Smckusick if (shmd->shmd_uva && 35445737Smckusick shmd->shmd_uva == (vm_offset_t)uap->shmaddr) 35541490Smckusick break; 35645737Smckusick if (i == shminfo.shmseg) 35764833Storek return (EINVAL); 35845737Smckusick shmufree(p, shmd); 35945737Smckusick shmsegs[shmd->shmd_id % SHMMMNI].shm_lpid = p->p_pid; 36064833Storek return (0); 36141490Smckusick } 36241490Smckusick 36347540Skarels shmfork(p1, p2, isvfork) 36447540Skarels struct proc *p1, *p2; 36545737Smckusick int isvfork; 36641490Smckusick { 36745737Smckusick register struct shmdesc *shmd; 36845737Smckusick register int size; 36941490Smckusick 37045737Smckusick /* 37145737Smckusick * Copy parents descriptive information 37245737Smckusick */ 37345737Smckusick size = shminfo.shmseg * sizeof(struct shmdesc); 37445737Smckusick shmd = (struct shmdesc *)malloc(size, M_SHM, M_WAITOK); 37547540Skarels bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmd, size); 37647540Skarels p2->p_vmspace->vm_shm = (caddr_t)shmd; 37745737Smckusick /* 37845737Smckusick * Increment reference counts 37945737Smckusick */ 38045737Smckusick for (size = 0; size < shminfo.shmseg; size++, shmd++) 38145737Smckusick if (shmd->shmd_uva) 38245737Smckusick shmsegs[shmd->shmd_id % SHMMMNI].shm_nattch++; 38341490Smckusick } 38441490Smckusick 38545737Smckusick shmexit(p) 38645737Smckusick struct proc *p; 38741490Smckusick { 38845737Smckusick register struct shmdesc *shmd; 38945737Smckusick register int i; 39041490Smckusick 39147540Skarels shmd = (struct shmdesc *)p->p_vmspace->vm_shm; 39245737Smckusick for (i = 0; i < shminfo.shmseg; i++, shmd++) 39345737Smckusick if (shmd->shmd_uva) 39445737Smckusick shmufree(p, shmd); 39547540Skarels free((caddr_t)p->p_vmspace->vm_shm, M_SHM); 39647540Skarels p->p_vmspace->vm_shm = NULL; 39741490Smckusick } 39841490Smckusick 39941490Smckusick shmvalid(id) 40041490Smckusick register int id; 40141490Smckusick { 40241490Smckusick register struct shmid_ds *shp; 40341490Smckusick 40441490Smckusick if (id < 0 || (id % SHMMMNI) >= shminfo.shmmni) 40542961Smckusick return(EINVAL); 40641490Smckusick shp = &shmsegs[id % SHMMMNI]; 40741490Smckusick if (shp->shm_perm.seq == (id / SHMMMNI) && 40841490Smckusick (shp->shm_perm.mode & (SHM_ALLOC|SHM_DEST)) == SHM_ALLOC) 40942961Smckusick return(0); 41042961Smckusick return(EINVAL); 41141490Smckusick } 41241490Smckusick 41341490Smckusick /* 41441490Smckusick * Free user resources associated with a shared memory segment 41541490Smckusick */ 41645737Smckusick shmufree(p, shmd) 41742922Smckusick struct proc *p; 41845737Smckusick struct shmdesc *shmd; 41941490Smckusick { 42041490Smckusick register struct shmid_ds *shp; 42141490Smckusick 42245737Smckusick shp = &shmsegs[shmd->shmd_id % SHMMMNI]; 42349710Shibler (void) vm_deallocate(&p->p_vmspace->vm_map, shmd->shmd_uva, 42445737Smckusick ctob(clrnd(btoc(shp->shm_segsz)))); 42545737Smckusick shmd->shmd_id = 0; 42645737Smckusick shmd->shmd_uva = 0; 42741490Smckusick shp->shm_dtime = time.tv_sec; 42841490Smckusick if (--shp->shm_nattch <= 0 && (shp->shm_perm.mode & SHM_DEST)) 42941490Smckusick shmfree(shp); 43041490Smckusick } 43141490Smckusick 43241490Smckusick /* 43341490Smckusick * Deallocate resources associated with a shared memory segment 43441490Smckusick */ 43541490Smckusick shmfree(shp) 43641490Smckusick register struct shmid_ds *shp; 43741490Smckusick { 43841490Smckusick 43941490Smckusick if (shp->shm_handle == NULL) 44041490Smckusick panic("shmfree"); 44145737Smckusick /* 44245737Smckusick * Lose our lingering object reference by deallocating space 44345737Smckusick * in kernel. Pager will also be deallocated as a side-effect. 44445737Smckusick */ 44545737Smckusick vm_deallocate(shm_map, 44645737Smckusick ((struct shmhandle *)shp->shm_handle)->shmh_kva, 44749668Shibler ctob(clrnd(btoc(shp->shm_segsz)))); 44845737Smckusick free((caddr_t)shp->shm_handle, M_SHM); 44941490Smckusick shp->shm_handle = NULL; 45041490Smckusick shmtot -= clrnd(btoc(shp->shm_segsz)); 45141490Smckusick shp->shm_perm.mode = 0; 45241490Smckusick /* 45341490Smckusick * Increment the sequence number to ensure that outstanding 45441490Smckusick * shmids for this segment will be invalid in the event that 45541490Smckusick * the segment is reallocated. Note that shmids must be 45641490Smckusick * positive as decreed by SVID. 45741490Smckusick */ 45841490Smckusick shp->shm_perm.seq++; 45941490Smckusick if ((int)(shp->shm_perm.seq * SHMMMNI) < 0) 46041490Smckusick shp->shm_perm.seq = 0; 46141490Smckusick } 46241490Smckusick 46341490Smckusick /* 46441490Smckusick * XXX This routine would be common to all sysV style IPC 46541490Smckusick * (if the others were implemented). 46641490Smckusick */ 46742961Smckusick ipcaccess(ipc, mode, cred) 46841490Smckusick register struct ipc_perm *ipc; 46942961Smckusick int mode; 47042961Smckusick register struct ucred *cred; 47141490Smckusick { 47241490Smckusick register int m; 47341490Smckusick 47442961Smckusick if (cred->cr_uid == 0) 47541490Smckusick return(0); 47641490Smckusick /* 47741490Smckusick * Access check is based on only one of owner, group, public. 47841490Smckusick * If not owner, then check group. 47941490Smckusick * If not a member of the group, then check public access. 48041490Smckusick */ 48141490Smckusick mode &= 0700; 48241490Smckusick m = ipc->mode; 48342961Smckusick if (cred->cr_uid != ipc->uid && cred->cr_uid != ipc->cuid) { 48441490Smckusick m <<= 3; 48542961Smckusick if (!groupmember(ipc->gid, cred) && 48642961Smckusick !groupmember(ipc->cgid, cred)) 48741490Smckusick m <<= 3; 48841490Smckusick } 48941490Smckusick if ((mode&m) == mode) 49042961Smckusick return (0); 49142961Smckusick return (EACCES); 49241490Smckusick } 49341490Smckusick #endif /* SYSVSHM */ 494