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 * 1241490Smckusick * from: Utah $Hdr: uipc_shm.c 1.9 89/08/14$ 1341490Smckusick * 14*42922Smckusick * @(#)sysv_shm.c 7.4 (Berkeley) 06/05/90 1541490Smckusick */ 1641490Smckusick 1741490Smckusick /* 1841490Smckusick * System V shared memory routines. 1941490Smckusick */ 2041490Smckusick 2141490Smckusick #ifdef SYSVSHM 2241490Smckusick 2341490Smckusick #include "machine/pte.h" 2441490Smckusick 2541490Smckusick #include "param.h" 2641490Smckusick #include "systm.h" 2741490Smckusick #include "user.h" 2841490Smckusick #include "kernel.h" 2941490Smckusick #include "proc.h" 3041490Smckusick #include "vm.h" 3141490Smckusick #include "shm.h" 3241490Smckusick #include "mapmem.h" 3341490Smckusick #include "malloc.h" 3441490Smckusick 3541490Smckusick #ifdef HPUXCOMPAT 3641490Smckusick #include "../hpux/hpux.h" 3741490Smckusick #endif 3841490Smckusick 3941490Smckusick int shmat(), shmctl(), shmdt(), shmget(); 4041490Smckusick int (*shmcalls[])() = { shmat, shmctl, shmdt, shmget }; 4141490Smckusick int shmtot = 0; 4241490Smckusick 4341490Smckusick int shmfork(), shmexit(); 4441490Smckusick struct mapmemops shmops = { shmfork, (int (*)())0, shmexit, shmexit }; 4541490Smckusick 4641490Smckusick shminit() 4741490Smckusick { 4841490Smckusick register int i; 4941490Smckusick 5041490Smckusick if (shminfo.shmmni > SHMMMNI) 5141490Smckusick shminfo.shmmni = SHMMMNI; 5241490Smckusick for (i = 0; i < shminfo.shmmni; i++) { 5341490Smckusick shmsegs[i].shm_perm.mode = 0; 5441490Smckusick shmsegs[i].shm_perm.seq = 0; 5541490Smckusick } 5641490Smckusick } 5741490Smckusick 5841490Smckusick /* entry point for all SHM calls */ 5941490Smckusick shmsys() 6041490Smckusick { 6141490Smckusick struct a { 6241490Smckusick int which; 6341490Smckusick } *uap = (struct a *)u.u_ap; 6441490Smckusick 6541490Smckusick if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) { 6641490Smckusick u.u_error = EINVAL; 6741490Smckusick return; 6841490Smckusick } 6941490Smckusick (*shmcalls[uap->which])(u.u_ap+1); 7041490Smckusick } 7141490Smckusick 7241490Smckusick /* get a shared memory segment */ 7341490Smckusick shmget(ap) 7441490Smckusick int *ap; 7541490Smckusick { 7641490Smckusick register struct a { 7741490Smckusick key_t key; 7841490Smckusick int size; 7941490Smckusick int shmflg; 8041490Smckusick } *uap = (struct a *)ap; 81*42922Smckusick struct proc *p = u.u_procp; 8241490Smckusick register struct shmid_ds *shp; 8341490Smckusick register int i; 8441490Smckusick int rval = 0, size; 8541490Smckusick caddr_t kva; 8641490Smckusick 8741490Smckusick /* look up the specified shm_id */ 8841490Smckusick if (uap->key != IPC_PRIVATE) { 8941490Smckusick for (i = 0; i < shminfo.shmmni; i++) 9041490Smckusick if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) && 9141490Smckusick shmsegs[i].shm_perm.key == uap->key) { 9241490Smckusick rval = i; 9341490Smckusick break; 9441490Smckusick } 9541490Smckusick } else 9641490Smckusick i = shminfo.shmmni; 9741490Smckusick 9841490Smckusick /* create a new shared segment if necessary */ 9941490Smckusick if (i == shminfo.shmmni) { 10041490Smckusick if ((uap->shmflg & IPC_CREAT) == 0) { 10141490Smckusick u.u_error = ENOENT; 10241490Smckusick return; 10341490Smckusick } 10441490Smckusick if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) { 10541490Smckusick u.u_error = EINVAL; 10641490Smckusick return; 10741490Smckusick } 10841490Smckusick for (i = 0; i < shminfo.shmmni; i++) 10941490Smckusick if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) == 0) { 11041490Smckusick rval = i; 11141490Smckusick break; 11241490Smckusick } 11341490Smckusick if (i == shminfo.shmmni) { 11441490Smckusick u.u_error = ENOSPC; 11541490Smckusick return; 11641490Smckusick } 11741490Smckusick size = clrnd(btoc(uap->size)); 11841490Smckusick if (shmtot + size > shminfo.shmall) { 11941490Smckusick u.u_error = ENOMEM; 12041490Smckusick return; 12141490Smckusick } 12241490Smckusick shp = &shmsegs[rval]; 12341490Smckusick /* 12441490Smckusick * We need to do a couple of things to ensure consistency 12541490Smckusick * in case we sleep in malloc(). We mark segment as 12641490Smckusick * allocated so that other shmgets() will not allocate it. 12741490Smckusick * We mark it as "destroyed" to insure that shmvalid() is 12841490Smckusick * false making most operations fail (XXX). We set the key, 12941490Smckusick * so that other shmget()s will fail. 13041490Smckusick */ 13141490Smckusick shp->shm_perm.mode = SHM_ALLOC | SHM_DEST; 13241490Smckusick shp->shm_perm.key = uap->key; 13341490Smckusick kva = (caddr_t) malloc((u_long)ctob(size), M_SHM, M_WAITOK); 13441490Smckusick if (kva == NULL) { 13541490Smckusick shp->shm_perm.mode = 0; 13641490Smckusick u.u_error = ENOMEM; 13741490Smckusick return; 13841490Smckusick } 13941490Smckusick if (!claligned(kva)) 14041490Smckusick panic("shmget: non-aligned memory"); 14141490Smckusick bzero(kva, (u_int)ctob(size)); 14241490Smckusick shmtot += size; 14341490Smckusick shp->shm_perm.cuid = shp->shm_perm.uid = u.u_uid; 14441490Smckusick shp->shm_perm.cgid = shp->shm_perm.gid = u.u_gid; 14541490Smckusick shp->shm_perm.mode = SHM_ALLOC | (uap->shmflg&0777); 14641490Smckusick shp->shm_handle = (void *) kvtopte(kva); 14741490Smckusick shp->shm_segsz = uap->size; 148*42922Smckusick shp->shm_cpid = p->p_pid; 14941490Smckusick shp->shm_lpid = shp->shm_nattch = 0; 15041490Smckusick shp->shm_atime = shp->shm_dtime = 0; 15141490Smckusick shp->shm_ctime = time.tv_sec; 15241490Smckusick } else { 15341490Smckusick shp = &shmsegs[rval]; 15441490Smckusick /* XXX: probably not the right thing to do */ 15541490Smckusick if (shp->shm_perm.mode & SHM_DEST) { 15641490Smckusick u.u_error = EBUSY; 15741490Smckusick return; 15841490Smckusick } 15941490Smckusick if (!ipcaccess(&shp->shm_perm, uap->shmflg&0777)) 16041490Smckusick return; 16141490Smckusick if (uap->size && uap->size > shp->shm_segsz) { 16241490Smckusick u.u_error = EINVAL; 16341490Smckusick return; 16441490Smckusick } 16541490Smckusick if ((uap->shmflg&IPC_CREAT) && (uap->shmflg&IPC_EXCL)) { 16641490Smckusick u.u_error = EEXIST; 16741490Smckusick return; 16841490Smckusick } 16941490Smckusick } 17041490Smckusick u.u_r.r_val1 = shp->shm_perm.seq * SHMMMNI + rval; 17141490Smckusick } 17241490Smckusick 17341490Smckusick /* shared memory control */ 17441490Smckusick shmctl(ap) 17541490Smckusick int *ap; 17641490Smckusick { 17741490Smckusick register struct a { 17841490Smckusick int shmid; 17941490Smckusick int cmd; 18041490Smckusick caddr_t buf; 18141490Smckusick } *uap = (struct a *)ap; 182*42922Smckusick struct proc *p = u.u_procp; 18341490Smckusick register struct shmid_ds *shp; 18441490Smckusick struct shmid_ds sbuf; 18541490Smckusick 18641490Smckusick if (!shmvalid(uap->shmid)) 18741490Smckusick return; 18841490Smckusick shp = &shmsegs[uap->shmid % SHMMMNI]; 18941490Smckusick switch (uap->cmd) { 19041490Smckusick case IPC_STAT: 19141490Smckusick if (ipcaccess(&shp->shm_perm, IPC_R)) 19241490Smckusick u.u_error = 19341490Smckusick copyout((caddr_t)shp, uap->buf, sizeof(*shp)); 19441490Smckusick break; 19541490Smckusick 19641490Smckusick case IPC_SET: 19741490Smckusick if (u.u_uid && u.u_uid != shp->shm_perm.uid && 19841490Smckusick u.u_uid != shp->shm_perm.cuid) { 19941490Smckusick u.u_error = EPERM; 20041490Smckusick break; 20141490Smckusick } 20241490Smckusick u.u_error = copyin(uap->buf, (caddr_t)&sbuf, sizeof sbuf); 20341490Smckusick if (!u.u_error) { 20441490Smckusick shp->shm_perm.uid = sbuf.shm_perm.uid; 20541490Smckusick shp->shm_perm.gid = sbuf.shm_perm.gid; 20641490Smckusick shp->shm_perm.mode = (shp->shm_perm.mode & ~0777) 20741490Smckusick | (sbuf.shm_perm.mode & 0777); 20841490Smckusick shp->shm_ctime = time.tv_sec; 20941490Smckusick } 21041490Smckusick break; 21141490Smckusick 21241490Smckusick case IPC_RMID: 21341490Smckusick if (u.u_uid && u.u_uid != shp->shm_perm.uid && 21441490Smckusick u.u_uid != shp->shm_perm.cuid) { 21541490Smckusick u.u_error = EPERM; 21641490Smckusick break; 21741490Smckusick } 21841490Smckusick /* set ctime? */ 21941490Smckusick shp->shm_perm.key = IPC_PRIVATE; 22041490Smckusick shp->shm_perm.mode |= SHM_DEST; 22141490Smckusick if (shp->shm_nattch <= 0) 22241490Smckusick shmfree(shp); 22341490Smckusick break; 22441490Smckusick 22541490Smckusick #ifdef HPUXCOMPAT 22641490Smckusick case SHM_LOCK: 22741490Smckusick case SHM_UNLOCK: 22841490Smckusick /* don't really do anything, but make them think we did */ 229*42922Smckusick if ((p->p_flag & SHPUX) == 0) 23041490Smckusick u.u_error = EINVAL; 23141490Smckusick else if (u.u_uid && u.u_uid != shp->shm_perm.uid && 23241490Smckusick u.u_uid != shp->shm_perm.cuid) 23341490Smckusick u.u_error = EPERM; 23441490Smckusick break; 23541490Smckusick #endif 23641490Smckusick 23741490Smckusick default: 23841490Smckusick u.u_error = EINVAL; 23941490Smckusick break; 24041490Smckusick } 24141490Smckusick } 24241490Smckusick 24341490Smckusick shmat(ap) 24441490Smckusick int *ap; 24541490Smckusick { 24641490Smckusick struct a { 24741490Smckusick int shmid; 24841490Smckusick caddr_t shmaddr; 24941490Smckusick int shmflg; 25041490Smckusick } *uap = (struct a *)ap; 251*42922Smckusick struct proc *p = u.u_procp; 25241490Smckusick register struct shmid_ds *shp; 25341490Smckusick register int size; 25441490Smckusick struct mapmem *mp; 25541490Smckusick caddr_t uva; 256*42922Smckusick int error, prot, shmmapin(); 25741490Smckusick 25841490Smckusick if (!shmvalid(uap->shmid)) 25941490Smckusick return; 26041490Smckusick shp = &shmsegs[uap->shmid % SHMMMNI]; 26141490Smckusick if (shp->shm_handle == NULL) 26242349Smckusick panic("shmat NULL handle"); 26341490Smckusick if (!ipcaccess(&shp->shm_perm, 26441490Smckusick (uap->shmflg&SHM_RDONLY) ? IPC_R : IPC_R|IPC_W)) 26541490Smckusick return; 26641490Smckusick uva = uap->shmaddr; 26741490Smckusick if (uva && ((int)uva & (SHMLBA-1))) { 26841490Smckusick if (uap->shmflg & SHM_RND) 26941490Smckusick uva = (caddr_t) ((int)uva & ~(SHMLBA-1)); 27041490Smckusick else { 27141490Smckusick u.u_error = EINVAL; 27241490Smckusick return; 27341490Smckusick } 27441490Smckusick } 27541490Smckusick /* 27641490Smckusick * Make sure user doesn't use more than their fair share 27741490Smckusick */ 27841490Smckusick size = 0; 27941490Smckusick for (mp = u.u_mmap; mp; mp = mp->mm_next) 28041490Smckusick if (mp->mm_ops == &shmops) 28141490Smckusick size++; 28241490Smckusick if (size >= shminfo.shmseg) { 28341490Smckusick u.u_error = EMFILE; 28441490Smckusick return; 28541490Smckusick } 28641490Smckusick /* 28741490Smckusick * Allocate a mapped memory region descriptor and 28841490Smckusick * attempt to expand the user page table to allow for region 28941490Smckusick */ 29041490Smckusick prot = (uap->shmflg & SHM_RDONLY) ? MM_RO : MM_RW; 29141490Smckusick #if defined(hp300) 29241490Smckusick prot |= MM_CI; 29341490Smckusick #endif 29441490Smckusick size = ctob(clrnd(btoc(shp->shm_segsz))); 295*42922Smckusick error = mmalloc(p, uap->shmid, &uva, (segsz_t)size, prot, &shmops, &mp); 296*42922Smckusick if (error) { 297*42922Smckusick u.u_error = error; 29841490Smckusick return; 299*42922Smckusick } 300*42922Smckusick if (u.u_error = mmmapin(p, mp, shmmapin)) { 301*42922Smckusick if (error = mmfree(p, mp)) 302*42922Smckusick u.u_error = error; 30341490Smckusick return; 30441490Smckusick } 30541490Smckusick /* 30641490Smckusick * Fill in the remaining fields 30741490Smckusick */ 308*42922Smckusick shp->shm_lpid = p->p_pid; 30941490Smckusick shp->shm_atime = time.tv_sec; 31041490Smckusick shp->shm_nattch++; 31141490Smckusick u.u_r.r_val1 = (int) uva; 31241490Smckusick } 31341490Smckusick 31441490Smckusick shmdt(ap) 31541490Smckusick int *ap; 31641490Smckusick { 31741490Smckusick register struct a { 31841490Smckusick caddr_t shmaddr; 31941490Smckusick } *uap = (struct a *)ap; 320*42922Smckusick struct proc *p = u.u_procp; 32141490Smckusick register struct mapmem *mp; 32241490Smckusick 32341490Smckusick for (mp = u.u_mmap; mp; mp = mp->mm_next) 32441490Smckusick if (mp->mm_ops == &shmops && mp->mm_uva == uap->shmaddr) 32541490Smckusick break; 32641490Smckusick if (mp == MMNIL) { 32741490Smckusick u.u_error = EINVAL; 32841490Smckusick return; 32941490Smckusick } 330*42922Smckusick shmsegs[mp->mm_id % SHMMMNI].shm_lpid = p->p_pid; 331*42922Smckusick u.u_error = shmufree(p, mp); 33241490Smckusick } 33341490Smckusick 33441490Smckusick shmmapin(mp, off) 33541490Smckusick struct mapmem *mp; 33641490Smckusick { 33741490Smckusick register struct shmid_ds *shp; 33841490Smckusick 33941490Smckusick shp = &shmsegs[mp->mm_id % SHMMMNI]; 34041490Smckusick if (off >= ctob(clrnd(btoc(shp->shm_segsz)))) 34141490Smckusick return(-1); 34241490Smckusick return(((struct pte *)shp->shm_handle)[btop(off)].pg_pfnum); 34341490Smckusick } 34441490Smckusick 34541490Smckusick /* 34641490Smckusick * Increment attach count on fork 34741490Smckusick */ 34841490Smckusick shmfork(mp, ischild) 34941490Smckusick register struct mapmem *mp; 35041490Smckusick { 35141490Smckusick if (!ischild) 35241490Smckusick shmsegs[mp->mm_id % SHMMMNI].shm_nattch++; 35341490Smckusick } 35441490Smckusick 35541490Smckusick /* 35641490Smckusick * Detach from shared memory segment on exit (or exec) 35741490Smckusick */ 35841490Smckusick shmexit(mp) 35941490Smckusick register struct mapmem *mp; 36041490Smckusick { 361*42922Smckusick struct proc *p = u.u_procp; /* XXX */ 362*42922Smckusick 363*42922Smckusick u.u_error = shmufree(p, mp); 36441490Smckusick } 36541490Smckusick 36641490Smckusick shmvalid(id) 36741490Smckusick register int id; 36841490Smckusick { 36941490Smckusick register struct shmid_ds *shp; 37041490Smckusick 37141490Smckusick if (id < 0 || (id % SHMMMNI) >= shminfo.shmmni) 37241490Smckusick return(0); 37341490Smckusick shp = &shmsegs[id % SHMMMNI]; 37441490Smckusick if (shp->shm_perm.seq == (id / SHMMMNI) && 37541490Smckusick (shp->shm_perm.mode & (SHM_ALLOC|SHM_DEST)) == SHM_ALLOC) 37641490Smckusick return(1); 37741490Smckusick u.u_error = EINVAL; 37841490Smckusick return(0); 37941490Smckusick } 38041490Smckusick 38141490Smckusick /* 38241490Smckusick * Free user resources associated with a shared memory segment 38341490Smckusick */ 384*42922Smckusick shmufree(p, mp) 385*42922Smckusick struct proc *p; 38641490Smckusick struct mapmem *mp; 38741490Smckusick { 38841490Smckusick register struct shmid_ds *shp; 389*42922Smckusick int error; 39041490Smckusick 39141490Smckusick shp = &shmsegs[mp->mm_id % SHMMMNI]; 392*42922Smckusick mmmapout(p, mp); 393*42922Smckusick error = mmfree(p, mp); 39441490Smckusick shp->shm_dtime = time.tv_sec; 39541490Smckusick if (--shp->shm_nattch <= 0 && (shp->shm_perm.mode & SHM_DEST)) 39641490Smckusick shmfree(shp); 397*42922Smckusick return (error); 39841490Smckusick } 39941490Smckusick 40041490Smckusick /* 40141490Smckusick * Deallocate resources associated with a shared memory segment 40241490Smckusick */ 40341490Smckusick shmfree(shp) 40441490Smckusick register struct shmid_ds *shp; 40541490Smckusick { 40641490Smckusick caddr_t kva; 40741490Smckusick 40841490Smckusick if (shp->shm_handle == NULL) 40941490Smckusick panic("shmfree"); 41041490Smckusick kva = (caddr_t) ptetokv(shp->shm_handle); 41141490Smckusick free(kva, M_SHM); 41241490Smckusick shp->shm_handle = NULL; 41341490Smckusick shmtot -= clrnd(btoc(shp->shm_segsz)); 41441490Smckusick shp->shm_perm.mode = 0; 41541490Smckusick /* 41641490Smckusick * Increment the sequence number to ensure that outstanding 41741490Smckusick * shmids for this segment will be invalid in the event that 41841490Smckusick * the segment is reallocated. Note that shmids must be 41941490Smckusick * positive as decreed by SVID. 42041490Smckusick */ 42141490Smckusick shp->shm_perm.seq++; 42241490Smckusick if ((int)(shp->shm_perm.seq * SHMMMNI) < 0) 42341490Smckusick shp->shm_perm.seq = 0; 42441490Smckusick } 42541490Smckusick 42641490Smckusick /* 42741490Smckusick * XXX This routine would be common to all sysV style IPC 42841490Smckusick * (if the others were implemented). 42941490Smckusick */ 43041490Smckusick ipcaccess(ipc, mode) 43141490Smckusick register struct ipc_perm *ipc; 43241490Smckusick { 43341490Smckusick register int m; 43441490Smckusick 43541490Smckusick if (u.u_uid == 0) 43641490Smckusick return(0); 43741490Smckusick /* 43841490Smckusick * Access check is based on only one of owner, group, public. 43941490Smckusick * If not owner, then check group. 44041490Smckusick * If not a member of the group, then check public access. 44141490Smckusick */ 44241490Smckusick mode &= 0700; 44341490Smckusick m = ipc->mode; 44441490Smckusick if (u.u_uid != ipc->uid && u.u_uid != ipc->cuid) { 44541490Smckusick m <<= 3; 44641490Smckusick if (!groupmember(ipc->gid, u.u_cred) && 44741490Smckusick !groupmember(ipc->cgid, u.u_cred)) 44841490Smckusick m <<= 3; 44941490Smckusick } 45041490Smckusick if ((mode&m) == mode) 45141490Smckusick return (1); 45241490Smckusick u.u_error = EACCES; 45341490Smckusick return (0); 45441490Smckusick } 45541490Smckusick 45641490Smckusick #endif /* SYSVSHM */ 457