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*42349Smckusick * @(#)sysv_shm.c 7.3 (Berkeley) 05/25/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; 8141490Smckusick register struct shmid_ds *shp; 8241490Smckusick register int i; 8341490Smckusick int rval = 0, size; 8441490Smckusick caddr_t kva; 8541490Smckusick 8641490Smckusick /* look up the specified shm_id */ 8741490Smckusick if (uap->key != IPC_PRIVATE) { 8841490Smckusick for (i = 0; i < shminfo.shmmni; i++) 8941490Smckusick if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) && 9041490Smckusick shmsegs[i].shm_perm.key == uap->key) { 9141490Smckusick rval = i; 9241490Smckusick break; 9341490Smckusick } 9441490Smckusick } else 9541490Smckusick i = shminfo.shmmni; 9641490Smckusick 9741490Smckusick /* create a new shared segment if necessary */ 9841490Smckusick if (i == shminfo.shmmni) { 9941490Smckusick if ((uap->shmflg & IPC_CREAT) == 0) { 10041490Smckusick u.u_error = ENOENT; 10141490Smckusick return; 10241490Smckusick } 10341490Smckusick if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) { 10441490Smckusick u.u_error = EINVAL; 10541490Smckusick return; 10641490Smckusick } 10741490Smckusick for (i = 0; i < shminfo.shmmni; i++) 10841490Smckusick if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) == 0) { 10941490Smckusick rval = i; 11041490Smckusick break; 11141490Smckusick } 11241490Smckusick if (i == shminfo.shmmni) { 11341490Smckusick u.u_error = ENOSPC; 11441490Smckusick return; 11541490Smckusick } 11641490Smckusick size = clrnd(btoc(uap->size)); 11741490Smckusick if (shmtot + size > shminfo.shmall) { 11841490Smckusick u.u_error = ENOMEM; 11941490Smckusick return; 12041490Smckusick } 12141490Smckusick shp = &shmsegs[rval]; 12241490Smckusick /* 12341490Smckusick * We need to do a couple of things to ensure consistency 12441490Smckusick * in case we sleep in malloc(). We mark segment as 12541490Smckusick * allocated so that other shmgets() will not allocate it. 12641490Smckusick * We mark it as "destroyed" to insure that shmvalid() is 12741490Smckusick * false making most operations fail (XXX). We set the key, 12841490Smckusick * so that other shmget()s will fail. 12941490Smckusick */ 13041490Smckusick shp->shm_perm.mode = SHM_ALLOC | SHM_DEST; 13141490Smckusick shp->shm_perm.key = uap->key; 13241490Smckusick kva = (caddr_t) malloc((u_long)ctob(size), M_SHM, M_WAITOK); 13341490Smckusick if (kva == NULL) { 13441490Smckusick shp->shm_perm.mode = 0; 13541490Smckusick u.u_error = ENOMEM; 13641490Smckusick return; 13741490Smckusick } 13841490Smckusick if (!claligned(kva)) 13941490Smckusick panic("shmget: non-aligned memory"); 14041490Smckusick bzero(kva, (u_int)ctob(size)); 14141490Smckusick shmtot += size; 14241490Smckusick shp->shm_perm.cuid = shp->shm_perm.uid = u.u_uid; 14341490Smckusick shp->shm_perm.cgid = shp->shm_perm.gid = u.u_gid; 14441490Smckusick shp->shm_perm.mode = SHM_ALLOC | (uap->shmflg&0777); 14541490Smckusick shp->shm_handle = (void *) kvtopte(kva); 14641490Smckusick shp->shm_segsz = uap->size; 14741490Smckusick shp->shm_cpid = u.u_procp->p_pid; 14841490Smckusick shp->shm_lpid = shp->shm_nattch = 0; 14941490Smckusick shp->shm_atime = shp->shm_dtime = 0; 15041490Smckusick shp->shm_ctime = time.tv_sec; 15141490Smckusick } else { 15241490Smckusick shp = &shmsegs[rval]; 15341490Smckusick /* XXX: probably not the right thing to do */ 15441490Smckusick if (shp->shm_perm.mode & SHM_DEST) { 15541490Smckusick u.u_error = EBUSY; 15641490Smckusick return; 15741490Smckusick } 15841490Smckusick if (!ipcaccess(&shp->shm_perm, uap->shmflg&0777)) 15941490Smckusick return; 16041490Smckusick if (uap->size && uap->size > shp->shm_segsz) { 16141490Smckusick u.u_error = EINVAL; 16241490Smckusick return; 16341490Smckusick } 16441490Smckusick if ((uap->shmflg&IPC_CREAT) && (uap->shmflg&IPC_EXCL)) { 16541490Smckusick u.u_error = EEXIST; 16641490Smckusick return; 16741490Smckusick } 16841490Smckusick } 16941490Smckusick u.u_r.r_val1 = shp->shm_perm.seq * SHMMMNI + rval; 17041490Smckusick } 17141490Smckusick 17241490Smckusick /* shared memory control */ 17341490Smckusick shmctl(ap) 17441490Smckusick int *ap; 17541490Smckusick { 17641490Smckusick register struct a { 17741490Smckusick int shmid; 17841490Smckusick int cmd; 17941490Smckusick caddr_t buf; 18041490Smckusick } *uap = (struct a *)ap; 18141490Smckusick register struct shmid_ds *shp; 18241490Smckusick struct shmid_ds sbuf; 18341490Smckusick 18441490Smckusick if (!shmvalid(uap->shmid)) 18541490Smckusick return; 18641490Smckusick shp = &shmsegs[uap->shmid % SHMMMNI]; 18741490Smckusick switch (uap->cmd) { 18841490Smckusick case IPC_STAT: 18941490Smckusick if (ipcaccess(&shp->shm_perm, IPC_R)) 19041490Smckusick u.u_error = 19141490Smckusick copyout((caddr_t)shp, uap->buf, sizeof(*shp)); 19241490Smckusick break; 19341490Smckusick 19441490Smckusick case IPC_SET: 19541490Smckusick if (u.u_uid && u.u_uid != shp->shm_perm.uid && 19641490Smckusick u.u_uid != shp->shm_perm.cuid) { 19741490Smckusick u.u_error = EPERM; 19841490Smckusick break; 19941490Smckusick } 20041490Smckusick u.u_error = copyin(uap->buf, (caddr_t)&sbuf, sizeof sbuf); 20141490Smckusick if (!u.u_error) { 20241490Smckusick shp->shm_perm.uid = sbuf.shm_perm.uid; 20341490Smckusick shp->shm_perm.gid = sbuf.shm_perm.gid; 20441490Smckusick shp->shm_perm.mode = (shp->shm_perm.mode & ~0777) 20541490Smckusick | (sbuf.shm_perm.mode & 0777); 20641490Smckusick shp->shm_ctime = time.tv_sec; 20741490Smckusick } 20841490Smckusick break; 20941490Smckusick 21041490Smckusick case IPC_RMID: 21141490Smckusick if (u.u_uid && u.u_uid != shp->shm_perm.uid && 21241490Smckusick u.u_uid != shp->shm_perm.cuid) { 21341490Smckusick u.u_error = EPERM; 21441490Smckusick break; 21541490Smckusick } 21641490Smckusick /* set ctime? */ 21741490Smckusick shp->shm_perm.key = IPC_PRIVATE; 21841490Smckusick shp->shm_perm.mode |= SHM_DEST; 21941490Smckusick if (shp->shm_nattch <= 0) 22041490Smckusick shmfree(shp); 22141490Smckusick break; 22241490Smckusick 22341490Smckusick #ifdef HPUXCOMPAT 22441490Smckusick case SHM_LOCK: 22541490Smckusick case SHM_UNLOCK: 22641490Smckusick /* don't really do anything, but make them think we did */ 22741490Smckusick if ((u.u_procp->p_flag & SHPUX) == 0) 22841490Smckusick u.u_error = EINVAL; 22941490Smckusick else if (u.u_uid && u.u_uid != shp->shm_perm.uid && 23041490Smckusick u.u_uid != shp->shm_perm.cuid) 23141490Smckusick u.u_error = EPERM; 23241490Smckusick break; 23341490Smckusick #endif 23441490Smckusick 23541490Smckusick default: 23641490Smckusick u.u_error = EINVAL; 23741490Smckusick break; 23841490Smckusick } 23941490Smckusick } 24041490Smckusick 24141490Smckusick shmat(ap) 24241490Smckusick int *ap; 24341490Smckusick { 24441490Smckusick struct a { 24541490Smckusick int shmid; 24641490Smckusick caddr_t shmaddr; 24741490Smckusick int shmflg; 24841490Smckusick } *uap = (struct a *)ap; 24941490Smckusick register struct shmid_ds *shp; 25041490Smckusick register int size; 25141490Smckusick struct mapmem *mp; 25241490Smckusick caddr_t uva; 25341490Smckusick int prot, shmmapin(); 25441490Smckusick 25541490Smckusick if (!shmvalid(uap->shmid)) 25641490Smckusick return; 25741490Smckusick shp = &shmsegs[uap->shmid % SHMMMNI]; 25841490Smckusick if (shp->shm_handle == NULL) 259*42349Smckusick panic("shmat NULL handle"); 26041490Smckusick if (!ipcaccess(&shp->shm_perm, 26141490Smckusick (uap->shmflg&SHM_RDONLY) ? IPC_R : IPC_R|IPC_W)) 26241490Smckusick return; 26341490Smckusick uva = uap->shmaddr; 26441490Smckusick if (uva && ((int)uva & (SHMLBA-1))) { 26541490Smckusick if (uap->shmflg & SHM_RND) 26641490Smckusick uva = (caddr_t) ((int)uva & ~(SHMLBA-1)); 26741490Smckusick else { 26841490Smckusick u.u_error = EINVAL; 26941490Smckusick return; 27041490Smckusick } 27141490Smckusick } 27241490Smckusick /* 27341490Smckusick * Make sure user doesn't use more than their fair share 27441490Smckusick */ 27541490Smckusick size = 0; 27641490Smckusick for (mp = u.u_mmap; mp; mp = mp->mm_next) 27741490Smckusick if (mp->mm_ops == &shmops) 27841490Smckusick size++; 27941490Smckusick if (size >= shminfo.shmseg) { 28041490Smckusick u.u_error = EMFILE; 28141490Smckusick return; 28241490Smckusick } 28341490Smckusick /* 28441490Smckusick * Allocate a mapped memory region descriptor and 28541490Smckusick * attempt to expand the user page table to allow for region 28641490Smckusick */ 28741490Smckusick prot = (uap->shmflg & SHM_RDONLY) ? MM_RO : MM_RW; 28841490Smckusick #if defined(hp300) 28941490Smckusick prot |= MM_CI; 29041490Smckusick #endif 29141490Smckusick size = ctob(clrnd(btoc(shp->shm_segsz))); 29242087Sbostic mp = mmalloc(uap->shmid, &uva, (segsz_t)size, prot, &shmops); 29341490Smckusick if (mp == MMNIL) 29441490Smckusick return; 29541490Smckusick if (!mmmapin(mp, shmmapin)) { 29641490Smckusick mmfree(mp); 29741490Smckusick return; 29841490Smckusick } 29941490Smckusick /* 30041490Smckusick * Fill in the remaining fields 30141490Smckusick */ 30241490Smckusick shp->shm_lpid = u.u_procp->p_pid; 30341490Smckusick shp->shm_atime = time.tv_sec; 30441490Smckusick shp->shm_nattch++; 30541490Smckusick u.u_r.r_val1 = (int) uva; 30641490Smckusick } 30741490Smckusick 30841490Smckusick shmdt(ap) 30941490Smckusick int *ap; 31041490Smckusick { 31141490Smckusick register struct a { 31241490Smckusick caddr_t shmaddr; 31341490Smckusick } *uap = (struct a *)ap; 31441490Smckusick register struct mapmem *mp; 31541490Smckusick 31641490Smckusick for (mp = u.u_mmap; mp; mp = mp->mm_next) 31741490Smckusick if (mp->mm_ops == &shmops && mp->mm_uva == uap->shmaddr) 31841490Smckusick break; 31941490Smckusick if (mp == MMNIL) { 32041490Smckusick u.u_error = EINVAL; 32141490Smckusick return; 32241490Smckusick } 32341490Smckusick shmsegs[mp->mm_id % SHMMMNI].shm_lpid = u.u_procp->p_pid; 32441490Smckusick shmufree(mp); 32541490Smckusick } 32641490Smckusick 32741490Smckusick shmmapin(mp, off) 32841490Smckusick struct mapmem *mp; 32941490Smckusick { 33041490Smckusick register struct shmid_ds *shp; 33141490Smckusick 33241490Smckusick shp = &shmsegs[mp->mm_id % SHMMMNI]; 33341490Smckusick if (off >= ctob(clrnd(btoc(shp->shm_segsz)))) 33441490Smckusick return(-1); 33541490Smckusick return(((struct pte *)shp->shm_handle)[btop(off)].pg_pfnum); 33641490Smckusick } 33741490Smckusick 33841490Smckusick /* 33941490Smckusick * Increment attach count on fork 34041490Smckusick */ 34141490Smckusick shmfork(mp, ischild) 34241490Smckusick register struct mapmem *mp; 34341490Smckusick { 34441490Smckusick if (!ischild) 34541490Smckusick shmsegs[mp->mm_id % SHMMMNI].shm_nattch++; 34641490Smckusick } 34741490Smckusick 34841490Smckusick /* 34941490Smckusick * Detach from shared memory segment on exit (or exec) 35041490Smckusick */ 35141490Smckusick shmexit(mp) 35241490Smckusick register struct mapmem *mp; 35341490Smckusick { 35441490Smckusick shmufree(mp); 35541490Smckusick } 35641490Smckusick 35741490Smckusick shmvalid(id) 35841490Smckusick register int id; 35941490Smckusick { 36041490Smckusick register struct shmid_ds *shp; 36141490Smckusick 36241490Smckusick if (id < 0 || (id % SHMMMNI) >= shminfo.shmmni) 36341490Smckusick return(0); 36441490Smckusick shp = &shmsegs[id % SHMMMNI]; 36541490Smckusick if (shp->shm_perm.seq == (id / SHMMMNI) && 36641490Smckusick (shp->shm_perm.mode & (SHM_ALLOC|SHM_DEST)) == SHM_ALLOC) 36741490Smckusick return(1); 36841490Smckusick u.u_error = EINVAL; 36941490Smckusick return(0); 37041490Smckusick } 37141490Smckusick 37241490Smckusick /* 37341490Smckusick * Free user resources associated with a shared memory segment 37441490Smckusick */ 37541490Smckusick shmufree(mp) 37641490Smckusick struct mapmem *mp; 37741490Smckusick { 37841490Smckusick register struct shmid_ds *shp; 37941490Smckusick 38041490Smckusick shp = &shmsegs[mp->mm_id % SHMMMNI]; 38141490Smckusick mmmapout(mp); 38241490Smckusick mmfree(mp); 38341490Smckusick shp->shm_dtime = time.tv_sec; 38441490Smckusick if (--shp->shm_nattch <= 0 && (shp->shm_perm.mode & SHM_DEST)) 38541490Smckusick shmfree(shp); 38641490Smckusick } 38741490Smckusick 38841490Smckusick /* 38941490Smckusick * Deallocate resources associated with a shared memory segment 39041490Smckusick */ 39141490Smckusick shmfree(shp) 39241490Smckusick register struct shmid_ds *shp; 39341490Smckusick { 39441490Smckusick caddr_t kva; 39541490Smckusick 39641490Smckusick if (shp->shm_handle == NULL) 39741490Smckusick panic("shmfree"); 39841490Smckusick kva = (caddr_t) ptetokv(shp->shm_handle); 39941490Smckusick free(kva, M_SHM); 40041490Smckusick shp->shm_handle = NULL; 40141490Smckusick shmtot -= clrnd(btoc(shp->shm_segsz)); 40241490Smckusick shp->shm_perm.mode = 0; 40341490Smckusick /* 40441490Smckusick * Increment the sequence number to ensure that outstanding 40541490Smckusick * shmids for this segment will be invalid in the event that 40641490Smckusick * the segment is reallocated. Note that shmids must be 40741490Smckusick * positive as decreed by SVID. 40841490Smckusick */ 40941490Smckusick shp->shm_perm.seq++; 41041490Smckusick if ((int)(shp->shm_perm.seq * SHMMMNI) < 0) 41141490Smckusick shp->shm_perm.seq = 0; 41241490Smckusick } 41341490Smckusick 41441490Smckusick /* 41541490Smckusick * XXX This routine would be common to all sysV style IPC 41641490Smckusick * (if the others were implemented). 41741490Smckusick */ 41841490Smckusick ipcaccess(ipc, mode) 41941490Smckusick register struct ipc_perm *ipc; 42041490Smckusick { 42141490Smckusick register int m; 42241490Smckusick 42341490Smckusick if (u.u_uid == 0) 42441490Smckusick return(0); 42541490Smckusick /* 42641490Smckusick * Access check is based on only one of owner, group, public. 42741490Smckusick * If not owner, then check group. 42841490Smckusick * If not a member of the group, then check public access. 42941490Smckusick */ 43041490Smckusick mode &= 0700; 43141490Smckusick m = ipc->mode; 43241490Smckusick if (u.u_uid != ipc->uid && u.u_uid != ipc->cuid) { 43341490Smckusick m <<= 3; 43441490Smckusick if (!groupmember(ipc->gid, u.u_cred) && 43541490Smckusick !groupmember(ipc->cgid, u.u_cred)) 43641490Smckusick m <<= 3; 43741490Smckusick } 43841490Smckusick if ((mode&m) == mode) 43941490Smckusick return (1); 44041490Smckusick u.u_error = EACCES; 44141490Smckusick return (0); 44241490Smckusick } 44341490Smckusick 44441490Smckusick #endif /* SYSVSHM */ 445