OpenCores
URL https://opencores.org/ocsvn/c0or1k/c0or1k/trunk

Subversion Repositories c0or1k

[/] [c0or1k/] [trunk/] [conts/] [posix/] [mm0/] [mm/] [user.c] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 drasko
/*
2
 * Functions to validate, map and unmap user buffers.
3
 *
4
 * Copyright (C) 2008 Bahadir Balban
5
 */
6
#include L4LIB_INC_ARCH(syslib.h)
7
#include <vm_area.h>
8
#include <task.h>
9
#include <user.h>
10
#include <l4/api/errno.h>
11
#include <malloc/malloc.h>
12
 
13
/*
14
 * Checks if the given user virtual address range is
15
 * validly owned by that user with given flags.
16
 *
17
 * FIXME: This scans the vmas page by page, we can do it faster
18
 * by leaping from one vma to next.
19
 */
20
int pager_validate_user_range(struct tcb *user, void *userptr, unsigned long size,
21
                              unsigned int vmflags)
22
{
23
        struct vm_area *vma;
24
        unsigned long start = page_align(userptr);
25
        unsigned long end = page_align_up(userptr + size);
26
 
27
        /* Find the vma that maps that virtual address */
28
        for (unsigned long vaddr = start; vaddr < end; vaddr += PAGE_SIZE) {
29
                if (!(vma = find_vma(vaddr, &user->vm_area_head->list))) {
30
                        //printf("%s: No VMA found for 0x%x on task: %d\n",
31
                        //       __FUNCTION__, vaddr, user->tid);
32
                        return -1;
33
                }
34
                if ((vma->flags & vmflags) != vmflags)
35
                        return -1;
36
        }
37
 
38
        return 0;
39
}
40
 
41
/*
42
 * Validates and maps the user virtual address range to the pager.
43
 * Every virtual page needs to be mapped individually because it's
44
 * not guaranteed pages are physically contiguous.
45
 *
46
 * FIXME: There's no logic here to make non-contiguous physical pages
47
 * to get mapped virtually contiguous.
48
 */
49
void *pager_get_user_page(struct tcb *user, void *userptr,
50
                          unsigned long size, unsigned int vm_flags)
51
{
52
        unsigned long start = page_align(userptr);
53
        unsigned long end = page_align_up(userptr + size);
54
        void *mapped = 0;
55
 
56
        /* Validate that user task owns this address range */
57
        if (pager_validate_user_range(user, userptr, size, vm_flags) < 0)
58
                return 0;
59
 
60
        /* Map first page and calculate the mapped address of pointer */
61
        mapped = page_to_virt(task_prefault_page(user, start, vm_flags));
62
        mapped = (void *)(((unsigned long)mapped) |
63
                          ((unsigned long)(PAGE_MASK & (unsigned long)userptr)));
64
 
65
        /* Map the rest of the pages, if any */
66
        for (unsigned long i = start + PAGE_SIZE; i < end; i += PAGE_SIZE)
67
                BUG();
68
 
69
        return mapped;
70
}
71
 
72
/*
73
 * Copy from one buffer to another. Stop if maxlength or
74
 * a page boundary is hit.
75
 */
76
int strncpy_page(void *to_ptr, void *from_ptr, int maxlength)
77
{
78
        int count = 0;
79
        char *to = to_ptr, *from = from_ptr;
80
 
81
        do {
82
                if ((to[count] = from[count]) == '\0') {
83
                        count++;
84
                        break;
85
                } else
86
                        count++;
87
        } while (count < maxlength && !page_boundary(&from[count]));
88
 
89
        if (page_boundary(&from[count]))
90
                return -EFAULT;
91
        if (count == maxlength)
92
                return -E2BIG;
93
 
94
        return count;
95
}
96
 
97
/*
98
 * Copy from one buffer to another. Stop if maxlength or
99
 * a page boundary is hit. Breaks if unsigned long sized copy value is 0,
100
 * as opposed to a 0 byte as in string copy. If byte size 0 was used
101
 * a valid pointer with a 0 byte in it would give a false termination.
102
 */
