/**************************************************************************** * mm/shm/shmat.c * * Copyright (C) 2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include "shm/shm.h" #ifdef CONFIG_MM_SHM /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: shmat * * Description: * The shmat() function attaches the shared memory segment associated with * the shared memory identifier specified by shmid to the address space of * the calling process. The segment is attached at the address specified * by one of the following criteria: * * - If shmaddr is a null pointer, the segment is attached at the first * available address as selected by the system. * - If shmaddr is not a null pointer and (shmflg & SHM_RND) is non- * zero, the segment is attached at the address given by * (shmaddr - ((uintptr_t)shmaddr % SHMLBA)). * - If shmaddr is not a null pointer and (shmflg & SHM_RND) is 0, the * segment is attached at the address given by shmaddr. * - The segment is attached for reading if (shmflg & SHM_RDONLY) is * non-zero and the calling process has read permission; otherwise, if * it is 0 and the calling process has read and write permission, the * segment is attached for reading and writing. * * Input Parameters: * shmid - Shared memory identifier * smaddr - Determines mapping of the shared memory region * shmflg - See SHM_* definitions in include/sys/shm.h. Only SHM_RDONLY * and SHM_RND are supported. * * Returned Value: * Upon successful completion, shmat() will increment the value of * shm_nattch in the data structure associated with the shared memory ID * of the attached shared memory segment and return the segment's start * address. * * Otherwise, the shared memory segment will not be attached, shmat() will * return -1, and errno will be set to indicate the error. * * - EACCES * Operation permission is denied to the calling process * - EINVAL * The value of shmid is not a valid shared memory identifier, the * shmaddr is not a null pointer, and the value of * (shmaddr -((uintptr_t)shmaddr % SHMLBA)) is an illegal address for * attaching shared memory; or the shmaddr is not a null pointer, * (shmflg & SHM_RND) is 0, and the value of shmaddr is an illegal * address for attaching shared memory. * - EMFILE * The number of shared memory segments attached to the calling * process would exceed the system-imposed limit. * - ENOMEM * The available data space is not large enough to accommodate the * shared memory segment. * ****************************************************************************/ FAR void *shmat(int shmid, FAR const void *shmaddr, int shmflg) { FAR struct shm_region_s *region; FAR struct task_group_s *group; FAR struct tcb_s *tcb; uintptr_t vaddr; unsigned int npages; int ret; /* Get the region associated with the shmid */ DEBUGASSERT(shmid >= 0 && shmid < CONFIG_ARCH_SHM_MAXREGIONS); region = &g_shminfo.si_region[shmid]; DEBUGASSERT((region->sr_flags & SRFLAG_INUSE) != 0); /* Get the TCB and group containing our virtual memory allocator */ tcb = sched_self(); DEBUGASSERT(tcb && tcb->group); group = tcb->group; DEBUGASSERT(group->tg_shm.gs_handle != NULL && group->tg_shm.gs_vaddr[shmid] == 0); /* Get exclusive access to the region data structure */ ret = sem_wait(®ion->sr_sem); if (ret < 0) { shmdbg("sem_wait failed: %d\n", ret); goto errout; } /* Set aside a virtual address space to span this physical region */ vaddr = (uintptr_t)gran_alloc(group->tg_shm.gs_handle, region->sr_ds.shm_segsz); if (vaddr == 0) { shmdbg("gran_alloc() failed\n"); ret = -ENOMEM; goto errout_with_semaphore; } /* Convert the region size to pages */ npages = MM_PGALIGNUP(region->sr_ds.shm_segsz); /* Attach, i.e, map, on shared memory region to the user virtual address. */ ret = up_shmat(region->sr_pages, npages, vaddr); if (ret < 0) { shmdbg("up_shmat() failed\n"); goto errout_with_vaddr; } /* Save the virtual address of the region. We will need that in shmat() * to do the reverse lookup: Give the virtual address of the region to * detach, we need to get the region table index. */ group->tg_shm.gs_vaddr[shmid] = vaddr; /* Increment the count of processes attached to this region */ region->sr_ds.shm_nattch++; /* Save the process ID of the the last operation */ region->sr_ds.shm_lpid = tcb->pid; /* Save the time of the last shmat() */ region->sr_ds.shm_atime = time(NULL); /* Release our lock on the entry */ sem_post(®ion->sr_sem); return (FAR void *)vaddr; errout_with_vaddr: gran_free(group->tg_shm.gs_handle, (FAR void *)vaddr, region->sr_ds.shm_segsz); errout_with_semaphore: sem_post(®ion->sr_sem); set_errno(-ret); errout: return (FAR void *)ERROR; } #endif /* CONFIG_MM_SHM */