root / prex-0.9.0 / sys / mem / vm.c @ 03e9c04a
History | View | Annotate | Download (20.2 KB)
1 |
/*-
|
---|---|
2 |
* Copyright (c) 2005-2009, Kohsuke Ohtani
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions
|
7 |
* are met:
|
8 |
* 1. Redistributions of source code must retain the above copyright
|
9 |
* notice, this list of conditions and the following disclaimer.
|
10 |
* 2. Redistributions in binary form must reproduce the above copyright
|
11 |
* notice, this list of conditions and the following disclaimer in the
|
12 |
* documentation and/or other materials provided with the distribution.
|
13 |
* 3. Neither the name of the author nor the names of any co-contributors
|
14 |
* may be used to endorse or promote products derived from this software
|
15 |
* without specific prior written permission.
|
16 |
*
|
17 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
18 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
21 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
23 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
24 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
26 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
27 |
* SUCH DAMAGE.
|
28 |
*/
|
29 |
|
30 |
/*
|
31 |
* vm.c - virtual memory allocator
|
32 |
*/
|
33 |
|
34 |
/*
|
35 |
* A task owns its private virtual address space. All threads in
|
36 |
* a task share one same memory space.
|
37 |
* When new task is made, the address mapping of the parent task
|
38 |
* is copied to child task's. In this time, the read-only space
|
39 |
* is shared with old map.
|
40 |
*
|
41 |
* Since this kernel does not do page out to the physical storage,
|
42 |
* it is guaranteed that the allocated memory is always continuing
|
43 |
* and existing. Thereby, a kernel and drivers can be constructed
|
44 |
* very simply.
|
45 |
*/
|
46 |
|
47 |
#include <kernel.h> |
48 |
#include <kmem.h> |
49 |
#include <thread.h> |
50 |
#include <page.h> |
51 |
#include <task.h> |
52 |
#include <sched.h> |
53 |
#include <hal.h> |
54 |
#include <vm.h> |
55 |
|
56 |
/* forward declarations */
|
57 |
static void seg_init(struct seg *); |
58 |
static struct seg *seg_create(struct seg *, vaddr_t, size_t); |
59 |
static void seg_delete(struct seg *, struct seg *); |
60 |
static struct seg *seg_lookup(struct seg *, vaddr_t, size_t); |
61 |
static struct seg *seg_alloc(struct seg *, size_t); |
62 |
static void seg_free(struct seg *, struct seg *); |
63 |
static struct seg *seg_reserve(struct seg *, vaddr_t, size_t); |
64 |
static int do_allocate(vm_map_t, void **, size_t, int); |
65 |
static int do_free(vm_map_t, void *); |
66 |
static int do_attribute(vm_map_t, void *, int); |
67 |
static int do_map(vm_map_t, void *, size_t, void **); |
68 |
static vm_map_t do_dup(vm_map_t);
|
69 |
|
70 |
|
71 |
static struct vm_map kernel_map; /* vm mapping for kernel */ |
72 |
|
73 |
/**
|
74 |
* vm_allocate - allocate zero-filled memory for specified address
|
75 |
*
|
76 |
* If "anywhere" argument is true, the "addr" argument will be
|
77 |
* ignored. In this case, the address of free space will be
|
78 |
* found automatically.
|
79 |
*
|
80 |
* The allocated area has writable, user-access attribute by
|
81 |
* default. The "addr" and "size" argument will be adjusted
|
82 |
* to page boundary.
|
83 |
*/
|
84 |
int
|
85 |
vm_allocate(task_t task, void **addr, size_t size, int anywhere) |
86 |
{ |
87 |
int error;
|
88 |
void *uaddr;
|
89 |
|
90 |
sched_lock(); |
91 |
|
92 |
if (!task_valid(task)) {
|
93 |
sched_unlock(); |
94 |
return ESRCH;
|
95 |
} |
96 |
if (task != curtask && !task_capable(CAP_EXTMEM)) {
|
97 |
sched_unlock(); |
98 |
return EPERM;
|
99 |
} |
100 |
if (copyin(addr, &uaddr, sizeof(uaddr))) { |
101 |
sched_unlock(); |
102 |
return EFAULT;
|
103 |
} |
104 |
if (anywhere == 0 && !user_area(*addr)) { |
105 |
sched_unlock(); |
106 |
return EACCES;
|
107 |
} |
108 |
|
109 |
error = do_allocate(task->map, &uaddr, size, anywhere); |
110 |
if (!error) {
|
111 |
if (copyout(&uaddr, addr, sizeof(uaddr))) |
112 |
error = EFAULT; |
113 |
} |
114 |
sched_unlock(); |
115 |
return error;
|
116 |
} |
117 |
|
118 |
static int |
119 |
do_allocate(vm_map_t map, void **addr, size_t size, int anywhere) |
120 |
{ |
121 |
struct seg *seg;
|
122 |
vaddr_t start, end; |
123 |
paddr_t pa; |
124 |
|
125 |
if (size == 0) |
126 |
return EINVAL;
|
127 |
if (map->total + size >= MAXMEM)
|
128 |
return ENOMEM;
|
129 |
|
130 |
/*
|
131 |
* Allocate segment
|
132 |
*/
|
133 |
if (anywhere) {
|
134 |
size = round_page(size); |
135 |
if ((seg = seg_alloc(&map->head, size)) == NULL) |
136 |
return ENOMEM;
|
137 |
} else {
|
138 |
start = trunc_page((vaddr_t)*addr); |
139 |
end = round_page(start + size); |
140 |
size = (size_t)(end - start); |
141 |
|
142 |
if ((seg = seg_reserve(&map->head, start, size)) == NULL) |
143 |
return ENOMEM;
|
144 |
} |
145 |
seg->flags = SEG_READ | SEG_WRITE; |
146 |
|
147 |
/*
|
148 |
* Allocate physical pages, and map them into virtual address
|
149 |
*/
|
150 |
if ((pa = page_alloc(size)) == 0) |
151 |
goto err1;
|
152 |
|
153 |
if (mmu_map(map->pgd, pa, seg->addr, size, PG_WRITE))
|
154 |
goto err2;
|
155 |
|
156 |
seg->phys = pa; |
157 |
|
158 |
/* Zero fill */
|
159 |
memset(ptokv(pa), 0, seg->size);
|
160 |
*addr = (void *)seg->addr;
|
161 |
map->total += size; |
162 |
return 0; |
163 |
|
164 |
err2:
|
165 |
page_free(pa, size); |
166 |
err1:
|
167 |
seg_free(&map->head, seg); |
168 |
return ENOMEM;
|
169 |
} |
170 |
|
171 |
/*
|
172 |
* Deallocate memory segment for specified address.
|
173 |
*
|
174 |
* The "addr" argument points to a memory segment previously
|
175 |
* allocated through a call to vm_allocate() or vm_map(). The
|
176 |
* number of bytes freed is the number of bytes of the
|
177 |
* allocated segment. If one of the segment of previous and next
|
178 |
* are free, it combines with them, and larger free segment is
|
179 |
* created.
|
180 |
*/
|
181 |
int
|
182 |
vm_free(task_t task, void *addr)
|
183 |
{ |
184 |
int error;
|
185 |
|
186 |
sched_lock(); |
187 |
if (!task_valid(task)) {
|
188 |
sched_unlock(); |
189 |
return ESRCH;
|
190 |
} |
191 |
if (task != curtask && !task_capable(CAP_EXTMEM)) {
|
192 |
sched_unlock(); |
193 |
return EPERM;
|
194 |
} |
195 |
if (!user_area(addr)) {
|
196 |
sched_unlock(); |
197 |
return EFAULT;
|
198 |
} |
199 |
|
200 |
error = do_free(task->map, addr); |
201 |
|
202 |
sched_unlock(); |
203 |
return error;
|
204 |
} |
205 |
|
206 |
static int |
207 |
do_free(vm_map_t map, void *addr)
|
208 |
{ |
209 |
struct seg *seg;
|
210 |
vaddr_t va; |
211 |
|
212 |
va = trunc_page((vaddr_t)addr); |
213 |
|
214 |
/*
|
215 |
* Find the target segment.
|
216 |
*/
|
217 |
seg = seg_lookup(&map->head, va, 1);
|
218 |
if (seg == NULL || seg->addr != va || (seg->flags & SEG_FREE)) |
219 |
return EINVAL;
|
220 |
|
221 |
/*
|
222 |
* Unmap pages of the segment.
|
223 |
*/
|
224 |
mmu_map(map->pgd, seg->phys, seg->addr, seg->size, PG_UNMAP); |
225 |
|
226 |
/*
|
227 |
* Relinquish use of the page if it is not shared and mapped.
|
228 |
*/
|
229 |
if (!(seg->flags & SEG_SHARED) && !(seg->flags & SEG_MAPPED))
|
230 |
page_free(seg->phys, seg->size); |
231 |
|
232 |
map->total -= seg->size; |
233 |
seg_free(&map->head, seg); |
234 |
|
235 |
return 0; |
236 |
} |
237 |
|
238 |
/*
|
239 |
* Change attribute of specified virtual address.
|
240 |
*
|
241 |
* The "addr" argument points to a memory segment previously
|
242 |
* allocated through a call to vm_allocate(). The attribute
|
243 |
* type can be chosen a combination of PROT_READ, PROT_WRITE.
|
244 |
* Note: PROT_EXEC is not supported, yet.
|
245 |
*/
|
246 |
int
|
247 |
vm_attribute(task_t task, void *addr, int attr) |
248 |
{ |
249 |
int error;
|
250 |
|
251 |
sched_lock(); |
252 |
if (attr == 0 || attr & ~(PROT_READ | PROT_WRITE)) { |
253 |
sched_unlock(); |
254 |
return EINVAL;
|
255 |
} |
256 |
if (!task_valid(task)) {
|
257 |
sched_unlock(); |
258 |
return ESRCH;
|
259 |
} |
260 |
if (task != curtask && !task_capable(CAP_EXTMEM)) {
|
261 |
sched_unlock(); |
262 |
return EPERM;
|
263 |
} |
264 |
if (!user_area(addr)) {
|
265 |
sched_unlock(); |
266 |
return EFAULT;
|
267 |
} |
268 |
|
269 |
error = do_attribute(task->map, addr, attr); |
270 |
|
271 |
sched_unlock(); |
272 |
return error;
|
273 |
} |
274 |
|
275 |
static int |
276 |
do_attribute(vm_map_t map, void *addr, int attr) |
277 |
{ |
278 |
struct seg *seg;
|
279 |
int new_flags, map_type;
|
280 |
paddr_t old_pa, new_pa; |
281 |
vaddr_t va; |
282 |
|
283 |
va = trunc_page((vaddr_t)addr); |
284 |
|
285 |
/*
|
286 |
* Find the target segment.
|
287 |
*/
|
288 |
seg = seg_lookup(&map->head, va, 1);
|
289 |
if (seg == NULL || seg->addr != va || (seg->flags & SEG_FREE)) { |
290 |
return EINVAL; /* not allocated */ |
291 |
} |
292 |
/*
|
293 |
* The attribute of the mapped segment can not be changed.
|
294 |
*/
|
295 |
if (seg->flags & SEG_MAPPED)
|
296 |
return EINVAL;
|
297 |
|
298 |
/*
|
299 |
* Check new and old flag.
|
300 |
*/
|
301 |
new_flags = 0;
|
302 |
if (seg->flags & SEG_WRITE) {
|
303 |
if (!(attr & PROT_WRITE))
|
304 |
new_flags = SEG_READ; |
305 |
} else {
|
306 |
if (attr & PROT_WRITE)
|
307 |
new_flags = SEG_READ | SEG_WRITE; |
308 |
} |
309 |
if (new_flags == 0) |
310 |
return 0; /* same attribute */ |
311 |
|
312 |
map_type = (new_flags & SEG_WRITE) ? PG_WRITE : PG_READ; |
313 |
|
314 |
/*
|
315 |
* If it is shared segment, duplicate it.
|
316 |
*/
|
317 |
if (seg->flags & SEG_SHARED) {
|
318 |
|
319 |
old_pa = seg->phys; |
320 |
|
321 |
/* Allocate new physical page. */
|
322 |
if ((new_pa = page_alloc(seg->size)) == 0) |
323 |
return ENOMEM;
|
324 |
|
325 |
/* Copy source page */
|
326 |
memcpy(ptokv(new_pa), ptokv(old_pa), seg->size); |
327 |
|
328 |
/* Map new segment */
|
329 |
if (mmu_map(map->pgd, new_pa, seg->addr, seg->size,
|
330 |
map_type)) { |
331 |
page_free(new_pa, seg->size); |
332 |
return ENOMEM;
|
333 |
} |
334 |
seg->phys = new_pa; |
335 |
|
336 |
/* Unlink from shared list */
|
337 |
seg->sh_prev->sh_next = seg->sh_next; |
338 |
seg->sh_next->sh_prev = seg->sh_prev; |
339 |
if (seg->sh_prev == seg->sh_next)
|
340 |
seg->sh_prev->flags &= ~SEG_SHARED; |
341 |
seg->sh_next = seg->sh_prev = seg; |
342 |
} else {
|
343 |
if (mmu_map(map->pgd, seg->phys, seg->addr, seg->size,
|
344 |
map_type)) |
345 |
return ENOMEM;
|
346 |
} |
347 |
seg->flags = new_flags; |
348 |
return 0; |
349 |
} |
350 |
|
351 |
/**
|
352 |
* vm_map - map another task's memory to current task.
|
353 |
*
|
354 |
* Note: This routine does not support mapping to the specific address.
|
355 |
*/
|
356 |
int
|
357 |
vm_map(task_t target, void *addr, size_t size, void **alloc) |
358 |
{ |
359 |
int error;
|
360 |
|
361 |
sched_lock(); |
362 |
if (!task_valid(target)) {
|
363 |
sched_unlock(); |
364 |
return ESRCH;
|
365 |
} |
366 |
if (target == curtask) {
|
367 |
sched_unlock(); |
368 |
return EINVAL;
|
369 |
} |
370 |
if (!task_capable(CAP_EXTMEM)) {
|
371 |
sched_unlock(); |
372 |
return EPERM;
|
373 |
} |
374 |
if (!user_area(addr)) {
|
375 |
sched_unlock(); |
376 |
return EFAULT;
|
377 |
} |
378 |
|
379 |
error = do_map(target->map, addr, size, alloc); |
380 |
|
381 |
sched_unlock(); |
382 |
return error;
|
383 |
} |
384 |
|
385 |
static int |
386 |
do_map(vm_map_t map, void *addr, size_t size, void **alloc) |
387 |
{ |
388 |
struct seg *seg, *cur, *tgt;
|
389 |
vm_map_t curmap; |
390 |
vaddr_t start, end; |
391 |
paddr_t pa; |
392 |
size_t offset; |
393 |
int map_type;
|
394 |
void *tmp;
|
395 |
|
396 |
if (size == 0) |
397 |
return EINVAL;
|
398 |
if (map->total + size >= MAXMEM)
|
399 |
return ENOMEM;
|
400 |
|
401 |
/* check fault */
|
402 |
tmp = NULL;
|
403 |
if (copyout(&tmp, alloc, sizeof(tmp))) |
404 |
return EFAULT;
|
405 |
|
406 |
start = trunc_page((vaddr_t)addr); |
407 |
end = round_page((vaddr_t)addr + size); |
408 |
size = (size_t)(end - start); |
409 |
offset = (size_t)((vaddr_t)addr - start); |
410 |
|
411 |
/*
|
412 |
* Find the segment that includes target address
|
413 |
*/
|
414 |
seg = seg_lookup(&map->head, start, size); |
415 |
if (seg == NULL || (seg->flags & SEG_FREE)) |
416 |
return EINVAL; /* not allocated */ |
417 |
tgt = seg; |
418 |
|
419 |
/*
|
420 |
* Find the free segment in current task
|
421 |
*/
|
422 |
curmap = curtask->map; |
423 |
if ((seg = seg_alloc(&curmap->head, size)) == NULL) |
424 |
return ENOMEM;
|
425 |
cur = seg; |
426 |
|
427 |
/*
|
428 |
* Try to map into current memory
|
429 |
*/
|
430 |
if (tgt->flags & SEG_WRITE)
|
431 |
map_type = PG_WRITE; |
432 |
else
|
433 |
map_type = PG_READ; |
434 |
|
435 |
pa = tgt->phys + (paddr_t)(start - tgt->addr); |
436 |
if (mmu_map(curmap->pgd, pa, cur->addr, size, map_type)) {
|
437 |
seg_free(&curmap->head, seg); |
438 |
return ENOMEM;
|
439 |
} |
440 |
|
441 |
cur->flags = tgt->flags | SEG_MAPPED; |
442 |
cur->phys = pa; |
443 |
|
444 |
tmp = (void *)(cur->addr + offset);
|
445 |
copyout(&tmp, alloc, sizeof(tmp));
|
446 |
|
447 |
curmap->total += size; |
448 |
return 0; |
449 |
} |
450 |
|
451 |
/*
|
452 |
* Create new virtual memory space.
|
453 |
* No memory is inherited.
|
454 |
*
|
455 |
* Must be called with scheduler locked.
|
456 |
*/
|
457 |
vm_map_t |
458 |
vm_create(void)
|
459 |
{ |
460 |
struct vm_map *map;
|
461 |
|
462 |
/* Allocate new map structure */
|
463 |
if ((map = kmem_alloc(sizeof(*map))) == NULL) |
464 |
return NULL; |
465 |
|
466 |
map->refcnt = 1;
|
467 |
map->total = 0;
|
468 |
|
469 |
/* Allocate new page directory */
|
470 |
if ((map->pgd = mmu_newmap()) == NO_PGD) {
|
471 |
kmem_free(map); |
472 |
return NULL; |
473 |
} |
474 |
seg_init(&map->head); |
475 |
return map;
|
476 |
} |
477 |
|
478 |
/*
|
479 |
* Terminate specified virtual memory space.
|
480 |
* This is called when task is terminated.
|
481 |
*/
|
482 |
void
|
483 |
vm_terminate(vm_map_t map) |
484 |
{ |
485 |
struct seg *seg, *tmp;
|
486 |
|
487 |
if (--map->refcnt > 0) |
488 |
return;
|
489 |
|
490 |
sched_lock(); |
491 |
seg = &map->head; |
492 |
do {
|
493 |
if (seg->flags != SEG_FREE) {
|
494 |
/* Unmap segment */
|
495 |
mmu_map(map->pgd, seg->phys, seg->addr, |
496 |
seg->size, PG_UNMAP); |
497 |
|
498 |
/* Free segment if it is not shared and mapped */
|
499 |
if (!(seg->flags & SEG_SHARED) &&
|
500 |
!(seg->flags & SEG_MAPPED)) { |
501 |
page_free(seg->phys, seg->size); |
502 |
} |
503 |
} |
504 |
tmp = seg; |
505 |
seg = seg->next; |
506 |
seg_delete(&map->head, tmp); |
507 |
} while (seg != &map->head);
|
508 |
|
509 |
if (map == curtask->map) {
|
510 |
/*
|
511 |
* Switch to the kernel page directory before
|
512 |
* deleting current page directory.
|
513 |
*/
|
514 |
mmu_switch(kernel_map.pgd); |
515 |
} |
516 |
|
517 |
mmu_terminate(map->pgd); |
518 |
kmem_free(map); |
519 |
sched_unlock(); |
520 |
} |
521 |
|
522 |
/*
|
523 |
* Duplicate specified virtual memory space.
|
524 |
* This is called when new task is created.
|
525 |
*
|
526 |
* Returns new map id, NULL if it fails.
|
527 |
*
|
528 |
* All segments of original memory map are copied to new memory map.
|
529 |
* If the segment is read-only, executable, or shared segment, it is
|
530 |
* no need to copy. These segments are physically shared with the
|
531 |
* original map.
|
532 |
*/
|
533 |
vm_map_t |
534 |
vm_dup(vm_map_t org_map) |
535 |
{ |
536 |
vm_map_t new_map; |
537 |
|
538 |
sched_lock(); |
539 |
new_map = do_dup(org_map); |
540 |
sched_unlock(); |
541 |
return new_map;
|
542 |
} |
543 |
|
544 |
static vm_map_t
|
545 |
do_dup(vm_map_t org_map) |
546 |
{ |
547 |
vm_map_t new_map; |
548 |
struct seg *tmp, *src, *dest;
|
549 |
int map_type;
|
550 |
|
551 |
if ((new_map = vm_create()) == NULL) |
552 |
return NULL; |
553 |
|
554 |
new_map->total = org_map->total; |
555 |
/*
|
556 |
* Copy all segments
|
557 |
*/
|
558 |
tmp = &new_map->head; |
559 |
src = &org_map->head; |
560 |
|
561 |
/*
|
562 |
* Copy top segment
|
563 |
*/
|
564 |
*tmp = *src; |
565 |
tmp->next = tmp->prev = tmp; |
566 |
|
567 |
if (src == src->next) /* Blank memory ? */ |
568 |
return new_map;
|
569 |
|
570 |
do {
|
571 |
ASSERT(src != NULL);
|
572 |
ASSERT(src->next != NULL);
|
573 |
|
574 |
if (src == &org_map->head) {
|
575 |
dest = tmp; |
576 |
} else {
|
577 |
/* Create new segment struct */
|
578 |
dest = kmem_alloc(sizeof(*dest));
|
579 |
if (dest == NULL) |
580 |
return NULL; |
581 |
|
582 |
*dest = *src; /* memcpy */
|
583 |
|
584 |
dest->prev = tmp; |
585 |
dest->next = tmp->next; |
586 |
tmp->next->prev = dest; |
587 |
tmp->next = dest; |
588 |
tmp = dest; |
589 |
} |
590 |
if (src->flags == SEG_FREE) {
|
591 |
/*
|
592 |
* Skip free segment
|
593 |
*/
|
594 |
} else {
|
595 |
/* Check if the segment can be shared */
|
596 |
if (!(src->flags & SEG_WRITE) &&
|
597 |
!(src->flags & SEG_MAPPED)) { |
598 |
dest->flags |= SEG_SHARED; |
599 |
} |
600 |
|
601 |
if (!(dest->flags & SEG_SHARED)) {
|
602 |
/* Allocate new physical page. */
|
603 |
dest->phys = page_alloc(src->size); |
604 |
if (dest->phys == 0) |
605 |
return NULL; |
606 |
|
607 |
/* Copy source page */
|
608 |
memcpy(ptokv(dest->phys), ptokv(src->phys), |
609 |
src->size); |
610 |
} |
611 |
/* Map the segment to virtual address */
|
612 |
if (dest->flags & SEG_WRITE)
|
613 |
map_type = PG_WRITE; |
614 |
else
|
615 |
map_type = PG_READ; |
616 |
|
617 |
if (mmu_map(new_map->pgd, dest->phys, dest->addr,
|
618 |
dest->size, map_type)) |
619 |
return NULL; |
620 |
} |
621 |
src = src->next; |
622 |
} while (src != &org_map->head);
|
623 |
|
624 |
/*
|
625 |
* No error. Now, link all shared segments
|
626 |
*/
|
627 |
dest = &new_map->head; |
628 |
src = &org_map->head; |
629 |
do {
|
630 |
if (dest->flags & SEG_SHARED) {
|
631 |
src->flags |= SEG_SHARED; |
632 |
dest->sh_prev = src; |
633 |
dest->sh_next = src->sh_next; |
634 |
src->sh_next->sh_prev = dest; |
635 |
src->sh_next = dest; |
636 |
} |
637 |
dest = dest->next; |
638 |
src = src->next; |
639 |
} while (src != &org_map->head);
|
640 |
return new_map;
|
641 |
} |
642 |
|
643 |
/*
|
644 |
* Switch VM mapping.
|
645 |
*
|
646 |
* Since a kernel task does not have user mode memory image, we
|
647 |
* don't have to setup the page directory for it. Thus, an idle
|
648 |
* thread and interrupt threads can be switched quickly.
|
649 |
*/
|
650 |
void
|
651 |
vm_switch(vm_map_t map) |
652 |
{ |
653 |
|
654 |
if (map != &kernel_map)
|
655 |
mmu_switch(map->pgd); |
656 |
} |
657 |
|
658 |
/*
|
659 |
* Increment reference count of VM mapping.
|
660 |
*/
|
661 |
int
|
662 |
vm_reference(vm_map_t map) |
663 |
{ |
664 |
|
665 |
map->refcnt++; |
666 |
return 0; |
667 |
} |
668 |
|
669 |
/*
|
670 |
* Load task image for boot task.
|
671 |
* Return 0 on success, or errno on failure.
|
672 |
*/
|
673 |
int
|
674 |
vm_load(vm_map_t map, struct module *mod, void **stack) |
675 |
{ |
676 |
char *src;
|
677 |
void *text, *data;
|
678 |
int error;
|
679 |
|
680 |
DPRINTF(("Loading task: %s\n", mod->name));
|
681 |
|
682 |
/*
|
683 |
* We have to switch VM mapping to touch the virtual
|
684 |
* memory space of a target task without page fault.
|
685 |
*/
|
686 |
vm_switch(map); |
687 |
|
688 |
src = ptokv(mod->phys); |
689 |
text = (void *)mod->text;
|
690 |
data = (void *)mod->data;
|
691 |
|
692 |
/*
|
693 |
* Create text segment
|
694 |
*/
|
695 |
error = do_allocate(map, &text, mod->textsz, 0);
|
696 |
if (error)
|
697 |
return error;
|
698 |
memcpy(text, src, mod->textsz); |
699 |
error = do_attribute(map, text, PROT_READ); |
700 |
if (error)
|
701 |
return error;
|
702 |
|
703 |
/*
|
704 |
* Create data & BSS segment
|
705 |
*/
|
706 |
if (mod->datasz + mod->bsssz != 0) { |
707 |
error = do_allocate(map, &data, mod->datasz + mod->bsssz, 0);
|
708 |
if (error)
|
709 |
return error;
|
710 |
if (mod->datasz > 0) { |
711 |
src = src + (mod->data - mod->text); |
712 |
memcpy(data, src, mod->datasz); |
713 |
} |
714 |
} |
715 |
/*
|
716 |
* Create stack
|
717 |
*/
|
718 |
*stack = (void *)USRSTACK;
|
719 |
error = do_allocate(map, stack, DFLSTKSZ, 0);
|
720 |
if (error)
|
721 |
return error;
|
722 |
|
723 |
/* Free original pages */
|
724 |
page_free(mod->phys, mod->size); |
725 |
return 0; |
726 |
} |
727 |
|
728 |
/*
|
729 |
* Translate virtual address of current task to physical address.
|
730 |
* Returns physical address on success, or NULL if no mapped memory.
|
731 |
*/
|
732 |
paddr_t |
733 |
vm_translate(vaddr_t addr, size_t size) |
734 |
{ |
735 |
|
736 |
return mmu_extract(curtask->map->pgd, addr, size);
|
737 |
} |
738 |
|
739 |
int
|
740 |
vm_info(struct vminfo *info)
|
741 |
{ |
742 |
u_long target = info->cookie; |
743 |
task_t task = info->task; |
744 |
u_long i; |
745 |
vm_map_t map; |
746 |
struct seg *seg;
|
747 |
|
748 |
sched_lock(); |
749 |
if (!task_valid(task)) {
|
750 |
sched_unlock(); |
751 |
return ESRCH;
|
752 |
} |
753 |
map = task->map; |
754 |
seg = &map->head; |
755 |
i = 0;
|
756 |
do {
|
757 |
if (i++ == target) {
|
758 |
info->cookie = i; |
759 |
info->virt = seg->addr; |
760 |
info->size = seg->size; |
761 |
info->flags = seg->flags; |
762 |
info->phys = seg->phys; |
763 |
sched_unlock(); |
764 |
return 0; |
765 |
} |
766 |
seg = seg->next; |
767 |
} while (seg != &map->head);
|
768 |
sched_unlock(); |
769 |
return ESRCH;
|
770 |
} |
771 |
|
772 |
void
|
773 |
vm_init(void)
|
774 |
{ |
775 |
pgd_t pgd; |
776 |
|
777 |
/*
|
778 |
* Setup vm mapping for kernel task.
|
779 |
*/
|
780 |
if ((pgd = mmu_newmap()) == NO_PGD)
|
781 |
panic("vm_init");
|
782 |
kernel_map.pgd = pgd; |
783 |
mmu_switch(pgd); |
784 |
|
785 |
seg_init(&kernel_map.head); |
786 |
kernel_task.map = &kernel_map; |
787 |
} |
788 |
|
789 |
|
790 |
/*
|
791 |
* Initialize segment.
|
792 |
*/
|
793 |
static void |
794 |
seg_init(struct seg *seg)
|
795 |
{ |
796 |
|
797 |
seg->next = seg->prev = seg; |
798 |
seg->sh_next = seg->sh_prev = seg; |
799 |
seg->addr = PAGE_SIZE; |
800 |
seg->phys = 0;
|
801 |
seg->size = USERLIMIT - PAGE_SIZE; |
802 |
seg->flags = SEG_FREE; |
803 |
} |
804 |
|
805 |
/*
|
806 |
* Create new free segment after the specified segment.
|
807 |
* Returns segment on success, or NULL on failure.
|
808 |
*/
|
809 |
static struct seg * |
810 |
seg_create(struct seg *prev, vaddr_t addr, size_t size)
|
811 |
{ |
812 |
struct seg *seg;
|
813 |
|
814 |
if ((seg = kmem_alloc(sizeof(*seg))) == NULL) |
815 |
return NULL; |
816 |
|
817 |
seg->addr = addr; |
818 |
seg->size = size; |
819 |
seg->phys = 0;
|
820 |
seg->flags = SEG_FREE; |
821 |
seg->sh_next = seg->sh_prev = seg; |
822 |
|
823 |
seg->next = prev->next; |
824 |
seg->prev = prev; |
825 |
prev->next->prev = seg; |
826 |
prev->next = seg; |
827 |
|
828 |
return seg;
|
829 |
} |
830 |
|
831 |
/*
|
832 |
* Delete specified segment.
|
833 |
*/
|
834 |
static void |
835 |
seg_delete(struct seg *head, struct seg *seg) |
836 |
{ |
837 |
|
838 |
/*
|
839 |
* If it is shared segment, unlink from shared list.
|
840 |
*/
|
841 |
if (seg->flags & SEG_SHARED) {
|
842 |
seg->sh_prev->sh_next = seg->sh_next; |
843 |
seg->sh_next->sh_prev = seg->sh_prev; |
844 |
if (seg->sh_prev == seg->sh_next)
|
845 |
seg->sh_prev->flags &= ~SEG_SHARED; |
846 |
} |
847 |
if (head != seg)
|
848 |
kmem_free(seg); |
849 |
} |
850 |
|
851 |
/*
|
852 |
* Find the segment at the specified address.
|
853 |
*/
|
854 |
static struct seg * |
855 |
seg_lookup(struct seg *head, vaddr_t addr, size_t size)
|
856 |
{ |
857 |
struct seg *seg;
|
858 |
|
859 |
seg = head; |
860 |
do {
|
861 |
if (seg->addr <= addr &&
|
862 |
seg->addr + seg->size >= addr + size) { |
863 |
return seg;
|
864 |
} |
865 |
seg = seg->next; |
866 |
} while (seg != head);
|
867 |
return NULL; |
868 |
} |
869 |
|
870 |
/*
|
871 |
* Allocate free segment for specified size.
|
872 |
*/
|
873 |
static struct seg * |
874 |
seg_alloc(struct seg *head, size_t size)
|
875 |
{ |
876 |
struct seg *seg;
|
877 |
|
878 |
seg = head; |
879 |
do {
|
880 |
if ((seg->flags & SEG_FREE) && seg->size >= size) {
|
881 |
if (seg->size != size) {
|
882 |
/*
|
883 |
* Split this segment and return its head.
|
884 |
*/
|
885 |
if (seg_create(seg,
|
886 |
seg->addr + size, |
887 |
seg->size - size) == NULL)
|
888 |
return NULL; |
889 |
} |
890 |
seg->size = size; |
891 |
return seg;
|
892 |
} |
893 |
seg = seg->next; |
894 |
} while (seg != head);
|
895 |
return NULL; |
896 |
} |
897 |
|
898 |
/*
|
899 |
* Delete specified free segment.
|
900 |
*/
|
901 |
static void |
902 |
seg_free(struct seg *head, struct seg *seg) |
903 |
{ |
904 |
struct seg *prev, *next;
|
905 |
|
906 |
ASSERT(seg->flags != SEG_FREE); |
907 |
|
908 |
seg->flags = SEG_FREE; |
909 |
|
910 |
/*
|
911 |
* If it is shared segment, unlink from shared list.
|
912 |
*/
|
913 |
if (seg->flags & SEG_SHARED) {
|
914 |
seg->sh_prev->sh_next = seg->sh_next; |
915 |
seg->sh_next->sh_prev = seg->sh_prev; |
916 |
if (seg->sh_prev == seg->sh_next)
|
917 |
seg->sh_prev->flags &= ~SEG_SHARED; |
918 |
} |
919 |
/*
|
920 |
* If next segment is free, merge with it.
|
921 |
*/
|
922 |
next = seg->next; |
923 |
if (next != head && (next->flags & SEG_FREE)) {
|
924 |
seg->next = next->next; |
925 |
next->next->prev = seg; |
926 |
seg->size += next->size; |
927 |
kmem_free(next); |
928 |
} |
929 |
/*
|
930 |
* If previous segment is free, merge with it.
|
931 |
*/
|
932 |
prev = seg->prev; |
933 |
if (seg != head && (prev->flags & SEG_FREE)) {
|
934 |
prev->next = seg->next; |
935 |
seg->next->prev = prev; |
936 |
prev->size += seg->size; |
937 |
kmem_free(seg); |
938 |
} |
939 |
} |
940 |
|
941 |
/*
|
942 |
* Reserve the segment at the specified address/size.
|
943 |
*/
|
944 |
static struct seg * |
945 |
seg_reserve(struct seg *head, vaddr_t addr, size_t size)
|
946 |
{ |
947 |
struct seg *seg, *prev, *next;
|
948 |
size_t diff; |
949 |
|
950 |
/*
|
951 |
* Find the block which includes specified block.
|
952 |
*/
|
953 |
seg = seg_lookup(head, addr, size); |
954 |
if (seg == NULL || !(seg->flags & SEG_FREE)) |
955 |
return NULL; |
956 |
|
957 |
/*
|
958 |
* Check previous segment to split segment.
|
959 |
*/
|
960 |
prev = NULL;
|
961 |
if (seg->addr != addr) {
|
962 |
prev = seg; |
963 |
diff = (size_t)(addr - seg->addr); |
964 |
seg = seg_create(prev, addr, prev->size - diff); |
965 |
if (seg == NULL) |
966 |
return NULL; |
967 |
prev->size = diff; |
968 |
} |
969 |
/*
|
970 |
* Check next segment to split segment.
|
971 |
*/
|
972 |
if (seg->size != size) {
|
973 |
next = seg_create(seg, seg->addr + size, seg->size - size); |
974 |
if (next == NULL) { |
975 |
if (prev) {
|
976 |
/* Undo previous seg_create() operation */
|
977 |
seg_free(head, seg); |
978 |
} |
979 |
return NULL; |
980 |
} |
981 |
seg->size = size; |
982 |
} |
983 |
seg->flags = 0;
|
984 |
return seg;
|
985 |
} |