103
int bufncpy_page(void *to_ptr, void *from_ptr, int maxlength)
104
{
105
        int count = 0;
106
        unsigned long *to = to_ptr, *from = from_ptr;
107
 
108
        do {
109
                if ((to[count] = from[count]) == 0) {
110
                        count++;
111
                        break;
112
                } else
113
                        count++;
114
        } while (count < maxlength && !page_boundary(&from[count]));
115
 
116
        if (page_boundary(&from[count]))
117
                return -EFAULT;
118
        if (count == maxlength)
119
                return -E2BIG;
120
 
121
        return count;
122
}
123
 
124
/*
125
 * Copies src to dest for given size, return -EFAULT on page boundaries.
126
 */
127
int memcpy_page(void *dst, void *src, int size, int fault_on_dest)
128
{
129
        int count = 0;
130
        char *to = dst, *from = src;
131
 
132
        if (!fault_on_dest) {
133
                do {
134
                        to[count] = from[count];
135
                        count++;
136
                } while (count < size &&
137
                         !page_boundary(&from[count]));
138
        } else {
139
                do {
140
                        to[count] = from[count];
141
                        count++;
142
                } while (count < size &&
143
                         !page_boundary(&to[count]));
144
        }
145
 
146
        if (page_boundary(&from[count]))
147
                return -EFAULT;
148
 
149
        return count;
150
}
151
 
152
int copy_from_user(struct tcb *task, void *buf, char *user, int size)
153
{
154
        int copied = 0, ret = 0, total = 0;
155
        int count = size;
156
        void *mapped = 0;
157
 
158
        if (!(mapped = pager_get_user_page(task, user, TILL_PAGE_ENDS(user),
159
                                           VM_READ)))
160
                return -EINVAL;
161
 
162
        while ((ret = memcpy_page(buf + copied, mapped, count, 0)) < 0) {
163
                copied += TILL_PAGE_ENDS(mapped);
164
                count -= TILL_PAGE_ENDS(mapped);
165
                if (!(mapped =
166
                      pager_get_user_page(task, user + copied,
167
                                          TILL_PAGE_ENDS(user + copied),
168
                                          VM_READ)))
169
                        return -EINVAL;
170
        }
171
 
172
        /* Note copied is always in bytes */
173
        total = copied + ret;
174
 
175
        return total;
176
}
177
 
178
int copy_to_user(struct tcb *task, char *user, void *buf, int size)
179
{
180
        int copied = 0, ret = 0, total = 0;
181
        int count = size;
182
        void *mapped = 0;
183
 
184
        /* Map the user page */
185
        if (!(mapped = pager_get_user_page(task, user,
186
                                           TILL_PAGE_ENDS(user),
187
                                           VM_READ | VM_WRITE)))
188
                return -EINVAL;
189
 
190
        while ((ret = memcpy_page(mapped, buf + copied, count, 1)) < 0) {
191
                copied += TILL_PAGE_ENDS(mapped);
192
                count -= TILL_PAGE_ENDS(mapped);
193
                if (!(mapped = pager_get_user_page(task, user + copied,
194
                                                   TILL_PAGE_ENDS(user + copied),
195
                                                   VM_READ | VM_WRITE)))
196
                        return -EINVAL;
197
        }
198
 
199
        /* Note copied is always in bytes */
200
        total = copied + ret;
201
 
202
        return total;
203
}
204
 
205
/*
206
 * Copies a variable sized userspace string or array of pointers
207
 * (think &argv[0]), into buffer. If a page boundary is hit,
208
 * unmaps the previous page, validates and maps the new page.
209
 */
210
int copy_user_buf(struct tcb *task, void *buf, char *user, int maxlength,
211
                  int elem_size)
