1/*
2 * Copyright (c) 2011, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdlib.h>
11#include <sys/shm.h>
12#include <assert.h>
13#include <stdbool.h>
14#include <errno.h>
15#include <stdio.h>
16#include <barrelfish/barrelfish.h>
17#include <octopus/init.h>
18#include <octopus/capability_storage.h>
19#include <vfs/fdtab.h>
20#include "posixcompat.h"
21
22struct _shm {
23    struct shmid_ds ds;
24    bool used;
25    key_t key;
26    struct capref frame;
27};
28
29struct _shmap {
30    bool used;
31    void *mem;
32    int id;
33};
34
35#define MAX_SHMS        32
36
37struct _shm shms[MAX_SHMS];
38struct _shmap shmaps[MAX_SHMS];
39
40void *shmat(int shmid, const void *shmaddr, int shmflg)
41{
42    assert(shmid >= 0 && shmid < MAX_SHMS);
43    struct _shm *s = &shms[shmid];
44    errval_t err;
45
46    printf("%s:%s:%d: shmid = %d, shmaddr= %p, shmflg = %d\n",
47           __FILE__, __FUNCTION__, __LINE__, shmid, shmaddr, shmflg);
48
49    struct frame_identity id;
50    vregion_flags_t attr;
51
52    int i;
53
54    for(i = 0; i < MAX_SHMS; i++) {
55      if(!shmaps[i].used) {
56        break;
57      }
58    }
59
60    if(i == MAX_SHMS) {
61        // Out of space for mappings
62        errno = ENOSPC;
63        return NULL;
64    }
65
66    struct _shmap *m = &shmaps[i];
67
68    err = frame_identify(s->frame, &id);
69    if(err_is_fail(err)) {
70        USER_PANIC_ERR(err, "frame_identify");
71    }
72
73    if(shmflg & SHM_RDONLY) {
74        attr = VREGION_FLAGS_READ;
75    } else {
76        attr = VREGION_FLAGS_READ_WRITE;
77    }
78
79    if(shmaddr != NULL) {
80        err = vspace_map_one_frame_fixed_attr((lvaddr_t)shmaddr, id.bytes,
81                                              s->frame, attr, NULL, NULL);
82        m->mem = (void *)shmaddr;
83    } else {
84        err = vspace_map_one_frame_attr(&m->mem, id.bytes, s->frame,
85                                        attr, NULL, NULL);
86    }
87
88    if(err_is_fail(err)) {
89        DEBUG_ERR(err, "vspace_map_one_frame_(fixed_)attr");
90        // XXX: Probably cause it didn't fit the virtual address space
91        m->mem = NULL;
92        errno = ENOMEM;
93        return NULL;
94    }
95
96    s->ds.shm_nattch++;
97    m->used = true;
98    m->id   = shmid;
99
100    POSIXCOMPAT_DEBUG("shmat(%d, %p, %d) = %p\n",
101                      shmid, shmaddr, shmflg, m->mem);
102    return m->mem;
103}
104
105int shmget(key_t key, size_t size, int shmflg)
106{
107    int i;
108    bool newkey = true;
109
110    // XXX: Private key not supported yet
111    assert(key != IPC_PRIVATE);
112
113    POSIXCOMPAT_DEBUG("key is not ipc_private\n");
114    for(i = 0; i < MAX_SHMS; i++) {
115        if(shms[i].used && shms[i].key == key) {
116            newkey = false;
117            break;
118        }
119    }
120
121    if(i == MAX_SHMS) {
122        // Allocate an SHM descriptor
123        for(i = 0; i < MAX_SHMS; i++) {
124            if(!shms[i].used && shms[i].ds.shm_nattch == 0) {
125                break;
126            }
127        }
128    }
129
130    if(i == MAX_SHMS) {
131        // Out of descriptors
132        errno = ENOSPC;
133        return -1;
134    }
135
136    if (newkey) {
137        struct _shm *s = &shms[i];
138        char skey[128];
139        snprintf(skey, 128, "%lu", key);
140
141        POSIXCOMPAT_DEBUG("get capability %s\n", skey);
142        oct_init(); // XXX: do some posixcompat initialization
143        // XXX: Not multi-processing safe!
144        errval_t err = oct_get_capability(skey, &s->frame);
145        if(err_is_fail(err) && err_no(err) != OCT_ERR_CAP_NAME_UNKNOWN) {
146            USER_PANIC_ERR(err, "oct_get_capability");
147        }
148        if(err == OCT_ERR_CAP_NAME_UNKNOWN) {
149            if(!(shmflg & IPC_CREAT)) {
150                errno = ENOENT;
151                return -1;
152            }
153
154            // Allocate frame (don't map it yet)
155            err = frame_alloc(&s->frame, size, NULL);
156            if(err_is_fail(err)) {
157                DEBUG_ERR(err, "frame_alloc");
158                if(err_no(err) == LIB_ERR_RAM_ALLOC_MS_CONSTRAINTS) {
159                    errno = ENOMEM;
160                }
161                return -1;
162            }
163
164            char buffer[1024];
165            debug_print_cap_at_capref(buffer, 1024, s->frame);
166            POSIXCOMPAT_DEBUG("%s:%d: store cap skey=%s capability=%s\n",
167                              __FILE__, __LINE__, skey, buffer);
168            // XXX: This can fail if someone else won the race
169            err = oct_put_capability(skey, s->frame);
170            if(err_is_fail(err)) {
171                USER_PANIC_ERR(err, "oct_put_capability");
172            }
173
174            s->ds.shm_nattch = 0;
175        }
176
177        // Assign to local cache
178        s->used = true;
179        s->key = key;
180    }
181
182    POSIXCOMPAT_DEBUG("shmget(%ld, %zu, %d) = %d\n",
183                      key, size, shmflg, i);
184
185    return i;
186}
187
188int shmctl(int shmid, int cmd, struct shmid_ds *buf)
189{
190    char skey[128];
191
192    if(shmid < 0 || shmid >= MAX_SHMS) {
193        return -1;
194    }
195
196    struct _shm *s = &shms[shmid];
197
198    if(!s->used) {
199        return -1;
200    }
201
202    POSIXCOMPAT_DEBUG("shmctl(%d, %d, %p)\n", shmid, cmd, buf);
203
204    switch(cmd) {
205    case IPC_STAT:
206        assert(!"NYI");
207        break;
208
209    case IPC_SET:
210        assert(!"NYI");
211        break;
212
213    case IPC_RMID:
214        snprintf(skey, 128, "%lu", s->key);
215
216        // This can fail if someone else won the race, but
217        // we don't really care, the key has been removed anyway
218        oct_init();
219        oct_remove_capability(skey);
220        s->used = false;
221        break;
222
223    default:
224        return -1;
225    }
226
227    return 0;
228}
229
230int shmdt(const void *shmaddr)
231{
232    struct _shm *s = NULL;
233    struct _shmap *m = NULL;
234    errval_t err;
235
236    int i;
237    for(i = 0; i < MAX_SHMS; i++) {
238        if(shmaps[i].used && shmaps[i].mem == shmaddr) {
239            m = &shmaps[i];
240            break;
241        }
242    }
243
244    if(m == NULL) {
245        errno = EINVAL;
246        return -1;
247    }
248
249    POSIXCOMPAT_DEBUG("shmdt(%p)\n", shmaddr);
250    s = &shms[i];
251
252    assert(s->ds.shm_nattch > 0);
253
254    err = vspace_unmap(shmaddr);
255    if(err_is_fail(err)) {
256        DEBUG_ERR(err, "vspace_unmap");
257        return -1;
258    }
259
260    s->ds.shm_nattch--;
261    m->used = false;
262
263    if(s->ds.shm_nattch==0)
264      s->used = false;
265
266    return 0;
267}
268