141490Smckusick /* 241490Smckusick * Copyright (c) 1988 University of Utah. 341490Smckusick * Copyright (c) 1990 The Regents of the University of California. 441490Smckusick * All rights reserved. 541490Smckusick * 641490Smckusick * This code is derived from software contributed to Berkeley by 741490Smckusick * the Systems Programming Group of the University of Utah Computer 841490Smckusick * Science Department. Originally from University of Wisconsin. 941490Smckusick * 1041490Smckusick * %sccs.include.redist.c% 1141490Smckusick * 12*54856Shibler * from: Utah $Hdr: uipc_shm.c 1.11 92/04/23$ 1341490Smckusick * 14*54856Shibler * @(#)sysv_shm.c 7.18 (Berkeley) 07/09/92 1541490Smckusick */ 1641490Smckusick 1741490Smckusick /* 1841490Smckusick * System V shared memory routines. 1943630Skarels * TEMPORARY, until mmap is in place; 2043630Skarels * needed now for HP-UX compatibility and X server (yech!). 2141490Smckusick */ 2241490Smckusick 2341490Smckusick #ifdef SYSVSHM 2441490Smckusick 2541490Smckusick #include "param.h" 2641490Smckusick #include "systm.h" 2741490Smckusick #include "kernel.h" 2841490Smckusick #include "proc.h" 2941490Smckusick #include "shm.h" 3041490Smckusick #include "malloc.h" 3145737Smckusick #include "mman.h" 3248446Skarels #include "vm/vm.h" 3348446Skarels #include "vm/vm_kern.h" 3448446Skarels #include "vm/vm_inherit.h" 3548446Skarels #include "vm/vm_pager.h" 3641490Smckusick 3741490Smckusick int shmat(), shmctl(), shmdt(), shmget(); 3841490Smckusick int (*shmcalls[])() = { shmat, shmctl, shmdt, shmget }; 3941490Smckusick int shmtot = 0; 4041490Smckusick 4145737Smckusick /* 4245737Smckusick * Per process internal structure for managing segments. 4345737Smckusick * Each process using shm will have an array of ``shmseg'' of these. 4445737Smckusick */ 4545737Smckusick struct shmdesc { 4645737Smckusick vm_offset_t shmd_uva; 4745737Smckusick int shmd_id; 4845737Smckusick }; 4941490Smckusick 5045737Smckusick /* 5145737Smckusick * Per segment internal structure (shm_handle). 5245737Smckusick */ 5345737Smckusick struct shmhandle { 5445737Smckusick vm_offset_t shmh_kva; 5545737Smckusick caddr_t shmh_id; 5645737Smckusick }; 5745737Smckusick 5845737Smckusick vm_map_t shm_map; /* address space for shared memory segments */ 5945737Smckusick 6041490Smckusick shminit() 6141490Smckusick { 6241490Smckusick register int i; 6345737Smckusick vm_offset_t whocares1, whocares2; 6441490Smckusick 6545737Smckusick shm_map = kmem_suballoc(kernel_map, &whocares1, &whocares2, 6645737Smckusick shminfo.shmall * NBPG, FALSE); 6741490Smckusick if (shminfo.shmmni > SHMMMNI) 6841490Smckusick shminfo.shmmni = SHMMMNI; 6941490Smckusick for (i = 0; i < shminfo.shmmni; i++) { 7041490Smckusick shmsegs[i].shm_perm.mode = 0; 7141490Smckusick shmsegs[i].shm_perm.seq = 0; 7241490Smckusick } 7341490Smckusick } 7441490Smckusick 7542961Smckusick /* 7642961Smckusick * Entry point for all SHM calls 7742961Smckusick */ 7842961Smckusick shmsys(p, uap, retval) 7942961Smckusick struct proc *p; 8042961Smckusick struct args { 8143630Skarels u_int which; 8242961Smckusick } *uap; 8342961Smckusick int *retval; 8441490Smckusick { 8541490Smckusick 8642961Smckusick if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 8744405Skarels return (EINVAL); 8844405Skarels return ((*shmcalls[uap->which])(p, &uap[1], retval)); 8941490Smckusick } 9041490Smckusick 9142961Smckusick /* 9242961Smckusick * Get a shared memory segment 9342961Smckusick */ 9442961Smckusick shmget(p, uap, retval) 9542961Smckusick struct proc *p; 9642961Smckusick register struct args { 9741490Smckusick key_t key; 9841490Smckusick int size; 9941490Smckusick int shmflg; 10042961Smckusick } *uap; 10142961Smckusick int *retval; 10242961Smckusick { 10341490Smckusick register struct shmid_ds *shp; 10447540Skarels register struct ucred *cred = p->p_ucred; 10541490Smckusick register int i; 10642961Smckusick int error, size, rval = 0; 10745737Smckusick register struct shmhandle *shmh; 10841490Smckusick 10941490Smckusick /* look up the specified shm_id */ 11041490Smckusick if (uap->key != IPC_PRIVATE) { 11141490Smckusick for (i = 0; i < shminfo.shmmni; i++) 11241490Smckusick if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) && 11341490Smckusick shmsegs[i].shm_perm.key == uap->key) { 11441490Smckusick rval = i; 11541490Smckusick break; 11641490Smckusick } 11741490Smckusick } else 11841490Smckusick i = shminfo.shmmni; 11941490Smckusick 12041490Smckusick /* create a new shared segment if necessary */ 12141490Smckusick if (i == shminfo.shmmni) { 12242961Smckusick if ((uap->shmflg & IPC_CREAT) == 0) 12342961Smckusick return (ENOENT); 12442961Smckusick if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 12542961Smckusick return (EINVAL); 12641490Smckusick for (i = 0; i < shminfo.shmmni; i++) 12741490Smckusick if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) == 0) { 12841490Smckusick rval = i; 12941490Smckusick break; 13041490Smckusick } 13142961Smckusick if (i == shminfo.shmmni) 13242961Smckusick return (ENOSPC); 13341490Smckusick size = clrnd(btoc(uap->size)); 13442961Smckusick if (shmtot + size > shminfo.shmall) 13542961Smckusick return (ENOMEM); 13641490Smckusick shp = &shmsegs[rval]; 13741490Smckusick /* 13841490Smckusick * We need to do a couple of things to ensure consistency 13941490Smckusick * in case we sleep in malloc(). We mark segment as 14041490Smckusick * allocated so that other shmgets() will not allocate it. 14141490Smckusick * We mark it as "destroyed" to insure that shmvalid() is 14241490Smckusick * false making most operations fail (XXX). We set the key, 14341490Smckusick * so that other shmget()s will fail. 14441490Smckusick */ 14541490Smckusick shp->shm_perm.mode = SHM_ALLOC | SHM_DEST; 14641490Smckusick shp->shm_perm.key = uap->key; 14745737Smckusick shmh = (struct shmhandle *) 14845737Smckusick malloc(sizeof(struct shmhandle), M_SHM, M_WAITOK); 14945737Smckusick shmh->shmh_kva = 0; 15045737Smckusick shmh->shmh_id = (caddr_t)(0xc0000000|rval); /* XXX */ 15145737Smckusick error = vm_mmap(shm_map, &shmh->shmh_kva, ctob(size), 15245737Smckusick VM_PROT_ALL, MAP_ANON, shmh->shmh_id, 0); 15345737Smckusick if (error) { 15445737Smckusick free((caddr_t)shmh, M_SHM); 15541490Smckusick shp->shm_perm.mode = 0; 15645737Smckusick return(ENOMEM); 15741490Smckusick } 15845737Smckusick shp->shm_handle = (void *) shmh; 15941490Smckusick shmtot += size; 16042961Smckusick shp->shm_perm.cuid = shp->shm_perm.uid = cred->cr_uid; 16142961Smckusick shp->shm_perm.cgid = shp->shm_perm.gid = cred->cr_gid; 16241490Smckusick shp->shm_perm.mode = SHM_ALLOC | (uap->shmflg&0777); 16341490Smckusick shp->shm_segsz = uap->size; 16442922Smckusick shp->shm_cpid = p->p_pid; 16541490Smckusick shp->shm_lpid = shp->shm_nattch = 0; 16641490Smckusick shp->shm_atime = shp->shm_dtime = 0; 16741490Smckusick shp->shm_ctime = time.tv_sec; 16841490Smckusick } else { 16941490Smckusick shp = &shmsegs[rval]; 17041490Smckusick /* XXX: probably not the right thing to do */ 17142961Smckusick if (shp->shm_perm.mode & SHM_DEST) 17242961Smckusick return (EBUSY); 17343408Shibler if (error = ipcaccess(&shp->shm_perm, uap->shmflg&0777, cred)) 17442961Smckusick return (error); 17542961Smckusick if (uap->size && uap->size > shp->shm_segsz) 17642961Smckusick return (EINVAL); 17742961Smckusick if ((uap->shmflg&IPC_CREAT) && (uap->shmflg&IPC_EXCL)) 17842961Smckusick return (EEXIST); 17941490Smckusick } 18042961Smckusick *retval = shp->shm_perm.seq * SHMMMNI + rval; 18143408Shibler return (0); 18241490Smckusick } 18341490Smckusick 18442961Smckusick /* 18542961Smckusick * Shared memory control 18642961Smckusick */ 18742961Smckusick /* ARGSUSED */ 18842961Smckusick shmctl(p, uap, retval) 18942961Smckusick struct proc *p; 19042961Smckusick register struct args { 19141490Smckusick int shmid; 19241490Smckusick int cmd; 19341490Smckusick caddr_t buf; 19442961Smckusick } *uap; 19542961Smckusick int *retval; 19642961Smckusick { 19741490Smckusick register struct shmid_ds *shp; 19847540Skarels register struct ucred *cred = p->p_ucred; 19941490Smckusick struct shmid_ds sbuf; 20042961Smckusick int error; 20141490Smckusick 20242961Smckusick if (error = shmvalid(uap->shmid)) 20342961Smckusick return (error); 20441490Smckusick shp = &shmsegs[uap->shmid % SHMMMNI]; 20541490Smckusick switch (uap->cmd) { 20641490Smckusick case IPC_STAT: 20743408Shibler if (error = ipcaccess(&shp->shm_perm, IPC_R, cred)) 20842961Smckusick return (error); 20942961Smckusick return (copyout((caddr_t)shp, uap->buf, sizeof(*shp))); 21041490Smckusick 21141490Smckusick case IPC_SET: 21242961Smckusick if (cred->cr_uid && cred->cr_uid != shp->shm_perm.uid && 21342961Smckusick cred->cr_uid != shp->shm_perm.cuid) 21442961Smckusick return (EPERM); 21542961Smckusick if (error = copyin(uap->buf, (caddr_t)&sbuf, sizeof sbuf)) 21642961Smckusick return (error); 21742961Smckusick shp->shm_perm.uid = sbuf.shm_perm.uid; 21842961Smckusick shp->shm_perm.gid = sbuf.shm_perm.gid; 21942961Smckusick shp->shm_perm.mode = (shp->shm_perm.mode & ~0777) 22042961Smckusick | (sbuf.shm_perm.mode & 0777); 22142961Smckusick shp->shm_ctime = time.tv_sec; 22241490Smckusick break; 22341490Smckusick 22441490Smckusick case IPC_RMID: 22542961Smckusick if (cred->cr_uid && cred->cr_uid != shp->shm_perm.uid && 22642961Smckusick cred->cr_uid != shp->shm_perm.cuid) 22742961Smckusick return (EPERM); 22841490Smckusick /* set ctime? */ 22941490Smckusick shp->shm_perm.key = IPC_PRIVATE; 23041490Smckusick shp->shm_perm.mode |= SHM_DEST; 23141490Smckusick if (shp->shm_nattch <= 0) 23241490Smckusick shmfree(shp); 23341490Smckusick break; 23441490Smckusick 23541490Smckusick default: 23642961Smckusick return (EINVAL); 23741490Smckusick } 23842961Smckusick return (0); 23941490Smckusick } 24041490Smckusick 24142961Smckusick /* 24242961Smckusick * Attach to shared memory segment. 24342961Smckusick */ 24442961Smckusick shmat(p, uap, retval) 24542961Smckusick struct proc *p; 24642961Smckusick register struct args { 24741490Smckusick int shmid; 24841490Smckusick caddr_t shmaddr; 24941490Smckusick int shmflg; 25042961Smckusick } *uap; 25142961Smckusick int *retval; 25242961Smckusick { 25341490Smckusick register struct shmid_ds *shp; 25441490Smckusick register int size; 25541490Smckusick caddr_t uva; 25645737Smckusick int error; 25745737Smckusick int flags; 25845737Smckusick vm_prot_t prot; 25945737Smckusick struct shmdesc *shmd; 26041490Smckusick 26145737Smckusick /* 26245737Smckusick * Allocate descriptors now (before validity check) 26345737Smckusick * in case malloc() blocks. 26445737Smckusick */ 26547540Skarels shmd = (struct shmdesc *)p->p_vmspace->vm_shm; 26645737Smckusick size = shminfo.shmseg * sizeof(struct shmdesc); 26745737Smckusick if (shmd == NULL) { 26845737Smckusick shmd = (struct shmdesc *)malloc(size, M_SHM, M_WAITOK); 26945737Smckusick bzero((caddr_t)shmd, size); 27047540Skarels p->p_vmspace->vm_shm = (caddr_t)shmd; 27145737Smckusick } 27242961Smckusick if (error = shmvalid(uap->shmid)) 27342961Smckusick return (error); 27441490Smckusick shp = &shmsegs[uap->shmid % SHMMMNI]; 27541490Smckusick if (shp->shm_handle == NULL) 27642349Smckusick panic("shmat NULL handle"); 27743408Shibler if (error = ipcaccess(&shp->shm_perm, 27847540Skarels (uap->shmflg&SHM_RDONLY) ? IPC_R : IPC_R|IPC_W, p->p_ucred)) 27942961Smckusick return (error); 28041490Smckusick uva = uap->shmaddr; 28141490Smckusick if (uva && ((int)uva & (SHMLBA-1))) { 28241490Smckusick if (uap->shmflg & SHM_RND) 28341490Smckusick uva = (caddr_t) ((int)uva & ~(SHMLBA-1)); 28442961Smckusick else 28542961Smckusick return (EINVAL); 28641490Smckusick } 28741490Smckusick /* 28841490Smckusick * Make sure user doesn't use more than their fair share 28941490Smckusick */ 29045737Smckusick for (size = 0; size < shminfo.shmseg; size++) { 29145737Smckusick if (shmd->shmd_uva == 0) 29245737Smckusick break; 29345737Smckusick shmd++; 29445737Smckusick } 29542961Smckusick if (size >= shminfo.shmseg) 29642961Smckusick return (EMFILE); 29741490Smckusick size = ctob(clrnd(btoc(shp->shm_segsz))); 29845737Smckusick prot = VM_PROT_READ; 29945737Smckusick if ((uap->shmflg & SHM_RDONLY) == 0) 30045737Smckusick prot |= VM_PROT_WRITE; 30145737Smckusick flags = MAP_ANON|MAP_SHARED; 30245737Smckusick if (uva) 30345737Smckusick flags |= MAP_FIXED; 30445737Smckusick else 30545737Smckusick uva = (caddr_t)0x1000000; /* XXX */ 30653313Smckusick error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)&uva, 30753313Smckusick (vm_size_t)size, prot, flags, 30853313Smckusick ((struct shmhandle *)shp->shm_handle)->shmh_id, 0); 30942961Smckusick if (error) 31045737Smckusick return(error); 31145737Smckusick shmd->shmd_uva = (vm_offset_t)uva; 31245737Smckusick shmd->shmd_id = uap->shmid; 31341490Smckusick /* 31441490Smckusick * Fill in the remaining fields 31541490Smckusick */ 31642922Smckusick shp->shm_lpid = p->p_pid; 31741490Smckusick shp->shm_atime = time.tv_sec; 31841490Smckusick shp->shm_nattch++; 31942961Smckusick *retval = (int) uva; 32043408Shibler return (0); 32141490Smckusick } 32241490Smckusick 32342961Smckusick /* 32442961Smckusick * Detach from shared memory segment. 32542961Smckusick */ 32642961Smckusick /* ARGSUSED */ 32742961Smckusick shmdt(p, uap, retval) 32842961Smckusick struct proc *p; 32942961Smckusick struct args { 33042961Smckusick caddr_t shmaddr; 33142961Smckusick } *uap; 33242961Smckusick int *retval; 33341490Smckusick { 33445737Smckusick register struct shmdesc *shmd; 33545737Smckusick register int i; 33641490Smckusick 33747540Skarels shmd = (struct shmdesc *)p->p_vmspace->vm_shm; 33845737Smckusick for (i = 0; i < shminfo.shmseg; i++, shmd++) 33945737Smckusick if (shmd->shmd_uva && 34045737Smckusick shmd->shmd_uva == (vm_offset_t)uap->shmaddr) 34141490Smckusick break; 34245737Smckusick if (i == shminfo.shmseg) 34345737Smckusick return(EINVAL); 34445737Smckusick shmufree(p, shmd); 34545737Smckusick shmsegs[shmd->shmd_id % SHMMMNI].shm_lpid = p->p_pid; 34641490Smckusick } 34741490Smckusick 34847540Skarels shmfork(p1, p2, isvfork) 34947540Skarels struct proc *p1, *p2; 35045737Smckusick int isvfork; 35141490Smckusick { 35245737Smckusick register struct shmdesc *shmd; 35345737Smckusick register int size; 35441490Smckusick 35545737Smckusick /* 35645737Smckusick * Copy parents descriptive information 35745737Smckusick */ 35845737Smckusick size = shminfo.shmseg * sizeof(struct shmdesc); 35945737Smckusick shmd = (struct shmdesc *)malloc(size, M_SHM, M_WAITOK); 36047540Skarels bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmd, size); 36147540Skarels p2->p_vmspace->vm_shm = (caddr_t)shmd; 36245737Smckusick /* 36345737Smckusick * Increment reference counts 36445737Smckusick */ 36545737Smckusick for (size = 0; size < shminfo.shmseg; size++, shmd++) 36645737Smckusick if (shmd->shmd_uva) 36745737Smckusick shmsegs[shmd->shmd_id % SHMMMNI].shm_nattch++; 36841490Smckusick } 36941490Smckusick 37045737Smckusick shmexit(p) 37145737Smckusick struct proc *p; 37241490Smckusick { 37345737Smckusick register struct shmdesc *shmd; 37445737Smckusick register int i; 37541490Smckusick 37647540Skarels shmd = (struct shmdesc *)p->p_vmspace->vm_shm; 37745737Smckusick for (i = 0; i < shminfo.shmseg; i++, shmd++) 37845737Smckusick if (shmd->shmd_uva) 37945737Smckusick shmufree(p, shmd); 38047540Skarels free((caddr_t)p->p_vmspace->vm_shm, M_SHM); 38147540Skarels p->p_vmspace->vm_shm = NULL; 38241490Smckusick } 38341490Smckusick 38441490Smckusick shmvalid(id) 38541490Smckusick register int id; 38641490Smckusick { 38741490Smckusick register struct shmid_ds *shp; 38841490Smckusick 38941490Smckusick if (id < 0 || (id % SHMMMNI) >= shminfo.shmmni) 39042961Smckusick return(EINVAL); 39141490Smckusick shp = &shmsegs[id % SHMMMNI]; 39241490Smckusick if (shp->shm_perm.seq == (id / SHMMMNI) && 39341490Smckusick (shp->shm_perm.mode & (SHM_ALLOC|SHM_DEST)) == SHM_ALLOC) 39442961Smckusick return(0); 39542961Smckusick return(EINVAL); 39641490Smckusick } 39741490Smckusick 39841490Smckusick /* 39941490Smckusick * Free user resources associated with a shared memory segment 40041490Smckusick */ 40145737Smckusick shmufree(p, shmd) 40242922Smckusick struct proc *p; 40345737Smckusick struct shmdesc *shmd; 40441490Smckusick { 40541490Smckusick register struct shmid_ds *shp; 40641490Smckusick 40745737Smckusick shp = &shmsegs[shmd->shmd_id % SHMMMNI]; 40849710Shibler (void) vm_deallocate(&p->p_vmspace->vm_map, shmd->shmd_uva, 40945737Smckusick ctob(clrnd(btoc(shp->shm_segsz)))); 41045737Smckusick shmd->shmd_id = 0; 41145737Smckusick shmd->shmd_uva = 0; 41241490Smckusick shp->shm_dtime = time.tv_sec; 41341490Smckusick if (--shp->shm_nattch <= 0 && (shp->shm_perm.mode & SHM_DEST)) 41441490Smckusick shmfree(shp); 41541490Smckusick } 41641490Smckusick 41741490Smckusick /* 41841490Smckusick * Deallocate resources associated with a shared memory segment 41941490Smckusick */ 42041490Smckusick shmfree(shp) 42141490Smckusick register struct shmid_ds *shp; 42241490Smckusick { 42341490Smckusick 42441490Smckusick if (shp->shm_handle == NULL) 42541490Smckusick panic("shmfree"); 42645737Smckusick /* 42745737Smckusick * Lose our lingering object reference by deallocating space 42845737Smckusick * in kernel. Pager will also be deallocated as a side-effect. 42945737Smckusick */ 43045737Smckusick vm_deallocate(shm_map, 43145737Smckusick ((struct shmhandle *)shp->shm_handle)->shmh_kva, 43249668Shibler ctob(clrnd(btoc(shp->shm_segsz)))); 43345737Smckusick free((caddr_t)shp->shm_handle, M_SHM); 43441490Smckusick shp->shm_handle = NULL; 43541490Smckusick shmtot -= clrnd(btoc(shp->shm_segsz)); 43641490Smckusick shp->shm_perm.mode = 0; 43741490Smckusick /* 43841490Smckusick * Increment the sequence number to ensure that outstanding 43941490Smckusick * shmids for this segment will be invalid in the event that 44041490Smckusick * the segment is reallocated. Note that shmids must be 44141490Smckusick * positive as decreed by SVID. 44241490Smckusick */ 44341490Smckusick shp->shm_perm.seq++; 44441490Smckusick if ((int)(shp->shm_perm.seq * SHMMMNI) < 0) 44541490Smckusick shp->shm_perm.seq = 0; 44641490Smckusick } 44741490Smckusick 44841490Smckusick /* 44941490Smckusick * XXX This routine would be common to all sysV style IPC 45041490Smckusick * (if the others were implemented). 45141490Smckusick */ 45242961Smckusick ipcaccess(ipc, mode, cred) 45341490Smckusick register struct ipc_perm *ipc; 45442961Smckusick int mode; 45542961Smckusick register struct ucred *cred; 45641490Smckusick { 45741490Smckusick register int m; 45841490Smckusick 45942961Smckusick if (cred->cr_uid == 0) 46041490Smckusick return(0); 46141490Smckusick /* 46241490Smckusick * Access check is based on only one of owner, group, public. 46341490Smckusick * If not owner, then check group. 46441490Smckusick * If not a member of the group, then check public access. 46541490Smckusick */ 46641490Smckusick mode &= 0700; 46741490Smckusick m = ipc->mode; 46842961Smckusick if (cred->cr_uid != ipc->uid && cred->cr_uid != ipc->cuid) { 46941490Smckusick m <<= 3; 47042961Smckusick if (!groupmember(ipc->gid, cred) && 47142961Smckusick !groupmember(ipc->cgid, cred)) 47241490Smckusick m <<= 3; 47341490Smckusick } 47441490Smckusick if ((mode&m) == mode) 47542961Smckusick return (0); 47642961Smckusick return (EACCES); 47741490Smckusick } 47841490Smckusick #endif /* SYSVSHM */ 479