212
{
213
        int count = maxlength;
214
        int copied = 0, ret = 0, total = 0;
215
        void *mapped = 0;
216
        int (*copy_func)(void *, void *, int count);
217
 
218
        /* This bit determines what size copier function to use. */
219
        if (elem_size == sizeof(char))
220
                copy_func = strncpy_page;
221
        else if (elem_size == sizeof(unsigned long))
222
                copy_func = bufncpy_page;
223
        else
224
                return -EINVAL;
225
 
226
        /* Map the first page the user buffer is in */
227
        if (!(mapped = pager_get_user_page(task, user, TILL_PAGE_ENDS(user),
228
                                           VM_READ)))
229
                return -EINVAL;
230
 
231
        while ((ret = copy_func(buf + copied, mapped, count)) < 0) {
232
                if (ret == -E2BIG)
233
                        return ret;
234
                else if (ret == -EFAULT) {
235
                        /*
236
                         * Copied is always in bytes no matter what elem_size is
237
                         * because we know we hit a page boundary and we increase
238
                         * by the page boundary bytes
239
                         */
240
                        copied += TILL_PAGE_ENDS(mapped);
241
                        count -= TILL_PAGE_ENDS(mapped);
242
                        if (!(mapped =
243
                              pager_get_user_page(task, user + copied,
244
                                                  TILL_PAGE_ENDS(user + copied),
245
                                                  VM_READ)))
246
                                return -EINVAL;
247
                }
248
        }
249
 
250
        /* Note copied is always in bytes */
251
        total = (copied / elem_size) + ret;
252
 
253
        return total;
254
}
255
 
256
/*
257
 * Calls copy_user_buf with char-sized copying. This matters because
258
 * buffer is variable and the terminator must be in char size
259
 */
260
int copy_user_string(struct tcb *task, void *buf, char *user, int maxlength)
261
{
262
        return copy_user_buf(task, buf, user, maxlength, sizeof(char));
263
}
264
 
265
/*
266
 * Calls copy_user_buf with unsigned long sized copying. This matters
267
 * because buffer is variable and the terminator must be in ulong size
268
 */
269
static inline int
270
copy_user_ptrs(struct tcb *task, void *buf, char *user,
271
                 int maxlength)
272
{
273
        return copy_user_buf(task, buf, user, maxlength, sizeof(unsigned long));
274
}
275
 
276
int copy_user_args(struct tcb *task, struct args_struct *args,
277
                   void *argv_user, int args_max)
278
{
279
        char **argv = 0;
280
        void *argsbuf;
281
        char *curbuf;
282
        int argc = 0;
283
        int used;
284
        int count;
285
 
286
        if (!(argsbuf = kzalloc(args_max)))
287
                return -ENOMEM;
288
 
289
        /*
290
         * First, copy the null-terminated array of
291
         * pointers to argument strings.
292
         */
293
        if ((count = copy_user_ptrs(task, argsbuf, argv_user, args_max)) < 0)
294
                goto out;
295
 
296
        /* On success, we get the number of arg strings + the terminator */
297
        argc = count - 1;
298
        used = count * sizeof(char *);
299
        argv = argsbuf;
300
        curbuf = argsbuf + used;
301
 
302
        /* Now we copy each argument string into buffer */
303
        for (int i = 0; i < argc; i++) {
304
                /* Copy string into empty space in buffer */
305
                if ((count = copy_user_string(task, curbuf, argv[i],
306
                                              args_max - used)) < 0)
307
                        goto out;
308
 
309
                /* Replace pointer to string with copied location */
310
                argv[i] = curbuf;
311
 
312
                /* Update current empty buffer location */
313
                curbuf += count;
314
 
315
                /* Increase used buffer count */
316
                used += count;
317
        }
318
 
319
        /* Set up the args struct */
320
        args->argc = argc;
321
        args->argv = argv;
322
        args->size = used;
323
 
324
        return 0;
325
 
326
out:
327
        kfree(argsbuf);
328
        return count;
329
}
330
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.