source: mainline/kernel/generic/src/mm/frame.c@ 0f6a3376

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export 0.2.0.4
Last change on this file since 0f6a3376 was d630139, checked in by Jakub Jermar <jakub@…>, 18 years ago

Add arm32 architecture. The 32 suffix is used to specify that 16-bit Thumb
instructions are not used. The arm32 code is mostly composed of placeholders
that need to be replaced by real implementation. So far, the arm32 tree
only compiles. If run under GXEmul simulator, an infinit loop at the
kernel entry point will be entered.

  • Property mode set to 100644
File size: 29.1 KB
Line 
1/*
2 * Copyright (c) 2001-2005 Jakub Jermar
3 * Copyright (c) 2005 Sergey Bondari
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup genericmm
31 * @{
32 */
33
34/**
35 * @file
36 * @brief Physical frame allocator.
37 *
38 * This file contains the physical frame allocator and memory zone management.
39 * The frame allocator is built on top of the buddy allocator.
40 *
41 * @see buddy.c
42 */
43
44/*
45 * Locking order
46 *
47 * In order to access particular zone, the process must first lock
48 * the zones.lock, then lock the zone and then unlock the zones.lock.
49 * This insures, that we can fiddle with the zones in runtime without
50 * affecting the processes.
51 *
52 */
53
54#include <arch/types.h>
55#include <mm/frame.h>
56#include <mm/as.h>
57#include <panic.h>
58#include <debug.h>
59#include <adt/list.h>
60#include <synch/spinlock.h>
61#include <arch/asm.h>
62#include <arch.h>
63#include <print.h>
64#include <align.h>
65#include <mm/slab.h>
66#include <bitops.h>
67#include <macros.h>
68#include <config.h>
69
70typedef struct {
71 count_t refcount; /**< tracking of shared frames */
72 uint8_t buddy_order; /**< buddy system block order */
73 link_t buddy_link; /**< link to the next free block inside one order */
74 void *parent; /**< If allocated by slab, this points there */
75} frame_t;
76
77typedef struct {
78 SPINLOCK_DECLARE(lock); /**< this lock protects everything below */
79 pfn_t base; /**< frame_no of the first frame in the frames array */
80 count_t count; /**< Size of zone */
81
82 frame_t *frames; /**< array of frame_t structures in this zone */
83 count_t free_count; /**< number of free frame_t structures */
84 count_t busy_count; /**< number of busy frame_t structures */
85
86 buddy_system_t *buddy_system; /**< buddy system for the zone */
87 int flags;
88} zone_t;
89
90/*
91 * The zoneinfo.lock must be locked when accessing zoneinfo structure.
92 * Some of the attributes in zone_t structures are 'read-only'
93 */
94
95typedef struct {
96 SPINLOCK_DECLARE(lock);
97 unsigned int count;
98 zone_t *info[ZONES_MAX];
99} zones_t;
100
101static zones_t zones;
102
103
104/*********************************/
105/* Helper functions */
106static inline index_t frame_index(zone_t *zone, frame_t *frame)
107{
108 return (index_t)(frame - zone->frames);
109}
110static inline index_t frame_index_abs(zone_t *zone, frame_t *frame)
111{
112 return (index_t)(frame - zone->frames) + zone->base;
113}
114static inline int frame_index_valid(zone_t *zone, index_t index)
115{
116 return index >= 0 && index < zone->count;
117}
118
119/** Compute pfn_t from frame_t pointer & zone pointer */
120static index_t make_frame_index(zone_t *zone, frame_t *frame)
121{
122 return frame - zone->frames;
123}
124
125/** Initialize frame structure
126 *
127 * Initialize frame structure.
128 *
129 * @param frame Frame structure to be initialized.
130 */
131static void frame_initialize(frame_t *frame)
132{
133 frame->refcount = 1;
134 frame->buddy_order = 0;
135}
136
137/*************************************/
138/* Zoneinfo functions */
139
140/**
141 * Insert-sort zone into zones list
142 *
143 * @param newzone New zone to be inserted into zone list
144 * @return zone number on success, -1 on error
145 */
146static int zones_add_zone(zone_t *newzone)
147{
148 unsigned int i, j;
149 ipl_t ipl;
150 zone_t *z;
151
152 ipl = interrupts_disable();
153 spinlock_lock(&zones.lock);
154 /* Try to merge */
155 if (zones.count + 1 == ZONES_MAX)
156 panic("Maximum zone(%d) count exceeded.", ZONES_MAX);
157 for (i = 0; i < zones.count; i++) {
158 /* Check for overflow */
159 z = zones.info[i];
160 if (overlaps(newzone->base,newzone->count,
161 z->base, z->count)) {
162 printf("Zones overlap!\n");
163 return -1;
164 }
165 if (newzone->base < z->base)
166 break;
167 }
168 /* Move other zones up */
169 for (j = i;j < zones.count; j++)
170 zones.info[j + 1] = zones.info[j];
171 zones.info[i] = newzone;
172 zones.count++;
173 spinlock_unlock(&zones.lock);
174 interrupts_restore(ipl);
175
176 return i;
177}
178
179/**
180 * Try to find a zone where can we find the frame
181
182 * Assume interrupts are disabled.
183
184 * @param frame Frame number contained in zone
185 * @param pzone If not null, it is used as zone hint. Zone index
186 * is filled into the variable on success.
187 * @return Pointer to locked zone containing frame
188 */
189static zone_t * find_zone_and_lock(pfn_t frame, unsigned int *pzone)
190{
191 unsigned int i;
192 unsigned int hint = pzone ? *pzone : 0;
193 zone_t *z;
194
195 spinlock_lock(&zones.lock);
196
197 if (hint >= zones.count || hint < 0)
198 hint = 0;
199
200 i = hint;
201 do {
202 z = zones.info[i];
203 spinlock_lock(&z->lock);
204 if (z->base <= frame && z->base + z->count > frame) {
205 spinlock_unlock(&zones.lock); /* Unlock the global lock */
206 if (pzone)
207 *pzone = i;
208 return z;
209 }
210 spinlock_unlock(&z->lock);
211
212 i++;
213 if (i >= zones.count)
214 i = 0;
215 } while(i != hint);
216
217 spinlock_unlock(&zones.lock);
218 return NULL;
219}
220
221/** @return True if zone can allocate specified order */
222static int zone_can_alloc(zone_t *z, uint8_t order)
223{
224 return buddy_system_can_alloc(z->buddy_system, order);
225}
226
227/** Find and lock zone that can allocate order frames.
228 *
229 * Assume interrupts are disabled.
230 *
231 * @param order Size (2^order) of free space we are trying to find
232 * @param pzone Pointer to preferred zone or NULL, on return contains zone number
233 */
234static zone_t * find_free_zone_and_lock(uint8_t order, unsigned int *pzone)
235{
236 unsigned int i;
237 zone_t *z;
238 unsigned int hint = pzone ? *pzone : 0;
239
240 spinlock_lock(&zones.lock);
241 if (hint >= zones.count)
242 hint = 0;
243 i = hint;
244 do {
245 z = zones.info[i];
246
247 spinlock_lock(&z->lock);
248
249 /* Check if the zone has 2^order frames area available */
250 if (zone_can_alloc(z, order)) {
251 spinlock_unlock(&zones.lock);
252 if (pzone)
253 *pzone = i;
254 return z;
255 }
256 spinlock_unlock(&z->lock);
257 if (++i >= zones.count)
258 i = 0;
259 } while(i != hint);
260 spinlock_unlock(&zones.lock);
261 return NULL;
262}
263
264/**************************/
265/* Buddy system functions */
266/**************************/
267
268/** Buddy system find_block implementation
269 *
270 * Find block that is parent of current list.
271 * That means go to lower addresses, until such block is found
272 *
273 * @param order - Order of parent must be different then this parameter!!
274 */
275static link_t *zone_buddy_find_block(buddy_system_t *b, link_t *child,
276 uint8_t order)
277{
278 frame_t * frame;
279 zone_t * zone;
280 index_t index;
281
282 frame = list_get_instance(child, frame_t, buddy_link);
283 zone = (zone_t *) b->data;
284
285 index = frame_index(zone, frame);
286 do {
287 if (zone->frames[index].buddy_order != order) {
288 return &zone->frames[index].buddy_link;
289 }
290 } while(index-- > 0);
291 return NULL;
292}
293
294static void zone_buddy_print_id(buddy_system_t *b, link_t *block)
295{
296 frame_t * frame;
297 zone_t * zone;
298 index_t index;
299
300 frame = list_get_instance(block, frame_t, buddy_link);
301 zone = (zone_t *) b->data;
302 index = frame_index(zone, frame);
303 printf("%zd", index);
304}
305
306/** Buddy system find_buddy implementation
307 *
308 * @param b Buddy system.
309 * @param block Block for which buddy should be found
310 *
311 * @return Buddy for given block if found
312 */
313static link_t * zone_buddy_find_buddy(buddy_system_t *b, link_t * block)
314{
315 frame_t * frame;
316 zone_t * zone;
317 index_t index;
318 bool is_left, is_right;
319
320 frame = list_get_instance(block, frame_t, buddy_link);
321 zone = (zone_t *) b->data;
322 ASSERT(IS_BUDDY_ORDER_OK(frame_index_abs(zone, frame), frame->buddy_order));
323
324 is_left = IS_BUDDY_LEFT_BLOCK_ABS(zone, frame);
325 is_right = IS_BUDDY_RIGHT_BLOCK_ABS(zone, frame);
326
327 ASSERT(is_left ^ is_right);
328 if (is_left) {
329 index = (frame_index(zone, frame)) + (1 << frame->buddy_order);
330 } else { /* if (is_right) */
331 index = (frame_index(zone, frame)) - (1 << frame->buddy_order);
332 }
333
334 if (frame_index_valid(zone, index)) {
335 if (zone->frames[index].buddy_order == frame->buddy_order &&
336 zone->frames[index].refcount == 0) {
337 return &zone->frames[index].buddy_link;
338 }
339 }
340
341 return NULL;
342}
343
344/** Buddy system bisect implementation
345 *
346 * @param b Buddy system.
347 * @param block Block to bisect
348 *
349 * @return right block
350 */
351static link_t * zone_buddy_bisect(buddy_system_t *b, link_t * block) {
352 frame_t * frame_l, * frame_r;
353
354 frame_l = list_get_instance(block, frame_t, buddy_link);
355 frame_r = (frame_l + (1 << (frame_l->buddy_order - 1)));
356
357 return &frame_r->buddy_link;
358}
359
360/** Buddy system coalesce implementation
361 *
362 * @param b Buddy system.
363 * @param block_1 First block
364 * @param block_2 First block's buddy
365 *
366 * @return Coalesced block (actually block that represents lower address)
367 */
368static link_t * zone_buddy_coalesce(buddy_system_t *b, link_t * block_1,
369 link_t * block_2)
370{
371 frame_t *frame1, *frame2;
372
373 frame1 = list_get_instance(block_1, frame_t, buddy_link);
374 frame2 = list_get_instance(block_2, frame_t, buddy_link);
375
376 return frame1 < frame2 ? block_1 : block_2;
377}
378
379/** Buddy system set_order implementation
380 *
381 * @param b Buddy system.
382 * @param block Buddy system block
383 * @param order Order to set
384 */
385static void zone_buddy_set_order(buddy_system_t *b, link_t * block, uint8_t order) {
386 frame_t * frame;
387 frame = list_get_instance(block, frame_t, buddy_link);
388 frame->buddy_order = order;
389}
390
391/** Buddy system get_order implementation
392 *
393 * @param b Buddy system.
394 * @param block Buddy system block
395 *
396 * @return Order of block
397 */
398static uint8_t zone_buddy_get_order(buddy_system_t *b, link_t * block) {
399 frame_t * frame;
400 frame = list_get_instance(block, frame_t, buddy_link);
401 return frame->buddy_order;
402}
403
404/** Buddy system mark_busy implementation
405 *
406 * @param b Buddy system
407 * @param block Buddy system block
408 *
409 */
410static void zone_buddy_mark_busy(buddy_system_t *b, link_t * block) {
411 frame_t * frame;
412
413 frame = list_get_instance(block, frame_t, buddy_link);
414 frame->refcount = 1;
415}
416
417/** Buddy system mark_available implementation
418 *
419 * @param b Buddy system
420 * @param block Buddy system block
421 *
422 */
423static void zone_buddy_mark_available(buddy_system_t *b, link_t * block) {
424 frame_t * frame;
425 frame = list_get_instance(block, frame_t, buddy_link);
426 frame->refcount = 0;
427}
428
429static buddy_system_operations_t zone_buddy_system_operations = {
430 .find_buddy = zone_buddy_find_buddy,
431 .bisect = zone_buddy_bisect,
432 .coalesce = zone_buddy_coalesce,
433 .set_order = zone_buddy_set_order,
434 .get_order = zone_buddy_get_order,
435 .mark_busy = zone_buddy_mark_busy,
436 .mark_available = zone_buddy_mark_available,
437 .find_block = zone_buddy_find_block,
438 .print_id = zone_buddy_print_id
439};
440
441/******************/
442/* Zone functions */
443/******************/
444
445/** Allocate frame in particular zone
446 *
447 * Assume zone is locked
448 * Panics if allocation is impossible.
449 *
450 * @param zone Zone to allocate from.
451 * @param order Allocate exactly 2^order frames.
452 *
453 * @return Frame index in zone
454 *
455 */
456static pfn_t zone_frame_alloc(zone_t *zone, uint8_t order)
457{
458 pfn_t v;
459 link_t *tmp;
460 frame_t *frame;
461
462 /* Allocate frames from zone buddy system */
463 tmp = buddy_system_alloc(zone->buddy_system, order);
464
465 ASSERT(tmp);
466
467 /* Update zone information. */
468 zone->free_count -= (1 << order);
469 zone->busy_count += (1 << order);
470
471 /* Frame will be actually a first frame of the block. */
472 frame = list_get_instance(tmp, frame_t, buddy_link);
473
474 /* get frame address */
475 v = make_frame_index(zone, frame);
476 return v;
477}
478
479/** Free frame from zone
480 *
481 * Assume zone is locked
482 *
483 * @param zone Pointer to zone from which the frame is to be freed
484 * @param frame_idx Frame index relative to zone
485 */
486static void zone_frame_free(zone_t *zone, index_t frame_idx)
487{
488 frame_t *frame;
489 uint8_t order;
490
491 frame = &zone->frames[frame_idx];
492
493 /* remember frame order */
494 order = frame->buddy_order;
495
496 ASSERT(frame->refcount);
497
498 if (!--frame->refcount) {
499 buddy_system_free(zone->buddy_system, &frame->buddy_link);
500
501 /* Update zone information. */
502 zone->free_count += (1 << order);
503 zone->busy_count -= (1 << order);
504 }
505}
506
507/** Return frame from zone */
508static frame_t * zone_get_frame(zone_t *zone, index_t frame_idx)
509{
510 ASSERT(frame_idx < zone->count);
511 return &zone->frames[frame_idx];
512}
513
514/** Mark frame in zone unavailable to allocation */
515static void zone_mark_unavailable(zone_t *zone, index_t frame_idx)
516{
517 frame_t *frame;
518 link_t *link;
519
520 frame = zone_get_frame(zone, frame_idx);
521 if (frame->refcount)
522 return;
523 link = buddy_system_alloc_block(zone->buddy_system,
524 &frame->buddy_link);
525 ASSERT(link);
526 zone->free_count--;
527}
528
529/**
530 * Join 2 zones
531 *
532 * Expect zone_t *z to point to space at least zone_conf_size large
533 *
534 * Assume z1 & z2 are locked
535 *
536 * @param z Target zone structure pointer
537 * @param z1 Zone to merge
538 * @param z2 Zone to merge
539 */
540static void _zone_merge(zone_t *z, zone_t *z1, zone_t *z2)
541{
542 uint8_t max_order;
543 unsigned int i;
544 int z2idx;
545 pfn_t frame_idx;
546 frame_t *frame;
547
548 ASSERT(!overlaps(z1->base,z1->count,z2->base,z2->count));
549 ASSERT(z1->base < z2->base);
550
551 spinlock_initialize(&z->lock, "zone_lock");
552 z->base = z1->base;
553 z->count = z2->base+z2->count - z1->base;
554 z->flags = z1->flags & z2->flags;
555
556 z->free_count = z1->free_count + z2->free_count;
557 z->busy_count = z1->busy_count + z2->busy_count;
558
559 max_order = fnzb(z->count);
560
561 z->buddy_system = (buddy_system_t *)&z[1];
562 buddy_system_create(z->buddy_system, max_order,
563 &zone_buddy_system_operations,
564 (void *) z);
565
566 z->frames = (frame_t *)((uint8_t *) z->buddy_system + buddy_conf_size(max_order));
567 for (i = 0; i < z->count; i++) {
568 /* This marks all frames busy */
569 frame_initialize(&z->frames[i]);
570 }
571 /* Copy frames from both zones to preserve full frame orders,
572 * parents etc. Set all free frames with refcount=0 to 1, because
573 * we add all free frames to buddy allocator later again, clear
574 * order to 0. Don't set busy frames with refcount=0, as they
575 * will not be reallocated during merge and it would make later
576 * problems with allocation/free.
577 */
578 for (i = 0; i < z1->count; i++)
579 z->frames[i] = z1->frames[i];
580 for (i = 0; i < z2->count; i++) {
581 z2idx = i + (z2->base - z1->base);
582 z->frames[z2idx] = z2->frames[i];
583 }
584 i = 0;
585 while (i < z->count) {
586 if (z->frames[i].refcount) {
587 /* skip busy frames */
588 i += 1 << z->frames[i].buddy_order;
589 } else { /* Free frames, set refcount=1 */
590 /* All free frames have refcount=0, we need not
591 * to check the order */
592 z->frames[i].refcount = 1;
593 z->frames[i].buddy_order = 0;
594 i++;
595 }
596 }
597 /* Add free blocks from the 2 original zones */
598 while (zone_can_alloc(z1, 0)) {
599 frame_idx = zone_frame_alloc(z1, 0);
600 frame = &z->frames[frame_idx];
601 frame->refcount = 0;
602 buddy_system_free(z->buddy_system, &frame->buddy_link);
603 }
604 while (zone_can_alloc(z2, 0)) {
605 frame_idx = zone_frame_alloc(z2, 0);
606 frame = &z->frames[frame_idx + (z2->base-z1->base)];
607 frame->refcount = 0;
608 buddy_system_free(z->buddy_system, &frame->buddy_link);
609 }
610}
611
612/** Return old configuration frames into the zone
613 *
614 * We have several cases
615 * - the conf. data is outside of zone -> exit, shall we call frame_free??
616 * - the conf. data was created by zone_create or
617 * updated with reduce_region -> free every frame
618 *
619 * @param newzone The actual zone where freeing should occur
620 * @param oldzone Pointer to old zone configuration data that should
621 * be freed from new zone
622 */
623static void return_config_frames(zone_t *newzone, zone_t *oldzone)
624{
625 pfn_t pfn;
626 frame_t *frame;
627 count_t cframes;
628 unsigned int i;
629
630 pfn = ADDR2PFN((uintptr_t)KA2PA(oldzone));
631 cframes = SIZE2FRAMES(zone_conf_size(oldzone->count));
632
633 if (pfn < newzone->base || pfn >= newzone->base + newzone->count)
634 return;
635
636 frame = &newzone->frames[pfn - newzone->base];
637 ASSERT(!frame->buddy_order);
638
639 for (i = 0; i < cframes; i++) {
640 newzone->busy_count++;
641 zone_frame_free(newzone, pfn+i-newzone->base);
642 }
643}
644
645/** Reduce allocated block to count of order 0 frames
646 *
647 * The allocated block need 2^order frames of space. Reduce all frames
648 * in block to order 0 and free the unneeded frames. This means, that
649 * when freeing the previously allocated block starting with frame_idx,
650 * you have to free every frame.
651 *
652 * @param zone
653 * @param frame_idx Index to block
654 * @param count Allocated space in block
655 */
656static void zone_reduce_region(zone_t *zone, pfn_t frame_idx, count_t count)
657{
658 count_t i;
659 uint8_t order;
660 frame_t *frame;
661
662 ASSERT(frame_idx + count < zone->count);
663
664 order = zone->frames[frame_idx].buddy_order;
665 ASSERT((count_t) (1 << order) >= count);
666
667 /* Reduce all blocks to order 0 */
668 for (i = 0; i < (count_t) (1 << order); i++) {
669 frame = &zone->frames[i + frame_idx];
670 frame->buddy_order = 0;
671 if (! frame->refcount)
672 frame->refcount = 1;
673 ASSERT(frame->refcount == 1);
674 }
675 /* Free unneeded frames */
676 for (i = count; i < (count_t) (1 << order); i++) {
677 zone_frame_free(zone, i + frame_idx);
678 }
679}
680
681/** Merge zones z1 and z2
682 *
683 * - the zones must be 2 zones with no zone existing in between,
684 * which means that z2 = z1+1
685 *
686 * - When you create a new zone, the frame allocator configuration does
687 * not to be 2^order size. Once the allocator is running it is no longer
688 * possible, merged configuration data occupies more space :-/
689 */
690void zone_merge(unsigned int z1, unsigned int z2)
691{
692 ipl_t ipl;
693 zone_t *zone1, *zone2, *newzone;
694 unsigned int cframes;
695 uint8_t order;
696 unsigned int i;
697 pfn_t pfn;
698
699 ipl = interrupts_disable();
700 spinlock_lock(&zones.lock);
701
702 if (z1 < 0 || z1 >= zones.count || z2 < 0 || z2 >= zones.count)
703 goto errout;
704 /* We can join only 2 zones with none existing inbetween */
705 if (z2-z1 != 1)
706 goto errout;
707
708 zone1 = zones.info[z1];
709 zone2 = zones.info[z2];
710 spinlock_lock(&zone1->lock);
711 spinlock_lock(&zone2->lock);
712
713 cframes = SIZE2FRAMES(zone_conf_size(zone2->base+zone2->count-zone1->base));
714 if (cframes == 1)
715 order = 0;
716 else
717 order = fnzb(cframes - 1) + 1;
718
719 /* Allocate zonedata inside one of the zones */
720 if (zone_can_alloc(zone1, order))
721 pfn = zone1->base + zone_frame_alloc(zone1, order);
722 else if (zone_can_alloc(zone2, order))
723 pfn = zone2->base + zone_frame_alloc(zone2, order);
724 else
725 goto errout2;
726
727 newzone = (zone_t *) PA2KA(PFN2ADDR(pfn));
728
729 _zone_merge(newzone, zone1, zone2);
730
731 /* Free unneeded config frames */
732 zone_reduce_region(newzone, pfn - newzone->base, cframes);
733 /* Subtract zone information from busy frames */
734 newzone->busy_count -= cframes;
735
736 /* Replace existing zones in zoneinfo list */
737 zones.info[z1] = newzone;
738 for (i = z2 + 1; i < zones.count; i++)
739 zones.info[i - 1] = zones.info[i];
740 zones.count--;
741
742 /* Free old zone information */
743 return_config_frames(newzone, zone1);
744 return_config_frames(newzone, zone2);
745errout2:
746 /* Nobody is allowed to enter to zone, so we are safe
747 * to touch the spinlocks last time */
748 spinlock_unlock(&zone1->lock);
749 spinlock_unlock(&zone2->lock);
750errout:
751 spinlock_unlock(&zones.lock);
752 interrupts_restore(ipl);
753}
754
755/**
756 * Merge all zones into one big zone
757 *
758 * It is reasonable to do this on systems whose bios reports parts in chunks,
759 * so that we could have 1 zone (it's faster).
760 */
761void zone_merge_all(void)
762{
763 int count = zones.count;
764
765 while (zones.count > 1 && --count) {
766 zone_merge(0,1);
767 break;
768 }
769}
770
771/** Create frame zone
772 *
773 * Create new frame zone.
774 *
775 * @param start Physical address of the first frame within the zone.
776 * @param count Count of frames in zone
777 * @param z Address of configuration information of zone
778 * @param flags Zone flags.
779 *
780 * @return Initialized zone.
781 */
782static void zone_construct(pfn_t start, count_t count, zone_t *z, int flags)
783{
784 unsigned int i;
785 uint8_t max_order;
786
787 spinlock_initialize(&z->lock, "zone_lock");
788 z->base = start;
789 z->count = count;
790 z->flags = flags;
791 z->free_count = count;
792 z->busy_count = 0;
793
794 /*
795 * Compute order for buddy system, initialize
796 */
797 max_order = fnzb(count);
798 z->buddy_system = (buddy_system_t *)&z[1];
799
800 buddy_system_create(z->buddy_system, max_order,
801 &zone_buddy_system_operations,
802 (void *) z);
803
804 /* Allocate frames _after_ the conframe */
805 /* Check sizes */
806 z->frames = (frame_t *)((uint8_t *) z->buddy_system + buddy_conf_size(max_order));
807 for (i = 0; i < count; i++) {
808 frame_initialize(&z->frames[i]);
809 }
810
811 /* Stuffing frames */
812 for (i = 0; i < count; i++) {
813 z->frames[i].refcount = 0;
814 buddy_system_free(z->buddy_system, &z->frames[i].buddy_link);
815 }
816}
817
818/** Compute configuration data size for zone
819 *
820 * @param count Size of zone in frames
821 * @return Size of zone configuration info (in bytes)
822 */
823uintptr_t zone_conf_size(count_t count)
824{
825 int size = sizeof(zone_t) + count*sizeof(frame_t);
826 int max_order;
827
828 max_order = fnzb(count);
829 size += buddy_conf_size(max_order);
830 return size;
831}
832
833/** Create and add zone to system
834 *
835 * @param start First frame number (absolute)
836 * @param count Size of zone in frames
837 * @param confframe Where configuration frames are supposed to be.
838 * Automatically checks, that we will not disturb the
839 * kernel and possibly init.
840 * If confframe is given _outside_ this zone, it is expected,
841 * that the area is already marked BUSY and big enough
842 * to contain zone_conf_size() amount of data.
843 * If the confframe is inside the area, the zone free frame
844 * information is modified not to include it.
845 *
846 * @return Zone number or -1 on error
847 */
848int zone_create(pfn_t start, count_t count, pfn_t confframe, int flags)
849{
850 zone_t *z;
851 uintptr_t addr;
852 count_t confcount;
853 unsigned int i;
854 int znum;
855
856 /* Theoretically we could have here 0, practically make sure
857 * nobody tries to do that. If some platform requires, remove
858 * the assert
859 */
860 ASSERT(confframe);
861 /* If conframe is supposed to be inside our zone, then make sure
862 * it does not span kernel & init
863 */
864 confcount = SIZE2FRAMES(zone_conf_size(count));
865 if (confframe >= start && confframe < start+count) {
866 for (;confframe < start + count; confframe++) {
867 addr = PFN2ADDR(confframe);
868 if (overlaps(addr, PFN2ADDR(confcount), KA2PA(config.base), config.kernel_size))
869 continue;
870
871 if (overlaps(addr, PFN2ADDR(confcount), KA2PA(config.stack_base), config.stack_size))
872 continue;
873
874 bool overlap = false;
875 count_t i;
876 for (i = 0; i < init.cnt; i++)
877 if (overlaps(addr, PFN2ADDR(confcount), KA2PA(init.tasks[i].addr), init.tasks[i].size)) {
878 overlap = true;
879 break;
880 }
881 if (overlap)
882 continue;
883
884 break;
885 }
886 if (confframe >= start + count)
887 panic("Cannot find configuration data for zone.");
888 }
889
890 z = (zone_t *)PA2KA(PFN2ADDR(confframe));
891 zone_construct(start, count, z, flags);
892 znum = zones_add_zone(z);
893 if (znum == -1)
894 return -1;
895
896 /* If confdata in zone, mark as unavailable */
897 if (confframe >= start && confframe < start + count)
898 for (i = confframe; i < confframe + confcount; i++) {
899 zone_mark_unavailable(z, i - z->base);
900 }
901 return znum;
902}
903
904/***************************************/
905/* Frame functions */
906
907/** Set parent of frame */
908void frame_set_parent(pfn_t pfn, void *data, unsigned int hint)
909{
910 zone_t *zone = find_zone_and_lock(pfn, &hint);
911
912 ASSERT(zone);
913
914 zone_get_frame(zone, pfn-zone->base)->parent = data;
915 spinlock_unlock(&zone->lock);
916}
917
918void * frame_get_parent(pfn_t pfn, unsigned int hint)
919{
920 zone_t *zone = find_zone_and_lock(pfn, &hint);
921 void *res;
922
923 ASSERT(zone);
924 res = zone_get_frame(zone, pfn - zone->base)->parent;
925
926 spinlock_unlock(&zone->lock);
927 return res;
928}
929
930/** Allocate power-of-two frames of physical memory.
931 *
932 * @param order Allocate exactly 2^order frames.
933 * @param flags Flags for host zone selection and address processing.
934 * @param pzone Preferred zone
935 *
936 * @return Physical address of the allocated frame.
937 *
938 */
939void * frame_alloc_generic(uint8_t order, int flags, unsigned int *pzone)
940{
941 ipl_t ipl;
942 int freed;
943 pfn_t v;
944 zone_t *zone;
945
946loop:
947 ipl = interrupts_disable();
948
949 /*
950 * First, find suitable frame zone.
951 */
952 zone = find_free_zone_and_lock(order, pzone);
953
954 /* If no memory, reclaim some slab memory,
955 if it does not help, reclaim all */
956 if (!zone && !(flags & FRAME_NO_RECLAIM)) {
957 freed = slab_reclaim(0);
958 if (freed)
959 zone = find_free_zone_and_lock(order, pzone);
960 if (!zone) {
961 freed = slab_reclaim(SLAB_RECLAIM_ALL);
962 if (freed)
963 zone = find_free_zone_and_lock(order, pzone);
964 }
965 }
966 if (!zone) {
967 /*
968 * TODO: Sleep until frames are available again.
969 */
970 interrupts_restore(ipl);
971
972 if (flags & FRAME_ATOMIC)
973 return 0;
974
975 panic("Sleep not implemented.\n");
976 goto loop;
977 }
978
979 v = zone_frame_alloc(zone, order);
980 v += zone->base;
981
982 spinlock_unlock(&zone->lock);
983 interrupts_restore(ipl);
984
985 if (flags & FRAME_KA)
986 return (void *)PA2KA(PFN2ADDR(v));
987 return (void *)PFN2ADDR(v);
988}
989
990/** Free a frame.
991 *
992 * Find respective frame structure for supplied physical frame address.
993 * Decrement frame reference count.
994 * If it drops to zero, move the frame structure to free list.
995 *
996 * @param Frame Physical Address of of the frame to be freed.
997 */
998void frame_free(uintptr_t frame)
999{
1000 ipl_t ipl;
1001 zone_t *zone;
1002 pfn_t pfn = ADDR2PFN(frame);
1003
1004 ipl = interrupts_disable();
1005
1006 /*
1007 * First, find host frame zone for addr.
1008 */
1009 zone = find_zone_and_lock(pfn,NULL);
1010 ASSERT(zone);
1011
1012 zone_frame_free(zone, pfn-zone->base);
1013
1014 spinlock_unlock(&zone->lock);
1015 interrupts_restore(ipl);
1016}
1017
1018/** Add reference to frame.
1019 *
1020 * Find respective frame structure for supplied PFN and
1021 * increment frame reference count.
1022 *
1023 * @param pfn Frame number of the frame to be freed.
1024 */
1025void frame_reference_add(pfn_t pfn)
1026{
1027 ipl_t ipl;
1028 zone_t *zone;
1029 frame_t *frame;
1030
1031 ipl = interrupts_disable();
1032
1033 /*
1034 * First, find host frame zone for addr.
1035 */
1036 zone = find_zone_and_lock(pfn,NULL);
1037 ASSERT(zone);
1038
1039 frame = &zone->frames[pfn-zone->base];
1040 frame->refcount++;
1041
1042 spinlock_unlock(&zone->lock);
1043 interrupts_restore(ipl);
1044}
1045
1046/** Mark given range unavailable in frame zones */
1047void frame_mark_unavailable(pfn_t start, count_t count)
1048{
1049 unsigned int i;
1050 zone_t *zone;
1051 unsigned int prefzone = 0;
1052
1053 for (i = 0; i < count; i++) {
1054 zone = find_zone_and_lock(start + i, &prefzone);
1055 if (!zone) /* PFN not found */
1056 continue;
1057 zone_mark_unavailable(zone, start + i - zone->base);
1058
1059 spinlock_unlock(&zone->lock);
1060 }
1061}
1062
1063/** Initialize physical memory management
1064 *
1065 * Initialize physical memory managemnt.
1066 */
1067void frame_init(void)
1068{
1069 if (config.cpu_active == 1) {
1070 zones.count = 0;
1071 spinlock_initialize(&zones.lock, "zones.lock");
1072 }
1073 /* Tell the architecture to create some memory */
1074 frame_arch_init();
1075 if (config.cpu_active == 1) {
1076 frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)), SIZE2FRAMES(config.kernel_size));
1077 frame_mark_unavailable(ADDR2PFN(KA2PA(config.stack_base)), SIZE2FRAMES(config.stack_size));
1078
1079 count_t i;
1080 for (i = 0; i < init.cnt; i++)
1081 frame_mark_unavailable(ADDR2PFN(KA2PA(init.tasks[i].addr)), SIZE2FRAMES(init.tasks[i].size));
1082
1083 if (ballocs.size)
1084 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)), SIZE2FRAMES(ballocs.size));
1085
1086 /* Black list first frame, as allocating NULL would
1087 * fail in some places */
1088 frame_mark_unavailable(0, 1);
1089 }
1090}
1091
1092
1093
1094/** Prints list of zones
1095 *
1096 */
1097void zone_print_list(void) {
1098 zone_t *zone = NULL;
1099 unsigned int i;
1100 ipl_t ipl;
1101
1102 ipl = interrupts_disable();
1103 spinlock_lock(&zones.lock);
1104 printf("# base address free frames busy frames\n");
1105 printf("-- ------------ ------------ ------------\n");
1106 for (i = 0; i < zones.count; i++) {
1107 zone = zones.info[i];
1108 spinlock_lock(&zone->lock);
1109 printf("%-2d %12p %12zd %12zd\n", i, PFN2ADDR(zone->base), zone->free_count, zone->busy_count);
1110 spinlock_unlock(&zone->lock);
1111 }
1112 spinlock_unlock(&zones.lock);
1113 interrupts_restore(ipl);
1114}
1115
1116/** Prints zone details.
1117 *
1118 * @param num Zone base address or zone number.
1119 */
1120void zone_print_one(unsigned int num) {
1121 zone_t *zone = NULL;
1122 ipl_t ipl;
1123 unsigned int i;
1124
1125 ipl = interrupts_disable();
1126 spinlock_lock(&zones.lock);
1127
1128 for (i = 0; i < zones.count; i++) {
1129 if (i == num || PFN2ADDR(zones.info[i]->base) == num) {
1130 zone = zones.info[i];
1131 break;
1132 }
1133 }
1134 if (!zone) {
1135 printf("Zone not found.\n");
1136 goto out;
1137 }
1138
1139 spinlock_lock(&zone->lock);
1140 printf("Memory zone information\n");
1141 printf("Zone base address: %#.*p\n", sizeof(uintptr_t) * 2, PFN2ADDR(zone->base));
1142 printf("Zone size: %zd frames (%zdK)\n", zone->count, ((zone->count) * FRAME_SIZE) >> 10);
1143 printf("Allocated space: %zd frames (%zdK)\n", zone->busy_count, (zone->busy_count * FRAME_SIZE) >> 10);
1144 printf("Available space: %zd frames (%zdK)\n", zone->free_count, (zone->free_count * FRAME_SIZE) >> 10);
1145 buddy_system_structure_print(zone->buddy_system, FRAME_SIZE);
1146
1147 spinlock_unlock(&zone->lock);
1148out:
1149 spinlock_unlock(&zones.lock);
1150 interrupts_restore(ipl);
1151}
1152
1153/** @}
1154 */
Note: See TracBrowser for help on using the repository browser.