1*433d6423SLionel Sambuc #include "inc.h" 2*433d6423SLionel Sambuc 3*433d6423SLionel Sambuc #define MAX_SHM_NR 1024 4*433d6423SLionel Sambuc 5*433d6423SLionel Sambuc struct shm_struct { 6*433d6423SLionel Sambuc key_t key; 7*433d6423SLionel Sambuc int id; 8*433d6423SLionel Sambuc struct shmid_ds shmid_ds; 9*433d6423SLionel Sambuc vir_bytes page; 10*433d6423SLionel Sambuc int vm_id; 11*433d6423SLionel Sambuc }; 12*433d6423SLionel Sambuc static struct shm_struct shm_list[MAX_SHM_NR]; 13*433d6423SLionel Sambuc static int shm_list_nr = 0; 14*433d6423SLionel Sambuc 15*433d6423SLionel Sambuc static struct shm_struct *shm_find_key(key_t key) 16*433d6423SLionel Sambuc { 17*433d6423SLionel Sambuc int i; 18*433d6423SLionel Sambuc if (key == IPC_PRIVATE) 19*433d6423SLionel Sambuc return NULL; 20*433d6423SLionel Sambuc for (i = 0; i < shm_list_nr; i++) 21*433d6423SLionel Sambuc if (shm_list[i].key == key) 22*433d6423SLionel Sambuc return shm_list+i; 23*433d6423SLionel Sambuc return NULL; 24*433d6423SLionel Sambuc } 25*433d6423SLionel Sambuc 26*433d6423SLionel Sambuc static struct shm_struct *shm_find_id(int id) 27*433d6423SLionel Sambuc { 28*433d6423SLionel Sambuc int i; 29*433d6423SLionel Sambuc for (i = 0; i < shm_list_nr; i++) 30*433d6423SLionel Sambuc if (shm_list[i].id == id) 31*433d6423SLionel Sambuc return shm_list+i; 32*433d6423SLionel Sambuc return NULL; 33*433d6423SLionel Sambuc } 34*433d6423SLionel Sambuc 35*433d6423SLionel Sambuc /*===========================================================================* 36*433d6423SLionel Sambuc * do_shmget * 37*433d6423SLionel Sambuc *===========================================================================*/ 38*433d6423SLionel Sambuc int do_shmget(message *m) 39*433d6423SLionel Sambuc { 40*433d6423SLionel Sambuc struct shm_struct *shm; 41*433d6423SLionel Sambuc long key, size, old_size; 42*433d6423SLionel Sambuc int flag; 43*433d6423SLionel Sambuc int id; 44*433d6423SLionel Sambuc 45*433d6423SLionel Sambuc key = m->m_lc_ipc_shmget.key; 46*433d6423SLionel Sambuc old_size = size = m->m_lc_ipc_shmget.size; 47*433d6423SLionel Sambuc flag = m->m_lc_ipc_shmget.flag; 48*433d6423SLionel Sambuc 49*433d6423SLionel Sambuc if ((shm = shm_find_key(key))) { 50*433d6423SLionel Sambuc if (!check_perm(&shm->shmid_ds.shm_perm, who_e, flag)) 51*433d6423SLionel Sambuc return EACCES; 52*433d6423SLionel Sambuc if ((flag & IPC_CREAT) && (flag & IPC_EXCL)) 53*433d6423SLionel Sambuc return EEXIST; 54*433d6423SLionel Sambuc if (size && shm->shmid_ds.shm_segsz < size) 55*433d6423SLionel Sambuc return EINVAL; 56*433d6423SLionel Sambuc id = shm->id; 57*433d6423SLionel Sambuc } else { /* no key found */ 58*433d6423SLionel Sambuc if (!(flag & IPC_CREAT)) 59*433d6423SLionel Sambuc return ENOENT; 60*433d6423SLionel Sambuc if (size <= 0) 61*433d6423SLionel Sambuc return EINVAL; 62*433d6423SLionel Sambuc /* round up to a multiple of PAGE_SIZE */ 63*433d6423SLionel Sambuc if (size % PAGE_SIZE) 64*433d6423SLionel Sambuc size += PAGE_SIZE - size % PAGE_SIZE; 65*433d6423SLionel Sambuc if (size <= 0) 66*433d6423SLionel Sambuc return EINVAL; 67*433d6423SLionel Sambuc 68*433d6423SLionel Sambuc if (shm_list_nr == MAX_SHM_NR) 69*433d6423SLionel Sambuc return ENOMEM; 70*433d6423SLionel Sambuc /* TODO: shmmni should be changed... */ 71*433d6423SLionel Sambuc if (identifier == SHMMNI) 72*433d6423SLionel Sambuc return ENOSPC; 73*433d6423SLionel Sambuc shm = &shm_list[shm_list_nr]; 74*433d6423SLionel Sambuc memset(shm, 0, sizeof(struct shm_struct)); 75*433d6423SLionel Sambuc shm->page = (vir_bytes) mmap(0, size, 76*433d6423SLionel Sambuc PROT_READ|PROT_WRITE, MAP_ANON, -1, 0); 77*433d6423SLionel Sambuc if (shm->page == (vir_bytes) MAP_FAILED) 78*433d6423SLionel Sambuc return ENOMEM; 79*433d6423SLionel Sambuc shm->vm_id = vm_getphys(sef_self(), (void *) shm->page); 80*433d6423SLionel Sambuc memset((void *)shm->page, 0, size); 81*433d6423SLionel Sambuc 82*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.cuid = 83*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.uid = getnuid(who_e); 84*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.cgid = 85*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.gid = getngid(who_e); 86*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.mode = flag & 0777; 87*433d6423SLionel Sambuc shm->shmid_ds.shm_segsz = old_size; 88*433d6423SLionel Sambuc shm->shmid_ds.shm_atime = 0; 89*433d6423SLionel Sambuc shm->shmid_ds.shm_dtime = 0; 90*433d6423SLionel Sambuc shm->shmid_ds.shm_ctime = time(NULL); 91*433d6423SLionel Sambuc shm->shmid_ds.shm_cpid = getnpid(who_e); 92*433d6423SLionel Sambuc shm->shmid_ds.shm_lpid = 0; 93*433d6423SLionel Sambuc shm->shmid_ds.shm_nattch = 0; 94*433d6423SLionel Sambuc shm->id = id = identifier++; 95*433d6423SLionel Sambuc shm->key = key; 96*433d6423SLionel Sambuc 97*433d6423SLionel Sambuc shm_list_nr++; 98*433d6423SLionel Sambuc } 99*433d6423SLionel Sambuc 100*433d6423SLionel Sambuc m->m_lc_ipc_shmget.retid = id; 101*433d6423SLionel Sambuc return OK; 102*433d6423SLionel Sambuc } 103*433d6423SLionel Sambuc 104*433d6423SLionel Sambuc /*===========================================================================* 105*433d6423SLionel Sambuc * do_shmat * 106*433d6423SLionel Sambuc *===========================================================================*/ 107*433d6423SLionel Sambuc int do_shmat(message *m) 108*433d6423SLionel Sambuc { 109*433d6423SLionel Sambuc int id, flag; 110*433d6423SLionel Sambuc vir_bytes addr; 111*433d6423SLionel Sambuc void *ret; 112*433d6423SLionel Sambuc struct shm_struct *shm; 113*433d6423SLionel Sambuc 114*433d6423SLionel Sambuc id = m->m_lc_ipc_shmat.id; 115*433d6423SLionel Sambuc addr = (vir_bytes) m->m_lc_ipc_shmat.addr; 116*433d6423SLionel Sambuc flag = m->m_lc_ipc_shmat.flag; 117*433d6423SLionel Sambuc 118*433d6423SLionel Sambuc if (addr && (addr % PAGE_SIZE)) { 119*433d6423SLionel Sambuc if (flag & SHM_RND) 120*433d6423SLionel Sambuc addr -= (addr % PAGE_SIZE); 121*433d6423SLionel Sambuc else 122*433d6423SLionel Sambuc return EINVAL; 123*433d6423SLionel Sambuc } 124*433d6423SLionel Sambuc 125*433d6423SLionel Sambuc if (!(shm = shm_find_id(id))) 126*433d6423SLionel Sambuc return EINVAL; 127*433d6423SLionel Sambuc 128*433d6423SLionel Sambuc if (flag & SHM_RDONLY) 129*433d6423SLionel Sambuc flag = 0444; 130*433d6423SLionel Sambuc else 131*433d6423SLionel Sambuc flag = 0666; 132*433d6423SLionel Sambuc if (!check_perm(&shm->shmid_ds.shm_perm, who_e, flag)) 133*433d6423SLionel Sambuc return EACCES; 134*433d6423SLionel Sambuc 135*433d6423SLionel Sambuc ret = vm_remap(who_e, sef_self(), (void *)addr, (void *)shm->page, 136*433d6423SLionel Sambuc shm->shmid_ds.shm_segsz); 137*433d6423SLionel Sambuc if (ret == MAP_FAILED) 138*433d6423SLionel Sambuc return ENOMEM; 139*433d6423SLionel Sambuc 140*433d6423SLionel Sambuc shm->shmid_ds.shm_atime = time(NULL); 141*433d6423SLionel Sambuc shm->shmid_ds.shm_lpid = getnpid(who_e); 142*433d6423SLionel Sambuc /* nattach is updated lazily */ 143*433d6423SLionel Sambuc 144*433d6423SLionel Sambuc m->m_lc_ipc_shmat.retaddr = ret; 145*433d6423SLionel Sambuc return OK; 146*433d6423SLionel Sambuc } 147*433d6423SLionel Sambuc 148*433d6423SLionel Sambuc /*===========================================================================* 149*433d6423SLionel Sambuc * update_refcount_and_destroy * 150*433d6423SLionel Sambuc *===========================================================================*/ 151*433d6423SLionel Sambuc void update_refcount_and_destroy(void) 152*433d6423SLionel Sambuc { 153*433d6423SLionel Sambuc int i, j; 154*433d6423SLionel Sambuc 155*433d6423SLionel Sambuc for (i = 0, j = 0; i < shm_list_nr; i++) { 156*433d6423SLionel Sambuc u8_t rc; 157*433d6423SLionel Sambuc 158*433d6423SLionel Sambuc rc = vm_getrefcount(sef_self(), (void *) shm_list[i].page); 159*433d6423SLionel Sambuc if (rc == (u8_t) -1) { 160*433d6423SLionel Sambuc printf("IPC: can't find physical region.\n"); 161*433d6423SLionel Sambuc continue; 162*433d6423SLionel Sambuc } 163*433d6423SLionel Sambuc shm_list[i].shmid_ds.shm_nattch = rc - 1; 164*433d6423SLionel Sambuc 165*433d6423SLionel Sambuc if (shm_list[i].shmid_ds.shm_nattch || 166*433d6423SLionel Sambuc !(shm_list[i].shmid_ds.shm_perm.mode & SHM_DEST)) { 167*433d6423SLionel Sambuc if (i != j) 168*433d6423SLionel Sambuc shm_list[j] = shm_list[i]; 169*433d6423SLionel Sambuc j++; 170*433d6423SLionel Sambuc } else { 171*433d6423SLionel Sambuc int size = shm_list[i].shmid_ds.shm_segsz; 172*433d6423SLionel Sambuc if (size % PAGE_SIZE) 173*433d6423SLionel Sambuc size += PAGE_SIZE - size % PAGE_SIZE; 174*433d6423SLionel Sambuc munmap((void *)shm_list[i].page, size); 175*433d6423SLionel Sambuc } 176*433d6423SLionel Sambuc } 177*433d6423SLionel Sambuc shm_list_nr = j; 178*433d6423SLionel Sambuc } 179*433d6423SLionel Sambuc 180*433d6423SLionel Sambuc /*===========================================================================* 181*433d6423SLionel Sambuc * do_shmdt * 182*433d6423SLionel Sambuc *===========================================================================*/ 183*433d6423SLionel Sambuc int do_shmdt(message *m) 184*433d6423SLionel Sambuc { 185*433d6423SLionel Sambuc vir_bytes addr; 186*433d6423SLionel Sambuc phys_bytes vm_id; 187*433d6423SLionel Sambuc int i; 188*433d6423SLionel Sambuc 189*433d6423SLionel Sambuc addr = (vir_bytes) m->m_lc_ipc_shmdt.addr; 190*433d6423SLionel Sambuc 191*433d6423SLionel Sambuc if ((vm_id = vm_getphys(who_e, (void *) addr)) == 0) 192*433d6423SLionel Sambuc return EINVAL; 193*433d6423SLionel Sambuc 194*433d6423SLionel Sambuc for (i = 0; i < shm_list_nr; i++) { 195*433d6423SLionel Sambuc if (shm_list[i].vm_id == vm_id) { 196*433d6423SLionel Sambuc struct shm_struct *shm = &shm_list[i]; 197*433d6423SLionel Sambuc 198*433d6423SLionel Sambuc shm->shmid_ds.shm_atime = time(NULL); 199*433d6423SLionel Sambuc shm->shmid_ds.shm_lpid = getnpid(who_e); 200*433d6423SLionel Sambuc /* nattch is updated lazily */ 201*433d6423SLionel Sambuc 202*433d6423SLionel Sambuc vm_unmap(who_e, (void *) addr); 203*433d6423SLionel Sambuc break; 204*433d6423SLionel Sambuc } 205*433d6423SLionel Sambuc } 206*433d6423SLionel Sambuc if (i == shm_list_nr) 207*433d6423SLionel Sambuc printf("IPC: do_shmdt impossible error! could not find id %lu to unmap\n", 208*433d6423SLionel Sambuc vm_id); 209*433d6423SLionel Sambuc 210*433d6423SLionel Sambuc update_refcount_and_destroy(); 211*433d6423SLionel Sambuc 212*433d6423SLionel Sambuc return OK; 213*433d6423SLionel Sambuc } 214*433d6423SLionel Sambuc 215*433d6423SLionel Sambuc /*===========================================================================* 216*433d6423SLionel Sambuc * do_shmctl * 217*433d6423SLionel Sambuc *===========================================================================*/ 218*433d6423SLionel Sambuc int do_shmctl(message *m) 219*433d6423SLionel Sambuc { 220*433d6423SLionel Sambuc int id = m->m_lc_ipc_shmctl.id; 221*433d6423SLionel Sambuc int cmd = m->m_lc_ipc_shmctl.cmd; 222*433d6423SLionel Sambuc struct shmid_ds *ds = (struct shmid_ds *)m->m_lc_ipc_shmctl.buf; 223*433d6423SLionel Sambuc struct shmid_ds tmp_ds; 224*433d6423SLionel Sambuc struct shm_struct *shm = NULL; 225*433d6423SLionel Sambuc struct shminfo sinfo; 226*433d6423SLionel Sambuc struct shm_info s_info; 227*433d6423SLionel Sambuc uid_t uid; 228*433d6423SLionel Sambuc int r, i; 229*433d6423SLionel Sambuc 230*433d6423SLionel Sambuc if (cmd == IPC_STAT) 231*433d6423SLionel Sambuc update_refcount_and_destroy(); 232*433d6423SLionel Sambuc 233*433d6423SLionel Sambuc if ((cmd == IPC_STAT || 234*433d6423SLionel Sambuc cmd == IPC_SET || 235*433d6423SLionel Sambuc cmd == IPC_RMID) && 236*433d6423SLionel Sambuc !(shm = shm_find_id(id))) 237*433d6423SLionel Sambuc return EINVAL; 238*433d6423SLionel Sambuc 239*433d6423SLionel Sambuc switch (cmd) { 240*433d6423SLionel Sambuc case IPC_STAT: 241*433d6423SLionel Sambuc if (!ds) 242*433d6423SLionel Sambuc return EFAULT; 243*433d6423SLionel Sambuc /* check whether it has read permission */ 244*433d6423SLionel Sambuc if (!check_perm(&shm->shmid_ds.shm_perm, who_e, 0444)) 245*433d6423SLionel Sambuc return EACCES; 246*433d6423SLionel Sambuc r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds, 247*433d6423SLionel Sambuc who_e, (vir_bytes)ds, sizeof(struct shmid_ds)); 248*433d6423SLionel Sambuc if (r != OK) 249*433d6423SLionel Sambuc return EFAULT; 250*433d6423SLionel Sambuc break; 251*433d6423SLionel Sambuc case IPC_SET: 252*433d6423SLionel Sambuc uid = getnuid(who_e); 253*433d6423SLionel Sambuc if (uid != shm->shmid_ds.shm_perm.cuid && 254*433d6423SLionel Sambuc uid != shm->shmid_ds.shm_perm.uid && 255*433d6423SLionel Sambuc uid != 0) 256*433d6423SLionel Sambuc return EPERM; 257*433d6423SLionel Sambuc r = sys_datacopy(who_e, (vir_bytes)ds, 258*433d6423SLionel Sambuc SELF, (vir_bytes)&tmp_ds, sizeof(struct shmid_ds)); 259*433d6423SLionel Sambuc if (r != OK) 260*433d6423SLionel Sambuc return EFAULT; 261*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.uid = tmp_ds.shm_perm.uid; 262*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.gid = tmp_ds.shm_perm.gid; 263*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.mode &= ~0777; 264*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.mode |= tmp_ds.shm_perm.mode & 0666; 265*433d6423SLionel Sambuc shm->shmid_ds.shm_ctime = time(NULL); 266*433d6423SLionel Sambuc break; 267*433d6423SLionel Sambuc case IPC_RMID: 268*433d6423SLionel Sambuc uid = getnuid(who_e); 269*433d6423SLionel Sambuc if (uid != shm->shmid_ds.shm_perm.cuid && 270*433d6423SLionel Sambuc uid != shm->shmid_ds.shm_perm.uid && 271*433d6423SLionel Sambuc uid != 0) 272*433d6423SLionel Sambuc return EPERM; 273*433d6423SLionel Sambuc shm->shmid_ds.shm_perm.mode |= SHM_DEST; 274*433d6423SLionel Sambuc /* destroy if possible */ 275*433d6423SLionel Sambuc update_refcount_and_destroy(); 276*433d6423SLionel Sambuc break; 277*433d6423SLionel Sambuc case IPC_INFO: 278*433d6423SLionel Sambuc if (!ds) 279*433d6423SLionel Sambuc return EFAULT; 280*433d6423SLionel Sambuc sinfo.shmmax = (unsigned long) -1; 281*433d6423SLionel Sambuc sinfo.shmmin = 1; 282*433d6423SLionel Sambuc sinfo.shmmni = MAX_SHM_NR; 283*433d6423SLionel Sambuc sinfo.shmseg = (unsigned long) -1; 284*433d6423SLionel Sambuc sinfo.shmall = (unsigned long) -1; 285*433d6423SLionel Sambuc r = sys_datacopy(SELF, (vir_bytes)&sinfo, 286*433d6423SLionel Sambuc who_e, (vir_bytes)ds, sizeof(struct shminfo)); 287*433d6423SLionel Sambuc if (r != OK) 288*433d6423SLionel Sambuc return EFAULT; 289*433d6423SLionel Sambuc m->m_lc_ipc_shmctl.ret = (shm_list_nr - 1); 290*433d6423SLionel Sambuc if (m->m_lc_ipc_shmctl.ret < 0) 291*433d6423SLionel Sambuc m->m_lc_ipc_shmctl.ret = 0; 292*433d6423SLionel Sambuc break; 293*433d6423SLionel Sambuc case SHM_INFO: 294*433d6423SLionel Sambuc if (!ds) 295*433d6423SLionel Sambuc return EFAULT; 296*433d6423SLionel Sambuc s_info.used_ids = shm_list_nr; 297*433d6423SLionel Sambuc s_info.shm_tot = 0; 298*433d6423SLionel Sambuc for (i = 0; i < shm_list_nr; i++) 299*433d6423SLionel Sambuc s_info.shm_tot += 300*433d6423SLionel Sambuc shm_list[i].shmid_ds.shm_segsz/PAGE_SIZE; 301*433d6423SLionel Sambuc s_info.shm_rss = s_info.shm_tot; 302*433d6423SLionel Sambuc s_info.shm_swp = 0; 303*433d6423SLionel Sambuc s_info.swap_attempts = 0; 304*433d6423SLionel Sambuc s_info.swap_successes = 0; 305*433d6423SLionel Sambuc r = sys_datacopy(SELF, (vir_bytes)&s_info, 306*433d6423SLionel Sambuc who_e, (vir_bytes)ds, sizeof(struct shm_info)); 307*433d6423SLionel Sambuc if (r != OK) 308*433d6423SLionel Sambuc return EFAULT; 309*433d6423SLionel Sambuc m->m_lc_ipc_shmctl.ret = shm_list_nr - 1; 310*433d6423SLionel Sambuc if (m->m_lc_ipc_shmctl.ret < 0) 311*433d6423SLionel Sambuc m->m_lc_ipc_shmctl.ret = 0; 312*433d6423SLionel Sambuc break; 313*433d6423SLionel Sambuc case SHM_STAT: 314*433d6423SLionel Sambuc if (id < 0 || id >= shm_list_nr) 315*433d6423SLionel Sambuc return EINVAL; 316*433d6423SLionel Sambuc shm = &shm_list[id]; 317*433d6423SLionel Sambuc r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds, 318*433d6423SLionel Sambuc who_e, (vir_bytes)ds, sizeof(struct shmid_ds)); 319*433d6423SLionel Sambuc if (r != OK) 320*433d6423SLionel Sambuc return EFAULT; 321*433d6423SLionel Sambuc m->m_lc_ipc_shmctl.ret = shm->id; 322*433d6423SLionel Sambuc break; 323*433d6423SLionel Sambuc default: 324*433d6423SLionel Sambuc return EINVAL; 325*433d6423SLionel Sambuc } 326*433d6423SLionel Sambuc return OK; 327*433d6423SLionel Sambuc } 328*433d6423SLionel Sambuc 329*433d6423SLionel Sambuc #if 0 330*433d6423SLionel Sambuc static void list_shm_ds(void) 331*433d6423SLionel Sambuc { 332*433d6423SLionel Sambuc int i; 333*433d6423SLionel Sambuc printf("key\tid\tpage\n"); 334*433d6423SLionel Sambuc for (i = 0; i < shm_list_nr; i++) 335*433d6423SLionel Sambuc printf("%ld\t%d\t%lx\n", 336*433d6423SLionel Sambuc shm_list[i].key, 337*433d6423SLionel Sambuc shm_list[i].id, 338*433d6423SLionel Sambuc shm_list[i].page); 339*433d6423SLionel Sambuc } 340*433d6423SLionel Sambuc #endif 341*433d6423SLionel Sambuc 342*433d6423SLionel Sambuc /*===========================================================================* 343*433d6423SLionel Sambuc * is_shm_nil * 344*433d6423SLionel Sambuc *===========================================================================*/ 345*433d6423SLionel Sambuc int is_shm_nil(void) 346*433d6423SLionel Sambuc { 347*433d6423SLionel Sambuc return (shm_list_nr == 0); 348*433d6423SLionel Sambuc } 349