2 * Copyright (c) 2001-2005 Jakub Jermar
3 * Copyright (c) 2005 Sergey Bondari
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
30 /** @addtogroup genericmm
36 * @brief Physical frame allocator.
38 * This file contains the physical frame allocator and memory zone management.
39 * The frame allocator is built on top of the buddy allocator.
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.
55 #include <arch/types.h>
61 #include <synch/spinlock.h>
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 */
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 */
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 */
86 buddy_system_t
*buddy_system
; /**< buddy system for the zone */
91 * The zoneinfo.lock must be locked when accessing zoneinfo structure.
92 * Some of the attributes in zone_t structures are 'read-only'
96 SPINLOCK_DECLARE(lock
);
98 zone_t
*info
[ZONES_MAX
];
102 /*********************************/
103 /* Helper functions */
104 static inline index_t
frame_index(zone_t
*zone
, frame_t
*frame
)
106 return (index_t
)(frame
- zone
->frames
);
108 static inline index_t
frame_index_abs(zone_t
*zone
, frame_t
*frame
)
110 return (index_t
)(frame
- zone
->frames
) + zone
->base
;
112 static inline int frame_index_valid(zone_t
*zone
, index_t index
)
114 return index
>= 0 && index
< zone
->count
;
117 /** Compute pfn_t from frame_t pointer & zone pointer */
118 static index_t
make_frame_index(zone_t
*zone
, frame_t
*frame
)
120 return frame
- zone
->frames
;
123 /** Initialize frame structure
125 * Initialize frame structure.
127 * @param frame Frame structure to be initialized.
129 static void frame_initialize(frame_t
*frame
)
132 frame
->buddy_order
= 0;
135 /*************************************/
136 /* Zoneinfo functions */
139 * Insert-sort zone into zones list
141 * @param newzone New zone to be inserted into zone list
142 * @return zone number on success, -1 on error
144 static int zones_add_zone(zone_t
*newzone
)
150 ipl
= interrupts_disable();
151 spinlock_lock(&zones
.lock
);
153 if (zones
.count
+ 1 == ZONES_MAX
)
154 panic("Maximum zone(%d) count exceeded.", ZONES_MAX
);
155 for (i
= 0; i
< zones
.count
; i
++) {
156 /* Check for overflow */
158 if (overlaps(newzone
->base
,newzone
->count
,
159 z
->base
, z
->count
)) {
160 printf("Zones overlap!\n");
163 if (newzone
->base
< z
->base
)
166 /* Move other zones up */
167 for (j
= i
;j
< zones
.count
; j
++)
168 zones
.info
[j
+ 1] = zones
.info
[j
];
169 zones
.info
[i
] = newzone
;
171 spinlock_unlock(&zones
.lock
);
172 interrupts_restore(ipl
);
178 * Try to find a zone where can we find the frame
180 * Assume interrupts are disabled.
182 * @param frame Frame number contained in zone
183 * @param pzone If not null, it is used as zone hint. Zone index
184 * is filled into the variable on success.
185 * @return Pointer to locked zone containing frame
187 static zone_t
* find_zone_and_lock(pfn_t frame
, int *pzone
)
190 int hint
= pzone
? *pzone
: 0;
193 spinlock_lock(&zones
.lock
);
195 if (hint
>= zones
.count
|| hint
< 0)
201 spinlock_lock(&z
->lock
);
202 if (z
->base
<= frame
&& z
->base
+ z
->count
> frame
) {
203 spinlock_unlock(&zones
.lock
); /* Unlock the global lock */
208 spinlock_unlock(&z
->lock
);
211 if (i
>= zones
.count
)
215 spinlock_unlock(&zones
.lock
);
219 /** @return True if zone can allocate specified order */
220 static int zone_can_alloc(zone_t
*z
, uint8_t order
)
222 return buddy_system_can_alloc(z
->buddy_system
, order
);
225 /** Find and lock zone that can allocate order frames.
227 * Assume interrupts are disabled.
229 * @param order Size (2^order) of free space we are trying to find
230 * @param pzone Pointer to preferred zone or NULL, on return contains zone number
232 static zone_t
* find_free_zone_and_lock(uint8_t order
, int *pzone
)
236 int hint
= pzone
? *pzone
: 0;
238 spinlock_lock(&zones
.lock
);
239 if (hint
>= zones
.count
)
245 spinlock_lock(&z
->lock
);
247 /* Check if the zone has 2^order frames area available */
248 if (zone_can_alloc(z
, order
)) {
249 spinlock_unlock(&zones
.lock
);
254 spinlock_unlock(&z
->lock
);
255 if (++i
>= zones
.count
)
258 spinlock_unlock(&zones
.lock
);
262 /**************************/
263 /* Buddy system functions */
264 /**************************/
266 /** Buddy system find_block implementation
268 * Find block that is parent of current list.
269 * That means go to lower addresses, until such block is found
271 * @param order - Order of parent must be different then this parameter!!
273 static link_t
*zone_buddy_find_block(buddy_system_t
*b
, link_t
*child
,
280 frame
= list_get_instance(child
, frame_t
, buddy_link
);
281 zone
= (zone_t
*) b
->data
;
283 index
= frame_index(zone
, frame
);
285 if (zone
->frames
[index
].buddy_order
!= order
) {
286 return &zone
->frames
[index
].buddy_link
;
288 } while(index
-- > 0);
292 static void zone_buddy_print_id(buddy_system_t
*b
, link_t
*block
)
298 frame
= list_get_instance(block
, frame_t
, buddy_link
);
299 zone
= (zone_t
*) b
->data
;
300 index
= frame_index(zone
, frame
);
301 printf("%zd", index
);
304 /** Buddy system find_buddy implementation
306 * @param b Buddy system.
307 * @param block Block for which buddy should be found
309 * @return Buddy for given block if found
311 static link_t
* zone_buddy_find_buddy(buddy_system_t
*b
, link_t
* block
)
316 bool is_left
, is_right
;
318 frame
= list_get_instance(block
, frame_t
, buddy_link
);
319 zone
= (zone_t
*) b
->data
;
320 ASSERT(IS_BUDDY_ORDER_OK(frame_index_abs(zone
, frame
), frame
->buddy_order
));
322 is_left
= IS_BUDDY_LEFT_BLOCK_ABS(zone
, frame
);
323 is_right
= IS_BUDDY_RIGHT_BLOCK_ABS(zone
, frame
);
325 ASSERT(is_left
^ is_right
);
327 index
= (frame_index(zone
, frame
)) + (1 << frame
->buddy_order
);
328 } else { /* if (is_right) */
329 index
= (frame_index(zone
, frame
)) - (1 << frame
->buddy_order
);
332 if (frame_index_valid(zone
, index
)) {
333 if (zone
->frames
[index
].buddy_order
== frame
->buddy_order
&&
334 zone
->frames
[index
].refcount
== 0) {
335 return &zone
->frames
[index
].buddy_link
;
342 /** Buddy system bisect implementation
344 * @param b Buddy system.
345 * @param block Block to bisect
347 * @return right block
349 static link_t
* zone_buddy_bisect(buddy_system_t
*b
, link_t
* block
) {
350 frame_t
* frame_l
, * frame_r
;
352 frame_l
= list_get_instance(block
, frame_t
, buddy_link
);
353 frame_r
= (frame_l
+ (1 << (frame_l
->buddy_order
- 1)));
355 return &frame_r
->buddy_link
;
358 /** Buddy system coalesce implementation
360 * @param b Buddy system.
361 * @param block_1 First block
362 * @param block_2 First block's buddy
364 * @return Coalesced block (actually block that represents lower address)
366 static link_t
* zone_buddy_coalesce(buddy_system_t
*b
, link_t
* block_1
,
369 frame_t
*frame1
, *frame2
;
371 frame1
= list_get_instance(block_1
, frame_t
, buddy_link
);
372 frame2
= list_get_instance(block_2
, frame_t
, buddy_link
);
374 return frame1
< frame2
? block_1
: block_2
;
377 /** Buddy system set_order implementation
379 * @param b Buddy system.
380 * @param block Buddy system block
381 * @param order Order to set
383 static void zone_buddy_set_order(buddy_system_t
*b
, link_t
* block
, uint8_t order
) {
385 frame
= list_get_instance(block
, frame_t
, buddy_link
);
386 frame
->buddy_order
= order
;
389 /** Buddy system get_order implementation
391 * @param b Buddy system.
392 * @param block Buddy system block
394 * @return Order of block
396 static uint8_t zone_buddy_get_order(buddy_system_t
*b
, link_t
* block
) {
398 frame
= list_get_instance(block
, frame_t
, buddy_link
);
399 return frame
->buddy_order
;
402 /** Buddy system mark_busy implementation
404 * @param b Buddy system
405 * @param block Buddy system block
408 static void zone_buddy_mark_busy(buddy_system_t
*b
, link_t
* block
) {
411 frame
= list_get_instance(block
, frame_t
, buddy_link
);
415 /** Buddy system mark_available implementation
417 * @param b Buddy system
418 * @param block Buddy system block
421 static void zone_buddy_mark_available(buddy_system_t
*b
, link_t
* block
) {
423 frame
= list_get_instance(block
, frame_t
, buddy_link
);
427 static struct buddy_system_operations zone_buddy_system_operations
= {
428 .find_buddy
= zone_buddy_find_buddy
,
429 .bisect
= zone_buddy_bisect
,
430 .coalesce
= zone_buddy_coalesce
,
431 .set_order
= zone_buddy_set_order
,
432 .get_order
= zone_buddy_get_order
,
433 .mark_busy
= zone_buddy_mark_busy
,
434 .mark_available
= zone_buddy_mark_available
,
435 .find_block
= zone_buddy_find_block
,
436 .print_id
= zone_buddy_print_id
443 /** Allocate frame in particular zone
445 * Assume zone is locked
446 * Panics if allocation is impossible.
448 * @param zone Zone to allocate from.
449 * @param order Allocate exactly 2^order frames.
451 * @return Frame index in zone
454 static pfn_t
zone_frame_alloc(zone_t
*zone
, uint8_t order
)
460 /* Allocate frames from zone buddy system */
461 tmp
= buddy_system_alloc(zone
->buddy_system
, order
);
465 /* Update zone information. */
466 zone
->free_count
-= (1 << order
);
467 zone
->busy_count
+= (1 << order
);
469 /* Frame will be actually a first frame of the block. */
470 frame
= list_get_instance(tmp
, frame_t
, buddy_link
);
472 /* get frame address */
473 v
= make_frame_index(zone
, frame
);
477 /** Free frame from zone
479 * Assume zone is locked
481 * @param zone Pointer to zone from which the frame is to be freed
482 * @param frame_idx Frame index relative to zone
484 static void zone_frame_free(zone_t
*zone
, index_t frame_idx
)
489 frame
= &zone
->frames
[frame_idx
];
491 /* remember frame order */
492 order
= frame
->buddy_order
;
494 ASSERT(frame
->refcount
);
496 if (!--frame
->refcount
) {
497 buddy_system_free(zone
->buddy_system
, &frame
->buddy_link
);
499 /* Update zone information. */
500 zone
->free_count
+= (1 << order
);
501 zone
->busy_count
-= (1 << order
);
505 /** Return frame from zone */
506 static frame_t
* zone_get_frame(zone_t
*zone
, index_t frame_idx
)
508 ASSERT(frame_idx
< zone
->count
);
509 return &zone
->frames
[frame_idx
];
512 /** Mark frame in zone unavailable to allocation */
513 static void zone_mark_unavailable(zone_t
*zone
, index_t frame_idx
)
518 frame
= zone_get_frame(zone
, frame_idx
);
521 link
= buddy_system_alloc_block(zone
->buddy_system
,
530 * Expect zone_t *z to point to space at least zone_conf_size large
532 * Assume z1 & z2 are locked
534 * @param z Target zone structure pointer
535 * @param z1 Zone to merge
536 * @param z2 Zone to merge
538 static void _zone_merge(zone_t
*z
, zone_t
*z1
, zone_t
*z2
)
545 ASSERT(!overlaps(z1
->base
,z1
->count
,z2
->base
,z2
->count
));
546 ASSERT(z1
->base
< z2
->base
);
548 spinlock_initialize(&z
->lock
, "zone_lock");
550 z
->count
= z2
->base
+z2
->count
- z1
->base
;
551 z
->flags
= z1
->flags
& z2
->flags
;
553 z
->free_count
= z1
->free_count
+ z2
->free_count
;
554 z
->busy_count
= z1
->busy_count
+ z2
->busy_count
;
556 max_order
= fnzb(z
->count
);
558 z
->buddy_system
= (buddy_system_t
*)&z
[1];
559 buddy_system_create(z
->buddy_system
, max_order
,
560 &zone_buddy_system_operations
,
563 z
->frames
= (frame_t
*)((void *)z
->buddy_system
+buddy_conf_size(max_order
));
564 for (i
= 0; i
< z
->count
; i
++) {
565 /* This marks all frames busy */
566 frame_initialize(&z
->frames
[i
]);
568 /* Copy frames from both zones to preserve full frame orders,
569 * parents etc. Set all free frames with refcount=0 to 1, because
570 * we add all free frames to buddy allocator later again, clear
571 * order to 0. Don't set busy frames with refcount=0, as they
572 * will not be reallocated during merge and it would make later
573 * problems with allocation/free.
575 for (i
=0; i
<z1
->count
; i
++)
576 z
->frames
[i
] = z1
->frames
[i
];
577 for (i
=0; i
< z2
->count
; i
++) {
578 z2idx
= i
+ (z2
->base
- z1
->base
);
579 z
->frames
[z2idx
] = z2
->frames
[i
];
582 while (i
< z
->count
) {
583 if (z
->frames
[i
].refcount
) {
584 /* skip busy frames */
585 i
+= 1 << z
->frames
[i
].buddy_order
;
586 } else { /* Free frames, set refcount=1 */
587 /* All free frames have refcount=0, we need not
588 * to check the order */
589 z
->frames
[i
].refcount
= 1;
590 z
->frames
[i
].buddy_order
= 0;
594 /* Add free blocks from the 2 original zones */
595 while (zone_can_alloc(z1
, 0)) {
596 frame_idx
= zone_frame_alloc(z1
, 0);
597 frame
= &z
->frames
[frame_idx
];
599 buddy_system_free(z
->buddy_system
, &frame
->buddy_link
);
601 while (zone_can_alloc(z2
, 0)) {
602 frame_idx
= zone_frame_alloc(z2
, 0);
603 frame
= &z
->frames
[frame_idx
+ (z2
->base
-z1
->base
)];
605 buddy_system_free(z
->buddy_system
, &frame
->buddy_link
);
609 /** Return old configuration frames into the zone
611 * We have several cases
612 * - the conf. data is outside of zone -> exit, shall we call frame_free??
613 * - the conf. data was created by zone_create or
614 * updated with reduce_region -> free every frame
616 * @param newzone The actual zone where freeing should occur
617 * @param oldzone Pointer to old zone configuration data that should
618 * be freed from new zone
620 static void return_config_frames(zone_t
*newzone
, zone_t
*oldzone
)
627 pfn
= ADDR2PFN((uintptr_t)KA2PA(oldzone
));
628 cframes
= SIZE2FRAMES(zone_conf_size(oldzone
->count
));
630 if (pfn
< newzone
->base
|| pfn
>= newzone
->base
+ newzone
->count
)
633 frame
= &newzone
->frames
[pfn
- newzone
->base
];
634 ASSERT(!frame
->buddy_order
);
636 for (i
=0; i
< cframes
; i
++) {
637 newzone
->busy_count
++;
638 zone_frame_free(newzone
, pfn
+i
-newzone
->base
);
642 /** Reduce allocated block to count of order 0 frames
644 * The allocated block need 2^order frames of space. Reduce all frames
645 * in block to order 0 and free the unneeded frames. This means, that
646 * when freeing the previously allocated block starting with frame_idx,
647 * you have to free every frame.
650 * @param frame_idx Index to block
651 * @param count Allocated space in block
653 static void zone_reduce_region(zone_t
*zone
, pfn_t frame_idx
, count_t count
)
659 ASSERT(frame_idx
+count
< zone
->count
);
661 order
= zone
->frames
[frame_idx
].buddy_order
;
662 ASSERT((1 << order
) >= count
);
664 /* Reduce all blocks to order 0 */
665 for (i
=0; i
< (1 << order
); i
++) {
666 frame
= &zone
->frames
[i
+ frame_idx
];
667 frame
->buddy_order
= 0;
668 if (! frame
->refcount
)
670 ASSERT(frame
->refcount
== 1);
672 /* Free unneeded frames */
673 for (i
=count
; i
< (1 << order
); i
++) {
674 zone_frame_free(zone
, i
+ frame_idx
);
678 /** Merge zones z1 and z2
680 * - the zones must be 2 zones with no zone existing in between,
681 * which means that z2 = z1+1
683 * - When you create a new zone, the frame allocator configuration does
684 * not to be 2^order size. Once the allocator is running it is no longer
685 * possible, merged configuration data occupies more space :-/
687 void zone_merge(int z1
, int z2
)
690 zone_t
*zone1
, *zone2
, *newzone
;
696 ipl
= interrupts_disable();
697 spinlock_lock(&zones
.lock
);
699 if (z1
< 0 || z1
>= zones
.count
|| z2
< 0 || z2
>= zones
.count
)
701 /* We can join only 2 zones with none existing inbetween */
705 zone1
= zones
.info
[z1
];
706 zone2
= zones
.info
[z2
];
707 spinlock_lock(&zone1
->lock
);
708 spinlock_lock(&zone2
->lock
);
710 cframes
= SIZE2FRAMES(zone_conf_size(zone2
->base
+zone2
->count
-zone1
->base
));
714 order
= fnzb(cframes
- 1) + 1;
716 /* Allocate zonedata inside one of the zones */
717 if (zone_can_alloc(zone1
, order
))
718 pfn
= zone1
->base
+ zone_frame_alloc(zone1
, order
);
719 else if (zone_can_alloc(zone2
, order
))
720 pfn
= zone2
->base
+ zone_frame_alloc(zone2
, order
);
724 newzone
= (zone_t
*)PA2KA(PFN2ADDR(pfn
));
726 _zone_merge(newzone
, zone1
, zone2
);
728 /* Free unneeded config frames */
729 zone_reduce_region(newzone
, pfn
- newzone
->base
, cframes
);
730 /* Subtract zone information from busy frames */
731 newzone
->busy_count
-= cframes
;
733 /* Replace existing zones in zoneinfo list */
734 zones
.info
[z1
] = newzone
;
735 for (i
= z2
+ 1; i
< zones
.count
; i
++)
736 zones
.info
[i
- 1] = zones
.info
[i
];
739 /* Free old zone information */
740 return_config_frames(newzone
, zone1
);
741 return_config_frames(newzone
, zone2
);
743 /* Nobody is allowed to enter to zone, so we are safe
744 * to touch the spinlocks last time */
745 spinlock_unlock(&zone1
->lock
);
746 spinlock_unlock(&zone2
->lock
);
748 spinlock_unlock(&zones
.lock
);
749 interrupts_restore(ipl
);
753 * Merge all zones into one big zone
755 * It is reasonable to do this on systems whose bios reports parts in chunks,
756 * so that we could have 1 zone (it's faster).
758 void zone_merge_all(void)
760 int count
= zones
.count
;
762 while (zones
.count
> 1 && --count
) {
768 /** Create frame zone
770 * Create new frame zone.
772 * @param start Physical address of the first frame within the zone.
773 * @param count Count of frames in zone
774 * @param z Address of configuration information of zone
775 * @param flags Zone flags.
777 * @return Initialized zone.
779 static void zone_construct(pfn_t start
, count_t count
, zone_t
*z
, int flags
)
784 spinlock_initialize(&z
->lock
, "zone_lock");
788 z
->free_count
= count
;
792 * Compute order for buddy system, initialize
794 max_order
= fnzb(count
);
795 z
->buddy_system
= (buddy_system_t
*)&z
[1];
797 buddy_system_create(z
->buddy_system
, max_order
,
798 &zone_buddy_system_operations
,
801 /* Allocate frames _after_ the conframe */
803 z
->frames
= (frame_t
*)((void *)z
->buddy_system
+buddy_conf_size(max_order
));
804 for (i
= 0; i
<count
; i
++) {
805 frame_initialize(&z
->frames
[i
]);
808 /* Stuffing frames */
809 for (i
= 0; i
< count
; i
++) {
810 z
->frames
[i
].refcount
= 0;
811 buddy_system_free(z
->buddy_system
, &z
->frames
[i
].buddy_link
);
815 /** Compute configuration data size for zone
817 * @param count Size of zone in frames
818 * @return Size of zone configuration info (in bytes)
820 uintptr_t zone_conf_size(count_t count
)
822 int size
= sizeof(zone_t
) + count
*sizeof(frame_t
);
825 max_order
= fnzb(count
);
826 size
+= buddy_conf_size(max_order
);
830 /** Create and add zone to system
832 * @param start First frame number (absolute)
833 * @param count Size of zone in frames
834 * @param confframe Where configuration frames are supposed to be.
835 * Automatically checks, that we will not disturb the
836 * kernel and possibly init.
837 * If confframe is given _outside_ this zone, it is expected,
838 * that the area is already marked BUSY and big enough
839 * to contain zone_conf_size() amount of data.
840 * If the confframe is inside the area, the zone free frame
841 * information is modified not to include it.
843 * @return Zone number or -1 on error
845 int zone_create(pfn_t start
, count_t count
, pfn_t confframe
, int flags
)
853 /* Theoretically we could have here 0, practically make sure
854 * nobody tries to do that. If some platform requires, remove
858 /* If conframe is supposed to be inside our zone, then make sure
859 * it does not span kernel & init
861 confcount
= SIZE2FRAMES(zone_conf_size(count
));
862 if (confframe
>= start
&& confframe
< start
+count
) {
863 for (;confframe
< start
+ count
; confframe
++) {
864 addr
= PFN2ADDR(confframe
);
865 if (overlaps(addr
, PFN2ADDR(confcount
), KA2PA(config
.base
), config
.kernel_size
))
868 if (overlaps(addr
, PFN2ADDR(confcount
), KA2PA(config
.stack_base
), config
.stack_size
))
871 bool overlap
= false;
873 for (i
= 0; i
< init
.cnt
; i
++)
874 if (overlaps(addr
, PFN2ADDR(confcount
), KA2PA(init
.tasks
[i
].addr
), init
.tasks
[i
].size
)) {
883 if (confframe
>= start
+ count
)
884 panic("Cannot find configuration data for zone.");
887 z
= (zone_t
*)PA2KA(PFN2ADDR(confframe
));
888 zone_construct(start
, count
, z
, flags
);
889 znum
= zones_add_zone(z
);
893 /* If confdata in zone, mark as unavailable */
894 if (confframe
>= start
&& confframe
< start
+count
)
895 for (i
=confframe
; i
<confframe
+confcount
; i
++) {
896 zone_mark_unavailable(z
, i
- z
->base
);
901 /***************************************/
902 /* Frame functions */
904 /** Set parent of frame */
905 void frame_set_parent(pfn_t pfn
, void *data
, int hint
)
907 zone_t
*zone
= find_zone_and_lock(pfn
, &hint
);
911 zone_get_frame(zone
, pfn
-zone
->base
)->parent
= data
;
912 spinlock_unlock(&zone
->lock
);
915 void * frame_get_parent(pfn_t pfn
, int hint
)
917 zone_t
*zone
= find_zone_and_lock(pfn
, &hint
);
921 res
= zone_get_frame(zone
, pfn
- zone
->base
)->parent
;
923 spinlock_unlock(&zone
->lock
);
927 /** Allocate power-of-two frames of physical memory.
929 * @param order Allocate exactly 2^order frames.
930 * @param flags Flags for host zone selection and address processing.
931 * @param pzone Preferred zone
933 * @return Physical address of the allocated frame.
936 void * frame_alloc_generic(uint8_t order
, int flags
, int *pzone
)
944 ipl
= interrupts_disable();
947 * First, find suitable frame zone.
949 zone
= find_free_zone_and_lock(order
, pzone
);
951 /* If no memory, reclaim some slab memory,
952 if it does not help, reclaim all */
953 if (!zone
&& !(flags
& FRAME_NO_RECLAIM
)) {
954 freed
= slab_reclaim(0);
956 zone
= find_free_zone_and_lock(order
, pzone
);
958 freed
= slab_reclaim(SLAB_RECLAIM_ALL
);
960 zone
= find_free_zone_and_lock(order
, pzone
);
965 * TODO: Sleep until frames are available again.
967 interrupts_restore(ipl
);
969 if (flags
& FRAME_ATOMIC
)
972 panic("Sleep not implemented.\n");
976 v
= zone_frame_alloc(zone
, order
);
979 spinlock_unlock(&zone
->lock
);
980 interrupts_restore(ipl
);
982 if (flags
& FRAME_KA
)
983 return (void *)PA2KA(PFN2ADDR(v
));
984 return (void *)PFN2ADDR(v
);
989 * Find respective frame structure for supplied physical frame address.
990 * Decrement frame reference count.
991 * If it drops to zero, move the frame structure to free list.
993 * @param Frame Physical Address of of the frame to be freed.
995 void frame_free(uintptr_t frame
)
999 pfn_t pfn
= ADDR2PFN(frame
);
1001 ipl
= interrupts_disable();
1004 * First, find host frame zone for addr.
1006 zone
= find_zone_and_lock(pfn
,NULL
);
1009 zone_frame_free(zone
, pfn
-zone
->base
);
1011 spinlock_unlock(&zone
->lock
);
1012 interrupts_restore(ipl
);
1015 /** Add reference to frame.
1017 * Find respective frame structure for supplied PFN and
1018 * increment frame reference count.
1020 * @param pfn Frame number of the frame to be freed.
1022 void frame_reference_add(pfn_t pfn
)
1028 ipl
= interrupts_disable();
1031 * First, find host frame zone for addr.
1033 zone
= find_zone_and_lock(pfn
,NULL
);
1036 frame
= &zone
->frames
[pfn
-zone
->base
];
1039 spinlock_unlock(&zone
->lock
);
1040 interrupts_restore(ipl
);
1043 /** Mark given range unavailable in frame zones */
1044 void frame_mark_unavailable(pfn_t start
, count_t count
)
1050 for (i
=0; i
< count
; i
++) {
1051 zone
= find_zone_and_lock(start
+ i
, &prefzone
);
1052 if (!zone
) /* PFN not found */
1054 zone_mark_unavailable(zone
, start
+ i
- zone
->base
);
1056 spinlock_unlock(&zone
->lock
);
1060 /** Initialize physical memory management
1062 * Initialize physical memory managemnt.
1064 void frame_init(void)
1066 if (config
.cpu_active
== 1) {
1068 spinlock_initialize(&zones
.lock
, "zones.lock");
1070 /* Tell the architecture to create some memory */
1072 if (config
.cpu_active
== 1) {
1073 frame_mark_unavailable(ADDR2PFN(KA2PA(config
.base
)), SIZE2FRAMES(config
.kernel_size
));
1074 frame_mark_unavailable(ADDR2PFN(KA2PA(config
.stack_base
)), SIZE2FRAMES(config
.stack_size
));
1077 for (i
= 0; i
< init
.cnt
; i
++)
1078 frame_mark_unavailable(ADDR2PFN(KA2PA(init
.tasks
[i
].addr
)), SIZE2FRAMES(init
.tasks
[i
].size
));
1081 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs
.base
)), SIZE2FRAMES(ballocs
.size
));
1083 /* Black list first frame, as allocating NULL would
1084 * fail in some places */
1085 frame_mark_unavailable(0, 1);
1091 /** Prints list of zones
1094 void zone_print_list(void) {
1095 zone_t
*zone
= NULL
;
1099 ipl
= interrupts_disable();
1100 spinlock_lock(&zones
.lock
);
1101 printf("# base address free frames busy frames\n");
1102 printf("-- ------------ ------------ ------------\n");
1103 for (i
= 0; i
< zones
.count
; i
++) {
1104 zone
= zones
.info
[i
];
1105 spinlock_lock(&zone
->lock
);
1106 printf("%-2d %12p %12zd %12zd\n", i
, PFN2ADDR(zone
->base
), zone
->free_count
, zone
->busy_count
);
1107 spinlock_unlock(&zone
->lock
);
1109 spinlock_unlock(&zones
.lock
);
1110 interrupts_restore(ipl
);
1113 /** Prints zone details.
1115 * @param num Zone base address or zone number.
1117 void zone_print_one(int num
) {
1118 zone_t
*zone
= NULL
;
1122 ipl
= interrupts_disable();
1123 spinlock_lock(&zones
.lock
);
1125 for (i
= 0; i
< zones
.count
; i
++) {
1126 if (i
== num
|| PFN2ADDR(zones
.info
[i
]->base
) == num
) {
1127 zone
= zones
.info
[i
];
1132 printf("Zone not found.\n");
1136 spinlock_lock(&zone
->lock
);
1137 printf("Memory zone information\n");
1138 printf("Zone base address: %#.*p\n", sizeof(uintptr_t) * 2, PFN2ADDR(zone
->base
));
1139 printf("Zone size: %zd frames (%zdK)\n", zone
->count
, ((zone
->count
) * FRAME_SIZE
) >> 10);
1140 printf("Allocated space: %zd frames (%zdK)\n", zone
->busy_count
, (zone
->busy_count
* FRAME_SIZE
) >> 10);
1141 printf("Available space: %zd frames (%zdK)\n", zone
->free_count
, (zone
->free_count
* FRAME_SIZE
) >> 10);
1142 buddy_system_structure_print(zone
->buddy_system
, FRAME_SIZE
);
1144 spinlock_unlock(&zone
->lock
);
1146 spinlock_unlock(&zones
.lock
);
1147 interrupts_restore(ipl
);